mirror of https://gitee.com/answerdev/answer.git
feat: add brand upload component
This commit is contained in:
parent
c918ae4c48
commit
acd263a9db
|
@ -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
|
||||
|
|
|
@ -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' },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
|
|
|
@ -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;
|
|
@ -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"
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
|
@ -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">
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -251,6 +251,10 @@ const routes: RouteNode[] = [
|
|||
path: 'smtp',
|
||||
page: 'pages/Admin/Smtp',
|
||||
},
|
||||
{
|
||||
path: 'branding',
|
||||
page: 'pages/Admin/Branding',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue