feat: add brand upload component

This commit is contained in:
shuai 2022-11-10 12:19:34 +08:00
parent c918ae4c48
commit acd263a9db
12 changed files with 121 additions and 9 deletions

View File

@ -915,6 +915,7 @@ ui:
general: General
interface: Interface
smtp: SMTP
branding: Branding
dashboard:
title: Dashboard
welcome: Welcome to Answer Admin!
@ -1109,3 +1110,5 @@ ui:
msg: SMTP authentication cannot be empty.
'yes': 'Yes'
'no': 'No'
branding:
page_title: Branding

View File

@ -54,7 +54,12 @@ export const ADMIN_NAV_MENUS = [
},
{
name: 'settings',
child: [{ name: 'general' }, { name: 'interface' }, { name: 'smtp' }],
child: [
{ name: 'general' },
{ name: 'interface' },
{ name: 'branding' },
{ name: 'smtp' },
],
},
];

View File

@ -0,0 +1,39 @@
import { FC } from 'react';
import { ButtonGroup, Button } from 'react-bootstrap';
import { Icon, UploadImg } from '@/components';
interface Props {
type: string;
imgPath: string;
uploadCallback: (data: FormData) => Promise<any>;
deleteCallback: (type: string) => void;
}
const Index: FC<Props> = ({
type,
imgPath,
uploadCallback,
deleteCallback,
}) => {
return (
<div className="d-flex">
<div className="bg-gray-300 upload-img-wrap me-2 d-flex align-items-center justify-content-center">
<img src={imgPath} alt="" height={100} />
</div>
<ButtonGroup vertical className="fit-content">
<UploadImg type={type} upload={uploadCallback} className="mb-0">
<Icon name="cloud-upload" />
</UploadImg>
<Button
variant="outline-secondary"
onClick={() => deleteCallback(type)}>
<Icon name="trash" />
</Button>
</ButtonGroup>
</div>
);
};
export default Index;

View File

@ -3,10 +3,12 @@ import { useTranslation } from 'react-i18next';
interface IProps {
type: string;
className?: string;
children?: React.ReactNode;
upload: (data: FormData) => Promise<any>;
}
const Index: React.FC<IProps> = ({ type, upload }) => {
const Index: React.FC<IProps> = ({ type, upload, children, className }) => {
const { t } = useTranslation();
const [status, setStatus] = useState(false);
@ -35,8 +37,8 @@ const Index: React.FC<IProps> = ({ type, upload }) => {
};
return (
<label className="mb-2 btn btn-outline-secondary uploadBtn">
{status ? t('upload_img.loading') : t('upload_img.name')}
<label className={`btn btn-outline-secondary uploadBtn ${className}`}>
{children || (status ? t('upload_img.loading') : t('upload_img.name'))}
<input
type="file"
className="d-none"

View File

@ -25,6 +25,7 @@ import Empty from './Empty';
import BaseUserCard from './BaseUserCard';
import FollowingTags from './FollowingTags';
import QueryGroup from './QueryGroup';
import BrandUpload from './BrandUpload';
export {
Avatar,
@ -55,5 +56,6 @@ export {
FollowingTags,
htmlRender,
QueryGroup,
BrandUpload,
};
export type { EditorRef };

View File

@ -915,6 +915,7 @@ ui:
general: General
interface: Interface
smtp: SMTP
branding: Branding
dashboard:
title: Dashboard
welcome: Welcome to Answer Admin!
@ -1109,3 +1110,5 @@ ui:
msg: SMTP authentication cannot be empty.
'yes': 'Yes'
'no': 'No'
branding:
page_title: Branding

View File

@ -2,6 +2,10 @@
@import '~bootstrap/scss/bootstrap';
@import '~bootstrap-icons';
.bg-gray-300 {
background-color: $gray-300;
}
.focus {
color: $input-focus-color !important;
background-color: $input-focus-bg !important;
@ -143,6 +147,7 @@ a {
.fit-content {
height: fit-content;
width: fit-content;
flex: none;
}
@ -210,3 +215,8 @@ a {
}
}
}
.upload-img-wrap {
flex-grow: 1;
height: 128px;
}

View File

@ -0,0 +1,37 @@
import { FC, memo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { BrandUpload } from '@/components';
const Index: FC = () => {
const { t } = useTranslation('translation', {
keyPrefix: 'admin.branding',
});
const [img, setImg] = useState(
'https://image-static.segmentfault.com/405/057/4050570037-636c7b0609a49',
);
const imgUpload = (file: any) => {
return new Promise((resolve) => {
console.log(file);
resolve(true);
});
};
return (
<div>
<h3 className="mb-4">{t('page_title')}</h3>
<BrandUpload
type="logo"
imgPath={img}
uploadCallback={imgUpload}
deleteCallback={() => {
console.log('delete');
setImg('');
}}
/>
</div>
);
};
export default memo(Index);

View File

@ -1,4 +1,4 @@
import React, { FC, FormEvent, useEffect, useState } from 'react';
import { FC, FormEvent, useEffect, useState } from 'react';
import { Form, Button, Image, Stack } from 'react-bootstrap';
import { Trans, useTranslation } from 'react-i18next';
@ -187,7 +187,7 @@ const Interface: FC = () => {
) : null}
</div>
<div className="d-inline-flex">
<UploadImg type="logo" upload={imgUpload} />
<UploadImg type="logo" upload={imgUpload} className="mb-2" />
</div>
</Stack>
<Form.Text as="div" className="text-muted">

View File

@ -1,15 +1,18 @@
import { FC } from 'react';
import { Container, Row, Col } from 'react-bootstrap';
import { useTranslation } from 'react-i18next';
import { Outlet } from 'react-router-dom';
import { Outlet, useLocation } from 'react-router-dom';
import { AccordionNav, AdminHeader, PageTitle } from '@/components';
import { ADMIN_NAV_MENUS } from '@/common/constants';
import './index.scss';
const formPaths = ['general', 'smtp', 'interface', 'branding'];
const Dashboard: FC = () => {
const { t } = useTranslation('translation', { keyPrefix: 'page_title' });
const { pathname } = useLocation();
return (
<>
<PageTitle title={t('admin')} />
@ -19,7 +22,7 @@ const Dashboard: FC = () => {
<Col lg={2}>
<AccordionNav menus={ADMIN_NAV_MENUS} />
</Col>
<Col lg={10}>
<Col lg={formPaths.find((v) => pathname.includes(v)) ? 6 : 10}>
<Outlet />
</Col>
</Row>

View File

@ -364,7 +364,11 @@ const Index: React.FC = () => {
className="me-3 rounded"
/>
<div>
<UploadImg type="avatar" upload={avatarUpload} />
<UploadImg
type="avatar"
upload={avatarUpload}
className="mb-2"
/>
<div>
<Form.Text className="text-muted mt-0">
<Trans i18nKey="settings.profile.avatar.text">

View File

@ -251,6 +251,10 @@ const routes: RouteNode[] = [
path: 'smtp',
page: 'pages/Admin/Smtp',
},
{
path: 'branding',
page: 'pages/Admin/Branding',
},
],
},
{