From 037e103ab571efa50e6af5cfd6950c0b9fec8005 Mon Sep 17 00:00:00 2001 From: j2rong4cn <36783515+j2rong4cn@users.noreply.github.com> Date: Mon, 14 Jul 2025 16:38:25 +0800 Subject: [PATCH] feat: setting: add `Load Default Settings` button; home: display loaded folder info (#100) * fix: resolve all prompt errors * feat(home): display loaded folder info * feat(setting): add `Load Default Settings` button --- src/app/App.tsx | 2 +- src/components/FullLoading.tsx | 2 +- src/components/Markdown.tsx | 9 +- src/components/ModalInput.tsx | 2 +- src/hooks/useFetch.ts | 22 ++-- src/lang/en/home.json | 1 + src/lang/en/settings.json | 2 + src/pages/home/Layout.tsx | 2 +- src/pages/home/Nav.tsx | 129 +++++++++++++++------- src/pages/home/previews/asciinema.tsx | 1 + src/pages/home/previews/heic.tsx | 15 ++- src/pages/home/previews/video_box.tsx | 2 +- src/pages/home/toolbar/LocalSettings.tsx | 10 +- src/pages/login/CornerBottom.tsx | 6 +- src/pages/login/CornerTop.tsx | 6 +- src/pages/manage/common/DeletePopover.tsx | 2 +- src/pages/manage/metas/AddOrEdit.tsx | 16 ++- src/pages/manage/metas/Metas.tsx | 12 +- src/pages/manage/settings/Common.tsx | 23 +++- src/pages/manage/settings/S3.tsx | 2 +- src/pages/manage/settings/SettingItem.tsx | 14 +-- src/pages/manage/storages/Storages.tsx | 6 +- src/pages/manage/users/AddOrEdit.tsx | 6 +- src/store/local_settings.ts | 1 + src/store/obj.ts | 33 ++---- src/utils/notify.tsx | 8 +- tsconfig.json | 1 + 27 files changed, 186 insertions(+), 149 deletions(-) diff --git a/src/app/App.tsx b/src/app/App.tsx index 3da2894..8ed152b 100644 --- a/src/app/App.tsx +++ b/src/app/App.tsx @@ -19,7 +19,7 @@ import { MustUser } from "./MustUser" import "./index.css" import { useI18n } from "@solid-primitives/i18n" import { initialLang, langMap, loadedLangs } from "./i18n" -import { Resp } from "~/types" +import { PEmptyResp, PResp, Resp } from "~/types" import { setArchiveExtensions } from "~/store/archive" const Home = lazy(() => import("~/pages/home/Layout")) diff --git a/src/components/FullLoading.tsx b/src/components/FullLoading.tsx index 28c971b..a310e60 100644 --- a/src/components/FullLoading.tsx +++ b/src/components/FullLoading.tsx @@ -44,7 +44,7 @@ export const FullLoading = (props: { export const MaybeLoading = (props: { children?: JSXElement - loading: boolean + loading?: boolean }) => { return ( }> diff --git a/src/components/Markdown.tsx b/src/components/Markdown.tsx index ea68d1b..5b20946 100644 --- a/src/components/Markdown.tsx +++ b/src/components/Markdown.tsx @@ -31,6 +31,7 @@ const [isTocDisabled, setTocDisabled] = createStorageSignal( export { isTocVisible, setTocDisabled } +type PluginType = typeof remarkGfm | typeof rehypeRaw function MarkdownToc(props: { disabled?: boolean markdownRef: HTMLDivElement @@ -214,10 +215,10 @@ export function Markdown(props: { }) return content }) - const [remarkPlugins, setRemarkPlugins] = createSignal([ + const [remarkPlugins, setRemarkPlugins] = createSignal([ remarkGfm, ]) - const [rehypePlugins, setRehypePlugins] = createSignal([ + const [rehypePlugins, setRehypePlugins] = createSignal([ rehypeRaw, ]) createEffect( @@ -228,8 +229,8 @@ export function Markdown(props: { const { default: reMarkMath } = await import("remark-math") const { default: rehypeKatex } = await import("rehype-katex") insertKatexCSS() - setRemarkPlugins([...remarkPlugins(), reMarkMath]) - setRehypePlugins([...rehypePlugins(), rehypeKatex]) + setRemarkPlugins([...remarkPlugins(), reMarkMath as PluginType]) + setRehypePlugins([...rehypePlugins(), rehypeKatex as PluginType]) } insertMermaidJS() setTimeout(() => { diff --git a/src/components/ModalInput.tsx b/src/components/ModalInput.tsx index 93bcfb1..667a1ed 100644 --- a/src/components/ModalInput.tsx +++ b/src/components/ModalInput.tsx @@ -92,7 +92,7 @@ export const ModalInput = (props: ModalInputProps) => { initialFocus="#modal-input" > - props.onDrop?.(e, setValue)}> + props.onDrop?.(e, setValue)}> {/* */} {t(props.title)} diff --git a/src/hooks/useFetch.ts b/src/hooks/useFetch.ts index 3fd5fae..511e40e 100644 --- a/src/hooks/useFetch.ts +++ b/src/hooks/useFetch.ts @@ -5,17 +5,14 @@ export const useLoading = ( p: (...arg: any[]) => Promise, fetch?: boolean, t?: boolean, // initial loading true -): [ - Accessor, - (...arg: any[]) => Promise, -] => { - const [loading, setLoading] = createSignal(t ?? false) +): [Accessor, typeof p] => { + const [loading, setLoading] = createSignal(t) return [ loading, async (...arg: any[]) => { setLoading(true) const data = await p(...arg) - if (!fetch || (data as unknown as EmptyResp).code !== 401) { + if (!fetch || (data as EmptyResp).code !== 401) { // why? // because if setLoading(false) here will rerender before navigate // maybe cause some bugs @@ -29,10 +26,7 @@ export const useLoading = ( export const useFetch = ( p: (...arg: any[]) => PResp, loading?: boolean, -): [ - Accessor, - (...arg: Parameters) => PResp, -] => { +): [Accessor, typeof p] => { return useLoading(p, true, loading) } @@ -40,13 +34,13 @@ const useListLoading = ( p: (key: K, ...arg: any[]) => Promise, fetch?: boolean, initial?: K, -): [Accessor, (key: K, ...arg: any[]) => Promise] => { - const [loading, setLoading] = createSignal(initial) +): [Accessor, typeof p] => { + const [loading, setLoading] = createSignal(initial) return [ loading, async (key: K, ...arg: any[]) => { setLoading(() => key) - const data: unknown = await p(key, ...arg) + const data = await p(key, ...arg) if (!fetch || (data as EmptyResp).code !== 401) { setLoading(undefined) } @@ -58,6 +52,6 @@ const useListLoading = ( export const useListFetch = ( p: (key: K, ...arg: any[]) => PResp, initial?: K, -): [Accessor, (key: K, ...arg: any[]) => Promise] => { +): [Accessor, typeof p] => { return useListLoading(p, true, initial) } diff --git a/src/lang/en/home.json b/src/lang/en/home.json index 196c0ea..ab062ad 100644 --- a/src/lang/en/home.json +++ b/src/lang/en/home.json @@ -1,5 +1,6 @@ { "obj": { + "selected": "Selected", "name": "Name", "size": "Size", "modified": "Modified" diff --git a/src/lang/en/settings.json b/src/lang/en/settings.json index 143b5e6..77df030 100755 --- a/src/lang/en/settings.json +++ b/src/lang/en/settings.json @@ -124,5 +124,7 @@ "version": "Version", "video_autoplay": "Video autoplay", "video_types": "Video types", + "load_default_setting": "Load default settings", + "load_default_setting_success": "Loaded successfully, Not saved", "webauthn_login_enabled": "Webauthn login enabled" } diff --git a/src/pages/home/Layout.tsx b/src/pages/home/Layout.tsx index fe598cd..de14427 100644 --- a/src/pages/home/Layout.tsx +++ b/src/pages/home/Layout.tsx @@ -11,7 +11,7 @@ const Index = () => { useTitle(getSetting("site_title")) const announcement = getSetting("announcement") if (announcement) { - notify.render(() => ) + notify.render() } return ( <> diff --git a/src/pages/home/Nav.tsx b/src/pages/home/Nav.tsx index 8c51092..04c0766 100644 --- a/src/pages/home/Nav.tsx +++ b/src/pages/home/Nav.tsx @@ -4,11 +4,13 @@ import { BreadcrumbLink, BreadcrumbProps, BreadcrumbSeparator, + HStack, + Text, } from "@hope-ui/solid" import { Link } from "@solidjs/router" import { createMemo, For, Show } from "solid-js" import { usePath, useRouter, useT } from "~/hooks" -import { getSetting, local } from "~/store" +import { getSetting, local, objStore, State } from "~/store" import { encodePath, hoverColor, joinBase } from "~/utils" export const Nav = () => { @@ -17,6 +19,38 @@ export const Nav = () => { const t = useT() const { setPathAs } = usePath() + const folderInfo = createMemo(() => { + const { folder, file } = objStore.objs.reduce( + (acc, item) => { + if (item.is_dir) { + acc.folder++ + } else { + acc.file++ + } + return acc + }, + { folder: 0, file: 0 }, + ) + + const parts: string[] = [] + if (folder) parts.push(`${t("home.search.scopes.folder")}:${folder}`) + if (file) parts.push(`${t("home.search.scopes.file")}:${file}`) + return parts.join(" ") + }) + const selectInfo = createMemo(() => { + const { selected } = objStore.objs.reduce( + (acc, item) => { + if (item.selected) acc.selected++ + return acc + }, + { selected: 0 }, + ) + + if (!selected) { + return "" + } + return ` ${t("home.obj.selected")}:${selected}` + }) const stickyProps = createMemo(() => { const mask: BreadcrumbProps = { _after: { @@ -48,45 +82,58 @@ export const Nav = () => { }) return ( - - - {(name, i) => { - const isLast = createMemo(() => i() === paths().length - 1) - const path = paths() - .slice(0, i() + 1) - .join("/") - const href = encodePath(path) - let text = () => name - if (text() === "") { - text = () => getSetting("home_icon") + t("manage.sidemenu.home") - } - return ( - - setPathAs(path)} - > - {text} - - - - - - ) - }} - - + + + + {(name, i) => { + const isLast = createMemo(() => i() === paths().length - 1) + const path = paths() + .slice(0, i() + 1) + .join("/") + const href = encodePath(path) + let text = () => name + if (text() === "") { + text = () => getSetting("home_icon") + t("manage.sidemenu.home") + } + return ( + + setPathAs(path)} + > + {text()} + + + + + + ) + }} + + + + + {folderInfo()} + {selectInfo()} + + + ) } diff --git a/src/pages/home/previews/asciinema.tsx b/src/pages/home/previews/asciinema.tsx index eccff07..163b794 100644 --- a/src/pages/home/previews/asciinema.tsx +++ b/src/pages/home/previews/asciinema.tsx @@ -1,4 +1,5 @@ import { Box } from "@hope-ui/solid" +// @ts-ignore import * as AsciinemaPlayer from "asciinema-player" import "asciinema-player/dist/bundle/asciinema-player.css" import { onMount } from "solid-js" diff --git a/src/pages/home/previews/heic.tsx b/src/pages/home/previews/heic.tsx index 8d41e20..4455048 100644 --- a/src/pages/home/previews/heic.tsx +++ b/src/pages/home/previews/heic.tsx @@ -30,7 +30,7 @@ const Preview = () => { let libheif: any let decoder: any - let canvas: HTMLCanvasElement | null = null + let canvas: HTMLCanvasElement | undefined onMount(() => { window.addEventListener("keydown", onKeydown) @@ -74,21 +74,20 @@ const Preview = () => { } // 加载脚本 - const loadScript = (src: string, id: string) => { - return new Promise((resolve, reject) => { + const loadScript = (src: string, id: string) => + new Promise((resolve, reject) => { const script = document.createElement("script") script.src = src script.id = id script.onload = () => resolve() - script.onerror = () => reject(new Error(`脚本加载失败: ${src}`)) + script.onerror = () => reject(`脚本加载失败: ${src}`) document.head.appendChild(script) }) - } // 获取WASM文件 const fetchWasm = async (url: string) => { const response = await fetch(url) - if (!response.ok) throw new Error(`WASM加载失败: ${url}`) + if (!response.ok) throw `WASM加载失败: ${url}` return await response.arrayBuffer() } @@ -100,13 +99,13 @@ const Preview = () => { // 获取HEIC文件 const response = await fetch(url) - if (!response.ok) throw new Error("文件获取失败") + if (!response.ok) throw "文件获取失败" const buffer = await response.arrayBuffer() // 解码HEIC文件 const images = decoder.decode(buffer) if (!images || images.length === 0) { - throw new Error("没有可解码的图像") + throw "没有可解码的图像" } // 显示第一张图像 diff --git a/src/pages/home/previews/video_box.tsx b/src/pages/home/previews/video_box.tsx index 0759f60..ff31ed6 100644 --- a/src/pages/home/previews/video_box.tsx +++ b/src/pages/home/previews/video_box.tsx @@ -166,7 +166,7 @@ export const VideoBox = (props: { whiteSpace: "nowrap", }} defaultChecked={autoNext === "true"} - onChange={(e) => { + onChange={(e: { currentTarget: HTMLInputElement }) => { props.onAutoNextChange(e.currentTarget.checked) localStorage.setItem( "video_auto_next", diff --git a/src/pages/home/toolbar/LocalSettings.tsx b/src/pages/home/toolbar/LocalSettings.tsx index 3834a74..e2993e9 100644 --- a/src/pages/home/toolbar/LocalSettings.tsx +++ b/src/pages/home/toolbar/LocalSettings.tsx @@ -58,13 +58,7 @@ function LocalSettingEdit(props: LocalSetting) { - + {(item) => ( @@ -81,7 +75,7 @@ function LocalSettingEdit(props: LocalSetting) { { + onChange={(e: { currentTarget: HTMLInputElement }) => { setLocal(props.key, e.currentTarget.checked.toString()) }} /> diff --git a/src/pages/login/CornerBottom.tsx b/src/pages/login/CornerBottom.tsx index 1eec452..500a08d 100644 --- a/src/pages/login/CornerBottom.tsx +++ b/src/pages/login/CornerBottom.tsx @@ -36,11 +36,7 @@ const CornerBottom = (props: Props) => { - + ) diff --git a/src/pages/login/CornerTop.tsx b/src/pages/login/CornerTop.tsx index ec502e5..fe8b01c 100644 --- a/src/pages/login/CornerTop.tsx +++ b/src/pages/login/CornerTop.tsx @@ -36,11 +36,7 @@ const CornerTop = (props: Props) => { - + ) diff --git a/src/pages/manage/common/DeletePopover.tsx b/src/pages/manage/common/DeletePopover.tsx index 8c3e789..f0b5e4e 100644 --- a/src/pages/manage/common/DeletePopover.tsx +++ b/src/pages/manage/common/DeletePopover.tsx @@ -12,7 +12,7 @@ import { useT } from "~/hooks" export interface DeletePopoverProps { name: string - loading: boolean + loading?: boolean onClick: () => void } export const DeletePopover = (props: DeletePopoverProps) => { diff --git a/src/pages/manage/metas/AddOrEdit.tsx b/src/pages/manage/metas/AddOrEdit.tsx index 958a3ec..9472969 100644 --- a/src/pages/manage/metas/AddOrEdit.tsx +++ b/src/pages/manage/metas/AddOrEdit.tsx @@ -105,7 +105,7 @@ const AddOrEdit = () => { const initEdit = async () => { const resp = await loadMeta() - handleResp(resp, setMeta) + handleResp(resp, setMeta) } if (id) { initEdit() @@ -154,14 +154,12 @@ const AddOrEdit = () => { // @ts-ignore - setMeta(item.name as keyof Meta, val) - } - sub={meta[item.sub] as boolean} - onSub={(val: boolean): void => setMeta(item.sub, val)} - help={item.help} + type={item.type} + value={meta[item.name]} + onChange={(val: any): void => setMeta(item.name, val)} + sub={meta[item.sub]} + onSub={(val): void => setMeta(item.sub, val)} + help={(item as { help: boolean }).help} /> ) }} diff --git a/src/pages/manage/metas/Metas.tsx b/src/pages/manage/metas/Metas.tsx index 38b23be..d398d22 100644 --- a/src/pages/manage/metas/Metas.tsx +++ b/src/pages/manage/metas/Metas.tsx @@ -19,7 +19,7 @@ import { useT, } from "~/hooks" import { handleResp, notify, r } from "~/utils" -import { Meta, PageResp } from "~/types" +import { Meta, PEmptyResp, PPageResp } from "~/types" import { DeletePopover } from "../common/DeletePopover" import { Wether } from "~/components" @@ -27,16 +27,18 @@ const Metas = () => { const t = useT() useManageTitle("manage.sidemenu.metas") const { to } = useRouter() - const [getMetasLoading, getMetas] = useFetch(() => r.get("/admin/meta/list")) + const [getMetasLoading, getMetas] = useFetch( + (): PPageResp => r.get("/admin/meta/list"), + ) const [metas, setMetas] = createSignal([]) const refresh = async () => { - const resp: PageResp = await getMetas() + const resp = await getMetas() handleResp(resp, (data) => setMetas(data.content)) } refresh() - const [deleting, deleteMeta] = useListFetch((id: number) => - r.post(`/admin/meta/delete?id=${id}`), + const [deleting, deleteMeta] = useListFetch( + (id: number): PEmptyResp => r.post(`/admin/meta/delete?id=${id}`), ) return ( diff --git a/src/pages/manage/settings/Common.tsx b/src/pages/manage/settings/Common.tsx index 6f1cbf3..bfe26d7 100644 --- a/src/pages/manage/settings/Common.tsx +++ b/src/pages/manage/settings/Common.tsx @@ -21,18 +21,22 @@ const CommonSettings = (props: CommonSettingsProps) => { const [settings, setSettings] = createStore([]) const refresh = async () => { const resp = await getSettings() - handleResp(resp, setSettings) + handleResp(resp, setSettings) } refresh() const [saveLoading, saveSettings] = useFetch( (): PEmptyResp => r.post("/admin/setting/save", getTarget(settings)), ) + const [defaultLoading, defaultSettings] = useFetch( + (): PResp => + r.post(`/admin/setting/default?group=${props.group}`), + ) const [loading, setLoading] = createSignal(false) return ( - {(item, _) => ( + {(item) => ( { @@ -57,7 +61,7 @@ const CommonSettings = (props: CommonSettingsProps) => { @@ -70,6 +74,19 @@ const CommonSettings = (props: CommonSettingsProps) => { > {t("global.save")} + ) diff --git a/src/pages/manage/settings/S3.tsx b/src/pages/manage/settings/S3.tsx index 567895f..5340e95 100644 --- a/src/pages/manage/settings/S3.tsx +++ b/src/pages/manage/settings/S3.tsx @@ -24,7 +24,7 @@ const S3Settings = () => { const [settings, setSettings] = createStore([]) const refresh = async () => { const resp = await getSettings() - handleResp(resp, setSettings) + handleResp(resp, setSettings) } refresh() const [saveLoading, saveSettings] = useFetch( diff --git a/src/pages/manage/settings/SettingItem.tsx b/src/pages/manage/settings/SettingItem.tsx index c700d50..4cd992e 100644 --- a/src/pages/manage/settings/SettingItem.tsx +++ b/src/pages/manage/settings/SettingItem.tsx @@ -26,7 +26,6 @@ import { TiDelete } from "solid-icons/ti" export type ItemProps = SettingItem & { onChange?: (value: string) => void onDelete?: () => void - // value: () => string; hideLabel?: boolean w?: string } @@ -58,7 +57,6 @@ const Item = (props: ItemProps) => { props.onChange?.(e.currentTarget.value)} readOnly={props.flag === Flag.READONLY} @@ -67,11 +65,9 @@ const Item = (props: ItemProps) => { - // props.onChange?.(props.value() === "true" ? "false" : "true") - props.onChange?.(e.currentTarget.checked ? "true" : "false") + checked={props.value === "true"} + onChange={(e: { currentTarget: HTMLInputElement }) => + props.onChange?.(e.currentTarget?.checked ? "true" : "false") } readOnly={props.flag === Flag.READONLY} /> @@ -80,7 +76,6 @@ const Item = (props: ItemProps) => {