@fe-free/core 4.1.39 → 4.1.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/package.json +3 -3
- package/src/crud/crud.tsx +5 -2
- package/src/crud/helper.tsx +3 -3
- package/src/crud/table/index.tsx +7 -2
- package/src/crud/use_operate.tsx +1 -1
- package/src/crud_of_pure/crud_of_pure.stories.tsx +7 -1
- package/src/crud_of_pure/index.tsx +52 -2
- package/src/crud_of_pure/style.scss +4 -0
- package/src/index.ts +0 -1
- package/src/infinite_list/index.tsx +5 -2
- package/src/scroll/index.tsx +18 -10
- package/src/markdown/chart.tsx +0 -318
- package/src/markdown/code.tsx +0 -42
- package/src/markdown/deep_seek.tsx +0 -53
- package/src/markdown/hm_chart.tsx +0 -176
- package/src/markdown/index.tsx +0 -53
- package/src/markdown/knowledge_ref.tsx +0 -54
- package/src/markdown/markdown.stories.tsx +0 -526
- package/src/markdown/style.scss +0 -46
package/src/markdown/code.tsx
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
|
|
2
|
-
import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism';
|
|
3
|
-
import { ChartBlock } from './chart';
|
|
4
|
-
import { HMChartBlock } from './hm_chart';
|
|
5
|
-
|
|
6
|
-
function CodeBlock(props: any) {
|
|
7
|
-
const { children, className, ...rest } = props;
|
|
8
|
-
const match = /language-(\w+)/.exec(props.className || '');
|
|
9
|
-
|
|
10
|
-
// 如果是 chart 类型的代码块,使用 ChartBlock 组件
|
|
11
|
-
if (match && match[1] === 'chart') {
|
|
12
|
-
return <ChartBlock>{children}</ChartBlock>;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
// 如果是 hmchart 类型的代码块,使用 HMChartBlock 组件
|
|
16
|
-
if (match && match[1] === 'hmchart') {
|
|
17
|
-
return <HMChartBlock>{children}</HMChartBlock>;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
return (
|
|
21
|
-
<div className="markdown-body-block-code">
|
|
22
|
-
{match ? (
|
|
23
|
-
<SyntaxHighlighter
|
|
24
|
-
{...rest}
|
|
25
|
-
style={vscDarkPlus}
|
|
26
|
-
language={match?.[1]}
|
|
27
|
-
showLineNumbers
|
|
28
|
-
PreTag="div"
|
|
29
|
-
wrapLongLines
|
|
30
|
-
>
|
|
31
|
-
{children}
|
|
32
|
-
</SyntaxHighlighter>
|
|
33
|
-
) : (
|
|
34
|
-
<code {...rest} className={className}>
|
|
35
|
-
{children}
|
|
36
|
-
</code>
|
|
37
|
-
)}
|
|
38
|
-
</div>
|
|
39
|
-
);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export { CodeBlock };
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { DownOutlined, UpOutlined } from '@fe-free/icons';
|
|
2
|
-
import { useState } from 'react';
|
|
3
|
-
|
|
4
|
-
function DeepSeekBlock(props: { children: string }) {
|
|
5
|
-
const [show, setShow] = useState(true);
|
|
6
|
-
|
|
7
|
-
return (
|
|
8
|
-
<div className="markdown-body-block-deep-seek mb-3 flex flex-col gap-2 text-[12px] text-03">
|
|
9
|
-
<div
|
|
10
|
-
className="cursor-pointer"
|
|
11
|
-
onClick={() => {
|
|
12
|
-
setShow((v) => !v);
|
|
13
|
-
}}
|
|
14
|
-
>
|
|
15
|
-
深度思考 {show ? <UpOutlined /> : <DownOutlined />}
|
|
16
|
-
</div>
|
|
17
|
-
{show && (
|
|
18
|
-
<div className="relative pl-[15px]">
|
|
19
|
-
<div className="top=0 absolute left-0 h-full w-[2px] bg-[#00000014]" />
|
|
20
|
-
{props.children === '<br/>' ? undefined : props.children}
|
|
21
|
-
</div>
|
|
22
|
-
)}
|
|
23
|
-
</div>
|
|
24
|
-
);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function processWithDeepSeek(text: string) {
|
|
28
|
-
// 开始 <think> 才算开始
|
|
29
|
-
if (!text.startsWith('<think>')) {
|
|
30
|
-
return text;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const [left, right] = text.split('</think>');
|
|
34
|
-
|
|
35
|
-
let newText = text;
|
|
36
|
-
|
|
37
|
-
// 如果 think 部分是 <think>\n\n</think>,相当于没有,则直接返回 right
|
|
38
|
-
if (text.startsWith('<think>\n\n</think>')) {
|
|
39
|
-
newText = right;
|
|
40
|
-
}
|
|
41
|
-
// 否则做一些处理
|
|
42
|
-
else {
|
|
43
|
-
newText =
|
|
44
|
-
left
|
|
45
|
-
.replace('<think>\n', '<think>')
|
|
46
|
-
.replace('\n</think>', '</think>')
|
|
47
|
-
.replace(/\n/g, '<br/>') + (right || '');
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return newText;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export { DeepSeekBlock, processWithDeepSeek };
|
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
import { Column, Line, Pie, Scatter } from '@ant-design/plots';
|
|
2
|
-
import React, { useMemo } from 'react';
|
|
3
|
-
|
|
4
|
-
enum EnumType {
|
|
5
|
-
/** 折线图 */
|
|
6
|
-
LINE = 'line',
|
|
7
|
-
/** 柱状图 */
|
|
8
|
-
BAR = 'bar',
|
|
9
|
-
/** 饼图 */
|
|
10
|
-
PIE = 'pie',
|
|
11
|
-
/** 散点图 */
|
|
12
|
-
SCATTER = 'scatter',
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
interface HMChartConfigLine<D> {
|
|
16
|
-
/** 折线图 */
|
|
17
|
-
type: EnumType.LINE;
|
|
18
|
-
/** 标题 */
|
|
19
|
-
title?: string;
|
|
20
|
-
/** 就是纯粹的数据 */
|
|
21
|
-
data: D[];
|
|
22
|
-
/** 横坐标字段 */
|
|
23
|
-
xField: keyof D;
|
|
24
|
-
/** 纵坐标字段 */
|
|
25
|
-
yField: keyof D;
|
|
26
|
-
/** 系列字段,多折线图用 */
|
|
27
|
-
seriesField?: keyof D;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
interface HMChartConfigBar<D> {
|
|
31
|
-
/** 柱状图 */
|
|
32
|
-
type: EnumType.BAR;
|
|
33
|
-
/** 标题 */
|
|
34
|
-
title?: string;
|
|
35
|
-
/** 就是纯粹的数据 */
|
|
36
|
-
data: D[];
|
|
37
|
-
/** 横坐标字段 */
|
|
38
|
-
xField: keyof D;
|
|
39
|
-
/** 纵坐标字段 */
|
|
40
|
-
yField: keyof D;
|
|
41
|
-
/** 系列字段,多折线图用 */
|
|
42
|
-
seriesField?: keyof D;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
interface HMChartConfigPie<D> {
|
|
46
|
-
/** 饼图 */
|
|
47
|
-
type: EnumType.PIE;
|
|
48
|
-
/** 标题 */
|
|
49
|
-
title?: string;
|
|
50
|
-
/** 就是纯粹的数据 */
|
|
51
|
-
data: D[];
|
|
52
|
-
/** 角度字段 */
|
|
53
|
-
angleField: keyof D;
|
|
54
|
-
/** 颜色字段 */
|
|
55
|
-
colorField: keyof D;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
interface HMChartConfigScatter<D> {
|
|
59
|
-
/** 散点图 */
|
|
60
|
-
type: EnumType.SCATTER;
|
|
61
|
-
/** 标题 */
|
|
62
|
-
title?: string;
|
|
63
|
-
/** 就是纯粹的数据 */
|
|
64
|
-
data: D[];
|
|
65
|
-
/** 横坐标字段 */
|
|
66
|
-
xField: keyof D;
|
|
67
|
-
/** 纵坐标字段 */
|
|
68
|
-
yField: keyof D;
|
|
69
|
-
/** 颜色字段 */
|
|
70
|
-
colorField?: keyof D;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
type HMChartConfig<D> =
|
|
74
|
-
| HMChartConfigLine<D>
|
|
75
|
-
| HMChartConfigBar<D>
|
|
76
|
-
| HMChartConfigPie<D>
|
|
77
|
-
| HMChartConfigScatter<D>;
|
|
78
|
-
|
|
79
|
-
// 错误处理组件
|
|
80
|
-
function ChartError(props: { children?: React.ReactNode }) {
|
|
81
|
-
const { children } = props;
|
|
82
|
-
return (
|
|
83
|
-
<div className="markdown-body-block-hmchart">
|
|
84
|
-
<div style={{ textAlign: 'center', padding: '20px' }}>{children || '图表发生错误'}</div>
|
|
85
|
-
</div>
|
|
86
|
-
);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
class ErrorBoundary extends React.Component {
|
|
90
|
-
state = { hasError: false };
|
|
91
|
-
|
|
92
|
-
static getDerivedStateFromError(error) {
|
|
93
|
-
console.error('ErrorBoundary:', error);
|
|
94
|
-
return { hasError: true };
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
componentDidCatch(error, info) {
|
|
98
|
-
console.error('Error caught:', error, info);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
render() {
|
|
102
|
-
if (this.state.hasError) {
|
|
103
|
-
return <ChartError />;
|
|
104
|
-
}
|
|
105
|
-
return this.props.children;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function ChartOfLine(props: { config: HMChartConfigLine<any> }) {
|
|
110
|
-
return <Line colorField={props.config.seriesField} {...props.config} />;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function ChartOfBar(props: { config: HMChartConfigBar<any> }) {
|
|
114
|
-
return <Column colorField={props.config.seriesField} {...props.config} />;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
function ChartOfPie(props: { config: HMChartConfigPie<any> }) {
|
|
118
|
-
return <Pie {...props.config} />;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function ChartOfScatter(props: { config: HMChartConfigScatter<any> }) {
|
|
122
|
-
return <Scatter {...props.config} />;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// 主 ChartBlock 组件
|
|
126
|
-
function HMChartBlockBase(props: any) {
|
|
127
|
-
const { children } = props;
|
|
128
|
-
|
|
129
|
-
const chartConfig = useMemo(() => {
|
|
130
|
-
try {
|
|
131
|
-
return JSON.parse(children);
|
|
132
|
-
} catch (error) {
|
|
133
|
-
console.error('Failed to parse chart data:', error);
|
|
134
|
-
return null;
|
|
135
|
-
}
|
|
136
|
-
}, [children]);
|
|
137
|
-
|
|
138
|
-
if (!chartConfig) {
|
|
139
|
-
return <ChartError />;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
const { type, ...config } = chartConfig;
|
|
143
|
-
|
|
144
|
-
switch (type) {
|
|
145
|
-
case EnumType.LINE:
|
|
146
|
-
return <ChartOfLine config={config} />;
|
|
147
|
-
|
|
148
|
-
case EnumType.BAR:
|
|
149
|
-
return <ChartOfBar config={config} />;
|
|
150
|
-
|
|
151
|
-
case EnumType.PIE:
|
|
152
|
-
return <ChartOfPie config={config} />;
|
|
153
|
-
|
|
154
|
-
case EnumType.SCATTER:
|
|
155
|
-
return <ChartOfScatter config={config} />;
|
|
156
|
-
|
|
157
|
-
default:
|
|
158
|
-
return <ChartError>不支持的图表类型:{type}</ChartError>;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
function HMChartBlock(props: any) {
|
|
163
|
-
const { children } = props;
|
|
164
|
-
|
|
165
|
-
// eslint-disable-next-line no-irregular-whitespace
|
|
166
|
-
const content = children?.replace(/ /g, '');
|
|
167
|
-
|
|
168
|
-
return (
|
|
169
|
-
<ErrorBoundary>
|
|
170
|
-
<HMChartBlockBase>{content}</HMChartBlockBase>
|
|
171
|
-
</ErrorBoundary>
|
|
172
|
-
);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
export { HMChartBlock };
|
|
176
|
-
export type { HMChartConfig };
|
package/src/markdown/index.tsx
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import 'github-markdown-css/github-markdown-light.css';
|
|
2
|
-
import { useMemo } from 'react';
|
|
3
|
-
import ReactMarkdown from 'react-markdown';
|
|
4
|
-
import rehypeRaw from 'rehype-raw';
|
|
5
|
-
import remarkGfm from 'remark-gfm';
|
|
6
|
-
import { CodeBlock } from './code';
|
|
7
|
-
import { DeepSeekBlock, processWithDeepSeek } from './deep_seek';
|
|
8
|
-
import { KnowledgeRefBlock, processWithKnowledgeRef } from './knowledge_ref';
|
|
9
|
-
import './style.scss';
|
|
10
|
-
|
|
11
|
-
interface MarkdownProps {
|
|
12
|
-
children: string;
|
|
13
|
-
knowledgeRefs?: { id: string }[];
|
|
14
|
-
onKnowledgeRef?: (id?: string) => void;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function Markdown(props: MarkdownProps) {
|
|
18
|
-
const { children, knowledgeRefs, onKnowledgeRef } = props;
|
|
19
|
-
|
|
20
|
-
const newChildren = useMemo(() => {
|
|
21
|
-
let processed = processWithDeepSeek(children);
|
|
22
|
-
processed = processWithKnowledgeRef(processed, knowledgeRefs);
|
|
23
|
-
return processed;
|
|
24
|
-
}, [children, knowledgeRefs]);
|
|
25
|
-
|
|
26
|
-
const KnowledgeRefComponent = useMemo(
|
|
27
|
-
() => (p: any) => {
|
|
28
|
-
return (
|
|
29
|
-
<KnowledgeRefBlock {...p} knowledgeRefs={knowledgeRefs} onKnowledgeRef={onKnowledgeRef} />
|
|
30
|
-
);
|
|
31
|
-
},
|
|
32
|
-
[knowledgeRefs, onKnowledgeRef],
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
return (
|
|
36
|
-
<div className="markdown-body">
|
|
37
|
-
<ReactMarkdown
|
|
38
|
-
remarkPlugins={[remarkGfm]}
|
|
39
|
-
rehypePlugins={[rehypeRaw]}
|
|
40
|
-
components={{
|
|
41
|
-
code: CodeBlock,
|
|
42
|
-
// @ts-ignore
|
|
43
|
-
think: DeepSeekBlock,
|
|
44
|
-
'knowledge-ref': KnowledgeRefComponent,
|
|
45
|
-
}}
|
|
46
|
-
>
|
|
47
|
-
{newChildren}
|
|
48
|
-
</ReactMarkdown>
|
|
49
|
-
</div>
|
|
50
|
-
);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export { Markdown };
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { useCallback } from 'react';
|
|
2
|
-
|
|
3
|
-
function KnowledgeRefBlock(props: any) {
|
|
4
|
-
const { knowledgeRefs, onKnowledgeRef } = props;
|
|
5
|
-
const id = props['data-id'];
|
|
6
|
-
|
|
7
|
-
const handleClick = useCallback(() => {
|
|
8
|
-
onKnowledgeRef?.(id);
|
|
9
|
-
}, [id, onKnowledgeRef]);
|
|
10
|
-
|
|
11
|
-
if (id) {
|
|
12
|
-
const index = knowledgeRefs?.findIndex((ref: { id: string }) => ref.id === id);
|
|
13
|
-
|
|
14
|
-
// 没有数据的时候显示 id
|
|
15
|
-
if (index === -1 || index === undefined) {
|
|
16
|
-
return <span>[^knowledge:{id}]</span>;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
return (
|
|
20
|
-
<span
|
|
21
|
-
data-id={id}
|
|
22
|
-
onClick={handleClick}
|
|
23
|
-
className="markdown-body-block-knowledge-ref mx-1 h-[16px] min-w-[16px] cursor-pointer rounded-full border border-blue-500 text-center text-[12px] text-blue-600"
|
|
24
|
-
>
|
|
25
|
-
{index + 1}
|
|
26
|
-
</span>
|
|
27
|
-
);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return (
|
|
31
|
-
<span className="markdown-body-block-knowledge-ref-source" onClick={handleClick}>
|
|
32
|
-
来源>>
|
|
33
|
-
</span>
|
|
34
|
-
);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function processWithKnowledgeRef(text: string, knowledgeRefs?: { id: string }[]) {
|
|
38
|
-
// 匹配 [^knowledge:X-Y] 格式
|
|
39
|
-
const knowledgeRefRegex = /\[\^knowledge:(\d+-\d+)\]/g;
|
|
40
|
-
|
|
41
|
-
let count = 0;
|
|
42
|
-
let newText = text.replace(knowledgeRefRegex, (_match, id) => {
|
|
43
|
-
count++;
|
|
44
|
-
return `<knowledge-ref data-id="${id}">${id}</knowledge-ref>`;
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
if (count > 0 && knowledgeRefs && knowledgeRefs.length > 0) {
|
|
48
|
-
newText = `${newText}\n\n<knowledge-ref>来源>></knowledge>`;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
return newText;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export { KnowledgeRefBlock, processWithKnowledgeRef };
|