🎉 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