mirror of
https://ghproxy.com/https://github.com/StreakingMan/solvable-sheep-game
synced 2026-09-26 14:45:01 +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;
|