mirror of
https://ghproxy.com/https://github.com/StreakingMan/solvable-sheep-game
synced 2025-05-24 18:28:14 +08:00
729 lines
28 KiB
TypeScript
729 lines
28 KiB
TypeScript
import React, { FC, ReactNode, useEffect, useState } from 'react';
|
||
import style from './ConfigDialog.module.scss';
|
||
import classNames from 'classnames';
|
||
import { Icon, Sound, Theme } from '../themes/interface';
|
||
import { QRCodeCanvas } from 'qrcode.react';
|
||
import Bmob from 'hydrogen-js-sdk';
|
||
import {
|
||
captureElement,
|
||
CUSTOM_THEME_FILE_VALIDATE_STORAGE_KEY,
|
||
LAST_CUSTOM_THEME_ID_STORAGE_KEY,
|
||
CUSTOM_THEME_STORAGE_KEY,
|
||
deleteThemeUnusedSounds,
|
||
getFileBase64String,
|
||
linkReg,
|
||
randomString,
|
||
wrapThemeDefaultSounds,
|
||
LAST_UPLOAD_TIME_STORAGE_KEY,
|
||
} from '../utils';
|
||
import { copy } from 'clipboard';
|
||
import { CloseIcon } from './CloseIcon';
|
||
import WxQrCode from './WxQrCode';
|
||
|
||
const InputContainer: FC<{
|
||
label: string;
|
||
required?: boolean;
|
||
children?: ReactNode;
|
||
}> = ({ label, children, required }) => {
|
||
return (
|
||
<>
|
||
<div className={style.divider} />
|
||
<div
|
||
className={classNames(
|
||
'flex-container flex-center flex-no-wrap',
|
||
style.inputContainer,
|
||
required && style.required
|
||
)}
|
||
>
|
||
<span className={style.label}>{label}</span>
|
||
<div className={'flex-container flex-column flex-grow'}>
|
||
{children}
|
||
</div>
|
||
</div>
|
||
</>
|
||
);
|
||
};
|
||
|
||
interface CustomIcon extends Icon {
|
||
content: string;
|
||
}
|
||
|
||
interface CustomTheme extends Theme<any> {
|
||
icons: CustomIcon[];
|
||
}
|
||
|
||
const ConfigDialog: FC<{
|
||
closeMethod: () => void;
|
||
previewMethod: (theme: Theme<string>) => void;
|
||
}> = ({ closeMethod, previewMethod }) => {
|
||
// 错误提示
|
||
const [configError, setConfigError] = useState<string>('');
|
||
// 生成链接
|
||
const [genLink, setGenLink] = useState<string>('');
|
||
|
||
// 主题大对象
|
||
const [customTheme, setCustomTheme] = useState<CustomTheme>({
|
||
title: '',
|
||
sounds: [],
|
||
pure: false,
|
||
icons: new Array(10).fill(0).map(() => ({
|
||
name: randomString(4),
|
||
content: '',
|
||
clickSound: '',
|
||
tripleSound: '',
|
||
})),
|
||
});
|
||
function updateCustomTheme(key: keyof CustomTheme, value: any) {
|
||
if (['sounds', 'icons'].includes(key)) {
|
||
if (Array.isArray(value)) {
|
||
setCustomTheme({
|
||
...customTheme,
|
||
[key]: [...value],
|
||
});
|
||
} else {
|
||
setCustomTheme({
|
||
...customTheme,
|
||
[key]: [...customTheme[key as 'sounds' | 'icons'], value],
|
||
});
|
||
}
|
||
} else {
|
||
setCustomTheme({
|
||
...customTheme,
|
||
[key]: value,
|
||
});
|
||
}
|
||
}
|
||
useEffect(() => {
|
||
console.log(customTheme);
|
||
}, [customTheme]);
|
||
|
||
// 音效
|
||
const [newSound, setNewSound] = useState<Sound>({ name: '', src: '' });
|
||
const [soundError, setSoundError] = useState<string>('');
|
||
const onNewSoundChange = (key: keyof Sound, value: string) => {
|
||
setNewSound({
|
||
...newSound,
|
||
[key]: value,
|
||
});
|
||
};
|
||
const onAddNewSoundClick = () => {
|
||
setSoundError('');
|
||
let error = '';
|
||
if (!linkReg.test(newSound.src)) error = '请输入https链接';
|
||
if (!newSound.name) error = '请输入音效名称';
|
||
if (customTheme.sounds.find((s) => s.name === newSound.name))
|
||
error = '名称已存在';
|
||
if (error) {
|
||
setSoundError(error);
|
||
} else {
|
||
updateCustomTheme('sounds', newSound);
|
||
setNewSound({ name: '', src: '' });
|
||
}
|
||
};
|
||
const onDeleteSoundClick = (idx: number) => {
|
||
const deleteSoundName = customTheme.sounds[idx].name;
|
||
const findIconUseIdx = customTheme.icons.findIndex(
|
||
({ clickSound, tripleSound }) =>
|
||
[clickSound, tripleSound].includes(deleteSoundName)
|
||
);
|
||
if (findIconUseIdx !== -1) {
|
||
return setSoundError(
|
||
`第${findIconUseIdx + 1}项图标有使用该音效,请取消后再删除`
|
||
);
|
||
}
|
||
|
||
const newSounds = customTheme.sounds.slice();
|
||
newSounds.splice(idx, 1);
|
||
updateCustomTheme('sounds', newSounds);
|
||
};
|
||
|
||
// 本地文件选择
|
||
const [bgmError, setBgmError] = useState<string>('');
|
||
const [backgroundError, setBackgroundError] = useState<string>('');
|
||
const [iconErrors, setIconErrors] = useState<string[]>(
|
||
new Array(10).fill('')
|
||
);
|
||
// 文件体积校验开关
|
||
const [enableFileSizeValidate, setEnableFileSizeValidate] =
|
||
useState<boolean>(
|
||
localStorage.getItem(CUSTOM_THEME_FILE_VALIDATE_STORAGE_KEY) !==
|
||
'false'
|
||
);
|
||
useEffect(() => {
|
||
localStorage.setItem(
|
||
CUSTOM_THEME_FILE_VALIDATE_STORAGE_KEY,
|
||
enableFileSizeValidate + ''
|
||
);
|
||
}, [enableFileSizeValidate]);
|
||
const makeIconErrors = (idx: number, error: string) =>
|
||
new Array(10)
|
||
.fill('')
|
||
.map((item, _idx) => (idx === _idx ? error : iconErrors[_idx]));
|
||
const onFileChange: (props: {
|
||
type: 'bgm' | 'background' | 'sound' | 'icon';
|
||
file?: File;
|
||
idx?: number;
|
||
}) => void = ({ type, file, idx }) => {
|
||
if (!file) return;
|
||
switch (type) {
|
||
case 'bgm':
|
||
setBgmError('');
|
||
if (enableFileSizeValidate && file.size > 80 * 1024) {
|
||
return setBgmError('请选择80k以内全损音质的文件');
|
||
}
|
||
getFileBase64String(file)
|
||
.then((res) => {
|
||
updateCustomTheme('bgm', res);
|
||
})
|
||
.catch((e) => {
|
||
setBgmError(e);
|
||
});
|
||
break;
|
||
case 'background':
|
||
setBackgroundError('');
|
||
if (enableFileSizeValidate && file.size > 80 * 1024) {
|
||
return setBackgroundError('请选择80k以内全损画质的图片');
|
||
}
|
||
getFileBase64String(file)
|
||
.then((res) => {
|
||
updateCustomTheme('background', res);
|
||
})
|
||
.catch((e) => {
|
||
setBackgroundError(e);
|
||
});
|
||
break;
|
||
case 'sound':
|
||
setSoundError('');
|
||
if (enableFileSizeValidate && file.size > 10 * 1024) {
|
||
return setSoundError('请选择10k以内的音频文件');
|
||
}
|
||
getFileBase64String(file)
|
||
.then((res) => {
|
||
onNewSoundChange('src', res);
|
||
})
|
||
.catch((e) => {
|
||
setSoundError(e);
|
||
});
|
||
break;
|
||
case 'icon':
|
||
if (idx == null) return;
|
||
setIconErrors(makeIconErrors(idx, ''));
|
||
if (enableFileSizeValidate && file.size > 5 * 1024) {
|
||
return setIconErrors(
|
||
makeIconErrors(idx, '请选择5k以内的图片文件')
|
||
);
|
||
}
|
||
getFileBase64String(file)
|
||
.then((res) => {
|
||
updateCustomTheme(
|
||
'icons',
|
||
customTheme.icons.map((icon, _idx) =>
|
||
_idx === idx ? { ...icon, content: res } : icon
|
||
)
|
||
);
|
||
})
|
||
.catch((e) => {
|
||
setIconErrors(makeIconErrors(idx, e));
|
||
});
|
||
break;
|
||
}
|
||
};
|
||
|
||
// 图标更新
|
||
const updateIcons = (key: keyof CustomIcon, value: string, idx: number) => {
|
||
const newIcons = customTheme.icons.map((icon, _idx) =>
|
||
_idx === idx
|
||
? {
|
||
...icon,
|
||
[key]: value,
|
||
}
|
||
: icon
|
||
);
|
||
updateCustomTheme('icons', newIcons);
|
||
};
|
||
|
||
// 初始化
|
||
useEffect(() => {
|
||
const lastId = localStorage.getItem(LAST_CUSTOM_THEME_ID_STORAGE_KEY);
|
||
lastId && setGenLink(`${location.origin}?customTheme=${lastId}`);
|
||
try {
|
||
const configString = localStorage.getItem(CUSTOM_THEME_STORAGE_KEY);
|
||
if (configString) {
|
||
const parseRes = JSON.parse(configString);
|
||
if (typeof parseRes === 'object') {
|
||
setCustomTheme(parseRes);
|
||
}
|
||
}
|
||
} catch (e) {
|
||
console.log(e);
|
||
}
|
||
}, []);
|
||
|
||
// 校验主题
|
||
const validateTheme: () => Promise<string> = async () => {
|
||
// 校验
|
||
if (!customTheme.title) return Promise.reject('请输入标题');
|
||
if (customTheme.bgm && !linkReg.test(customTheme.bgm))
|
||
return Promise.reject('bgm请输入https链接');
|
||
if (customTheme.background && !linkReg.test(customTheme.background))
|
||
return Promise.reject('背景图请输入https链接');
|
||
if (!customTheme.maxLevel || customTheme.maxLevel < 5)
|
||
return Promise.reject('请输入大于5的关卡数');
|
||
const findIconError = iconErrors.find((i) => !!i);
|
||
if (findIconError)
|
||
return Promise.reject(`图标素材有错误:${findIconError}`);
|
||
const findUnfinishedIconIdx = customTheme.icons.findIndex(
|
||
(icon) => !icon.content
|
||
);
|
||
if (findUnfinishedIconIdx !== -1) {
|
||
setIconErrors(makeIconErrors(findUnfinishedIconIdx, '请填写链接'));
|
||
return Promise.reject(
|
||
`第${findUnfinishedIconIdx + 1}图标素材未完成`
|
||
);
|
||
}
|
||
|
||
return Promise.resolve('');
|
||
};
|
||
|
||
// 预览
|
||
const onPreviewClick = () => {
|
||
setConfigError('');
|
||
validateTheme()
|
||
.then(() => {
|
||
const cloneTheme = JSON.parse(JSON.stringify(customTheme));
|
||
wrapThemeDefaultSounds(cloneTheme);
|
||
previewMethod(cloneTheme);
|
||
localStorage.setItem(
|
||
CUSTOM_THEME_STORAGE_KEY,
|
||
JSON.stringify(customTheme)
|
||
);
|
||
closeMethod();
|
||
})
|
||
.catch((e) => {
|
||
setConfigError(e);
|
||
});
|
||
};
|
||
|
||
// 生成二维码和链接
|
||
const [uploading, setUploading] = useState<boolean>(false);
|
||
const onGenQrLinkClick = () => {
|
||
if (uploading) return;
|
||
if (!enableFileSizeValidate)
|
||
return setConfigError('请先开启文件大小校验');
|
||
let passTime = Number.MAX_SAFE_INTEGER;
|
||
const lastUploadTime = localStorage.getItem(
|
||
LAST_UPLOAD_TIME_STORAGE_KEY
|
||
);
|
||
if (lastUploadTime) {
|
||
passTime = Date.now() - Number(lastUploadTime);
|
||
}
|
||
if (passTime < 1000 * 60 * 15) {
|
||
return setConfigError(
|
||
`为节省请求数,15分钟内只能生成一次二维码,还剩大约${
|
||
15 - Math.round(passTime / 1000 / 60)
|
||
}分钟,先本地预览调整下吧~`
|
||
);
|
||
}
|
||
setUploading(true);
|
||
setConfigError('');
|
||
validateTheme()
|
||
.then(() => {
|
||
const cloneTheme = JSON.parse(JSON.stringify(customTheme));
|
||
deleteThemeUnusedSounds(cloneTheme);
|
||
const stringify = JSON.stringify(cloneTheme);
|
||
localStorage.setItem(CUSTOM_THEME_STORAGE_KEY, stringify);
|
||
const query = Bmob.Query('config');
|
||
query.set('content', stringify);
|
||
query
|
||
.save()
|
||
.then((res) => {
|
||
localStorage.setItem(
|
||
LAST_CUSTOM_THEME_ID_STORAGE_KEY,
|
||
//@ts-ignore
|
||
res.objectId
|
||
);
|
||
localStorage.setItem(
|
||
LAST_UPLOAD_TIME_STORAGE_KEY,
|
||
Date.now().toString()
|
||
);
|
||
setTimeout(() => {
|
||
setGenLink(
|
||
`${location.origin}?customTheme=${
|
||
/*@ts-ignore*/
|
||
res.objectId || id
|
||
}`
|
||
);
|
||
}, 3000);
|
||
})
|
||
.catch(({ error, code }) => {
|
||
setTimeout(() => {
|
||
setConfigError(
|
||
code === 10007
|
||
? '上传数据长度已超过bmob的限制'
|
||
: error
|
||
);
|
||
}, 3000);
|
||
})
|
||
.finally(() => {
|
||
setTimeout(() => {
|
||
setUploading(false);
|
||
}, 3000);
|
||
});
|
||
})
|
||
.catch((e) => {
|
||
setConfigError(e);
|
||
setUploading(false);
|
||
});
|
||
};
|
||
|
||
// 彩蛋
|
||
const [pureClickTime, setPureClickTime] = useState<number>(0);
|
||
useEffect(() => {
|
||
updateCustomTheme(
|
||
'pure',
|
||
pureClickTime % 5 === 0 && pureClickTime !== 0
|
||
);
|
||
}, [pureClickTime]);
|
||
|
||
return (
|
||
<div className={classNames(style.dialog)}>
|
||
<div className={style.closeBtn} onClick={closeMethod}>
|
||
<CloseIcon fill={'#fff'} />
|
||
</div>
|
||
<h2>自定义主题</h2>
|
||
|
||
<InputContainer label={'标题'} required>
|
||
<input
|
||
placeholder={'请输入标题'}
|
||
value={customTheme.title}
|
||
onChange={(e) => updateCustomTheme('title', e.target.value)}
|
||
/>
|
||
</InputContainer>
|
||
<InputContainer label={'描述'}>
|
||
<input
|
||
placeholder={'请输入描述'}
|
||
value={customTheme.desc || ''}
|
||
onChange={(e) => updateCustomTheme('desc', e.target.value)}
|
||
/>
|
||
</InputContainer>
|
||
<InputContainer label={'BGM'}>
|
||
<div className={style.tip}>
|
||
接口上传体积有限制,上传文件请全力压缩到80k以下
|
||
</div>
|
||
<input
|
||
type={'file'}
|
||
accept={'.mp3'}
|
||
onChange={(e) =>
|
||
onFileChange({
|
||
type: 'bgm',
|
||
file: e.target.files?.[0],
|
||
})
|
||
}
|
||
/>
|
||
{bgmError && <div className={style.errorTip}>{bgmError}</div>}
|
||
<input
|
||
placeholder={'或者输入https外链'}
|
||
value={customTheme.bgm || ''}
|
||
onChange={(e) => updateCustomTheme('bgm', e.target.value)}
|
||
/>
|
||
{customTheme.bgm && <audio src={customTheme.bgm} controls />}
|
||
</InputContainer>
|
||
<InputContainer label={'背景图'}>
|
||
<div className={style.tip}>
|
||
接口上传体积有限制,上传文件请全力压缩到80k以下
|
||
</div>
|
||
<input
|
||
type={'file'}
|
||
accept={'.jpg,.png,.gif'}
|
||
onChange={(e) =>
|
||
onFileChange({
|
||
type: 'background',
|
||
file: e.target.files?.[0],
|
||
})
|
||
}
|
||
/>
|
||
{backgroundError && (
|
||
<div className={style.errorTip}>{backgroundError}</div>
|
||
)}
|
||
<div className={'flex-container flex-center'}>
|
||
<input
|
||
placeholder={'或者输入https外链'}
|
||
value={customTheme.background || ''}
|
||
onChange={(e) =>
|
||
updateCustomTheme('background', e.target.value)
|
||
}
|
||
/>
|
||
{customTheme.background && (
|
||
<img
|
||
alt="加载失败"
|
||
src={customTheme.background}
|
||
className={style.imgPreview}
|
||
/>
|
||
)}
|
||
</div>
|
||
<div className={'flex-container flex-center flex-wrap'}>
|
||
<div className={'flex-spacer flex-container flex-center'}>
|
||
<span>毛玻璃</span>
|
||
<input
|
||
type={'checkbox'}
|
||
checked={!!customTheme.backgroundBlur}
|
||
onChange={(e) =>
|
||
updateCustomTheme(
|
||
'backgroundBlur',
|
||
e.target.checked
|
||
)
|
||
}
|
||
/>
|
||
</div>
|
||
<div className={'flex-spacer flex-container flex-center'}>
|
||
<span>深色</span>
|
||
<input
|
||
type={'checkbox'}
|
||
checked={!!customTheme.dark}
|
||
onChange={(e) =>
|
||
updateCustomTheme('dark', e.target.checked)
|
||
}
|
||
/>
|
||
</div>
|
||
<div className={'flex-spacer flex-container flex-center'}>
|
||
<span>纯色</span>
|
||
<input
|
||
type={'color'}
|
||
value={customTheme.backgroundColor || '#ffffff'}
|
||
onChange={(e) =>
|
||
updateCustomTheme(
|
||
'backgroundColor',
|
||
e.target.value
|
||
)
|
||
}
|
||
/>
|
||
</div>
|
||
</div>
|
||
<div className={style.tip}>
|
||
使用图片或者纯色作为背景,图片可开启毛玻璃效果。如果你使用了深色的图片和颜色,请开启深色模式,此时标题等文字将变为亮色
|
||
</div>
|
||
</InputContainer>
|
||
<InputContainer label={'关卡数'} required>
|
||
<input
|
||
type={'number'}
|
||
placeholder={'最低5关,最高...理论上无限'}
|
||
value={customTheme.maxLevel || ''}
|
||
onChange={(e) =>
|
||
updateCustomTheme('maxLevel', Number(e.target.value))
|
||
}
|
||
/>
|
||
</InputContainer>
|
||
<InputContainer label={'音效素材'}>
|
||
<div className={'flex-container flex-left-center'}>
|
||
{customTheme.sounds.map((sound, idx) => {
|
||
return (
|
||
<div key={sound.name} className={style.soundItem}>
|
||
<audio src={sound.src} controls />
|
||
<div className={style.inner}>
|
||
<span>{sound.name}</span>
|
||
<CloseIcon
|
||
fill={'#fff'}
|
||
onClick={() => onDeleteSoundClick(idx)}
|
||
/>
|
||
</div>
|
||
</div>
|
||
);
|
||
})}
|
||
</div>
|
||
<input
|
||
placeholder={'输入音效名称'}
|
||
value={newSound.name}
|
||
onChange={(e) => onNewSoundChange('name', e.target.value)}
|
||
/>
|
||
<div className={style.tip}>
|
||
接口上传体积有限制,上传文件请全力压缩到10k以下
|
||
</div>
|
||
<input
|
||
type={'file'}
|
||
accept={'.mp3'}
|
||
onChange={(e) =>
|
||
onFileChange({
|
||
type: 'sound',
|
||
file: e.target.files?.[0],
|
||
})
|
||
}
|
||
/>
|
||
<input
|
||
placeholder={'或者输入https外链'}
|
||
value={newSound.src}
|
||
onChange={(e) => onNewSoundChange('src', e.target.value)}
|
||
/>
|
||
{soundError && (
|
||
<div className={style.errorTip}>{soundError}</div>
|
||
)}
|
||
<button onClick={onAddNewSoundClick}>添加音效</button>
|
||
</InputContainer>
|
||
<InputContainer label={'图标素材'} required>
|
||
<div className={style.tip}>
|
||
接口上传体积有限制,上传文件请全力压缩到5k以下,推荐尺寸56*56
|
||
</div>
|
||
</InputContainer>
|
||
{customTheme.icons.map((icon, idx) => (
|
||
<div key={icon.name} className={style.iconInputGroup}>
|
||
<img
|
||
alt=""
|
||
className={style.iconPreview}
|
||
src={icon.content}
|
||
/>
|
||
<div className={style.iconInput}>
|
||
<input
|
||
type={'file'}
|
||
accept={'.jpg,.png,.gif'}
|
||
onChange={(e) =>
|
||
onFileChange({
|
||
type: 'icon',
|
||
file: e.target.files?.[0],
|
||
idx,
|
||
})
|
||
}
|
||
/>
|
||
<div
|
||
className={
|
||
'flex-container flex-center flex-no-wrap'
|
||
}
|
||
style={{ wordBreak: 'keep-all' }}
|
||
>
|
||
<input
|
||
placeholder={'或者输入https外链'}
|
||
value={customTheme.icons[idx].content}
|
||
onBlur={(e) => {
|
||
setIconErrors(
|
||
makeIconErrors(
|
||
idx,
|
||
linkReg.test(e.target.value)
|
||
? ''
|
||
: '请输入https外链'
|
||
)
|
||
);
|
||
}}
|
||
onChange={(e) =>
|
||
updateIcons('content', e.target.value, idx)
|
||
}
|
||
/>
|
||
{iconErrors[idx] && (
|
||
<div className={style.errorTip}>
|
||
{iconErrors[idx]}
|
||
</div>
|
||
)}
|
||
</div>
|
||
<div className={'flex-container'}>
|
||
<select
|
||
className={'flex-grow'}
|
||
value={customTheme.icons[idx].clickSound}
|
||
onChange={(e) =>
|
||
updateIcons(
|
||
'clickSound',
|
||
e.target.value,
|
||
idx
|
||
)
|
||
}
|
||
>
|
||
<option value="">默认点击音效</option>
|
||
{customTheme.sounds.map((sound) => (
|
||
<option key={sound.name} value={sound.name}>
|
||
{sound.name}
|
||
</option>
|
||
))}
|
||
</select>
|
||
<select
|
||
className={'flex-grow'}
|
||
value={customTheme.icons[idx].tripleSound}
|
||
onChange={(e) =>
|
||
updateIcons(
|
||
'tripleSound',
|
||
e.target.value,
|
||
idx
|
||
)
|
||
}
|
||
>
|
||
<option value="">默认三连音效</option>
|
||
{customTheme.sounds.map((sound) => (
|
||
<option key={sound.name} value={sound.name}>
|
||
{sound.name}
|
||
</option>
|
||
))}
|
||
</select>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
))}
|
||
{/*<InputContainer label={'操作音效'}>??</InputContainer>*/}
|
||
|
||
{genLink && (
|
||
<div className={'flex-container flex-center flex-column'}>
|
||
<QRCodeCanvas
|
||
id="qrCode"
|
||
value={genLink}
|
||
size={300}
|
||
className={classNames(
|
||
style.qrCode,
|
||
uploading && style.uploading
|
||
)}
|
||
/>
|
||
<button
|
||
onClick={() =>
|
||
captureElement(
|
||
'qrCode',
|
||
`${customTheme.title}-${localStorage.getItem(
|
||
LAST_CUSTOM_THEME_ID_STORAGE_KEY
|
||
)}`
|
||
)
|
||
}
|
||
className="primary"
|
||
>
|
||
下载二维码
|
||
</button>
|
||
<div style={{ fontSize: 12 }}>{genLink}</div>
|
||
<button onClick={() => copy(genLink)} className="primary">
|
||
复制链接
|
||
</button>
|
||
</div>
|
||
)}
|
||
<div className={style.tip}>
|
||
接口上传内容总体积有限制,上传文件失败请进一步压缩文件,或者使用外链(自行搜索【免费图床】【免费mp3外链】【对象存储服务】等关键词)。
|
||
本地整活,勾选右侧关闭文件大小校验👉
|
||
<input
|
||
type={'checkbox'}
|
||
checked={!enableFileSizeValidate}
|
||
onChange={(e) =>
|
||
setEnableFileSizeValidate(!e.target.checked)
|
||
}
|
||
/>
|
||
(谨慎操作,单文件不超过1M为宜,文件过大可能导致崩溃,介时请刷新浏览器)
|
||
</div>
|
||
{configError && <div className={style.errorTip}>{configError}</div>}
|
||
{customTheme.pure && (
|
||
<div className={style.tip}>
|
||
🎉🎉🎉恭喜发现彩蛋,生成的主题将开启纯净模式~
|
||
</div>
|
||
)}
|
||
<WxQrCode onClick={() => setPureClickTime(pureClickTime + 1)} />
|
||
<div className={'flex-container'}>
|
||
<button
|
||
className={'primary flex-grow'}
|
||
onClick={onPreviewClick}
|
||
>
|
||
保存并预览
|
||
</button>
|
||
<button
|
||
className={classNames(
|
||
'primary flex-grow',
|
||
style.uploadBtn,
|
||
uploading && style.uploading
|
||
)}
|
||
onClick={onGenQrLinkClick}
|
||
>
|
||
生成二维码&链接
|
||
</button>
|
||
</div>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default ConfigDialog;
|