mirror of
https://github.com/sunniejs/vue-h5-template.git
synced 2026-04-29 18:34:09 +08:00
docs: update docs
This commit is contained in:
parent
235386120f
commit
5b32fedd9a
31
docs/.vitepress/cache/deps/_metadata.json
vendored
31
docs/.vitepress/cache/deps/_metadata.json
vendored
@ -1,31 +0,0 @@
|
||||
{
|
||||
"hash": "239dbc0d",
|
||||
"configHash": "0cbf0876",
|
||||
"lockfileHash": "05441bb6",
|
||||
"browserHash": "dec8ef8e",
|
||||
"optimized": {
|
||||
"vue": {
|
||||
"src": "../../../../node_modules/vue/dist/vue.runtime.esm-bundler.js",
|
||||
"file": "vue.js",
|
||||
"fileHash": "c8a520fe",
|
||||
"needsInterop": false
|
||||
},
|
||||
"vitepress > @vue/devtools-api": {
|
||||
"src": "../../../../node_modules/@vue/devtools-api/dist/index.js",
|
||||
"file": "vitepress___@vue_devtools-api.js",
|
||||
"fileHash": "b3eaab79",
|
||||
"needsInterop": false
|
||||
},
|
||||
"vitepress > @vueuse/core": {
|
||||
"src": "../../../../node_modules/@vueuse/core/index.mjs",
|
||||
"file": "vitepress___@vueuse_core.js",
|
||||
"fileHash": "e7d34837",
|
||||
"needsInterop": false
|
||||
}
|
||||
},
|
||||
"chunks": {
|
||||
"chunk-RMNUDMGF": {
|
||||
"file": "chunk-RMNUDMGF.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
7
docs/.vitepress/cache/deps_temp_0f62c440/chunk-DKDEFF44.js.map
vendored
Normal file
7
docs/.vitepress/cache/deps_temp_0f62c440/chunk-DKDEFF44.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -1,4 +1,4 @@
|
||||
// node_modules/@vue/devtools-shared/dist/index.js
|
||||
// node_modules/.pnpm/@vue+devtools-shared@7.7.7/node_modules/@vue/devtools-shared/dist/index.js
|
||||
var __create = Object.create;
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
@ -258,7 +258,7 @@ function isUrlString(str) {
|
||||
}
|
||||
var deepClone = (0, import_rfdc.default)({ circles: true });
|
||||
|
||||
// node_modules/perfect-debounce/dist/index.mjs
|
||||
// node_modules/.pnpm/perfect-debounce@1.0.0/node_modules/perfect-debounce/dist/index.mjs
|
||||
var DEBOUNCE_DEFAULTS = {
|
||||
trailing: true
|
||||
};
|
||||
@ -315,7 +315,7 @@ async function _applyPromised(fn, _this, args) {
|
||||
return await fn.apply(_this, args);
|
||||
}
|
||||
|
||||
// node_modules/hookable/dist/index.mjs
|
||||
// node_modules/.pnpm/hookable@5.5.3/node_modules/hookable/dist/index.mjs
|
||||
function flatHooks(configHooks, hooks2 = {}, parentName) {
|
||||
for (const key in configHooks) {
|
||||
const subHook = configHooks[key];
|
||||
@ -520,11 +520,11 @@ function createHooks() {
|
||||
return new Hookable();
|
||||
}
|
||||
|
||||
// node_modules/birpc/dist/index.mjs
|
||||
// node_modules/.pnpm/birpc@2.5.0/node_modules/birpc/dist/index.mjs
|
||||
var { clearTimeout: clearTimeout2, setTimeout: setTimeout2 } = globalThis;
|
||||
var random = Math.random.bind(Math);
|
||||
|
||||
// node_modules/@vue/devtools-kit/dist/index.js
|
||||
// node_modules/.pnpm/@vue+devtools-kit@7.7.7/node_modules/@vue/devtools-kit/dist/index.js
|
||||
var __create2 = Object.create;
|
||||
var __defProp2 = Object.defineProperty;
|
||||
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
||||
File diff suppressed because one or more lines are too long
@ -35,9 +35,9 @@ import {
|
||||
unref,
|
||||
watch,
|
||||
watchEffect
|
||||
} from "./chunk-RMNUDMGF.js";
|
||||
} from "./chunk-DKDEFF44.js";
|
||||
|
||||
// node_modules/@vueuse/shared/index.mjs
|
||||
// node_modules/.pnpm/@vueuse+shared@12.8.2/node_modules/@vueuse/shared/index.mjs
|
||||
function computedEager(fn, options) {
|
||||
var _a;
|
||||
const result = shallowRef();
|
||||
@ -1569,7 +1569,7 @@ function whenever(source, cb, options) {
|
||||
return stop;
|
||||
}
|
||||
|
||||
// node_modules/@vueuse/core/index.mjs
|
||||
// node_modules/.pnpm/@vueuse+core@12.8.2/node_modules/@vueuse/core/index.mjs
|
||||
function computedAsync(evaluationCallback, initialState, optionsOrRef) {
|
||||
let options;
|
||||
if (isRef(optionsOrRef)) {
|
||||
File diff suppressed because one or more lines are too long
@ -2,7 +2,10 @@ import {
|
||||
BaseTransition,
|
||||
BaseTransitionPropsValidators,
|
||||
Comment,
|
||||
DeprecationTypes,
|
||||
EffectScope,
|
||||
ErrorCodes,
|
||||
ErrorTypeStrings,
|
||||
Fragment,
|
||||
KeepAlive,
|
||||
ReactiveEffect,
|
||||
@ -10,8 +13,10 @@ import {
|
||||
Suspense,
|
||||
Teleport,
|
||||
Text,
|
||||
TrackOpTypes,
|
||||
Transition,
|
||||
TransitionGroup,
|
||||
TriggerOpTypes,
|
||||
VueElement,
|
||||
assertNumber,
|
||||
callWithAsyncErrorHandling,
|
||||
@ -51,12 +56,17 @@ import {
|
||||
effectScope,
|
||||
getCurrentInstance,
|
||||
getCurrentScope,
|
||||
getCurrentWatcher,
|
||||
getTransitionRawChildren,
|
||||
guardReactiveProps,
|
||||
h,
|
||||
handleError,
|
||||
hasInjectionContext,
|
||||
hydrate,
|
||||
hydrateOnIdle,
|
||||
hydrateOnInteraction,
|
||||
hydrateOnMediaQuery,
|
||||
hydrateOnVisible,
|
||||
initCustomFormatter,
|
||||
initDirectivesForSSR,
|
||||
inject,
|
||||
@ -89,6 +99,7 @@ import {
|
||||
onServerPrefetch,
|
||||
onUnmounted,
|
||||
onUpdated,
|
||||
onWatcherCleanup,
|
||||
openBlock,
|
||||
popScopeId,
|
||||
provide,
|
||||
@ -129,9 +140,13 @@ import {
|
||||
useAttrs,
|
||||
useCssModule,
|
||||
useCssVars,
|
||||
useHost,
|
||||
useId,
|
||||
useModel,
|
||||
useSSRContext,
|
||||
useShadowRoot,
|
||||
useSlots,
|
||||
useTemplateRef,
|
||||
useTransitionState,
|
||||
vModelCheckbox,
|
||||
vModelDynamic,
|
||||
@ -153,12 +168,15 @@ import {
|
||||
withMemo,
|
||||
withModifiers,
|
||||
withScopeId
|
||||
} from "./chunk-RMNUDMGF.js";
|
||||
} from "./chunk-DKDEFF44.js";
|
||||
export {
|
||||
BaseTransition,
|
||||
BaseTransitionPropsValidators,
|
||||
Comment,
|
||||
DeprecationTypes,
|
||||
EffectScope,
|
||||
ErrorCodes,
|
||||
ErrorTypeStrings,
|
||||
Fragment,
|
||||
KeepAlive,
|
||||
ReactiveEffect,
|
||||
@ -166,8 +184,10 @@ export {
|
||||
Suspense,
|
||||
Teleport,
|
||||
Text,
|
||||
TrackOpTypes,
|
||||
Transition,
|
||||
TransitionGroup,
|
||||
TriggerOpTypes,
|
||||
VueElement,
|
||||
assertNumber,
|
||||
callWithAsyncErrorHandling,
|
||||
@ -207,12 +227,17 @@ export {
|
||||
effectScope,
|
||||
getCurrentInstance,
|
||||
getCurrentScope,
|
||||
getCurrentWatcher,
|
||||
getTransitionRawChildren,
|
||||
guardReactiveProps,
|
||||
h,
|
||||
handleError,
|
||||
hasInjectionContext,
|
||||
hydrate,
|
||||
hydrateOnIdle,
|
||||
hydrateOnInteraction,
|
||||
hydrateOnMediaQuery,
|
||||
hydrateOnVisible,
|
||||
initCustomFormatter,
|
||||
initDirectivesForSSR,
|
||||
inject,
|
||||
@ -245,6 +270,7 @@ export {
|
||||
onServerPrefetch,
|
||||
onUnmounted,
|
||||
onUpdated,
|
||||
onWatcherCleanup,
|
||||
openBlock,
|
||||
popScopeId,
|
||||
provide,
|
||||
@ -285,9 +311,13 @@ export {
|
||||
useAttrs,
|
||||
useCssModule,
|
||||
useCssVars,
|
||||
useHost,
|
||||
useId,
|
||||
useModel,
|
||||
useSSRContext,
|
||||
useShadowRoot,
|
||||
useSlots,
|
||||
useTemplateRef,
|
||||
useTransitionState,
|
||||
vModelCheckbox,
|
||||
vModelDynamic,
|
||||
@ -3,8 +3,8 @@ export default {
|
||||
collapsible: true,
|
||||
items: [
|
||||
{ text: "启动项目", link: "/guide/vue3/start" },
|
||||
{ text: "vite.config.ts 基础配置", link: "/guide/vue3/base" },
|
||||
{ text: "vite插件集成", link: "/guide/vue3/vite" },
|
||||
{ text: "vite.config.mts 基础配置", link: "/guide/vue3/base" },
|
||||
{ text: "Vite 插件集成", link: "/guide/vue3/vite" },
|
||||
{ text: "多环境变量", link: "/guide/vue3/env" },
|
||||
{ text: "viewport 适配方案", link: "/guide/vue3/viewport" },
|
||||
{ text: "UI组件库", link: "/guide/vue3/ui" },
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
欢迎有意愿参与到开源的朋友,加入到本文档的编写,书写文档不仅是教会别人知识,更是用自己的表达方式概括自己所学习知识的一种方式,这对个人来说是不可多得的成长机会。
|
||||
|
||||
```bash
|
||||
|
||||
# 拉取项目
|
||||
git clone https://github.com/sunniejs/vue-h5-template
|
||||
|
||||
@ -11,9 +10,11 @@ git clone https://github.com/sunniejs/vue-h5-template
|
||||
git checkout -b docs origin/docs
|
||||
|
||||
# 安装依赖
|
||||
yarn install
|
||||
pnpm install
|
||||
|
||||
# 启动项目
|
||||
yarn start
|
||||
pnpm start
|
||||
|
||||
# 打包
|
||||
pnpm build
|
||||
```
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
# 快速上手
|
||||
|
||||
## node 版本要求
|
||||
## Node 版本要求
|
||||
|
||||
推荐 20.19.0+以上的版本,毕竟 2025 年了,别掐着 16+的版本了,你也可以使用[nvm](https://github.com/nvm-sh/nvm)或[nvm-windows](https://github.com/coreybutler/nvm-windows)在同一台电脑上管理多个 node 版本。
|
||||
推荐 Node.js 20.10.0+ 以上的版本,你可以使用 [nvm](https://github.com/nvm-sh/nvm) 或 [nvm-windows](https://github.com/coreybutler/nvm-windows) 在同一台电脑上管理多个 Node 版本。
|
||||
|
||||
## 包管理器
|
||||
|
||||
尽量使用 yarn 或者 pnpm,本项目仅保证在 yarn 或 pnpm 下正确运行,npm 涉及到网络环境等各种情况的限制不做过多考虑。
|
||||
推荐使用 pnpm(>= 9.12.0),本项目仅保证在 pnpm 下正确运行。
|
||||
|
||||
## 启动项目
|
||||
|
||||
|
||||
@ -1,67 +1,123 @@
|
||||
# axios 封装及接口管理
|
||||
|
||||
`utils/request.js` 封装 axios , 开发者需要根据后台接口做修改。
|
||||
`utils/request/index.ts` 封装 axios,开发者需要根据后台接口做修改。
|
||||
|
||||
- `service.interceptors.request.use` 里可以设置请求头,比如设置 `token`
|
||||
- `config.hideloading` 是在 api 文件夹下的接口参数里设置,下文会讲
|
||||
- `service.interceptors.response.use` 里可以对接口返回数据处理,比如 401 删除本地信息,重新登录
|
||||
|
||||
```javascript
|
||||
## Axios 封装
|
||||
|
||||
```typescript
|
||||
import axios from "axios";
|
||||
import store from "@/store";
|
||||
import { Toast } from "vant";
|
||||
// 根据环境不同引入不同api地址
|
||||
import { baseApi } from "@/config";
|
||||
// create an axios instance
|
||||
const service = axios.create({
|
||||
baseURL: baseApi, // url = base api url + request url
|
||||
withCredentials: true, // send cookies when cross-domain requests
|
||||
timeout: 5000, // request timeout
|
||||
import type {
|
||||
AxiosError,
|
||||
AxiosInstance,
|
||||
AxiosRequestConfig,
|
||||
AxiosResponse,
|
||||
InternalAxiosRequestConfig,
|
||||
} from "axios";
|
||||
import { showToast } from "vant";
|
||||
|
||||
const service: AxiosInstance = axios.create({
|
||||
baseURL: import.meta.env.VITE_API_BASE_URL || "",
|
||||
withCredentials: false,
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
// request 拦截器 request interceptor
|
||||
service.interceptors.request.use(
|
||||
(config) => {
|
||||
// 不传递默认开启loading
|
||||
if (!config.hideloading) {
|
||||
// loading
|
||||
Toast.loading({
|
||||
forbidClick: true,
|
||||
});
|
||||
}
|
||||
if (store.getters.token) {
|
||||
config.headers["X-Token"] = "";
|
||||
}
|
||||
(config: InternalAxiosRequestConfig) => {
|
||||
return config;
|
||||
},
|
||||
(error) => {
|
||||
// do something with request error
|
||||
console.log(error); // for debug
|
||||
(error: AxiosError) => {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
},
|
||||
);
|
||||
// respone拦截器
|
||||
|
||||
service.interceptors.response.use(
|
||||
(response) => {
|
||||
Toast.clear();
|
||||
(response: AxiosResponse) => {
|
||||
const res = response.data;
|
||||
if (res.status && res.status !== 200) {
|
||||
// 登录超时,重新登录
|
||||
if (res.status === 401) {
|
||||
store.dispatch("FedLogOut").then(() => {
|
||||
location.reload();
|
||||
});
|
||||
}
|
||||
return Promise.reject(res || "error");
|
||||
if (res.code !== 200) {
|
||||
showToast(res.msg);
|
||||
return Promise.reject(res.msg || "Error");
|
||||
} else {
|
||||
return Promise.resolve(res);
|
||||
return res.data;
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
Toast.clear();
|
||||
console.log("err" + error); // for debug
|
||||
return Promise.reject(error);
|
||||
}
|
||||
(error: AxiosError) => {
|
||||
showToast(error.message);
|
||||
return Promise.reject(error.message);
|
||||
},
|
||||
);
|
||||
|
||||
export const http = {
|
||||
get<T = any>(url: string, config?: AxiosRequestConfig): Promise<T> {
|
||||
return service.get(url, config);
|
||||
},
|
||||
post<T = any>(
|
||||
url: string,
|
||||
data?: object,
|
||||
config?: AxiosRequestConfig,
|
||||
): Promise<T> {
|
||||
return service.post(url, data, config);
|
||||
},
|
||||
put<T = any>(
|
||||
url: string,
|
||||
data?: object,
|
||||
config?: AxiosRequestConfig,
|
||||
): Promise<T> {
|
||||
return service.put(url, data, config);
|
||||
},
|
||||
delete<T = any>(url: string, config?: AxiosRequestConfig): Promise<T> {
|
||||
return service.delete(url, config);
|
||||
},
|
||||
};
|
||||
|
||||
export default service;
|
||||
```
|
||||
|
||||
## useFetchApi 封装
|
||||
|
||||
项目同时提供了基于 `@vueuse/core` 的 `createFetch` 封装,支持响应式的请求:
|
||||
|
||||
```typescript
|
||||
import { createFetch } from "@vueuse/core";
|
||||
import { useCookies } from "@vueuse/integrations/useCookies";
|
||||
import { showNotify } from "vant";
|
||||
|
||||
const useFetchApi = createFetch({
|
||||
baseUrl: "",
|
||||
options: {
|
||||
async beforeFetch({ options }) {
|
||||
const myToken =
|
||||
useCookies().get(
|
||||
(import.meta.env.VITE_TOKEN_KEY as string) || "Authorization",
|
||||
) || "";
|
||||
options.headers = {
|
||||
...options.headers,
|
||||
Authorization: `Bearer ${myToken}`,
|
||||
};
|
||||
return { options };
|
||||
},
|
||||
afterFetch(ctx) {
|
||||
// 处理响应数据...
|
||||
return ctx;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default useFetchApi;
|
||||
```
|
||||
|
||||
## 接口管理
|
||||
|
||||
在 `src/api/index.ts` 中统一管理接口:
|
||||
|
||||
```typescript
|
||||
import { http } from "@/utils/request";
|
||||
|
||||
export function loginPassword() {
|
||||
return http.post("/mock-api/login", {
|
||||
data: { name: "123" },
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
# vite.config.ts 基础配置
|
||||
# vite.config.mts 基础配置
|
||||
|
||||
如果你的 `Vue Router` 模式是 hash
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
base: './',
|
||||
```
|
||||
|
||||
如果你的 `Vue Router` 模式是 history 这里的 publicPath 和你的 `Vue Router` `base` **保持一致**
|
||||
如果你的 `Vue Router` 模式是 history 这里的 base 和你的 `Vue Router` `base` **保持一致**
|
||||
|
||||
```javascript
|
||||
base: '/app/',
|
||||
@ -46,10 +46,9 @@ export default function ({ command, mode }: ConfigEnv): UserConfig {
|
||||
css: {
|
||||
preprocessorOptions: {
|
||||
scss: {
|
||||
quietDeps: true,
|
||||
silenceDeprecations: ["legacy-js-api"],
|
||||
// 配置 nutui 全局 scss 变量
|
||||
additionalData: `@use "@nutui/nutui/dist/styles/variables.scss" as *; @use '@/styles/vant.scss' as *;`,
|
||||
additionalData: `@use "@/styles/variable.scss" as *;@use "@nutui/nutui/dist/styles/variables.scss" as *;`,
|
||||
quietDeps: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@ -1,13 +1,48 @@
|
||||
`package.json` 里的 `scripts` 配置 `dev` `dev:test` `dev:prod` ,通过 `--mode xxx` 来执行不同环境
|
||||
# 多环境变量
|
||||
|
||||
- 通过 `yarn dev` 启动本地环境参数 , 执行 `development`
|
||||
- 通过 `yarn dev:test` 启动测试环境参数 , 执行 `test`
|
||||
- 通过 `yarn dev:prod` 启动正式环境参数 , 执行 `prod`
|
||||
`package.json` 里的 `scripts` 配置 `dev` `build`,通过 `--mode xxx` 来执行不同环境
|
||||
|
||||
```javascript
|
||||
- 通过 `pnpm dev` 启动本地环境参数,执行 `development`
|
||||
- 通过 `pnpm build` 打包正式环境参数,执行 `production`
|
||||
|
||||
```json
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"dev:test": "vite --mode test",
|
||||
"dev:prod": "vite --mode production",
|
||||
"build": "vite build"
|
||||
}
|
||||
```
|
||||
|
||||
## 环境变量配置
|
||||
|
||||
项目中有三个环境变量文件:
|
||||
|
||||
- `.env.development` - 开发环境
|
||||
- `.env.test` - 测试环境
|
||||
- `.env.production` - 生产环境
|
||||
|
||||
可配置的变量:
|
||||
|
||||
```bash
|
||||
# 是否开启数据mock
|
||||
VITE_USE_MOCK=true
|
||||
|
||||
# Token Key
|
||||
VITE_TOKEN_KEY=Authorization
|
||||
|
||||
# 是否开启调试工具
|
||||
VITE_USE_ERUDA=true
|
||||
|
||||
# 是否开启压缩
|
||||
VITE_USE_COMPRESS=false
|
||||
|
||||
# 是否开启打包后生成报告
|
||||
VITE_USE_REPORT=false
|
||||
|
||||
# 是否开启https
|
||||
VITE_USE_HTTPS=false
|
||||
|
||||
# 是否开启PWA
|
||||
VITE_USE_PWA=false
|
||||
```
|
||||
|
||||
在代码中通过 `import.meta.env.VITE_XXX` 访问环境变量。
|
||||
|
||||
@ -1,51 +1,96 @@
|
||||
<!--
|
||||
* @Author: Vinton
|
||||
* @Date: 2022-08-22 11:08:11
|
||||
* @Description: file content
|
||||
-->
|
||||
# i18n 文本多语言解决方案
|
||||
|
||||
```javascript
|
||||
import { createI18n } from 'vue-i18n'; // 引入第三方最新的i18n注意版本
|
||||
项目采用 `vue-i18n` 实现多语言,并支持按需懒加载语言包,提升首屏加载速度。
|
||||
|
||||
/**
|
||||
* @description: 加载当前配置的语言配置目录,随意添加
|
||||
*/
|
||||
export function loadLang() {
|
||||
const modules: Record<string, any> = import.meta.glob('./lang/*.ts', { eager: true });
|
||||
const langs: Record<string, any> = {};
|
||||
## 目录结构
|
||||
|
||||
for (const path in modules) {
|
||||
const name = path.replace(/(\.\/lang\/|\.ts)/g, '');
|
||||
langs[name] = modules[path].lang;
|
||||
}
|
||||
return langs;
|
||||
}
|
||||
```bash
|
||||
├── locales
|
||||
│ ├── index.ts # i18n 配置和初始化
|
||||
│ ├── lang-base.ts # 基础语言配置
|
||||
│ └── langs
|
||||
│ ├── en-US
|
||||
│ │ └── common.json
|
||||
│ └── zh-CN
|
||||
│ └── common.json
|
||||
```
|
||||
|
||||
## 核心实现
|
||||
|
||||
```typescript
|
||||
import { createI18n } from "vue-i18n";
|
||||
import type { App } from "vue";
|
||||
|
||||
const LOCALE_KEY = "lang";
|
||||
const DEFAULT_LOCALE = "zh-CN";
|
||||
|
||||
// 扫描所有语言文件(懒加载)
|
||||
const modules = import.meta.glob("./langs/**/*.json");
|
||||
|
||||
// i18n 实例(初始不加载 messages,按需加载)
|
||||
export const i18n = createI18n({
|
||||
// globalInjection: true,
|
||||
// legacy: false,
|
||||
locale: 'zh-cn', // 默认语言,当前这里的数据要跟配置的lang目录下面的文件名字前缀一致
|
||||
fallbackLocale: 'zh-cn',
|
||||
messages: loadLang(), // 记载当前引入的语言目录的处理过后的数据
|
||||
legacy: false,
|
||||
globalInjection: true,
|
||||
locale: "",
|
||||
fallbackLocale: DEFAULT_LOCALE,
|
||||
messages: {},
|
||||
});
|
||||
|
||||
/**
|
||||
* @description: 切换当前多语言
|
||||
*/
|
||||
export function setLang(locale?: string) {
|
||||
if (locale) {
|
||||
localStorage.setItem('lang', locale);
|
||||
// 设置语言
|
||||
export async function setLang(locale?: string) {
|
||||
const target = locale || localStorage.getItem(LOCALE_KEY) || DEFAULT_LOCALE;
|
||||
|
||||
if (!i18n.global.availableLocales.includes(target)) {
|
||||
// 按需加载语言包
|
||||
const loader = localeLoaders[target];
|
||||
if (loader) {
|
||||
const messages = await loader();
|
||||
i18n.global.setLocaleMessage(target, messages);
|
||||
}
|
||||
}
|
||||
i18n.global.locale = locale || localStorage.getItem('lang') || '';
|
||||
|
||||
i18n.global.locale.value = target;
|
||||
localStorage.setItem(LOCALE_KEY, target);
|
||||
document.documentElement.lang = target;
|
||||
}
|
||||
|
||||
// 初始化(main.ts 调用)
|
||||
export async function setupI18n(app: App) {
|
||||
app.use(i18n);
|
||||
await setLang();
|
||||
}
|
||||
```
|
||||
|
||||
# css图片解决方案
|
||||
## 在组件中切换语言
|
||||
|
||||
目前在业务开发中,CSS 的样式多语言也会经常用到,可能一些图片的字体比较复杂,代码很难实现。或者我们为了减少多语言的配置,加快开发效率也会使用多语言的配置,目前这里提供 scss 的图片多语言的方案
|
||||
```html
|
||||
<script setup lang="ts">
|
||||
import { setLang } from "@/locales";
|
||||
|
||||
```css
|
||||
const changeLang = (type: string) => {
|
||||
setLang(type);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button @click="changeLang('zh-CN')">中文</button>
|
||||
<button @click="changeLang('en-US')">English</button>
|
||||
</template>
|
||||
```
|
||||
|
||||
## 在模板中使用
|
||||
|
||||
```html
|
||||
<template>
|
||||
<div>{{ $t('common.title') }}</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
# CSS 图片多语言方案
|
||||
|
||||
目前在业务开发中,CSS 的样式多语言也会经常用到。这里提供 scss 的图片多语言方案:
|
||||
|
||||
```scss
|
||||
@mixin main-lang-bg($width, $height, $preUrl, $posUrl) {
|
||||
width: $width;
|
||||
height: $height;
|
||||
@ -53,37 +98,29 @@ export function setLang(locale?: string) {
|
||||
background-size: 100% 100%;
|
||||
@include loop-lang-bg($preUrl, $posUrl);
|
||||
}
|
||||
// 背景图多语言
|
||||
|
||||
@mixin loop-lang-bg($preUrl, $posUrl) {
|
||||
$list: zh-cn, en-us; // 配置需要的多语言,根据项目来
|
||||
$list: zh-CN, en-US;
|
||||
@each $i in $list {
|
||||
&.#{$i} {
|
||||
background-image: url('#{$preUrl}/#{$i}/#{$posUrl}');
|
||||
background-image: url("#{$preUrl}/#{$i}/#{$posUrl}");
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
## 定义图片的目录格式
|
||||
```bash
|
||||
├── button
|
||||
│ ├── en-us
|
||||
│ │ └── confirm.png
|
||||
│ ├── zh-cn
|
||||
│ │ └── confirm.png
|
||||
```
|
||||
|
||||
## HTML的使用方式
|
||||
## 使用方式
|
||||
|
||||
```html
|
||||
<template>
|
||||
<div :class="['btn-confirm', i18n.global.locale]"></div>
|
||||
<div :class="['btn-confirm', i18n.global.locale.value]"></div>
|
||||
</template>
|
||||
<script lang="ts" setup name="HomePage">
|
||||
import { i18n } from '/@/i18n';
|
||||
<script lang="ts" setup>
|
||||
import { i18n } from "@/locales";
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.btn-confirm {
|
||||
@include main-lang-bg(302px, 82px, '/@/assets/button', 'confirm.png');
|
||||
}
|
||||
.btn-confirm {
|
||||
@include main-lang-bg(302px, 82px, "@/assets/button", "confirm.png");
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
@ -1,3 +1,18 @@
|
||||
# Eslint + Pettier + Stylelint 统一开发规范
|
||||
# ESLint + Prettier + Stylelint 统一开发规范
|
||||
|
||||
根目录下的 `.eslintrc.js`、`.stylelint.config.js`、`.prettier.config.js` 内置了 lint 规则,帮助你规范地开发代码,有助于提高团队的代码质量和协作性,可以根据团队的规则进行修改
|
||||
根目录下的 `eslint.config.mjs`、`stylelint.config.js`、`prettier.config.js` 内置了 lint 规则,帮助你规范地开发代码,有助于提高团队的代码质量和协作性,可以根据团队的规则进行修改。
|
||||
|
||||
项目使用 ESLint flat config(`eslint.config.mjs`),配合 `husky` + `lint-staged` 在 git commit 时自动执行代码检查。
|
||||
|
||||
## 常用命令
|
||||
|
||||
```bash
|
||||
# ESLint 检查并修复
|
||||
pnpm lint:eslint
|
||||
|
||||
# Prettier 格式化
|
||||
pnpm lint:prettier
|
||||
|
||||
# Stylelint 检查并修复
|
||||
pnpm lint:stylelint
|
||||
```
|
||||
|
||||
@ -1,34 +1,30 @@
|
||||
<!--
|
||||
* @Author: Vinton
|
||||
* @Date: 2022-08-22 10:39:13
|
||||
* @Description: file content
|
||||
-->
|
||||
# Pinia 状态管理
|
||||
|
||||
下一代 vuex,使用极其方便,ts 兼容好
|
||||
下一代 vuex,使用极其方便,ts 兼容好。项目使用 `pinia-plugin-persistedstate` 实现状态持久化。
|
||||
|
||||
目录结构
|
||||
|
||||
```bash
|
||||
├── store
|
||||
│ ├── modules
|
||||
│ │ └── user.js
|
||||
│ ├── index.js
|
||||
│ │ └── user.ts
|
||||
│ ├── index.ts
|
||||
```
|
||||
目前pinia分为两种编程模式,Options API 和 Composition API,我们这边都会列举出来实现的业务逻辑效果是一样的,提供大家思路
|
||||
|
||||
### Options API:
|
||||
### Options API(当前项目使用):
|
||||
|
||||
```typescript
|
||||
import { loginPassword } from "@/api";
|
||||
import { defineStore } from "pinia";
|
||||
|
||||
```javascript
|
||||
interface StoreUser {
|
||||
token: string;
|
||||
info: Record<any, any>;
|
||||
}
|
||||
|
||||
export const useUserStore = defineStore({
|
||||
id: 'app-user',
|
||||
export const useUserStore = defineStore("user", {
|
||||
state: (): StoreUser => ({
|
||||
token: token,
|
||||
token: "",
|
||||
info: {},
|
||||
}),
|
||||
getters: {
|
||||
@ -38,51 +34,63 @@ export const useUserStore = defineStore({
|
||||
},
|
||||
actions: {
|
||||
setInfo(info: any) {
|
||||
this.info = info ? info : '';
|
||||
this.info = info ?? "";
|
||||
},
|
||||
login() {
|
||||
return new Promise((resolve) => {
|
||||
const { data } = loginPassword();
|
||||
watch(data, () => {
|
||||
this.setInfo(data.value);
|
||||
// useCookies().set(VITE_TOKEN_KEY as string, data.value.token);
|
||||
resolve(data.value);
|
||||
});
|
||||
});
|
||||
async login() {
|
||||
try {
|
||||
const res = await loginPassword();
|
||||
this.setInfo(res);
|
||||
this.token = res.token;
|
||||
return res;
|
||||
} catch (error) {
|
||||
console.error("Login failed", error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
},
|
||||
persist: {
|
||||
pick: ["token"],
|
||||
storage: localStorage,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Composition API:
|
||||
```javascript
|
||||
export const useUserStore = defineStore('app-user', () => {
|
||||
const Token = ref(token);
|
||||
const info = ref<Record<any, any>>({});
|
||||
const setInfo = (info: any) => {
|
||||
info.value = info ? info : '';
|
||||
};
|
||||
const getUserInfo = () => {
|
||||
return info || {};
|
||||
};
|
||||
const login = () => {
|
||||
return new Promise((resolve) => {
|
||||
const { data } = loginPassword();
|
||||
watch(data, () => {
|
||||
setInfo(data.value);
|
||||
// useCookies().set(VITE_TOKEN_KEY as string, data.value.token);
|
||||
resolve(data.value);
|
||||
});
|
||||
});
|
||||
};
|
||||
return {
|
||||
Token,
|
||||
info,
|
||||
setInfo,
|
||||
login,
|
||||
getUserInfo,
|
||||
};
|
||||
})
|
||||
|
||||
```typescript
|
||||
export const useUserStore = defineStore(
|
||||
"user",
|
||||
() => {
|
||||
const token = ref("");
|
||||
const info = ref<Record<any, any>>({});
|
||||
|
||||
const getUserInfo = () => info.value || {};
|
||||
|
||||
const setInfo = (data: any) => {
|
||||
info.value = data ?? "";
|
||||
};
|
||||
|
||||
const login = async () => {
|
||||
try {
|
||||
const res = await loginPassword();
|
||||
setInfo(res);
|
||||
token.value = res.token;
|
||||
return res;
|
||||
} catch (error) {
|
||||
console.error("Login failed", error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
return { token, info, getUserInfo, setInfo, login };
|
||||
},
|
||||
{
|
||||
persist: {
|
||||
pick: ["token"],
|
||||
storage: localStorage,
|
||||
},
|
||||
},
|
||||
);
|
||||
```
|
||||
|
||||
使用
|
||||
|
||||
@ -2,27 +2,57 @@
|
||||
|
||||
本案例采用 `hash` 模式,开发者根据需求修改 `mode` `base`
|
||||
|
||||
**注意**:如果你使用了 `history` 模式, `vue.config.js` 中的 `publicPath` 要做对应的**修改**
|
||||
**注意**:如果你使用了 `history` 模式, `vite.config.mts` 中的 `base` 要做对应的**修改**
|
||||
|
||||
前往: [vite.config.js 基础配置](#base)
|
||||
前往: [vite.config.mts 基础配置](/guide/vue3/base)
|
||||
|
||||
```javascript
|
||||
import Vue from "vue";
|
||||
import { createRouter, createWebHistory, Router } from "vue-router";
|
||||
```typescript
|
||||
import type { RouteRecordRaw } from "vue-router";
|
||||
|
||||
Vue.use(Router);
|
||||
export const router = [
|
||||
export const routes: RouteRecordRaw[] = [
|
||||
{
|
||||
name: "root",
|
||||
path: "/",
|
||||
redirect: "/home",
|
||||
component: () => import("@/layout/basic/index.vue"),
|
||||
component: () => import("@/layout/index.vue"),
|
||||
children: [
|
||||
{
|
||||
path: "home",
|
||||
component: () => import("@/views/home/index.vue"),
|
||||
meta: {
|
||||
title: "common.tabbar.home",
|
||||
keepAlive: true,
|
||||
},
|
||||
},
|
||||
// ... 其他子路由
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "login",
|
||||
path: "/login",
|
||||
component: () => import("@/views/login/index.vue"),
|
||||
meta: {
|
||||
title: "",
|
||||
keepAlive: true,
|
||||
},
|
||||
},
|
||||
// 匹配不到重定向到首页
|
||||
{
|
||||
path: "/:pathMatch(.*)",
|
||||
redirect: "/home",
|
||||
},
|
||||
];
|
||||
```
|
||||
|
||||
const router: Router = createRouter({
|
||||
history: createWebHistory(),
|
||||
routes: routes,
|
||||
路由实例创建:
|
||||
|
||||
```typescript
|
||||
import { createRouter, createWebHashHistory } from "vue-router";
|
||||
import routes from "./routes";
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
routes,
|
||||
scrollBehavior: () => ({ left: 0, top: 0 }),
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
||||
@ -1,16 +1,26 @@
|
||||
# 启动项目
|
||||
|
||||
```bash
|
||||
## 环境要求
|
||||
|
||||
- Node.js >= 20.10.0
|
||||
- pnpm >= 9.12.0
|
||||
|
||||
```bash
|
||||
# 拉取项目
|
||||
git clone https://github.com/sunniejs/vue-h5-template
|
||||
|
||||
# 进入项目目录
|
||||
cd vue-h5-template
|
||||
|
||||
# 安装依赖
|
||||
- pnpm install
|
||||
- yarn install
|
||||
pnpm install
|
||||
|
||||
# 启动项目
|
||||
- pnpm dev
|
||||
- yarn dev
|
||||
pnpm dev
|
||||
|
||||
# 打包
|
||||
pnpm build
|
||||
|
||||
# 预览打包结果
|
||||
pnpm preview
|
||||
```
|
||||
|
||||
@ -1,37 +1,32 @@
|
||||
# 多 UI 组件库供选择
|
||||
|
||||
Vite 构建工具,`使用 vite-plugin-style-import` 和 `unplugin-vue-components/vite` 实现按需引入。
|
||||
|
||||
## 安装插件
|
||||
|
||||
```bash
|
||||
pnpm add unplugin-vue-components/vite -D
|
||||
```
|
||||
使用 `unplugin-vue-components` 和 `unplugin-auto-import` 实现按需引入,无需手动注册组件。
|
||||
|
||||
## 使用组件库
|
||||
|
||||
vant 、 varlet 和 nutUI 可以使用组件按需加载
|
||||
Vant、Varlet 和 NutUI 三个组件库均已配置按需加载。
|
||||
|
||||
在`config/vite/plugins/component.ts`下
|
||||
在 `build/vite/plugins/component.ts` 下配置:
|
||||
|
||||
```javascript
|
||||
```typescript
|
||||
import { VantResolver, VarletUIResolver } from 'unplugin-vue-components/resolvers';
|
||||
import NutUIResolver from '@nutui/auto-import-resolver';
|
||||
...
|
||||
|
||||
// ...
|
||||
resolvers: [VantResolver(), VarletUIResolver(), NutUIResolver()],
|
||||
...
|
||||
// ...
|
||||
```
|
||||
|
||||
## 不需要某个组件库
|
||||
|
||||
只需删除对应的 resolvers 即可
|
||||
只需删除对应的 resolvers 即可。
|
||||
|
||||
删除后需全局搜索删除不需要的组件,避免报错
|
||||
删除后需全局搜索删除不需要的组件,避免报错。
|
||||
|
||||
## 参考文档
|
||||
|
||||
- [nutUI](https://nutui.jd.com/#/zh-CN/component/button)
|
||||
- [NutUI](https://nutui.jd.com/#/zh-CN/component/button)
|
||||
|
||||
- [vant](https://vant-contrib.gitee.io/vant/#/zh-CN)
|
||||
- [Vant](https://vant-ui.github.io/vant/#/zh-CN)
|
||||
|
||||
- [varlet](https://varlet-varletjs.vercel.app/#/zh-CN/button)
|
||||
- [Varlet](https://varlet.pages.dev/#/zh-CN/button)
|
||||
|
||||
@ -1,17 +1,20 @@
|
||||
# vite
|
||||
# Vite 插件集成
|
||||
|
||||
基于原生 ES 模块提供了丰富的内建功能,如速度快到惊人的模块热更新(HMR),使用 Rollup 打包你的代码,并且它是预配置的,可输出用于生产环境的高度优化过的静态资源。更多关于[vite](https://cn.vitejs.dev/guide/)
|
||||
基于原生 ES 模块提供了丰富的内建功能,如速度快到惊人的模块热更新(HMR),使用 Rollup 打包你的代码,并且它是预配置的,可输出用于生产环境的高度优化过的静态资源。更多关于 [Vite](https://cn.vitejs.dev/guide/)
|
||||
|
||||
模版集成了如下的 vite 插件
|
||||
模版集成了如下的 vite 插件(配置目录:`build/vite/plugins/`)
|
||||
|
||||
- unplugin-auto-import(按需加载,自动引入)
|
||||
- unplugin-vue-components(按需加载,自动引入组件)
|
||||
- vite-plugin-compression(开启.gz 压缩)
|
||||
- vite-plugin-eruda(控制台,方便移动端调试)
|
||||
- vite-plugin-compression(开启 .gz 压缩)
|
||||
- @zhaojjiang/vite-plugin-eruda(控制台,方便移动端调试)
|
||||
- vite-plugin-imagemin(图片压缩)
|
||||
- vite-plugin-mock(引入 mockjs,本地模拟接口)
|
||||
- vite-plugin-pages(动态生成路由)
|
||||
- vite-plugin-progress(构建显示进度条)
|
||||
- vite-plugin-restart(监听配置文件修改自动重启 Vite)
|
||||
- vite-plugin-style-import(按需引入样式文件)
|
||||
- vite-plugin-svg-icons(加载 SVG 文件,自动引入)
|
||||
- vite-plugin-pwa(PWA 支持)
|
||||
- vite-plugin-qrcode(开发时生成二维码,方便移动端调试)
|
||||
- @vitejs/plugin-basic-ssl(本地 HTTPS 开发支持)
|
||||
- rollup-plugin-visualizer(打包分析报告)
|
||||
|
||||
92
types/auto-imports.d.ts
vendored
Normal file
92
types/auto-imports.d.ts
vendored
Normal file
@ -0,0 +1,92 @@
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
// @ts-nocheck
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
// Generated by unplugin-auto-import
|
||||
// biome-ignore lint: disable
|
||||
export {}
|
||||
declare global {
|
||||
const EffectScope: typeof import('vue').EffectScope
|
||||
const Snackbar: typeof import('@varlet/ui').Snackbar
|
||||
const acceptHMRUpdate: typeof import('pinia').acceptHMRUpdate
|
||||
const computed: typeof import('vue').computed
|
||||
const createApp: typeof import('vue').createApp
|
||||
const createPinia: typeof import('pinia').createPinia
|
||||
const customRef: typeof import('vue').customRef
|
||||
const defineAsyncComponent: typeof import('vue').defineAsyncComponent
|
||||
const defineComponent: typeof import('vue').defineComponent
|
||||
const defineStore: typeof import('pinia').defineStore
|
||||
const effectScope: typeof import('vue').effectScope
|
||||
const getActivePinia: typeof import('pinia').getActivePinia
|
||||
const getCurrentInstance: typeof import('vue').getCurrentInstance
|
||||
const getCurrentScope: typeof import('vue').getCurrentScope
|
||||
const getCurrentWatcher: typeof import('vue').getCurrentWatcher
|
||||
const h: typeof import('vue').h
|
||||
const inject: typeof import('vue').inject
|
||||
const isProxy: typeof import('vue').isProxy
|
||||
const isReactive: typeof import('vue').isReactive
|
||||
const isReadonly: typeof import('vue').isReadonly
|
||||
const isRef: typeof import('vue').isRef
|
||||
const isShallow: typeof import('vue').isShallow
|
||||
const mapActions: typeof import('pinia').mapActions
|
||||
const mapGetters: typeof import('pinia').mapGetters
|
||||
const mapState: typeof import('pinia').mapState
|
||||
const mapStores: typeof import('pinia').mapStores
|
||||
const mapWritableState: typeof import('pinia').mapWritableState
|
||||
const markRaw: typeof import('vue').markRaw
|
||||
const nextTick: typeof import('vue').nextTick
|
||||
const onActivated: typeof import('vue').onActivated
|
||||
const onBeforeMount: typeof import('vue').onBeforeMount
|
||||
const onBeforeRouteLeave: typeof import('vue-router').onBeforeRouteLeave
|
||||
const onBeforeRouteUpdate: typeof import('vue-router').onBeforeRouteUpdate
|
||||
const onBeforeUnmount: typeof import('vue').onBeforeUnmount
|
||||
const onBeforeUpdate: typeof import('vue').onBeforeUpdate
|
||||
const onDeactivated: typeof import('vue').onDeactivated
|
||||
const onErrorCaptured: typeof import('vue').onErrorCaptured
|
||||
const onMounted: typeof import('vue').onMounted
|
||||
const onRenderTracked: typeof import('vue').onRenderTracked
|
||||
const onRenderTriggered: typeof import('vue').onRenderTriggered
|
||||
const onScopeDispose: typeof import('vue').onScopeDispose
|
||||
const onServerPrefetch: typeof import('vue').onServerPrefetch
|
||||
const onUnmounted: typeof import('vue').onUnmounted
|
||||
const onUpdated: typeof import('vue').onUpdated
|
||||
const onWatcherCleanup: typeof import('vue').onWatcherCleanup
|
||||
const provide: typeof import('vue').provide
|
||||
const reactive: typeof import('vue').reactive
|
||||
const readonly: typeof import('vue').readonly
|
||||
const ref: typeof import('vue').ref
|
||||
const resolveComponent: typeof import('vue').resolveComponent
|
||||
const setActivePinia: typeof import('pinia').setActivePinia
|
||||
const setMapStoreSuffix: typeof import('pinia').setMapStoreSuffix
|
||||
const shallowReactive: typeof import('vue').shallowReactive
|
||||
const shallowReadonly: typeof import('vue').shallowReadonly
|
||||
const shallowRef: typeof import('vue').shallowRef
|
||||
const showToast: typeof import('vant/es').showToast
|
||||
const storeToRefs: typeof import('pinia').storeToRefs
|
||||
const toRaw: typeof import('vue').toRaw
|
||||
const toRef: typeof import('vue').toRef
|
||||
const toRefs: typeof import('vue').toRefs
|
||||
const toValue: typeof import('vue').toValue
|
||||
const triggerRef: typeof import('vue').triggerRef
|
||||
const unref: typeof import('vue').unref
|
||||
const useAttrs: typeof import('vue').useAttrs
|
||||
const useCssModule: typeof import('vue').useCssModule
|
||||
const useCssVars: typeof import('vue').useCssVars
|
||||
const useId: typeof import('vue').useId
|
||||
const useLink: typeof import('vue-router').useLink
|
||||
const useModel: typeof import('vue').useModel
|
||||
const useRoute: typeof import('vue-router').useRoute
|
||||
const useRouter: typeof import('vue-router').useRouter
|
||||
const useSlots: typeof import('vue').useSlots
|
||||
const useTemplateRef: typeof import('vue').useTemplateRef
|
||||
const watch: typeof import('vue').watch
|
||||
const watchEffect: typeof import('vue').watchEffect
|
||||
const watchPostEffect: typeof import('vue').watchPostEffect
|
||||
const watchSyncEffect: typeof import('vue').watchSyncEffect
|
||||
}
|
||||
// for type re-export
|
||||
declare global {
|
||||
// @ts-ignore
|
||||
export type { Component, Slot, Slots, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, ShallowRef, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
|
||||
import('vue')
|
||||
}
|
||||
60
types/components.d.ts
vendored
Normal file
60
types/components.d.ts
vendored
Normal file
@ -0,0 +1,60 @@
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
// biome-ignore lint: disable
|
||||
// oxlint-disable
|
||||
// ------
|
||||
// Generated by unplugin-vue-components
|
||||
// Read more: https://github.com/vuejs/core/pull/3399
|
||||
import { GlobalComponents } from 'vue'
|
||||
|
||||
export {}
|
||||
|
||||
/* prettier-ignore */
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
NutAvatar: typeof import('@nutui/nutui')['Avatar']
|
||||
NutButton: typeof import('@nutui/nutui')['Button']
|
||||
NutCard: typeof import('@nutui/nutui')['Card']
|
||||
NutCell: typeof import('@nutui/nutui')['Cell']
|
||||
NutCellGroup: typeof import('@nutui/nutui')['CellGroup']
|
||||
NutForm: typeof import('@nutui/nutui')['Form']
|
||||
NutFormItem: typeof import('@nutui/nutui')['FormItem']
|
||||
NutGrid: typeof import('@nutui/nutui')['Grid']
|
||||
NutGridItem: typeof import('@nutui/nutui')['GridItem']
|
||||
NutInput: typeof import('@nutui/nutui')['Input']
|
||||
NutSwiper: typeof import('@nutui/nutui')['Swiper']
|
||||
NutSwiperItem: typeof import('@nutui/nutui')['SwiperItem']
|
||||
NutTabbar: typeof import('@nutui/nutui')['Tabbar']
|
||||
NutTabbarItem: typeof import('@nutui/nutui')['TabbarItem']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
VanButton: typeof import('vant/es')['Button']
|
||||
VanNavBar: typeof import('vant/es')['NavBar']
|
||||
VarButton: typeof import('@varlet/ui')['_ButtonComponent']
|
||||
VarSpace: typeof import('@varlet/ui')['_SpaceComponent']
|
||||
}
|
||||
}
|
||||
|
||||
// For TSX support
|
||||
declare global {
|
||||
const NutAvatar: typeof import('@nutui/nutui')['Avatar']
|
||||
const NutButton: typeof import('@nutui/nutui')['Button']
|
||||
const NutCard: typeof import('@nutui/nutui')['Card']
|
||||
const NutCell: typeof import('@nutui/nutui')['Cell']
|
||||
const NutCellGroup: typeof import('@nutui/nutui')['CellGroup']
|
||||
const NutForm: typeof import('@nutui/nutui')['Form']
|
||||
const NutFormItem: typeof import('@nutui/nutui')['FormItem']
|
||||
const NutGrid: typeof import('@nutui/nutui')['Grid']
|
||||
const NutGridItem: typeof import('@nutui/nutui')['GridItem']
|
||||
const NutInput: typeof import('@nutui/nutui')['Input']
|
||||
const NutSwiper: typeof import('@nutui/nutui')['Swiper']
|
||||
const NutSwiperItem: typeof import('@nutui/nutui')['SwiperItem']
|
||||
const NutTabbar: typeof import('@nutui/nutui')['Tabbar']
|
||||
const NutTabbarItem: typeof import('@nutui/nutui')['TabbarItem']
|
||||
const RouterLink: typeof import('vue-router')['RouterLink']
|
||||
const RouterView: typeof import('vue-router')['RouterView']
|
||||
const VanButton: typeof import('vant/es')['Button']
|
||||
const VanNavBar: typeof import('vant/es')['NavBar']
|
||||
const VarButton: typeof import('@varlet/ui')['_ButtonComponent']
|
||||
const VarSpace: typeof import('@varlet/ui')['_SpaceComponent']
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user