作者 202304001

聊天页面上传文件

@@ -16,9 +16,8 @@ import { @@ -16,9 +16,8 @@ import {
16 } from "echarts/charts"; 16 } from "echarts/charts";
17 import { CanvasRenderer } from "echarts/renderers"; 17 import { CanvasRenderer } from "echarts/renderers";
18 import { useRef, useEffect, useState } from "react"; 18 import { useRef, useEffect, useState } from "react";
19 -import { ChartComponentProps, ChartData } from "./getChartData"; 19 +import { ChartComponentProps, ChartData, ValueItem } from "./getChartData";
20 20
21 -// 注册必要的组件  
22 echarts.use([ 21 echarts.use([
23 GridComponent, 22 GridComponent,
24 TooltipComponent, 23 TooltipComponent,
@@ -40,15 +39,17 @@ const tabList = [ @@ -40,15 +39,17 @@ const tabList = [
40 { key: "line", label: "折线图" }, 39 { key: "line", label: "折线图" },
41 ]; 40 ];
42 41
43 -// 图表配置生成器  
44 const getOption = (type: string, data: ChartData): EChartsOption => { 42 const getOption = (type: string, data: ChartData): EChartsOption => {
45 const commonOption = { 43 const commonOption = {
46 title: { text: `${tabList.find((t) => t.key === type)?.label}` }, 44 title: { text: `${tabList.find((t) => t.key === type)?.label}` },
47 tooltip: { trigger: "item" }, 45 tooltip: { trigger: "item" },
48 }; 46 };
49 47
50 - // 提取数值数组  
51 - const values = data.values.map((item) => item.value); 48 + const getValue = (item: number | ValueItem): number =>
  49 + typeof item === "number" ? item : item.value;
  50 +
  51 + const getItemStyle = (item: number | ValueItem) =>
  52 + typeof item === "object" ? item.itemStyle : undefined;
52 53
53 switch (type) { 54 switch (type) {
54 case "bar": 55 case "bar":
@@ -56,7 +57,22 @@ const getOption = (type: string, data: ChartData): EChartsOption => { @@ -56,7 +57,22 @@ const getOption = (type: string, data: ChartData): EChartsOption => {
56 ...commonOption, 57 ...commonOption,
57 xAxis: { type: "category", data: data.categories }, 58 xAxis: { type: "category", data: data.categories },
58 yAxis: { type: "value" }, 59 yAxis: { type: "value" },
59 - series: [{ data: values, type: "bar" }], 60 + series: [
  61 + {
  62 + data: data.values,
  63 + type: "bar",
  64 + itemStyle: {
  65 + color: (params) => {
  66 + const dataItem = params.data as ValueItem | number;
  67 + if (typeof dataItem === "object" && dataItem.itemStyle?.color) {
  68 + return dataItem.itemStyle.color;
  69 + }
  70 + // 返回一个默认颜色,比如 ECharts 默认色系中的颜色
  71 + return "#5470c6";
  72 + },
  73 + },
  74 + },
  75 + ],
60 }; 76 };
61 case "pie": 77 case "pie":
62 return { 78 return {
@@ -64,9 +80,10 @@ const getOption = (type: string, data: ChartData): EChartsOption => { @@ -64,9 +80,10 @@ const getOption = (type: string, data: ChartData): EChartsOption => {
64 series: [ 80 series: [
65 { 81 {
66 type: "pie", 82 type: "pie",
67 - data: data.categories.map((name, i) => ({  
68 - name,  
69 - value: values[i], 83 + data: data.values.map((item, i) => ({
  84 + name: data.categories[i],
  85 + value: getValue(item),
  86 + itemStyle: getItemStyle(item),
70 })), 87 })),
71 radius: "50%", 88 radius: "50%",
72 }, 89 },
@@ -79,15 +96,25 @@ const getOption = (type: string, data: ChartData): EChartsOption => { @@ -79,15 +96,25 @@ const getOption = (type: string, data: ChartData): EChartsOption => {
79 yAxis: { type: "value" }, 96 yAxis: { type: "value" },
80 series: [ 97 series: [
81 { 98 {
82 - data: values, 99 + data: data.values.map(getValue),
83 type: "line", 100 type: "line",
84 smooth: true, 101 smooth: true,
85 areaStyle: {}, 102 areaStyle: {},
  103 + itemStyle: {
  104 + color: (params) => {
  105 + const dataItem = params.data as ValueItem | number;
  106 + if (typeof dataItem === "object" && dataItem.itemStyle?.color) {
  107 + return dataItem.itemStyle.color;
  108 + }
  109 + // 返回一个默认颜色,比如 ECharts 默认色系中的颜色
  110 + return "#5470c6";
  111 + },
  112 + },
86 }, 113 },
87 ], 114 ],
88 }; 115 };
89 default: 116 default:
90 - return {}; 117 + return commonOption;
91 } 118 }
92 }; 119 };
93 120
@@ -101,30 +128,31 @@ export function ChartComponent({ @@ -101,30 +128,31 @@ export function ChartComponent({
101 useEffect(() => { 128 useEffect(() => {
102 if (!chartRef.current) return; 129 if (!chartRef.current) return;
103 130
104 - // 数据校验  
105 if ( 131 if (
106 !data?.categories?.length || 132 !data?.categories?.length ||
107 !data?.values?.length || 133 !data?.values?.length ||
108 - data.values.some((item) => typeof item.value !== "number") 134 + data.values.some(
  135 + (item) =>
  136 + typeof item !== "number" &&
  137 + (typeof (item as ValueItem).value !== "number" ||
  138 + ((item as ValueItem).itemStyle &&
  139 + typeof (item as ValueItem).itemStyle?.color !== "string")),
  140 + )
109 ) { 141 ) {
110 console.warn("Invalid chart data"); 142 console.warn("Invalid chart data");
111 return; 143 return;
112 } 144 }
113 145
114 - // 销毁旧实例  
115 if (chartInstance.current) { 146 if (chartInstance.current) {
116 chartInstance.current.dispose(); 147 chartInstance.current.dispose();
117 } 148 }
118 149
119 - // 初始化新图表  
120 chartInstance.current = echarts.init(chartRef.current); 150 chartInstance.current = echarts.init(chartRef.current);
121 chartInstance.current.setOption(getOption(activeTabKey, data)); 151 chartInstance.current.setOption(getOption(activeTabKey, data));
122 152
123 - // 窗口resize监听  
124 const resizeHandler = () => chartInstance.current?.resize(); 153 const resizeHandler = () => chartInstance.current?.resize();
125 window.addEventListener("resize", resizeHandler); 154 window.addEventListener("resize", resizeHandler);
126 155
127 - // 清理函数  
128 return () => { 156 return () => {
129 window.removeEventListener("resize", resizeHandler); 157 window.removeEventListener("resize", resizeHandler);
130 chartInstance.current?.dispose(); 158 chartInstance.current?.dispose();
@@ -133,7 +161,7 @@ export function ChartComponent({ @@ -133,7 +161,7 @@ export function ChartComponent({
133 161
134 return ( 162 return (
135 <Card 163 <Card
136 - style={{ width: "100%", minHeight: 400 }} 164 + style={{ width: "auto", minHeight: 400 }}
137 tabList={tabList} 165 tabList={tabList}
138 activeTabKey={activeTabKey} 166 activeTabKey={activeTabKey}
139 onTabChange={setActiveTabKey} 167 onTabChange={setActiveTabKey}
@@ -145,6 +173,7 @@ export function ChartComponent({ @@ -145,6 +173,7 @@ export function ChartComponent({
145 width: "100%", 173 width: "100%",
146 height: 400, 174 height: 400,
147 minHeight: 400, 175 minHeight: 400,
  176 + alignItems: "center",
148 }} 177 }}
149 /> 178 />
150 </Card> 179 </Card>
@@ -7,21 +7,21 @@ export interface ValueItem { @@ -7,21 +7,21 @@ export interface ValueItem {
7 7
8 export interface ChartData { 8 export interface ChartData {
9 categories: string[]; 9 categories: string[];
10 - values: ValueItem[]; 10 + values: Array<number | ValueItem>;
11 } 11 }
  12 +
12 export interface ChartComponentProps { 13 export interface ChartComponentProps {
13 data: { 14 data: {
14 categories: string[]; 15 categories: string[];
15 - values: ValueItem[]; 16 + values: Array<number | ValueItem>;
16 }; 17 };
17 } 18 }
18 19
19 export function extractDataFromText(text: string): ChartComponentProps | null { 20 export function extractDataFromText(text: string): ChartComponentProps | null {
20 - const startMarkers = ["```javascript", "```json"]; 21 + const startMarkers = ["```javascript", "```json", "```typescript"];
21 let dataStartIndex = -1; 22 let dataStartIndex = -1;
22 let markerLength = 0; 23 let markerLength = 0;
23 24
24 - // 查找有效的数据块起始标记  
25 for (const marker of startMarkers) { 25 for (const marker of startMarkers) {
26 const index = text.indexOf(marker); 26 const index = text.indexOf(marker);
27 if (index !== -1) { 27 if (index !== -1) {
@@ -33,20 +33,16 @@ export function extractDataFromText(text: string): ChartComponentProps | null { @@ -33,20 +33,16 @@ export function extractDataFromText(text: string): ChartComponentProps | null {
33 33
34 let parsedData: any = null; 34 let parsedData: any = null;
35 35
36 - // 如果找到有效数据块  
37 if (dataStartIndex !== -1) { 36 if (dataStartIndex !== -1) {
38 - // 查找数据块结束标记  
39 const dataEndIndex = text.indexOf("```", dataStartIndex + markerLength); 37 const dataEndIndex = text.indexOf("```", dataStartIndex + markerLength);
40 if (dataEndIndex !== -1) { 38 if (dataEndIndex !== -1) {
41 - // 提取并清理数据块内容  
42 const dataBlock = text 39 const dataBlock = text
43 .slice(dataStartIndex + markerLength, dataEndIndex) 40 .slice(dataStartIndex + markerLength, dataEndIndex)
44 .trim() 41 .trim()
45 - .replace(/'/g, '"') // 转换单引号为双引号  
46 - .replace(/(\w+)(?=\s*:)/g, '"$1"'); // 为键添加双引号 42 + .replace(/'/g, '"')
  43 + .replace(/(\w+)(?=\s*:)/g, '"$1"');
47 44
48 try { 45 try {
49 - // 尝试安全解析数据块内容  
50 parsedData = JSON.parse(dataBlock); 46 parsedData = JSON.parse(dataBlock);
51 } catch (error) { 47 } catch (error) {
52 return null; 48 return null;
@@ -54,11 +50,9 @@ export function extractDataFromText(text: string): ChartComponentProps | null { @@ -54,11 +50,9 @@ export function extractDataFromText(text: string): ChartComponentProps | null {
54 } 50 }
55 } 51 }
56 52
57 - // 如果没有找到数据块,尝试将整个文本解析为 ChartData  
58 if (!parsedData) { 53 if (!parsedData) {
59 try { 54 try {
60 const potentialData: any = JSON.parse(text); 55 const potentialData: any = JSON.parse(text);
61 - // 验证解析后的数据是否符合 ChartData 结构  
62 if ( 56 if (
63 potentialData && 57 potentialData &&
64 Array.isArray(potentialData.categories) && 58 Array.isArray(potentialData.categories) &&
@@ -66,32 +60,33 @@ export function extractDataFromText(text: string): ChartComponentProps | null { @@ -66,32 +60,33 @@ export function extractDataFromText(text: string): ChartComponentProps | null {
66 Array.isArray(potentialData.values) && 60 Array.isArray(potentialData.values) &&
67 potentialData.values.every( 61 potentialData.values.every(
68 (v: any) => 62 (v: any) =>
69 - typeof v?.value === "number" &&  
70 - v?.itemStyle &&  
71 - typeof v.itemStyle?.color === "string", 63 + typeof v === "number" ||
  64 + (typeof v?.value === "number" &&
  65 + (v.itemStyle === undefined ||
  66 + typeof v.itemStyle?.color === "string")),
72 ) 67 )
73 ) { 68 ) {
74 parsedData = potentialData; 69 parsedData = potentialData;
75 } 70 }
76 } catch (error) { 71 } catch (error) {
77 - return null; // 如果解析失败,返回 null 72 + return null;
78 } 73 }
79 } 74 }
80 75
81 - // 如果成功解析了数据并且符合 ChartData 类型,返回  
82 if ( 76 if (
83 parsedData && 77 parsedData &&
84 Array.isArray(parsedData.categories) && 78 Array.isArray(parsedData.categories) &&
85 Array.isArray(parsedData.values) && 79 Array.isArray(parsedData.values) &&
86 parsedData.values.every( 80 parsedData.values.every(
87 (v: any) => 81 (v: any) =>
88 - typeof v?.value === "number" &&  
89 - v?.itemStyle &&  
90 - typeof v.itemStyle?.color === "string", 82 + typeof v === "number" ||
  83 + (typeof v?.value === "number" &&
  84 + (v.itemStyle === undefined ||
  85 + typeof v.itemStyle?.color === "string")),
91 ) 86 )
92 ) { 87 ) {
93 return { data: parsedData as ChartData }; 88 return { data: parsedData as ChartData };
94 } 89 }
95 90
96 - return null; // 如果未满足条件,则返回 null 91 + return null;
97 } 92 }
1 @import "../styles/animation.scss"; 1 @import "../styles/animation.scss";
2 2
  3 +.disabled {
  4 + opacity: 0.5;
  5 + cursor: not-allowed !important;
  6 + pointer-events: none; /* 可选,禁用所有鼠标事件 */
  7 +}
  8 +
  9 +.message-file{
  10 + align-items: center;
  11 + padding: 6px 12px;
  12 + background-color: var(--white);
  13 + border: 1px solid rgba(136, 136, 136, 0.2);
  14 + border-radius: 5px;
  15 +}
  16 +
  17 +
  18 +.attach-file {
  19 + position: absolute;
  20 + left: 30px;
  21 + bottom: 32px;
  22 + display: flex;
  23 + align-items: center;
  24 + padding: 6px 12px;
  25 + background-color: var(--white);
  26 + border: 1px solid rgba(136, 136, 136, 0.2);
  27 + border-radius: 5px;
  28 + cursor: default;
  29 + gap: 8px;
  30 +
  31 + &:hover .delete-file {
  32 + opacity: 1;
  33 + }
  34 +}
  35 +
  36 +.file-name {
  37 + max-width: 200px;
  38 + overflow: hidden;
  39 + text-overflow: ellipsis;
  40 + white-space: nowrap;
  41 +}
  42 +
  43 +.delete-file {
  44 + opacity: 0;
  45 + transition: opacity 0.2s ease;
  46 + cursor: pointer;
  47 + display: flex;
  48 + padding: 4px;
  49 + border-radius: 4px;
  50 +
  51 + &:hover {
  52 + background-color: rgba(0, 0, 0, 0.1);
  53 + }
  54 +}
  55 +
  56 +
3 .attach-images { 57 .attach-images {
4 position: absolute; 58 position: absolute;
5 left: 30px; 59 left: 30px;
@@ -655,9 +709,6 @@ @@ -655,9 +709,6 @@
655 min-height: 68px; 709 min-height: 68px;
656 } 710 }
657 711
658 -.chat-input:focus {  
659 -}  
660 -  
661 .chat-input-send { 712 .chat-input-send {
662 background-color: var(--primary); 713 background-color: var(--primary);
663 color: white; 714 color: white;
@@ -54,6 +54,7 @@ import ExcelIcon from "../icons/excel.svg"; @@ -54,6 +54,7 @@ import ExcelIcon from "../icons/excel.svg";
54 import WordIcon from "../icons/word.svg"; 54 import WordIcon from "../icons/word.svg";
55 import MindIcon from "../icons/mind.svg"; 55 import MindIcon from "../icons/mind.svg";
56 import PptIcon from "../icons/ppt.svg"; 56 import PptIcon from "../icons/ppt.svg";
  57 +import FileIcon from "../icons/file.svg";
57 58
58 import { 59 import {
59 BOT_HELLO, 60 BOT_HELLO,
@@ -135,9 +136,10 @@ import clsx from "clsx"; @@ -135,9 +136,10 @@ import clsx from "clsx";
135 import { getAvailableClientsCount, isMcpEnabled } from "../mcp/actions"; 136 import { getAvailableClientsCount, isMcpEnabled } from "../mcp/actions";
136 137
137 //20250317新增 138 //20250317新增
138 -import { toExcel } from "../utils/excelAndWordUtils/export2Excel"; 139 +import { getExcelData, toExcel } from "../utils/excelAndWordUtils/export2Excel";
139 import { exportWord } from "../utils/excelAndWordUtils/word"; 140 import { exportWord } from "../utils/excelAndWordUtils/word";
140 import { getMindPrompt } from "../utils/prompt"; 141 import { getMindPrompt } from "../utils/prompt";
  142 +import { message } from "antd";
141 const localStorage = safeLocalStorage(); 143 const localStorage = safeLocalStorage();
142 144
143 const ttsPlayer = createTTSPlayer(); 145 const ttsPlayer = createTTSPlayer();
@@ -436,6 +438,7 @@ export function ChatAction(props: { @@ -436,6 +438,7 @@ export function ChatAction(props: {
436 text: string; 438 text: string;
437 icon: JSX.Element; 439 icon: JSX.Element;
438 onClick: () => void; 440 onClick: () => void;
  441 + disabled?: boolean;
439 }) { 442 }) {
440 const iconRef = useRef<HTMLDivElement>(null); 443 const iconRef = useRef<HTMLDivElement>(null);
441 const textRef = useRef<HTMLDivElement>(null); 444 const textRef = useRef<HTMLDivElement>(null);
@@ -444,6 +447,8 @@ export function ChatAction(props: { @@ -444,6 +447,8 @@ export function ChatAction(props: {
444 icon: 16, 447 icon: 16,
445 }); 448 });
446 449
  450 + const { disabled = false } = props;
  451 +
447 function updateWidth() { 452 function updateWidth() {
448 if (!iconRef.current || !textRef.current) return; 453 if (!iconRef.current || !textRef.current) return;
449 const getWidth = (dom: HTMLDivElement) => dom.getBoundingClientRect().width; 454 const getWidth = (dom: HTMLDivElement) => dom.getBoundingClientRect().width;
@@ -457,8 +462,13 @@ export function ChatAction(props: { @@ -457,8 +462,13 @@ export function ChatAction(props: {
457 462
458 return ( 463 return (
459 <div 464 <div
460 - className={clsx(styles["chat-input-action"], "clickable")} 465 + className={clsx(
  466 + styles["chat-input-action"],
  467 + !disabled && "clickable",
  468 + disabled && styles["disabled"],
  469 + )}
461 onClick={() => { 470 onClick={() => {
  471 + if (disabled) return;
462 props.onClick(); 472 props.onClick();
463 setTimeout(updateWidth, 1); 473 setTimeout(updateWidth, 1);
464 }} 474 }}
@@ -523,6 +533,7 @@ function useScrollToBottom( @@ -523,6 +533,7 @@ function useScrollToBottom(
523 } 533 }
524 534
525 export function ChatActions(props: { 535 export function ChatActions(props: {
  536 + uploadFile: () => void;
526 uploadImage: () => void; 537 uploadImage: () => void;
527 setAttachImages: (images: string[]) => void; 538 setAttachImages: (images: string[]) => void;
528 setUploading: (uploading: boolean) => void; 539 setUploading: (uploading: boolean) => void;
@@ -660,6 +671,11 @@ export function ChatActions(props: { @@ -660,6 +671,11 @@ export function ChatActions(props: {
660 /> 671 />
661 )} 672 )}
662 <ChatAction 673 <ChatAction
  674 + onClick={props.uploadFile}
  675 + text="上传文件"
  676 + icon={props.uploading ? <LoadingButtonIcon /> : <FileIcon />}
  677 + />
  678 + <ChatAction
663 onClick={nextTheme} 679 onClick={nextTheme}
664 text={Locale.Chat.InputActions.Theme[theme]} 680 text={Locale.Chat.InputActions.Theme[theme]}
665 icon={ 681 icon={
@@ -1135,10 +1151,15 @@ function _Chat() { @@ -1135,10 +1151,15 @@ function _Chat() {
1135 1151
1136 const doSubmit = (userInput: string) => { 1152 const doSubmit = (userInput: string) => {
1137 if (userInput.trim() === "" && isEmpty(attachImages)) return; 1153 if (userInput.trim() === "" && isEmpty(attachImages)) return;
  1154 + if (fileData) {
  1155 + userInput = fileData + userInput;
  1156 + }
1138 const matchCommand = chatCommands.match(userInput); 1157 const matchCommand = chatCommands.match(userInput);
1139 if (matchCommand.matched) { 1158 if (matchCommand.matched) {
1140 setUserInput(""); 1159 setUserInput("");
1141 setPromptHints([]); 1160 setPromptHints([]);
  1161 + setFileData("");
  1162 + setFileName("");
1142 matchCommand.invoke(); 1163 matchCommand.invoke();
1143 return; 1164 return;
1144 } 1165 }
@@ -1148,7 +1169,10 @@ function _Chat() { @@ -1148,7 +1169,10 @@ function _Chat() {
1148 .then(() => setIsLoading(false)); 1169 .then(() => setIsLoading(false));
1149 setAttachImages([]); 1170 setAttachImages([]);
1150 chatStore.setLastInput(userInput); 1171 chatStore.setLastInput(userInput);
  1172 +
1151 setUserInput(""); 1173 setUserInput("");
  1174 + setFileData("");
  1175 + setFileName("");
1152 setPromptHints([]); 1176 setPromptHints([]);
1153 if (!isMobileScreen) inputRef.current?.focus(); 1177 if (!isMobileScreen) inputRef.current?.focus();
1154 setAutoScroll(true); 1178 setAutoScroll(true);
@@ -1584,6 +1608,7 @@ function _Chat() { @@ -1584,6 +1608,7 @@ function _Chat() {
1584 ); 1608 );
1585 1609
1586 async function uploadImage() { 1610 async function uploadImage() {
  1611 + if (fileData) return;
1587 const images: string[] = []; 1612 const images: string[] = [];
1588 images.push(...attachImages); 1613 images.push(...attachImages);
1589 1614
@@ -1744,7 +1769,40 @@ function _Chat() { @@ -1744,7 +1769,40 @@ function _Chat() {
1744 function toPowerpoint(pptMessage: string) { 1769 function toPowerpoint(pptMessage: string) {
1745 navigate("/powerpoint", { state: { msg: true, pptMessage: pptMessage } }); 1770 navigate("/powerpoint", { state: { msg: true, pptMessage: pptMessage } });
1746 } 1771 }
1747 - 1772 + //20250402新增上传文件
  1773 + const [fileData, setFileData] = useState("");
  1774 + const [fileName, setFileName] = useState("");
  1775 +
  1776 + async function uploadFile() {
  1777 + // 创建一个隐藏的文件输入框
  1778 + const fileInput = document.createElement("input");
  1779 + fileInput.type = "file";
  1780 + fileInput.accept = ".xlsx, .xls";
  1781 + fileInput.multiple = false;
  1782 + fileInput.onchange = (event: Event) => {
  1783 + const target = event.target as HTMLInputElement;
  1784 + const files = target.files;
  1785 + if (files && files.length > 0) {
  1786 + const file = files[0]; // 获取第一个文件
  1787 + getExcelData(file)
  1788 + .then((data) => {
  1789 + const value = `'''filedata
  1790 + ${file.name}
  1791 + ${JSON.stringify(data)}
  1792 + '''filedata
  1793 + `;
  1794 + setFileData(value);
  1795 + setFileName(file.name);
  1796 + console.log(value);
  1797 + })
  1798 + .catch((error) => {
  1799 + message.error("上传失败");
  1800 + });
  1801 + }
  1802 + fileInput.remove();
  1803 + };
  1804 + fileInput.click();
  1805 + }
1748 return ( 1806 return (
1749 <> 1807 <>
1750 <div className={styles.chat} key={session.id}> 1808 <div className={styles.chat} key={session.id}>
@@ -1992,7 +2050,7 @@ function _Chat() { @@ -1992,7 +2050,7 @@ function _Chat() {
1992 </> 2050 </>
1993 )} */} 2051 )} */}
1994 <ChatAction 2052 <ChatAction
1995 - text={Locale.Chat.Actions.Excel} 2053 + text={Locale.Export.Excel}
1996 icon={<ExcelIcon />} 2054 icon={<ExcelIcon />}
1997 onClick={() => 2055 onClick={() =>
1998 toExcel( 2056 toExcel(
@@ -2005,7 +2063,7 @@ function _Chat() { @@ -2005,7 +2063,7 @@ function _Chat() {
2005 ) && ( 2063 ) && (
2006 <> 2064 <>
2007 <ChatAction 2065 <ChatAction
2008 - text={Locale.Chat.Actions.Word} 2066 + text={Locale.Export.Word}
2009 icon={<WordIcon />} 2067 icon={<WordIcon />}
2010 onClick={() => 2068 onClick={() =>
2011 exportWord( 2069 exportWord(
@@ -2014,7 +2072,7 @@ function _Chat() { @@ -2014,7 +2072,7 @@ function _Chat() {
2014 } 2072 }
2015 /> 2073 />
2016 <ChatAction 2074 <ChatAction
2017 - text={Locale.Chat.Actions.Mind} 2075 + text={Locale.Export.Mind.title}
2018 icon={<MindIcon />} 2076 icon={<MindIcon />}
2019 onClick={() => { 2077 onClick={() => {
2020 toMind( 2078 toMind(
@@ -2026,7 +2084,7 @@ function _Chat() { @@ -2026,7 +2084,7 @@ function _Chat() {
2026 </> 2084 </>
2027 )} 2085 )}
2028 <ChatAction 2086 <ChatAction
2029 - text={Locale.Chat.Actions.Ppt} 2087 + text={Locale.Export.Ppt}
2030 icon={<PptIcon />} 2088 icon={<PptIcon />}
2031 onClick={() => 2089 onClick={() =>
2032 toPowerpoint( 2090 toPowerpoint(
@@ -2168,6 +2226,7 @@ function _Chat() { @@ -2168,6 +2226,7 @@ function _Chat() {
2168 /> 2226 />
2169 2227
2170 <ChatActions 2228 <ChatActions
  2229 + uploadFile={uploadFile}
2171 uploadImage={uploadImage} 2230 uploadImage={uploadImage}
2172 setAttachImages={setAttachImages} 2231 setAttachImages={setAttachImages}
2173 setUploading={setUploading} 2232 setUploading={setUploading}
@@ -2193,7 +2252,7 @@ function _Chat() { @@ -2193,7 +2252,7 @@ function _Chat() {
2193 <label 2252 <label
2194 className={clsx(styles["chat-input-panel-inner"], { 2253 className={clsx(styles["chat-input-panel-inner"], {
2195 [styles["chat-input-panel-inner-attach"]]: 2254 [styles["chat-input-panel-inner-attach"]]:
2196 - attachImages.length !== 0, 2255 + attachImages.length !== 0 || fileName,
2197 })} 2256 })}
2198 htmlFor="chat-input" 2257 htmlFor="chat-input"
2199 > 2258 >
@@ -2238,6 +2297,20 @@ function _Chat() { @@ -2238,6 +2297,20 @@ function _Chat() {
2238 })} 2297 })}
2239 </div> 2298 </div>
2240 )} 2299 )}
  2300 + {fileName && (
  2301 + <div className={styles["attach-file"]}>
  2302 + <span className={styles["file-name"]}>{fileName}</span>
  2303 + <div
  2304 + className={styles["delete-file"]}
  2305 + onClick={() => {
  2306 + setFileData("");
  2307 + setFileName("");
  2308 + }}
  2309 + >
  2310 + <DeleteIcon />
  2311 + </div>
  2312 + </div>
  2313 + )}
2241 <IconButton 2314 <IconButton
2242 icon={<SendWhiteIcon />} 2315 icon={<SendWhiteIcon />}
2243 text={Locale.Chat.Send} 2316 text={Locale.Chat.Send}
@@ -27,7 +27,7 @@ import clsx from "clsx"; @@ -27,7 +27,7 @@ import clsx from "clsx";
27 27
28 import { ChartComponentProps, extractDataFromText } from "./chart/getChartData"; 28 import { ChartComponentProps, extractDataFromText } from "./chart/getChartData";
29 import { ChartComponent } from "./chart"; 29 import { ChartComponent } from "./chart";
30 - 30 +import styles from "./chat.module.scss";
31 export function Mermaid(props: { code: string }) { 31 export function Mermaid(props: { code: string }) {
32 const ref = useRef<HTMLDivElement>(null); 32 const ref = useRef<HTMLDivElement>(null);
33 const [hasError, setHasError] = useState(false); 33 const [hasError, setHasError] = useState(false);
@@ -110,16 +110,11 @@ export function PreCode(props: { children: any }) { @@ -110,16 +110,11 @@ export function PreCode(props: { children: any }) {
110 useEffect(() => { 110 useEffect(() => {
111 if (ref.current) { 111 if (ref.current) {
112 const textContent = ref.current.innerText; 112 const textContent = ref.current.innerText;
113 - if (textContent) {  
114 - console.log("有textContent");  
115 - console.log(textContent);  
116 - const data = extractDataFromText(textContent);  
117 - console.log(data);  
118 - if (data) {  
119 - console.log("data");  
120 - setChartData(data);  
121 - }  
122 - } 113 +
  114 + textContent &&
  115 + extractDataFromText(textContent) &&
  116 + setChartData(extractDataFromText(textContent));
  117 +
123 const codeElements = ref.current.querySelectorAll( 118 const codeElements = ref.current.querySelectorAll(
124 "code", 119 "code",
125 ) as NodeListOf<HTMLElement>; 120 ) as NodeListOf<HTMLElement>;
@@ -281,11 +276,138 @@ function tryWrapHtmlCode(text: string) { @@ -281,11 +276,138 @@ function tryWrapHtmlCode(text: string) {
281 }, 276 },
282 ); 277 );
283 } 278 }
  279 +// 首先定义完善的类型
  280 +type FileMatch = {
  281 + fullMatch: string;
  282 + fileName: string;
  283 + start: number;
  284 + end: number;
  285 +};
284 286
285 function _MarkDownContent(props: { content: string }) { 287 function _MarkDownContent(props: { content: string }) {
286 const escapedContent = useMemo(() => { 288 const escapedContent = useMemo(() => {
287 return tryWrapHtmlCode(escapeBrackets(props.content)); 289 return tryWrapHtmlCode(escapeBrackets(props.content));
288 }, [props.content]); 290 }, [props.content]);
  291 +
  292 + type TextSegment = {
  293 + type: "text";
  294 + content: string;
  295 + start: number;
  296 + end: number;
  297 + };
  298 +
  299 + type ElementSegment = {
  300 + type: "element";
  301 + node: React.ReactElement;
  302 + };
  303 +
  304 + type Segment = TextSegment | ElementSegment;
  305 +
  306 + // 将子节点转换为可分析的段落段
  307 + function parseChildrenToSegments(children: React.ReactNode): Segment[] {
  308 + const segments: Segment[] = [];
  309 + let textBuffer = "";
  310 + let globalIndex = 0;
  311 +
  312 + React.Children.forEach(children, (child) => {
  313 + if (typeof child === "string") {
  314 + // 合并连续文本节点
  315 + textBuffer += child;
  316 + globalIndex += child.length;
  317 + } else {
  318 + // 遇到非文本节点时,先提交缓冲的文本
  319 + if (textBuffer) {
  320 + segments.push({
  321 + type: "text",
  322 + content: textBuffer,
  323 + start: globalIndex - textBuffer.length,
  324 + end: globalIndex,
  325 + });
  326 + textBuffer = "";
  327 + }
  328 + // 记录元素节点
  329 + segments.push({
  330 + type: "element",
  331 + node: child as React.ReactElement,
  332 + });
  333 + }
  334 + });
  335 +
  336 + // 提交最后缓冲的文本
  337 + if (textBuffer) {
  338 + segments.push({
  339 + type: "text",
  340 + content: textBuffer,
  341 + start: globalIndex - textBuffer.length,
  342 + end: globalIndex,
  343 + });
  344 + }
  345 +
  346 + return segments;
  347 + }
  348 +
  349 + // 主处理函数
  350 + function CustomParagraph({ children }: { children: React.ReactNode }) {
  351 + const segments = parseChildrenToSegments(children);
  352 + const fullText = segments
  353 + .filter((s): s is TextSegment => s.type === "text")
  354 + .map((s) => s.content)
  355 + .join("");
  356 + const regex = /'''filedata\s+([^\n]+)[\s\S]*?'''filedata/g;
  357 + const matches: Array<{
  358 + fileName: string;
  359 + start: number;
  360 + end: number;
  361 + }> = [];
  362 +
  363 + let match;
  364 + while ((match = regex.exec(fullText)) !== null) {
  365 + matches.push({
  366 + fileName: match[1].trim(),
  367 + start: match.index,
  368 + end: match.index + match[0].length,
  369 + });
  370 + }
  371 + const newChildren: React.ReactNode[] = [];
  372 + let lastPos = 0;
  373 + let currentSegmentIndex = 0;
  374 + matches.forEach((match) => {
  375 + while (currentSegmentIndex < segments.length) {
  376 + const segment = segments[currentSegmentIndex];
  377 + if (segment.type === "element") {
  378 + newChildren.push(segment.node);
  379 + currentSegmentIndex++;
  380 + continue;
  381 + }
  382 + const segmentEnd = segment.end;
  383 + if (segmentEnd <= match.start) {
  384 + newChildren.push(segment.content.slice(lastPos - segment.start));
  385 + lastPos = segmentEnd;
  386 + currentSegmentIndex++;
  387 + } else {
  388 + break;
  389 + }
  390 + }
  391 + newChildren.push(
  392 + <div key={`file-${match.start}`} className={styles["message-file"]}>
  393 + <span>{match.fileName}</span>
  394 + </div>,
  395 + );
  396 + lastPos = match.end;
  397 + });
  398 + while (currentSegmentIndex < segments.length) {
  399 + const segment = segments[currentSegmentIndex];
  400 + if (segment.type === "element") {
  401 + newChildren.push(segment.node);
  402 + } else {
  403 + newChildren.push(segment.content.slice(lastPos - segment.start));
  404 + }
  405 + currentSegmentIndex++;
  406 + }
  407 +
  408 + return <p dir="auto">{newChildren}</p>;
  409 + }
  410 +
289 return ( 411 return (
290 <ReactMarkdown 412 <ReactMarkdown
291 remarkPlugins={[RemarkMath, RemarkGfm, RemarkBreaks]} 413 remarkPlugins={[RemarkMath, RemarkGfm, RemarkBreaks]}
@@ -302,7 +424,7 @@ function _MarkDownContent(props: { content: string }) { @@ -302,7 +424,7 @@ function _MarkDownContent(props: { content: string }) {
302 components={{ 424 components={{
303 pre: PreCode, 425 pre: PreCode,
304 code: CustomCode, 426 code: CustomCode,
305 - p: (pProps) => <p {...pProps} dir="auto" />, 427 + p: CustomParagraph,
306 a: (aProps) => { 428 a: (aProps) => {
307 const href = aProps.href || ""; 429 const href = aProps.href || "";
308 if (/\.(aac|mp3|opus|wav)$/.test(href)) { 430 if (/\.(aac|mp3|opus|wav)$/.test(href)) {
@@ -160,13 +160,13 @@ export function WritingPage() { @@ -160,13 +160,13 @@ export function WritingPage() {
160 )} 160 )}
161 161
162 <ChatAction 162 <ChatAction
163 - text={Locale.Chat.Actions.Pdf} 163 + text={Locale.Export.Pdf}
164 icon={<PdfIcon />} 164 icon={<PdfIcon />}
165 onClick={() => {}} 165 onClick={() => {}}
166 /> 166 />
167 {htmlCode && ( 167 {htmlCode && (
168 <ChatAction 168 <ChatAction
169 - text={Locale.Chat.Actions.Word} 169 + text={Locale.Export.Word}
170 icon={<WordIcon />} 170 icon={<WordIcon />}
171 onClick={() => { 171 onClick={() => {
172 exportWord(htmlCode); 172 exportWord(htmlCode);
@@ -174,17 +174,18 @@ export function WritingPage() { @@ -174,17 +174,18 @@ export function WritingPage() {
174 /> 174 />
175 )} 175 )}
176 <ChatAction 176 <ChatAction
177 - text={Locale.Chat.Actions.Excel} 177 + text={Locale.Export.Excel}
178 icon={<ExcelIcon />} 178 icon={<ExcelIcon />}
179 onClick={() => {}} 179 onClick={() => {}}
  180 + disabled={true}
180 /> 181 />
181 <ChatAction 182 <ChatAction
182 - text={Locale.Chat.Actions.Ppt} 183 + text={Locale.Export.Ppt}
183 icon={<PptIcon />} 184 icon={<PptIcon />}
184 onClick={() => {}} 185 onClick={() => {}}
185 /> 186 />
186 <ChatAction 187 <ChatAction
187 - text={Locale.Chat.Actions.Mind} 188 + text={Locale.Export.Mind.title}
188 icon={<MindIcon />} 189 icon={<MindIcon />}
189 onClick={() => {}} 190 onClick={() => {}}
190 /> 191 />
  1 +<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1743555941264" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4341" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16"><path d="M213.333333 853.333333a42.666667 42.666667 0 0 1-85.333333 0v-256a42.666667 42.666667 0 0 1 85.333333 0v256z m213.333334 0a42.666667 42.666667 0 0 1-85.333334 0v-384a42.666667 42.666667 0 0 1 85.333334 0v384z m213.333333 0a42.666667 42.666667 0 0 1-85.333333 0v-341.333333a42.666667 42.666667 0 0 1 85.333333 0v341.333333z m213.333333 0a42.666667 42.666667 0 0 1-85.333333 0V384a42.666667 42.666667 0 0 1 85.333333 0v469.333333zM197.333333 459.946667a42.666667 42.666667 0 1 1-53.333333-66.56l213.333333-170.666667a42.666667 42.666667 0 0 1 42.496-6.314667l192.512 76.970667 237.312-158.208a42.666667 42.666667 0 0 1 47.36 70.997333l-256 170.666667a42.666667 42.666667 0 0 1-39.509333 4.096l-190.293333-76.074667-193.877334 155.136z" p-id="4342"></path></svg>
  1 +<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1743564801418" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2502" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16"><path d="M973.44 420.906667a42.666667 42.666667 0 0 0-32.853333-15.573334h-53.333334v-85.333333a85.333333 85.333333 0 0 0-85.333333-85.333333H533.333333l-93.866666-112.64A42.666667 42.666667 0 0 0 407.253333 106.666667h-277.333333a85.333333 85.333333 0 0 0-85.333333 85.333333v682.666667a37.333333 37.333333 0 0 0 0 8.533333v2.346667c0 2.133333 1.28 4.053333 2.133333 6.186666l1.28 2.56a27.306667 27.306667 0 0 0 3.2 5.333334v1.493333a47.573333 47.573333 0 0 0 5.546667 5.76 33.92 33.92 0 0 0 7.466666 4.693333h2.133334l5.973333 2.133334h782.933333a42.666667 42.666667 0 0 0 42.666667-34.346667l85.333333-426.666667a42.666667 42.666667 0 0 0-9.813333-31.786666zM387.2 192l93.866667 112.64a42.666667 42.666667 0 0 0 32.853333 15.36h288v85.333333H189.866667A42.666667 42.666667 0 0 0 149.333333 437.973333l-18.346666 77.013334V192z" fill="#363636" p-id="2503"></path></svg>
@@ -59,13 +59,8 @@ const cn = { @@ -59,13 +59,8 @@ const cn = {
59 Speech: "朗读", 59 Speech: "朗读",
60 StopSpeech: "停止", 60 StopSpeech: "停止",
61 //20250317新增 61 //20250317新增
62 - Word: "导出Word",  
63 - Excel: "下载Excel",  
64 - Pdf: "导出PDF",  
65 - Ppt: "导出PPT",  
66 - Mind: "生成思维导图",  
67 - Drag: "拖动模式",  
68 ReWrite: "重写", 62 ReWrite: "重写",
  63 + Chart: "查看图表",
69 }, 64 },
70 Commands: { 65 Commands: {
71 new: "新建聊天", 66 new: "新建聊天",
@@ -145,9 +140,14 @@ const cn = { @@ -145,9 +140,14 @@ const cn = {
145 Error: "分享失败", 140 Error: "分享失败",
146 }, 141 },
147 Mind: { 142 Mind: {
  143 + title: "生成思维导图",
148 ExportPng: "导出为PNG图片", 144 ExportPng: "导出为PNG图片",
149 ExportSvg: "导出为SVG", 145 ExportSvg: "导出为SVG",
150 }, 146 },
  147 + Word: "导出Word",
  148 + Excel: "下载Excel",
  149 + Pdf: "导出PDF",
  150 + Ppt: "导出PPT",
151 }, 151 },
152 Select: { 152 Select: {
153 Search: "搜索消息", 153 Search: "搜索消息",
@@ -884,7 +884,6 @@ const cn = { @@ -884,7 +884,6 @@ const cn = {
884 generateBg: "生成背景", 884 generateBg: "生成背景",
885 promptTitle: "背景提示词", 885 promptTitle: "背景提示词",
886 }, 886 },
887 - Thinking: "AI思考中",  
888 }; 887 };
889 888
890 type DeepPartial<T> = T extends object 889 type DeepPartial<T> = T extends object
@@ -10,13 +10,13 @@ const en: LocaleType = { @@ -10,13 +10,13 @@ const en: LocaleType = {
10 Error: { 10 Error: {
11 Unauthorized: isApp 11 Unauthorized: isApp
12 ? `😆 Oops, there's an issue. No worries: 12 ? `😆 Oops, there's an issue. No worries:
13 - \\ 1️⃣ New here? [Click to start chatting now 🚀](${SAAS_CHAT_UTM_URL})  
14 - \\ 2️⃣ Want to use your own OpenAI resources? [Click here](/#/settings) to change settings ⚙️` 13 + \\ 1️⃣ New here? [Click to start chatting now 🚀](${SAAS_CHAT_UTM_URL})
  14 + \\ 2️⃣ Want to use your own OpenAI resources? [Click here](/#/settings) to change settings ⚙️`
15 : `😆 Oops, there's an issue. Let's fix it: 15 : `😆 Oops, there's an issue. Let's fix it:
16 - \ 1️⃣ New here? [Click to start chatting now 🚀](${SAAS_CHAT_UTM_URL})  
17 - \ 2️⃣ Using a private setup? [Click here](/#/auth) to enter your key 🔑  
18 - \ 3️⃣ Want to use your own OpenAI resources? [Click here](/#/settings) to change settings ⚙️  
19 - `, 16 + \ 1️⃣ New here? [Click to start chatting now 🚀](${SAAS_CHAT_UTM_URL})
  17 + \ 2️⃣ Using a private setup? [Click here](/#/auth) to enter your key 🔑
  18 + \ 3️⃣ Want to use your own OpenAI resources? [Click here](/#/settings) to change settings ⚙️
  19 + `,
20 }, 20 },
21 Auth: { 21 Auth: {
22 Return: "Return", 22 Return: "Return",
@@ -59,8 +59,8 @@ const en: LocaleType = { @@ -59,8 +59,8 @@ const en: LocaleType = {
59 RefreshToast: "Title refresh request sent", 59 RefreshToast: "Title refresh request sent",
60 Speech: "Play", 60 Speech: "Play",
61 StopSpeech: "Stop", 61 StopSpeech: "Stop",
62 - Word: "ExportWord",  
63 - Excel: "ToExcel" 62 + ReWrite: "ReWrite",
  63 + Chart: "Chart",
64 }, 64 },
65 Commands: { 65 Commands: {
66 new: "Start a new chat", 66 new: "Start a new chat",
@@ -139,6 +139,15 @@ const en: LocaleType = { @@ -139,6 +139,15 @@ const en: LocaleType = {
139 Title: "Share Artifacts", 139 Title: "Share Artifacts",
140 Error: "Share Error", 140 Error: "Share Error",
141 }, 141 },
  142 + Mind: {
  143 + title: "Generate Mind Map",
  144 + ExportPng: "Export as PNG Image",
  145 + ExportSvg: "Export as SVG",
  146 + },
  147 + Word: "Export Word",
  148 + Excel: "Download Excel",
  149 + Pdf: "Export PDF",
  150 + Ppt: "Export PPT",
142 }, 151 },
143 Select: { 152 Select: {
144 Search: "Search", 153 Search: "Search",
@@ -861,6 +870,28 @@ const en: LocaleType = { @@ -861,6 +870,28 @@ const en: LocaleType = {
861 GenerateParams: "Generate Params", 870 GenerateParams: "Generate Params",
862 Detail: "Detail", 871 Detail: "Detail",
863 }, 872 },
  873 + BgRemoval: {
  874 + Title: "Smart Background Removal",
  875 + subTitle: "AI Background Removal",
  876 + error: {
  877 + reqErr: "Request Failed",
  878 + selectImg: "Please Select an Image",
  879 + code: "Please Enter the Access Password First",
  880 + prompt: "Please Enter the Prompt",
  881 + resultErr: "Failed to Load Result Image",
  882 + downLoadErr: "Please Complete Image Processing First",
  883 + statuErr: "Status Query Failed",
  884 + timeoutErr: "Processing Timed Out, Please Try Again Later",
  885 + imgLoadingErr: "Image Loading Failed",
  886 + },
  887 + success:
  888 + "Image Processing Successful, Please Save the Image Within One Hour!",
  889 + generateImg: "Generate Image",
  890 + bgRemoveBtn: "One-Click Background Removal",
  891 + downloadImg: "Download Image",
  892 + generateBg: "Generate Background",
  893 + promptTitle: "Background Prompt",
  894 + },
864 }; 895 };
865 896
866 export default en; 897 export default en;
@@ -55,9 +55,8 @@ const tw = { @@ -55,9 +55,8 @@ const tw = {
55 Edit: "編輯", 55 Edit: "編輯",
56 RefreshTitle: "重新整理標題", 56 RefreshTitle: "重新整理標題",
57 RefreshToast: "已傳送重新整理標題請求", 57 RefreshToast: "已傳送重新整理標題請求",
58 - //20250317新增  
59 - Word: "導出Word",  
60 - Excel: "下載Excel" 58 + ReWrite: "重寫",
  59 + Chart: "查看圖表",
61 }, 60 },
62 Commands: { 61 Commands: {
63 new: "新建聊天", 62 new: "新建聊天",
@@ -129,6 +128,15 @@ const tw = { @@ -129,6 +128,15 @@ const tw = {
129 Toast: "正在產生截圖", 128 Toast: "正在產生截圖",
130 Modal: "長按或按右鍵儲存圖片", 129 Modal: "長按或按右鍵儲存圖片",
131 }, 130 },
  131 + Mind: {
  132 + title: "生成思維導圖",
  133 + ExportPng: "導出為PNG圖片",
  134 + ExportSvg: "導出為SVG",
  135 + },
  136 + Word: "導出Word",
  137 + Excel: "下載Excel",
  138 + Pdf: "導出PDF",
  139 + Ppt: "導出PPT",
132 }, 140 },
133 Select: { 141 Select: {
134 Search: "查詢訊息", 142 Search: "查詢訊息",
@@ -536,6 +544,27 @@ const tw = { @@ -536,6 +544,27 @@ const tw = {
536 Topic: "主題", 544 Topic: "主題",
537 Time: "時間", 545 Time: "時間",
538 }, 546 },
  547 + BgRemoval: {
  548 + Title: "智能扣圖",
  549 + subTitle: "AI扣圖",
  550 + error: {
  551 + reqErr: "請求失敗",
  552 + selectImg: "請選擇圖片",
  553 + code: "請先輸入訪問密碼",
  554 + prompt: "請輸入提示詞",
  555 + resultErr: "結果圖片加載失敗",
  556 + downLoadErr: "請先完成圖片處理",
  557 + statuErr: "狀態查詢失敗",
  558 + timeoutErr: "處理超時,請稍後重試",
  559 + imgLoadingErr: "圖片加載失敗",
  560 + },
  561 + success: "圖片處理成功,請在一小時內保存圖片!",
  562 + generateImg: "生成圖片",
  563 + bgRemoveBtn: "一鍵扣圖",
  564 + downloadImg: "下載圖片",
  565 + generateBg: "生成背景",
  566 + promptTitle: "背景提示詞",
  567 + },
539 }; 568 };
540 569
541 type DeepPartial<T> = T extends object 570 type DeepPartial<T> = T extends object
1 /* eslint-disable */ 1 /* eslint-disable */
2 -import * as XLSX from 'xlsx'; 2 +import * as XLSX from "xlsx";
3 3
4 export function toExcel(content: string) { 4 export function toExcel(content: string) {
5 - let sheetName = 'result'; // 默认表名  
6 - let tableContent = '';  
7 -  
8 - // 查找标题内容(以 ** 或 ## 或 ### 开头)  
9 - const titleMatch = content.match(/^(#{2,3}|\*\*)\s*(.*)/m);  
10 - if (titleMatch) {  
11 - sheetName = titleMatch[2].trim().replace(/\s+/g, '_'); // 使用标题作为表名  
12 - // 提取表格内容(跳过标题部分)  
13 - tableContent = content.substring(titleMatch[0].length).trim();  
14 - } else {  
15 - tableContent = content; 5 + let sheetName = "result"; // 默认表名
  6 + let tableContent = "";
  7 +
  8 + // 查找标题内容(以 ** 或 ## 或 ### 开头)
  9 + const titleMatch = content.match(/^(#{2,3}|\*\*)\s*(.*)/m);
  10 + if (titleMatch) {
  11 + sheetName = titleMatch[2].trim().replace(/\s+/g, "_"); // 使用标题作为表名
  12 + // 提取表格内容(跳过标题部分)
  13 + tableContent = content.substring(titleMatch[0].length).trim();
  14 + } else {
  15 + tableContent = content;
  16 + }
  17 +
  18 + // 查找表格的起始位置(第一行以 | 开头且以 | 结尾)
  19 + let tableStartIndex = -1;
  20 + const lines = tableContent.split("\n");
  21 + for (let i = 0; i < lines.length; i++) {
  22 + if (lines[i].trim().startsWith("|") && lines[i].trim().endsWith("|")) {
  23 + tableStartIndex = i;
  24 + break;
16 } 25 }
  26 + }
17 27
18 - // 查找表格的起始位置(第一行以 | 开头且以 | 结尾)  
19 - let tableStartIndex = -1;  
20 - const lines = tableContent.split('\n');  
21 - for (let i = 0; i < lines.length; i++) {  
22 - if (lines[i].trim().startsWith('|') && lines[i].trim().endsWith('|')) {  
23 - tableStartIndex = i;  
24 - break;  
25 - } 28 + if (tableStartIndex === -1) {
  29 + console.error("表格内容未找到");
  30 + return;
  31 + }
  32 +
  33 + // 查找表格的结束位置(遇到不以 | 开头或者不以 | 结尾的行)
  34 + let tableEndIndex = -1;
  35 + for (let i = tableStartIndex; i < lines.length; i++) {
  36 + if (!lines[i].trim().startsWith("|") || !lines[i].trim().endsWith("|")) {
  37 + tableEndIndex = i;
  38 + break;
26 } 39 }
  40 + }
  41 +
  42 + if (tableEndIndex === -1) {
  43 + tableEndIndex = lines.length;
  44 + }
  45 +
  46 + // 提取表格内容
  47 + const tableData = lines
  48 + .slice(tableStartIndex, tableEndIndex)
  49 + .join("\n")
  50 + .trim();
27 51
28 - if (tableStartIndex === -1) {  
29 - console.error('表格内容未找到');  
30 - return; 52 + // 解析表格内容
  53 + const rows = tableData.split("\n");
  54 + const data: any[] = [];
  55 + let headers: string[] = [];
  56 +
  57 + rows.forEach((row, rowIndex) => {
  58 + // 去掉行首尾的 | 符号,并按 | 分割
  59 + const cells = row
  60 + .replace(/^\s*\|/g, "")
  61 + .replace(/\|\s*$/g, "")
  62 + .split(/\s*\|\s*/);
  63 +
  64 + // 跳过分隔线(假设分隔线的每个单元格都是由短横线组成)
  65 + if (
  66 + rowIndex > 0 &&
  67 + cells.every(
  68 + (cell) => cell.trim().replace(/-/g, "").length === 0, // 检查单元格内容是否只包含短横线
  69 + )
  70 + ) {
  71 + return;
31 } 72 }
32 73
33 - // 查找表格的结束位置(遇到不以 | 开头或者不以 | 结尾的行)  
34 - let tableEndIndex = -1;  
35 - for (let i = tableStartIndex; i < lines.length; i++) {  
36 - if (!lines[i].trim().startsWith('|') || !lines[i].trim().endsWith('|')) {  
37 - tableEndIndex = i;  
38 - break; 74 + if (rowIndex === 0) {
  75 + // 第一行是表头
  76 + headers = cells.map((cell) => cell.trim());
  77 + } else {
  78 + // 数据行
  79 + const rowData: any = {};
  80 + cells.forEach((cell, cellIndex) => {
  81 + if (cellIndex < headers.length) {
  82 + rowData[headers[cellIndex]] = cell.trim();
39 } 83 }
  84 + });
  85 + data.push(rowData);
40 } 86 }
  87 + });
41 88
42 - if (tableEndIndex === -1) {  
43 - tableEndIndex = lines.length;  
44 - } 89 + // 创建工作表
  90 + const ws = XLSX.utils.json_to_sheet(data);
  91 + const wb = XLSX.utils.book_new();
  92 + XLSX.utils.book_append_sheet(wb, ws, sheetName);
45 93
46 - // 提取表格内容  
47 - const tableData = lines.slice(tableStartIndex, tableEndIndex).join('\n').trim();  
48 -  
49 - // 解析表格内容  
50 - const rows = tableData.split('\n');  
51 - const data: any[] = [];  
52 - let headers: string[] = [];  
53 -  
54 - rows.forEach((row, rowIndex) => {  
55 - // 去掉行首尾的 | 符号,并按 | 分割  
56 - const cells = row.replace(/^\s*\|/g, '').replace(/\|\s*$/g, '').split(/\s*\|\s*/);  
57 -  
58 - // 跳过分隔线(假设分隔线的每个单元格都是由短横线组成)  
59 - if (  
60 - rowIndex > 0 &&  
61 - cells.every(cell =>  
62 - cell.trim().replace(/-/g, '').length === 0 // 检查单元格内容是否只包含短横线  
63 - )  
64 - ) {  
65 - return;  
66 - } 94 + // 生成文件并下载
  95 + XLSX.writeFile(wb, `${sheetName}.xlsx`);
  96 +}
  97 +
  98 +export function getExcelData(file: File): Promise<any[][]> {
  99 + return new Promise((resolve, reject) => {
  100 + const reader = new FileReader();
67 101
68 - if (rowIndex === 0) {  
69 - // 第一行是表头  
70 - headers = cells.map(cell => cell.trim());  
71 - } else {  
72 - // 数据行  
73 - const rowData: any = {};  
74 - cells.forEach((cell, cellIndex) => {  
75 - if (cellIndex < headers.length) {  
76 - rowData[headers[cellIndex]] = cell.trim();  
77 - }  
78 - });  
79 - data.push(rowData); 102 + reader.onload = (e) => {
  103 + try {
  104 + const data = e.target?.result;
  105 + if (!data) {
  106 + reject(new Error("Failed to read file data"));
  107 + return;
80 } 108 }
81 - });  
82 109
83 - // 创建工作表  
84 - const ws = XLSX.utils.json_to_sheet(data);  
85 - const wb = XLSX.utils.book_new();  
86 - XLSX.utils.book_append_sheet(wb, ws, sheetName); 110 + const workbook = XLSX.read(data, { type: "array" });
  111 + const firstSheetName = workbook.SheetNames[0];
  112 + const worksheet = workbook.Sheets[firstSheetName];
  113 +
  114 + // 使用类型断言将 unknown[] 转换为 any[][]
  115 + const jsonData = XLSX.utils.sheet_to_json(worksheet, {
  116 + header: 1,
  117 + }) as any[][];
  118 + resolve(jsonData.slice(0, 100));
  119 + } catch (error) {
  120 + reject(error);
  121 + }
  122 + };
  123 + reader.onerror = (error) => {
  124 + reject(error);
  125 + };
87 126
88 - // 生成文件并下载  
89 - XLSX.writeFile(wb, `${sheetName}.xlsx`);  
90 -}  
  127 + reader.readAsArrayBuffer(file);
  128 + });
  129 +}
@@ -33,7 +33,6 @@ @@ -33,7 +33,6 @@
33 "@xyflow/react": "^12.4.4", 33 "@xyflow/react": "^12.4.4",
34 "antd": "^5.24.4", 34 "antd": "^5.24.4",
35 "axios": "^1.7.5", 35 "axios": "^1.7.5",
36 - "chart.js": "^4.4.8",  
37 "cheerio": "^1.0.0", 36 "cheerio": "^1.0.0",
38 "clsx": "^2.1.1", 37 "clsx": "^2.1.1",
39 "docx": "^9.3.0", 38 "docx": "^9.3.0",
@@ -43,8 +42,6 @@ @@ -43,8 +42,6 @@
43 "file-saver": "^2.0.5", 42 "file-saver": "^2.0.5",
44 "fuse.js": "^7.0.0", 43 "fuse.js": "^7.0.0",
45 "heic2any": "^0.0.4", 44 "heic2any": "^0.0.4",
46 - "html-docx-js": "^0.3.1",  
47 - "html-docx-js-typescript": "^0.1.5",  
48 "html-to-image": "^1.11.11", 45 "html-to-image": "^1.11.11",
49 "idb-keyval": "^6.2.1", 46 "idb-keyval": "^6.2.1",
50 "lodash-es": "^4.17.21", 47 "lodash-es": "^4.17.21",
@@ -58,7 +55,6 @@ @@ -58,7 +55,6 @@
58 "openapi-client-axios": "^7.5.5", 55 "openapi-client-axios": "^7.5.5",
59 "rc-tooltip": "^6.4.0", 56 "rc-tooltip": "^6.4.0",
60 "react": "^18.2.0", 57 "react": "^18.2.0",
61 - "react-chartjs-2": "^5.3.0",  
62 "react-dom": "^18.2.0", 58 "react-dom": "^18.2.0",
63 "react-markdown": "^8.0.7", 59 "react-markdown": "^8.0.7",
64 "react-quill": "^2.0.0", 60 "react-quill": "^2.0.0",
@@ -109,7 +105,6 @@ @@ -109,7 +105,6 @@
109 "ts-node": "^10.9.2", 105 "ts-node": "^10.9.2",
110 "tsx": "^4.16.0", 106 "tsx": "^4.16.0",
111 "typescript": "5.2.2", 107 "typescript": "5.2.2",
112 - "vue-loader": "^17.3.1",  
113 "watch": "^1.0.2", 108 "watch": "^1.0.2",
114 "webpack": "^5.0.0-rc.6" 109 "webpack": "^5.0.0-rc.6"
115 }, 110 },
@@ -1841,11 +1841,6 @@ @@ -1841,11 +1841,6 @@
1841 "@jridgewell/resolve-uri" "^3.1.0" 1841 "@jridgewell/resolve-uri" "^3.1.0"
1842 "@jridgewell/sourcemap-codec" "^1.4.14" 1842 "@jridgewell/sourcemap-codec" "^1.4.14"
1843 1843
1844 -"@kurkle/color@^0.3.0":  
1845 - version "0.3.4"  
1846 - resolved "https://registry.npmmirror.com/@kurkle/color/-/color-0.3.4.tgz"  
1847 - integrity sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==  
1848 -  
1849 "@modelcontextprotocol/sdk@^1.0.4": 1844 "@modelcontextprotocol/sdk@^1.0.4":
1850 version "1.0.4" 1845 version "1.0.4"
1851 resolved "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.0.4.tgz" 1846 resolved "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.0.4.tgz"
@@ -3353,11 +3348,6 @@ braces@^3.0.2, braces@~3.0.2: @@ -3353,11 +3348,6 @@ braces@^3.0.2, braces@~3.0.2:
3353 dependencies: 3348 dependencies:
3354 fill-range "^7.0.1" 3349 fill-range "^7.0.1"
3355 3350
3356 -browser-or-node@^1.2.1:  
3357 - version "1.3.0"  
3358 - resolved "https://registry.npmmirror.com/browser-or-node/-/browser-or-node-1.3.0.tgz"  
3359 - integrity sha512-0F2z/VSnLbmEeBcUrSuDH5l0HxTXdQQzLjkmBR4cYfvg1zJrKSlmIZFqyFR8oX0NrwPhy3c3HQ6i3OxMbew4Tg==  
3360 -  
3361 browserslist@^4.14.3, browserslist@^4.21.3, browserslist@^4.21.5, browserslist@^4.23.1: 3351 browserslist@^4.14.3, browserslist@^4.21.3, browserslist@^4.21.5, browserslist@^4.23.1:
3362 version "4.24.4" 3352 version "4.24.4"
3363 resolved "https://registry.npmmirror.com/browserslist/-/browserslist-4.24.4.tgz" 3353 resolved "https://registry.npmmirror.com/browserslist/-/browserslist-4.24.4.tgz"
@@ -3507,13 +3497,6 @@ character-entities@^2.0.0: @@ -3507,13 +3497,6 @@ character-entities@^2.0.0:
3507 resolved "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz" 3497 resolved "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz"
3508 integrity sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ== 3498 integrity sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==
3509 3499
3510 -chart.js@^4.4.8:  
3511 - version "4.4.8"  
3512 - resolved "https://registry.npmmirror.com/chart.js/-/chart.js-4.4.8.tgz#54645b638e9d585099bc16b892947b5e6cd2a552"  
3513 - integrity sha512-IkGZlVpXP+83QpMm4uxEiGqSI7jFizwVtF3+n5Pc3k7sMO+tkd0qxh2OzLhenM0K80xtmAONWGBn082EiBQSDA==  
3514 - dependencies:  
3515 - "@kurkle/color" "^0.3.0"  
3516 -  
3517 cheerio-select@^2.1.0: 3500 cheerio-select@^2.1.0:
3518 version "2.1.0" 3501 version "2.1.0"
3519 resolved "https://registry.npmmirror.com/cheerio-select/-/cheerio-select-2.1.0.tgz" 3502 resolved "https://registry.npmmirror.com/cheerio-select/-/cheerio-select-2.1.0.tgz"
@@ -5506,11 +5489,6 @@ has@^1.0.3: @@ -5506,11 +5489,6 @@ has@^1.0.3:
5506 dependencies: 5489 dependencies:
5507 function-bind "^1.1.1" 5490 function-bind "^1.1.1"
5508 5491
5509 -hash-sum@^2.0.0:  
5510 - version "2.0.0"  
5511 - resolved "https://registry.npmmirror.com/hash-sum/-/hash-sum-2.0.0.tgz"  
5512 - integrity sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==  
5513 -  
5514 hash.js@^1.1.7: 5492 hash.js@^1.1.7:
5515 version "1.1.7" 5493 version "1.1.7"
5516 resolved "https://registry.npmmirror.com/hash.js/-/hash.js-1.1.7.tgz" 5494 resolved "https://registry.npmmirror.com/hash.js/-/hash.js-1.1.7.tgz"
@@ -5631,24 +5609,6 @@ hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: @@ -5631,24 +5609,6 @@ hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2:
5631 dependencies: 5609 dependencies:
5632 react-is "^16.7.0" 5610 react-is "^16.7.0"
5633 5611
5634 -html-docx-js-typescript@^0.1.5:  
5635 - version "0.1.5"  
5636 - resolved "https://registry.npmmirror.com/html-docx-js-typescript/-/html-docx-js-typescript-0.1.5.tgz"  
5637 - integrity sha512-GNojWFDYbpHSIgKml6/0oAom8mtHrHRTWKMyLRdeJQHO/CyeM6H39DYgzYvPp4OhBp2Ti8dxMKFq0/FkpYD4bg==  
5638 - dependencies:  
5639 - browser-or-node "^1.2.1"  
5640 - jszip "^3.4.0"  
5641 - tslib "^1.13.0"  
5642 -  
5643 -html-docx-js@^0.3.1:  
5644 - version "0.3.1"  
5645 - resolved "https://registry.npmmirror.com/html-docx-js/-/html-docx-js-0.3.1.tgz"  
5646 - integrity sha512-QSrMiRhxesqxYCa3f+2Z3ttIHPzSjDOL1tCOmIDIEET7HdabxXND6tAbsFMXAgRG4RADQ3wbl74ydMmjidaDPA==  
5647 - dependencies:  
5648 - jszip "^2.3.0"  
5649 - lodash.escape "^3.0.0"  
5650 - lodash.merge "^3.2.0"  
5651 -  
5652 html-encoding-sniffer@^3.0.0: 5612 html-encoding-sniffer@^3.0.0:
5653 version "3.0.0" 5613 version "3.0.0"
5654 resolved "https://registry.npmmirror.com/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz" 5614 resolved "https://registry.npmmirror.com/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz"
@@ -6591,7 +6551,7 @@ json5@^2.2.2, json5@^2.2.3: @@ -6591,7 +6551,7 @@ json5@^2.2.2, json5@^2.2.3:
6591 array-includes "^3.1.5" 6551 array-includes "^3.1.5"
6592 object.assign "^4.1.3" 6552 object.assign "^4.1.3"
6593 6553
6594 -jszip@*, jszip@^3.10.1, jszip@^3.4.0: 6554 +jszip@*, jszip@^3.10.1:
6595 version "3.10.1" 6555 version "3.10.1"
6596 resolved "https://registry.npmmirror.com/jszip/-/jszip-3.10.1.tgz" 6556 resolved "https://registry.npmmirror.com/jszip/-/jszip-3.10.1.tgz"
6597 integrity sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g== 6557 integrity sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==
@@ -6601,13 +6561,6 @@ jszip@*, jszip@^3.10.1, jszip@^3.4.0: @@ -6601,13 +6561,6 @@ jszip@*, jszip@^3.10.1, jszip@^3.4.0:
6601 readable-stream "~2.3.6" 6561 readable-stream "~2.3.6"
6602 setimmediate "^1.0.5" 6562 setimmediate "^1.0.5"
6603 6563
6604 -jszip@^2.3.0:  
6605 - version "2.7.0"  
6606 - resolved "https://registry.npmmirror.com/jszip/-/jszip-2.7.0.tgz"  
6607 - integrity sha512-JIsRKRVC3gTRo2vM4Wy9WBC3TRcfnIZU8k65Phi3izkvPH975FowRYtKGT6PxevA0XnJ/yO8b0QwV0ydVyQwfw==  
6608 - dependencies:  
6609 - pako "~1.0.2"  
6610 -  
6611 katex@^0.13.0: 6564 katex@^0.13.0:
6612 version "0.13.24" 6565 version "0.13.24"
6613 resolved "https://registry.npmjs.org/katex/-/katex-0.13.24.tgz" 6566 resolved "https://registry.npmjs.org/katex/-/katex-0.13.24.tgz"
@@ -6746,148 +6699,21 @@ lodash-es@^4.17.21: @@ -6746,148 +6699,21 @@ lodash-es@^4.17.21:
6746 resolved "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz" 6699 resolved "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz"
6747 integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== 6700 integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==
6748 6701
6749 -lodash._arraycopy@^3.0.0:  
6750 - version "3.0.0"  
6751 - resolved "https://registry.npmmirror.com/lodash._arraycopy/-/lodash._arraycopy-3.0.0.tgz"  
6752 - integrity sha512-RHShTDnPKP7aWxlvXKiDT6IX2jCs6YZLCtNhOru/OX2Q/tzX295vVBK5oX1ECtN+2r86S0Ogy8ykP1sgCZAN0A==  
6753 -  
6754 -lodash._arrayeach@^3.0.0:  
6755 - version "3.0.0"  
6756 - resolved "https://registry.npmmirror.com/lodash._arrayeach/-/lodash._arrayeach-3.0.0.tgz"  
6757 - integrity sha512-Mn7HidOVcl3mkQtbPsuKR0Fj0N6Q6DQB77CtYncZcJc0bx5qv2q4Gl6a0LC1AN+GSxpnBDNnK3CKEm9XNA4zqQ==  
6758 -  
6759 -lodash._basecopy@^3.0.0:  
6760 - version "3.0.1"  
6761 - resolved "https://registry.npmmirror.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz"  
6762 - integrity sha512-rFR6Vpm4HeCK1WPGvjZSJ+7yik8d8PVUdCJx5rT2pogG4Ve/2ZS7kfmO5l5T2o5V2mqlNIfSF5MZlr1+xOoYQQ==  
6763 -  
6764 -lodash._basefor@^3.0.0:  
6765 - version "3.0.3"  
6766 - resolved "https://registry.npmmirror.com/lodash._basefor/-/lodash._basefor-3.0.3.tgz"  
6767 - integrity sha512-6bc3b8grkpMgDcVJv9JYZAk/mHgcqMljzm7OsbmcE2FGUMmmLQTPHlh/dFqR8LA0GQ7z4K67JSotVKu5058v1A==  
6768 -  
6769 -lodash._bindcallback@^3.0.0:  
6770 - version "3.0.1"  
6771 - resolved "https://registry.npmmirror.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz"  
6772 - integrity sha512-2wlI0JRAGX8WEf4Gm1p/mv/SZ+jLijpj0jyaE/AXeuQphzCgD8ZQW4oSpoN8JAopujOFGU3KMuq7qfHBWlGpjQ==  
6773 -  
6774 -lodash._createassigner@^3.0.0:  
6775 - version "3.1.1"  
6776 - resolved "https://registry.npmmirror.com/lodash._createassigner/-/lodash._createassigner-3.1.1.tgz"  
6777 - integrity sha512-LziVL7IDnJjQeeV95Wvhw6G28Z8Q6da87LWKOPWmzBLv4u6FAT/x5v00pyGW0u38UoogNF2JnD3bGgZZDaNEBw==  
6778 - dependencies:  
6779 - lodash._bindcallback "^3.0.0"  
6780 - lodash._isiterateecall "^3.0.0"  
6781 - lodash.restparam "^3.0.0"  
6782 -  
6783 -lodash._getnative@^3.0.0:  
6784 - version "3.9.1"  
6785 - resolved "https://registry.npmmirror.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz"  
6786 - integrity sha512-RrL9VxMEPyDMHOd9uFbvMe8X55X16/cGM5IgOKgRElQZutpX89iS6vwl64duTV1/16w5JY7tuFNXqoekmh1EmA==  
6787 -  
6788 -lodash._isiterateecall@^3.0.0:  
6789 - version "3.0.9"  
6790 - resolved "https://registry.npmmirror.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz"  
6791 - integrity sha512-De+ZbrMu6eThFti/CSzhRvTKMgQToLxbij58LMfM8JnYDNSOjkjTCIaa8ixglOeGh2nyPlakbt5bJWJ7gvpYlQ==  
6792 -  
6793 -lodash._root@^3.0.0:  
6794 - version "3.0.1"  
6795 - resolved "https://registry.npmmirror.com/lodash._root/-/lodash._root-3.0.1.tgz"  
6796 - integrity sha512-O0pWuFSK6x4EXhM1dhZ8gchNtG7JMqBtrHdoUFUWXD7dJnNSUze1GuyQr5sOs0aCvgGeI3o/OJW8f4ca7FDxmQ==  
6797 -  
6798 lodash.debounce@^4.0.8: 6702 lodash.debounce@^4.0.8:
6799 version "4.0.8" 6703 version "4.0.8"
6800 resolved "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz" 6704 resolved "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz"
6801 integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== 6705 integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==
6802 6706
6803 -lodash.escape@^3.0.0:  
6804 - version "3.2.0"  
6805 - resolved "https://registry.npmmirror.com/lodash.escape/-/lodash.escape-3.2.0.tgz"  
6806 - integrity sha512-n1PZMXgaaDWZDSvuNZ/8XOcYO2hOKDqZel5adtR30VKQAtoWs/5AOeFA0vPV8moiPzlqe7F4cP2tzpFewQyelQ==  
6807 - dependencies:  
6808 - lodash._root "^3.0.0"  
6809 -  
6810 lodash.escape@^4.0.1: 6707 lodash.escape@^4.0.1:
6811 version "4.0.1" 6708 version "4.0.1"
6812 resolved "https://registry.npmjs.org/lodash.escape/-/lodash.escape-4.0.1.tgz" 6709 resolved "https://registry.npmjs.org/lodash.escape/-/lodash.escape-4.0.1.tgz"
6813 integrity sha512-nXEOnb/jK9g0DYMr1/Xvq6l5xMD7GDG55+GSYIYmS0G4tBk/hURD4JR9WCavs04t33WmJx9kCyp9vJ+mr4BOUw== 6710 integrity sha512-nXEOnb/jK9g0DYMr1/Xvq6l5xMD7GDG55+GSYIYmS0G4tBk/hURD4JR9WCavs04t33WmJx9kCyp9vJ+mr4BOUw==
6814 6711
6815 -lodash.isarguments@^3.0.0:  
6816 - version "3.1.0"  
6817 - resolved "https://registry.npmmirror.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz"  
6818 - integrity sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==  
6819 -  
6820 -lodash.isarray@^3.0.0:  
6821 - version "3.0.4"  
6822 - resolved "https://registry.npmmirror.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz"  
6823 - integrity sha512-JwObCrNJuT0Nnbuecmqr5DgtuBppuCvGD9lxjFpAzwnVtdGoDQ1zig+5W8k5/6Gcn0gZ3936HDAlGd28i7sOGQ==  
6824 -  
6825 -lodash.isplainobject@^3.0.0:  
6826 - version "3.2.0"  
6827 - resolved "https://registry.npmmirror.com/lodash.isplainobject/-/lodash.isplainobject-3.2.0.tgz"  
6828 - integrity sha512-P4wZnho5curNqeEq/x292Pb57e1v+woR7DJ84DURelKB46lby8aDEGVobSaYtzHdQBWQrJSdxcCwjlGOvvdIyg==  
6829 - dependencies:  
6830 - lodash._basefor "^3.0.0"  
6831 - lodash.isarguments "^3.0.0"  
6832 - lodash.keysin "^3.0.0"  
6833 -  
6834 -lodash.istypedarray@^3.0.0:  
6835 - version "3.0.6"  
6836 - resolved "https://registry.npmmirror.com/lodash.istypedarray/-/lodash.istypedarray-3.0.6.tgz"  
6837 - integrity sha512-lGWJ6N8AA3KSv+ZZxlTdn4f6A7kMfpJboeyvbFdE7IU9YAgweODqmOgdUHOA+c6lVWeVLysdaxciFXi+foVsWw==  
6838 -  
6839 -lodash.keys@^3.0.0:  
6840 - version "3.1.2"  
6841 - resolved "https://registry.npmmirror.com/lodash.keys/-/lodash.keys-3.1.2.tgz"  
6842 - integrity sha512-CuBsapFjcubOGMn3VD+24HOAPxM79tH+V6ivJL3CHYjtrawauDJHUk//Yew9Hvc6e9rbCrURGk8z6PC+8WJBfQ==  
6843 - dependencies:  
6844 - lodash._getnative "^3.0.0"  
6845 - lodash.isarguments "^3.0.0"  
6846 - lodash.isarray "^3.0.0"  
6847 -  
6848 -lodash.keysin@^3.0.0:  
6849 - version "3.0.8"  
6850 - resolved "https://registry.npmmirror.com/lodash.keysin/-/lodash.keysin-3.0.8.tgz"  
6851 - integrity sha512-YDB/5xkL3fBKFMDaC+cfGV00pbiJ6XoJIfRmBhv7aR6wWtbCW6IzkiWnTfkiHTF6ALD7ff83dAtB3OEaSoyQPg==  
6852 - dependencies:  
6853 - lodash.isarguments "^3.0.0"  
6854 - lodash.isarray "^3.0.0"  
6855 -  
6856 -lodash.merge@^3.2.0:  
6857 - version "3.3.2"  
6858 - resolved "https://registry.npmmirror.com/lodash.merge/-/lodash.merge-3.3.2.tgz"  
6859 - integrity sha512-ZgGZpRhWLjivGUbjtApZR4HyLv/UAyoYqESVYkK4aLBJVHRrbFpG+GNnE9JPijliME4LkKM0SFI/WyOiBiv1+w==  
6860 - dependencies:  
6861 - lodash._arraycopy "^3.0.0"  
6862 - lodash._arrayeach "^3.0.0"  
6863 - lodash._createassigner "^3.0.0"  
6864 - lodash._getnative "^3.0.0"  
6865 - lodash.isarguments "^3.0.0"  
6866 - lodash.isarray "^3.0.0"  
6867 - lodash.isplainobject "^3.0.0"  
6868 - lodash.istypedarray "^3.0.0"  
6869 - lodash.keys "^3.0.0"  
6870 - lodash.keysin "^3.0.0"  
6871 - lodash.toplainobject "^3.0.0"  
6872 -  
6873 lodash.merge@^4.6.2: 6712 lodash.merge@^4.6.2:
6874 version "4.6.2" 6713 version "4.6.2"
6875 resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz" 6714 resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz"
6876 integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== 6715 integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
6877 6716
6878 -lodash.restparam@^3.0.0:  
6879 - version "3.6.1"  
6880 - resolved "https://registry.npmmirror.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz"  
6881 - integrity sha512-L4/arjjuq4noiUJpt3yS6KIKDtJwNe2fIYgMqyYYKoeIfV1iEqvPwhCx23o+R9dzouGihDAPN1dTIRWa7zk8tw==  
6882 -  
6883 -lodash.toplainobject@^3.0.0:  
6884 - version "3.0.0"  
6885 - resolved "https://registry.npmmirror.com/lodash.toplainobject/-/lodash.toplainobject-3.0.0.tgz"  
6886 - integrity sha512-wMI0Ju1bvSmnBS3EcRRH/3zDnZOPpDtMtNDzbbNMKuTrEpALsf+sPyMeogmv63Y11qZQO7H1xFzohIEGRMjPYA==  
6887 - dependencies:  
6888 - lodash._basecopy "^3.0.0"  
6889 - lodash.keysin "^3.0.0"  
6890 -  
6891 lodash.unescape@^4.0.1: 6717 lodash.unescape@^4.0.1:
6892 version "4.0.1" 6718 version "4.0.1"
6893 resolved "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz" 6719 resolved "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz"
@@ -8485,11 +8311,6 @@ rc-virtual-list@^3.14.2, rc-virtual-list@^3.5.1, rc-virtual-list@^3.5.2: @@ -8485,11 +8311,6 @@ rc-virtual-list@^3.14.2, rc-virtual-list@^3.5.1, rc-virtual-list@^3.5.2:
8485 rc-resize-observer "^1.0.0" 8311 rc-resize-observer "^1.0.0"
8486 rc-util "^5.36.0" 8312 rc-util "^5.36.0"
8487 8313
8488 -react-chartjs-2@^5.3.0:  
8489 - version "5.3.0"  
8490 - resolved "https://registry.npmmirror.com/react-chartjs-2/-/react-chartjs-2-5.3.0.tgz#2d3286339a742bc7f77b5829c33ebab215f714cc"  
8491 - integrity sha512-UfZZFnDsERI3c3CZGxzvNJd02SHjaSJ8kgW1djn65H1KK8rehwTjyrRKOG3VTMG8wtHZ5rgAO5oTHtHi9GCCmw==  
8492 -  
8493 react-dom@^18.2.0: 8314 react-dom@^18.2.0:
8494 version "18.2.0" 8315 version "18.2.0"
8495 resolved "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz" 8316 resolved "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz"
@@ -9557,11 +9378,6 @@ tslib@2.3.0: @@ -9557,11 +9378,6 @@ tslib@2.3.0:
9557 resolved "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e" 9378 resolved "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e"
9558 integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg== 9379 integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==
9559 9380
9560 -tslib@^1.13.0:  
9561 - version "1.14.1"  
9562 - resolved "https://registry.npmmirror.com/tslib/-/tslib-1.14.1.tgz"  
9563 - integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==  
9564 -  
9565 tslib@^2.1.0, tslib@^2.4.0: 9381 tslib@^2.1.0, tslib@^2.4.0:
9566 version "2.5.0" 9382 version "2.5.0"
9567 resolved "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz" 9383 resolved "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz"
@@ -9860,15 +9676,6 @@ vfile@^5.0.0: @@ -9860,15 +9676,6 @@ vfile@^5.0.0:
9860 unist-util-stringify-position "^3.0.0" 9676 unist-util-stringify-position "^3.0.0"
9861 vfile-message "^3.0.0" 9677 vfile-message "^3.0.0"
9862 9678
9863 -vue-loader@^17.3.1:  
9864 - version "17.3.1"  
9865 - resolved "https://registry.npmmirror.com/vue-loader/-/vue-loader-17.3.1.tgz"  
9866 - integrity sha512-nmVu7KU8geOyzsStyyaxID/uBGDMS8BkPXb6Lu2SNkMawriIbb+hYrNtgftHMKxOSkjjjTF5OSSwPo3KP59egg==  
9867 - dependencies:  
9868 - chalk "^4.1.0"  
9869 - hash-sum "^2.0.0"  
9870 - watchpack "^2.4.0"  
9871 -  
9872 w3c-xmlserializer@^4.0.0: 9679 w3c-xmlserializer@^4.0.0:
9873 version "4.0.0" 9680 version "4.0.0"
9874 resolved "https://registry.npmmirror.com/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz" 9681 resolved "https://registry.npmmirror.com/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz"
@@ -9891,7 +9698,7 @@ watch@^1.0.2: @@ -9891,7 +9698,7 @@ watch@^1.0.2:
9891 exec-sh "^0.2.0" 9698 exec-sh "^0.2.0"
9892 minimist "^1.2.0" 9699 minimist "^1.2.0"
9893 9700
9894 -watchpack@^2.0.0, watchpack@^2.4.0: 9701 +watchpack@^2.0.0:
9895 version "2.4.2" 9702 version "2.4.2"
9896 resolved "https://registry.npmmirror.com/watchpack/-/watchpack-2.4.2.tgz" 9703 resolved "https://registry.npmmirror.com/watchpack/-/watchpack-2.4.2.tgz"
9897 integrity sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw== 9704 integrity sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==