提交 95c96252 authored 作者: 王鹏飞's avatar 王鹏飞

chore: update

上级 7dae00a4
...@@ -5,7 +5,6 @@ export function useAI() { ...@@ -5,7 +5,6 @@ export function useAI() {
const [ai, setAI] = useState<string>(localStorage.getItem('ai') || 'qwen') const [ai, setAI] = useState<string>(localStorage.getItem('ai') || 'qwen')
const [messages, setMessages] = useState<AIMessage[]>([]) const [messages, setMessages] = useState<AIMessage[]>([])
const [isLoading, setIsLoading] = useState<boolean>(false) const [isLoading, setIsLoading] = useState<boolean>(false)
console.log(messages)
useEffect(() => { useEffect(() => {
localStorage.setItem('ai', ai) localStorage.setItem('ai', ai)
}, [ai]) }, [ai])
......
...@@ -2,16 +2,24 @@ import { useState } from 'react' ...@@ -2,16 +2,24 @@ import { useState } from 'react'
import { Button, Checkbox, Flex, Modal, Radio, Select, Input, Form, Row, Col, Divider } from 'antd' import { Button, Checkbox, Flex, Modal, Radio, Select, Input, Form, Row, Col, Divider } from 'antd'
import { useDataFieldQuery } from '@/hooks/useQuery' import { useDataFieldQuery } from '@/hooks/useQuery'
import AppProgressSteps from '@/components/AppProgressSteps' import AppProgressSteps from '@/components/AppProgressSteps'
import { useSearchParams } from 'react-router'
export default function ButtonModal() { export default function ButtonModal() {
const [searchParams] = useSearchParams()
const results = searchParams.get('results')?.split(',') || []
const { data } = useDataFieldQuery() const { data } = useDataFieldQuery()
const [open, setOpen] = useState(false) const [open, setOpen] = useState(results.length > 0)
const [current, setCurrent] = useState(0) const [current, setCurrent] = useState(0)
const [form] = Form.useForm() const [form] = Form.useForm()
const initialValues = {
checkedList: results,
method: 'unified',
}
const checkedList = Form.useWatch('checkedList', form) || [] const checkedList = Form.useWatch('checkedList', form) || []
const processingMethod = Form.useWatch('processingMethod', form) const method = Form.useWatch('method', form)
const rule = Form.useWatch('rule', form) const rule = Form.useWatch('rule', form)
const rules = Form.useWatch('rules', form) || {} const rules = Form.useWatch('rules', form) || {}
...@@ -53,12 +61,12 @@ export default function ButtonModal() { ...@@ -53,12 +61,12 @@ export default function ButtonModal() {
title: '配置处理规则', title: '配置处理规则',
content: ( content: (
<> <>
<Form.Item label="缺失值字段处理方法" name="processingMethod"> <Form.Item label="缺失值字段处理方法" name="method">
<Radio.Group options={methodOptions} /> <Radio.Group options={methodOptions} />
</Form.Item> </Form.Item>
{/* 统一规则处理 */} {/* 统一规则处理 */}
{processingMethod === 'unified' && ( {method === 'unified' && (
<> <>
<Form.Item label="缺失值字段处理规则" name="rule"> <Form.Item label="缺失值字段处理规则" name="rule">
<Select options={ruleOptions} /> <Select options={ruleOptions} />
...@@ -73,7 +81,7 @@ export default function ButtonModal() { ...@@ -73,7 +81,7 @@ export default function ButtonModal() {
)} )}
{/* 逐个配置规则处理 */} {/* 逐个配置规则处理 */}
{processingMethod === 'individual' && {method === 'individual' &&
checkedList.map((field) => ( checkedList.map((field) => (
<div key={field} style={{ marginBottom: 10 }}> <div key={field} style={{ marginBottom: 10 }}>
<Form.Item label="字段">{field}</Form.Item> <Form.Item label="字段">{field}</Form.Item>
...@@ -174,7 +182,7 @@ export default function ButtonModal() { ...@@ -174,7 +182,7 @@ export default function ButtonModal() {
width={800} width={800}
onCancel={() => setOpen(false)}> onCancel={() => setOpen(false)}>
<div style={{ minHeight: 300, padding: '20px 0' }}> <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) => ( {steps.map((item, index) => (
<div key={index} hidden={current !== index} style={{ marginTop: '20px' }}> <div key={index} hidden={current !== index} style={{ marginTop: '20px' }}>
{item.content} {item.content}
......
import { useState } from 'react' import { useEffect, useState } from 'react'
import { Button, Radio, Flex, Modal } from 'antd' import { Button, Radio, Flex, Modal, Empty } from 'antd'
import type { RadioChangeEvent } from 'antd' import type { RadioChangeEvent } from 'antd'
import { useNavigate } from 'react-router' import { useNavigate } from 'react-router'
import AppSteps from '@/components/AppSteps' 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() { 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 navigate = useNavigate()
const [open, setOpen] = useState(false) const [open, setOpen] = useState(false)
const [current, setCurrent] = useState(0) const [current, setCurrent] = useState(0)
...@@ -41,16 +69,22 @@ export default function ButtonModal() { ...@@ -41,16 +69,22 @@ export default function ButtonModal() {
open={open} open={open}
footer={ footer={
<Flex justify="center" gap={20}> <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 > 0 && <Button onClick={() => prev()}>上一步</Button>}
{current < steps.length - 1 && ( {current < steps.length - 1 && (
<Button type="primary" onClick={() => next()}> <Button type="primary" onClick={() => next()} disabled={!!value}>
下一步 下一步
</Button> </Button>
)} )}
{current === steps.length - 1 && ( {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> </Button>
)} )}
...@@ -64,7 +98,8 @@ export default function ButtonModal() { ...@@ -64,7 +98,8 @@ export default function ButtonModal() {
<Flex justify="space-between" style={{ marginBottom: 20 }}> <Flex justify="space-between" style={{ marginBottom: 20 }}>
探索结果: 探索结果:
</Flex> </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> </div>
</Modal> </Modal>
</> </>
......
import { useState } from 'react' import { useEffect, useState } from 'react'
import { Button, Radio, Flex, Modal } from 'antd' import { Button, Radio, Flex, Modal, Empty } from 'antd'
import type { RadioChangeEvent } from 'antd' import type { RadioChangeEvent } from 'antd'
import { useNavigate } from 'react-router' import { useNavigate } from 'react-router'
import AppSteps from '@/components/AppSteps' 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() { 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 navigate = useNavigate()
const [open, setOpen] = useState(false) const [open, setOpen] = useState(false)
const [current, setCurrent] = useState(0) const [current, setCurrent] = useState(0)
...@@ -48,16 +76,22 @@ export default function ButtonModal() { ...@@ -48,16 +76,22 @@ export default function ButtonModal() {
open={open} open={open}
footer={ footer={
<Flex justify="center" gap={20}> <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 > 0 && <Button onClick={() => prev()}>上一步</Button>}
{current < steps.length - 1 && ( {current < steps.length - 1 && (
<Button type="primary" onClick={() => next()}> <Button type="primary" onClick={() => next()} disabled={!!value}>
下一步 下一步
</Button> </Button>
)} )}
{current === steps.length - 1 && ( {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> </Button>
)} )}
...@@ -71,7 +105,8 @@ export default function ButtonModal() { ...@@ -71,7 +105,8 @@ export default function ButtonModal() {
<Flex justify="space-between" style={{ marginBottom: 20 }}> <Flex justify="space-between" style={{ marginBottom: 20 }}>
探索结果: 探索结果:
</Flex> </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> </div>
</Modal> </Modal>
</> </>
......
import { useState } from 'react' import { useEffect, useState } from 'react'
import { Button, Radio, Flex, Modal } from 'antd' import { Button, Radio, Flex, Modal, Empty } from 'antd'
import type { RadioChangeEvent } from 'antd' import type { RadioChangeEvent } from 'antd'
import { useNavigate } from 'react-router' import { useNavigate } from 'react-router'
import AppSteps from '@/components/AppSteps' 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() { 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 navigate = useNavigate()
const [open, setOpen] = useState(false) const [open, setOpen] = useState(false)
const [current, setCurrent] = useState(0) const [current, setCurrent] = useState(0)
...@@ -24,14 +52,7 @@ export default function ButtonModal() { ...@@ -24,14 +52,7 @@ export default function ButtonModal() {
setCurrent(current - 1) setCurrent(current - 1)
} }
const steps = [ const steps = [{ title: '探索过小值字段' }, { title: '处理过小值' }]
{
title: '探索过小值字段',
},
{
title: '处理过小值',
},
]
const style: React.CSSProperties = { const style: React.CSSProperties = {
display: 'flex', display: 'flex',
...@@ -48,16 +69,22 @@ export default function ButtonModal() { ...@@ -48,16 +69,22 @@ export default function ButtonModal() {
open={open} open={open}
footer={ footer={
<Flex justify="center" gap={20}> <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 > 0 && <Button onClick={() => prev()}>上一步</Button>}
{current < steps.length - 1 && ( {current < steps.length - 1 && (
<Button type="primary" onClick={() => next()}> <Button type="primary" onClick={() => next()} disabled={!!value}>
下一步 下一步
</Button> </Button>
)} )}
{current === steps.length - 1 && ( {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> </Button>
)} )}
...@@ -71,7 +98,8 @@ export default function ButtonModal() { ...@@ -71,7 +98,8 @@ export default function ButtonModal() {
<Flex justify="space-between" style={{ marginBottom: 20 }}> <Flex justify="space-between" style={{ marginBottom: 20 }}>
探索结果: 探索结果:
</Flex> </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> </div>
</Modal> </Modal>
</> </>
......
import { useEffect, useState } from 'react' 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 type { CheckboxProps } from 'antd'
import { useNavigate } from 'react-router' import { useNavigate } from 'react-router'
import AppSteps from '@/components/AppSteps' import AppSteps from '@/components/AppSteps'
import { useAI } from '@/hooks/useAI' import { useAI } from '@/hooks/useAI'
import prompt from '../prompt'
const CheckboxGroup = Checkbox.Group const CheckboxGroup = Checkbox.Group
interface ResultItem {
name: string
desc: string
}
export default function ButtonModal() { export default function ButtonModal() {
const [results, setResults] = useState<ResultItem[]>([])
const resultsOptions = results.map((result) => result.name)
const { isLoading, messages, post } = useAI() const { isLoading, messages, post } = useAI()
const lastMessage = messages[messages.length - 1] 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(() => { useEffect(() => {
if (lastMessage?.role === 'assistant' && lastMessage?.content) { if (lastMessage?.role === 'assistant' && lastMessage?.content) {
try { try {
const parse = JSON.parse(lastMessage.content) const parse = JSON.parse(lastMessage.content)
setFields(parse.fields || []) console.log(parse)
setResults(parse.results || [])
} catch (error) { } catch (error) {
console.error(error) console.error(error)
} }
...@@ -27,27 +32,8 @@ export default function ButtonModal() { ...@@ -27,27 +32,8 @@ export default function ButtonModal() {
}, [lastMessage]) }, [lastMessage])
const handleSearch = () => { const handleSearch = () => {
post({ post({
response_format: { response_format: { type: 'json_object' },
type: 'json_object', messages: [{ role: 'user', content: prompt.null }],
},
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": [] }。`,
},
],
}) })
} }
...@@ -58,15 +44,15 @@ json ...@@ -58,15 +44,15 @@ json
const [checkedList, setCheckedList] = useState<string[]>([]) const [checkedList, setCheckedList] = useState<string[]>([])
const checkAll = fieldsOptions.length === checkedList.length const checkAll = resultsOptions.length === checkedList.length
const indeterminate = checkedList.length > 0 && checkedList.length < fieldsOptions.length const indeterminate = checkedList.length > 0 && checkedList.length < resultsOptions.length
const onChange = (list: string[]) => { const onChange = (list: string[]) => {
setCheckedList(list) setCheckedList(list)
} }
const onCheckAllChange: CheckboxProps['onChange'] = (e) => { const onCheckAllChange: CheckboxProps['onChange'] = (e) => {
setCheckedList(e.target.checked ? fieldsOptions : []) setCheckedList(e.target.checked ? resultsOptions : [])
} }
const next = () => { const next = () => {
...@@ -88,7 +74,8 @@ json ...@@ -88,7 +74,8 @@ json
全选 全选
</Checkbox> </Checkbox>
</Flex> </Flex>
<CheckboxGroup options={fieldsOptions} value={checkedList} onChange={onChange} /> <CheckboxGroup options={resultsOptions} value={checkedList} onChange={onChange} />
{!results.length && <Empty></Empty>}
</> </>
), ),
}, },
...@@ -99,9 +86,9 @@ json ...@@ -99,9 +86,9 @@ json
<Flex justify="space-between" style={{ marginBottom: 20 }}> <Flex justify="space-between" style={{ marginBottom: 20 }}>
探索结果: 探索结果:
</Flex> </Flex>
{fields.map((item: any) => ( {results.map((item) => (
<p key={item.english_name}> <p key={item.name}>
{item.name}: {item.desc} {item.name}: 该字段有缺失值的数据有:{item.desc}
</p> </p>
))} ))}
</> </>
...@@ -117,7 +104,7 @@ json ...@@ -117,7 +104,7 @@ json
全选 全选
</Checkbox> </Checkbox>
</Flex> </Flex>
<CheckboxGroup options={fieldsOptions} value={checkedList} onChange={onChange} /> <CheckboxGroup options={resultsOptions} value={checkedList} onChange={onChange} />
</> </>
), ),
}, },
...@@ -151,7 +138,12 @@ json ...@@ -151,7 +138,12 @@ json
</Button> </Button>
)} )}
{current === steps.length - 1 && ( {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> </Button>
)} )}
......
import { useState, useMemo } from 'react' import { useState, useMemo, useEffect } from 'react'
import { Button, Flex, Modal, Table, Checkbox } from 'antd' import { Button, Flex, Modal, Table, Checkbox } from 'antd'
import { DownloadOutlined } from '@ant-design/icons' 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() { 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 [open, setOpen] = useState(false)
const [title, setTitle] = useState('') const [title, setTitle] = useState('')
const handleOpen = (button: { name: string; key: string }) => { const handleOpen = (buttonName: string) => {
setSelectedButtons([button.key]) setSelectedButtons([buttonName])
setTitle(`探索` + button.name) setTitle(`探索` + buttonName)
setOpen(true) setOpen(true)
} }
const buttons = useMemo(() => { const [selectedButtons, setSelectedButtons] = useState<string[]>([])
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 dataSource = [ const dataSource = results.map((item) => {
{ return { ...item, key: item.name }
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 columns = useMemo(() => { const columns: any = useMemo(() => {
const baseColumns = [ const baseColumns = [
{ {
title: '序号', title: '序号',
dataIndex: 'key', key: 'index',
render(_value: any, _record: any, index: any) {
return index + 1
},
width: 62, width: 62,
align: 'center' as const, align: 'center',
}, },
{ {
title: '字段名称', title: '字段名称',
...@@ -104,24 +86,33 @@ export default function ButtonModal() { ...@@ -104,24 +86,33 @@ export default function ButtonModal() {
] ]
const selectedColumns = buttons const selectedColumns = buttons
.filter((button) => selectedButtons.includes(button.key)) .filter((button) => selectedButtons.includes(button))
.map((button) => ({ .map((button) => ({
title: button.name, title: button,
dataIndex: button.key, dataIndex: button,
align: 'center' as const, align: 'center' as const,
})) }))
return [...baseColumns, ...selectedColumns] return [...baseColumns, ...selectedColumns]
}, [selectedButtons, buttons]) }, [selectedButtons])
const handleButtonChange = (checkedValues: string[]) => { const handleButtonChange = (checkedValues: string[]) => {
setSelectedButtons(checkedValues) 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 ( return (
<> <>
{buttons.map((button) => ( {buttons.map((button) => (
<Button type="primary" key={button.key} onClick={() => handleOpen(button)}> <Button type="primary" key={button} onClick={() => handleOpen(button)}>
{button.name} {button}
</Button> </Button>
))} ))}
<Modal <Modal
...@@ -129,7 +120,9 @@ export default function ButtonModal() { ...@@ -129,7 +120,9 @@ export default function ButtonModal() {
open={open} open={open}
footer={ footer={
<Flex justify="center" gap={20}> <Flex justify="center" gap={20}>
<Button type="primary">一键计算</Button> <Button type="primary" loading={isLoading} onClick={handleSearch}>
一键计算
</Button>
</Flex> </Flex>
} }
destroyOnClose destroyOnClose
...@@ -139,14 +132,14 @@ export default function ButtonModal() { ...@@ -139,14 +132,14 @@ export default function ButtonModal() {
<Flex justify="space-between" align="center" style={{ marginBottom: '20px' }}> <Flex justify="space-between" align="center" style={{ marginBottom: '20px' }}>
<div> <div>
请选择字段: 请选择字段:
<Button type="text" icon={<DownloadOutlined />} size="small"> <Button type="text" icon={<DownloadOutlined />} size="small" onClick={handleExportExcel}>
导出 导出
</Button> </Button>
</div> </div>
<Checkbox.Group value={selectedButtons} onChange={handleButtonChange}> <Checkbox.Group value={selectedButtons} onChange={handleButtonChange}>
{buttons.map((button) => ( {buttons.map((button) => (
<Checkbox key={button.key} value={button.key}> <Checkbox key={button} value={button}>
{button.name} {button}
</Checkbox> </Checkbox>
))} ))}
</Checkbox.Group> </Checkbox.Group>
......
import { useState } from 'react' import { useEffect, useState } from 'react'
import { Button, Flex, Modal } from 'antd' import { Button, Empty, Flex, Modal } from 'antd'
import { useNavigate } from 'react-router' import { useNavigate } from 'react-router'
import AppSteps from '@/components/AppSteps' import AppSteps from '@/components/AppSteps'
import { useAI } from '@/hooks/useAI'
import prompt from '../prompt'
export default function ButtonModal() { 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 navigate = useNavigate()
const [open, setOpen] = useState(false) const [open, setOpen] = useState(false)
const [current, setCurrent] = useState(0) const [current, setCurrent] = useState(0)
...@@ -16,14 +41,7 @@ export default function ButtonModal() { ...@@ -16,14 +41,7 @@ export default function ButtonModal() {
setCurrent(current - 1) setCurrent(current - 1)
} }
const steps = [ const steps = [{ title: '探索重复值' }, { title: '处理重复值' }]
{
title: '探索重复值',
},
{
title: '处理重复值',
},
]
return ( return (
<> <>
...@@ -35,16 +53,23 @@ export default function ButtonModal() { ...@@ -35,16 +53,23 @@ export default function ButtonModal() {
open={open} open={open}
footer={ footer={
<Flex justify="center" gap={20}> <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 > 0 && <Button onClick={() => prev()}>上一步</Button>}
{current < steps.length - 1 && ( {current < steps.length - 1 && (
<Button type="primary" onClick={() => next()}> <Button type="primary" onClick={() => next()} disabled={!results.length}>
下一步 下一步
</Button> </Button>
)} )}
{current === steps.length - 1 && ( {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> </Button>
)} )}
...@@ -58,8 +83,13 @@ export default function ButtonModal() { ...@@ -58,8 +83,13 @@ export default function ButtonModal() {
<Flex justify="space-between" style={{ marginBottom: 20 }}> <Flex justify="space-between" style={{ marginBottom: 20 }}>
探索结果: 探索结果:
</Flex> </Flex>
<p>1、第XX行、第XX行的数据完全一样</p> {results.map((lines, index) => (
<p>2、第XX行、第XX行、第XX行的数据完全一样</p> <p key={index}>
{index + 1}
{lines.map((line) => `、第${line}行`)}的数据完全一样
</p>
))}
{!results.length && <Empty></Empty>}
</div> </div>
</Modal> </Modal>
</> </>
......
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
...@@ -42,7 +42,7 @@ export default function DataWriteUpload() { ...@@ -42,7 +42,7 @@ export default function DataWriteUpload() {
}, },
} }
const { mutate } = useImportDataset() const { mutate, isPending } = useImportDataset()
const [form] = Form.useForm() const [form] = Form.useForm()
const navigate = useNavigate() const navigate = useNavigate()
...@@ -75,7 +75,7 @@ export default function DataWriteUpload() { ...@@ -75,7 +75,7 @@ export default function DataWriteUpload() {
<span style={{ marginLeft: '10px' }}>{file?.name}</span> <span style={{ marginLeft: '10px' }}>{file?.name}</span>
</Upload> </Upload>
<p>共计:{dataSource.length}条数据</p> <p>共计:{dataSource.length}条数据</p>
<Button type="primary" onClick={handleSave}> <Button type="primary" onClick={handleSave} loading={isPending}>
保存 保存
</Button> </Button>
</Flex> </Flex>
......
...@@ -14,6 +14,7 @@ export interface AIMessage { ...@@ -14,6 +14,7 @@ export interface AIMessage {
} }
export interface AIData { export interface AIData {
response_format?: { type: 'text' | 'json_object' }
model?: string model?: string
messages: AIMessage[] messages: AIMessage[]
} }
...@@ -66,6 +67,10 @@ export async function yiyan(data: AIData, handlers: AIStreamHandlers): Promise<v ...@@ -66,6 +67,10 @@ export async function yiyan(data: AIData, handlers: AIStreamHandlers): Promise<v
} }
}, },
onmessage(res) { onmessage(res) {
if (res.data === '[DONE]') {
handlers.onComplete?.()
return
}
try { try {
const message = JSON.parse(res.data) const message = JSON.parse(res.data)
handlers.onUpdate({ id: message.id, content: message.result || '' }) handlers.onUpdate({ id: message.id, content: message.result || '' })
...@@ -88,7 +93,7 @@ export async function yiyan(data: AIData, handlers: AIStreamHandlers): Promise<v ...@@ -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 // https://api-docs.deepseek.com/zh-cn/api/create-chat-completion
export async function deepseek(data: AIData, handlers: AIStreamHandlers): Promise<void> { export async function deepseek(data: AIData, handlers: AIStreamHandlers): Promise<void> {
const apiKey = 'sk-f1a6f0a7013241de8393cb2cb108e777' 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', { await fetchEventSource('/api/deepseek/chat/completions', {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${apiKey}` }, headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${apiKey}` },
...@@ -101,6 +106,10 @@ export async function deepseek(data: AIData, handlers: AIStreamHandlers): Promis ...@@ -101,6 +106,10 @@ export async function deepseek(data: AIData, handlers: AIStreamHandlers): Promis
} }
}, },
onmessage(res) { onmessage(res) {
if (res.data === '[DONE]') {
handlers.onComplete?.()
return
}
try { try {
const message = JSON.parse(res.data) const message = JSON.parse(res.data)
if (message.choices && message.choices.length > 0) { if (message.choices && message.choices.length > 0) {
...@@ -127,7 +136,7 @@ export async function deepseek(data: AIData, handlers: AIStreamHandlers): Promis ...@@ -127,7 +136,7 @@ export async function deepseek(data: AIData, handlers: AIStreamHandlers): Promis
// https://docs.siliconflow.cn/cn/api-reference/chat-completions/chat-completions // https://docs.siliconflow.cn/cn/api-reference/chat-completions/chat-completions
export async function siliconflow(data: AIData, handlers: AIStreamHandlers): Promise<void> { export async function siliconflow(data: AIData, handlers: AIStreamHandlers): Promise<void> {
const apiKey = 'sk-bivnwauskdbvpspvmdorrgkrpwlyfxbfcezqsfsevowzubdj' 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', { await fetchEventSource('/api/siliconflow/v1/chat/completions', {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${apiKey}` }, headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${apiKey}` },
...@@ -140,6 +149,10 @@ export async function siliconflow(data: AIData, handlers: AIStreamHandlers): Pro ...@@ -140,6 +149,10 @@ export async function siliconflow(data: AIData, handlers: AIStreamHandlers): Pro
} }
}, },
onmessage(res) { onmessage(res) {
if (res.data === '[DONE]') {
handlers.onComplete?.()
return
}
try { try {
const message = JSON.parse(res.data) const message = JSON.parse(res.data)
if (message.choices && message.choices.length > 0) { if (message.choices && message.choices.length > 0) {
...@@ -166,7 +179,7 @@ export async function siliconflow(data: AIData, handlers: AIStreamHandlers): Pro ...@@ -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 // https://help.aliyun.com/zh/model-studio/developer-reference/use-qwen-by-calling-api
export async function qwen(data: AIData, handlers: AIStreamHandlers): Promise<void> { export async function qwen(data: AIData, handlers: AIStreamHandlers): Promise<void> {
const apiKey = 'sk-afd0fcdb53bf4058b2068b8548820150' 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', { await fetchEventSource('/api/qwen/compatible-mode/v1/chat/completions', {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${apiKey}` }, headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${apiKey}` },
...@@ -179,6 +192,10 @@ export async function qwen(data: AIData, handlers: AIStreamHandlers): Promise<vo ...@@ -179,6 +192,10 @@ export async function qwen(data: AIData, handlers: AIStreamHandlers): Promise<vo
} }
}, },
onmessage(res) { onmessage(res) {
if (res.data === '[DONE]') {
handlers.onComplete?.()
return
}
try { try {
const message = JSON.parse(res.data) const message = JSON.parse(res.data)
if (message.choices && message.choices.length > 0) { if (message.choices && message.choices.length > 0) {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论