添加图表

This commit is contained in:
brian 2023-07-14 18:02:35 +08:00
parent e3ec40193e
commit 93814a83b6
11 changed files with 170 additions and 723 deletions

View File

@ -7,7 +7,13 @@ export default [
{ name: '注册', path: '/user/register', component: './User/Register' }
],
},
{ path: '/welcome', name: '欢迎', icon: 'smile', component: './Welcome' },
{
name: '智能分析',
path: '/add_chart',
icon: 'barChart',
component: './AddChart'
},
{
path: '/admin',
name: '管理页',
@ -18,6 +24,6 @@ export default [
{ path: '/admin/user-manage', name: '用户管理', icon: 'smile', component: './Admin/UserManage' },
],
},
{ path: '/', redirect: '/welcome' },
{ path: '/', redirect: '/add_chart' },
{ path: '*', layout: false, component: './404' },
];

View File

@ -0,0 +1,79 @@
import { genChartByAiUsingPOST } from '@/services/answerbi/chartController';
import { UploadOutlined } from '@ant-design/icons';
import { Button, Form, Input, Select, Space, Upload, message } from 'antd';
import TextArea from 'antd/es/input/TextArea';
import React from 'react';
const addChart: React.FC = () => {
const onFinish = async (values: any) => {
//对接后端,上传数据
const params = {
...values,
file: undefined,
};
try{
//需要取到上传的原始数据 file->file->originFileObj
const res = await genChartByAiUsingPOST(params, {}, values.file.file.originFileObj);
//正常情况下,如果没有返回值就分析失败,有就分析成功
if(!res?.data){
message.error('分析失败');
}else{
message.success('分析成功');
}
}catch(e: any){
//异常情况下,提示分析失败+具体失败原因
message.error('分析失败,' + e.message);
}
};
return (
<div className='add-chart'>
<Form
name="addChart"
onFinish={onFinish}
initialValues={{ }}
>
<Form.Item
name="goal"
label="分析目标"
rules={[{ required: true, message: '请输入分析目标!' }]}
>
<TextArea placeholder='请输入你的分析需求,比如:分析网站用户的增长情况'/>
</Form.Item>
<Form.Item name="chartName" label="图表名称">
<Input placeholder='请输入图表名称'/>
</Form.Item>
<Form.Item name="chartType" label="图表类型">
<Select
placeholder="请选择图表类型"
options={[
{value: '折线图', label: '折线图'},
{value: '柱状图', label: '柱状图'},
{value: '堆叠图', label: '堆叠图'},
{value: '饼图', label: '饼图'},
{value: '雷达图', label: '雷达图'},
]}
/>
</Form.Item>
<Form.Item name="file" label="上传原始数据">
<Upload name="file">
<Button icon={<UploadOutlined />}>EXCEL文件</Button>
</Upload>
</Form.Item>
<Form.Item wrapperCol={{ span: 12, offset: 6 }}>
<Space>
<Button type="primary" htmlType="submit"></Button>
<Button htmlType="reset"></Button>
</Space>
</Form.Item>
</Form>
</div>
);
};
export default addChart;

View File

