diff --git a/.eslintrc.cjs b/.eslintrc.cjs index e1b3daa..fdefba2 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -25,5 +25,6 @@ module.exports = { rules: { 'prettier/prettier': 'error', '@typescript-eslint/no-explicit-any': 0, + '@typescript-eslint/ban-ts-comment': 0, }, }; diff --git a/package.json b/package.json index 39cf14a..b7fbe62 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,8 @@ "release": "standard-version --commit-all" }, "dependencies": { + "classnames": "^2.3.2", + "qrcode.react": "^3.1.0", "react": "^18.2.0", "react-dom": "^18.2.0" }, @@ -30,6 +32,7 @@ "husky": "^8.0.1", "lint-staged": "^13.0.3", "prettier": "^2.7.1", + "sass": "^1.55.0", "standard-version": "^9.5.0", "stylelint": "^14.11.0", "stylelint-config-prettier-scss": "^0.0.1", diff --git a/src/App.css b/src/App.scss similarity index 50% rename from src/App.css rename to src/App.scss index 9083873..caa82e0 100644 --- a/src/App.css +++ b/src/App.scss @@ -10,21 +10,23 @@ margin: 0 auto; } -.scene-container { - width: 100%; - padding-bottom: 100%; - position: relative; - margin: 10% 0; -} +.scene { + &-container { + width: 100%; + padding-bottom: 100%; + position: relative; + margin: 10% 0; + } -.scene-inner { - position: absolute; - left: 0; - right: 0; - bottom: 0; - top: 0; - overflow: visible; - font-size: 28px; + &-inner { + position: absolute; + left: 0; + right: 0; + bottom: 0; + top: 0; + overflow: visible; + font-size: 28px; + } } .symbol { @@ -35,28 +37,28 @@ left: 0; top: 0; border-radius: 8px; -} -.symbol-inner { - position: absolute; - left: 0; - right: 0; - bottom: 0; - top: 0; - display: flex; - justify-content: center; - align-items: center; - border-radius: 8px; - border: 2px solid #444; - transition: 0.3s; - overflow: hidden; - user-select: none; -} + &-inner { + position: absolute; + left: 0; + right: 0; + bottom: 0; + top: 0; + display: flex; + justify-content: center; + align-items: center; + border-radius: 8px; + border: 2px solid #444; + transition: 0.3s; + overflow: hidden; + user-select: none; -.symbol-inner img { - width: 100%; - height: 100%; - object-fit: cover; + img { + width: 100%; + height: 100%; + object-fit: cover; + } + } } .queue-container { @@ -67,25 +69,6 @@ margin-bottom: 16px; } -.flex-container { - display: flex; - gap: 8px; -} - -.flex-center { - justify-content: center; - align-items: center; -} - -.flex-grow { - flex-grow: 1; -} - -.flex-between { - justify-content: space-between; - align-items: center; -} - .modal { position: fixed; width: 100vw; @@ -108,3 +91,8 @@ width: 36px; height: 36px; } + +.zhenghuo-button { + width: 100%; + margin-top: 8px; +} diff --git a/src/App.tsx b/src/App.tsx index 2f496a9..430a4a5 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -6,9 +6,14 @@ import React, { useState, } from 'react'; -import './App.css'; +import './App.scss'; import { GithubIcon } from './components/GithubIcon'; -import { parseThemePath, randomString, waitTimeout } from './utils'; +import { + parsePathCustomTheme, + parsePathThemeName, + randomString, + waitTimeout, +} from './utils'; import { defaultTheme } from './themes/default'; import { Icon, Theme } from './themes/interface'; import { fishermanTheme } from './themes/fisherman'; @@ -18,9 +23,10 @@ import { pddTheme } from './themes/pdd'; import { BeiAn } from './components/BeiAn'; import { Info } from './components/Info'; import { owTheme } from './themes/ow'; +import { ConfigDialog } from './components/ConfigDialog'; -// 主题 -const themes: Theme[] = [ +// 内置主题 +const builtInThemes: Theme[] = [ defaultTheme, fishermanTheme, jinlunTheme, @@ -156,8 +162,15 @@ const Symbol: FC = ({ x, y, icon, isCover, status, onClick }) => { style={{ opacity: isCover ? 0.5 : 1 }} > {typeof icon.content === 'string' ? ( - {icon.content} + icon.content.startsWith('http') ? ( + /*图片外链*/ + + ) : ( + /*字符表情*/ + {icon.content} + ) ) : ( + /*ReactNode*/ icon.content )} @@ -166,12 +179,12 @@ const Symbol: FC = ({ x, y, icon, isCover, status, onClick }) => { }; // 从url初始化主题 -const themeFromPath: string = parseThemePath(location.href); +const themeFromPath: string = parsePathThemeName(location.href); +const customThemeFromPath = parsePathCustomTheme(location.href); const App: FC = () => { - const [curTheme, setCurTheme] = useState>( - themes.find((theme) => theme.name === themeFromPath) ?? defaultTheme - ); + const [curTheme, setCurTheme] = useState>(defaultTheme); + const [themes, setThemes] = useState[]>(builtInThemes); const [scene, setScene] = useState(makeScene(1, curTheme.icons)); const [level, setLevel] = useState(1); @@ -182,6 +195,7 @@ const App: FC = () => { const [finished, setFinished] = useState(false); const [tipText, setTipText] = useState(''); const [animating, setAnimating] = useState(false); + const [configDialogShow, setConfigDialogShow] = useState(false); // 音效 const soundRefMap = useRef>({}); @@ -200,6 +214,21 @@ const App: FC = () => { } }, [bgmOn]); + // 初始化主题 + useEffect(() => { + if (customThemeFromPath) { + // 自定义主题 + setThemes([...themes, customThemeFromPath]); + setCurTheme(customThemeFromPath); + } else if (themeFromPath) { + // 内置主题 + setCurTheme( + themes.find((theme) => theme.name === themeFromPath) ?? + defaultTheme + ); + } + }, []); + // 主题切换 useEffect(() => { // 初始化时不加载bgm @@ -210,7 +239,8 @@ const App: FC = () => { }, 300); } restart(); - // 更改路径 + // 更改路径query + if (customThemeFromPath) return; history.pushState( {}, curTheme.title, @@ -420,12 +450,17 @@ const App: FC = () => { setAnimating(false); }; + // 自定义整活 + const customZhenghuo = (theme: Theme) => { + setCurTheme(theme); + }; + return ( <>

{curTheme.title}

-
+

-

+

主题: {/*TODO themes维护方式调整*/} @@ -486,10 +521,18 @@ const App: FC = () => { {/**/} + + + {/*提示弹窗*/} {finished && (

{tipText}

@@ -497,6 +540,13 @@ const App: FC = () => {
)} + {/*自定义主题弹窗*/} + setConfigDialogShow(false)} + previewMethod={customZhenghuo} + /> + {/*bgm*/}