提交 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, {
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 { useState } from 'react'
import { Button, Flex, Modal, Form, Divider, Select, Radio, Input } from 'antd'
import { useCreateLargeScreen } from '../query'
import { useEffect, useState } from 'react'
import { Button, Flex, Modal, Form, Divider, Select, Radio, Input, message, ColorPicker, Space, Tooltip } from 'antd'
import { useCreateLargeScreen, useUpdateLargeScreen, useViewLargeScreen } from '../query'
import SelectChart from '@/components/chart/SelectChart'
import AppUpload from '@/components/AppUpload'
import {
BoldOutlined,
ItalicOutlined,
AlignLeftOutlined,
AlignCenterOutlined,
AlignRightOutlined,
} from '@ant-design/icons'
export default function ButtonModal({ title = '新建大屏' }) {
const [open, setOpen] = useState(false)
interface Chart {
id: string
type: string
config: any
}
interface Props {
id?: string
setOpen: (open: boolean) => void
}
const ModalContent = ({ setOpen, id = '' }: Props) => {
const [form] = Form.useForm()
const [selectedCharts, setSelectedCharts] = useState<Chart[]>([])
const { data } = useViewLargeScreen(id)
// Add form field watchers
const titleStyle = Form.useWatch('titleStyle', form)
useEffect(() => {
if (id && data) {
try {
const parsedContent = JSON.parse(data.content)
form.setFieldsValue({ ...parsedContent.form })
setSelectedCharts(parsedContent.charts)
} catch (e) {
console.error('解析数据失败:', e)
}
}
}, [id, data, form])
const { mutate: mutateCreate } = useCreateLargeScreen()
const { mutate: mutateUpdate } = useUpdateLargeScreen()
// 预览
const handlePreview = () => {
form.validateFields().then((values) => {
const ids = selectedCharts.map((item) => item.id)
if (!ids.length) {
message.warning('请至少选择一个图表')
return
}
const params = {
...values,
component_ids: ids.join(','),
content: JSON.stringify({ form: values, charts: selectedCharts }),
}
// 先保存,然后在新窗口打开预览
mutateUpdate(
{ id, ...params },
{
onSuccess: () => {
window.open(`/data/screen/view/${id}`, '_blank')
},
}
)
})
}
const { mutate } = useCreateLargeScreen()
// 保存
const handleSubmit = () => {
form.validateFields().then((values) => {
const params = { ...values, content: JSON.stringify(values) }
mutate(params, {
const ids = selectedCharts.map((item) => item.id)
if (!ids.length) {
message.warning('请至少选择一个图表')
return
}
const params = {
...values,
component_ids: ids.join(','),
content: JSON.stringify({ form: values, charts: selectedCharts }),
}
if (id) {
mutateUpdate(
{ id, ...params },
{
onSuccess: () => setOpen(false),
}
)
} else {
mutateCreate(params, {
onSuccess: () => setOpen(false),
})
}
})
}
return (
<>
<Button type="primary" onClick={() => setOpen(true)}>
{title}
</Button>
<Modal
title={title}
open={open}
footer={
<Flex justify="center" gap={20}>
<Button autoInsertSpace onClick={() => setOpen(false)}>
取消
</Button>
<Button type="primary">预览</Button>
<Button type="primary" autoInsertSpace onClick={handleSubmit}>
保存
</Button>
</Flex>
}
destroyOnClose
width={1000}
onCancel={() => setOpen(false)}>
<div style={{ minHeight: 300, padding: '20px 0' }}>
<Form form={form} preserve={false} initialValues={{ size: '1920*1080', bg: 'default', layout: 'auto' }}>
<Form.Item label="名称" name="name" rules={[{ required: true, message: '请输入' }]}>
<Input placeholder="请输入" />
<Form
form={form}
preserve={false}
initialValues={{
size: '1920*1080',
bg: 'default',
layout: 'auto',
titleStyle: {
fontSize: 24,
color: '#000000',
bold: false,
italic: false,
align: 'center',
},
}}>
<Form.Item label="名称" name="name" rules={[{ required: true, message: '请输入名称' }]}>
<Input placeholder="请输入名称" />
</Form.Item>
<Form.Item label="标题" name="title" rules={[{ required: true, message: '请输入标题' }]}>
<Space>
<Form.Item name="title" noStyle>
<Input placeholder="请输入标题" />
</Form.Item>
<Form.Item name={['titleStyle', 'fontSize']} noStyle>
<Select
options={[
{ label: '16px', value: 16 },
{ label: '18px', value: 18 },
{ label: '20px', value: 20 },
{ label: '24px', value: 24 },
{ label: '28px', value: 28 },
{ label: '32px', value: 32 },
{ label: '36px', value: 36 },
{ label: '40px', value: 40 },
]}
style={{ width: 120 }}
/>
</Form.Item>
<Form.Item name={['titleStyle', 'color']} noStyle getValueFromEvent={(e) => e.toHexString()}>
<ColorPicker />
</Form.Item>
<Form.Item name={['titleStyle', 'bold']} noStyle>
<Tooltip title="加粗">
<Button
type={titleStyle?.bold ? 'primary' : 'default'}
icon={<BoldOutlined />}
onClick={() => form.setFieldValue(['titleStyle', 'bold'], !titleStyle?.bold)}
/>
</Tooltip>
</Form.Item>
<Form.Item name={['titleStyle', 'italic']} noStyle>
<Tooltip title="斜体">
<Button
type={titleStyle?.italic ? 'primary' : 'default'}
icon={<ItalicOutlined />}
onClick={() => form.setFieldValue(['titleStyle', 'italic'], !titleStyle?.italic)}
/>
</Tooltip>
</Form.Item>
<Form.Item name={['titleStyle', 'align']} noStyle>
<Space>
<Tooltip title="左对齐">
<Button
type={titleStyle?.align === 'left' ? 'primary' : 'default'}
icon={<AlignLeftOutlined />}
onClick={() => form.setFieldValue(['titleStyle', 'align'], 'left')}
/>
</Tooltip>
<Tooltip title="居中">
<Button
type={titleStyle?.align === 'center' ? 'primary' : 'default'}
icon={<AlignCenterOutlined />}
onClick={() => form.setFieldValue(['titleStyle', 'align'], 'center')}
/>
</Tooltip>
<Tooltip title="右对齐">
<Button
type={titleStyle?.align === 'right' ? 'primary' : 'default'}
icon={<AlignRightOutlined />}
onClick={() => form.setFieldValue(['titleStyle', 'align'], 'right')}
/>
</Tooltip>
</Space>
</Form.Item>
<Form.Item label="标题" name="title" rules={[{ required: true, message: '请输入' }]}>
<Input placeholder="请输入" />
</Space>
</Form.Item>
<Form.Item label="大小" name="size" rules={[{ required: true, message: '请选择' }]}>
<Form.Item label="大小" name="size" rules={[{ required: true, message: '请选择大小' }]}>
<Select
options={[
{ label: '1920*1080', value: '1920*1080' },
{ label: '1024*768', value: '1024*768' },
]}
placeholder="请选择"></Select>
placeholder="请选择大小"
/>
</Form.Item>
<Form.Item label="背景" name="bg" rules={[{ required: true, message: '请选择' }]}>
<Form.Item label="背景" name="bg" rules={[{ required: true, message: '请选择背景' }]}>
<Radio.Group
options={[
{ label: '默认', value: 'default' },
{ label: '纯色', value: 'color' },
{ label: '图片', value: 'image' },
]}></Radio.Group>
]}
/>
</Form.Item>
<Form.Item noStyle shouldUpdate={(prevValues, currentValues) => prevValues.bg !== currentValues.bg}>
{({ getFieldValue }) => {
const bgType = getFieldValue('bg')
if (bgType === 'color') {
return (
<Form.Item label="背景颜色" name="bgColor" getValueFromEvent={(e) => e.toHexString()}>
<ColorPicker />
</Form.Item>
)
}
if (bgType === 'image') {
return (
<Form.Item label="背景图片" name="bgImage">
<AppUpload accept="image/*" />
</Form.Item>
)
}
return null
}}
</Form.Item>
<Divider />
<Button type="primary">添加可视化组件</Button>
<Form.Item label="组件布局方式" name="layout" rules={[{ required: true, message: '请选择' }]}>
<Radio.Group options={[{ label: '自动布局', value: 'auto' }]}></Radio.Group>
<SelectChart value={selectedCharts} onChange={setSelectedCharts} />
<Form.Item label="组件布局方式" name="layout" rules={[{ required: true, message: '请选择布局方式' }]}>
<Radio.Group options={[{ label: '自动布局', value: 'auto' }]} />
</Form.Item>
</Form>
</div>
<Flex justify="center" gap={20}>
<Button autoInsertSpace onClick={() => setOpen(false)}>
取消
</Button>
{id && (
<Button type="primary" onClick={handlePreview}>
预览
</Button>
)}
<Button type="primary" autoInsertSpace onClick={handleSubmit}>
保存
</Button>
</Flex>
</>
)
}
export default function ButtonModal({ id = '' }) {
const [open, setOpen] = useState(false)
const title = id ? '编辑' : '新建'
return (
<>
<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 setOpen={(open) => setOpen(open)} id={id} />
</Modal>
</>
)
......
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 (
<>
<Link to={`/data/screen/view/${record.id}`} target="_blank">
<Button color="primary" variant="text">
查看
</Button>
<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 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论