Koa商城项目-轮播图模块(前端)

 前言

通过这次独自做前后端发现有很多需要提升的地方,很多细节处理不到位。下面简单看一下本人自己做的效果吧~~

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地址啦

  • 6
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

苦逼的猿宝

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值