添加页面
This commit is contained in:
parent
4b7957256e
commit
804266c6dd
1
.gitignore
vendored
1
.gitignore
vendored
@ -13,3 +13,4 @@ dist/
|
||||
.idea
|
||||
|
||||
coverage
|
||||
tailwind.config.js
|
||||
|
@ -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
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: {
|
||||
postcssOptions: {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
},
|
||||
@ -157,6 +156,7 @@ export default defineConfig({
|
||||
minimizerOptions: { targets },
|
||||
}),
|
||||
],
|
||||
realContentHash: true,
|
||||
},
|
||||
experiments: {
|
||||
css: true,
|
||||
|
@ -1,3 +1,2 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@unocss preflights;
|
||||
@unocss default;
|
||||
|
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 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
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