diff --git a/components/main.tsx b/components/main.tsx index 14cad38..ed3d97a 100644 --- a/components/main.tsx +++ b/components/main.tsx @@ -25,7 +25,7 @@ function MTBarLink({ type }: { type: string }) { ); } -export default function MainNode({ children }: { children: JSX.Element }) { +export default function MainNode({ children }: { children: JSX.Element | JSX.Element[] }) { return (
diff --git a/components/students/chatStudents.ts b/components/students/chatStudents.ts new file mode 100644 index 0000000..3c34e1f --- /dev/null +++ b/components/students/chatStudents.ts @@ -0,0 +1,13 @@ +import { fileInfo, schaleInfo, studentsJson, studentInfo } from './students'; + +export async function getStudentJson(): Promise { + const studentsJson: fileInfo[] = await fetch('../students.json').then(r => r.json()); + const schaleJson: schaleInfo[] = await fetch('https://schale.gg/data/cn/students.min.json?v=102').then(r => r.json()); + return { fileJson: studentsJson, schaleJson }; +} + +export function getStudentInfo({ fileJson: studentsJson, schaleJson }: studentsJson, studentId: number): studentInfo { + const json = studentsJson && studentsJson.filter(info => (info.id === studentId))[0]; + const schale = schaleJson && schaleJson.filter(info => (info.Id === studentId))[0]; + return { file: json, schale }; +} \ No newline at end of file diff --git a/components/students/infoStudents.ts b/components/students/infoStudents.ts new file mode 100644 index 0000000..7d82757 --- /dev/null +++ b/components/students/infoStudents.ts @@ -0,0 +1,13 @@ +import { fileInfo, schaleInfo, studentsJson, studentInfo } from './students'; + +export async function getStudentsJson(): Promise { + const fileJson: fileInfo[] = await fetch('../students.json').then(r => r.json()); + const schaleJson: schaleInfo[] = await fetch('https://schale.gg/data/cn/students.min.json?v=102').then(r => r.json()); + return { fileJson, schaleJson }; +} + +export function getStudentInfo({ fileJson, schaleJson }: studentsJson, studentId: number): studentInfo { + const file = fileJson && fileJson.filter(info => (info.id === studentId))[0]; + const schale = schaleJson && schaleJson.filter(info => (info.Id === studentId))[0]; + return { file, schale }; +} \ No newline at end of file diff --git a/components/students.ts b/components/students/students.ts similarity index 66% rename from components/students.ts rename to components/students/students.ts index a656e09..9faaa19 100644 --- a/components/students.ts +++ b/components/students/students.ts @@ -1,9 +1,9 @@ -interface studentsInfo { +export interface fileInfo { id?: number; info?: string; } -interface schaleInfo { +export interface schaleInfo { Id: number; IsReleased: boolean[]; DefaultOrder: number; @@ -64,24 +64,12 @@ interface schaleInfo { RegenCost: number; } -export interface Main { - studentsJson?: studentsInfo[]; +export interface studentsJson { + fileJson?: fileInfo[]; schaleJson?: schaleInfo[]; } -export interface MainInfo { - json?: studentsInfo; +export interface studentInfo { + file?: fileInfo; schale?: schaleInfo; -} - -export async function getStudentJson(): Promise
{ - const studentsJson: studentsInfo[] = await fetch('/students.json').then(r => r.json()); - const schaleJson: schaleInfo[] = await fetch('https://schale.gg/data/cn/students.min.json?v=102').then(r => r.json()); - return { studentsJson, schaleJson }; -} - -export function getStudentInfo({ studentsJson, schaleJson }: Main, studentId: number): MainInfo { - const json = studentsJson && studentsJson.filter(info => (info.id === studentId))[0]; - const schale = schaleJson && schaleJson.filter(info => (info.Id === studentId))[0]; - return { json, schale }; } \ No newline at end of file diff --git a/pages/_app.tsx b/pages/_app.tsx index 93fcb18..cc0b795 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -1,13 +1,8 @@ import type { AppProps } from 'next/app'; -import MainNode from '@/components/main'; //Global Style import '@/styles/globals.scss'; export default function App({ Component, pageProps }: AppProps) { - return ( - - - - ); + return (); } \ No newline at end of file diff --git a/pages/api/icon/birth.ts b/pages/api/icon/birth.ts new file mode 100644 index 0000000..07029b7 --- /dev/null +++ b/pages/api/icon/birth.ts @@ -0,0 +1,22 @@ +import type { NextApiRequest, NextApiResponse } from 'next' + +export default function handler( + req: NextApiRequest, + res: NextApiResponse +) { + res.setHeader('Access-Control-Allow-Origin', '*'); + res.setHeader('Content-Type', 'image/svg+xml'); + const { fill = 'fff' } = req.query; + res.status(200).send(` + + + + + + + + + + + `); +} \ No newline at end of file diff --git a/pages/chat.tsx b/pages/chat.tsx index 4f26b16..ef89db5 100644 --- a/pages/chat.tsx +++ b/pages/chat.tsx @@ -1,24 +1,166 @@ //Components import { NextSeo } from 'next-seo'; +import MainNode from '@/components/main'; + +//Styles +import ItemStyles from '@/styles/Item.module.scss'; +import ChatStyles from '@/styles/Chat.module.scss'; //Methods +import { useState } from 'react'; import getTitle from '@/components/title'; +import { studentsJson } from '@/components/students/students'; +import { getStudentInfo, getStudentsJson } from '@/components/students/infoStudents'; -//Styles -import styles from '@/styles/Item.module.scss'; +interface RepeatProps { + variable: T, + repeat: number, + func: (variable: T) => T, + components: (variable: T) => JSX.Element, +} + +interface StudentProps { + id: number, + allInfo: studentsJson, + onClick: React.MouseEventHandler, + select: boolean, +} + +interface ContentProps { + id: number, + allInfo: studentsJson +} + +function Repeat({ variable, repeat, func, components }: RepeatProps) { + const array = new Array(repeat).fill(null); + array.forEach((_, i) => { + array[i] = components(variable); + variable = func(variable); + }); + return <>{array}; +} + +function Student({ id, allInfo, onClick, select }: StudentProps) { + const info = getStudentInfo(allInfo, id); + return ( +
+
+ {`${info.schale?.Name} +
+
+

{info.schale?.Name}

+

{info.file?.info}

+
+
+
+ ); +} + +function Content({ id, allInfo }: ContentProps) { + const info = getStudentInfo(allInfo, id); + return ( +
+
+ {`${info.schale?.Name} +
+
+

{info.schale?.Name}

+

{info.file?.info}

+
+
+ {`${info.schale?.Name} +

{info.schale?.Birthday}

+
+
+ ); +} export default function Info() { + const [state, setState] = useState<{ + student: number, + studentsList: number[], + studentsJson: { + data: studentsJson, + fetch: boolean, + } + }>({ + student: 0, + studentsList: [10000, 10002], + studentsJson: { data: {}, fetch: false }, + }); + if (!state.studentsJson.fetch) getStudentsJson().then(r => setState({ + student: state.student, + studentsList: state.studentsList, + studentsJson: { data: r, fetch: true }, + })); return ( - <> + -
- +
+
+

学生({state.studentsList.length})

+

{ + const id = Number(window.prompt('Input id', '10001')) || 10001; + let { studentsList } = state; + studentsList.push(id); + setState({ + student: state.student, + studentsList: studentsList, + studentsJson: state.studentsJson, + }) + }}>+

+
+
+
+ +

所有学生

+
+
+ {state.studentsJson.fetch && + v + 1} + components={v => { + const id = state.studentsList[v]; + return ( + setState({ + student: id, + studentsList: state.studentsList, + studentsJson: state.studentsJson, + })} + select={state.student === id} + /> + ); + }} + /> + } +
-
- +
+ {state.student !== 0 ? + + : +

请选择学生。

+ }
- + ); } diff --git a/pages/index.tsx b/pages/index.tsx index dfab956..2844028 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -1,16 +1,17 @@ //Components import { NextSeo } from 'next-seo'; +import MainNode from '@/components/main'; //Methods import getTitle from '@/components/title'; export default function Home() { return ( - <> +

请选择栏目。

- +
); } diff --git a/pages/info.tsx b/pages/info.tsx index dc63e6c..1b668ba 100644 --- a/pages/info.tsx +++ b/pages/info.tsx @@ -1,20 +1,36 @@ //Components import { NextSeo } from 'next-seo'; +import MainNode from '@/components/main'; + +//Styles +import styles from '@/styles/Item.module.scss'; //Methods import { useState } from 'react'; import getTitle from '@/components/title'; -import { getStudentInfo, getStudentJson, Main } from '@/components/students'; - -//Styles -import styles from '@/styles/Item.module.scss'; +import { studentsJson } from '@/components/students/students'; +import { getStudentInfo, getStudentsJson } from '@/components/students/infoStudents'; -function Repeat({ variable, repeat, func, components }: { +interface RepeatProps { variable: T, repeat: number, func: (variable: T) => T, components: (variable: T) => JSX.Element, -}) { +} + +interface StudentProps { + id: number, + allInfo: studentsJson, + onClick: React.MouseEventHandler, + select: boolean, +} + +interface ContentProps { + id: number, + allInfo: studentsJson +} + +function Repeat({ variable, repeat, func, components }: RepeatProps) { const array = new Array(repeat).fill(null); array.forEach((_, i) => { array[i] = components(variable); @@ -23,45 +39,75 @@ function Repeat({ variable, repeat, func, components }: { return <>{array}; } -function Student({ id, allInfo }: { id: number, allInfo: Main }) { +function Student({ id, allInfo, onClick, select }: StudentProps) { const info = getStudentInfo(allInfo, id); return ( -
+
- + {`${info.schale?.Name}
-

{info.schale && info.schale.Name}

-

{info.json && info.json.info}

+

{info.schale?.Name}

+

{info.file?.info}

); } +function Content({ id, allInfo }: ContentProps) { + const info = getStudentInfo(allInfo, id); + return ( +
+
+ {`${info.schale?.Name} +
+
+

{info.schale?.Name}

+

{info.file?.info}

+
+
+ {`${info.schale?.Name} +

{info.schale?.Birthday}

+
+
+ ); +} + export default function Info() { const [state, setState] = useState<{ student: number, - MainInfo: { - data: Main, + studentsJson: { + data: studentsJson, fetch: boolean, } }>({ student: 0, - MainInfo: { data: {}, fetch: false } + studentsJson: { data: {}, fetch: false } }); - if (!state.MainInfo.fetch) getStudentJson().then(r => setState({ + if (!state.studentsJson.fetch) getStudentsJson().then(r => setState({ student: state.student, - MainInfo: { data: r, fetch: true } + studentsJson: { data: r, fetch: true } })); return ( - <> +
-

学生(999)

+

学生(123)

@@ -69,26 +115,50 @@ export default function Info() {

所有学生

- {state.MainInfo.fetch && + {state.studentsJson.fetch && v + 1} - components={v => } + components={v => { + const { num, i } = [ + { num: 10000, i: 63 }, + { num: 13000, i: 13 }, + { num: 16000, i: 13 }, + { num: 20000, i: 25 }, + { num: 26000, i: 9 }, + ][v]; + return ( + v + 1} + components={v => ( + setState({ + student: v, + studentsJson: state.studentsJson, + })} + select={state.student === v} + /> + )} + /> + ); + }} /> }
{state.student !== 0 ? - ( -
- -
- ) - : (

请选择学生。

) + + : +

请选择学生。

}
- + ); } diff --git a/styles/Chat.module.scss b/styles/Chat.module.scss new file mode 100644 index 0000000..e3fc6be --- /dev/null +++ b/styles/Chat.module.scss @@ -0,0 +1,31 @@ +@media screen and (min-width: 580px) { + #title { + width: calc((100% - 70px) / 2); + } +} +@media screen and (max-width: 580px) { + #title { + width: calc(100% - 70px); + } +} + +#title { + position: fixed; + height: 70px; + display: flex; + align-items: center; + background-color: #f3f7f8; + border-bottom: #e8e8e8 1px solid; + user-select: none; + justify-content: space-between; + + p#left { + font-size: 20px; + margin-left: 10px; + } + + p#right { + font-size: 20px; + margin-right: 10px; + } +} diff --git a/styles/Item.module.scss b/styles/Item.module.scss index d4cac52..82a8ed8 100644 --- a/styles/Item.module.scss +++ b/styles/Item.module.scss @@ -1,8 +1,26 @@ -#infoBar { - height: 100%; - width: 50%; - background-color: #e3e3e5; - overflow: auto; +@media screen and (min-width: 580px) { + #infoBar { + width: 50%; + #title { + width: calc((100% - 70px) / 2); + } + } + #contentBar { + width: 50%; + display: flex; + } +} +@media screen and (max-width: 580px) { + #infoBar { + width: 100%; + #title { + width: calc(100% - 70px); + } + } + #contentBar { + width: 100%; + display: none; + } } #infoBar::-webkit-scrollbar { @@ -10,35 +28,41 @@ } #infoBar { + height: 100%; + background-color: #f3f7f8; + overflow: auto; + border-right: #dce5ec 2px solid; + #title { position: fixed; - width: calc((100% - 70px) / 2); height: 70px; display: flex; align-items: center; - background-color: #e3e3e5; - border-bottom: #d2d3d7 2px solid; - } + background-color: #f3f7f8; + border-bottom: #e8e8e8 1px solid; + user-select: none; - #title > p { - font-size: 20px; - margin-left: 10px; + p { + font-size: 20px; + margin-left: 10px; + } } #all { - background-color: #cfdae5; + background-color: #e3f0f8; height: 30px; display: flex; flex-direction: row; justify-content: flex-start; align-items: center; - border-bottom: #c8cfd7 2px solid; - } + border-bottom: #e8e8e8 1px solid; + user-select: none; - #all > img { - height: 15px; - margin-left: 15px; - margin-right: 6px; + img { + height: 15px; + margin-left: 15px; + margin-right: 6px; + } } #students { @@ -60,35 +84,35 @@ display: grid; justify-content: center; align-content: center; - } + user-select: none; + background-color: #00000038; - > div.img > img.col { - width: 65px; + img.col { + width: 65px; + } } > div.p { display: flex; flex-direction: column; justify-content: center; - } - > div.p > { - p { + > p { margin: 0; font-size: 18px; } - p.name { + > p.name { color: #000; } - p.info { - color: #484848; + > p.info { + color: #707070; } } > div.line { - background-color: #dbdbdd; + background-color: #e8e8e8; width: calc(100% - 30px); height: 2px; margin: auto; @@ -99,9 +123,78 @@ #contentBar { height: 100%; - width: 50%; - background-color: #efefef; - display: flex; + background-color: #fff; justify-content: center; align-items: center; + text-align: center; + + #content { + width: 100%; + height: 100%; + + > div.img { + margin: auto; + margin-top: 20px; + margin-bottom: 5px; + overflow: hidden; + width: 120px; + height: 120px; + border-radius: 50%; + display: grid; + justify-content: center; + align-content: center; + user-select: none; + + img.col { + width: 100%; + } + } + + > div.p { + display: flex; + flex-direction: column; + justify-content: center; + + > p { + margin: 0; + font-size: 18px; + } + + > p.name { + color: #000; + } + + > p.info { + color: #707070; + } + } + + > div.birthday { + width: fit-content; + height: 15px; + color: #5f7c8c; + border: #c2d0d9 2px solid; + border-radius: 20px; + margin: auto; + margin-top: 5px; + padding: 4px; + display: flex; + flex-direction: row; + flex-wrap: wrap; + align-content: center; + align-items: center; + + > img { + margin-left: 2px; + margin-right: 2px; + height: 17px; + } + + > p { + + margin-left: 2px; + margin-right: 2px; + } + } + } } diff --git a/styles/MainNode.module.scss b/styles/MainNode.module.scss index 2d7967a..cfa0554 100644 --- a/styles/MainNode.module.scss +++ b/styles/MainNode.module.scss @@ -27,26 +27,8 @@ div #main { animation-name: start; animation-timing-function: cubic-bezier(1, 0, 0, 1); z-index: 1000; -} - -@keyframes iconstart { - 0% { - width: 100px; - opacity: 0; - } - - 50% { - width: 100px; - opacity: 1; - } - - 100% { - width: 100px; - opacity: 0; - } -} + user-select: none; -#MTStart { > div { width: 0; height: auto; @@ -60,10 +42,27 @@ div #main { flex-direction: column; align-items: center; opacity: 0; + + > svg { + width: 50px; + } + } +} + +@keyframes iconstart { + 0% { + width: 100px; + opacity: 0; } - > div > svg { - width: 50px; + 50% { + width: 100px; + opacity: 1; + } + + 100% { + width: 100px; + opacity: 0; } } @@ -88,9 +87,8 @@ p.MTText { flex-direction: row; align-items: center; justify-content: flex-start; -} + user-select: none; -#MTBar { > a > div#icon { width: 20px; height: 100%; @@ -118,9 +116,8 @@ p.MTText { #MTLeftBar { width: 70px; background-color: #525e6e; -} + user-select: none; -#MTLeftBar { > a > div > img { margin: 20px; width: 25px; diff --git a/styles/globals.scss b/styles/globals.scss index cdb3dc7..ef2e15f 100644 --- a/styles/globals.scss +++ b/styles/globals.scss @@ -1,7 +1,13 @@ +@import url(https://cdn.jsdelivr.net/npm/misans/lib/misans-300-light.min.css); body { - margin: 0; + margin: 0; } * { - font-family: MiSans; -} \ No newline at end of file + font-family: MiSans; +} + +::selection { + background-color: #ff8ca1; + color: #fff; +}