diff --git a/package.json b/package.json index 24ca37d..48f4d79 100644 --- a/package.json +++ b/package.json @@ -29,11 +29,8 @@ "node": ">=17.9.0" }, "nodemonConfig": { - "exec": "tsx packages/core/src", + "exec": "pnpm common build && tsx packages/core/src", "ext": "ts", - "ignore": [ - "packages/common", - "packages/client" - ] + "ignore": ["packages/common", "packages/client"] } } diff --git a/packages/client/index.html b/packages/client/index.html index 95d4d27..0c82635 100644 --- a/packages/client/index.html +++ b/packages/client/index.html @@ -1,17 +1,26 @@ + + + + + MoeHub + + + - - - - - MoeHub - - - -
-
- - - - \ No newline at end of file + +
+ + + diff --git a/packages/client/package.json b/packages/client/package.json index 8e02a1a..7d3accd 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -12,13 +12,16 @@ "dependencies": { "@ant-design/icons": "^5.3.7", "@moehub/common": "workspace:^", + "@reduxjs/toolkit": "^2.2.6", "antd": "^5.18.1", "axios": "^1.7.2", "dayjs": "^1.11.11", "react": "^18.2.0", "react-dom": "^18.2.0", "react-helmet-async": "^2.0.5", + "react-redux": "^9.1.2", "react-router-dom": "^6.23.1", + "redux-persist": "^6.0.0", "swr": "^2.2.5", "tailwindcss": "^3.4.4" }, diff --git a/packages/client/src/App.tsx b/packages/client/src/App.tsx index 283e553..def5c13 100644 --- a/packages/client/src/App.tsx +++ b/packages/client/src/App.tsx @@ -1,10 +1,10 @@ -import { HashRouter, Route, Routes } from 'react-router-dom' +import { HashRouter as Router, Route, Routes } from 'react-router-dom' import Layout from '@/components/Layout' import routes from './routes' function App() { return ( - + {routes.map((route) => ( ))} - + ) } diff --git a/packages/client/src/components/CharacterForm/index.tsx b/packages/client/src/components/CharacterForm/index.tsx index c3c1fae..a6e75da 100644 --- a/packages/client/src/components/CharacterForm/index.tsx +++ b/packages/client/src/components/CharacterForm/index.tsx @@ -1,21 +1,162 @@ -import { Button, Collapse, DatePicker, Form, Input, InputNumber, Radio, Select, Space } from 'antd' -import type { MoehubDataCharacter } from '@moehub/common' +import { Button, ColorPicker, DatePicker, Form, Input, InputNumber, Radio, Select, Space, Tabs } from 'antd' +import type { MoehubDataCharacter, MoehubDataCharacterSubmit } from '@moehub/common' import dayjs from 'dayjs' -import { type getCharacter, getTags } from '@/http' +import { getTags } from '@/http' import useSWR from 'swr' import { useEffect } from 'react' +import ListForm from '../ListForm' + +export type MoehubDataCharacterHandle = Omit & { + birthday?: dayjs.Dayjs + color?: { toHex(): string; cleared: false | string } +} + +export function handleMoehubDataCharacter(values: MoehubDataCharacterHandle): MoehubDataCharacterSubmit { + console.log(values.color) + return { + ...values, + color: values.color ? (values.color.cleared === false ? values.color.toHex() : '') : undefined, + birthday: values.birthday ? new Date(values.birthday.toString()).getTime() : undefined + } +} interface CharacterFormProps { - onSubmit: (values: Omit & { birthday: dayjs.Dayjs }) => void - data?: ReturnType extends Promise ? U : never + onSubmit: (values: MoehubDataCharacterHandle) => void + data?: MoehubDataCharacter } +const items = (isDisabled: boolean, tags?: { label: string; value: string }[]) => [ + { + key: '1', + label: '基本信息', + children: ( + <> + + + + + + + + + 男性 + 女性 + 其它/未知 + + + + + + + + 动画 + 漫画 + Galgame + 游戏 + 轻小说 + 其它 + + + + ) + }, + { + key: '2', + label: '详细信息', + children: ( + <> + + + + )} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A 型 + B 型 + AB 型 + O 型 + + + + - - - - - - - 男性 - 女性 - 其它/未知 - - - - - - - - 动画 - 漫画 - Galgame - 游戏 - 轻小说 - 其它 - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - A 型 - B 型 - AB 型 - O 型 - - - - + + + + + + + + + + + + ) + }, + { + key: '2', + label: '主页设置', + children: ( + <> + + + + + + {(key) => ( + <> + + + + + + + + )} + + + + + {(key) => ( + <> + + + + + + + + )} + + + + + + + ) + }, + { + key: '3', + label: '高级设置', + children: ( + <> + + + + + + + + + + + + + + + + + ) + }, + { + key: '4', + label: '邮箱设置', + children: ( + <> + + + + + + + + + + + + + + + + + + + + ) + } +] - const { data, error } = useSWR('/api/user', getSettings) +const SettingsView: React.FC = () => { + const [form] = Form.useForm() + const settings = useSelector(getSettings) + const dispatch = useDispatch() + const navigate = useNavigate() - useEffect(() => { - if (data) form.setFieldsValue(data) - }, [form, data]) + useEffect(() => form.setFieldsValue(settings)) - function onSubmit() {} + function onSubmit(values: MoehubDataSettingsSubmit) { + const handler = (items: ([string, string] | { 0: string; 1: string })[]) => + items.map((item) => [item[0], item[1]]) as [string, string][] + console.log(values.site_logo, values['logo_url']) + const data = { + ...values, + home_buttons: values.home_buttons ? handler(values.home_buttons) : undefined, + home_timeline: values.home_timeline ? handler(values.home_timeline) : undefined + } + updateSettings(data).then(() => { + notification.success({ message: '保存成功' }) + dispatch(loadSettings(data)) + setTimeout(() => { + navigate(0) + }, 500) + }) + } return (
-

设置

+

系统设置

- {data ? ( -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ) - }, - { - key: '3', - label: '邮箱设置', - children: ( - <> - - - - - - - - - - - - - - - - - - 显示 - 隐藏 - - - - ) - } - ]} - /> -
- - - - - - - ) : error ? ( - - ) : ( - - )} +
+ +
+ + + + + +
) } -export default EditView +export default SettingsView diff --git a/packages/client/src/views/Admin/index.tsx b/packages/client/src/views/Admin/index.tsx index fc9e6f7..75c0c65 100644 --- a/packages/client/src/views/Admin/index.tsx +++ b/packages/client/src/views/Admin/index.tsx @@ -1,37 +1,31 @@ -import type { MoehubDataCharacter } from '@moehub/common' -import { Button, Card, Flex, Popconfirm, Space, Table, notification } from 'antd' -import Column from 'antd/es/table/Column' -import ColumnGroup from 'antd/es/table/ColumnGroup' -import { useEffect } from 'react' -import { Link, useNavigate } from 'react-router-dom' -import { deleteCharacter, getCharacters } from '@/http' -import Loading from '@/components/Loading' -import ErrorResult from '@/components/result/error' -import styles from './styles.module.css' -import Store from '@/store' -import useSWR from 'swr' +import { Button, Card, Flex } from 'antd' +import { Link } from 'react-router-dom' +import { useSelector } from 'react-redux' +import { getSettings } from '@/store/settingsReducer' const AdminView: React.FC = () => { - const navigate = useNavigate() - - useEffect(() => { - if (Store.get('login') !== 'yes') { - navigate('./login', { replace: true }) - } - }, [navigate]) - - const { data, isLoading } = useSWR('/api/character', getCharacters) + const { admin_username } = useSelector(getSettings) return (
-

管理后台

+

管理中心

- 欢迎来到管理后台,ArimuraSena! + 欢迎来到管理后台,{admin_username}

在这里,你可以:

+ + + + + +
diff --git a/packages/client/src/views/Character/index.tsx b/packages/client/src/views/Character/index.tsx index e5e3250..e88c515 100644 --- a/packages/client/src/views/Character/index.tsx +++ b/packages/client/src/views/Character/index.tsx @@ -5,6 +5,9 @@ import ErrorResult from '@/components/result/error' import styles from './styles.module.css' import useSWR from 'swr' import { getCharacter } from '@/http' +import { useSelector } from 'react-redux' +import { getSettings } from '@/store/settingsReducer' +import { useEffect } from 'react' interface InfoCardProps { children: React.ReactNode @@ -39,18 +42,33 @@ const InfoCard: React.FC = ({ title, children }) => ( const CharacterView: React.FC = () => { const { id: characterId } = useParams() const { data, error, isLoading } = useSWR(`/api/character/${characterId}`, () => getCharacter(Number(characterId))) + const { site_title } = useSelector(getSettings) + + useEffect(() => { + if (data) document.title = `${data.name} - ${site_title}` + }, [data, site_title]) if (isLoading) return if (error || !data) return - document.title = `${data.name} - MoeHub` - return (

角色详情页

{data.hitokoto ?
『{data.hitokoto}』
: null} + {data.songId ? ( + <> +
+