Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
S
saas-bi
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
EzijingWeb
saas-bi
Commits
95c96252
提交
95c96252
authored
3月 20, 2025
作者:
王鹏飞
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
chore: update
上级
7dae00a4
隐藏空白字符变更
内嵌
并排
正在显示
11 个修改的文件
包含
370 行增加
和
185 行删除
+370
-185
useAI.ts
src/hooks/useAI.ts
+0
-1
ButtonModal.tsx
src/modules/data/preprocess/null/components/ButtonModal.tsx
+14
-6
ErrorButtonModal.tsx
...les/data/read/exploration/components/ErrorButtonModal.tsx
+42
-7
MaxButtonModal.tsx
...dules/data/read/exploration/components/MaxButtonModal.tsx
+42
-7
MinButtonModal.tsx
...dules/data/read/exploration/components/MinButtonModal.tsx
+43
-15
NullButtonModal.tsx
...ules/data/read/exploration/components/NullButtonModal.tsx
+29
-37
OtherButtonModal.tsx
...les/data/read/exploration/components/OtherButtonModal.tsx
+84
-91
RepeatButtonModal.tsx
...es/data/read/exploration/components/RepeatButtonModal.tsx
+46
-16
prompt.ts
src/modules/data/read/exploration/prompt.ts
+48
-0
Index.tsx
src/modules/data/write/upload/views/Index.tsx
+2
-2
ai.ts
src/utils/ai.ts
+20
-3
没有找到文件。
src/hooks/useAI.ts
浏览文件 @
95c96252
...
...
@@ -5,7 +5,6 @@ export function useAI() {
const
[
ai
,
setAI
]
=
useState
<
string
>
(
localStorage
.
getItem
(
'ai'
)
||
'qwen'
)
const
[
messages
,
setMessages
]
=
useState
<
AIMessage
[]
>
([])
const
[
isLoading
,
setIsLoading
]
=
useState
<
boolean
>
(
false
)
console
.
log
(
messages
)
useEffect
(()
=>
{
localStorage
.
setItem
(
'ai'
,
ai
)
},
[
ai
])
...
...
src/modules/data/preprocess/null/components/ButtonModal.tsx
浏览文件 @
95c96252
...
...
@@ -2,16 +2,24 @@ import { useState } from 'react'
import
{
Button
,
Checkbox
,
Flex
,
Modal
,
Radio
,
Select
,
Input
,
Form
,
Row
,
Col
,
Divider
}
from
'antd'
import
{
useDataFieldQuery
}
from
'@/hooks/useQuery'
import
AppProgressSteps
from
'@/components/AppProgressSteps'
import
{
useSearchParams
}
from
'react-router'
export
default
function
ButtonModal
()
{
const
[
searchParams
]
=
useSearchParams
()
const
results
=
searchParams
.
get
(
'results'
)?.
split
(
','
)
||
[]
const
{
data
}
=
useDataFieldQuery
()
const
[
open
,
setOpen
]
=
useState
(
false
)
const
[
open
,
setOpen
]
=
useState
(
results
.
length
>
0
)
const
[
current
,
setCurrent
]
=
useState
(
0
)
const
[
form
]
=
Form
.
useForm
()
const
initialValues
=
{
checkedList
:
results
,
method
:
'unified'
,
}
const
checkedList
=
Form
.
useWatch
(
'checkedList'
,
form
)
||
[]
const
processingMethod
=
Form
.
useWatch
(
'processingM
ethod'
,
form
)
const
method
=
Form
.
useWatch
(
'm
ethod'
,
form
)
const
rule
=
Form
.
useWatch
(
'rule'
,
form
)
const
rules
=
Form
.
useWatch
(
'rules'
,
form
)
||
{}
...
...
@@ -53,12 +61,12 @@ export default function ButtonModal() {
title
:
'配置处理规则'
,
content
:
(
<>
<
Form
.
Item
label=
"缺失值字段处理方法"
name=
"
processingM
ethod"
>
<
Form
.
Item
label=
"缺失值字段处理方法"
name=
"
m
ethod"
>
<
Radio
.
Group
options=
{
methodOptions
}
/>
</
Form
.
Item
>
{
/* 统一规则处理 */
}
{
processingM
ethod
===
'unified'
&&
(
{
m
ethod
===
'unified'
&&
(
<>
<
Form
.
Item
label=
"缺失值字段处理规则"
name=
"rule"
>
<
Select
options=
{
ruleOptions
}
/>
...
...
@@ -73,7 +81,7 @@ export default function ButtonModal() {
)
}
{
/* 逐个配置规则处理 */
}
{
processingM
ethod
===
'individual'
&&
{
m
ethod
===
'individual'
&&
checkedList
.
map
((
field
)
=>
(
<
div
key=
{
field
}
style=
{
{
marginBottom
:
10
}
}
>
<
Form
.
Item
label=
"字段"
>
{
field
}
</
Form
.
Item
>
...
...
@@ -174,7 +182,7 @@ export default function ButtonModal() {
width=
{
800
}
onCancel=
{
()
=>
setOpen
(
false
)
}
>
<
div
style=
{
{
minHeight
:
300
,
padding
:
'20px 0'
}
}
>
<
Form
form=
{
form
}
labelCol=
{
{
span
:
5
}
}
preserve=
{
false
}
>
<
Form
form=
{
form
}
labelCol=
{
{
span
:
5
}
}
preserve=
{
false
}
initialValues=
{
initialValues
}
>
{
steps
.
map
((
item
,
index
)
=>
(
<
div
key=
{
index
}
hidden=
{
current
!==
index
}
style=
{
{
marginTop
:
'20px'
}
}
>
{
item
.
content
}
...
...
src/modules/data/read/exploration/components/ErrorButtonModal.tsx
浏览文件 @
95c96252
import
{
useState
}
from
'react'
import
{
Button
,
Radio
,
Flex
,
Modal
}
from
'antd'
import
{
use
Effect
,
use
State
}
from
'react'
import
{
Button
,
Radio
,
Flex
,
Modal
,
Empty
}
from
'antd'
import
type
{
RadioChangeEvent
}
from
'antd'
import
{
useNavigate
}
from
'react-router'
import
AppSteps
from
'@/components/AppSteps'
import
{
useAI
}
from
'@/hooks/useAI'
import
prompt
from
'../prompt'
const
plainOptions
=
[
'会员情况'
,
'客户满意度'
,
'出生日期'
,
'访问页面时长'
,
'交易状态'
,
'商品状态'
,
'快递反馈'
]
interface
ResultItem
{
name
:
string
desc
:
string
}
export
default
function
ButtonModal
()
{
const
[
results
,
setResults
]
=
useState
<
ResultItem
[]
>
([])
const
resultsOptions
=
results
.
map
((
result
)
=>
result
.
name
)
const
{
isLoading
,
messages
,
post
}
=
useAI
()
const
lastMessage
=
messages
[
messages
.
length
-
1
]
useEffect
(()
=>
{
if
(
lastMessage
?.
role
===
'assistant'
&&
lastMessage
?.
content
)
{
try
{
const
parse
=
JSON
.
parse
(
lastMessage
.
content
)
console
.
log
(
parse
)
setResults
(
parse
.
results
||
[])
}
catch
(
error
)
{
console
.
error
(
error
)
}
}
},
[
lastMessage
])
const
handleSearch
=
()
=>
{
post
({
response_format
:
{
type
:
'json_object'
},
messages
:
[{
role
:
'user'
,
content
:
prompt
.
min
}],
})
}
const
navigate
=
useNavigate
()
const
[
open
,
setOpen
]
=
useState
(
false
)
const
[
current
,
setCurrent
]
=
useState
(
0
)
...
...
@@ -41,16 +69,22 @@ export default function ButtonModal() {
open=
{
open
}
footer=
{
<
Flex
justify=
"center"
gap=
{
20
}
>
{
current
===
0
&&
<
Button
type=
"primary"
>
智能探索字段数据逻辑错误
</
Button
>
}
{
current
===
0
&&
(
<
Button
type=
"primary"
loading=
{
isLoading
}
onClick=
{
handleSearch
}
>
智能探索字段数据逻辑错误
</
Button
>
)
}
{
current
>
0
&&
<
Button
onClick=
{
()
=>
prev
()
}
>
上一步
</
Button
>
}
{
current
<
steps
.
length
-
1
&&
(
<
Button
type=
"primary"
onClick=
{
()
=>
next
()
}
>
<
Button
type=
"primary"
onClick=
{
()
=>
next
()
}
disabled=
{
!!
value
}
>
下一步
</
Button
>
)
}
{
current
===
steps
.
length
-
1
&&
(
<
Button
type=
"primary"
onClick=
{
()
=>
navigate
(
'/data/preprocess/error'
)
}
>
<
Button
type=
"primary"
onClick=
{
()
=>
navigate
({
pathname
:
'/data/preprocess/error'
,
search
:
`?results=${value}`
})
}
>
跳转数据预处理
</
Button
>
)
}
...
...
@@ -64,7 +98,8 @@ export default function ButtonModal() {
<
Flex
justify=
"space-between"
style=
{
{
marginBottom
:
20
}
}
>
探索结果:
</
Flex
>
<
Radio
.
Group
options=
{
plainOptions
}
value=
{
value
}
onChange=
{
onChange
}
style=
{
style
}
/>
<
Radio
.
Group
options=
{
resultsOptions
}
value=
{
value
}
onChange=
{
onChange
}
style=
{
style
}
/>
{
!
results
.
length
&&
<
Empty
></
Empty
>
}
</
div
>
</
Modal
>
</>
...
...
src/modules/data/read/exploration/components/MaxButtonModal.tsx
浏览文件 @
95c96252
import
{
useState
}
from
'react'
import
{
Button
,
Radio
,
Flex
,
Modal
}
from
'antd'
import
{
use
Effect
,
use
State
}
from
'react'
import
{
Button
,
Radio
,
Flex
,
Modal
,
Empty
}
from
'antd'
import
type
{
RadioChangeEvent
}
from
'antd'
import
{
useNavigate
}
from
'react-router'
import
AppSteps
from
'@/components/AppSteps'
import
{
useAI
}
from
'@/hooks/useAI'
import
prompt
from
'../prompt'
const
plainOptions
=
[
'会员情况'
,
'客户满意度'
,
'出生日期'
,
'访问页面时长'
,
'交易状态'
,
'商品状态'
,
'快递反馈'
]
interface
ResultItem
{
name
:
string
desc
:
string
}
export
default
function
ButtonModal
()
{
const
[
results
,
setResults
]
=
useState
<
ResultItem
[]
>
([])
const
resultsOptions
=
results
.
map
((
result
)
=>
result
.
name
)
const
{
isLoading
,
messages
,
post
}
=
useAI
()
const
lastMessage
=
messages
[
messages
.
length
-
1
]
useEffect
(()
=>
{
if
(
lastMessage
?.
role
===
'assistant'
&&
lastMessage
?.
content
)
{
try
{
const
parse
=
JSON
.
parse
(
lastMessage
.
content
)
console
.
log
(
parse
)
setResults
(
parse
.
results
||
[])
}
catch
(
error
)
{
console
.
error
(
error
)
}
}
},
[
lastMessage
])
const
handleSearch
=
()
=>
{
post
({
response_format
:
{
type
:
'json_object'
},
messages
:
[{
role
:
'user'
,
content
:
prompt
.
max
}],
})
}
const
navigate
=
useNavigate
()
const
[
open
,
setOpen
]
=
useState
(
false
)
const
[
current
,
setCurrent
]
=
useState
(
0
)
...
...
@@ -48,16 +76,22 @@ export default function ButtonModal() {
open=
{
open
}
footer=
{
<
Flex
justify=
"center"
gap=
{
20
}
>
{
current
===
0
&&
<
Button
type=
"primary"
>
智能探索过大值字段
</
Button
>
}
{
current
===
0
&&
(
<
Button
type=
"primary"
loading=
{
isLoading
}
onClick=
{
handleSearch
}
>
智能探索过大值字段
</
Button
>
)
}
{
current
>
0
&&
<
Button
onClick=
{
()
=>
prev
()
}
>
上一步
</
Button
>
}
{
current
<
steps
.
length
-
1
&&
(
<
Button
type=
"primary"
onClick=
{
()
=>
next
()
}
>
<
Button
type=
"primary"
onClick=
{
()
=>
next
()
}
disabled=
{
!!
value
}
>
下一步
</
Button
>
)
}
{
current
===
steps
.
length
-
1
&&
(
<
Button
type=
"primary"
onClick=
{
()
=>
navigate
(
'/data/preprocess/max'
)
}
>
<
Button
type=
"primary"
onClick=
{
()
=>
navigate
({
pathname
:
'/data/preprocess/max'
,
search
:
`?results=${value}`
})
}
>
跳转数据预处理
</
Button
>
)
}
...
...
@@ -71,7 +105,8 @@ export default function ButtonModal() {
<
Flex
justify=
"space-between"
style=
{
{
marginBottom
:
20
}
}
>
探索结果:
</
Flex
>
<
Radio
.
Group
options=
{
plainOptions
}
value=
{
value
}
onChange=
{
onChange
}
style=
{
style
}
/>
<
Radio
.
Group
options=
{
resultsOptions
}
value=
{
value
}
onChange=
{
onChange
}
style=
{
style
}
/>
{
!
results
.
length
&&
<
Empty
></
Empty
>
}
</
div
>
</
Modal
>
</>
...
...
src/modules/data/read/exploration/components/MinButtonModal.tsx
浏览文件 @
95c96252
import
{
useState
}
from
'react'
import
{
Button
,
Radio
,
Flex
,
Modal
}
from
'antd'
import
{
use
Effect
,
use
State
}
from
'react'
import
{
Button
,
Radio
,
Flex
,
Modal
,
Empty
}
from
'antd'
import
type
{
RadioChangeEvent
}
from
'antd'
import
{
useNavigate
}
from
'react-router'
import
AppSteps
from
'@/components/AppSteps'
import
{
useAI
}
from
'@/hooks/useAI'
import
prompt
from
'../prompt'
const
plainOptions
=
[
'会员情况'
,
'客户满意度'
,
'出生日期'
,
'访问页面时长'
,
'交易状态'
,
'商品状态'
,
'快递反馈'
]
interface
ResultItem
{
name
:
string
desc
:
string
}
export
default
function
ButtonModal
()
{
const
[
results
,
setResults
]
=
useState
<
ResultItem
[]
>
([])
const
resultsOptions
=
results
.
map
((
result
)
=>
result
.
name
)
const
{
isLoading
,
messages
,
post
}
=
useAI
()
const
lastMessage
=
messages
[
messages
.
length
-
1
]
useEffect
(()
=>
{
if
(
lastMessage
?.
role
===
'assistant'
&&
lastMessage
?.
content
)
{
try
{
const
parse
=
JSON
.
parse
(
lastMessage
.
content
)
console
.
log
(
parse
)
setResults
(
parse
.
results
||
[])
}
catch
(
error
)
{
console
.
error
(
error
)
}
}
},
[
lastMessage
])
const
handleSearch
=
()
=>
{
post
({
response_format
:
{
type
:
'json_object'
},
messages
:
[{
role
:
'user'
,
content
:
prompt
.
min
}],
})
}
const
navigate
=
useNavigate
()
const
[
open
,
setOpen
]
=
useState
(
false
)
const
[
current
,
setCurrent
]
=
useState
(
0
)
...
...
@@ -24,14 +52,7 @@ export default function ButtonModal() {
setCurrent
(
current
-
1
)
}
const
steps
=
[
{
title
:
'探索过小值字段'
,
},
{
title
:
'处理过小值'
,
},
]
const
steps
=
[{
title
:
'探索过小值字段'
},
{
title
:
'处理过小值'
}]
const
style
:
React
.
CSSProperties
=
{
display
:
'flex'
,
...
...
@@ -48,16 +69,22 @@ export default function ButtonModal() {
open=
{
open
}
footer=
{
<
Flex
justify=
"center"
gap=
{
20
}
>
{
current
===
0
&&
<
Button
type=
"primary"
>
智能探索过小值字段
</
Button
>
}
{
current
===
0
&&
(
<
Button
type=
"primary"
loading=
{
isLoading
}
onClick=
{
handleSearch
}
>
智能探索过小值字段
</
Button
>
)
}
{
current
>
0
&&
<
Button
onClick=
{
()
=>
prev
()
}
>
上一步
</
Button
>
}
{
current
<
steps
.
length
-
1
&&
(
<
Button
type=
"primary"
onClick=
{
()
=>
next
()
}
>
<
Button
type=
"primary"
onClick=
{
()
=>
next
()
}
disabled=
{
!!
value
}
>
下一步
</
Button
>
)
}
{
current
===
steps
.
length
-
1
&&
(
<
Button
type=
"primary"
onClick=
{
()
=>
navigate
(
'/data/preprocess/min'
)
}
>
<
Button
type=
"primary"
onClick=
{
()
=>
navigate
({
pathname
:
'/data/preprocess/min'
,
search
:
`?results=${value}`
})
}
>
跳转数据预处理
</
Button
>
)
}
...
...
@@ -71,7 +98,8 @@ export default function ButtonModal() {
<
Flex
justify=
"space-between"
style=
{
{
marginBottom
:
20
}
}
>
探索结果:
</
Flex
>
<
Radio
.
Group
options=
{
plainOptions
}
value=
{
value
}
onChange=
{
onChange
}
style=
{
style
}
/>
<
Radio
.
Group
options=
{
resultsOptions
}
value=
{
value
}
onChange=
{
onChange
}
style=
{
style
}
/>
{
!
results
.
length
&&
<
Empty
></
Empty
>
}
</
div
>
</
Modal
>
</>
...
...
src/modules/data/read/exploration/components/NullButtonModal.tsx
浏览文件 @
95c96252
import
{
useEffect
,
useState
}
from
'react'
import
{
Button
,
Checkbox
,
Flex
,
Modal
}
from
'antd'
import
{
Button
,
Checkbox
,
Empty
,
Flex
,
Modal
}
from
'antd'
import
type
{
CheckboxProps
}
from
'antd'
import
{
useNavigate
}
from
'react-router'
import
AppSteps
from
'@/components/AppSteps'
import
{
useAI
}
from
'@/hooks/useAI'
import
prompt
from
'../prompt'
const
CheckboxGroup
=
Checkbox
.
Group
interface
ResultItem
{
name
:
string
desc
:
string
}
export
default
function
ButtonModal
()
{
const
[
results
,
setResults
]
=
useState
<
ResultItem
[]
>
([])
const
resultsOptions
=
results
.
map
((
result
)
=>
result
.
name
)
const
{
isLoading
,
messages
,
post
}
=
useAI
()
const
lastMessage
=
messages
[
messages
.
length
-
1
]
const
[
fields
,
setFields
]
=
useState
<
any
>
([])
const
fieldsOptions
=
fields
.
map
((
field
:
any
)
=>
({
label
:
field
.
name
,
value
:
field
.
english_name
,
}))
useEffect
(()
=>
{
if
(
lastMessage
?.
role
===
'assistant'
&&
lastMessage
?.
content
)
{
try
{
const
parse
=
JSON
.
parse
(
lastMessage
.
content
)
setFields
(
parse
.
fields
||
[])
console
.
log
(
parse
)
setResults
(
parse
.
results
||
[])
}
catch
(
error
)
{
console
.
error
(
error
)
}
...
...
@@ -27,27 +32,8 @@ export default function ButtonModal() {
},
[
lastMessage
])
const
handleSearch
=
()
=>
{
post
({
response_format
:
{
type
:
'json_object'
,
},
messages
:
[
{
role
:
'user'
,
content
:
`找出数据集中有缺失值的字段,并返回 JSON 格式的结果。返回内容仅包含缺失字段的名称(name)及其对应的英文名(english_name)该字段有缺失值的数据有哪些,字段像主键的数据(desc)。如果没有缺失值,则返回空数组 []。
返回格式示例:
json
{
"fields": [
{"name": "字段1", "english_name": "字段英文名", "desc": "A000001、A000002..."},
{"name": "字段2", "english_name": "字段英文名2", "desc": "A000001、A000002..."}
]
}
要求:
仅包含缺失值的字段(为空、null、undefined 等视为缺失)。
确保字段名称和英文名准确无误。
如果所有字段都有值,则返回 { "fields": [] }。`
,
},
],
response_format
:
{
type
:
'json_object'
},
messages
:
[{
role
:
'user'
,
content
:
prompt
.
null
}],
})
}
...
...
@@ -58,15 +44,15 @@ json
const
[
checkedList
,
setCheckedList
]
=
useState
<
string
[]
>
([])
const
checkAll
=
field
sOptions
.
length
===
checkedList
.
length
const
indeterminate
=
checkedList
.
length
>
0
&&
checkedList
.
length
<
field
sOptions
.
length
const
checkAll
=
result
sOptions
.
length
===
checkedList
.
length
const
indeterminate
=
checkedList
.
length
>
0
&&
checkedList
.
length
<
result
sOptions
.
length
const
onChange
=
(
list
:
string
[])
=>
{
setCheckedList
(
list
)
}
const
onCheckAllChange
:
CheckboxProps
[
'onChange'
]
=
(
e
)
=>
{
setCheckedList
(
e
.
target
.
checked
?
field
sOptions
:
[])
setCheckedList
(
e
.
target
.
checked
?
result
sOptions
:
[])
}
const
next
=
()
=>
{
...
...
@@ -88,7 +74,8 @@ json
全选
</
Checkbox
>
</
Flex
>
<
CheckboxGroup
options=
{
fieldsOptions
}
value=
{
checkedList
}
onChange=
{
onChange
}
/>
<
CheckboxGroup
options=
{
resultsOptions
}
value=
{
checkedList
}
onChange=
{
onChange
}
/>
{
!
results
.
length
&&
<
Empty
></
Empty
>
}
</>
),
},
...
...
@@ -99,9 +86,9 @@ json
<
Flex
justify=
"space-between"
style=
{
{
marginBottom
:
20
}
}
>
探索结果:
</
Flex
>
{
fields
.
map
((
item
:
any
)
=>
(
<
p
key=
{
item
.
english_
name
}
>
{
item
.
name
}
:
{
item
.
desc
}
{
results
.
map
((
item
)
=>
(
<
p
key=
{
item
.
name
}
>
{
item
.
name
}
:
该字段有缺失值的数据有:
{
item
.
desc
}
</
p
>
))
}
</>
...
...
@@ -117,7 +104,7 @@ json
全选
</
Checkbox
>
</
Flex
>
<
CheckboxGroup
options=
{
field
sOptions
}
value=
{
checkedList
}
onChange=
{
onChange
}
/>
<
CheckboxGroup
options=
{
result
sOptions
}
value=
{
checkedList
}
onChange=
{
onChange
}
/>
</>
),
},
...
...
@@ -151,7 +138,12 @@ json
</
Button
>
)
}
{
current
===
steps
.
length
-
1
&&
(
<
Button
type=
"primary"
onClick=
{
()
=>
navigate
(
'/data/preprocess/null'
)
}
>
<
Button
type=
"primary"
disabled=
{
!
checkedList
.
length
}
onClick=
{
()
=>
navigate
({
pathname
:
'/data/preprocess/null'
,
search
:
`?results=${checkedList.join(',')}`
})
}
>
跳转数据预处理
</
Button
>
)
}
...
...
src/modules/data/read/exploration/components/OtherButtonModal.tsx
浏览文件 @
95c96252
import
{
useState
,
useMemo
}
from
'react'
import
{
useState
,
useMemo
,
useEffect
}
from
'react'
import
{
Button
,
Flex
,
Modal
,
Table
,
Checkbox
}
from
'antd'
import
{
DownloadOutlined
}
from
'@ant-design/icons'
import
{
useDataFieldQuery
}
from
'@/hooks/useQuery'
import
{
useAI
}
from
'@/hooks/useAI'
import
{
utils
,
writeFile
}
from
'xlsx'
interface
ResultItem
{
name
:
string
}
const
buttons
=
[
'最大值'
,
'最小值'
,
'平均值'
,
'中位数'
,
'众数'
,
'1/4位数'
,
'3/4位数'
,
'方差'
,
'标准差'
,
'极差'
]
export
default
function
ButtonModal
()
{
const
{
data
}
=
useDataFieldQuery
()
const
[
results
,
setResults
]
=
useState
<
ResultItem
[]
>
(
data
.
filter
((
item
:
any
)
=>
{
return
item
.
type
.
includes
(
'DECIMAL'
)
||
item
.
type
.
includes
(
'SMALLINT'
)
})
)
const
{
isLoading
,
messages
,
post
}
=
useAI
()
const
lastMessage
=
messages
[
messages
.
length
-
1
]
useEffect
(()
=>
{
if
(
lastMessage
?.
role
===
'assistant'
&&
lastMessage
?.
content
)
{
try
{
const
parse
=
JSON
.
parse
(
lastMessage
.
content
)
console
.
log
(
parse
)
setResults
(
parse
.
results
||
{})
}
catch
(
error
)
{
console
.
error
(
error
)
}
}
},
[
lastMessage
])
const
handleSearch
=
()
=>
{
const
names
=
results
.
map
((
item
:
any
)
=>
item
.
name
).
join
(
','
)
post
({
response_format
:
{
type
:
'json_object'
},
messages
:
[
{
role
:
'user'
,
content
:
`请帮我计算数据集中,字段
${
names
}
的
${
selectedButtons
.
join
(
'、'
)}
返回格式示例:
json
{
results:[
{ name: "字段1", 计算类型(中文):"计算结果" },
{ name: "字段2", 计算类型(中文):"计算结果" },
]
}`
,
},
],
})
}
const
[
open
,
setOpen
]
=
useState
(
false
)
const
[
title
,
setTitle
]
=
useState
(
''
)
const
handleOpen
=
(
button
:
{
name
:
string
;
key
:
string
}
)
=>
{
setSelectedButtons
([
button
.
key
])
setTitle
(
`探索`
+
button
.
n
ame
)
const
handleOpen
=
(
button
Name
:
string
)
=>
{
setSelectedButtons
([
button
Name
])
setTitle
(
`探索`
+
button
N
ame
)
setOpen
(
true
)
}
const
buttons
=
useMemo
(()
=>
{
return
[
{
name
:
'最大值'
,
key
:
'max'
},
{
name
:
'最小值'
,
key
:
'min'
},
{
name
:
'平均值'
,
key
:
'mean'
},
{
name
:
'中位数'
,
key
:
'median'
},
{
name
:
'众数'
,
key
:
'mode'
},
{
name
:
'1/4位数'
,
key
:
'q1'
},
{
name
:
'3/4位数'
,
key
:
'q3'
},
{
name
:
'方差'
,
key
:
'variance'
},
{
name
:
'标准差'
,
key
:
'stdDev'
},
{
name
:
'极差'
,
key
:
'range'
},
]
},
[])
const
[
selectedButtons
,
setSelectedButtons
]
=
useState
<
string
[]
>
([
'max'
])
const
[
selectedButtons
,
setSelectedButtons
]
=
useState
<
string
[]
>
([])
const
dataSource
=
[
{
key
:
'1'
,
name
:
'字段1'
,
max
:
99
,
min
:
'--'
,
mean
:
50
,
median
:
50
,
mode
:
50
,
q1
:
25
,
q3
:
75
,
variance
:
100
,
stdDev
:
10
,
range
:
99
,
},
{
key
:
'2'
,
name
:
'字段2'
,
max
:
102586
,
min
:
'--'
,
mean
:
51293
,
median
:
51293
,
mode
:
51293
,
q1
:
25646
,
q3
:
76939
,
variance
:
1000000
,
stdDev
:
1000
,
range
:
102586
,
},
{
key
:
'3'
,
name
:
'字段3'
,
max
:
859
,
min
:
'--'
,
mean
:
430
,
median
:
430
,
mode
:
430
,
q1
:
215
,
q3
:
645
,
variance
:
10000
,
stdDev
:
100
,
range
:
859
,
},
{
key
:
'4'
,
name
:
'字段4'
,
max
:
1568
,
min
:
'--'
,
mean
:
784
,
median
:
784
,
mode
:
784
,
q1
:
392
,
q3
:
1176
,
variance
:
20000
,
stdDev
:
141
,
range
:
1568
,
},
]
const
dataSource
=
results
.
map
((
item
)
=>
{
return
{
...
item
,
key
:
item
.
name
}
})
const
columns
=
useMemo
(()
=>
{
const
columns
:
any
=
useMemo
(()
=>
{
const
baseColumns
=
[
{
title
:
'序号'
,
dataIndex
:
'key'
,
key
:
'index'
,
render
(
_value
:
any
,
_record
:
any
,
index
:
any
)
{
return
index
+
1
},
width
:
62
,
align
:
'center'
as
const
,
align
:
'center'
,
},
{
title
:
'字段名称'
,
...
...
@@ -104,24 +86,33 @@ export default function ButtonModal() {
]
const
selectedColumns
=
buttons
.
filter
((
button
)
=>
selectedButtons
.
includes
(
button
.
key
))
.
filter
((
button
)
=>
selectedButtons
.
includes
(
button
))
.
map
((
button
)
=>
({
title
:
button
.
name
,
dataIndex
:
button
.
key
,
title
:
button
,
dataIndex
:
button
,
align
:
'center'
as
const
,
}))
return
[...
baseColumns
,
...
selectedColumns
]
},
[
selectedButtons
,
buttons
])
},
[
selectedButtons
])
const
handleButtonChange
=
(
checkedValues
:
string
[])
=>
{
setSelectedButtons
(
checkedValues
)
}
// 导出
const
handleExportExcel
=
()
=>
{
const
worksheet
=
utils
.
json_to_sheet
(
results
)
const
workbook
=
utils
.
book_new
()
utils
.
book_append_sheet
(
workbook
,
worksheet
,
'Sheet1'
)
writeFile
(
workbook
,
'data.xlsx'
)
}
return
(
<>
{
buttons
.
map
((
button
)
=>
(
<
Button
type=
"primary"
key=
{
button
.
key
}
onClick=
{
()
=>
handleOpen
(
button
)
}
>
{
button
.
name
}
<
Button
type=
"primary"
key=
{
button
}
onClick=
{
()
=>
handleOpen
(
button
)
}
>
{
button
}
</
Button
>
))
}
<
Modal
...
...
@@ -129,7 +120,9 @@ export default function ButtonModal() {
open=
{
open
}
footer=
{
<
Flex
justify=
"center"
gap=
{
20
}
>
<
Button
type=
"primary"
>
一键计算
</
Button
>
<
Button
type=
"primary"
loading=
{
isLoading
}
onClick=
{
handleSearch
}
>
一键计算
</
Button
>
</
Flex
>
}
destroyOnClose
...
...
@@ -139,14 +132,14 @@ export default function ButtonModal() {
<
Flex
justify=
"space-between"
align=
"center"
style=
{
{
marginBottom
:
'20px'
}
}
>
<
div
>
请选择字段:
<
Button
type=
"text"
icon=
{
<
DownloadOutlined
/>
}
size=
"small"
>
<
Button
type=
"text"
icon=
{
<
DownloadOutlined
/>
}
size=
"small"
onClick=
{
handleExportExcel
}
>
导出
</
Button
>
</
div
>
<
Checkbox
.
Group
value=
{
selectedButtons
}
onChange=
{
handleButtonChange
}
>
{
buttons
.
map
((
button
)
=>
(
<
Checkbox
key=
{
button
.
key
}
value=
{
button
.
key
}
>
{
button
.
name
}
<
Checkbox
key=
{
button
}
value=
{
button
}
>
{
button
}
</
Checkbox
>
))
}
</
Checkbox
.
Group
>
...
...
src/modules/data/read/exploration/components/RepeatButtonModal.tsx
浏览文件 @
95c96252
import
{
useState
}
from
'react'
import
{
Button
,
Flex
,
Modal
}
from
'antd'
import
{
use
Effect
,
use
State
}
from
'react'
import
{
Button
,
Empty
,
Flex
,
Modal
}
from
'antd'
import
{
useNavigate
}
from
'react-router'
import
AppSteps
from
'@/components/AppSteps'
import
{
useAI
}
from
'@/hooks/useAI'
import
prompt
from
'../prompt'
export
default
function
ButtonModal
()
{
const
[
results
,
setResults
]
=
useState
<
Array
<
Array
<
number
>>>
([])
const
{
isLoading
,
messages
,
post
}
=
useAI
()
const
lastMessage
=
messages
[
messages
.
length
-
1
]
useEffect
(()
=>
{
if
(
lastMessage
?.
role
===
'assistant'
&&
lastMessage
?.
content
)
{
try
{
const
parse
=
JSON
.
parse
(
lastMessage
.
content
)
console
.
log
(
parse
)
setResults
(
parse
.
results
||
[])
}
catch
(
error
)
{
console
.
error
(
error
)
}
}
},
[
lastMessage
])
const
handleSearch
=
()
=>
{
post
({
response_format
:
{
type
:
'json_object'
},
messages
:
[{
role
:
'user'
,
content
:
prompt
.
repeat
}],
})
}
const
navigate
=
useNavigate
()
const
[
open
,
setOpen
]
=
useState
(
false
)
const
[
current
,
setCurrent
]
=
useState
(
0
)
...
...
@@ -16,14 +41,7 @@ export default function ButtonModal() {
setCurrent
(
current
-
1
)
}
const
steps
=
[
{
title
:
'探索重复值'
,
},
{
title
:
'处理重复值'
,
},
]
const
steps
=
[{
title
:
'探索重复值'
},
{
title
:
'处理重复值'
}]
return
(
<>
...
...
@@ -35,16 +53,23 @@ export default function ButtonModal() {
open=
{
open
}
footer=
{
<
Flex
justify=
"center"
gap=
{
20
}
>
{
current
===
0
&&
<
Button
type=
"primary"
>
智能探索重复值字段
</
Button
>
}
{
current
===
0
&&
(
<
Button
type=
"primary"
loading=
{
isLoading
}
onClick=
{
handleSearch
}
>
智能探索重复值字段
</
Button
>
)
}
{
current
>
0
&&
<
Button
onClick=
{
()
=>
prev
()
}
>
上一步
</
Button
>
}
{
current
<
steps
.
length
-
1
&&
(
<
Button
type=
"primary"
onClick=
{
()
=>
next
()
}
>
<
Button
type=
"primary"
onClick=
{
()
=>
next
()
}
disabled=
{
!
results
.
length
}
>
下一步
</
Button
>
)
}
{
current
===
steps
.
length
-
1
&&
(
<
Button
type=
"primary"
onClick=
{
()
=>
navigate
(
'/data/preprocess/repeat'
)
}
>
<
Button
type=
"primary"
onClick=
{
()
=>
navigate
({
pathname
:
'/data/preprocess/repeat'
,
search
:
`?results=${JSON.stringify(results)}`
})
}
>
跳转数据预处理
</
Button
>
)
}
...
...
@@ -58,8 +83,13 @@ export default function ButtonModal() {
<
Flex
justify=
"space-between"
style=
{
{
marginBottom
:
20
}
}
>
探索结果:
</
Flex
>
<
p
>
1、第XX行、第XX行的数据完全一样
</
p
>
<
p
>
2、第XX行、第XX行、第XX行的数据完全一样
</
p
>
{
results
.
map
((
lines
,
index
)
=>
(
<
p
key=
{
index
}
>
{
index
+
1
}
{
lines
.
map
((
line
)
=>
`、第${line}行`
)
}
的数据完全一样
</
p
>
))
}
{
!
results
.
length
&&
<
Empty
></
Empty
>
}
</
div
>
</
Modal
>
</>
...
...
src/modules/data/read/exploration/prompt.ts
0 → 100644
浏览文件 @
95c96252
const
prompt
=
{
null
:
`找出数据集中有缺失值的字段,并返回 JSON 格式的结果。返回内容仅包含缺失字段的名称(name)及其对应的数据有哪些,字段像主键的数据(desc)。如果没有缺失值,则返回空数组 []。
返回格式示例:
json
{
"results": [
{"name": "字段1", "desc": "A000001、A000002..."},
{"name": "字段2", "desc": "A000001、A000002..."}
]
}
要求:
仅包含缺失值的字段(为空、null、undefined 等视为缺失)。
如果所有字段都有值,则返回 { "results": [] }。`
,
repeat
:
`请根据我给你的数据集,帮我检查这个数据集里面是否存在有重复值数据的情况,即:所有字段的值出现完全相同的多条数据。如果存在,请告诉我。并返回 JSON 格式的结果。返回内容仅包重复数据所在的行,如果没有重复记录,则返回空数组 []。
返回格式示例:
json
{
"results": [[1,11],[7,12,13]]
}
要求:
如果没有重复数据,则返回 { "results": [] }。`
,
max
:
`请根据我给你的数据集,帮我检查这个数据集里面是否存在有违反常规或者疑似不正常的过大值的情况。如果存在,请告诉我。
返回格式示例:
json
{
"results": [{name: '字段1', desc: 'XXXX值过大,参考XXXXXXXX}]
}
要求:
如果没有重复数据,则返回 { "results": [] }。`
,
min
:
`请根据我给你的数据集,帮我检查这个数据集里面是否存在有违反常规或者疑似不正常的过小值的情况。如果存在,请告诉我。
返回格式示例:
json
{
"results": [{name: '字段1', desc: 'XXXX值过小,参考XXXXXXXX}]
}
要求:
如果没有重复数据,则返回 { "results": [] }。`
,
error
:
`请根据我给你的数据集,帮我检查这个数据集里面是否存在有违反常规或者疑似不正常的逻辑错误的情况。如果存在,请告诉我。
返回格式示例:
json
{
"results": [{name: '字段1', desc: 'XXXX值存在逻辑错误,参考XXXXXXXX}]
}
要求:
如果没有重复数据,则返回 { "results": [] }。`
,
}
export
default
prompt
src/modules/data/write/upload/views/Index.tsx
浏览文件 @
95c96252
...
...
@@ -42,7 +42,7 @@ export default function DataWriteUpload() {
},
}
const
{
mutate
}
=
useImportDataset
()
const
{
mutate
,
isPending
}
=
useImportDataset
()
const
[
form
]
=
Form
.
useForm
()
const
navigate
=
useNavigate
()
...
...
@@ -75,7 +75,7 @@ export default function DataWriteUpload() {
<
span
style=
{
{
marginLeft
:
'10px'
}
}
>
{
file
?.
name
}
</
span
>
</
Upload
>
<
p
>
共计:
{
dataSource
.
length
}
条数据
</
p
>
<
Button
type=
"primary"
onClick=
{
handleSave
}
>
<
Button
type=
"primary"
onClick=
{
handleSave
}
loading=
{
isPending
}
>
保存
</
Button
>
</
Flex
>
...
...
src/utils/ai.ts
浏览文件 @
95c96252
...
...
@@ -14,6 +14,7 @@ export interface AIMessage {
}
export
interface
AIData
{
response_format
?:
{
type
:
'text'
|
'json_object'
}
model
?:
string
messages
:
AIMessage
[]
}
...
...
@@ -66,6 +67,10 @@ export async function yiyan(data: AIData, handlers: AIStreamHandlers): Promise<v
}
},
onmessage
(
res
)
{
if
(
res
.
data
===
'[DONE]'
)
{
handlers
.
onComplete
?.()
return
}
try
{
const
message
=
JSON
.
parse
(
res
.
data
)
handlers
.
onUpdate
({
id
:
message
.
id
,
content
:
message
.
result
||
''
})
...
...
@@ -88,7 +93,7 @@ export async function yiyan(data: AIData, handlers: AIStreamHandlers): Promise<v
// https://api-docs.deepseek.com/zh-cn/api/create-chat-completion
export
async
function
deepseek
(
data
:
AIData
,
handlers
:
AIStreamHandlers
):
Promise
<
void
>
{
const
apiKey
=
'sk-f1a6f0a7013241de8393cb2cb108e777'
const
params
=
{
model
:
'deepseek-
chat
'
,
stream
:
true
,
...
data
}
const
params
=
{
model
:
'deepseek-
reasoner
'
,
stream
:
true
,
...
data
}
await
fetchEventSource
(
'/api/deepseek/chat/completions'
,
{
method
:
'POST'
,
headers
:
{
'Content-Type'
:
'application/json'
,
Authorization
:
`Bearer
${
apiKey
}
`
},
...
...
@@ -101,6 +106,10 @@ export async function deepseek(data: AIData, handlers: AIStreamHandlers): Promis
}
},
onmessage
(
res
)
{
if
(
res
.
data
===
'[DONE]'
)
{
handlers
.
onComplete
?.()
return
}
try
{
const
message
=
JSON
.
parse
(
res
.
data
)
if
(
message
.
choices
&&
message
.
choices
.
length
>
0
)
{
...
...
@@ -127,7 +136,7 @@ export async function deepseek(data: AIData, handlers: AIStreamHandlers): Promis
// https://docs.siliconflow.cn/cn/api-reference/chat-completions/chat-completions
export
async
function
siliconflow
(
data
:
AIData
,
handlers
:
AIStreamHandlers
):
Promise
<
void
>
{
const
apiKey
=
'sk-bivnwauskdbvpspvmdorrgkrpwlyfxbfcezqsfsevowzubdj'
const
params
=
{
model
:
'deepseek-ai/DeepSeek-
V3
'
,
stream
:
true
,
...
data
}
const
params
=
{
model
:
'deepseek-ai/DeepSeek-
R1
'
,
stream
:
true
,
...
data
}
await
fetchEventSource
(
'/api/siliconflow/v1/chat/completions'
,
{
method
:
'POST'
,
headers
:
{
'Content-Type'
:
'application/json'
,
Authorization
:
`Bearer
${
apiKey
}
`
},
...
...
@@ -140,6 +149,10 @@ export async function siliconflow(data: AIData, handlers: AIStreamHandlers): Pro
}
},
onmessage
(
res
)
{
if
(
res
.
data
===
'[DONE]'
)
{
handlers
.
onComplete
?.()
return
}
try
{
const
message
=
JSON
.
parse
(
res
.
data
)
if
(
message
.
choices
&&
message
.
choices
.
length
>
0
)
{
...
...
@@ -166,7 +179,7 @@ export async function siliconflow(data: AIData, handlers: AIStreamHandlers): Pro
// https://help.aliyun.com/zh/model-studio/developer-reference/use-qwen-by-calling-api
export
async
function
qwen
(
data
:
AIData
,
handlers
:
AIStreamHandlers
):
Promise
<
void
>
{
const
apiKey
=
'sk-afd0fcdb53bf4058b2068b8548820150'
const
params
=
{
model
:
'qwen-max'
,
stream
:
true
,
...
data
}
const
params
=
{
model
:
'qwen-max
-latest
'
,
stream
:
true
,
...
data
}
await
fetchEventSource
(
'/api/qwen/compatible-mode/v1/chat/completions'
,
{
method
:
'POST'
,
headers
:
{
'Content-Type'
:
'application/json'
,
Authorization
:
`Bearer
${
apiKey
}
`
},
...
...
@@ -179,6 +192,10 @@ export async function qwen(data: AIData, handlers: AIStreamHandlers): Promise<vo
}
},
onmessage
(
res
)
{
if
(
res
.
data
===
'[DONE]'
)
{
handlers
.
onComplete
?.()
return
}
try
{
const
message
=
JSON
.
parse
(
res
.
data
)
if
(
message
.
choices
&&
message
.
choices
.
length
>
0
)
{
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论