Просмотр исходного кода

:tada: create antd v6 & antd-x framework

China Kassapa 3 месяцев назад
Родитель
Сommit
880f5f66b2

+ 1 - 0
.gitignore

@@ -5,5 +5,6 @@
 *.swp
 *.log
 .stop
+*.xz
 
 /k8s/

+ 1 - 1
dashboard-v6/README.md

@@ -5,6 +5,6 @@
 ```bash
 npm install
 
-# http://localhost:4000/pcd-v2/
+# http://localhost:4000/pcd-v2026/
 npm run dev
 ```

+ 5 - 4
dashboard-v6/package.json

@@ -1,7 +1,7 @@
 {
   "name": "dashboard",
   "private": true,
-  "version": "0.0.0",
+  "version": "2026.1.1",
   "type": "module",
   "scripts": {
     "dev": "vite",
@@ -10,17 +10,18 @@
     "preview": "vite preview"
   },
   "dependencies": {
+    "@ant-design/x": "^2.1.2",
     "@graphiql/react": "^0.37.3",
-    "@reduxjs/toolkit": "^2.11.0",
+    "@reduxjs/toolkit": "^2.11.2",
     "@types/js-cookie": "^3.0.6",
-    "antd": "^6.0.1",
+    "antd": "^6.1.3",
     "jose": "^6.1.3",
     "js-cookie": "^3.0.5",
     "react": "^19.2.0",
     "react-dom": "^19.2.0",
     "react-intl": "^7.1.14",
     "react-redux": "^9.2.0",
-    "react-router": "^7.10.1",
+    "react-router": "^7.11.0",
     "usehooks-ts": "^3.1.1"
   },
   "devDependencies": {

+ 2 - 1
dashboard-v6/scripts/dashboard.sh

@@ -2,11 +2,12 @@
 
 set -e
 
+# @ant-design/pro-components
 npm install --save \
     react-router react-intl @reduxjs/toolkit react-redux \
     @graphiql/react \
     usehooks-ts jose \
     js-cookie @types/js-cookie \
-    antd @ant-design/pro-components
+    antd @ant-design/x
 
 exit 0

+ 0 - 42
dashboard-v6/src/App.css

@@ -1,42 +0,0 @@
-#root {
-  max-width: 1280px;
-  margin: 0 auto;
-  padding: 2rem;
-  text-align: center;
-}
-
-.logo {
-  height: 6em;
-  padding: 1.5em;
-  will-change: filter;
-  transition: filter 300ms;
-}
-.logo:hover {
-  filter: drop-shadow(0 0 2em #646cffaa);
-}
-.logo.react:hover {
-  filter: drop-shadow(0 0 2em #61dafbaa);
-}
-
-@keyframes logo-spin {
-  from {
-    transform: rotate(0deg);
-  }
-  to {
-    transform: rotate(360deg);
-  }
-}
-
-@media (prefers-reduced-motion: no-preference) {
-  a:nth-of-type(2) .logo {
-    animation: logo-spin infinite 20s linear;
-  }
-}
-
-.card {
-  padding: 2em;
-}
-
-.read-the-docs {
-  color: #888;
-}

+ 21 - 31
dashboard-v6/src/App.tsx

@@ -1,35 +1,25 @@
-import { useState } from 'react'
-import reactLogo from './assets/react.svg'
-import viteLogo from '/vite.svg'
-import './App.css'
+import { Suspense } from "react";
+import { IntlProvider } from "react-intl";
+import { Provider } from "react-redux";
 
-function App() {
-  const [count, setCount] = useState(0)
+import Loading from "./components/Loading";
+import Router from "./Router";
+import store from "./store";
+import { detect as detect_locale, messages as get_messages } from "./locales";
 
+const locale = detect_locale();
+const messages = get_messages(locale);
+
+const Widget = () => {
   return (
-    <>
-      <div>
-        <a href="https://vite.dev" target="_blank">
-          <img src={viteLogo} className="logo" alt="Vite logo" />
-        </a>
-        <a href="https://react.dev" target="_blank">
-          <img src={reactLogo} className="logo react" alt="React logo" />
-        </a>
-      </div>
-      <h1>Vite + React</h1>
-      <div className="card">
-        <button onClick={() => setCount((count) => count + 1)}>
-          count is {count}
-        </button>
-        <p>
-          Edit <code>src/App.tsx</code> and save to test HMR
-        </p>
-      </div>
-      <p className="read-the-docs">
-        Click on the Vite and React logos to learn more
-      </p>
-    </>
-  )
-}
+    <IntlProvider locale={locale} messages={messages}>
+      <Suspense fallback={<Loading />}>
+        <Provider store={store}>
+          <Router />
+        </Provider>
+      </Suspense>
+    </IntlProvider>
+  );
+};
 
-export default App
+export default Widget;

+ 45 - 0
dashboard-v6/src/Router.tsx

@@ -0,0 +1,45 @@
+import { lazy } from "react";
+import { createBrowserRouter } from "react-router";
+import { RouterProvider } from "react-router/dom";
+
+const UsersSignIn = lazy(() => import("./pages/users/sign-in"));
+const UsersPersonal = lazy(() => import("./pages/users/personal"));
+const DashboardIndex = lazy(() => import("./pages/dashboard/index"));
+const Home = lazy(() => import("./pages/home"));
+
+const AnonymousLayout = lazy(() => import("./layouts/anonymous"));
+const DashboardLayout = lazy(() => import("./layouts/dashboard"));
+
+const router = createBrowserRouter(
+  [
+    {
+      path: "/",
+      children: [
+        { index: true, Component: Home },
+        {
+          path: "anonymous",
+          Component: AnonymousLayout,
+          children: [{ path: "sign-in", Component: UsersSignIn }],
+        },
+        {
+          path: "dashboard",
+          Component: DashboardLayout,
+          children: [
+            { index: true, Component: DashboardIndex },
+            {
+              path: "users",
+              children: [{ path: "personal", Component: UsersPersonal }],
+            },
+          ],
+        },
+      ],
+    },
+  ],
+  { basename: import.meta.env.BASE_URL }
+);
+
+const Widget = () => {
+  return <RouterProvider router={router} />;
+};
+
+export default Widget;

+ 0 - 0
dashboard-v6/src/api/index.ts


+ 6 - 0
dashboard-v6/src/components/Loading.tsx

@@ -0,0 +1,6 @@
+const Widget = () => {
+  // TODO
+  return <>Loading</>;
+};
+
+export default Widget;

+ 5 - 63
dashboard-v6/src/index.css

@@ -1,68 +1,10 @@
-:root {
-  font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
-  line-height: 1.5;
-  font-weight: 400;
-
-  color-scheme: light dark;
-  color: rgba(255, 255, 255, 0.87);
-  background-color: #242424;
-
-  font-synthesis: none;
-  text-rendering: optimizeLegibility;
-  -webkit-font-smoothing: antialiased;
-  -moz-osx-font-smoothing: grayscale;
-}
-
-a {
-  font-weight: 500;
-  color: #646cff;
-  text-decoration: inherit;
-}
-a:hover {
-  color: #535bf2;
-}
-
+html,
 body {
+  padding: 0;
   margin: 0;
-  display: flex;
-  place-items: center;
-  min-width: 320px;
-  min-height: 100vh;
-}
-
-h1 {
-  font-size: 3.2em;
-  line-height: 1.1;
-}
-
-button {
-  border-radius: 8px;
-  border: 1px solid transparent;
-  padding: 0.6em 1.2em;
-  font-size: 1em;
-  font-weight: 500;
-  font-family: inherit;
-  background-color: #1a1a1a;
-  cursor: pointer;
-  transition: border-color 0.25s;
-}
-button:hover {
-  border-color: #646cff;
-}
-button:focus,
-button:focus-visible {
-  outline: 4px auto -webkit-focus-ring-color;
+  background-color: #fff;
 }
 
-@media (prefers-color-scheme: light) {
-  :root {
-    color: #213547;
-    background-color: #ffffff;
-  }
-  a:hover {
-    color: #747bff;
-  }
-  button {
-    background-color: #f9f9f9;
-  }
+#root {
+  padding: 24px;
 }

+ 6 - 0
dashboard-v6/src/layouts/Footer.tsx

@@ -0,0 +1,6 @@
+const Widget = () => {
+  // TODO
+  return <div>base layout footer</div>;
+};
+
+export default Widget;

+ 21 - 0
dashboard-v6/src/layouts/anonymous/index.tsx

@@ -0,0 +1,21 @@
+import { Outlet } from "react-router";
+
+import Footer from "../Footer";
+
+const Widget = () => {
+  // TODO
+  return (
+    <div>
+      <div>anonymous header</div>
+      <div>
+        <Outlet />
+      </div>
+      <div>
+        anonymous layout footer
+        <Footer />
+      </div>
+    </div>
+  );
+};
+
+export default Widget;

+ 21 - 0
dashboard-v6/src/layouts/dashboard/index.tsx

@@ -0,0 +1,21 @@
+import { Outlet } from "react-router";
+
+import Footer from "../Footer";
+
+const Widget = () => {
+  // TODO
+  return (
+    <div>
+      <div>dashboard header</div>
+      <div>
+        <Outlet />
+      </div>
+      <div>
+        dashboard layout footer
+        <Footer />
+      </div>
+    </div>
+  );
+};
+
+export default Widget;

+ 3 - 0
dashboard-v6/src/locales/en-US.ts

@@ -0,0 +1,3 @@
+export default {
+  "buttons.ok": "Ok",
+};

+ 51 - 0
dashboard-v6/src/locales/index.ts

@@ -0,0 +1,51 @@
+import Cookies from "js-cookie";
+import { type MessageFormatElement } from "react-intl";
+import dayjs from "dayjs";
+import isLeapYear from "dayjs/plugin/isLeapYear";
+import timezone from "dayjs/plugin/timezone";
+import utc from "dayjs/plugin/utc";
+import localizedFormat from "dayjs/plugin/localizedFormat";
+import "dayjs/locale/zh-cn";
+import "dayjs/locale/zh-tw";
+import "dayjs/locale/en";
+
+dayjs.extend(isLeapYear);
+dayjs.extend(utc);
+dayjs.extend(timezone);
+dayjs.extend(localizedFormat);
+
+import enUS from "./en-US";
+import zhHans from "./zh-Hans";
+import zhHant from "./zh-Hant";
+
+const KEY = "locale";
+
+export const detect = (): string => Cookies.get(KEY) || "en-US";
+
+export const set = (locale: string) => {
+  switch (locale) {
+    case "zh-Hans":
+      dayjs.locale("zh-cn");
+      break;
+    case "zh-Hants":
+      dayjs.locale("zh-tw");
+      break;
+    default:
+      dayjs.locale("en-us");
+      break;
+  }
+  Cookies.set(KEY, locale);
+};
+
+export const messages = (
+  locale: string
+): Record<string, string> | Record<string, MessageFormatElement[]> => {
+  switch (locale) {
+    case "zh-Hans":
+      return zhHans;
+    case "zh-Hants":
+      return zhHant;
+    default:
+      return enUS;
+  }
+};

+ 1 - 0
dashboard-v6/src/locales/zh-Hans.ts

@@ -0,0 +1 @@
+export default {};

+ 1 - 0
dashboard-v6/src/locales/zh-Hant.ts

@@ -0,0 +1 @@
+export default {};

+ 9 - 7
dashboard-v6/src/main.tsx

@@ -1,10 +1,12 @@
-import { StrictMode } from 'react'
-import { createRoot } from 'react-dom/client'
-import './index.css'
-import App from './App.tsx'
+import { StrictMode } from "react";
+import { createRoot } from "react-dom/client";
 
-createRoot(document.getElementById('root')!).render(
+import App from "./App.tsx";
+
+import "./index.css";
+
+createRoot(document.getElementById("root")!).render(
   <StrictMode>
     <App />
-  </StrictMode>,
-)
+  </StrictMode>
+);

+ 6 - 0
dashboard-v6/src/pages/dashboard/index.tsx

@@ -0,0 +1,6 @@
+const Widget = () => {
+  // TODO
+  return <>dashboard index</>;
+};
+
+export default Widget;

+ 6 - 0
dashboard-v6/src/pages/home.tsx

@@ -0,0 +1,6 @@
+const Widget = () => {
+  // TODO
+  return <>home</>;
+};
+
+export default Widget;

+ 6 - 0
dashboard-v6/src/pages/users/personal/index.tsx

@@ -0,0 +1,6 @@
+const Widget = () => {
+  // TODO
+  return <>User personal</>;
+};
+
+export default Widget;

+ 6 - 0
dashboard-v6/src/pages/users/sign-in.tsx

@@ -0,0 +1,6 @@
+const Widget = () => {
+  // TODO
+  return <>user sign-in</>;
+};
+
+export default Widget;

+ 41 - 0
dashboard-v6/src/reducers/layout.ts

@@ -0,0 +1,41 @@
+import { createSlice, type PayloadAction } from "@reduxjs/toolkit";
+
+import type { RootState } from "../store";
+
+export interface IRefresh {
+  title: string;
+  subhead: string;
+  description: string;
+  copyright: string;
+  version: string;
+}
+
+interface LayoutState {
+  title?: string;
+  subhead?: string;
+  description?: string;
+  copyright?: string;
+  version?: string;
+}
+
+const initialState: LayoutState = {};
+
+export const layoutSlice = createSlice({
+  name: "layout",
+  initialState,
+  reducers: {
+    refresh: (state, action: PayloadAction<IRefresh>) => {
+      state.version = action.payload.version;
+      state.subhead = action.payload.subhead;
+      state.description = action.payload.description;
+      state.copyright = action.payload.copyright;
+      state.version = action.payload.version;
+    },
+  },
+});
+
+export const { refresh } = layoutSlice.actions;
+
+export const selectVersion = (state: RootState) => state.layout.version;
+
+export default layoutSlice.reducer;

+ 63 - 0
dashboard-v6/src/reducers/session.ts

@@ -0,0 +1,63 @@
+import { createSlice, type PayloadAction } from "@reduxjs/toolkit";
+import * as jose from "jose";
+
+import type { RootState } from "../store";
+
+export const SIGN_IN = "/anonymous/sign-in";
+export const PERSONAL = "/dashboard/personal";
+
+export interface ISignIn {
+  token: string;
+  roles: string[];
+}
+
+const KEY = "token";
+export const get = (): string | null => {
+  return sessionStorage.getItem(KEY);
+};
+
+const set = (token: string) => {
+  sessionStorage.setItem(KEY, token);
+};
+
+const remove = () => {
+  sessionStorage.removeItem(KEY);
+};
+
+interface SessionState {
+  name?: string;
+  roles: string[];
+}
+
+const initialState: SessionState = { roles: [] };
+
+export const sessionSlice = createSlice({
+  name: "session",
+  initialState,
+  reducers: {
+    signOut: (state) => {
+      state.name = undefined;
+      state.roles = [];
+      remove();
+    },
+    signIn: (state, action: PayloadAction<ISignIn>) => {
+      try {
+        const claims = jose.decodeJwt(action.payload.token);
+        if (claims.sub) {
+          state.name = claims.sub;
+          state.roles = action.payload.roles;
+        }
+        set(action.payload.token);
+      } catch (e) {
+        console.error(e);
+        state.name = undefined;
+      }
+    },
+  },
+});
+
+export const { signIn, signOut } = sessionSlice.actions;
+
+export const currentUser = (state: RootState) => state.session.name;
+
+export default sessionSlice.reducer;

+ 17 - 0
dashboard-v6/src/store.ts

@@ -0,0 +1,17 @@
+import { configureStore } from "@reduxjs/toolkit";
+
+import layoutReducer from "./reducers/layout";
+import sessionReducer from "./reducers/session";
+
+const store = configureStore({
+  reducer: {
+    layout: layoutReducer,
+    session: sessionReducer,
+  },
+});
+
+export type RootState = ReturnType<typeof store.getState>;
+export type AppDispatch = typeof store.dispatch;
+export type AppStore = typeof store;
+
+export default store;

+ 1 - 1
dashboard-v6/vite.config.ts

@@ -3,7 +3,7 @@ import react from "@vitejs/plugin-react";
 
 // https://vite.dev/config/
 export default defineConfig({
-  base: "/pcd-v2/",
+  base: "/pcd-v2026/",
   server: {
     host: "127.0.0.1",
     port: 4000,

+ 1 - 1
scripts/third-dependencies-pack.sh

@@ -5,7 +5,7 @@ set -e
 export VERSION=$(date "+%4Y%m%d%H%M%S")
 
 if [[ "$#" -eq 1 && "$1" == "dashboard" ]]; then
-    XZ_OPT=-9 tar -cJf dashboard-v6-$VERSION.tar.xz -C dashboard-v6/dashboard node_modules package-lock.json
+    XZ_OPT=-9 tar -cJf dashboard-v6-$VERSION.tar.xz -C dashboard-v6 node_modules package-lock.json
     XZ_OPT=-9 tar -cJf dashboard-v4-$VERSION.tar.xz -C dashboard-v4/dashboard node_modules yarn.lock
 elif [[ "$#" -eq 1 && "$1" == "laravel" ]]; then
     XZ_OPT=-9 tar -cJf dashboard-$VERSION.tar.xz -C xxx node_modules package-lock.json