添加页面
This commit is contained in:
parent
4b7957256e
commit
804266c6dd
1
.gitignore
vendored
1
.gitignore
vendored
@ -13,3 +13,4 @@ dist/
|
|||||||
.idea
|
.idea
|
||||||
|
|
||||||
coverage
|
coverage
|
||||||
|
tailwind.config.js
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
"lint-fix": "eslint --fix ."
|
"lint-fix": "eslint --fix ."
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@arco-design/web-react": "^2.64.1",
|
||||||
"@reduxjs/toolkit": "^2.3.0",
|
"@reduxjs/toolkit": "^2.3.0",
|
||||||
"cron": "^3.2.1",
|
"cron": "^3.2.1",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
@ -25,6 +26,7 @@
|
|||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-i18next": "^15.1.0",
|
"react-i18next": "^15.1.0",
|
||||||
|
"react-icons": "^5.3.0",
|
||||||
"react-redux": "^9.1.2",
|
"react-redux": "^9.1.2",
|
||||||
"semver": "^7.6.3",
|
"semver": "^7.6.3",
|
||||||
"uuid": "^11.0.3",
|
"uuid": "^11.0.3",
|
||||||
@ -39,6 +41,7 @@
|
|||||||
"@types/react": "^18.2.48",
|
"@types/react": "^18.2.48",
|
||||||
"@types/react-dom": "^18.2.18",
|
"@types/react-dom": "^18.2.18",
|
||||||
"@types/semver": "^7.5.8",
|
"@types/semver": "^7.5.8",
|
||||||
|
"@unocss/postcss": "0.65.0-beta.2",
|
||||||
"@vitest/coverage-v8": "2.1.4",
|
"@vitest/coverage-v8": "2.1.4",
|
||||||
"autoprefixer": "^10.4.20",
|
"autoprefixer": "^10.4.20",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
@ -50,10 +53,10 @@
|
|||||||
"postcss": "^8.4.49",
|
"postcss": "^8.4.49",
|
||||||
"postcss-loader": "^8.1.1",
|
"postcss-loader": "^8.1.1",
|
||||||
"prettier": "^3.3.3",
|
"prettier": "^3.3.3",
|
||||||
"tailwindcss": "^3.4.15",
|
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"typescript": "^5.6.3",
|
"typescript": "^5.6.3",
|
||||||
"typescript-eslint": "^8.8.1",
|
"typescript-eslint": "^8.8.1",
|
||||||
|
"unocss": "0.65.0-beta.2",
|
||||||
"vitest": "^2.1.4"
|
"vitest": "^2.1.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
1961
pnpm-lock.yaml
generated
1961
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
5
postcss.config.mjs
Normal file
5
postcss.config.mjs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import UnoCSS from "@unocss/postcss";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
plugins: [UnoCSS()],
|
||||||
|
};
|
@ -54,7 +54,6 @@ export default defineConfig({
|
|||||||
options: {
|
options: {
|
||||||
postcssOptions: {
|
postcssOptions: {
|
||||||
plugins: {
|
plugins: {
|
||||||
tailwindcss: {},
|
|
||||||
autoprefixer: {},
|
autoprefixer: {},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -157,6 +156,7 @@ export default defineConfig({
|
|||||||
minimizerOptions: { targets },
|
minimizerOptions: { targets },
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
realContentHash: true,
|
||||||
},
|
},
|
||||||
experiments: {
|
experiments: {
|
||||||
css: true,
|
css: true,
|
||||||
|
@ -1,3 +1,2 @@
|
|||||||
@tailwind base;
|
@unocss preflights;
|
||||||
@tailwind components;
|
@unocss default;
|
||||||
@tailwind utilities;
|
|
||||||
|
114
src/pages/components/layout/MainLayout.tsx
Normal file
114
src/pages/components/layout/MainLayout.tsx
Normal 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;
|
11
src/pages/components/layout/index.css
Normal file
11
src/pages/components/layout/index.css
Normal 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;
|
||||||
|
}
|
@ -1,10 +1,15 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import ReactDOM from "react-dom/client";
|
import ReactDOM from "react-dom/client";
|
||||||
import App from "./App.tsx";
|
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";
|
import "@App/index.css";
|
||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
|
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<App />
|
<MainLayout className="!flex-col !px-4 box-border">
|
||||||
|
<App />
|
||||||
|
</MainLayout>
|
||||||
</React.StrictMode>
|
</React.StrictMode>
|
||||||
);
|
);
|
||||||
|
17
src/store/hooks.ts
Normal file
17
src/store/hooks.ts
Normal 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
35
src/store/store.ts
Normal 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>;
|
@ -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
8
uno.config.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { defineConfig, presetUno } from "unocss";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
content: {
|
||||||
|
filesystem: ["./src/**/*.{html,js,ts,jsx,tsx}"],
|
||||||
|
},
|
||||||
|
presets: [presetUno()],
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user