添加页面

This commit is contained in:
王一之 2024-11-29 17:28:06 +08:00
parent 4b7957256e
commit 804266c6dd
13 changed files with 1935 additions and 245 deletions

1
.gitignore vendored
View File

@ -13,3 +13,4 @@ dist/
.idea
coverage
tailwind.config.js

View File

@ -16,6 +16,7 @@
"lint-fix": "eslint --fix ."
},
"dependencies": {
"@arco-design/web-react": "^2.64.1",
"@reduxjs/toolkit": "^2.3.0",
"cron": "^3.2.1",
"dayjs": "^1.11.13",
@ -25,6 +26,7 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-i18next": "^15.1.0",
"react-icons": "^5.3.0",
"react-redux": "^9.1.2",
"semver": "^7.6.3",
"uuid": "^11.0.3",
@ -39,6 +41,7 @@
"@types/react": "^18.2.48",
"@types/react-dom": "^18.2.18",
"@types/semver": "^7.5.8",
"@unocss/postcss": "0.65.0-beta.2",
"@vitest/coverage-v8": "2.1.4",
"autoprefixer": "^10.4.20",
"cross-env": "^7.0.3",
@ -50,10 +53,10 @@
"postcss": "^8.4.49",
"postcss-loader": "^8.1.1",
"prettier": "^3.3.3",
"tailwindcss": "^3.4.15",
"ts-node": "^10.9.2",
"typescript": "^5.6.3",
"typescript-eslint": "^8.8.1",
"unocss": "0.65.0-beta.2",
"vitest": "^2.1.4"
}
}

1961
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

5
postcss.config.mjs Normal file
View File

@ -0,0 +1,5 @@
import UnoCSS from "@unocss/postcss";
export default {
plugins: [UnoCSS()],
};

View File

