提交 29c52a95 authored 作者: 王鹏飞's avatar 王鹏飞

chore: update

上级 89430342
import { useDataQuery } from '@/hooks/useQuery'
import { Column, Line, Pie, Radar, Scatter, WordCloud } from '@ant-design/plots'
export default function Chart({ type = '1', ...props }) {
const { data } = useDataQuery()
switch (type) {
case '1':
return <Column data={data.list} {...props} />
case '2': {
const defaultConfig = {
data: data.list,
point: { shapeField: 'point', sizeField: 4 },
style: { lineWidth: 2 },
}
return <Line {...defaultConfig} {...props} />
}
case '3': {
const defaultConfig = {
data: data.list,
angleField: props.yField,
colorField: props.xField,
}
return <Pie {...defaultConfig} />
}
case '4':
return <Radar data={data.list} {...props} />
case '5':
return <Scatter data={data.list} {...props} />
case '7':
return <WordCloud data={data.list} {...props} />
default:
return <Column data={data.list} {...props} />
}
}
import { useState } from 'react'
import { useEffect, useState } from 'react'
import { Button, Flex, Modal, Form, Divider, Select, Radio, Row, Col, Input } from 'antd'
import { useDataQuery, useDataFieldQuery } from '@/hooks/useQuery'
import { useCreateChart } from '@/hooks/useChartQuery'
import { useDataFieldQuery } from '@/hooks/useQuery'
import { useCreateChart, useUpdateChart, useViewChartQuery } from '@/hooks/useChartQuery'
import { useAI } from '@/hooks/useAI'
import { Column } from '@ant-design/plots'
import Chart from './Chart'
interface Props {
id?: string
type: string
setOpen: (open: boolean) => void
}
const ModalContent = ({ setOpen, type }: Props) => {
const { data } = useDataQuery()
const ModalContent = ({ setOpen, type, id = '' }: Props) => {
const { fieldOptions } = useDataFieldQuery()
const { data: chartData } = useViewChartQuery(id)
const [form] = Form.useForm()
useEffect(() => {
if (id && chartData) {
try {
const parsedContent = JSON.parse(chartData.content)
form.setFieldsValue({ ...parsedContent.form })
} catch (e) {
console.error('解析图表数据失败:', e)
}
}
}, [id, chartData, form])
const [results, setResults] = useState({})
const xField = Form.useWatch('x', form)
const yField = Form.useWatch('y', form)
const colorField = Form.useWatch('colorField', form)
const title = Form.useWatch('title', form)
const config = { data: data.list, xField, yField, colorField: xField, title, ...results }
const config = { xField, yField, colorField, title, ...results }
const { post } = useAI({
onComplete: (message) => {
......@@ -59,17 +72,28 @@ const ModalContent = ({ setOpen, type }: Props) => {
})
}
const { mutate } = useCreateChart()
const { mutate: mutateCreate } = useCreateChart()
const { mutate: mutateUpdate } = useUpdateChart()
// 保存
const handleSubmit = () => {
form.validateFields().then((values) => {
const params = { ...values, type, content: JSON.stringify({ config }) }
mutate(params, {
onSuccess: () => setOpen(false),
})
const params = { ...values, type, content: JSON.stringify({ form: values, config }) }
if (id) {
mutateUpdate(
{ id, ...params },
{
onSuccess: () => setOpen(false),
}
)
} else {
mutateCreate(params, {
onSuccess: () => setOpen(false),
})
}
})
}
return (
<>
<Form
......@@ -84,11 +108,11 @@ const ModalContent = ({ setOpen, type }: Props) => {
has_title: '无',
fill_image: '纯色',
}}>
<Form.Item label="组件名称" name="name">
<Form.Item label="组件名称" name="name" rules={[{ required: true, message: '请输入组件名称' }]}>
<Input placeholder="请输入" />
</Form.Item>
<Divider orientation="left" orientationMargin="0">
步骤1:数字字段设置
数字字段设置
</Divider>
<Row gutter={20}>
<Col span={8}>
......@@ -127,17 +151,17 @@ const ModalContent = ({ setOpen, type }: Props) => {
</Col>
</Row>
<Divider orientation="left" orientationMargin="0">
步骤2:辅助可视化设置
辅助可视化设置
</Divider>
<Row gutter={20}>
<Col span={12}>
<Form.Item label="请选择“标签”字段" name="label">
<Form.Item label="请选择“标签”字段" name="colorField">
<Select options={fieldOptions} placeholder="请选择"></Select>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="请选择颜色规则" name="fill_color">
<Radio.Group options={['自动颜色', '不同柱子颜色不同']}></Radio.Group>
<Radio.Group options={['自动颜色']}></Radio.Group>
</Form.Item>
</Col>
</Row>
......@@ -171,9 +195,9 @@ const ModalContent = ({ setOpen, type }: Props) => {
</Col>
</Row>
<Divider orientation="left" orientationMargin="0">
步骤3:预览组件效果
预览组件效果
</Divider>
{xField && yField && <Column {...config} />}
<Chart type={type} {...config} />
</Form>
<Flex justify="center" gap={20}>
......@@ -189,15 +213,16 @@ const ModalContent = ({ setOpen, type }: Props) => {
)
}
export default function ButtonModal({ title = '新建柱状图', type = '1' }) {
export default function ButtonModal({ type = '1', id = '' }) {
const [open, setOpen] = useState(false)
const title = id ? '编辑' : '新建'
return (
<>
<Button type="primary" onClick={() => setOpen(true)}>
<Button color="primary" variant={id ? 'text' : 'solid'} onClick={() => setOpen(true)}>
{title}
</Button>
<Modal title={title} open={open} footer={null} destroyOnClose width={1000} onCancel={() => setOpen(false)}>
<ModalContent type={type} setOpen={(open) => setOpen(open)} />
<ModalContent type={type} setOpen={(open) => setOpen(open)} id={id} />
</Modal>
</>
)
......
import { Button, Card, Flex } from 'antd'
import { ReactNode } from 'react'
import { lazy, ReactNode } from 'react'
import AppList, { AppListProps } from '@/components/AppList'
import ViewDataButtonModal from '../data/ViewMyDataButtonModal'
import ViewChartButtonModal from './ViewChartButtonModal'
import { getChartComponentList } from '@/api/data'
import { useDeleteChart } from '@/hooks/useChartQuery'
const chartType: any = {
1: '柱状图',
2: '折线图',
3: '饼状图',
4: '雷达图',
5: '散点图',
6: '气泡图',
7: '词云',
8: '地图',
9: '指标卡',
10: '漏斗图',
11: '直方图',
12: '表格',
13: '帕累托图',
14: '矩形树图',
}
import { CHART_TYPE } from '@/utils/constants'
const ButtonModal = lazy(() => import('@/components/chart/ChartButtonModal'))
export default function DataWrap({
title,
buttons,
type,
}: {
type: string
title: string
buttons: ReactNode
buttons?: ReactNode
children?: ReactNode
}) {
const { mutate } = useDeleteChart()
......@@ -58,8 +42,8 @@ export default function DataWrap({
title: '组件类型',
dataIndex: 'type',
align: 'center',
render(value) {
return chartType[value] || value
render(value: keyof typeof CHART_TYPE) {
return CHART_TYPE[value] || value
},
},
{ title: '组件名称', dataIndex: 'name', align: 'center' },
......@@ -75,9 +59,7 @@ export default function DataWrap({
return (
<>
<ViewChartButtonModal id={record.id}></ViewChartButtonModal>
<Button color="primary" variant="text">
编辑
</Button>
<ButtonModal type={type} id={record.id}></ButtonModal>
<Button color="danger" variant="text" onClick={() => handleRemove(record)}>
删除
</Button>
......@@ -92,7 +74,7 @@ export default function DataWrap({
<Card className="app-card" title={title} style={{ flex: 1, overflowX: 'hidden' }}>
<Flex justify="space-between" style={{ marginBottom: '20px' }}>
<Flex wrap gap={10}>
{buttons}
<ButtonModal type={type}></ButtonModal>
</Flex>
<ViewDataButtonModal></ViewDataButtonModal>
</Flex>
......
import { Button, Modal } from 'antd'
import AppList, { AppListProps } from '@/components/AppList'
import { getChartComponentList } from '@/api/data'
import { CHART_TYPE } from '@/utils/constants'
import { useState, useEffect } from 'react'
import ViewChartButtonModal from './ViewChartButtonModal'
interface SelectChartProps {
type?: string
value?: any[]
onChange?: (value: any[]) => void
}
export default function SelectChart({ type, value = [], onChange }: SelectChartProps) {
const [open, setOpen] = useState(false)
const [selectedRows, setSelectedRows] = useState<any[]>([])
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([])
const [dataSource, setDataSource] = useState<any[]>(value)
useEffect(() => {
setDataSource(value)
}, [value])
const listOptions: AppListProps = {
queryKey: ['chartComponentList', { type }],
fetchApi: async (params) => {
const { data } = await getChartComponentList({ ...params, type })
return { ...data }
},
columns: [
{
title: '序号',
key: 'index',
render(_value, _record, index) {
return index + 1
},
width: 62,
align: 'center',
},
{
title: '组件类型',
dataIndex: 'type',
align: 'center',
render(value: keyof typeof CHART_TYPE) {
return CHART_TYPE[value] || value
},
},
{ title: '组件名称', dataIndex: 'name', align: 'center' },
{ title: '创建人', dataIndex: 'created_operator_name', align: 'center' },
{ title: '创建时间', dataIndex: 'created_time', align: 'center' },
{ title: '更新时间', dataIndex: 'updated_time', align: 'center' },
],
rowSelection: {
type: 'checkbox',
selectedRowKeys,
onChange: (keys: React.Key[], rows: any[]) => {
setSelectedRowKeys(keys)
setSelectedRows(rows)
},
getCheckboxProps: (record: any) => ({
disabled: dataSource.some((item) => item.id === record.id),
}),
},
}
const removeDuplicates = (originalArray: any[], newArray: any[]) => {
const uniqueArray = [...originalArray]
newArray.forEach((newItem) => {
const isDuplicate = uniqueArray.some((item) => item.id === newItem.id)
if (!isDuplicate) {
uniqueArray.push(newItem)
}
})
return uniqueArray
}
const handleRemove = (record: any) => {
const newDataSource = dataSource.filter((item) => item.id !== record.id)
setDataSource(newDataSource)
onChange?.(newDataSource)
}
const handleOk = () => {
const newDataSource = removeDuplicates(dataSource, selectedRows)
setDataSource(newDataSource)
onChange?.(newDataSource)
setSelectedRows([])
setSelectedRowKeys([])
setOpen(false)
}
const selectListOptions: AppListProps = {
dataSource,
columns: [
{
title: '序号',
key: 'index',
render(_value, _record, index) {
return index + 1
},
width: 62,
align: 'center',
},
{
title: '组件类型',
dataIndex: 'type',
align: 'center',
render(value: keyof typeof CHART_TYPE) {
return CHART_TYPE[value] || value
},
},
{ title: '组件名称', dataIndex: 'name', align: 'center' },
{
title: '操作',
key: 'x',
width: 220,
align: 'center',
render(_value, record) {
return (
<>
<ViewChartButtonModal id={record.id}></ViewChartButtonModal>
<Button color="danger" variant="text" onClick={() => handleRemove(record)}>
删除
</Button>
</>
)
},
},
],
}
return (
<>
<Button type="primary" onClick={() => setOpen(true)} style={{ marginBottom: 20 }}>
添加可视化组件
</Button>
<AppList {...selectListOptions} style={{ marginBottom: 20 }} />
<Modal title="添加可视化组件" width="1000px" open={open} onCancel={() => setOpen(false)} onOk={handleOk}>
<AppList {...listOptions} />
</Modal>
</>
)
}
import { useState } from 'react'
import { Button, Modal } from 'antd'
import { Column } from '@ant-design/plots'
import { useViewChartQuery } from '@/hooks/useChartQuery'
import Chart from './Chart'
export default function ViewDataButtonModal({ id }: { id: string }) {
const [open, setOpen] = useState(false)
......@@ -34,7 +34,7 @@ function ModalContent({ id }: { id: string }) {
return (
<div>
<Column {...config} />
<Chart type={data.type} {...config} />
</div>
)
}
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { createChartComponent, deleteChartComponent, getChartComponent } from '@/api/data'
import { createChartComponent, updateChartComponent, deleteChartComponent, getChartComponent } from '@/api/data'
import { message } from 'antd'
// 创建
......@@ -15,6 +15,19 @@ export function useCreateChart() {
})
}
// 编辑
export function useUpdateChart() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: (data: { id: string; name: string; type: string; content: string }) => updateChartComponent(data),
onSuccess: () => {
message.success('编辑成功')
queryClient.invalidateQueries({ queryKey: ['chartComponentList'] })
},
})
}
// 删除
export function useDeleteChart() {
const queryClient = useQueryClient()
......@@ -36,5 +49,7 @@ export function useViewChartQuery(id: string) {
return getChartComponent({ id })
},
select: (res) => res.data,
enabled: !!id,
staleTime: 0,
})
}
import { lazy } from 'react'
import ChartWrap from '@/components/chart/ChartWrap'
const ButtonModal = lazy(() => import('@/components/chart/ChartButtonModal'))
export default function DataProcess() {
return <ChartWrap title="可视化:柱状图" type="1" buttons={<ButtonModal title="新建柱状图" type="1" />}></ChartWrap>
return <ChartWrap title="可视化:柱状图" type="1"></ChartWrap>
}
import { lazy } from 'react'
import ChartWrap from '@/components/chart/ChartWrap'
const ButtonModal = lazy(() => import('@/components/chart/ChartButtonModal'))
export default function DataProcess() {
return <ChartWrap title="可视化:气泡图" type="6" buttons={<ButtonModal title="新建气泡图" type="6" />}></ChartWrap>
return <ChartWrap title="可视化:气泡图" type="6"></ChartWrap>
}
import { lazy } from 'react'
import ChartWrap from '@/components/chart/ChartWrap'
const ButtonModal = lazy(() => import('@/components/chart/ChartButtonModal'))
export default function DataProcess() {
return <ChartWrap title="可视化:漏斗图" type="10" buttons={<ButtonModal title="新建漏斗图" type="10" />}></ChartWrap>
return <ChartWrap title="可视化:漏斗图" type="10"></ChartWrap>
}
import { lazy } from 'react'
import ChartWrap from '@/components/chart/ChartWrap'
const ButtonModal = lazy(() => import('@/components/chart/ChartButtonModal'))
export default function DataProcess() {
return <ChartWrap title="可视化:直方图" type="11" buttons={<ButtonModal title="新建直方图" type="11" />}></ChartWrap>
return <ChartWrap title="可视化:直方图" type="11"></ChartWrap>
}
import { lazy } from 'react'
import ChartWrap from '@/components/chart/ChartWrap'
const ButtonModal = lazy(() => import('@/components/chart/ChartButtonModal'))
export default function DataProcess() {
return <ChartWrap title="可视化:指标卡" type="9" buttons={<ButtonModal title="新建指标卡" type="9" />}></ChartWrap>
return <ChartWrap title="可视化:指标卡" type="9"></ChartWrap>
}
import { lazy } from 'react'
import ChartWrap from '@/components/chart/ChartWrap'
const ButtonModal = lazy(() => import('@/components/chart/ChartButtonModal'))
export default function DataProcess() {
return <ChartWrap title="可视化:折线图" type="2" buttons={<ButtonModal title="新建折线图" type="2" />}></ChartWrap>
return <ChartWrap title="可视化:折线图" type="2"></ChartWrap>
}
import { lazy } from 'react'
import ChartWrap from '@/components/chart/ChartWrap'
const ButtonModal = lazy(() => import('@/components/chart/ChartButtonModal'))
export default function DataProcess() {
return <ChartWrap title="可视化:地图" type="8" buttons={<ButtonModal title="新建地图" type="8" />}></ChartWrap>
return <ChartWrap title="可视化:地图" type="8"></ChartWrap>
}
import { lazy } from 'react'
import ChartWrap from '@/components/chart/ChartWrap'
const ButtonModal = lazy(() => import('@/components/chart/ChartButtonModal'))
export default function DataProcess() {
return (
<ChartWrap title="可视化:帕累托图" type="13" buttons={<ButtonModal title="新建帕累托图" type="13" />}></ChartWrap>
)
return <ChartWrap title="可视化:帕累托图" type="13"></ChartWrap>
}
import { lazy } from 'react'
import ChartWrap from '@/components/chart/ChartWrap'
const ButtonModal = lazy(() => import('@/components/chart/ChartButtonModal'))
export default function DataProcess() {
return <ChartWrap title="可视化:饼状图" type="3" buttons={<ButtonModal title="新建饼状图" type="3" />}></ChartWrap>
return <ChartWrap title="可视化:饼状图" type="3"></ChartWrap>
}
import { lazy } from 'react'
import ChartWrap from '@/components/chart/ChartWrap'
const ButtonModal = lazy(() => import('@/components/chart/ChartButtonModal'))
export default function DataProcess() {
return <ChartWrap title="可视化:散点图" type="5" buttons={<ButtonModal title="新建散点图" type="5" />}></ChartWrap>
return <ChartWrap title="可视化:散点图" type="5"></ChartWrap>
}
import { lazy } from 'react'
import ChartWrap from '@/components/chart/ChartWrap'
const ButtonModal = lazy(() => import('@/components/chart/ChartButtonModal'))
export default function DataProcess() {
return <ChartWrap title="可视化:雷达图" type="4" buttons={<ButtonModal title="新建雷达图" type="4" />}></ChartWrap>
return <ChartWrap title="可视化:雷达图" type="4"></ChartWrap>
}
import { lazy } from 'react'
import ChartWrap from '@/components/chart/ChartWrap'
const ButtonModal = lazy(() => import('@/components/chart/ChartButtonModal'))
export default function DataProcess() {
return <ChartWrap title="可视化:表格" type="12" buttons={<ButtonModal title="新建表格" type="12" />}></ChartWrap>
return <ChartWrap title="可视化:表格" type="12"></ChartWrap>
}
import { lazy } from 'react'
import ChartWrap from '@/components/chart/ChartWrap'
const ButtonModal = lazy(() => import('@/components/chart/ChartButtonModal'))
export default function DataProcess() {
return (
<ChartWrap title="可视化:矩形树图" type="14" buttons={<ButtonModal title="新建矩形树图" type="14" />}></ChartWrap>
)
return <ChartWrap title="可视化:矩形树图" type="14"></ChartWrap>
}
import { lazy } from 'react'
import ChartWrap from '@/components/chart/ChartWrap'
const ButtonModal = lazy(() => import('@/components/chart/ChartButtonModal'))
export default function DataProcess() {
return <ChartWrap title="可视化:词云" type="7" buttons={<ButtonModal title="新建词云" type="7" />}></ChartWrap>
return <ChartWrap title="可视化:词云" type="7"></ChartWrap>
}
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { createLargeScreen, deleteLargeScreen } from './api'
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { createLargeScreen, deleteLargeScreen, getLargeScreen, updateLargeScreen } from './api'
import { message } from 'antd'
// 创建
......@@ -15,6 +15,18 @@ export function useCreateLargeScreen() {
})
}
// 更新
export function useUpdateLargeScreen() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: (data: { id: string; name: string; type: string; content: string }) => updateLargeScreen(data),
onSuccess: () => {
message.success('更新成功')
queryClient.invalidateQueries({ queryKey: ['largeScreenList'] })
},
})
}
// 删除
export function useDeleteLargeScreen() {
const queryClient = useQueryClient()
......@@ -27,3 +39,13 @@ export function useDeleteLargeScreen() {
},
})
}
export function useViewLargeScreen(id: string) {
return useQuery({
queryKey: ['largeScreen', id],
queryFn: () => getLargeScreen({ id }),
select: (res) => res.data,
enabled: !!id,
staleTime: 0,
})
}
import { lazy } from 'react'
import type { RouteObject } from 'react-router'
export const routes: RouteObject[] = [
{
path: '/data/screen/view/:id',
Component: lazy(() => import('./views/View')),
},
]
......@@ -4,6 +4,7 @@ import ViewDataButtonModal from '@/components/data/ViewMyDataButtonModal'
import ButtonModal from '../components/ButtonModal'
import { getLargeScreenList } from '../api'
import { useDeleteLargeScreen } from '../query'
import { Link } from 'react-router'
export default function DataWrap() {
const { mutate } = useDeleteLargeScreen()
......@@ -39,12 +40,12 @@ export default function DataWrap() {
render(_value, record) {
return (
<>
<Button color="primary" variant="text">
查看
</Button>
<Button color="primary" variant="text">
编辑
</Button>
<Link to={`/data/screen/view/${record.id}`} target="_blank">
<Button color="primary" variant="text">
查看
</Button>
</Link>
<ButtonModal id={record.id}></ButtonModal>
<Button color="danger" variant="text" onClick={() => handleRemove(record)}>
删除
</Button>
......@@ -56,7 +57,7 @@ export default function DataWrap() {
}
return (
<Flex gap={20} style={{ height: '100%' }}>
<Card className="app-card" style={{ flex: 1, overflowX: 'hidden' }}>
<Card className="app-card" title="数据可视化大屏" style={{ flex: 1, overflowX: 'hidden' }}>
<Flex justify="space-between" style={{ marginBottom: '20px' }}>
<Flex wrap gap={10}>
<ButtonModal></ButtonModal>
......
import { useParams } from 'react-router'
import { useViewLargeScreen } from '../query'
import Chart from '@/components/chart/Chart'
import { Card } from 'antd'
export default function LargeScreenView() {
const { id } = useParams()
const { data } = useViewLargeScreen(id || '')
if (!data) return null
const components = data.component.map((item: any) => {
try {
const { config } = JSON.parse(item.content)
return { ...item, config }
} catch (error) {
console.log(error)
}
return item
})
try {
const { form } = JSON.parse(data.content)
const getBackground = () => {
switch (form.bg) {
case 'default':
return '#f0f2f5'
case 'color':
return form.bgColor || '#ffffff'
case 'image':
return form.bgImage ? `url(${form.bgImage?.url})` : '#ffffff'
default:
return '#ffffff'
}
}
return (
<div
style={{
width: '100vw',
minHeight: '100%',
background: getBackground(),
backgroundSize: form.bg === 'image' ? 'cover' : 'auto',
backgroundPosition: form.bg === 'image' ? 'center' : 'auto',
}}>
<div style={{ padding: 20 }}>
<h2
style={{
fontSize: `${form.titleStyle.fontSize}px`,
color: form.titleStyle.color,
fontWeight: form.titleStyle.bold ? 'bold' : 'normal',
fontStyle: form.titleStyle.italic ? 'italic' : 'normal',
textAlign: form.titleStyle.align,
marginBottom: '20px',
}}>
{form.title}
</h2>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(400px, 1fr))', gap: '20px' }}>
{components.map((item: any) => (
<Card title={item.name} key={item.id}>
<Chart type={item.type} {...item.config} />
</Card>
))}
</div>
</div>
</div>
)
} catch (e) {
console.error('解析数据失败:', e)
return null
}
}
export const CHART_TYPE = {
1: '柱状图',
2: '折线图',
3: '饼状图',
4: '雷达图',
5: '散点图',
6: '气泡图',
7: '词云',
8: '地图',
9: '指标卡',
10: '漏斗图',
11: '直方图',
12: '表格',
13: '帕累托图',
14: '矩形树图',
}
export const CHART_TYPE_OPTIONS = Object.entries(CHART_TYPE).map(([key, value]) => ({
label: value,
value: key,
}))
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论