提交 5e707104 authored 作者: 王鹏飞's avatar 王鹏飞

chore: update

上级 22ce87b3
import { Suspense } from 'react'
import { Suspense, useEffect } from 'react'
import { Spin } from 'antd'
import { useRoutes } from 'react-router'
import { useRoutes, useSearchParams, useLocation } from 'react-router'
import routes from './router/routes'
import './App.scss'
const params = new URLSearchParams(window.location.search)
const experimentId = params.get('experiment_id') || '7028276368903241728'
const App = () => {
const element = useRoutes(routes)
const [searchParams, setSearchParams] = useSearchParams()
const location = useLocation()
useEffect(() => {
const currentId = searchParams.get('experiment_id')
if (!currentId) {
const newSearchParams = new URLSearchParams(location.search)
newSearchParams.set('experiment_id', experimentId)
setSearchParams(newSearchParams)
}
}, [location.search, searchParams, setSearchParams])
return (
<Suspense
fallback={
......
......@@ -46,6 +46,7 @@ export default function SelectFieldButtonModal({
placeholder="请选择字段"
options={currentFieldOptions}
value={value}
allowClear
onChange={setValue}
style={{ width: '100%' }}></Select>
</div>
......
......@@ -4,6 +4,8 @@ import { useDataFieldQuery } from '@/hooks/useQuery'
import AppProgressSteps from '@/components/AppProgressSteps'
import { useProcessData } from '../query'
import { MinusCircleOutlined } from '@ant-design/icons'
import { useAI } from '@/hooks/useAI'
import MarkdownRender from '@/components/MarkdownRender'
export default function ButtonModal() {
const { fieldOptions, getFieldName } = useDataFieldQuery()
......@@ -19,7 +21,81 @@ export default function ButtonModal() {
const field = Form.useWatch('field', form)
const action = Form.useWatch('action', form)
const rules = Form.useWatch('rules', form) || []
const min = Form.useWatch('min', form)
const max = Form.useWatch('max', form)
const step = Form.useWatch('step', form)
const [result, setResult] = useState('')
const { isLoading, post } = useAI({
onComplete: (message) => {
setResult(message?.content || '')
},
})
const handleAI = async () => {
post({
messages: [
{
role: 'user',
content: `任务目标
分析指定字段的分箱规则是否合理,包括 最大值、最小值、步长值 的设置,特别关注 小于最小值 和 大于最大值 的边界情况,并提供改进建议。
输入要求
字段信息 :[${getFieldName(field)}]
数据样本:[在此粘贴数据片段或上传文件(CSV/XLSX)]
分箱参数 :
${action === '固定步长分箱' ? `最小值 ${min}、最大值 ${max}、步长 ${step}。` : `刻度值:${rules.join(',')}`}
输出格式:
| 序号 | 分箱范围 | 当前分箱合理性 | 合理性判断依据 | 改进建议(格式:建议名称:描述,执行方式:自动/人工)
| 1 | <5(小于最小值) | 不合理 | 存在10%数据低于最小值阈值 | 建议类型:调整最小值为3,执行方式:人工
| 2 | 5-100(含100) | 合理 | 数据分布均匀 | 无需调整
| 3 | 100-200(不含100 | 不合理 | 数据集中在150-200,分箱过宽 | 建议类型:调整步长为50,执行方式:自动
执行要求
分箱规则 :
区间闭合 :
最小值:包含下界(如 >=5)。
最大值:包含上界(如 <=200)。
分箱逻辑 :
标准分箱 :
第一个分箱:[最小值, 最小值+步长](含上界)。
后续分箱:(前一个分箱上限, 前一个上限+步长](如 100-200 为 (100, 200])。
边界分箱 :
<最小值:包含所有小于最小值的值。
>最大值:包含所有大于最大值的值。
合理性评估标准 :
数据覆盖 :是否覆盖所有数据(包括边界值)。
业务逻辑 :是否符合业务需求(如年龄分箱需避免跨年龄段)。
数据分布均衡性 :分箱内数据量是否合理(如避免空箱或数据堆积)。`,
},
],
})
}
// 处理下一步按钮逻辑
const handleNext = async () => {
if (current === 0) {
......@@ -36,10 +112,8 @@ export default function ButtonModal() {
// 开始处理
const handleStart = () => {
const values = form.getFieldsValue()
const params = { ...values, rules: JSON.stringify(values.rules), action: '固定步长分箱' }
mutate(params, {
onSuccess: handleClose,
})
const params = { ...values, rules: JSON.stringify(values.rules) }
mutate(params)
}
// 关闭并重置
......@@ -47,6 +121,7 @@ export default function ButtonModal() {
setOpen(false)
setCurrent(0)
remove()
setResult('')
}
const [groupName, setGroupName] = useState('')
......@@ -88,33 +163,49 @@ export default function ButtonModal() {
<>
<Form.Item label="数据分箱操作字段">{getFieldName(field)}</Form.Item>
<Form.Item name="action">
<Radio.Group block options={['固定步长分箱', '自定义刻度值分箱']} optionType="button" buttonStyle="solid" />
<Flex align="center" justify="center" style={{ marginBottom: 20 }}>
<Form.Item name="action" noStyle>
<Radio.Group options={['固定步长分箱', '自定义刻度值分箱']} optionType="button" buttonStyle="solid" />
</Form.Item>
</Flex>
</Form.Item>
{action === '固定步长分箱' && (
<>
<Form.Item label="最小值">
<Form.Item name="min" rules={[{ required: true, message: '请输入最小值' }]} noStyle>
<Input placeholder="请输入" />
</Form.Item>
<p>分箱的取值规则为:大于等于最小值。例如最小值为5,则为&gt;=5</p>
<Form.Item
label="最小值"
name="min"
labelCol={{ span: 2 }}
rules={[{ required: true, message: '请输入最小值' }]}
extra="分箱的取值规则为:大于等于最小值。例如最小值为5,则为&gt;=5">
<Input placeholder="请输入" />
</Form.Item>
<Form.Item label="最大值">
<Form.Item name="max" rules={[{ required: true, message: '请输入最大值' }]} noStyle>
<Input placeholder="请输入" />
</Form.Item>
<p>分箱的取值规则为:小于等于最大值。例如最大值为200,则为&lt;=200</p>
<Form.Item
label="最大值"
name="max"
labelCol={{ span: 2 }}
rules={[{ required: true, message: '请输入最大值' }]}
extra="分箱的取值规则为:小于等于最大值。例如最大值为200,则为&lt;=200">
<Input placeholder="请输入" />
</Form.Item>
<Form.Item label="步长值">
<Form.Item name="step" rules={[{ required: true, message: '请输入步长值' }]} noStyle>
<Input placeholder="请输入" />
</Form.Item>
<p>以步长为分箱。例如步长值为100,则分箱结果为:5-100(含100),100-200(不含100)</p>
<Form.Item
label="步长值"
name="step"
labelCol={{ span: 2 }}
rules={[{ required: true, message: '请输入步长值' }]}
extra="以步长为分箱。例如步长值为100,则分箱结果为:5-100(含100),100-200(不含100)">
<Input placeholder="请输入" />
</Form.Item>
<Form.Item label="边界">
<Form.Item name="include_min" noStyle valuePropName="checked">
<Form.Item label="边界" labelCol={{ span: 2 }}>
<Form.Item
name="include_min"
valuePropName="checked"
extra="勾选示例:分箱结果为:小于5(不含5),5-100(含100),100-200(不含100)">
<Checkbox>包含小于最小值</Checkbox>
</Form.Item>
<Form.Item name="include_max" noStyle valuePropName="checked">
<Form.Item
name="include_max"
valuePropName="checked"
extra="勾选示例:分箱结果为:5-100(含100),100-200(不含100),大于200(不含200)">
<Checkbox>包含大于最大值</Checkbox>
</Form.Item>
</Form.Item>
......@@ -125,7 +216,7 @@ export default function ButtonModal() {
<Form.List name="rules">
{(fields, { add, remove }) => (
<>
<Form.Item label="数据分组名称">
<Form.Item label={null}>
<Flex gap={10} align="center">
<Input
placeholder="请输入"
......@@ -135,6 +226,7 @@ export default function ButtonModal() {
/>
<Button
type="primary"
disabled={!groupName}
onClick={() => {
if (!groupName) return
add(groupName) // 添加新分组
......@@ -166,6 +258,19 @@ export default function ButtonModal() {
</Form.Item>
</>
)}
{result && (
<div
style={{
marginTop: 20,
background: '#f5f5f5',
padding: 20,
maxHeight: 300,
overflow: 'auto',
borderRadius: 10,
}}>
<MarkdownRender>{result}</MarkdownRender>
</div>
)}
</>
),
},
......@@ -202,7 +307,11 @@ export default function ButtonModal() {
open={open}
footer={
<Flex justify="center" gap={20}>
{current === 1 && <Button type="primary">AI智能建议</Button>}
{current === 1 && (
<Button type="primary" onClick={handleAI} loading={isLoading}>
AI智能建议
</Button>
)}
{current > 0 && <Button onClick={() => setCurrent(current - 1)}>上一步</Button>}
{current < steps.length - 1 && (
<Button type="primary" onClick={handleNext}>
......@@ -222,11 +331,9 @@ export default function ButtonModal() {
<div style={{ minHeight: 300, padding: '20px 0' }}>
<Form
form={form}
labelCol={{ span: 5 }}
labelCol={{ span: 6 }}
preserve={false}
initialValues={{
action: '固定步长分箱',
}}>
initialValues={{ action: '固定步长分箱', include_max: false, include_min: false }}>
{steps.map((item, index) => (
<div key={index} hidden={current !== index} style={{ marginTop: '20px' }}>
{item.content}
......
......@@ -4,62 +4,64 @@ import { useDataFieldQuery } from '@/hooks/useQuery'
import AppProgressSteps from '@/components/AppProgressSteps'
import FunctionInput from '@/components/data/FunctionInput'
import { useProcessData } from '../query'
import { useAI } from '@/hooks/useAI'
import MarkdownRender from '@/components/MarkdownRender'
const functionShortcutOptions = [
{
label: '获取“年份”信息',
label: '获取"年份"信息',
value: 'YEAR',
demo: '如:“2025年”',
demo: '如:"2025年"',
},
// {
// label: '获取“季度”信息',
// label: '获取"季度"信息',
// value: 'YEAR',
// demo: '如:“一季度”',
// demo: '如:"一季度"',
// },
// {
// label: '获取“年季度”信息',
// label: '获取"年季度"信息',
// value: 'YEAR',
// demo: '如:“2025年一季度”',
// demo: '如:"2025年一季度"',
// },
{
label: '获取“月份”信息',
label: '获取"月份"信息',
value: 'MONTH',
demo: '如:“3月”',
demo: '如:"3月"',
},
{
label: '获取“年月份”信息',
label: '获取"年月份"信息',
value: 'DATE',
demo: '如:“2025年3月”',
demo: '如:"2025年3月"',
},
{
label: '获取“周”信息',
label: '获取"周"信息',
value: 'NETWORKDAYS',
demo: '如:“12周”',
demo: '如:"12周"',
},
// {
// label: '获取“年周”信息',
// label: '获取"年周"信息',
// value: 'YEAR',
// demo: '如:“2025年12周”',
// demo: '如:"2025年12周"',
// },
{
label: '获取“日”信息',
label: '获取"日"信息',
value: 'DAY',
demo: '如:“21日”',
demo: '如:"21日"',
},
{
label: '获取“小时”信息',
label: '获取"小时"信息',
value: 'HOUR',
demo: '如:“12时”',
demo: '如:"12时"',
},
{
label: '获取“分钟”信息',
label: '获取"分钟"信息',
value: 'MINUTE',
demo: '如:“21分”',
demo: '如:"21分"',
},
{
label: '获取“秒”信息',
label: '获取"秒"信息',
value: 'SECOND',
demo: '如:“18秒”',
demo: '如:"18秒"',
},
]
......@@ -67,21 +69,21 @@ const functionOptions = [
{ label: 'DATE:组合年、月、日为标准日期', value: 'DATE' },
{ label: 'DATEDIF:计算两个日期间的年/月/日差值', value: 'DATEDIF' },
{ label: 'DATEVALUE:将文本日期转换为 Excel 可识别的序列号', value: 'DATEVALUE' },
{ label: 'DAY:提取日期中的“日”部分', value: 'DAY' },
{ label: 'DAY:提取日期中的"日"部分', value: 'DAY' },
{ label: 'EDATE:返回指定日期之前/之后几个月的日期', value: 'EDATE' },
{ label: 'EMONTH:返回指定日期之前/之后几个月的最后一天', value: 'EMONTH' },
{ label: 'HOUR:提取时间中的“小时”部分', value: 'HOUR' },
{ label: 'MINUTE:提取时间中的“分钟”部分', value: 'MINUTE' },
{ label: 'MONTH:提取日期中的“月”部分', value: 'MONTH' },
{ label: 'HOUR:提取时间中的"小时"部分', value: 'HOUR' },
{ label: 'MINUTE:提取时间中的"分钟"部分', value: 'MINUTE' },
{ label: 'MONTH:提取日期中的"月"部分', value: 'MONTH' },
{ label: 'NETWORKDAYS:计算两个日期之间的工作日天数(排除周末和节假日)', value: 'NETWORKDAYS' },
{ label: 'NOW:返回当前日期和时间(精确到秒)', value: 'NOW' },
{ label: 'SECOND:提取时间中的“秒”部分', value: 'SECOND' },
{ label: 'SECOND:提取时间中的"秒"部分', value: 'SECOND' },
{ label: 'TIME:组合时、分、秒为标准时间', value: 'TIME' },
{ label: 'TIMEVALUE:将文本时间转换为小数', value: 'TIMEVALUE' },
{ label: 'TODAY:返回当前系统日期(无参数)', value: 'TODAY' },
{ label: 'WEEKDAY:返回日期对应的星期几(数字形式)', value: 'WEEKDAY' },
{ label: 'WORKDAY:计算指定工作日天数后的日期(跳过周末和节假日)', value: 'WORKDAY' },
{ label: 'YEAR:提取日期中的“年”部分', value: 'YEAR' },
{ label: 'YEAR:提取日期中的"年"部分', value: 'YEAR' },
]
export default function ButtonModal() {
......@@ -92,6 +94,44 @@ export default function ButtonModal() {
const [form] = Form.useForm()
const action = Form.useWatch('action', form)
const [result, setResult] = useState('')
const { isLoading, post } = useAI({
onComplete: (message) => {
setResult(message?.content || '')
},
})
const handleAI = async () => {
const values = form.getFieldsValue()
const selectedFunction = values.function
const functionFormula = values.content || ''
post({
messages: [
{
role: 'user',
content: `请检查以下日期函数公式是否正确,并提供改进建议:
1. 当前选择的函数:${selectedFunction}
2. 输入的公式:${functionFormula}
请按照以下格式回答:
1. 语法检查:检查函数语法是否正确,包括括号匹配、参数格式等
2. 参数检查:检查参数类型和数量是否符合函数要求
3. 逻辑检查:检查函数逻辑是否合理,是否符合预期用途
4. 改进建议:如果有问题,请提供具体的改进建议
5. 示例:提供一个正确的使用示例
注意:
- 请确保检查结果清晰易懂
- 如果公式完全正确,请明确指出
- 如果发现潜在问题,请详细说明原因
- 提供的示例应该与当前使用场景相关`,
},
],
})
}
// 处理下一步按钮逻辑
const handleNext = async () => {
if (current === 0) {
......@@ -117,9 +157,7 @@ export default function ButtonModal() {
fields.push(values.field)
}
const params = { ...values, fields: JSON.stringify(fields) } // 将提取的参数赋值给 fields
mutate(params, {
onSuccess: handleClose,
})
mutate(params)
}
// 关闭并重置
......@@ -127,6 +165,7 @@ export default function ButtonModal() {
setOpen(false)
setCurrent(0)
remove()
setResult('')
}
const handleFunctionChange = (value: string) => {
form.setFieldsValue({ content: `${value}()` })
......@@ -199,6 +238,19 @@ export default function ButtonModal() {
</Form.Item>
</>
)}
{result && (
<div
style={{
marginTop: 20,
background: '#f5f5f5',
padding: 20,
maxHeight: 300,
overflow: 'auto',
borderRadius: 10,
}}>
<MarkdownRender>{result}</MarkdownRender>
</div>
)}
</>
),
},
......@@ -228,7 +280,11 @@ export default function ButtonModal() {
const Footer = () => {
return (
<Flex justify="center" gap={20}>
{current === 1 && action === '日期函数计算' && <Button type="primary">AI智能建议</Button>}
{current === 1 && action === '日期函数计算' && (
<Button type="primary" onClick={handleAI} loading={isLoading}>
AI检查函数公式
</Button>
)}
{current > 0 && <Button onClick={() => setCurrent(current - 1)}>上一步</Button>}
{current < steps.length - 1 && (
<Button type="primary" onClick={handleNext}>
......
......@@ -3,6 +3,8 @@ import { Button, Flex, Modal, Radio, Input, Form, Row, Col } from 'antd'
import { useDataFieldQuery } from '@/hooks/useQuery'
import AppProgressSteps from '@/components/AppProgressSteps'
import { useProcessData } from '../query'
import { useAI } from '@/hooks/useAI'
import MarkdownRender from '@/components/MarkdownRender'
export default function ButtonModal() {
const { fieldOptions, getFieldName } = useDataFieldQuery()
......@@ -16,6 +18,63 @@ export default function ButtonModal() {
const formFunction = Form.useWatch('function', form)
const formSmFun = Form.useWatch('sm_fun', form)
const [result, setResult] = useState('')
const { isLoading, post } = useAI({
onComplete: (message) => {
setResult(message?.content || '')
},
})
const handleAI = async () => {
post({
messages: [
{
role: 'user',
content: `任务目标
分析指定字段的脱敏方法是否合理,包括 脱敏类型、遮蔽符号、遮蔽范围 的设置,并提供改进建议。
字段信息 :[${getFieldName(field)}]
脱敏参数 :
脱敏方法:${action}
遮蔽符号:${form.getFieldValue('char')}
遮蔽范围:${form.getFieldValue('sm_fun')}
其他规则 :${form.getFieldValue('char')}
输出格式:
| 序号 | 字段名称 | 当前脱敏方法 | 遮蔽符号 | 合理性判断 | 合理性判断依据 | 改进建议(格式:建议类型:描述,执行方式:自动/人工)
| 1 | 手机号 | 固定遮蔽 | * | 不合理 | 保留位数不足(仅最后2位可见) 建议类型:调整结束位数为-4,执行方式:自动
| 2 | 姓名 | 内容替换 | [敏感] | 合理 | 符合隐私保护要求 | 无需调整
执行要求
1.
合理性评估标准 :
隐私合规性 :是否符合数据保护法规(如GDPR、CCPA)。
信息可用性 :脱敏后是否仍能用于业务需求(如保留关键标识符)。
遮蔽范围合理性 :
部分遮蔽 :开始和结束位数是否有效(如不超出字段长度)。
全部遮蔽 :是否适用于无需保留任何信息的场景。
符号一致性 :遮蔽符号是否统一(如避免混用*和X)。`,
},
],
})
}
// 处理下一步按钮逻辑
const handleNext = async () => {
if (current === 0) {
......@@ -33,9 +92,7 @@ export default function ButtonModal() {
const handleStart = () => {
const values = form.getFieldsValue()
const params = { ...values, rules: JSON.stringify(values.rules) }
mutate(params, {
onSuccess: handleClose,
})
mutate(params)
}
// 关闭并重置
......@@ -43,6 +100,7 @@ export default function ButtonModal() {
setOpen(false)
setCurrent(0)
remove()
setResult('')
}
// 步骤定义
const steps = [
......@@ -82,12 +140,15 @@ export default function ButtonModal() {
<>
<Form.Item label="数据脱敏操作字段">{getFieldName(field)}</Form.Item>
<Form.Item name="action">
<Radio.Group
block
options={['基础脱敏方法', '高级脱敏方法', '特殊脱敏方法']}
optionType="button"
buttonStyle="solid"
/>
<Flex align="center" justify="center" style={{ marginBottom: 20 }}>
<Form.Item name="action" noStyle>
<Radio.Group
options={['基础脱敏方法', '高级脱敏方法', '特殊脱敏方法']}
optionType="button"
buttonStyle="solid"
/>
</Form.Item>
</Flex>
</Form.Item>
{action === '基础脱敏方法' && (
<>
......@@ -131,14 +192,14 @@ export default function ButtonModal() {
{formFunction === '内容替换' && (
<>
<Form.Item label="请输入替换内容" name="char" rules={[{ required: true, message: '请输入' }]}>
<Form.Item label="请输入替换内容" name="sm_fun" rules={[{ required: true, message: '请输入' }]}>
<Input placeholder="请输入" />
</Form.Item>
</>
)}
{formFunction === '内容模糊' && (
<>
<Form.Item label="请输入模糊内容" name="char" rules={[{ required: true, message: '请输入' }]}>
<Form.Item label="请输入模糊内容" name="sm_fun" rules={[{ required: true, message: '请输入' }]}>
<Input placeholder="请输入" />
</Form.Item>
</>
......@@ -186,6 +247,19 @@ export default function ButtonModal() {
</Form.Item>
</>
)}
{result && (
<div
style={{
marginTop: 20,
background: '#f5f5f5',
padding: 20,
maxHeight: 300,
overflow: 'auto',
borderRadius: 10,
}}>
<MarkdownRender>{result}</MarkdownRender>
</div>
)}
</>
),
},
......@@ -222,7 +296,11 @@ export default function ButtonModal() {
open={open}
footer={
<Flex justify="center" gap={20}>
{current === 1 && <Button type="primary">AI智能建议</Button>}
{current === 1 && (
<Button type="primary" onClick={handleAI} loading={isLoading}>
AI智能建议
</Button>
)}
{current > 0 && <Button onClick={() => setCurrent(current - 1)}>上一步</Button>}
{current < steps.length - 1 && (
<Button type="primary" onClick={handleNext}>
......
......@@ -5,6 +5,8 @@ import AppProgressSteps from '@/components/AppProgressSteps'
import { useProcessData } from '../query'
import { MinusCircleOutlined } from '@ant-design/icons'
import { uniqBy } from 'lodash-es'
import { useAI } from '@/hooks/useAI'
import MarkdownRender from '@/components/MarkdownRender'
export default function ButtonModal() {
const { data = { list: [] } } = useDataQuery()
......@@ -33,6 +35,59 @@ export default function ButtonModal() {
}
}, [field, data.list])
const [result, setResult] = useState('')
const { isLoading, post } = useAI({
onComplete: (message) => {
setResult(message?.content || '')
},
})
const handleAI = async () => {
post({
messages: [
{
role: 'user',
content: `任务目标
分析指定字段的分组规则是否合理,包括 分组逻辑、数据分布、业务规则匹配 ,并提供改进建议。输入要求
字段信息 :[${getFieldName(field)}]
数据样本:${JSON.stringify(dataSource)}
分组参数 :
分组规则 (如"按年龄段分组:0-18、19-65、65+")。
分组依据 (如"按销售额分组为低/中/高")。
输出格式:
| 序号 | 分组名称 | 当前分组合理性 | 合理性判断依据 | 改进建议(格式:建议类型:描述,执行方式:自动/人工)
| 1 | 0-18岁 | 不合理 | 无数据落入此区间 | 建议类型:删除此分组,执行方式:人工
| 2 | 19-65岁 | 合理 | 数据分布均匀 | 无需调整
| 1 | 65+岁 | 不合理 | 包含极端值(如年龄300岁) | 建议类型:调整上限为120岁,执行方式:人工
执行要求
合理性评估标准 :
数据覆盖 :分组是否覆盖所有数据值,无遗漏或重叠。
业务匹配 :是否符合业务需求(如医疗分组需符合年龄段标准)。
数据分布均衡性 :分组内数据量是否合理(如避免空组或数据堆积)。
逻辑一致性 :分组边界是否合理(如"65+岁"是否包含65岁)。`,
},
],
})
}
// 处理下一步按钮逻辑
const handleNext = async () => {
if (current === 0) {
......@@ -60,9 +115,7 @@ export default function ButtonModal() {
})
),
}
mutate(params, {
onSuccess: handleClose,
})
mutate(params)
}
// 关闭并重置
......@@ -70,6 +123,7 @@ export default function ButtonModal() {
setOpen(false)
setCurrent(0)
remove()
setResult('')
}
const [groupName, setGroupName] = useState('')
// 步骤定义
......@@ -122,6 +176,7 @@ export default function ButtonModal() {
/>
<Button
type="primary"
disabled={!groupName}
onClick={() => {
if (!groupName) return
add({ name: groupName, fields: [] }) // 添加新分组
......@@ -136,7 +191,16 @@ export default function ButtonModal() {
<Form.Item {...restField} label={rules[name]?.name} style={{ flex: 1 }} key={key}>
<Flex gap={10}>
<Form.Item name={[name, 'fields']} noStyle>
<Select options={dataSource} mode="multiple" style={{ width: '60%' }} />
<Select
options={dataSource.filter((item) => {
return !rules.some(
(rule: any, index: number) => index !== name && rule.fields?.includes(item.value)
)
})}
mode="multiple"
allowClear
style={{ width: '60%' }}
/>
</Form.Item>
<MinusCircleOutlined onClick={() => remove(name)} />
</Flex>
......@@ -145,6 +209,19 @@ export default function ButtonModal() {
</>
)}
</Form.List>
{result && (
<div
style={{
marginTop: 20,
background: '#f5f5f5',
padding: 20,
maxHeight: 300,
overflow: 'auto',
borderRadius: 10,
}}>
<MarkdownRender>{result}</MarkdownRender>
</div>
)}
</>
),
},
......@@ -159,19 +236,10 @@ export default function ButtonModal() {
<AppProgressSteps
current={progress}
items={[
{
title: '新建字段',
},
{
title: '复制数据',
},
{
title: '数据分组处理',
},
{
title: '处理结果',
description: <>{message[3]}</>,
},
{ title: '新建字段' },
{ title: '复制数据' },
{ title: '数据分组处理' },
{ title: '处理结果', description: <>{message[3]}</> },
]}
/>
</Flex>
......@@ -190,7 +258,11 @@ export default function ButtonModal() {
open={open}
footer={
<Flex justify="center" gap={20}>
{current === 1 && <Button type="primary">AI智能建议</Button>}
{current === 1 && (
<Button type="primary" onClick={handleAI} loading={isLoading}>
AI智能建议
</Button>
)}
{current > 0 && <Button onClick={() => setCurrent(current - 1)}>上一步</Button>}
{current < steps.length - 1 && (
<Button type="primary" onClick={handleNext}>
......
......@@ -3,28 +3,21 @@ import { Button, Flex, Modal, Input, Form, Select } from 'antd'
import AppProgressSteps from '@/components/AppProgressSteps'
import FunctionInput from '@/components/data/FunctionInput'
import { useProcessData } from '../query'
import { useAI } from '@/hooks/useAI'
import MarkdownRender from '@/components/MarkdownRender'
const functionOptions = [
{ label: 'ABS:返回数字的绝对值', value: 'ABS' },
{ label: 'AVERAGE:计算数值的平均值', value: 'AVERAGE' },
{ label: 'CEILING:将数值向上舍入到指定基数的倍数', value: 'CEILING' },
{ label: 'COUNT:统计包含数字的单元格数量', value: 'COUNT' },
{ label: 'COUNTIF:统计满足条件的单元格数量', value: 'COUNTIF' },
{ label: 'FLOOR:将数值向下舍入到指定基数的倍数', value: 'FLOOR' },
{ label: 'INT:将数值向下舍入到最接近的整数', value: 'INT' },
{ label: 'MAX:返回一组数值中的最大值', value: 'MAX' },
{ label: 'MIN:返回一组数值中的最小值', value: 'MIN' },
{ label: 'MOD:返回两数相除的余数', value: 'MOD' },
{ label: 'POWER:计算数值的指定次幂', value: 'POWER' },
{ label: 'PRODUCT:计算所有参数的乘积', value: 'PRODUCT' },
{ label: 'ROUND:将数值四舍五入到指定小数位数', value: 'ROUND' },
{ label: 'ROUNDUP:将数值向上舍入到指定小数位数', value: 'ROUNDUP' },
{ label: 'ROUNDDOWN:将数值向下舍入到指定小数位数', value: 'ROUNDDOWN' },
{ label: 'SQRT:返回数值的平方根', value: 'SQRT' },
{ label: 'SUM:计算所有数值的总和', value: 'SUM' },
{ label: 'SUMIF:对满足条件的单元格求和', value: 'SUMIF' },
{ label: 'SUMPRODUCT:计算多个数组的对应元素乘积之和', value: 'SUMPRODUCT' },
{ label: 'TRUNC:截断数值的小数部分(不四舍五入)', value: 'TRUNC' },
{ label: 'AND:所有条件均为 TRUE 时返回 TRUE,否则返回 FALSE', value: 'AND' },
{ label: 'FALSE:直接返回逻辑值 FALSE', value: 'FALSE' },
{ label: 'IF:根据条件返回不同结果', value: 'IF' },
{ label: 'IFERROR:捕获错误并替换为指定值', value: 'IFERROR' },
{ label: 'IFNA:专门处理 #N/A 错误', value: 'IFNA' },
{ label: 'IFS:多条件分支判断', value: 'IFS' },
{ label: 'NOT:对逻辑值取反', value: 'NOT' },
{ label: 'OR:任一条件为 TRUE 时返回 TRUE', value: 'OR' },
{ label: 'SWITCH:根据表达式匹配值返回对应结果', value: 'SWITCH' },
{ label: 'TRUE:直接返回逻辑值 TRUE', value: 'TRUE' },
{ label: 'XOR:异或逻辑,仅当奇数个条件为 TRUE 时返回 TRUE', value: 'XOR' },
]
export default function ButtonModal() {
......@@ -32,6 +25,44 @@ export default function ButtonModal() {
const [current, setCurrent] = useState(0)
const [form] = Form.useForm()
const [result, setResult] = useState('')
const { isLoading, post } = useAI({
onComplete: (message) => {
setResult(message?.content || '')
},
})
const handleAI = async () => {
const values = form.getFieldsValue()
const selectedFunction = values.function
const functionFormula = values.content || ''
post({
messages: [
{
role: 'user',
content: `请检查以下逻辑计算函数公式是否正确,并提供改进建议:
1. 当前选择的函数:${selectedFunction}
2. 输入的公式:${functionFormula}
请按照以下格式回答:
1. 语法检查:检查函数语法是否正确,包括括号匹配、参数格式等
2. 参数检查:检查参数类型和数量是否符合函数要求
3. 逻辑检查:检查函数逻辑是否合理,是否符合预期用途
4. 改进建议:如果有问题,请提供具体的改进建议
5. 示例:提供一个正确的使用示例
注意:
- 请确保检查结果清晰易懂
- 如果公式完全正确,请明确指出
- 如果发现潜在问题,请详细说明原因
- 提供的示例应该与当前使用场景相关`,
},
],
})
}
// 处理下一步按钮逻辑
const handleNext = async () => {
if (current === 0) {
......@@ -54,9 +85,7 @@ export default function ButtonModal() {
const fieldsMatch = content.match(/\((.*?)\)/) // 匹配括号内的内容
const fields = fieldsMatch ? fieldsMatch[1].split(',').map((field: string) => field.trim()) : []
const params = { ...values, fields: JSON.stringify(fields) } // 将提取的参数赋值给 fields
mutate(params, {
onSuccess: handleClose,
})
mutate(params)
}
// 关闭并重置
......@@ -64,6 +93,7 @@ export default function ButtonModal() {
setOpen(false)
setCurrent(0)
remove()
setResult('')
}
const handleFunctionChange = (value: string) => {
form.setFieldsValue({ content: `${value}()` })
......@@ -97,6 +127,19 @@ export default function ButtonModal() {
<Form.Item label="请输入函数公式" name="content" rules={[{ required: true, message: '请输入函数公式' }]}>
<FunctionInput fieldType="string" />
</Form.Item>
{result && (
<div
style={{
marginTop: 20,
background: '#f5f5f5',
padding: 20,
maxHeight: 300,
overflow: 'auto',
borderRadius: 10,
}}>
<MarkdownRender>{result}</MarkdownRender>
</div>
)}
</>
),
},
......@@ -126,7 +169,11 @@ export default function ButtonModal() {
const Footer = () => {
return (
<Flex justify="center" gap={20}>
{current === 1 && <Button type="primary">AI检查函数公式</Button>}
{current === 1 && (
<Button type="primary" onClick={handleAI} loading={isLoading}>
AI检查函数公式
</Button>
)}
{current > 0 && <Button onClick={() => setCurrent(current - 1)}>上一步</Button>}
{current < steps.length - 1 && (
<Button type="primary" onClick={handleNext}>
......
......@@ -4,16 +4,18 @@ import { useDataQuery, useDataFieldQuery } from '@/hooks/useQuery'
import AppProgressSteps from '@/components/AppProgressSteps'
import { uniqBy } from 'lodash-es'
import { useProcessData } from '../query'
import { useAI } from '@/hooks/useAI'
import MarkdownRender from '@/components/MarkdownRender'
export default function ButtonModal() {
const { data = { list: [] } } = useDataQuery()
const { fieldOptions, getFieldName } = useDataFieldQuery()
const [open, setOpen] = useState(false)
const [current, setCurrent] = useState(0)
const [form] = Form.useForm()
const [dataSource, setDataSource] = useState<{ key: number; raw_value: string; new_value: string }[]>([])
const name = Form.useWatch('name', form)
const field = Form.useWatch('field', form)
useEffect(() => {
......@@ -36,6 +38,55 @@ export default function ButtonModal() {
)
}
const [result, setResult] = useState('')
const { isLoading, post } = useAI({
onComplete: (message) => {
setResult(message?.content || '')
},
})
const handleAI = async () => {
post({
messages: [
{
role: 'user',
content: `任务目标
分析指定字段值与目标字段的映射关系是否合理,并提供改进建议。
输入要求
1.
字段列表 :[${getFieldName(field)}${name}]
2.
数据样本:${JSON.stringify(dataSource)}
输出格式:
| 序号 | 字段对(源→目标) | 映射问题描述 | 合理性判断 | 改进建议(格式:建议名称:描述,执行方式:自动/人工)
| 1 | 省份→城市 | 省份为“北京”,城市为“上海” | 不合理 | 建议类型:修正城市为“北京”,执行方式:自动
| 2 | 订单状态→物流单号 | 状态为“已发货”,物流单号为空 | 不合理 | 建议类型:标记为异常需人工核查,执行方式:人工
执行要求
1.
合理性判断标准 :
逻辑一致性 :
检查字段值是否符合业务关联(如“省份→城市”是否匹配)。
检查数值范围是否合理(如“折扣率应≤100%”)。
业务规则匹配 :
若用户提供规则(如“性别字段应为“男/女”),则优先校验。
数据分布异常 :
检测是否存在矛盾值(如“年龄20岁,职业为退休人员”)。`,
},
],
})
}
// 处理下一步按钮逻辑
const handleNext = async () => {
if (current === 0) {
......@@ -54,9 +105,7 @@ export default function ButtonModal() {
return { ...result, [item.raw_value]: item.new_value }
}, {})
const params = { ...values, rule: JSON.stringify(rule) }
mutate(params, {
onSuccess: handleClose,
})
mutate(params)
}
// 关闭并重置
......@@ -64,6 +113,7 @@ export default function ButtonModal() {
setOpen(false)
setCurrent(0)
remove()
setResult('')
}
// 步骤定义
const steps = [
......@@ -102,7 +152,7 @@ export default function ButtonModal() {
<Form.Item>
<Table
bordered
pagination={{ pageSize: 10 }}
pagination={{ pageSize: 10, hideOnSinglePage: true }}
columns={[
{ title: '序号', dataIndex: 'key', align: 'center', width: 80 },
{ title: '原始值', dataIndex: 'raw_value', align: 'center' },
......@@ -122,6 +172,19 @@ export default function ButtonModal() {
dataSource={dataSource}
/>
</Form.Item>
{result && (
<div
style={{
marginTop: 20,
background: '#f5f5f5',
padding: 20,
maxHeight: 300,
overflow: 'auto',
borderRadius: 10,
}}>
<MarkdownRender>{result}</MarkdownRender>
</div>
)}
</>
),
},
......@@ -158,7 +221,11 @@ export default function ButtonModal() {
open={open}
footer={
<Flex justify="center" gap={20}>
{current === 1 && <Button type="primary">AI智能建议</Button>}
{current === 1 && (
<Button type="primary" onClick={handleAI} loading={isLoading}>
AI智能建议
</Button>
)}
{current > 0 && <Button onClick={() => setCurrent(current - 1)}>上一步</Button>}
{current < steps.length - 1 && (
<Button type="primary" onClick={handleNext}>
......
......@@ -3,6 +3,8 @@ import { Button, Flex, Modal, Input, Form, Select } from 'antd'
import AppProgressSteps from '@/components/AppProgressSteps'
import FunctionInput from '@/components/data/FunctionInput'
import { useProcessData } from '../query'
import { useAI } from '@/hooks/useAI'
import MarkdownRender from '@/components/MarkdownRender'
const functionOptions = [
{ label: 'ABS:返回数字的绝对值', value: 'ABS' },
......@@ -32,6 +34,43 @@ export default function ButtonModal() {
const [current, setCurrent] = useState(0)
const [form] = Form.useForm()
const [result, setResult] = useState('')
const { isLoading, post } = useAI({
onComplete: (message) => {
setResult(message?.content || '')
},
})
const handleAI = async () => {
const values = form.getFieldsValue()
const selectedFunction = values.function
const functionFormula = values.content || ''
post({
messages: [
{
role: 'user',
content: `请检查以下数值计算函数公式是否正确,并提供改进建议:
1. 当前选择的函数:${selectedFunction}
2. 输入的公式:${functionFormula}
请按照以下格式回答:
1. 语法检查:检查函数语法是否正确,包括括号匹配、参数格式等
2. 参数检查:检查参数类型和数量是否符合函数要求
3. 逻辑检查:检查函数逻辑是否合理,是否符合预期用途
4. 改进建议:如果有问题,请提供具体的改进建议
5. 示例:提供一个正确的使用示例
注意:
- 请确保检查结果清晰易懂
- 如果公式完全正确,请明确指出
- 如果发现潜在问题,请详细说明原因
- 提供的示例应该与当前使用场景相关`,
},
],
})
}
// 处理下一步按钮逻辑
const handleNext = async () => {
if (current === 0) {
......@@ -54,9 +93,7 @@ export default function ButtonModal() {
const fieldsMatch = content.match(/\((.*?)\)/) // 匹配括号内的内容
const fields = fieldsMatch ? fieldsMatch[1].split(',').map((field: string) => field.trim()) : []
const params = { ...values, fields: JSON.stringify(fields) } // 将提取的参数赋值给 fields
mutate(params, {
onSuccess: handleClose,
})
mutate(params)
}
// 关闭并重置
......@@ -64,6 +101,7 @@ export default function ButtonModal() {
setOpen(false)
setCurrent(0)
remove()
setResult('')
}
const handleFunctionChange = (value: string) => {
form.setFieldsValue({ content: `${value}()` })
......@@ -97,6 +135,19 @@ export default function ButtonModal() {
<Form.Item label="请输入函数公式" name="content" rules={[{ required: true, message: '请输入函数公式' }]}>
<FunctionInput fieldType="string" />
</Form.Item>
{result && (
<div
style={{
marginTop: 20,
background: '#f5f5f5',
padding: 20,
maxHeight: 300,
overflow: 'auto',
borderRadius: 10,
}}>
<MarkdownRender>{result}</MarkdownRender>
</div>
)}
</>
),
},
......@@ -126,7 +177,11 @@ export default function ButtonModal() {
const Footer = () => {
return (
<Flex justify="center" gap={20}>
{current === 1 && <Button type="primary">AI检查函数公式</Button>}
{current === 1 && (
<Button type="primary" onClick={handleAI} loading={isLoading}>
AI检查函数公式
</Button>
)}
{current > 0 && <Button onClick={() => setCurrent(current - 1)}>上一步</Button>}
{current < steps.length - 1 && (
<Button type="primary" onClick={handleNext}>
......
......@@ -3,6 +3,8 @@ import { Button, Flex, Modal, Input, Form, Select } from 'antd'
import AppProgressSteps from '@/components/AppProgressSteps'
import FunctionInput from '@/components/data/FunctionInput'
import { useProcessData } from '../query'
import { useAI } from '@/hooks/useAI'
import MarkdownRender from '@/components/MarkdownRender'
const functionOptions = [
{ label: 'CHAR:返回 ASCII 码对应的字符', value: 'CHAR' },
......@@ -46,6 +48,43 @@ export default function ButtonModal() {
const { mutate, isPending, progress, message, remove } = useProcessData()
const [result, setResult] = useState('')
const { isLoading, post } = useAI({
onComplete: (message) => {
setResult(message?.content || '')
},
})
const handleAI = async () => {
const values = form.getFieldsValue()
const selectedFunction = values.function
const functionFormula = values.content || ''
post({
messages: [
{
role: 'user',
content: `请检查以下文本计算函数公式是否正确,并提供改进建议:
1. 当前选择的函数:${selectedFunction}
2. 输入的公式:${functionFormula}
请按照以下格式回答:
1. 语法检查:检查函数语法是否正确,包括括号匹配、参数格式等
2. 参数检查:检查参数类型和数量是否符合函数要求
3. 逻辑检查:检查函数逻辑是否合理,是否符合预期用途
4. 改进建议:如果有问题,请提供具体的改进建议
5. 示例:提供一个正确的使用示例
注意:
- 请确保检查结果清晰易懂
- 如果公式完全正确,请明确指出
- 如果发现潜在问题,请详细说明原因
- 提供的示例应该与当前使用场景相关`,
},
],
})
}
// 开始处理
const handleStart = () => {
const values = form.getFieldsValue()
......@@ -55,9 +94,7 @@ export default function ButtonModal() {
const fieldsMatch = content.match(/\((.*?)\)/) // 匹配括号内的内容
const fields = fieldsMatch ? fieldsMatch[1].split(',').map((field: string) => field.trim()) : []
const params = { ...values, fields: JSON.stringify(fields) } // 将提取的参数赋值给 fields
mutate(params, {
onSuccess: handleClose,
})
mutate(params)
}
// 关闭并重置
......@@ -65,6 +102,7 @@ export default function ButtonModal() {
setOpen(false)
setCurrent(0)
remove()
setResult('')
}
const handleFunctionChange = (value: string) => {
......@@ -99,6 +137,19 @@ export default function ButtonModal() {
<Form.Item label="请输入函数公式" name="content" rules={[{ required: true, message: '请输入函数公式' }]}>
<FunctionInput fieldType="string" />
</Form.Item>
{result && (
<div
style={{
marginTop: 20,
background: '#f5f5f5',
padding: 20,
maxHeight: 300,
overflow: 'auto',
borderRadius: 10,
}}>
<MarkdownRender>{result}</MarkdownRender>
</div>
)}
</>
),
},
......@@ -128,7 +179,11 @@ export default function ButtonModal() {
const Footer = () => {
return (
<Flex justify="center" gap={20}>
{current === 1 && <Button type="primary">AI检查函数公式</Button>}
{current === 1 && (
<Button type="primary" onClick={handleAI} loading={isLoading}>
AI检查函数公式
</Button>
)}
{current > 0 && <Button onClick={() => setCurrent(current - 1)}>上一步</Button>}
{current < steps.length - 1 && (
<Button type="primary" onClick={handleNext}>
......
......@@ -12,7 +12,19 @@ export default function DataWriteUpload() {
messages: [
{
role: 'user',
content: '请仔细阅读我给你的数据集里面的数据,然后帮助我详细解释一下数据集里面每一个字段的含义。',
content: `请基于提供的数据集,为每个字段生成结构化说明,需包含以下要素:
字段名称及定义
数据类型(如字符串/数值/时间戳等)
示例值展示
业务场景中的实际含义
数据采集来源说明(如可识别)
请以表格形式呈现,重点字段用★标注,并指出可能存在的数据异常情况`,
},
],
})
......@@ -21,7 +33,19 @@ export default function DataWriteUpload() {
messages: [
{
role: 'user',
content: '请仔细阅读我给你的数据集里面的数据,然后帮助我梳理数据集里面字段与字段之间的业务逻辑关系。',
content: `作为数据分析专家,请基于提供的数据集,系统分析字段间的业务逻辑关系,
需包含:
1、核心业务流程图(订单-支付-物流-售后全链路)
2、关键字段的依赖关系(如:单价×销量=销售额)
3、业务规则验证(如优惠金额与实际付款的校验逻辑)
4、异常数据关联模式(如交易状态与物流状态矛盾案例)
【输出要求】 语言:中文口语化,禁用复杂术语`,
},
],
})
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论