PromptButtonGroup.tsx 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. import { useEffect, useState } from "react";
  2. import { Button, Dropdown, Space } from "antd";
  3. import type { MenuProps } from "antd";
  4. import type { IArticleListResponse } from "../../api/Article";
  5. import { get } from "../../request";
  6. import { useAppSelector } from "../../hooks";
  7. import { currentUser } from "../../reducers/current-user";
  8. // 接口定义
  9. export interface IPromptNode {
  10. text: string;
  11. prompt?: string;
  12. children?: IPromptNode[];
  13. }
  14. // Markdown 解析函数
  15. export function parseMarkdownToPromptNodes(markdown: string): IPromptNode[] {
  16. const lines = markdown
  17. .split("\n")
  18. .map((l) => l.trim())
  19. .filter(Boolean);
  20. const result: IPromptNode[] = [];
  21. let currentButton: IPromptNode | null = null;
  22. let currentChild: { title: string; content: string[] } | null = null;
  23. for (const line of lines) {
  24. if (line.startsWith("# ")) {
  25. if (currentButton) {
  26. if (currentChild) {
  27. currentButton.children = currentButton.children || [];
  28. currentButton.children.push({
  29. text: currentChild.title,
  30. prompt: currentChild.content.join("\n"),
  31. });
  32. }
  33. result.push(currentButton);
  34. }
  35. currentButton = { text: line.replace("# ", ""), children: [] };
  36. currentChild = null;
  37. } else if (line.startsWith("## ")) {
  38. if (currentChild) {
  39. currentButton!.children!.push({
  40. text: currentChild.title,
  41. prompt: currentChild.content.join("\n"),
  42. });
  43. }
  44. currentChild = {
  45. title: line.replace("## ", ""),
  46. content: [],
  47. };
  48. } else {
  49. currentChild?.content.push(line);
  50. }
  51. }
  52. if (currentChild) {
  53. currentButton!.children!.push({
  54. text: currentChild.title,
  55. prompt: currentChild.content.join("\n"),
  56. });
  57. }
  58. if (currentButton) {
  59. // 若没有children但有currentChild.prompt,作为主按钮 prompt
  60. if (!currentButton.children?.length && currentChild?.content.length) {
  61. currentButton.prompt = currentChild.content.join("\n");
  62. delete currentButton.children;
  63. }
  64. result.push(currentButton);
  65. }
  66. return result;
  67. }
  68. interface IWidget {
  69. onText?: (prompt: string) => void;
  70. disabled?: boolean;
  71. }
  72. // 按钮组组件
  73. const PromptButtonGroup = ({ disabled = false, onText }: IWidget) => {
  74. const user = useAppSelector(currentUser);
  75. const [data, setData] = useState<IPromptNode[]>([]);
  76. useEffect(() => {
  77. if (!user) {
  78. return;
  79. }
  80. const getPromptOne = async (studio: string) => {
  81. const urlTpl = `/v2/article?view=template&studio_name=${studio}&subtitle=_template_prompt_&content=true`;
  82. const json = await get<IArticleListResponse>(urlTpl);
  83. if (json.ok) {
  84. if (json.data.rows.length > 0) {
  85. if (json.data.rows[0].content) {
  86. return json.data.rows[0].content;
  87. }
  88. }
  89. }
  90. return false;
  91. };
  92. const getPrompt = async () => {
  93. const my = await getPromptOne(user.realName);
  94. if (my) {
  95. setData(parseMarkdownToPromptNodes(my));
  96. return;
  97. }
  98. const system = await getPromptOne("admin");
  99. if (system) {
  100. setData(parseMarkdownToPromptNodes(system));
  101. }
  102. };
  103. getPrompt().catch((e) => console.error(e));
  104. }, [user, user?.realName]);
  105. return (
  106. <Space>
  107. {data.map((node) => {
  108. if (node.children && node.children.length > 0) {
  109. const items: MenuProps["items"] = node.children.map((child, idx) => ({
  110. key: `${node.text}-${idx}`,
  111. label: child.text,
  112. onClick: () => onText && onText(child.prompt || ""),
  113. }));
  114. return (
  115. <Dropdown key={node.text} menu={{ items }} trigger={["click"]}>
  116. <Button type="link" size="small" disabled={disabled}>
  117. {node.text}
  118. </Button>
  119. </Dropdown>
  120. );
  121. } else {
  122. return (
  123. <Button
  124. key={node.text}
  125. disabled={disabled}
  126. onClick={() => onText && onText(node.prompt || "")}
  127. >
  128. {node.text}
  129. </Button>
  130. );
  131. }
  132. })}
  133. </Space>
  134. );
  135. };
  136. export default PromptButtonGroup;