🎉 init:
This commit is contained in:
commit
bdaa469836
|
@ -0,0 +1 @@
|
||||||
|
VITE_BASE_API_URL=/dev
|
|
@ -0,0 +1,18 @@
|
||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
env: { browser: true, es2020: true },
|
||||||
|
extends: [
|
||||||
|
'eslint:recommended',
|
||||||
|
'plugin:@typescript-eslint/recommended',
|
||||||
|
'plugin:react-hooks/recommended',
|
||||||
|
],
|
||||||
|
ignorePatterns: ['dist', '.eslintrc.cjs'],
|
||||||
|
parser: '@typescript-eslint/parser',
|
||||||
|
plugins: ['react-refresh'],
|
||||||
|
rules: {
|
||||||
|
'react-refresh/only-export-components': [
|
||||||
|
'warn',
|
||||||
|
{ allowConstantExport: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
Binary file not shown.
|
@ -0,0 +1 @@
|
||||||
|
nodeLinker: node-modules
|
|
@ -0,0 +1,30 @@
|
||||||
|
# React + TypeScript + Vite
|
||||||
|
|
||||||
|
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
|
||||||
|
|
||||||
|
Currently, two official plugins are available:
|
||||||
|
|
||||||
|
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
|
||||||
|
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
|
||||||
|
|
||||||
|
## Expanding the ESLint configuration
|
||||||
|
|
||||||
|
If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
|
||||||
|
|
||||||
|
- Configure the top-level `parserOptions` property like this:
|
||||||
|
|
||||||
|
```js
|
||||||
|
export default {
|
||||||
|
// other rules...
|
||||||
|
parserOptions: {
|
||||||
|
ecmaVersion: 'latest',
|
||||||
|
sourceType: 'module',
|
||||||
|
project: ['./tsconfig.json', './tsconfig.node.json'],
|
||||||
|
tsconfigRootDir: __dirname,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked`
|
||||||
|
- Optionally add `plugin:@typescript-eslint/stylistic-type-checked`
|
||||||
|
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"symbol_url": "请参考README.md,复制官网提供的JS链接",
|
||||||
|
"use_typescript": false,
|
||||||
|
"save_dir": "./src/components/iconfont",
|
||||||
|
"trim_icon_prefix": "icon",
|
||||||
|
"unit": "px",
|
||||||
|
"default_icon_size": 18
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
<!--
|
||||||
|
* @LastEditors: John
|
||||||
|
* @Date: 2024-06-17 17:20:03
|
||||||
|
* @LastEditTime: 2024-06-18 10:51:36
|
||||||
|
* @Author: John
|
||||||
|
-->
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||||
|
<meta
|
||||||
|
name="viewport"
|
||||||
|
content="width=device-width, initial-scale=1.0,maximum-scale=1, minimum-scale=1, user-scalable=no"
|
||||||
|
/>
|
||||||
|
<title>red devils</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,49 @@
|
||||||
|
{
|
||||||
|
"name": "red-devils",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "tsc && vite build",
|
||||||
|
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||||
|
"preview": "vite preview",
|
||||||
|
"iconfont": "npx iconfont-h5"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@hyper-fetch/core": "^5.7.5",
|
||||||
|
"@hyper-fetch/react": "^5.7.5",
|
||||||
|
"@tanstack/react-query": "^5.45.1",
|
||||||
|
"@web3modal/wagmi": "^5.0.2",
|
||||||
|
"antd-mobile": "^5.36.1",
|
||||||
|
"i18next": "^23.11.5",
|
||||||
|
"normalize.css": "^8.0.1",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-dom": "^18.2.0",
|
||||||
|
"react-i18next": "^14.1.2",
|
||||||
|
"react-router-dom": "^6.23.1",
|
||||||
|
"viem": "^2.14.2",
|
||||||
|
"wagmi": "^2.10.2",
|
||||||
|
"zustand": "^4.5.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^20.14.2",
|
||||||
|
"@types/postcss-pxtorem": "^6",
|
||||||
|
"@types/react": "^18.2.66",
|
||||||
|
"@types/react-dom": "^18.2.22",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^7.2.0",
|
||||||
|
"@typescript-eslint/parser": "^7.2.0",
|
||||||
|
"@vitejs/plugin-react": "^4.2.1",
|
||||||
|
"autoprefixer": "^10.4.19",
|
||||||
|
"eslint": "^8.57.0",
|
||||||
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
|
"eslint-plugin-react-refresh": "^0.4.6",
|
||||||
|
"postcss": "^8.4.38",
|
||||||
|
"postcss-pxtorem": "5.1.1",
|
||||||
|
"react-iconfont-cli": "^2.0.2",
|
||||||
|
"typescript": "^5.2.2",
|
||||||
|
"vite": "^5.2.0",
|
||||||
|
"vite-plugin-compression": "^0.5.1",
|
||||||
|
"vite-plugin-node-polyfills": "^0.22.0"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* @LastEditors: John
|
||||||
|
* @Date: 2024-06-17 18:25:10
|
||||||
|
* @LastEditTime: 2024-06-17 18:45:37
|
||||||
|
* @Author: John
|
||||||
|
*/
|
||||||
|
export default {
|
||||||
|
plugins: {
|
||||||
|
autoprefixer: {},
|
||||||
|
"postcss-pxtorem": {
|
||||||
|
rootValue: 37.5,
|
||||||
|
propList: ["*"],
|
||||||
|
exclude: (e) => {
|
||||||
|
if (/.module\.css$/.test(e)) {
|
||||||
|
console.log(e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 8.5 KiB |
|
@ -0,0 +1,4 @@
|
||||||
|
body {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
/*
|
||||||
|
* @LastEditors: John
|
||||||
|
* @Date: 2024-06-17 17:20:03
|
||||||
|
* @LastEditTime: 2024-06-17 17:36:20
|
||||||
|
* @Author: John
|
||||||
|
*/
|
||||||
|
import { Route, Routes } from "react-router-dom";
|
||||||
|
import "./App.css";
|
||||||
|
import Home from "./pages/Home";
|
||||||
|
function App() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Routes>
|
||||||
|
<Route path="/" element={<Home />} />
|
||||||
|
</Routes>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App;
|
|
@ -0,0 +1,103 @@
|
||||||
|
/*
|
||||||
|
* @LastEditors: John
|
||||||
|
* @Date: 2024-06-17 18:01:43
|
||||||
|
* @LastEditTime: 2024-06-17 18:16:57
|
||||||
|
* @Author: John
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* @LastEditors: John
|
||||||
|
* @Date: 2024-06-17 18:01:43
|
||||||
|
* @LastEditTime: 2024-06-17 18:05:15
|
||||||
|
* @Author: John
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* @LastEditors: John
|
||||||
|
* @Date: 2024-03-06 11:26:45
|
||||||
|
* @LastEditTime: 2024-05-27 16:32:26
|
||||||
|
* @Author: John
|
||||||
|
*/
|
||||||
|
import { createWeb3Modal } from "@web3modal/wagmi/react";
|
||||||
|
import { defaultWagmiConfig } from "@web3modal/wagmi/react/config";
|
||||||
|
|
||||||
|
import { WagmiProvider } from "wagmi";
|
||||||
|
import { bsc } from "wagmi/chains";
|
||||||
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||||
|
import { bnbTestNetwork } from "@/constants/wallet";
|
||||||
|
import { PropsWithChildren } from "react";
|
||||||
|
|
||||||
|
// 0. Setup queryClient
|
||||||
|
const queryClient = new QueryClient();
|
||||||
|
|
||||||
|
// 1. Get projectId at https://cloud.walletconnect.com
|
||||||
|
const projectId = "10834bf2b5d92d012e4b0b05e1150246";
|
||||||
|
|
||||||
|
// 2. Create wagmiConfig
|
||||||
|
const metadata = {
|
||||||
|
name: "red-devils",
|
||||||
|
description: "red devils dapp",
|
||||||
|
url: import.meta.env.BASE_URL, // origin must match your domain & subdomain
|
||||||
|
icons: [`${import.meta.env.BASE_URL}/favicon.svg`],
|
||||||
|
};
|
||||||
|
|
||||||
|
let chains = import.meta.env.PROD
|
||||||
|
? ([bsc] as const)
|
||||||
|
: ([bnbTestNetwork] as const);
|
||||||
|
|
||||||
|
export const config = defaultWagmiConfig({
|
||||||
|
chains, // required
|
||||||
|
projectId, // required
|
||||||
|
metadata, // required
|
||||||
|
enableWalletConnect: true, // Optional - true by default
|
||||||
|
enableInjected: true, // Optional - true by default
|
||||||
|
enableEIP6963: true, // Optional - true by default
|
||||||
|
enableCoinbase: false, // Optional - true by default
|
||||||
|
multiInjectedProviderDiscovery: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// console.log(window.ethereum);
|
||||||
|
// 3. Create modal
|
||||||
|
createWeb3Modal({
|
||||||
|
wagmiConfig: config,
|
||||||
|
projectId,
|
||||||
|
themeVariables: {
|
||||||
|
"--w3m-accent": "#ea6d28",
|
||||||
|
},
|
||||||
|
featuredWalletIds: [
|
||||||
|
...(window.ethereum
|
||||||
|
? []
|
||||||
|
: ["c57ca95b47569778a828d19178114f4db188b89b763c899ba0be274e97267d96"]),
|
||||||
|
...(window.okxwallet
|
||||||
|
? []
|
||||||
|
: ["971e689d0a5be527bac79629b4ee9b925e82208e5168b733496a09c0faed0709"]),
|
||||||
|
...(window.bitkeep?.ethereum
|
||||||
|
? []
|
||||||
|
: ["38f5d18bd8522c244bdd70cb4a68e0e718865155811c043f052fb9f1c51de662"]),
|
||||||
|
...(window.ethereum?.isTokenPocket
|
||||||
|
? []
|
||||||
|
: ["20459438007b75f4f4acb98bf29aa3b800550309646d375da5fd4aac6c2a2c66"]),
|
||||||
|
],
|
||||||
|
allWallets: "HIDE",
|
||||||
|
});
|
||||||
|
|
||||||
|
export function WalletProvider({ children }: PropsWithChildren) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<WagmiProvider config={config}>
|
||||||
|
<QueryClientProvider client={queryClient}>
|
||||||
|
{children}
|
||||||
|
</QueryClientProvider>
|
||||||
|
</WagmiProvider>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
ethereum?: { isTokenPocket?: boolean };
|
||||||
|
okxwallet: {
|
||||||
|
bitcoin: any;
|
||||||
|
};
|
||||||
|
unisat: any;
|
||||||
|
bitkeep?: { ethereum?: any };
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
/*
|
||||||
|
* @LastEditors: John
|
||||||
|
* @Date: 2024-06-17 17:57:13
|
||||||
|
* @LastEditTime: 2024-06-17 17:57:30
|
||||||
|
* @Author: John
|
||||||
|
*/
|
||||||
|
export enum ASYNC_STORAGE_KEY {
|
||||||
|
Token = "user.token",
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { defineChain } from "viem/utils";
|
||||||
|
|
||||||
|
export const bnbTestNetwork = defineChain({
|
||||||
|
id: 97,
|
||||||
|
name: "BNB-test",
|
||||||
|
nativeCurrency: {
|
||||||
|
name: "tBNB",
|
||||||
|
symbol: "tBNB",
|
||||||
|
decimals: 18,
|
||||||
|
},
|
||||||
|
rpcUrls: {
|
||||||
|
default: {
|
||||||
|
http: ["https://data-seed-prebsc-1-s3.binance.org:8545"],
|
||||||
|
webSocket: undefined,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
blockExplorers: {
|
||||||
|
default: {
|
||||||
|
name: "BNB-test",
|
||||||
|
url: "https://testnet.bscscan.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testnet: true,
|
||||||
|
});
|
|
@ -0,0 +1,78 @@
|
||||||
|
/*
|
||||||
|
* @LastEditors: John
|
||||||
|
* @Date: 2024-06-17 17:37:21
|
||||||
|
* @LastEditTime: 2024-06-18 09:54:18
|
||||||
|
* @Author: John
|
||||||
|
*/
|
||||||
|
import { createContext, useContext, useState, PropsWithChildren } from "react";
|
||||||
|
|
||||||
|
type EventName = "TEST";
|
||||||
|
export type EventCallBackData = {
|
||||||
|
TEST: {};
|
||||||
|
};
|
||||||
|
// 定义事件的类型
|
||||||
|
type EventType = {
|
||||||
|
[eventName in EventName]?: ((data: EventCallBackData[EventName]) => void)[];
|
||||||
|
};
|
||||||
|
|
||||||
|
// 定义事件总线上下文的类型
|
||||||
|
type EventBusContextType = {
|
||||||
|
subscribe: <T extends EventName>(
|
||||||
|
eventName: T,
|
||||||
|
callback: (data: EventCallBackData[T]) => void
|
||||||
|
) => void;
|
||||||
|
unsubscribe: <T extends EventName>(
|
||||||
|
eventName: T,
|
||||||
|
callback: (data: EventCallBackData[T]) => void
|
||||||
|
) => void;
|
||||||
|
emit: <T extends EventName>(eventName: T, data: EventCallBackData[T]) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const EventBusContext = createContext<EventBusContextType | undefined>(
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
|
||||||
|
export const useEventBus = () => {
|
||||||
|
const context = useContext(EventBusContext);
|
||||||
|
if (!context) {
|
||||||
|
throw new Error("useEventBus must be used within an EventBusProvider");
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
};
|
||||||
|
|
||||||
|
const EventBusProvider = ({ children }: PropsWithChildren) => {
|
||||||
|
const [events, setEvents] = useState<EventType>({});
|
||||||
|
|
||||||
|
const subscribe = (eventName: EventName, callback: (data: any) => void) => {
|
||||||
|
setEvents((prevEvents) => ({
|
||||||
|
...prevEvents,
|
||||||
|
[eventName]: [...(prevEvents[eventName] || []), callback],
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const unsubscribe = (eventName: EventName, callback: (data: any) => void) => {
|
||||||
|
setEvents((prevEvents) => ({
|
||||||
|
...prevEvents,
|
||||||
|
[eventName]: prevEvents[eventName]?.filter((cb) => cb !== callback),
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const emit = (eventName: EventName, data: any) => {
|
||||||
|
const eventCallbacks = events[eventName] || [];
|
||||||
|
eventCallbacks.forEach((callback) => callback(data));
|
||||||
|
};
|
||||||
|
|
||||||
|
const eventBusContextValue: EventBusContextType = {
|
||||||
|
subscribe,
|
||||||
|
unsubscribe,
|
||||||
|
emit,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EventBusContext.Provider value={eventBusContextValue}>
|
||||||
|
{children}
|
||||||
|
</EventBusContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EventBusProvider;
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* @LastEditors: John
|
||||||
|
* @Date: 2024-06-18 10:30:43
|
||||||
|
* @LastEditTime: 2024-06-18 10:34:19
|
||||||
|
* @Author: John
|
||||||
|
*/
|
||||||
|
import i18next from "i18next";
|
||||||
|
import { initReactI18next } from "react-i18next";
|
||||||
|
import en from "./translation/en.json";
|
||||||
|
i18next.use(initReactI18next).init({
|
||||||
|
compatibilityJSON: "v3", // <--- add this line.
|
||||||
|
lng: "en", // if you're using a language detector, do not define the lng option
|
||||||
|
// lng: LANGUAGE["en-US"],
|
||||||
|
debug: false,
|
||||||
|
resources: {
|
||||||
|
en: {
|
||||||
|
translation: en,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// if you see an error like: "Argument of type 'DefaultTFuncReturn' is not assignable to parameter of type xyz"
|
||||||
|
// set returnNull to false (and also in the i18next.d.ts options)
|
||||||
|
// returnNull: false,
|
||||||
|
});
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"AppName": "red devils"
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* @LastEditors: John
|
||||||
|
* @Date: 2024-06-17 17:20:03
|
||||||
|
* @LastEditTime: 2024-06-18 10:46:11
|
||||||
|
* @Author: John
|
||||||
|
*/
|
||||||
|
import "@/i18n/init.ts";
|
||||||
|
import "antd-mobile/es/global";
|
||||||
|
import ReactDOM from "react-dom/client";
|
||||||
|
import App from "./App.tsx";
|
||||||
|
import "normalize.css";
|
||||||
|
import "./index.css";
|
||||||
|
import { HashRouter } from "react-router-dom";
|
||||||
|
import EventBusProvider from "./context/EventBusContext.tsx";
|
||||||
|
import { WalletProvider } from "./components/WalletProvider.tsx";
|
||||||
|
import flexible from "./utils/flexible.ts";
|
||||||
|
|
||||||
|
flexible(window, document);
|
||||||
|
ReactDOM.createRoot(document.getElementById("root")!).render(
|
||||||
|
<>
|
||||||
|
<WalletProvider>
|
||||||
|
<EventBusProvider>
|
||||||
|
<HashRouter>
|
||||||
|
<App />
|
||||||
|
</HashRouter>
|
||||||
|
</EventBusProvider>
|
||||||
|
</WalletProvider>
|
||||||
|
</>
|
||||||
|
);
|
|
@ -0,0 +1,10 @@
|
||||||
|
.Home {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 10px;
|
||||||
|
.text {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
import classes from "./Home.module.css";
|
||||||
|
import useUserStore from "@/store/User";
|
||||||
|
import { copyText } from "@/utils";
|
||||||
|
import { useWeb3Modal } from "@web3modal/wagmi/react";
|
||||||
|
import Button from "antd-mobile/es/components/button";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
export default function () {
|
||||||
|
const { Token, UpdateToken } = useUserStore();
|
||||||
|
const { open } = useWeb3Modal();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
useEffect(() => {
|
||||||
|
UpdateToken("user token abc");
|
||||||
|
|
||||||
|
return () => {};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
console.log("user token:", Token);
|
||||||
|
|
||||||
|
return () => {};
|
||||||
|
}, [Token]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className={classes.Home}>
|
||||||
|
<span className={classes.text}>{t("AppName")}</span>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
open();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Connect Wallet
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
copyText("123");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
copy text
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* @LastEditors: John
|
||||||
|
* @Date: 2024-06-18 10:09:21
|
||||||
|
* @LastEditTime: 2024-06-18 10:27:04
|
||||||
|
* @Author: John
|
||||||
|
*/
|
||||||
|
import { Client } from "@hyper-fetch/core";
|
||||||
|
import { BASE_RESPONSE } from "./module";
|
||||||
|
|
||||||
|
const client = new Client({ url: import.meta.env.VITE_BASE_API_URL })
|
||||||
|
.onAuth((req) => {
|
||||||
|
return req;
|
||||||
|
})
|
||||||
|
.onRequest((req) => {
|
||||||
|
req.setHeaders({});
|
||||||
|
return req;
|
||||||
|
})
|
||||||
|
.onResponse((res) => {
|
||||||
|
return res;
|
||||||
|
});
|
||||||
|
|
||||||
|
export const POST = <P, R = any>({ url }: { url: string }) => {
|
||||||
|
return client.createRequest<BASE_RESPONSE<R>, P>()({
|
||||||
|
method: "POST",
|
||||||
|
endpoint: url,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const GET = <P, R = any>({ url }: { url: string }) => {
|
||||||
|
return client.createRequest<BASE_RESPONSE<R>, any, any, P>()({
|
||||||
|
method: "GET",
|
||||||
|
endpoint: url,
|
||||||
|
});
|
||||||
|
};
|
|
@ -0,0 +1,6 @@
|
||||||
|
export type BASE_RESPONSE<T = any> = {
|
||||||
|
code: 0 | 200;
|
||||||
|
data: T | null;
|
||||||
|
msg: string;
|
||||||
|
timeMillis: number;
|
||||||
|
}; // What's returned from request
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* @LastEditors: John
|
||||||
|
* @Date: 2024-06-17 17:45:43
|
||||||
|
* @LastEditTime: 2024-06-17 17:57:42
|
||||||
|
* @Author: John
|
||||||
|
*/
|
||||||
|
import { ASYNC_STORAGE_KEY } from "@/constants";
|
||||||
|
import { create } from "zustand";
|
||||||
|
import { createJSONStorage, persist } from "zustand/middleware";
|
||||||
|
|
||||||
|
interface UserState {
|
||||||
|
Token: string;
|
||||||
|
UpdateToken: (t: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useUserStore = create<UserState>()(
|
||||||
|
persist(
|
||||||
|
(set, _get) => ({
|
||||||
|
Token: "",
|
||||||
|
UpdateToken: (t) => set({ Token: t }),
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
name: ASYNC_STORAGE_KEY.Token, // name of item in the storage (must be unique)
|
||||||
|
storage: createJSONStorage(() => localStorage), // (optional) by default the 'localStorage' is used
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
export default useUserStore;
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* @LastEditors: John
|
||||||
|
* @Date: 2024-01-23 10:39:17
|
||||||
|
* @LastEditTime: 2024-06-18 10:35:21
|
||||||
|
* @Author: John
|
||||||
|
*/
|
||||||
|
// import the original type declarations
|
||||||
|
import "i18next";
|
||||||
|
// import all namespaces (for the default language, only)
|
||||||
|
import en from "../i18n/translation/en.json";
|
||||||
|
|
||||||
|
declare module "i18next" {
|
||||||
|
// Extend CustomTypeOptions
|
||||||
|
interface CustomTypeOptions {
|
||||||
|
// custom namespace type, if you changed it
|
||||||
|
defaultNS: "en";
|
||||||
|
// custom resources type
|
||||||
|
resources: {
|
||||||
|
en: typeof en;
|
||||||
|
};
|
||||||
|
// other
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* @LastEditors: John
|
||||||
|
* @Date: 2024-01-09 09:34:24
|
||||||
|
* @LastEditTime: 2024-06-17 18:37:01
|
||||||
|
* @Author: John
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default function flexible(window: Window, document: Document) {
|
||||||
|
var docEl = document.documentElement;
|
||||||
|
var dpr = window.devicePixelRatio || 1;
|
||||||
|
|
||||||
|
// adjust body font size
|
||||||
|
function setBodyFontSize() {
|
||||||
|
if (document.body) {
|
||||||
|
document.body.style.fontSize = 12 * dpr + "px";
|
||||||
|
} else {
|
||||||
|
document.addEventListener("DOMContentLoaded", setBodyFontSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setBodyFontSize();
|
||||||
|
|
||||||
|
// set 1rem = viewWidth / 10
|
||||||
|
function setRemUnit() {
|
||||||
|
var rem = docEl.clientWidth / 10;
|
||||||
|
// console.log("rem:", rem);
|
||||||
|
docEl.style.fontSize = rem + "px";
|
||||||
|
}
|
||||||
|
|
||||||
|
setRemUnit();
|
||||||
|
|
||||||
|
// reset rem unit on page resize
|
||||||
|
window.addEventListener("resize", setRemUnit);
|
||||||
|
window.addEventListener("pageshow", function (e) {
|
||||||
|
if (e.persisted) {
|
||||||
|
setRemUnit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// detect 0.5px supports
|
||||||
|
if (dpr >= 2) {
|
||||||
|
var fakeBody = document.createElement("body");
|
||||||
|
var testElement = document.createElement("div");
|
||||||
|
testElement.style.border = ".5px solid transparent";
|
||||||
|
fakeBody.appendChild(testElement);
|
||||||
|
docEl.appendChild(fakeBody);
|
||||||
|
if (testElement.offsetHeight === 1) {
|
||||||
|
docEl.classList.add("hairlines");
|
||||||
|
}
|
||||||
|
docEl.removeChild(fakeBody);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,79 @@
|
||||||
|
/*
|
||||||
|
* @LastEditors: John
|
||||||
|
* @Date: 2024-06-17 18:19:27
|
||||||
|
* @LastEditTime: 2024-06-18 10:48:05
|
||||||
|
* @Author: John
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Toast from "antd-mobile/es/components/toast";
|
||||||
|
|
||||||
|
export const ua = navigator.userAgent;
|
||||||
|
export const isIOS = /iphone|ipad|ipod|ios/i.test(ua);
|
||||||
|
export const isAndroid = /android|XiaoMi|MiuiBrowser/i.test(ua);
|
||||||
|
export const isMobile = isIOS || isAndroid;
|
||||||
|
export const isOKApp = /OKApp/i.test(ua);
|
||||||
|
|
||||||
|
export function shortenString(
|
||||||
|
inputString: string,
|
||||||
|
startLength: number,
|
||||||
|
endLength: number
|
||||||
|
) {
|
||||||
|
if (inputString.length <= startLength + endLength) {
|
||||||
|
return inputString; // 如果字符串长度小于等于要保留的前后字符数之和,直接返回原字符串
|
||||||
|
}
|
||||||
|
|
||||||
|
const startPart = inputString.slice(0, startLength);
|
||||||
|
const endPart = inputString.slice(-endLength);
|
||||||
|
|
||||||
|
return `${startPart}...${endPart}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义一个函数,用于获取指定参数的值
|
||||||
|
export function getUrlQueryParam(key: string): string | undefined {
|
||||||
|
console.log(window.location);
|
||||||
|
const query: Map<string, string> = new Map();
|
||||||
|
const queryStr = window.location.href.split("?")[1];
|
||||||
|
if (queryStr) {
|
||||||
|
const queryStrArr = queryStr.split("&");
|
||||||
|
queryStrArr.forEach((v) => {
|
||||||
|
const queryArr = v.split("=");
|
||||||
|
query.set(queryArr[0], queryArr[1]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return query.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function copyText(text: string) {
|
||||||
|
const value = text;
|
||||||
|
// 1、创建DOM input框
|
||||||
|
const input = document.createElement("input");
|
||||||
|
// 2、隐藏input
|
||||||
|
input.setAttribute(
|
||||||
|
"style",
|
||||||
|
`
|
||||||
|
opacity: 0;
|
||||||
|
z-index: 999;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
`
|
||||||
|
);
|
||||||
|
|
||||||
|
// 3、将指定文本赋值给input
|
||||||
|
input.value = value;
|
||||||
|
// 4、将input插入文档
|
||||||
|
document.body.appendChild(input);
|
||||||
|
// 5、选中文本
|
||||||
|
// @ts-ignore
|
||||||
|
input.select();
|
||||||
|
// 6、复制到剪切板
|
||||||
|
const isCopySuccess = document.execCommand("copy");
|
||||||
|
|
||||||
|
// 7、复制成功后提示
|
||||||
|
isCopySuccess &&
|
||||||
|
Toast.show({
|
||||||
|
icon: "success",
|
||||||
|
content: "Copy successful",
|
||||||
|
});
|
||||||
|
// 8、 销毁DOM
|
||||||
|
document.body.removeChild(input);
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
/// <reference types="vite/client" />
|
||||||
|
|
||||||
|
interface ImportMetaEnv {
|
||||||
|
readonly VITE_BASE_API_URL: string;
|
||||||
|
// 更多环境变量...
|
||||||
|
readonly MODE: "development" | "production" | "test";
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* @LastEditors: John
|
||||||
|
* @Date: 2024-06-17 17:20:03
|
||||||
|
* @LastEditTime: 2024-06-17 17:41:09
|
||||||
|
* @Author: John
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2020",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||||
|
"module": "ESNext",
|
||||||
|
"skipLibCheck": true,
|
||||||
|
|
||||||
|
/* Bundler mode */
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
|
||||||
|
/* Linting */
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./src/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["src"],
|
||||||
|
"references": [{ "path": "./tsconfig.node.json" }]
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"strict": true
|
||||||
|
},
|
||||||
|
"include": ["vite.config.ts"]
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { defineConfig } from "vite";
|
||||||
|
import react from "@vitejs/plugin-react";
|
||||||
|
import path from "path";
|
||||||
|
import viteCompression from "vite-plugin-compression";
|
||||||
|
import { nodePolyfills } from "vite-plugin-node-polyfills";
|
||||||
|
|
||||||
|
// https://vitejs.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [
|
||||||
|
react(),
|
||||||
|
viteCompression({ deleteOriginFile: false }),
|
||||||
|
nodePolyfills(),
|
||||||
|
],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
"@": path.resolve(__dirname, "./src"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
css: {},
|
||||||
|
});
|
Loading…
Reference in New Issue