@ -54,7 +54,6 @@ export default defineConfig({
options: {
postcssOptions: {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
},
@ -157,6 +156,7 @@ export default defineConfig({
minimizerOptions: { targets },
}),
],
realContentHash: true,
},
experiments: {
css: true,

View File

@ -1,3 +1,2 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@unocss preflights;
@unocss default;

View File

@ -0,0 +1,114 @@
import {
Button,
ConfigProvider,
Dropdown,
Empty,
Input,
Layout,
Menu,
Modal,
Space,
Typography,
} from "@arco-design/web-react";
import { RefInputType } from "@arco-design/web-react/es/Input/interface";
import { IconDesktop, IconMoonFill, IconSunFill } from "@arco-design/web-react/icon";
import React, { ReactNode, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import "./index.css";
const MainLayout: React.FC<{
children: ReactNode;
className: string;
}> = ({ children, className }) => {
const [lightMode, setLightMode] = useState(localStorage.lightMode || "auto");
const importRef = useRef<RefInputType>(null);
const [importVisible, setImportVisible] = useState(false);
const { t } = useTranslation();
return (
<ConfigProvider
renderEmpty={() => {
return <Empty description={t("no_data")} />;
}}
>
<Layout>
<Layout.Header
style={{
height: "50px",
borderBottom: "1px solid var(--color-neutral-3)",
}}
className="flex items-center justify-between px-4"
>
<Modal
title={t("import_link")}
visible={importVisible}
onOk={async () => {
setImportVisible(false);
}}
onCancel={() => {
setImportVisible(false);
}}
>
<Input ref={importRef} defaultValue="" />
</Modal>
<div className="flex row items-center">
<img style={{ height: "40px" }} src="/assets/logo.png" alt="ScriptCat" />
<Typography.Title heading={4} className="!m-0">
ScriptCat
</Typography.Title>
</div>
<Space size="small" className="action-tools">
<Dropdown
droplist={
<Menu
onClickMenuItem={(key) => {
setLightMode(key);
localStorage.lightMode = key;
}}
selectedKeys={[lightMode]}
>
<Menu.Item key="light">
<IconSunFill /> Light
</Menu.Item>
<Menu.Item key="dark">
<IconMoonFill /> Dark
</Menu.Item>
<Menu.Item key="auto">
<IconDesktop /> {t("system_follow")}
</Menu.Item>
</Menu>
}
position="bl"
>
<Button
type="text"
size="small"
icon={
<>
{lightMode === "auto" && <IconDesktop />}
{lightMode === "light" && <IconSunFill />}
{lightMode === "dark" && <IconMoonFill />}
</>
}
style={{
color: "var(--color-text-1)",
}}
className="!text-lg"
/>
</Dropdown>
</Space>
</Layout.Header>
<Layout
className={`absolute top-50px bottom-0 w-full ${className}`}
style={{
background: "var(--color-fill-2)",
}}
>
{children}
</Layout>
</Layout>
</ConfigProvider>
);
};
export default MainLayout;

View File

@ -0,0 +1,11 @@
.arco-dropdown-menu-selected {
background-color: var(--color-fill-2) !important;
}
.action-tools .arco-dropdown-popup-visible .arco-icon-down {
transform: rotate(180deg);
}
.action-tools>.arco-btn {
padding: 0 8px;
}

View File

@ -1,10 +1,15 @@
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.tsx";
import MainLayout from "../components/layout/MainLayout.tsx";
import "@arco-design/web-react/dist/css/arco.css";
import "@App/locales/locales";
import "@App/index.css";
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
<React.StrictMode>
<App />
<MainLayout className="!flex-col !px-4 box-border">
<App />
</MainLayout>
</React.StrictMode>
);

17
src/store/hooks.ts Normal file
View File

@ -0,0 +1,17 @@
// This file serves as a central hub for re-exporting pre-typed Redux hooks.
// These imports are restricted elsewhere to ensure consistent
// usage of typed hooks throughout the application.
// We disable the ESLint rule here because this is the designated place
// for importing and re-exporting the typed versions of hooks.
import { useDispatch, useSelector } from "react-redux";
import type { AppDispatch, RootState } from "./store";
import { asyncThunkCreator, buildCreateSlice } from "@reduxjs/toolkit";
// Use throughout your app instead of plain `useDispatch` and `useSelector`
export const useAppDispatch = useDispatch.withTypes<AppDispatch>();
export const useAppSelector = useSelector.withTypes<RootState>();
// `buildCreateSlice` allows us to create a slice with async thunks.
export const createAppSlice = buildCreateSlice({
creators: { asyncThunk: asyncThunkCreator },
});

35
src/store/store.ts Normal file
View File

@ -0,0 +1,35 @@
import type { Action, ThunkAction } from "@reduxjs/toolkit";
import { combineSlices, configureStore } from "@reduxjs/toolkit";
import { setupListeners } from "@reduxjs/toolkit/query";
// `combineSlices` automatically combines the reducers using
// their `reducerPath`s, therefore we no longer need to call `combineReducers`.
const rootReducer = combineSlices();
// Infer the `RootState` type from the root reducer
export type RootState = ReturnType<typeof rootReducer>;
// The store setup is wrapped in `makeStore` to allow reuse
// when setting up tests that need the same store config
export const makeStore = (preloadedState?: Partial<RootState>) => {
const store = configureStore({
reducer: rootReducer,
// Adding the api middleware enables caching, invalidation, polling,
// and other useful features of `rtk-query`.
// middleware: (getDefaultMiddleware) => {
// return getDefaultMiddleware().concat(quotesApiSlice.middleware);
// },
preloadedState,
});
// configure listeners using the provided defaults
// optional, but required for `refetchOnFocus`/`refetchOnReconnect` behaviors
setupListeners(store.dispatch);
return store;
};
export const store = makeStore();
// Infer the type of `store`
export type AppStore = typeof store;
// Infer the `AppDispatch` type from the store itself
export type AppDispatch = AppStore["dispatch"];
export type AppThunk<ThunkReturnType = void> = ThunkAction<ThunkReturnType, RootState, unknown, Action>;

View File

@ -1,9 +0,0 @@
/* eslint-disable no-undef */
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./src/**/*.{html,tsx}"],
theme: {
extend: {},
},
plugins: [],
};

8
uno.config.ts Normal file
View File

@ -0,0 +1,8 @@
import { defineConfig, presetUno } from "unocss";
export default defineConfig({
content: {
filesystem: ["./src/**/*.{html,js,ts,jsx,tsx}"],
},
presets: [presetUno()],
});