From 3bcff30458eaabeb9b5b1dc722678759edd8afc8 Mon Sep 17 00:00:00 2001 From: luchang <835498386@qq.com> Date: Thu, 23 May 2024 15:53:10 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=9B=BE=E7=89=87?= =?UTF-8?q?=E5=A4=96=E9=93=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/config.prod.ts | 9 ++ src/components/Banner/Upload.tsx | 69 ++++++++++++++ src/components/Banner/index.less | 45 ++++----- src/components/Banner/index.tsx | 152 ++++++++++++++++++++----------- src/pages/Banner.tsx | 48 +++++----- src/pages/User/Login/index.tsx | 32 +------ 6 files changed, 229 insertions(+), 126 deletions(-) create mode 100644 config/config.prod.ts create mode 100644 src/components/Banner/Upload.tsx diff --git a/config/config.prod.ts b/config/config.prod.ts new file mode 100644 index 0000000..9465d02 --- /dev/null +++ b/config/config.prod.ts @@ -0,0 +1,9 @@ +import { defineConfig } from '@umijs/max'; + +export default defineConfig({ + define: { + 'process.env': { + API_HOST_URL: 'http://localhost:3000', + }, + }, +}); diff --git a/src/components/Banner/Upload.tsx b/src/components/Banner/Upload.tsx new file mode 100644 index 0000000..8f50444 --- /dev/null +++ b/src/components/Banner/Upload.tsx @@ -0,0 +1,69 @@ +import React, { useState } from 'react'; +import { Image, Upload } from 'antd'; +import { LoadingOutlined, PlusOutlined, CloseOutlined } from '@ant-design/icons'; +import type { GetProp, UploadProps } from 'antd'; +import styles from './index.less'; + +interface IFormUpload { + value?: string; + onChange?: (val: string) => void; +} +type FileType = Parameters>[0]; + +const getBase64 = (img: FileType, callback: (url: string) => void) => { + const reader = new FileReader(); + reader.addEventListener('load', () => callback(reader.result as string)); + reader.readAsDataURL(img); +}; + +const FormUpload: React.FC = ({ onChange }) => { + const [loading, setLoading] = useState(false); + const [imageUrl, setImageUrl] = useState(''); + + const handleUpload = async (info: any) => { + if (info.file.status === 'uploading') { + setLoading(true); + return; + } + if (info.file.status === 'done') { + getBase64(info.file.originFileObj as FileType, (url) => { + setLoading(false); + setImageUrl(url); + }); + } + onChange?.(info); + }; + + const uploadButton = ( + + ); + + return ( + <> + {imageUrl ? ( +
+ +
setImageUrl('')}> + +
+
+ ) : ( + + {uploadButton} + + )} + + ); +}; + +export default FormUpload; diff --git a/src/components/Banner/index.less b/src/components/Banner/index.less index fe0d551..c758a9c 100644 --- a/src/components/Banner/index.less +++ b/src/components/Banner/index.less @@ -3,29 +3,30 @@ grid-template-columns: repeat(3, 1fr); gap: 20px; margin-bottom: 30px; - .bannerItem { - border-radius: 10px; - // overflow: hidden; - max-width: 400px; - position: relative; +} + +.bannerItem { + border-radius: 10px; + // overflow: hidden; + max-width: 400px; + position: relative; + .operation { + display: none; + position: absolute; + background-color: red; + width: 30px; + height: 30px; + border-radius: 50%; + text-align: center; + line-height: 30px; + color: white; + top: -10px; + right: -10px; + cursor: pointer; + } + &:hover { .operation { - display: none; - position: absolute; - background-color: red; - width: 30px; - height: 30px; - border-radius: 50%; - text-align: center; - line-height: 30px; - color: white; - top: -10px; - right: -10px; - cursor: pointer; - } - &:hover { - .operation { - display: block; - } + display: block; } } } diff --git a/src/components/Banner/index.tsx b/src/components/Banner/index.tsx index d8879cd..e791c5d 100644 --- a/src/components/Banner/index.tsx +++ b/src/components/Banner/index.tsx @@ -1,80 +1,126 @@ import React, { useState, useEffect, memo } from 'react'; -import { LoadingOutlined, PlusOutlined, CloseOutlined } from '@ant-design/icons'; -import { Image, Upload } from 'antd'; -import type { UploadProps } from 'antd'; +import { Image, Popconfirm, Table, Button, Modal, Form, Input } from 'antd'; +import type { TableColumnsType } from 'antd'; +import FormUpload from './Upload'; import styles from './index.less'; interface IItemImage { id: number; url: string; + path?: string; } -interface IBanner { +type FieldType = { + file?: string; + path?: string; +}; + +export interface IBanner { type: number; dataSource: IItemImage[]; - onChange: (info: any, type: number) => void; + onChange: (fileInfo: any, params: { type: number; path: string }) => void; onDelFile: (id: number) => void; } const Banner: React.FC = ({ type, dataSource, onChange, onDelFile }) => { - const [loading, setLoading] = useState(false); const [dataList, setDataList] = useState(dataSource); - - const handleChange: UploadProps['onChange'] = async (info) => { - if (info.file.status === 'uploading') { - setLoading(true); - return; - } - if (info.file.status === 'done') { - onChange?.(info, type); - } - setLoading(false); - }; + const [open, setOpen] = useState(); + const [form] = Form.useForm(); useEffect(() => { setDataList(dataSource); }, [dataSource]); - const uploadButton = ( - - ); + const columns: TableColumnsType = [ + { + title: '图片', + dataIndex: 'url', + width: '300px', + render: (_: any, record: any) => { + return ( + <> + + + ); + }, + }, + { + title: '跳转链接', + align: 'center', + dataIndex: 'path', + }, + { + title: '上传时间', + align: 'center', + dataIndex: 'createTime', + width: 200, + }, + { + title: '操作', + width: 80, + align: 'center', + dataIndex: 'operation', + render: (_: any, record: any) => + dataSource.length >= 1 ? ( + onDelFile((record as IItemImage).id)} + > + 删除 + + ) : null, + }, + ]; + + const handleOk = () => { + const { file, path } = form.getFieldsValue() || {}; + onChange?.(file, { + type, + path, + }); + setOpen(false); + }; return ( <> - - console.log(`current index: ${current}, prev index: ${prev}`), - }} - > -
- {dataList.map((item) => { - return ( -
- -
onDelFile(item.id)}> - -
-
- ); - })} -
-
-
- - {uploadButton} - +
+
+ setOpen(false)} + destroyOnClose + > +
+ + label="图片" + name="file" + rules={[{ required: true, message: 'Please input your username!' }]} + > + + + + label="跳转链接" name="path"> + + + +
+ ); }; diff --git a/src/pages/Banner.tsx b/src/pages/Banner.tsx index 6bf1e01..19fb10d 100644 --- a/src/pages/Banner.tsx +++ b/src/pages/Banner.tsx @@ -1,10 +1,9 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useMemo } from 'react'; import { PageContainer } from '@ant-design/pro-components'; -import { Card } from 'antd'; +import { Card, Tabs } from 'antd'; import { useModel } from '@umijs/max'; import Banner from '@/components/Banner'; import { getBanners, deleteBanner, uploadBanner } from '@/services/ant-design-pro/api'; -import styles from './index.less'; import { requestUpload } from '@/utils/upload'; import { bannerTypes } from '@/constant'; @@ -32,12 +31,11 @@ const Page = () => { * @param info 文件内容 * @param type 轮播图类型 */ - const handleFileUpload = async (info: any, type: number) => { - const file = info.file.originFileObj; - const fileName = info.file.name; + const handleFileUpload = async (fileInfo: any, params: { type: number; path: string }) => { + const file = fileInfo.file.originFileObj; + const fileName = fileInfo.file.name; const url = await requestUpload(file as Blob, fileName); - await uploadBanner({ url, type }); - await getBanners(); + await uploadBanner({ url, ...params }); getBannerList(); }; @@ -50,6 +48,25 @@ const Page = () => { getBannerList(); }; + const items = useMemo(() => { + return bannerTypes.map((itemTab) => { + const dataSource = dataList.filter((_item) => itemTab.type === _item.type); + return { + key: itemTab.type + '', + label: itemTab.name, + children: ( + + ), + }; + }); + }, [dataList]); + return ( <> @@ -64,20 +81,7 @@ const Page = () => { : 'background-image: linear-gradient(75deg, #FBFDFF 0%, #F5F7FF 100%)', }} > - {bannerTypes.map((item) => { - const dataSource = dataList.filter((_item) => item.type === _item.type); - return ( -
-

{item.name}

- -
- ); - })} +
diff --git a/src/pages/User/Login/index.tsx b/src/pages/User/Login/index.tsx index c3a2477..f2caf13 100644 --- a/src/pages/User/Login/index.tsx +++ b/src/pages/User/Login/index.tsx @@ -1,22 +1,8 @@ -import { Footer } from '@/components'; import { login } from '@/services/ant-design-pro/api'; -import { getFakeCaptcha } from '@/services/ant-design-pro/login'; -import { - AlipayCircleOutlined, - LockOutlined, - MobileOutlined, - TaobaoCircleOutlined, - UserOutlined, - WeiboCircleOutlined, -} from '@ant-design/icons'; -import { - LoginForm, - ProFormCaptcha, - ProFormCheckbox, - ProFormText, -} from '@ant-design/pro-components'; +import { LockOutlined, UserOutlined } from '@ant-design/icons'; +import { LoginForm, ProFormText } from '@ant-design/pro-components'; import { FormattedMessage, history, SelectLang, useIntl, useModel, Helmet } from '@umijs/max'; -import { Alert, message, Tabs } from 'antd'; +import { Alert, message } from 'antd'; import Settings from '../../../../config/defaultSettings'; import React, { useState } from 'react'; import { flushSync } from 'react-dom'; @@ -58,18 +44,6 @@ const useStyles = createStyles(({ token }) => { }; }); -const ActionIcons = () => { - const { styles } = useStyles(); - - return ( - <> - - - - - ); -}; - const Lang = () => { const { styles } = useStyles();