优化:按钮 Loading 效果 优化用户体验

This commit is contained in:
宇阳
2024-11-22 14:40:15 +08:00
parent eee0761473
commit afc74983ad
14 changed files with 89 additions and 69 deletions

View File

@@ -8,6 +8,8 @@ import "./index.scss"
const CatePage = () => { const CatePage = () => {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [btnLoading, setBtnLoading] = useState(false)
const [isModelOpen, setIsModelOpen] = useState(false); const [isModelOpen, setIsModelOpen] = useState(false);
const [cate, setCate] = useState<Cate>({} as Cate); const [cate, setCate] = useState<Cate>({} as Cate);
const [list, setList] = useState<Cate[]>([]); const [list, setList] = useState<Cate[]>([]);
@@ -21,6 +23,11 @@ const CatePage = () => {
setLoading(false); setLoading(false);
}; };
useEffect(() => {
setLoading(true);
getCateList();
}, []);
const addCateData = (id: number) => { const addCateData = (id: number) => {
setIsMethod("create") setIsMethod("create")
setIsModelOpen(true); setIsModelOpen(true);
@@ -52,6 +59,8 @@ const CatePage = () => {
}; };
const submit = async () => { const submit = async () => {
setBtnLoading(true)
form.validateFields().then(async (values: Cate) => { form.validateFields().then(async (values: Cate) => {
if (values.type === "cate") values.url = '/' if (values.type === "cate") values.url = '/'
@@ -71,6 +80,8 @@ const CatePage = () => {
getCateList(); getCateList();
setIsMethod("create") setIsMethod("create")
}) })
setBtnLoading(false)
}; };
const closeModel = () => { const closeModel = () => {
@@ -119,11 +130,6 @@ const CatePage = () => {
}) })
) )
useEffect(() => {
setLoading(true);
getCateList();
}, []);
return ( return (
<> <>
<Title value="分类管理"> <Title value="分类管理">
@@ -169,9 +175,8 @@ const CatePage = () => {
</Radio.Group> </Radio.Group>
</Form.Item> </Form.Item>
<Form.Item className='!mb-0 flex justify-end'> <Form.Item className='!mb-0 w-full'>
<Button onClick={closeModel}></Button> <Button type="primary" onClick={submit} loading={btnLoading} className='w-full ml-2'></Button>
<Button type="primary" onClick={submit} className='ml-2'></Button>
</Form.Item> </Form.Item>
</Form> </Form>
</Modal> </Modal>

View File

@@ -31,6 +31,8 @@ const PublishForm = ({ data, closeModel }: { data: Article, closeModel: () => vo
const [params] = useSearchParams() const [params] = useSearchParams()
const id = +params.get('id')! const id = +params.get('id')!
const [btnLoading, setBtnLoading] = useState(false)
const [form] = Form.useForm() const [form] = Form.useForm()
const navigate = useNavigate() const navigate = useNavigate()
@@ -53,7 +55,6 @@ const PublishForm = ({ data, closeModel }: { data: Article, closeModel: () => vo
form.setFieldsValue({ form.setFieldsValue({
...data, ...data,
top: data.config.top === 1,
status: data.config.status, status: data.config.status,
password: data.config.password, password: data.config.password,
cateIds, cateIds,
@@ -83,6 +84,8 @@ const PublishForm = ({ data, closeModel }: { data: Article, closeModel: () => vo
}; };
const onSubmit: FormProps<FieldType>['onFinish'] = async (values) => { const onSubmit: FormProps<FieldType>['onFinish'] = async (values) => {
setBtnLoading(true)
// 如果是文章标签,则先判断是否存在,如果不存在则添加 // 如果是文章标签,则先判断是否存在,如果不存在则添加
let tagIds: number[] = [] let tagIds: number[] = []
for (const item of (values.tagIds ? values.tagIds : [])) { for (const item of (values.tagIds ? values.tagIds : [])) {
@@ -145,6 +148,8 @@ const PublishForm = ({ data, closeModel }: { data: Article, closeModel: () => vo
navigate("/article") navigate("/article")
// 初始化表单 // 初始化表单
form.resetFields() form.resetFields()
setBtnLoading(false)
} }
// 初始表单数据 // 初始表单数据
@@ -222,7 +227,7 @@ const PublishForm = ({ data, closeModel }: { data: Article, closeModel: () => vo
</Form.Item> </Form.Item>
<Form.Item> <Form.Item>
<Button type="primary" htmlType="submit" className="w-full">{id ? "编辑文章" : "发布文章"}</Button> <Button type="primary" htmlType="submit" loading={btnLoading} className="w-full">{id ? "编辑文章" : "发布文章"}</Button>
</Form.Item> </Form.Item>
</Form> </Form>
</> </>

View File

@@ -8,6 +8,8 @@ import dayjs from 'dayjs';
const FootprintPage = () => { const FootprintPage = () => {
const [loading, setLoading] = useState<boolean>(false); const [loading, setLoading] = useState<boolean>(false);
const [btnLoading, setBtnLoading] = useState(false)
const [footprintList, setFootprintList] = useState<Footprint[]>([]); const [footprintList, setFootprintList] = useState<Footprint[]>([]);
const [isModelOpen, setIsModelOpen] = useState(false); const [isModelOpen, setIsModelOpen] = useState(false);
const [footprint, setFootprint] = useState<Footprint>({} as Footprint); const [footprint, setFootprint] = useState<Footprint>({} as Footprint);
@@ -129,6 +131,8 @@ const FootprintPage = () => {
}; };
const onSubmit = async () => { const onSubmit = async () => {
setBtnLoading(true)
form.validateFields().then(async (values: Footprint) => { form.validateFields().then(async (values: Footprint) => {
values.createTime = values.createTime.valueOf() values.createTime = values.createTime.valueOf()
values.images = values.images ? (values.images as string).split("\n") : [] values.images = values.images ? (values.images as string).split("\n") : []
@@ -144,6 +148,8 @@ const FootprintPage = () => {
reset() reset()
getFootprintList(); getFootprintList();
}); });
setBtnLoading(false)
}; };
const closeModel = () => reset(); const closeModel = () => reset();
@@ -229,9 +235,8 @@ const FootprintPage = () => {
<DatePicker showTime placeholder='请选择时间' className='w-full' /> <DatePicker showTime placeholder='请选择时间' className='w-full' />
</Form.Item> </Form.Item>
<Form.Item className='!mb-0 flex justify-end'> <Form.Item className='!mb-0 w-full'>
<Button onClick={closeModel}></Button> <Button type="primary" onClick={onSubmit} loading={btnLoading} className='w-full ml-2'></Button>
<Button type="primary" onClick={onSubmit} className='ml-2'></Button>
</Form.Item> </Form.Item>
</Form> </Form>
</Modal> </Modal>

View File

@@ -10,6 +10,8 @@ import "./index.scss"
const RolePage = () => { const RolePage = () => {
const [loading, setLoading] = useState<boolean>(false); const [loading, setLoading] = useState<boolean>(false);
const [bindingLoading, setBindingLoading] = useState<boolean>(false); const [bindingLoading, setBindingLoading] = useState<boolean>(false);
const [addLoading, setAddLoading] = useState(false)
const [id, setId] = useState(0) const [id, setId] = useState(0)
const [role, setRole] = useState<Role>({} as Role); const [role, setRole] = useState<Role>({} as Role);
const [roleList, setRoleList] = useState<Role[]>([]); const [roleList, setRoleList] = useState<Role[]>([]);
@@ -88,6 +90,8 @@ const RolePage = () => {
const onSubmit = async () => { const onSubmit = async () => {
setLoading(true); setLoading(true);
setAddLoading(true)
form.validateFields().then(async (values: Role) => { form.validateFields().then(async (values: Role) => {
if (role.id) { if (role.id) {
await editRoleDataAPI({ ...role, ...values }); await editRoleDataAPI({ ...role, ...values });
@@ -102,6 +106,8 @@ const RolePage = () => {
form.setFieldsValue({ name: '', description: '' }) form.setFieldsValue({ name: '', description: '' })
setRole({} as Role); setRole({} as Role);
}); });
setAddLoading(false)
}; };
const onChange: any = (list: number[]) => { const onChange: any = (list: number[]) => {
@@ -151,7 +157,7 @@ const RolePage = () => {
</Form.Item> </Form.Item>
<Form.Item> <Form.Item>
<Button type="primary" htmlType="submit" className="w-full">{role.id ? '编辑角色' : '新增角色'}</Button> <Button type="primary" htmlType="submit" loading={addLoading} className="w-full">{role.id ? '编辑角色' : '新增角色'}</Button>
</Form.Item> </Form.Item>
</Form> </Form>
</Card> </Card>
@@ -186,7 +192,7 @@ const RolePage = () => {
</Spin> </Spin>
</div> </div>
<Button type='primary' className='w-full mt-2' onClick={onBindingRouteSubmit}></Button> <Button type='primary' className='w-full mt-2' loading={bindingLoading} onClick={onBindingRouteSubmit}></Button>
</Modal> </Modal>
</> </>
); );

View File

@@ -7,6 +7,8 @@ import { ColumnsType } from 'antd/es/table';
const RoutePage = () => { const RoutePage = () => {
const [loading, setLoading] = useState<boolean>(false); const [loading, setLoading] = useState<boolean>(false);
const [btnLoading, setBtnLoading] = useState(false)
const [route, setRoute] = useState<Route>({} as Route); const [route, setRoute] = useState<Route>({} as Route);
const [list, setList] = useState<Route[]>([]); const [list, setList] = useState<Route[]>([]);
@@ -53,6 +55,8 @@ const RoutePage = () => {
const onSubmit = async () => { const onSubmit = async () => {
setLoading(true); setLoading(true);
setBtnLoading(true)
form.validateFields().then(async (values: Route) => { form.validateFields().then(async (values: Route) => {
if (route.id) { if (route.id) {
@@ -68,6 +72,8 @@ const RoutePage = () => {
form.setFieldsValue({ path: '', description: '' }) form.setFieldsValue({ path: '', description: '' })
setRoute({} as Route); setRoute({} as Route);
}); });
setBtnLoading(false)
}; };
return ( return (
@@ -93,7 +99,7 @@ const RoutePage = () => {
</Form.Item> </Form.Item>
<Form.Item> <Form.Item>
<Button type="primary" htmlType="submit" className="w-full">{route.id ? '编辑路由' : '新增路由'}</Button> <Button type="primary" htmlType="submit" loading={btnLoading} className="w-full">{route.id ? '编辑路由' : '新增路由'}</Button>
</Form.Item> </Form.Item>
</Form> </Form>
</Card> </Card>

View File

@@ -17,19 +17,22 @@ const UserPage = () => {
const store = useUserStore(); const store = useUserStore();
const getUserData = async () => { const getUserData = async () => {
setLoading(true);
const { data } = await getUserDataAPI(store.user?.id); const { data } = await getUserDataAPI(store.user?.id);
store.setUser(data); store.setUser(data);
form.setFieldsValue(data); form.setFieldsValue(data);
setLoading(false); setLoading(false);
}; };
useEffect(() => { useEffect(() => {
setLoading(true);
getUserData(); getUserData();
}, []); }, []);
const onSubmit = async (values: UserForm) => { const onSubmit = async (values: UserForm) => {
setLoading(false) setLoading(true)
await editUserDataAPI({ await editUserDataAPI({
id: store.user.id, ...values, id: store.user.id, ...values,
role: undefined role: undefined
@@ -37,6 +40,8 @@ const UserPage = () => {
message.success("🎉 修改用户信息成功"); message.success("🎉 修改用户信息成功");
store.setUser(values as User); store.setUser(values as User);
getUserData(); getUserData();
setLoading(false)
}; };
return ( return (

View File

@@ -2,12 +2,15 @@ import { Form, Input, Button, notification, Modal } from 'antd';
import { useUserStore } from '@/stores'; import { useUserStore } from '@/stores';
import { editAdminPassAPI } from '@/api/User'; import { editAdminPassAPI } from '@/api/User';
import { EditUser } from '@/types/app/user' import { EditUser } from '@/types/app/user'
import { useState } from 'react';
const { confirm } = Modal; const { confirm } = Modal;
const SystemPage = () => { const SystemPage = () => {
const store = useUserStore(); const store = useUserStore();
const [loading, setLoading] = useState(false)
const [form] = Form.useForm<EditUser>(); const [form] = Form.useForm<EditUser>();
const initialValues: EditUser = { const initialValues: EditUser = {
@@ -33,6 +36,8 @@ const SystemPage = () => {
const handleSubmit = async (values: EditUser) => { const handleSubmit = async (values: EditUser) => {
try { try {
setLoading(true)
await editAdminPassAPI(values); await editAdminPassAPI(values);
confirm({ confirm({
title: '提示', title: '提示',
@@ -43,7 +48,11 @@ const SystemPage = () => {
}, },
cancelButtonProps: { style: { display: 'none' } } cancelButtonProps: { style: { display: 'none' } }
}); });
setLoading(false)
} catch (error) { } catch (error) {
setLoading(false)
notification.error({ notification.error({
message: '错误', message: '错误',
description: '修改密码失败,请重试:' + error description: '修改密码失败,请重试:' + error
@@ -88,7 +97,7 @@ const SystemPage = () => {
</Form.Item> </Form.Item>
<Form.Item> <Form.Item>
<Button type="primary" htmlType="submit" className="w-full"> <Button type="primary" htmlType="submit" loading={loading} className="w-full">
</Button> </Button>
</Form.Item> </Form.Item>

View File

@@ -7,6 +7,7 @@ import FileUpload from '@/components/FileUpload';
const ThemePage = () => { const ThemePage = () => {
const [loading, setLoading] = useState<boolean>(false); const [loading, setLoading] = useState<boolean>(false);
const [isModalOpen, setIsModalOpen] = useState<boolean>(false); const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
const [swiperText, setSwiperText] = useState<string>(''); const [swiperText, setSwiperText] = useState<string>('');
const [social, setSocial] = useState<string>(''); const [social, setSocial] = useState<string>('');
@@ -23,15 +24,15 @@ const ThemePage = () => {
const getLayoutData = async () => { const getLayoutData = async () => {
setLoading(true); setLoading(true);
const { data } = await getThemeDataAPI(); const { data } = await getThemeDataAPI();
setTheme(data); setTheme(data);
console.log(data);
setSwiperText(data.swiperText ? JSON.parse(data.swiperText).join('\n') : ''); setSwiperText(data.swiperText ? JSON.parse(data.swiperText).join('\n') : '');
setSocial(data.social ? JSON.parse(data.social).join("\n") : ''); setSocial(data.social ? JSON.parse(data.social).join("\n") : '');
setCover(data.covers ? JSON.parse(data.covers).join("\n") : ''); setCover(data.covers ? JSON.parse(data.covers).join("\n") : '');
setRecoArticle(data.recoArticle ? JSON.parse(data.recoArticle).join("\n") : ''); setRecoArticle(data.recoArticle ? JSON.parse(data.recoArticle).join("\n") : '');
setLoading(false); setLoading(false);
}; };
@@ -55,6 +56,7 @@ const ThemePage = () => {
message: '成功', message: '成功',
description: '🎉 修改主题成功', description: '🎉 修改主题成功',
}); });
setLoading(false); setLoading(false);
}; };
@@ -194,7 +196,7 @@ const ThemePage = () => {
</div> </div>
</div> </div>
<Button type="primary" size="large" className="w-full mt-4" onClick={editLayoutData}></Button> <Button type="primary" size="large" className="w-full mt-4" loading={loading} onClick={editLayoutData}></Button>
</div> </div>
</Spin> </Spin>

View File

@@ -65,22 +65,6 @@ const WebPage = () => {
<Input placeholder="https://liuyuyang.net/favicon.ico" /> <Input placeholder="https://liuyuyang.net/favicon.ico" />
</Form.Item> </Form.Item>
{/* <Form.Item
label="光亮主题LOGO"
name="lightLogo"
rules={[{ required: true, message: '网站LOGO不能为空' }]}
>
<Input placeholder="https://liuyuyang.net/logo.png" />
</Form.Item>
<Form.Item
label="暗黑主题LOGO"
name="darkLogo"
rules={[{ required: true, message: '网站LOGO不能为空' }]}
>
<Input placeholder="https://liuyuyang.net/logo.png" />
</Form.Item> */}
<Form.Item <Form.Item
label="网站描述" label="网站描述"
name="description" name="description"
@@ -97,28 +81,6 @@ const WebPage = () => {
<Input placeholder="Java,前端,Python" /> <Input placeholder="Java,前端,Python" />
</Form.Item> </Form.Item>
{/* <Form.Item
label="随机文章封面"
name="covers"
rules={[{ required: true, message: '网站随机封面不能为空' }]}
>
<Input.TextArea
autoSize={{ minRows: 2, maxRows: 10 }}
placeholder="随机文章封面"
/>
</Form.Item>
<Form.Item
label="社交网站"
name="social"
rules={[{ required: true, message: '社交网站不能为空' }]}
>
<Input.TextArea
autoSize={{ minRows: 2, maxRows: 10 }}
placeholder="社交账号"
/>
</Form.Item> */}
<Form.Item <Form.Item
label="底部信息" label="底部信息"
name="footer" name="footer"

View File

@@ -4,7 +4,7 @@ import { Card } from 'antd';
import Title from '@/components/Title'; import Title from '@/components/Title';
import System from './components/System' import System from './components/System'
import Web from './components/Web' import Web from './components/Web'
import Layout from './components/Theme' import Theme from './components/Theme'
import My from './components/My' import My from './components/My'
// import Other from './components/Other' // import Other from './components/Other'
@@ -80,7 +80,7 @@ const SetupPage = () => {
<div className='w-full md:w-[80%] px-0 md:px-8'> <div className='w-full md:w-[80%] px-0 md:px-8'>
{active === "system" && <System />} {active === "system" && <System />}
{active === "web" && <Web />} {active === "web" && <Web />}
{active === "layout" && <Layout />} {active === "layout" && <Theme />}
{active === "my" && <My />} {active === "my" && <My />}
{/* {active === "other" && <Other />} */} {/* {active === "other" && <Other />} */}
</div> </div>

View File

@@ -9,6 +9,8 @@ import FileUpload from '@/components/FileUpload';
const SwiperPage = () => { const SwiperPage = () => {
const [loading, setLoading] = useState<boolean>(false); const [loading, setLoading] = useState<boolean>(false);
const [btnLoading, setBtnLoading] = useState(false)
const [swiper, setSwiper] = useState<Swiper>({} as Swiper); const [swiper, setSwiper] = useState<Swiper>({} as Swiper);
const [list, setList] = useState<Swiper[]>([]); const [list, setList] = useState<Swiper[]>([]);
const [isModalOpen, setIsModalOpen] = useState<boolean>(false); const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
@@ -62,7 +64,8 @@ const SwiperPage = () => {
}; };
const onSubmit = async () => { const onSubmit = async () => {
setLoading(true); setBtnLoading(true)
form.validateFields().then(async (values: Swiper) => { form.validateFields().then(async (values: Swiper) => {
if (swiper.id) { if (swiper.id) {
await editSwiperDataAPI({ ...swiper, ...values }); await editSwiperDataAPI({ ...swiper, ...values });
@@ -77,6 +80,8 @@ const SwiperPage = () => {
form.resetFields(); form.resetFields();
setSwiper({} as Swiper); setSwiper({} as Swiper);
}) })
setBtnLoading(false)
}; };
const handleTabChange = (key: string) => { const handleTabChange = (key: string) => {
@@ -141,7 +146,7 @@ const SwiperPage = () => {
</Form.Item> </Form.Item>
<Form.Item> <Form.Item>
<Button type="primary" htmlType="submit" className="w-full">{swiper.id ? '编辑轮播图' : '新增轮播图'}</Button> <Button type="primary" htmlType="submit" loading={btnLoading} className="w-full">{swiper.id ? '编辑轮播图' : '新增轮播图'}</Button>
</Form.Item> </Form.Item>
</Form> </Form>
</> </>

View File

@@ -7,6 +7,7 @@ import { ColumnsType } from 'antd/es/table';
const TagPage = () => { const TagPage = () => {
const [loading, setLoading] = useState<boolean>(false); const [loading, setLoading] = useState<boolean>(false);
const [tag, setTag] = useState<Tag>({} as Tag); const [tag, setTag] = useState<Tag>({} as Tag);
const [list, setList] = useState<Tag[]>([]); const [list, setList] = useState<Tag[]>([]);
@@ -87,7 +88,7 @@ const TagPage = () => {
</Form.Item> </Form.Item>
<Form.Item> <Form.Item>
<Button type="primary" htmlType="submit" className="w-full">{tag.id ? '编辑标签' : '新增标签'}</Button> <Button type="primary" htmlType="submit" loading={loading} className="w-full">{tag.id ? '编辑标签' : '新增标签'}</Button>
</Form.Item> </Form.Item>
</Form> </Form>
</Card> </Card>

View File

@@ -14,6 +14,7 @@ import dayjs from 'dayjs';
const UserPage = () => { const UserPage = () => {
const [loading, setLoading] = useState<boolean>(false); const [loading, setLoading] = useState<boolean>(false);
const [btnLoading, setBtnLoading] = useState(false)
const [userList, setUserList] = useState<User[]>([]); const [userList, setUserList] = useState<User[]>([]);
const [roleList, setRoleList] = useState<Role[]>([]); const [roleList, setRoleList] = useState<Role[]>([]);
@@ -143,6 +144,8 @@ const UserPage = () => {
} }
const onSubmit = async () => { const onSubmit = async () => {
setBtnLoading(true)
userForm.validateFields().then(async (values: User) => { userForm.validateFields().then(async (values: User) => {
if (user.id) { if (user.id) {
await editUserDataAPI({ ...user, ...values }); await editUserDataAPI({ ...user, ...values });
@@ -154,6 +157,8 @@ const UserPage = () => {
setDrawerVisible(false); setDrawerVisible(false);
getUserList(); getUserList();
}) })
setBtnLoading(false)
}; };
const [filterForm] = Form.useForm(); const [filterForm] = Form.useForm();
@@ -272,7 +277,7 @@ const UserPage = () => {
</Form.Item> </Form.Item>
<Form.Item> <Form.Item>
<Button type="primary" htmlType="submit" className="w-full">{user.id ? "编辑用户" : "创建用户"}</Button> <Button type="primary" htmlType="submit" loading={btnLoading} className="w-full">{user.id ? "编辑用户" : "创建用户"}</Button>
</Form.Item> </Form.Item>
</Form> </Form>
</Drawer> </Drawer>

View File

@@ -9,6 +9,8 @@ import './index.scss';
const LinkPage = () => { const LinkPage = () => {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [btnLoading, setBtnLoading] = useState(false)
const [tab, setTab] = useState<string>('list'); const [tab, setTab] = useState<string>('list');
const [list, setList] = useState<Web[]>([]); const [list, setList] = useState<Web[]>([]);
const [listTemp, setListTemp] = useState<Web[]>([]); const [listTemp, setListTemp] = useState<Web[]>([]);
@@ -72,9 +74,9 @@ const LinkPage = () => {
}; };
const submit = async () => { const submit = async () => {
form.validateFields().then(async (values: Web) => { setBtnLoading(true)
setLoading(true);
form.validateFields().then(async (values: Web) => {
if (isMethod === "edit") { if (isMethod === "edit") {
await editLinkDataAPI({ ...link, ...values }); await editLinkDataAPI({ ...link, ...values });
message.success('🎉 编辑网站成功'); message.success('🎉 编辑网站成功');
@@ -87,6 +89,8 @@ const LinkPage = () => {
setTab('list'); setTab('list');
reset() reset()
}); });
setBtnLoading(false)
}; };
const { Option } = Select; const { Option } = Select;
@@ -190,7 +194,7 @@ const LinkPage = () => {
</Form.Item> </Form.Item>
<Form.Item> <Form.Item>
<Button type="primary" htmlType="submit" className='w-full'>{isMethod === "edit" ? '编辑网站' : '新增网站'}</Button> <Button type="primary" htmlType="submit" loading={btnLoading} className='w-full'>{isMethod === "edit" ? '编辑网站' : '新增网站'}</Button>
</Form.Item> </Form.Item>
</Form> </Form>
</div> </div>