@ -1,4 +1,4 @@
import { listUserVOByPageUsingPOST } from '@/services/answerbi/userController';
import { listUserByPageUsingPOST } from '@/services/answerbi/userController';
import type { ActionType, ProColumns } from '@ant-design/pro-components';
import { ProTable, TableDropdown } from '@ant-design/pro-components';
import { useRef } from 'react';
@ -14,7 +14,7 @@ export const waitTime = async (time: number = 100) => {
await waitTimePromise(time);
};
const columns: ProColumns<API.UserVO>[] = [
const columns: ProColumns<API.User>[] = [
{
dataIndex: 'id',
valueType: 'indexBorder',
@ -33,23 +33,31 @@ const columns: ProColumns<API.UserVO>[] = [
{
title: '头像',
dataIndex: 'userAvatar',
search: false,
render: (_, record) => (
<div>
<Image src={record.userAvatar} width={100} />
<img src={record.userAvatar} width={100} />
</div>
)
},
{
title: '角色',
dataIndex: 'userRole',
valueType: 'select',
valueEnum :{
'user': {text : '普通用户',status : 'Default'},
'admin': {text:'管理员', status : 'Success'},
'ban': {text:'违规被封',status:'Error'},
}
},
{
title: '创建时间',
key: 'createdTime',
dataIndex: 'createdTime',
valueType: 'dateTime',
sorter : true,
search : false,
},
{
title: '操作',
valueType: 'option',
@ -63,9 +71,6 @@ const columns: ProColumns<API.UserVO>[] = [
>
</a>,
<a href={record.url} target="_blank" rel="noopener noreferrer" key="view">
</a>,
<TableDropdown
key="actionGroup"
onSelect={() => action?.reload()}
@ -81,15 +86,15 @@ const columns: ProColumns<API.UserVO>[] = [
export default () => {
const actionRef = useRef<ActionType>();
return (
<ProTable<API.UserVO>
<ProTable<API.User>
columns={columns}
actionRef={actionRef}
cardBordered
request={async (params = {}, sort, filter) => {
console.log(sort, filter);
const userList = await listUserVOByPageUsingPOST(params);
const userList = await listUserByPageUsingPOST(params);
return {
data: userList
data: userList.data?.records
};
}}
editable={{
@ -124,11 +129,11 @@ export default () => {
},
}}
pagination={{
pageSize: 5,
pageSize: 10,
onChange: (page) => console.log(page),
}}
dateFormatter="string"
headerTitle="高级表格"
headerTitle="用户列表"
/>
);
};

View File

@ -1,155 +0,0 @@
import {
ProFormDateTimePicker,
ProFormRadio,
ProFormSelect,
ProFormText,
ProFormTextArea,
StepsForm,
} from '@ant-design/pro-components';
import '@umijs/max';
import { Modal } from 'antd';
import React from 'react';
export type FormValueType = {
target?: string;
template?: string;
type?: string;
time?: string;
frequency?: string;
} & Partial<API.RuleListItem>;
export type UpdateFormProps = {
onCancel: (flag?: boolean, formVals?: FormValueType) => void;
onSubmit: (values: FormValueType) => Promise<void>;
updateModalOpen: boolean;
values: Partial<API.RuleListItem>;
};
const UpdateForm: React.FC<UpdateFormProps> = (props) => {
return (
<StepsForm
stepsProps={{
size: 'small',
}}
stepsFormRender={(dom, submitter) => {
return (
<Modal
width={640}
bodyStyle={{
padding: '32px 40px 48px',
}}
destroyOnClose
title={'规则配置'}
open={props.updateModalOpen}
footer={submitter}
onCancel={() => {
props.onCancel();
}}
>
{dom}
</Modal>
);
}}
onFinish={props.onSubmit}
>
<StepsForm.StepForm
initialValues={{
name: props.values.name,
desc: props.values.desc,
}}
title={'基本信息'}
>
<ProFormText
name="name"
label={'规则名称'}
width="md"
rules={[
{
required: true,
message: '请输入规则名称!',
},
]}
/>
<ProFormTextArea
name="desc"
width="md"
label={'规则描述'}
placeholder={'请输入至少五个字符'}
rules={[
{
required: true,
message: '请输入至少五个字符的规则描述!',
min: 5,
},
]}
/>
</StepsForm.StepForm>
<StepsForm.StepForm
initialValues={{
target: '0',
template: '0',
}}
title={'配置规则属性'}
>
<ProFormSelect
name="target"
width="md"
label={'监控对象'}
valueEnum={{
0: '表一',
1: '表二',
}}
/>
<ProFormSelect
name="template"
width="md"
label={'规则模板'}
valueEnum={{
0: '规则模板一',
1: '规则模板二',
}}
/>
<ProFormRadio.Group
name="type"
label={'规则类型'}
options={[
{
value: '0',
label: '强',
},
{
value: '1',
label: '弱',
},
]}
/>
</StepsForm.StepForm>
<StepsForm.StepForm
initialValues={{
type: '1',
frequency: 'month',
}}
title={'设定调度周期'}
>
<ProFormDateTimePicker
name="time"
width="md"
label={'开始时间'}
rules={[
{
required: true,
message: '请选择开始时间!',
},
]}
/>
<ProFormSelect
name="frequency"
label={'监控对象'}
width="md"
valueEnum={{
month: '月',
week: '周',
}}
/>
</StepsForm.StepForm>
</StepsForm>
);
};
export default UpdateForm;

View File

@ -1,329 +0,0 @@
import { addRule, removeRule, rule, updateRule } from '@/services/ant-design-pro/api';
import { PlusOutlined } from '@ant-design/icons';
import type { ActionType, ProColumns, ProDescriptionsItemProps } from '@ant-design/pro-components';
import {
FooterToolbar,
ModalForm,
PageContainer,
ProDescriptions,
ProFormText,
ProFormTextArea,
ProTable,
} from '@ant-design/pro-components';
import '@umijs/max';
import { Button, Drawer, Input, message } from 'antd';
import React, { useRef, useState } from 'react';
import type { FormValueType } from './components/UpdateForm';
import UpdateForm from './components/UpdateForm';
/**
* @en-US Add node
* @zh-CN
* @param fields
*/
const handleAdd = async (fields: API.RuleListItem) => {
const hide = message.loading('正在添加');
try {
await addRule({
...fields,
});
hide();
message.success('Added successfully');
return true;
} catch (error) {
hide();
message.error('Adding failed, please try again!');
return false;
}
};
/**
* @en-US Update node
* @zh-CN
*
* @param fields
*/
const handleUpdate = async (fields: FormValueType) => {
const hide = message.loading('Configuring');
try {
await updateRule({
name: fields.name,
desc: fields.desc,
key: fields.key,
});
hide();
message.success('Configuration is successful');
return true;
} catch (error) {
hide();
message.error('Configuration failed, please try again!');
return false;
}
};
/**
* Delete node
* @zh-CN
*
* @param selectedRows
*/
const handleRemove = async (selectedRows: API.RuleListItem[]) => {
const hide = message.loading('正在删除');
if (!selectedRows) return true;
try {
await removeRule({
key: selectedRows.map((row) => row.key),
});
hide();
message.success('Deleted successfully and will refresh soon');
return true;
} catch (error) {
hide();
message.error('Delete failed, please try again');
return false;
}
};
const TableList: React.FC = () => {
/**
* @en-US Pop-up window of new window
* @zh-CN
* */
const [createModalOpen, handleModalOpen] = useState<boolean>(false);
/**
* @en-US The pop-up window of the distribution update window
* @zh-CN
* */
const [updateModalOpen, handleUpdateModalOpen] = useState<boolean>(false);
const [showDetail, setShowDetail] = useState<boolean>(false);
const actionRef = useRef<ActionType>();
const [currentRow, setCurrentRow] = useState<API.RuleListItem>();
const [selectedRowsState, setSelectedRows] = useState<API.RuleListItem[]>([]);
/**
* @en-US International configuration
* @zh-CN
* */
const columns: ProColumns<API.RuleListItem>[] = [
{
title: '规则名称',
dataIndex: 'name',
tip: 'The rule name is the unique key',
render: (dom, entity) => {
return (
<a
onClick={() => {
setCurrentRow(entity);
setShowDetail(true);
}}
>
{dom}
</a>
);
},
},
{
title: '描述',
dataIndex: 'desc',
valueType: 'textarea',
},
{
title: '服务调用次数',
dataIndex: 'callNo',
sorter: true,
hideInForm: true,
renderText: (val: string) => `${val}${'万'}`,
},
{
title: '状态',
dataIndex: 'status',
hideInForm: true,
valueEnum: {
0: {
text: '关闭',
status: 'Default',
},
1: {
text: '运行中',
status: 'Processing',
},
2: {
text: '已上线',
status: 'Success',
},
3: {
text: '异常',
status: 'Error',
},
},
},
{
title: '上次调度时间',
sorter: true,
dataIndex: 'updatedAt',
valueType: 'dateTime',
renderFormItem: (item, { defaultRender, ...rest }, form) => {
const status = form.getFieldValue('status');
if (`${status}` === '0') {
return false;
}
if (`${status}` === '3') {
return <Input {...rest} placeholder={'请输入异常原因!'} />;
}
return defaultRender(item);
},
},
{
title: '操作',
dataIndex: 'option',
valueType: 'option',
render: (_, record) => [
<a
key="config"
onClick={() => {
handleUpdateModalOpen(true);
setCurrentRow(record);
}}
>
</a>,
<a key="subscribeAlert" href="https://procomponents.ant.design/">
</a>,
],
},
];
return (
<PageContainer>
<ProTable<API.RuleListItem, API.PageParams>
headerTitle={'查询表格'}
actionRef={actionRef}
rowKey="key"
search={{
labelWidth: 120,
}}
toolBarRender={() => [
<Button
type="primary"
key="primary"
onClick={() => {
handleModalOpen(true);
}}
>
<PlusOutlined />
</Button>,
]}
request={rule}
columns={columns}
rowSelection={{
onChange: (_, selectedRows) => {
setSelectedRows(selectedRows);
},
}}
/>
{selectedRowsState?.length > 0 && (
<FooterToolbar
extra={
<div>
{' '}
<a
style={{
fontWeight: 600,
}}
>
{selectedRowsState.length}
</a>{' '}
&nbsp;&nbsp;
<span>
{selectedRowsState.reduce((pre, item) => pre + item.callNo!, 0)}
</span>
</div>
}
>
<Button
onClick={async () => {
await handleRemove(selectedRowsState);
setSelectedRows([]);
actionRef.current?.reloadAndRest?.();
}}
>
</Button>
<Button type="primary"></Button>
</FooterToolbar>
)}
<ModalForm
title={'新建规则'}
width="400px"
open={createModalOpen}
onOpenChange={handleModalOpen}
onFinish={async (value) => {
const success = await handleAdd(value as API.RuleListItem);
if (success) {
handleModalOpen(false);
if (actionRef.current) {
actionRef.current.reload();
}
}
}}
>
<ProFormText
rules={[
{
required: true,
message: '规则名称为必填项',
},
]}
width="md"
name="name"
/>
<ProFormTextArea width="md" name="desc" />
</ModalForm>
<UpdateForm
onSubmit={async (value) => {
const success = await handleUpdate(value);
if (success) {
handleUpdateModalOpen(false);
setCurrentRow(undefined);
if (actionRef.current) {
actionRef.current.reload();
}
}
}}
onCancel={() => {
handleUpdateModalOpen(false);
if (!showDetail) {
setCurrentRow(undefined);
}
}}
updateModalOpen={updateModalOpen}
values={currentRow || {}}
/>
<Drawer
width={600}
open={showDetail}
onClose={() => {
setCurrentRow(undefined);
setShowDetail(false);
}}
closable={false}
>
{currentRow?.name && (
<ProDescriptions<API.RuleListItem>
column={2}
title={currentRow?.name}
request={async () => ({
data: currentRow || {},
})}
params={{
id: currentRow?.name,
}}
columns={columns as ProDescriptionsItemProps<API.RuleListItem>[]}
/>
)}
</Drawer>
</PageContainer>
);
};
export default TableList;

View File

@ -47,6 +47,42 @@ export async function editChartUsingPOST(
});
}
/** genChartByAi POST /api/chart/gen */
export async function genChartByAiUsingPOST(
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
params: API.genChartByAiUsingPOSTParams,
body: {},
file?: File,
options?: { [key: string]: any },
) {
const formData = new FormData();
if (file) {
formData.append('file', file);
}
Object.keys(body).forEach((ele) => {
const item = (body as any)[ele];
if (item !== undefined && item !== null) {
formData.append(
ele,
typeof item === 'object' && !(item instanceof File) ? JSON.stringify(item) : item,
);
}
});
return request<API.CommonResponseBiResponse_>('/api/chart/gen', {
method: 'POST',
params: {
...params,
},
data: formData,
requestType: 'form',
...(options || {}),
});
}
/** getChartById GET /api/chart/get */
export async function getChartByIdUsingGET(
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)

View File

@ -1,6 +1,13 @@
declare namespace API {
type BiResponse = {
chartId?: number;
genChart?: string;
genResult?: string;
};
type Chart = {
chartData?: string;
chartName?: string;
chartType?: string;
createdTime?: string;
deletedFlag?: number;
@ -14,18 +21,21 @@ declare namespace API {
type ChartAddRequest = {
chartData?: string;
chartName?: string;
chartType?: string;
goal?: string;
};
type ChartEditRequest = {
chartData?: string;
chartName?: string;
chartType?: string;
goal?: string;
id?: number;
};
type ChartQueryRequest = {
chartName?: string;
chartType?: string;
current?: number;
goal?: string;
@ -38,6 +48,7 @@ declare namespace API {
type ChartUpdateRequest = {
chartData?: string;
chartName?: string;
chartType?: string;
createdTime?: string;
deletedFlag?: number;
@ -48,6 +59,12 @@ declare namespace API {
updatedTime?: string;
};
type CommonResponseBiResponse_ = {
code?: number;
data?: BiResponse;
message?: string;
};
type CommonResponseBoolean_ = {
code?: number;
data?: boolean;
@ -130,6 +147,12 @@ declare namespace API {
id?: number;
};
type genChartByAiUsingPOSTParams = {
chartName?: string;
chartType?: string;
goal?: string;
};
type getChartByIdUsingGETParams = {
/** id */
id?: number;
@ -151,9 +174,9 @@ declare namespace API {
};
type LoginUserVO = {
createTime?: string;
createdTime?: string;
id?: number;
updateTime?: string;
updatedTime?: string;
userAvatar?: string;
userName?: string;
userProfile?: string;
@ -296,9 +319,9 @@ declare namespace API {
updatedTime?: string;
userAccount?: string;
userAvatar?: string;
userName?: string;
userPassword?: string;
userRole?: string;
username?: string;
};
type UserAddRequest = {
@ -316,11 +339,10 @@ declare namespace API {
type UserQueryRequest = {
current?: number;
id?: number;
mpOpenId?: string;
pageSize?: number;
sortField?: string;
sortOrder?: string;
unionId?: string;
userAccount?: string;
userName?: string;
userProfile?: string;
userRole?: string;
@ -347,11 +369,11 @@ declare namespace API {
};
type UserVO = {
createTime?: string;
createdTime?: string;
id?: number;
userAccount?: string;
userAvatar?: string;
userName?: string;
userAccount?: string;
userProfile?: string;
userRole?: string;
};

View File

@ -1,85 +0,0 @@
// @ts-ignore
/* eslint-disable */
import { request } from '@umijs/max';
/** 获取当前的用户 GET /api/currentUser */
export async function currentUser(options?: { [key: string]: any }) {
return request<{
data: API.CurrentUser;
}>('/api/currentUser', {
method: 'GET',
...(options || {}),
});
}
/** 退出登录接口 POST /api/login/outLogin */
export async function outLogin(options?: { [key: string]: any }) {
return request<Record<string, any>>('/api/login/outLogin', {
method: 'POST',
...(options || {}),
});
}
/** 登录接口 POST /api/login/account */
export async function login(body: API.LoginParams, options?: { [key: string]: any }) {
return request<API.LoginResult>('/api/login/account', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}
/** 此处后端没有提供注释 GET /api/notices */
export async function getNotices(options?: { [key: string]: any }) {
return request<API.NoticeIconList>('/api/notices', {
method: 'GET',
...(options || {}),
});
}
/** 获取规则列表 GET /api/rule */
export async function rule(
params: {
// query
/** 当前的页码 */
current?: number;
/** 页面的容量 */
pageSize?: number;
},
options?: { [key: string]: any },
) {
return request<API.RuleList>('/api/rule', {
method: 'GET',
params: {
...params,
},
...(options || {}),
});
}
/** 新建规则 PUT /api/rule */
export async function updateRule(options?: { [key: string]: any }) {
return request<API.RuleListItem>('/api/rule', {
method: 'PUT',
...(options || {}),
});
}
/** 新建规则 POST /api/rule */
export async function addRule(options?: { [key: string]: any }) {
return request<API.RuleListItem>('/api/rule', {
method: 'POST',
...(options || {}),
});
}
/** 删除规则 DELETE /api/rule */
export async function removeRule(options?: { [key: string]: any }) {
return request<Record<string, any>>('/api/rule', {
method: 'DELETE',
...(options || {}),
});
}

View File

@ -1,10 +0,0 @@
// @ts-ignore
/* eslint-disable */
// API 更新时间:
// API 唯一标识:
import * as api from './api';
import * as login from './login';
export default {
api,
login,
};

View File

@ -1,21 +0,0 @@
// @ts-ignore
/* eslint-disable */
import { request } from '@umijs/max';
/** 发送验证码 POST /api/login/captcha */
export async function getFakeCaptcha(
params: {
// query
/** 手机号 */
phone?: string;
},
options?: { [key: string]: any },
) {
return request<API.FakeCaptcha>('/api/login/captcha', {
method: 'GET',
params: {
...params,
},
...(options || {}),
});
}

View File

@ -1,101 +0,0 @@
// @ts-ignore
/* eslint-disable */
declare namespace API {
type CurrentUser = {
name?: string;
avatar?: string;
userid?: string;
email?: string;
signature?: string;
title?: string;
group?: string;
tags?: { key?: string; label?: string }[];
notifyCount?: number;
unreadCount?: number;
country?: string;
access?: string;
geographic?: {
province?: { label?: string; key?: string };
city?: { label?: string; key?: string };
};
address?: string;
phone?: string;
};
type LoginResult = {
status?: string;
type?: string;
currentAuthority?: string;
};
type PageParams = {
current?: number;
pageSize?: number;
};
type RuleListItem = {
key?: number;
disabled?: boolean;
href?: string;
avatar?: string;
name?: string;
owner?: string;
desc?: string;
callNo?: number;
status?: number;
updatedAt?: string;
createdAt?: string;
progress?: number;
};
type RuleList = {
data?: RuleListItem[];
/** 列表的内容总数 */
total?: number;
success?: boolean;
};
type FakeCaptcha = {
code?: number;
status?: string;
};
type LoginParams = {
username?: string;
password?: string;
autoLogin?: boolean;
type?: string;
};
type ErrorResponse = {
/** 业务约定的错误码 */
errorCode: string;
/** 业务上的错误信息 */
errorMessage?: string;
/** 业务上的请求是否成功 */
success?: boolean;
};
type NoticeIconList = {
data?: NoticeIconItem[];
/** 列表的内容总数 */
total?: number;
success?: boolean;
};
type NoticeIconItemType = 'notification' | 'message' | 'event';
type NoticeIconItem = {
id?: string;
extra?: string;
key?: string;
read?: boolean;
avatar?: string;
title?: string;
status?: string;
datetime?: string;
description?: string;
type?: NoticeIconItemType;
};
}