前言
通过这次独自做前后端发现有很多需要提升的地方,很多细节处理不到位。下面简单看一下本人自己做的效果吧~~
Git地址
https://gitee.com/ah-ah-bao/koa_system
效果图
前端代码
api/banner.ts
import request from "../utils/request";
export const getBannerList = (params: any = {}) => {
return request.post("/banner/list", { ...params });
};
export const getBannerAdd = (params = {}) => {
return request.post("/banner/add", params);
};
export const getBannerUpdate = (params = {}) => {
return request.post("/banner/update", params);
};
export const getBannerDetail = (params = {}) => {
return request.post("/banner/detail", params);
};
export const getBannerDelete = (params = {}) => {
return request.post("/banner/delete", params);
};
export const getBannerUpload = (params = {}) => {
return request.post("/banner/upload", params);
};
pages/BannerList/index.tsx
import { useEffect, useState } from 'react';
import { message, Table, Popconfirm } from 'antd';
import { getBannerList, getBannerDelete } from '../../api/banner'
import type { BannerData, pageGationBannerData } from '../../types/banner';
import { BannerColumnsList } from './columns'
import SearchForm from './SearchForm'
import BannerAddOrEdit from './BannerAddOrEdit';
const BannerList = () => {
const [bannerList, setBannerList] = useState<BannerData[]>([]);
const [columns, setColumns] = useState<any[]>([]);
const [visible, setVisible] = useState<boolean>(false);
const [selectedId, setSelectedId] = useState<number | null>(null);
const [pagination, setPagination] = useState({
page: 1,
pageSize: 10,
total: 0,
showSizeChanger: true,
hideOnSinglePage: false,
pageSizeOptions: ['10', '50', '100', '200'],
showTotal: (total: number) => `共 ${total} 条数据`,
})
const [queryParams, setQueryParams] = useState<pageGationBannerData>({
page: 1,
pageSize: 10,
bannername: '',
url: ''
})
const getTableColmuns = async () => {
var columns = [];
const bannerColumns: any = await BannerColumnsList();
columns = [
...bannerColumns,
{
title: '操作',
key: 'operation',
align: 'center',
width: 120,
render: (_text: any, record: any) =>
[
<div className='banner_opertion'>
<div key={`${record.id}-update`} onClick={() => handleUpdateClick(record.id)}>修改</div>
<Popconfirm
title="确定要删除这条记录吗?"
onConfirm={() => {
try {
getBannerDelete({
id: record.id
}).then((res: any) => {
if (res.data.code === 200) {
message.success('删除成功');
fetchData();
} else {
message.error(res.data.message);
}
})
} catch (error) {
message.error('删除失败!');
}
}
}
okText="确定"
cancelText="取消"
>
<div key={`${record.id}-delete`}>删除</div>
</Popconfirm>
</div>
]
}
];
return columns; // 确保返回值
}
const handleUpdateClick = (id: number) => {
setSelectedId(id);
setVisible(true);
};
const getBannerListData = async () => {
try {
const res = await getBannerList(queryParams);
if (res.data.code === 200) {
setBannerList(res.data.data);
setPagination({
...pagination,
})
} else {
message.error(res.data.message);
}
} catch (error) {
message.error('获取数据失败!');
}
}
const fetchData = async () => {
try {
await getBannerListData();
const column = await getTableColmuns();
setColumns(column);
} catch (error) {
message.error('获取数据失败!');
}
};
useEffect(() => {
fetchData();
}, [])
const handleMessageSearch = (msg: any) => {
setQueryParams({ ...queryParams, bannername: msg })
};
const getBannerAdd = (msg: any) => {
setVisible(msg)
}
const handleModalClose = () => {
setVisible(false);
setSelectedId(null);
};
useEffect(() => {
getBannerListData();
}, [queryParams]);
return (
<>
<SearchForm SumbitSearch={handleMessageSearch} SumbitAdd={getBannerAdd} />
<BannerAddOrEdit visible={visible} onClose={handleModalClose} id={selectedId} onRefresh={fetchData} />
<Table
columns={columns}
dataSource={bannerList}
pagination={pagination}
rowKey={(record: any) => `table_${record.id}`}
scroll={{ x: 1200 }}
/>
</>
)
}
export default BannerList;
pages/BannerList/columns.tsx
import type { TableProps } from 'antd';
import type { BannerData } from '../../types/banner';
import { Image } from 'antd';
export const BannerColumnsList: () => Promise<TableProps<BannerData>['columns']> = async () => {
return [
{
title: 'id',
dataIndex: 'id',
key: 'id',
align: 'center',
},
{
title: '轮播图名称',
dataIndex: 'bannername',
key: 'bannername',
align: 'center',
},
{
title: '图片',
dataIndex: 'url',
key: 'url',
align: 'center',
render: (url: string) => (
<Image
width={80}
height={80}
src={url}
/>
),
},
]
}
pages/BannerList/SearchForm.tsx
import type { FormProps } from 'antd';
import { Button, Form, Input, Row, Col } from 'antd';
import { SearchOutlined, PlusOutlined, RedoOutlined } from '@ant-design/icons';
import { useEffect } from 'react';
import './banner.sass'
type FieldType = {
bannername?: string;
};
const SearchForm = (props: any) => {
const [form] = Form.useForm();
const onFinish: FormProps<FieldType>['onFinish'] = (values) => {
props.SumbitSearch(values.bannername);
};
const onReset = () => {
form.resetFields();
onFinish(form.getFieldsValue())
};
//新增轮播图弹窗
const addBannerModal = () => {
// 弹窗显示
props.SumbitAdd(true)
}
useEffect(() => {
form.setFieldsValue({
bannername: ''
})
}, [])
return (
<>
<Form
name="basic"
labelCol={{ span: 4 }}
wrapperCol={{ span: 20 }}
onFinish={onFinish}
autoComplete="off"
form={form}
>
<Row gutter={24}>
<Col span={12}>
<Form.Item<FieldType>
label="轮播图名称"
name="bannername"
>
<Input />
</Form.Item></Col>
<Col span={12}>
<Form.Item wrapperCol={{ offset: 8, span: 16 }}>
<Button type="primary" htmlType="submit">
<SearchOutlined /> 搜索
</Button>
<Button className='banner_button' onClick={onReset}>
<RedoOutlined />重置
</Button>
<Button className='banner_button' type="primary" onClick={addBannerModal}>
<PlusOutlined /> 新增轮播图
</Button>
</Form.Item></Col>
</Row>
</Form>
</>
)
}
export default SearchForm;
pages/BannerList/BannerAddOrEdit.tsx
import React, { useState, useEffect } from 'react';
import { Modal, Button, message, Upload, Input, Form, Image } from 'antd';
import { UploadOutlined } from '@ant-design/icons';
import type { UploadProps } from 'antd';
import { getBannerAdd, getBannerUpdate, getBannerDetail } from '../../api/banner'
import { API_URL, getToken } from '../../../common'
import type { BannerAddOrEditProps, } from '../../types/banner'
const BannerAdd: React.FC<BannerAddOrEditProps> = ({ visible, onClose, onRefresh, id }) => {
const [localVisible, setLocalVisible] = useState(visible);
const [title, setTitle] = useState<string>('');
const [url, setUrl] = useState<string>('');
const [bannername, setBannername] = useState<string>('')
const [confirmLoading, setConfirmLoading] = useState<boolean>(false);
const [form] = Form.useForm();
useEffect(() => {
setLocalVisible(visible);
if (!id) {
setBannername('')
setUrl('')
} else {
getBannerDetail({
id: id
}).then((res: any) => {
if (res.data.code === 200) {
setTitle('修改轮播图')
setBannername(res.data.data.bannername)
setUrl(res.data.data.url)
form.setFieldsValue({
bannername: res.data.data.bannername,
url: res.data.data.url,
});
}
})
}
}, [visible]);
useEffect(() => {
if (id) {
setTitle('修改轮播图')
} else {
setTitle('新增轮播图')
}
}, [id])
const handleOk = () => {
try {
setConfirmLoading(true);
if (id) {
getBannerUpdate({
id: id,
bannername,
url
}).then((res: any) => {
if (res.data.code === 200) {
message.success(res.data.message);
setConfirmLoading(false);
setLocalVisible(false);
onClose();
onRefresh(); // 调用父组件传递的回调函数
} else {
message.error(res.data.message);
setConfirmLoading(false);
}
})
} else {
getBannerAdd({
bannername,
url
}).then((res: any) => {
if (res.data.code === 200) {
message.success(res.data.message);
setConfirmLoading(false);
setLocalVisible(false);
onClose();
onRefresh(); // 调用父组件传递的回调函数
} else {
message.error(res.data.message);
setConfirmLoading(false);
}
})
}
} catch (err: any) {
message.error(err);
}
};
const handleCancel = () => {
setLocalVisible(false);
onClose();
};
const props: UploadProps = {
name: 'file',
action: API_URL + '/banner/upload',
headers: {
Authorization: getToken() || 'defaultTokenValue',
},
onChange(info) {
if (info.file.status !== 'uploading') {
// console.log(info.file, info.fileList);
}
if (info.file.status === 'done') {
message.success(`${info.file.name} file uploaded successfully`);
setUrl(info.file.response.data.url);
} else if (info.file.status === 'error') {
message.error(`${info.file.name} file upload failed.`);
}
},
};
return (
<div>
<Modal title={title} open={localVisible} onOk={handleOk} onCancel={handleCancel} confirmLoading={confirmLoading} footer={
[
<Button key="cancel" onClick={handleCancel}>取消</Button>,
<Button key="confirm" type="primary" onClick={handleOk} loading={confirmLoading}>确认</Button>
]
}>
<Form layout="vertical" form={form}>
<Form.Item label="轮播图名称" name="bannername" rules={[{ required: true, message: '请输入轮播图名称' }]}>
<Input placeholder="请输入轮播图名称" value={bannername} onChange={(e) => setBannername(e.target.value)}></Input>
</Form.Item>
<Form.Item label="轮播图" name="url" rules={[{ required: true, message: '请上传轮播图' }]}>
<Upload {...props} maxCount={1} showUploadList={false}>
<Button icon={<UploadOutlined />}>上传</Button>
</Upload>
</Form.Item>
{
url ? <Form.Item label="轮播图预览" name="url">
<Image width={200} src={url} />
</Form.Item> : ''
}
</Form>
</Modal>
</div>
)
}
export default BannerAdd;
pages/BannerList/index.sass
.banner_button
margin-left: 10px
.banner_opertion
display: flex
justify-content: space-evenly
align-items: center
cursor: pointer
color: #26a0f1
完整代码
看本人git地址啦