图表管理功能(前后端实现增删改查)
图表管理功能(前后端实现增删改查)
后端
-
库表设计
create table chart ( id bigint auto_increment comment 'id' primary key, goal text null comment '分析目标', chartData text null comment '图表信息', chartType varchar(256) null comment '图表类型', genChart text null comment '生成的图表信息', genResult text null, userId bigint null comment '创建图标用户 id', createTime datetime default CURRENT_TIMESTAMP not null comment '创建时间', updateTime datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间', isDelete tinyint default 0 not null comment '是否删除', name varchar(128) null comment '图表名称' ) comment '图表信息表' collate = utf8mb4_unicode_ci;
-
增删改查代码自动生成工具生成:
使用Mybatis-Generator自动代码生成工具生成实体类,mapper接口和实现类。
-
controller层增删改查询接口实现
/** * 接口 * */ @RestController @RequestMapping("/chart") @Slf4j public class ChartController { @Resource private ChartService chartService; private final static Gson GSON = new Gson(); // region 增删改查 /** * 创建 * * @param chartAddRequest * @param request * @return */ @PostMapping("/add") public BaseResponse<Long> addChart(@RequestBody ChartAddRequest chartAddRequest, HttpServletRequest request) { if (chartAddRequest == null) { throw new BusinessException(ErrorCode.PARAMS_ERROR); } Chart chart = new Chart(); BeanUtils.copyProperties(chartAddRequest, chart); User loginUser = userService.getLoginUser(request); chart.setUserId(loginUser.getId()); boolean result = chartService.save(chart); ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR); long newChartId = chart.getId(); return ResultUtils.success(newChartId); } /** * 删除 * * @param deleteRequest * @param request * @return */ @PostMapping("/delete") public BaseResponse<Boolean> deleteChart(@RequestBody DeleteRequest deleteRequest, HttpServletRequest request) { if (deleteRequest == null || deleteRequest.getId() <= 0) { throw new BusinessException(ErrorCode.PARAMS_ERROR); } User user = userService.getLoginUser(request); long id = deleteRequest.getId(); // 判断是否存在 Chart oldChart = chartService.getById(id); ThrowUtils.throwIf(oldChart == null, ErrorCode.NOT_FOUND_ERROR); // 仅本人或管理员可删除 if (!oldChart.getUserId().equals(user.getId()) && !userService.isAdmin(request)) { throw new BusinessException(ErrorCode.NO_AUTH_ERROR); } boolean b = chartService.removeById(id); return ResultUtils.success(b); } /** * 更新(仅管理员) * * @param chartUpdateRequest * @return */ @PostMapping("/update") @AuthCheck(mustRole = UserConstant.ADMIN_ROLE) public BaseResponse<Boolean> updateChart(@RequestBody ChartUpdateRequest chartUpdateRequest) { if (chartUpdateRequest == null || chartUpdateRequest.getId() <= 0) { throw new BusinessException(ErrorCode.PARAMS_ERROR); } Chart chart = new Chart(); BeanUtils.copyProperties(chartUpdateRequest, chart); long id = chartUpdateRequest.getId(); // 判断是否存在 Chart oldChart = chartService.getById(id); ThrowUtils.throwIf(oldChart == null, ErrorCode.NOT_FOUND_ERROR); boolean result = chartService.updateById(chart); return ResultUtils.success(result); } /** * 根据 id 获取 * * @param id * @return */ @GetMapping("/get") public BaseResponse<Chart> getChartById(long id, HttpServletRequest request) { if (id <= 0) { throw new BusinessException(ErrorCode.PARAMS_ERROR); } Chart chart = chartService.getById(id); if (chart == null) { throw new BusinessException(ErrorCode.NOT_FOUND_ERROR); } return ResultUtils.success(chart); } /** * 分页获取列表 * * @param chartQueryRequest * @param request * @return */ @PostMapping("/list/page") public BaseResponse<Page<Chart>> listChartByPage(@RequestBody ChartQueryRequest chartQueryRequest, HttpServletRequest request) { long current = chartQueryRequest.getCurrent(); long size = chartQueryRequest.getPageSize(); // 限制爬虫 ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR); Page<Chart> chartPage = chartService.page(new Page<>(current, size), getQueryWrapper(chartQueryRequest)); return ResultUtils.success(chartPage); } /** * 分页获取当前用户创建的资源列表 * * @param chartQueryRequest * @param request * @return */ @PostMapping("/my/list/page") public BaseResponse<Page<Chart>> listMyChartByPage(@RequestBody ChartQueryRequest chartQueryRequest, HttpServletRequest request) { if (chartQueryRequest == null) { throw new BusinessException(ErrorCode.PARAMS_ERROR); } User loginUser = userService.getLoginUser(request); chartQueryRequest.setUserId(loginUser.getId()); long current = chartQueryRequest.getCurrent(); long size = chartQueryRequest.getPageSize(); // 限制爬虫 ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR); Page<Chart> chartPage = chartService.page(new Page<>(current, size), getQueryWrapper(chartQueryRequest)); return ResultUtils.success(chartPage); } }
前端(antdesign组件库)
-
开发一个表展示页
-
新添加路由
{ path: '/my_chart', name: '我的图表', icon: 'pieChart', component: './MyChart', },
-
创建页面
-
填充内容
-
获取需要的数据
-
定义一个获取数据的函数,调用接口(await把异步变成同步) listMyChartPageUsingPost和后端对应的查询接口由OpenApi工具根据Swagger接口自动生成,大幅提高开发效率。
const loadData = async () => { try { const res = await listMyChartByPageUsingPost(searchParams); if (res.data) { setChartList(res.data.records ?? []); } } catch (e: any) { message.error('获取图表失败', e.message); } };
-
-
首次页面加载时触发时加载数据(钩子函数:页面首次渲染时以及数组中的变量发生变化时执行钩子函数中的方法)
//钩子函数:页面首次渲染时以及数组中的变量发生变化时执行钩子函数中的方法 useEffect(() => { loadData(); }, [searchParams]);//搜索参数改变就会触发加载数据方法
-
引入Ant Design的List组件,https://ant.design/components/list-cn,复制示例代码
-
调整 List 组件中的内容为自己的(注意,获取用户头像可以从初始化状态中获取)
const { initialState } = useModel('@@initialState'); const { currentUser } = initialState || {};
-
针对样式,对数据做一些处理,比如统一隐藏图表自身的 title
-
-
-
实现分页展示表
逻辑:当用户点击切换页面的时候,组件触发onChange方法,改变搜索条件,当所搜条件改变的时候又会触发数据的加载,后端返回新的数据 。
- 使用AntDesign组件库中的分页组件
-
添加按表名称搜索表功能
- 添加搜索组件框组件,当用户的搜索条件(初始化)改变时,要添加到原始的搜索条件
-
(附)前端展示图表,页面,搜索源码
//前端分页查询图表展示预页面(源码)
const MyChartPage: React.FC = () => {
/**
* 初始值
*/
const initSearchParams = {
current: 1,
pageSize: 6,
};
const [searchParams, setSearchParams] = useState<API.ChartQueryRequest>({
...initSearchParams,
});
// const [selectedChartId, setSelectedChartId] = useState<number | undefined>(undefined);
/**
* 分页获取图表
*/
const [chartList, setChartList] = useState<API.Chart[]>();
const [chartTotal, setChartTotal] = useState<number>(0);
const [loading, setLoading] = useState<boolean>(true);
/**
* 获取当前用户
*/
const { initialState } = useModel('@@initialState');
const { currentUser } = initialState || {};
/**
* 删除图表
* @param chartId
*/
const handleDelete = (chartId: any) => {
Modal.confirm({
title: '确认删除',
icon: <ExclamationCircleOutlined />,
content: '确定要删除这个图表吗?',
okText: '确认',
cancelText: '取消',
onOk: async () => {
try {
const res = await deleteChartUsingPost({ id: chartId });
// 后端返回 boolean值
console.log('res:', res.data);
if (res.data) {
message.success('删除成功');
// 删除成功后重新加载图表数据
loadData();
} else {
message.error('删除失败');
}
} catch (e: any) {
message.error('删除失败' + e.message);
}
},
});
};
/**
* 加载图表数据
*/
const loadData = async () => {
setLoading(loading);
try {
let res = await listMyChartByPageUsingPost(searchParams);
if (res.data) {
setChartList(res.data.records ?? []);
setChartTotal(res.data.total ?? 0);
// 隐藏title
if (res.data.records) {
res.data.records.forEach((data) => {
const chartOption = JSON.parse(data.genChart ?? '{}');
// 取出title并且设置为 undefined
chartOption.title = undefined;
data.genChart = JSON.stringify(chartOption);
});
}
} else {
message.error('获取我的图表失败');
}
} catch (e: any) {
message.error('获取我的图表失败' + e.message);
}
setLoading(false);
};
/**
* 变化时执行此处
*/
useEffect(() => {
loadData();
}, [searchParams]);
return (
<div className="my-chart-page">
<div className="margin-20">
<Search
placeholder="请输入图标名称搜索"
loading={loading}
enterButton
onSearch={(value) => {
setSearchParams({
...initSearchParams,
chartName: value,
});
}}
/>
</div>
<List
grid={{
gutter: 16,
xs: 1,
sm: 1,
md: 1,
lg: 2,
xl: 2,
xxl: 2,
}}
pagination={{
// 设置分页
showTotal: () => `共 ${chartTotal} 条记录`,
showSizeChanger: true,
showQuickJumper: true,
pageSizeOptions: ['6', '10', '14', '20'],
onChange: (page, pageSize) => {
setSearchParams({
...searchParams,
current: page,
pageSize,
});
},
current: searchParams.current, // 当前页
pageSize: searchParams.pageSize, // 每页条数
total: chartTotal, // 总数
}}
loading={loading}
dataSource={chartList}
renderItem={(item) => (
<List.Item key={item.id}>
<Card style={{ width: '100%' }}>
<List.Item.Meta
avatar={<Avatar src={currentUser?.userAvatar} />}
title={currentUser?.userName}
/>
<p
style={{
textAlign: 'center',
fontWeight: 'bold',
color: 'black',
fontSize: '16px',
}}
>
{'分析目标:' + item.goal}
</p>
<List.Item.Meta
style={{ textAlign: 'right' }}
description={item.chartType ? '图表类型:' + item.chartType : undefined}
/>
<ReactECharts option={item.genChart && JSON.parse(item.genChart)} />
<p
style={{
textAlign: 'center',
fontWeight: 'bold',
color: '#e617ff',
fontSize: '16px',
}}
>
{'图表名称:' + item.name}
</p>
<Divider style={{ fontWeight: 'bold', color: 'blue', fontSize: '16px' }}>
智能分析结果
</Divider>
<p style={{ fontWeight: 'bold', color: '#0b93a1' }}>{item.getResult}</p>
<Row justify="end">
<Col>
<Button danger onClick={() => handleDelete(item.id)}>
删除
</Button>
</Col>
</Row>
</Card>
</List.Item>
)}
/>
</div>
);
};
export default MyChartPage;
尚且不懂知识点记录
-
react的数据状态:
//定义了一个searchParams的变量 const [searchParams,setSearchParams] = useState<Api.ChartQueryRequest>({ ...initSearchParams, //初始值设置,每页12条,...initSearchParams把此对象展开成新的对象,不会由对象污染 }); //定义变量存前端展示的数据,从数据返回值中取得数据 const [chartList, setChartList] = useState<API.Chart[]>(); //定义变量存储数据总数 const [total,setTotal] = useState<number>(0);
-
idea快捷键:
- 快速生成try-catch的快捷键:ctrl+alt+t
- 格式化代码:ctrl+alt+l
-
原子化css
通过className组合出一套组件