├── src
├── libs
│ └── .gitkeep
├── workers
│ └── .gitkeep
├── composables
│ └── .gitkeep
├── components-private
│ ├── .gitkeep
│ └── UserInfoCard
│ │ └── index.vue
├── utils
│ ├── request.ts
│ └── forceNavigateBack.ts
├── static
│ └── logo.png
├── layouts
│ └── HomeLayout.vue
├── modules
│ ├── pinia
│ │ └── index.ts
│ ├── polyfills
│ │ └── requestAnimationFrame.ts
│ └── axios
│ │ └── index.ts
├── shared
│ ├── env.ts
│ └── unocss.theme.ts
├── types.ts
├── styles
│ └── styles.scss
├── main.ts
├── components
│ └── TheLogo
│ │ └── index.vue
├── pages
│ ├── hello
│ │ └── name.vue
│ ├── index.vue
│ └── login
│ │ ├── children
│ │ ├── username-form.vue
│ │ └── captcha-form.vue
│ │ └── index.vue
├── pages.json
├── App.vue
├── apis
│ └── auth.ts
├── stores
│ └── auth.ts
├── uni.scss
└── manifest.json
├── netlify.toml
├── .npmrc
├── eslint.config.mjs
├── vercel.json
├── .gitignore
├── .env.development
├── .env.production
├── types
├── shims.d.ts
└── vite-env.d.ts
├── .env
├── patches
├── vue-demi@0.14.10.patch
└── @vueuse__core.patch
├── .vscode
├── extensions.json
└── settings.json
├── .github
├── renovate.json5
├── workflows
│ └── ci.yml
└── copilot-instructions.md
├── index.html
├── tsconfig.json
├── unocss.config.ts
├── vite.config.ts
├── UniApp Base.code-workspace
├── README.md
└── package.json
/src/libs/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/workers/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/composables/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components-private/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/netlify.toml:
--------------------------------------------------------------------------------
1 | [[redirects]]
2 | from = "/*"
3 | to = "/index.html"
4 | status = 200
5 |
--------------------------------------------------------------------------------
/src/utils/request.ts:
--------------------------------------------------------------------------------
1 | export {
2 | instance as request,
3 | } from '@/modules/axios/index';
4 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | shamefully-hoist=true
2 | strict-peer-dependencies=false
3 | package-manager-strict=false
4 |
--------------------------------------------------------------------------------
/eslint.config.mjs:
--------------------------------------------------------------------------------
1 | import moomfe from '@moomfe/eslint-config';
2 |
3 | export default moomfe();
4 |
--------------------------------------------------------------------------------
/src/static/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MoomFE-Starter-Template/UniApp-Base/HEAD/src/static/logo.png
--------------------------------------------------------------------------------
/src/layouts/HomeLayout.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/modules/pinia/index.ts:
--------------------------------------------------------------------------------
1 | import { createPinia } from 'pinia';
2 |
3 | const pinia = createPinia();
4 |
5 | export default pinia;
6 |
--------------------------------------------------------------------------------
/vercel.json:
--------------------------------------------------------------------------------
1 | {
2 | "rewrites": [
3 | {
4 | "source": "/(.*)",
5 | "destination": "/index.html"
6 | }
7 | ]
8 | }
9 |
--------------------------------------------------------------------------------
/src/shared/env.ts:
--------------------------------------------------------------------------------
1 | import { useStorageSync } from '@uni-helper/uni-use';
2 |
3 | /** Token */
4 | export const accessToken = useStorageSync('access_token', '');
5 |
--------------------------------------------------------------------------------
/src/types.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * 后端响应数据
3 | * - 根据后端返回的实际情况修改
4 | */
5 | export interface ResponseData {
6 | code: `${number}`;
7 | data: T;
8 | message: string;
9 | }
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .hbuilderx/launch.json
3 |
4 | node_modules
5 | dist
6 |
7 | components.d.ts
8 | auto-imports.d.ts
9 | .eslintrc-auto-import.json
10 |
11 | *.local
12 | *.log
--------------------------------------------------------------------------------
/.env.development:
--------------------------------------------------------------------------------
1 | # 环境变量配置文件
2 | # - https://cn.vitejs.dev/guide/env-and-mode#env-files
3 | # - 此文件为开发环境变量配置文件, 仅开发环境加载, 会覆盖 .env 文件中的相同配置
4 | # - 添加环境变量后, 需要在 `types/vite-env.d.ts` 文件中添加定义以支持 TypeScript 智能提示
5 |
--------------------------------------------------------------------------------
/.env.production:
--------------------------------------------------------------------------------
1 | # 环境变量配置文件
2 | # - https://cn.vitejs.dev/guide/env-and-mode#env-files
3 | # - 此文件为生产环境变量配置文件, 仅生产环境加载, 会覆盖 .env 文件中的相同配置
4 | # - 添加环境变量后, 需要在 `types/vite-env.d.ts` 文件中添加定义以支持 TypeScript 智能提示
5 |
--------------------------------------------------------------------------------
/types/shims.d.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 |
3 | declare module '*.vue' {
4 | import type { DefineComponent } from 'vue';
5 |
6 | const component: DefineComponent<{}, {}, any>;
7 | export default component;
8 | }
9 |
--------------------------------------------------------------------------------
/types/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | interface ImportMetaEnv {
4 | /** 接口请求基础路径 */
5 | readonly APP_BASE_URL: string;
6 | }
7 |
8 | interface ImportMeta {
9 | readonly env: ImportMetaEnv;
10 | }
11 |
--------------------------------------------------------------------------------
/.env:
--------------------------------------------------------------------------------
1 | # 环境变量配置文件
2 | # - https://cn.vitejs.dev/guide/env-and-mode#env-files
3 | # - 此文件为基础环境变量配置文件, 所有情况下都会加载
4 | # - 添加环境变量后, 需要在 `types/vite-env.d.ts` 文件中添加定义以支持 TypeScript 智能提示
5 |
6 | # 接口请求基础路径
7 | APP_BASE_URL = https://apifoxmock.com/m1/4781098-4434938-default
8 |
--------------------------------------------------------------------------------
/src/modules/polyfills/requestAnimationFrame.ts:
--------------------------------------------------------------------------------
1 | export default typeof requestAnimationFrame !== 'undefined'
2 | ? requestAnimationFrame
3 | : function (callback: FrameRequestCallback) {
4 | return setTimeout(() => {
5 | callback(Date.now());
6 | }, 1000 / 60);
7 | };
8 |
--------------------------------------------------------------------------------
/src/styles/styles.scss:
--------------------------------------------------------------------------------
1 | @import url("@unocss-applet/reset/uni-app/tailwind-compat.css");
2 |
3 |
4 | :root, page{
5 | // 修改 Wot Design 主题色
6 | --wot-color-theme: #2080F0;
7 |
8 | // 修改 NutUI 主题色
9 | --nut-primary-color: #2080F0;
10 | --nut-primary-color-end: #4098fc;
11 | }
--------------------------------------------------------------------------------
/src/utils/forceNavigateBack.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * 返回上一页, 如果没有上一页则返回首页
3 | */
4 | export function forceNavigateBack() {
5 | const pages = getCurrentPages();
6 |
7 | if (pages.length > 1)
8 | return uni.navigateBack();
9 |
10 | uni.reLaunch({
11 | url: '/pages/index',
12 | });
13 | }
14 |
--------------------------------------------------------------------------------
/patches/vue-demi@0.14.10.patch:
--------------------------------------------------------------------------------
1 | diff --git a/lib/v3/index.mjs b/lib/v3/index.mjs
2 | index be3a96d7433916d3a70f3d308cb4c1685883e4f8..c4529bbf3c16b7fc136162728d1560eb00a13c36 100644
3 | --- a/lib/v3/index.mjs
4 | +++ b/lib/v3/index.mjs
5 | @@ -32,3 +32,4 @@ export {
6 | isVue3,
7 | install,
8 | }
9 | +export const TransitionGroup = {};
10 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import { createSSRApp } from 'vue';
2 | import pinia from '@/modules/pinia/index';
3 | import App from './App.vue';
4 |
5 | import 'uno.css';
6 | import '@/styles/styles.scss';
7 |
8 | export function createApp() {
9 | const app = createSSRApp(App);
10 |
11 | app.use(pinia);
12 |
13 | return {
14 | app,
15 | };
16 | }
17 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "vue.volar",
4 | "dbaeumer.vscode-eslint",
5 | "antfu.iconify",
6 | "antfu.unocss",
7 | "csstools.postcss",
8 | "syler.sass-indented",
9 | "uni-helper.uni-app-schemas-vscode",
10 | "uni-helper.uni-app-snippets-vscode",
11 | "uni-helper.uni-ui-snippets-vscode",
12 | "uni-helper.uni-highlight-vscode"
13 | ],
14 | "unwantedRecommendations": [
15 | "octref.vetur",
16 | "vue.vscode-typescript-vue-plugin"
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/.github/renovate.json5:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "extends": [
4 | "github>moomfe/renovate-config"
5 | ],
6 | "ignoreDeps": [
7 | // UniApp 支持的 Vue 版本
8 | "vue",
9 | "pinia",
10 |
11 | // 使用了 pnpm 打补丁的包
12 | "vue-demi",
13 | "@vueuse/*",
14 |
15 | // https://github.com/Moonofweisheng/wot-design-uni/issues/716
16 | "sass",
17 |
18 | // 暂缓升级这几个依赖, 会导致小程序端启动失败
19 | "vite",
20 | "unocss",
21 | "unocss-applet",
22 | "@unocss-applet/*"
23 | ]
24 | }
25 |
--------------------------------------------------------------------------------
/src/components/TheLogo/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
17 |
18 |
25 |
--------------------------------------------------------------------------------
/src/pages/hello/name.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | 你好,{{ props.name }}
9 |
10 |
11 |
12 |
15 |
16 |
17 |
18 |
19 |
30 |
--------------------------------------------------------------------------------
/src/pages.json:
--------------------------------------------------------------------------------
1 | {
2 | // pages 数组中第一项表示应用启动页, 参考:https://uniapp.dcloud.io/collocation/pages
3 | "pages": [
4 | { "path": "pages/index" },
5 | { "path": "pages/hello/name" },
6 |
7 | // 登录页
8 | {
9 | "path": "pages/login/index",
10 | "style": {
11 | "navigationStyle": "custom"
12 | }
13 | }
14 | ],
15 | "globalStyle": {
16 | "navigationBarTitleText": "UniApp 项目模板",
17 | "navigationBarTextStyle": "black",
18 | "navigationBarBackgroundColor": "#F8F8F8",
19 | "backgroundColor": "#F8F8F8"
20 | },
21 | "easycom": {
22 | "autoscan": true,
23 | "custom": {}
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
27 |
28 |
31 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | - dev
8 | pull_request:
9 | branches:
10 | - main
11 |
12 | jobs:
13 | test-h5:
14 | runs-on: ubuntu-latest
15 | steps:
16 | - uses: actions/checkout@v5
17 | - uses: pnpm/action-setup@v4
18 | - name: Use Node.js v20
19 | uses: actions/setup-node@v6
20 | with:
21 | node-version: 24.11.1
22 | registry-url: https://registry.npmjs.org/
23 | cache: pnpm
24 | - name: Run build:h5
25 | run: pnpm build:h5
26 |
27 | test-mp:
28 | runs-on: ubuntu-latest
29 | steps:
30 | - uses: actions/checkout@v5
31 | - uses: pnpm/action-setup@v4
32 | - name: Use Node.js v20
33 | uses: actions/setup-node@v6
34 | with:
35 | node-version: 24.11.1
36 | registry-url: https://registry.npmjs.org/
37 | cache: pnpm
38 | - name: Run build:mp
39 | run: pnpm build:mp
40 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | // 启用 ESlint 的扁平配置支持
3 | "eslint.experimental.useFlatConfig": true,
4 |
5 | // 禁用默认格式化程序, 使用 ESlint 代替
6 | "prettier.enable": false,
7 | "editor.formatOnSave": false,
8 |
9 | // 自动修复
10 | "editor.codeActionsOnSave": {
11 | "source.fixAll.eslint": "explicit",
12 | "source.organizeImports": "never"
13 | },
14 |
15 | // 为所有支持的语言启用 ESlint
16 | "eslint.validate": [
17 | "javascript",
18 | "javascriptreact",
19 | "typescript",
20 | "typescriptreact",
21 | "vue",
22 | "html",
23 | "markdown",
24 | "json",
25 | "jsonc",
26 | "yaml",
27 | "toml",
28 | "gql",
29 | "graphql"
30 | ],
31 |
32 | "eslint.quiet": true,
33 |
34 | "unocss.autocomplete.matchType": "fuzzy",
35 | "unocss.remToPxPreview": true,
36 |
37 | "editor.tabSize": 2,
38 | "editor.detectIndentation": false,
39 | "editor.guides.bracketPairs": "active",
40 | "editor.renderWhitespace": "boundary",
41 | "editor.cursorSmoothCaretAnimation": "on",
42 |
43 | "files.associations": {
44 | "pages.json": "jsonc",
45 | "manifest.json": "jsonc",
46 | ".env": "shellscript"
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/pages/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | UniApp 项目模板
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
19 |
20 |
21 |
22 | ↓ ------ 示例页 ------ ↓
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
48 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "jsx": "preserve",
5 | "jsxFactory": "h",
6 | "jsxFragmentFactory": "Fragment",
7 | "lib": ["DOM", "ESNext"],
8 | "baseUrl": ".",
9 | "rootDir": ".",
10 | "module": "ESNext",
11 | "moduleResolution": "Bundler",
12 | "paths": {
13 | "~/*": ["./src/*"],
14 | "@/*": ["./src/*"],
15 | "@@/*": ["./*"]
16 | },
17 | "resolveJsonModule": true,
18 | "types": [
19 | "vite/client",
20 | "@dcloudio/types",
21 | "@uni-helper/uni-app-types",
22 | "@uni-helper/uni-ui-types",
23 | "miniprogram-api-typings",
24 | "wot-design-uni/global",
25 | "nutui-uniapp/global"
26 | ],
27 | "allowJs": true,
28 | "strict": true,
29 | "strictNullChecks": true,
30 | "noUnusedLocals": true,
31 | "outDir": "dist",
32 | "esModuleInterop": true,
33 | "forceConsistentCasingInFileNames": true,
34 | "skipLibCheck": true
35 | },
36 | "vueCompilerOptions": {
37 | "plugins": ["@uni-helper/uni-app-types/volar-plugin"]
38 | },
39 | "exclude": [
40 | "node_modules",
41 | "dist"
42 | ]
43 | }
44 |
--------------------------------------------------------------------------------
/patches/@vueuse__core.patch:
--------------------------------------------------------------------------------
1 | diff --git a/index.mjs b/index.mjs
2 | index 51256dd5e1819e08457077ba70a7a42b1489ac62..4529599cda4d3450a75f1b4bfe94bd57117659ab 100644
3 | --- a/index.mjs
4 | +++ b/index.mjs
5 | @@ -1,3 +1,4 @@
6 | +export { isString, isFunction } from 'mixte';
7 | import { noop, makeDestructurable, camelize, toValue, isClient, isObject, tryOnScopeDispose, isIOS, tryOnMounted, notNullish, objectOmit, promiseTimeout, until, increaseWithUnit, objectEntries, createSingletonPromise, useTimeoutFn, pausableWatch, toRef, createEventHook, computedWithControl, timestamp, pausableFilter, watchIgnorable, debounceFilter, createFilterWrapper, bypassFilter, toRefs, useIntervalFn, containsProp, hasOwn, throttleFilter, useDebounceFn, useThrottleFn, tryOnUnmounted, clamp, syncRef, objectPick, watchWithFilter, tryOnBeforeUnmount, identity, isDef, whenever, isWorker } from '@vueuse/shared';
8 | export * from '@vueuse/shared';
9 | import { isRef, ref, shallowRef, watchEffect, computed, inject, isVue3, version, defineComponent, h, TransitionGroup, shallowReactive, Fragment, watch, getCurrentInstance, customRef, onUpdated, onMounted, isVue2, readonly, reactive, toRaw, nextTick, markRaw, unref, getCurrentScope, set, del, isReadonly, onBeforeUpdate } from 'vue-demi';
10 |
--------------------------------------------------------------------------------
/src/apis/auth.ts:
--------------------------------------------------------------------------------
1 | import type { ResponseData } from '@/types';
2 | import { request } from '@/utils/request';
3 |
4 | /** 登录响应数据 */
5 | export interface LoginResponse {
6 | access_token: string;
7 | expires_in: number;
8 | }
9 |
10 | /** 账号密码登录数据 */
11 | export interface UsernameLoginData {
12 | username: string;
13 | password: string;
14 | }
15 | /** 验证码登录数据 */
16 | export interface CaptchaLoginData {
17 | mobile: string;
18 | captcha: string;
19 | }
20 |
21 | /** 用户信息 */
22 | export interface UserInfo {
23 | id: string;
24 | username: string;
25 | name: string;
26 | avatar: string;
27 | }
28 |
29 | /** 账号密码登录 */
30 | export function usernameLogin(data: UsernameLoginData) {
31 | return request.post>('/auth/login', data);
32 | }
33 |
34 | /** 验证码登录 */
35 | export function captchaLogin(data: CaptchaLoginData) {
36 | return request.post>('/auth/login/captcha', data);
37 | }
38 |
39 | /** 发送验证码 */
40 | export function sendCaptcha(mobile: string) {
41 | return request.post>('/auth/send_captcha', { mobile });
42 | }
43 |
44 | /** 获取登录用户信息 */
45 | export function getUserInfo() {
46 | return request.get>('/auth/info');
47 | }
48 |
49 | /** 退出登录 */
50 | export function logout() {
51 | return request.post>('/auth/logout');
52 | }
53 |
--------------------------------------------------------------------------------
/src/pages/login/children/username-form.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
34 |
35 |
43 |
44 |
58 |
--------------------------------------------------------------------------------
/src/stores/auth.ts:
--------------------------------------------------------------------------------
1 | import type { CaptchaLoginData, UsernameLoginData } from '@/apis/auth';
2 | import { defineStore } from 'pinia';
3 | import { captchaLogin, getUserInfo, logout as toLogout, sendCaptcha as toSendCaptcha, usernameLogin } from '@/apis/auth';
4 | import { accessToken } from '@/shared/env';
5 |
6 | export const useAuthStore = defineStore('auth', () => {
7 | /** 是否登录 */
8 | const isLogin = computed(() => !!accessToken.value);
9 |
10 | /** 用户名登录 */
11 | const loginByUsername = useRequestReactive((data: UsernameLoginData) => {
12 | return usernameLogin(data).then((res) => {
13 | accessToken.value = res.data.data.access_token;
14 | });
15 | });
16 |
17 | /** 发送验证码 */
18 | const sendCaptcha = useRequestReactive(toSendCaptcha);
19 | /** 验证码登录 */
20 | const loginByCaptcha = useRequestReactive((data: CaptchaLoginData) => {
21 | return captchaLogin(data).then((res) => {
22 | accessToken.value = res.data.data.access_token;
23 | });
24 | });
25 |
26 | /** 用户信息 */
27 | const info = useRequestReactive(() => getUserInfo().then(res => res.data));
28 |
29 | // 登录后获取用户信息
30 | wheneverImmediate(isLogin, () => {
31 | info.execute();
32 | });
33 |
34 | /** 退出登录 */
35 | const logout = useRequestReactive((showToast?: boolean) => {
36 | return toLogout().finally(() => {
37 | accessToken.value = '';
38 | showToast && uni.showToast({ title: '退出登录成功', icon: 'none' });
39 | });
40 | });
41 |
42 | return {
43 | isLogin,
44 |
45 | loginByUsername,
46 | sendCaptcha,
47 | loginByCaptcha,
48 | info,
49 | logout,
50 | };
51 | });
52 |
--------------------------------------------------------------------------------
/src/components-private/UserInfoCard/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 当前登录用户信息展示
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | {{ auth.info.data?.name }}
20 | {{ auth.info.data?.username }}
21 |
22 |
23 |
24 |
25 |
26 | 用户信息获取失败
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
47 |
--------------------------------------------------------------------------------
/src/modules/axios/index.ts:
--------------------------------------------------------------------------------
1 | import type { AxiosResponse } from 'axios';
2 | import type { ResponseData } from '@/types';
3 | import { createUniAppAxiosAdapter } from '@uni-helper/axios-adapter';
4 | import axios from 'axios';
5 | import { accessToken } from '@/shared/env';
6 |
7 | axios.defaults.adapter = createUniAppAxiosAdapter();
8 |
9 | export const instance = axios.create({
10 | baseURL: import.meta.env.APP_BASE_URL ?? '',
11 | adapter: createUniAppAxiosAdapter(),
12 | });
13 |
14 | // 请求拦截器
15 | instance.interceptors.request.use(
16 | (config) => {
17 | // 请求头携带 Token
18 | if (accessToken.value && !config.headers.Authorization)
19 | config.headers.Authorization = `Bearer ${accessToken.value}`;
20 |
21 | return config;
22 | },
23 | (error) => {
24 | return Promise.reject(error);
25 | },
26 | );
27 |
28 | // 响应拦截器
29 | instance.interceptors.response.use(
30 | (response: AxiosResponse) => {
31 | // 后端提示错误 ( 根据后端返回的实际情况修改 )
32 | if (response.data.code !== '20000') {
33 | // 处理用户鉴权相关问题 ( 根据后端返回的实际情况修改 )
34 | // - 40001: 未登录
35 | // - 40005: Token 失效
36 | if (['40001', '40005'].includes(response.data.code)) {
37 | uni.hideLoading();
38 | uni.showToast({ title: '登录已过期,请重新登录', icon: 'none' });
39 |
40 | return Promise.reject(response.data.message);
41 | }
42 |
43 | uni.hideLoading();
44 | uni.showToast({
45 | title: response.data.message,
46 | icon: 'none',
47 | });
48 |
49 | return Promise.reject(response.data.message);
50 | }
51 |
52 | return response;
53 | },
54 | (error) => {
55 | uni.hideLoading();
56 | uni.showToast({
57 | title: error.message ?? '请求失败,请稍后重试',
58 | icon: 'none',
59 | });
60 |
61 | return Promise.reject(error);
62 | },
63 | );
64 |
--------------------------------------------------------------------------------
/src/uni.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * 这里是uni-app内置的常用样式变量
3 | *
4 | * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量
5 | * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App
6 | *
7 | */
8 |
9 | /**
10 | * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能
11 | *
12 | * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
13 | */
14 |
15 | @import "nutui-uniapp/styles/variables.scss";
16 |
17 | /* 颜色变量 */
18 |
19 | /* 行为相关颜色 */
20 | $uni-color-primary: #007aff;
21 | $uni-color-success: #4cd964;
22 | $uni-color-warning: #f0ad4e;
23 | $uni-color-error: #dd524d;
24 |
25 | /* 文字基本颜色 */
26 | $uni-text-color: #333; // 基本色
27 | $uni-text-color-inverse: #fff; // 反色
28 | $uni-text-color-grey: #999; // 辅助灰色,如加载更多的提示信息
29 | $uni-text-color-placeholder: #808080;
30 | $uni-text-color-disable: #c0c0c0;
31 |
32 | /* 背景颜色 */
33 | $uni-bg-color: #fff;
34 | $uni-bg-color-grey: #f8f8f8;
35 | $uni-bg-color-hover: #f1f1f1; // 点击状态颜色
36 | $uni-bg-color-mask: rgba(0, 0, 0, 0.4); // 遮罩颜色
37 |
38 | /* 边框颜色 */
39 | $uni-border-color: #c8c7cc;
40 |
41 | /* 尺寸变量 */
42 |
43 | /* 文字尺寸 */
44 | $uni-font-size-sm: 12px;
45 | $uni-font-size-base: 14px;
46 | $uni-font-size-lg: 16px;
47 |
48 | /* 图片尺寸 */
49 | $uni-img-size-sm: 20px;
50 | $uni-img-size-base: 26px;
51 | $uni-img-size-lg: 40px;
52 |
53 | /* Border Radius */
54 | $uni-border-radius-sm: 2px;
55 | $uni-border-radius-base: 3px;
56 | $uni-border-radius-lg: 6px;
57 | $uni-border-radius-circle: 50%;
58 |
59 | /* 水平间距 */
60 | $uni-spacing-row-sm: 5px;
61 | $uni-spacing-row-base: 10px;
62 | $uni-spacing-row-lg: 15px;
63 |
64 | /* 垂直间距 */
65 | $uni-spacing-col-sm: 4px;
66 | $uni-spacing-col-base: 8px;
67 | $uni-spacing-col-lg: 12px;
68 |
69 | /* 透明度 */
70 | $uni-opacity-disabled: 0.3; // 组件禁用态的透明度
71 |
72 | /* 文章场景相关 */
73 | $uni-color-title: #2c405a; // 文章标题颜色
74 | $uni-font-size-title: 20px;
75 | $uni-color-subtitle: #555; // 二级标题颜色
76 | $uni-font-size-subtitle: 18px;
77 | $uni-color-paragraph: #3f536e; // 文章段落颜色
78 | $uni-font-size-paragraph: 15px;
--------------------------------------------------------------------------------
/src/pages/login/children/captcha-form.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
11 |
12 |
17 | {{ isStart ? `${Math.floor(output)}秒后重发` : '获取验证码' }}
18 |
19 |
20 |
21 |
22 |
23 |
24 |
65 |
66 |
74 |
75 |
89 |
--------------------------------------------------------------------------------
/src/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "UniApp-Base",
3 | "appid": "__UNI__27C8500",
4 | "description": "",
5 | "versionName": "1.0.0",
6 | "versionCode": "100",
7 | "transformPx": false,
8 | /* 5+App特有相关 */
9 | "app-plus": {
10 | "usingComponents": true,
11 | "nvueStyleCompiler": "uni-app",
12 | "compilerVersion": 3,
13 | "splashscreen": {
14 | "alwaysShowBeforeRender": true,
15 | "waiting": true,
16 | "autoclose": true,
17 | "delay": 0
18 | },
19 | /* 模块配置 */
20 | "modules": {},
21 | /* 应用发布信息 */
22 | "distribute": {
23 | /* android打包配置 */
24 | "android": {
25 | "permissions": [
26 | "",
27 | "",
28 | "",
29 | "",
30 | "",
31 | "",
32 | "",
33 | "",
34 | "",
35 | "",
36 | "",
37 | "",
38 | "",
39 | "",
40 | ""
41 | ],
42 | "abiFilters": ["arm64-v8a", "x86"]
43 | },
44 | /* ios打包配置 */
45 | "ios": {
46 | "dSYMs": false
47 | },
48 | /* SDK配置 */
49 | "sdkConfigs": {
50 | "ad": {}
51 | }
52 | }
53 | },
54 | /* 小程序特有相关 */
55 | "mp-weixin": {
56 | "appid": "",
57 | "setting": {
58 | "urlCheck": false
59 | },
60 | "usingComponents": true
61 | },
62 | "uniStatistics": {
63 | "enable": false
64 | },
65 | "vueVersion": "3"
66 | }
67 |
--------------------------------------------------------------------------------
/unocss.config.ts:
--------------------------------------------------------------------------------
1 | import type { Preset, SourceCodeTransformer } from 'unocss';
2 | import { dirname, resolve } from 'node:path';
3 | import process from 'node:process';
4 | import { fileURLToPath } from 'node:url';
5 | import { dataToEsm } from '@rollup/pluginutils';
6 | import fs from 'fs-extra';
7 | import { isString } from 'mixte';
8 | import { defineConfig, presetAttributify, presetIcons, presetUno, transformerDirectives, transformerVariantGroup } from 'unocss';
9 | import { presetApplet, presetRemRpx, transformerAttributify } from 'unocss-applet';
10 | import { presetExtra } from 'unocss-preset-extra';
11 |
12 | const __dirname = dirname(fileURLToPath(import.meta.url));
13 |
14 | const isApplet = process.env?.UNI_PLATFORM?.startsWith('mp') ?? false;
15 |
16 | const presets: Preset[] = [];
17 | const transformers: SourceCodeTransformer[] = [];
18 |
19 | if (isApplet) {
20 | // 默认预设, 和 Tailwind 类似
21 | presets.push(presetApplet({ variablePrefix: 'un:' }));
22 | // 将 rpx 单位转为 rem
23 | presets.push(presetRemRpx({ mode: 'rpx2rem' }));
24 | // 为小程序启用属性模式
25 | transformers.push(transformerAttributify({ prefix: 'un:', ignoreAttributes: ['block'] }));
26 | }
27 | else {
28 | // 默认预设, 和 Tailwind 类似
29 | presets.push(presetUno());
30 | // 属性模式
31 | presets.push(presetAttributify({ prefix: 'un:' }));
32 | // 将 rpx 单位转为 rem
33 | presets.push(presetRemRpx({ mode: 'rpx2rem' }));
34 | }
35 |
36 | export default defineConfig({
37 | theme: {
38 | // 过渡持续时间
39 | duration: {
40 | colors: '300ms',
41 | },
42 | },
43 | presets: [
44 | // 图标预设
45 | presetIcons(),
46 | // 类名简写及额外一些样式预设
47 | presetExtra(),
48 | // 在 UniApp 中使用 Unocss, 兼容不支持的语法
49 | ...presets,
50 | ],
51 | transformers: [
52 | // 在 CSS 中使用 @apply 指令
53 | transformerDirectives(),
54 | // 变体组功能
55 | transformerVariantGroup(),
56 | // 在 UniApp 中使用 Unocss, 兼容不支持的语法
57 | ...transformers,
58 | ],
59 | extendTheme: (theme) => {
60 | // 始终生成一个 UnoCSS 主题样式配置文件, 方便在 JS 中引用
61 | fs.outputFileSync(
62 | resolve(__dirname, './src/shared/unocss.theme.ts'),
63 | `/* eslint-disable */\n\n${dataToEsm(theme, {
64 | preferConst: true,
65 | indent: ' ',
66 | objectShorthand: true,
67 | })}`,
68 | );
69 | },
70 | postprocess: (util) => {
71 | util.entries.forEach((i) => {
72 | if (isString(i[1])) {
73 | i[1] = i[1].replace(/in (oklch|oklab)/g, '');
74 | }
75 | });
76 | },
77 | });
78 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { dirname, resolve } from 'node:path';
2 | import { fileURLToPath } from 'node:url';
3 | import Uni from '@dcloudio/vite-plugin-uni';
4 | import { MixteUseAutoImport } from '@mixte/use/register';
5 | import Inject from '@rollup/plugin-inject';
6 | import Components from '@uni-helper/vite-plugin-uni-components';
7 | import { UniUIResolver, WotResolver } from '@uni-helper/vite-plugin-uni-components/resolvers';
8 | import { isESModule } from 'mixte';
9 | import { NutResolver } from 'nutui-uniapp';
10 | import Unocss from 'unocss/vite';
11 | import AutoImport from 'unplugin-auto-import/vite';
12 | import { defineConfig } from 'vite';
13 | import Inspect from 'vite-plugin-inspect';
14 |
15 | const __dirname = dirname(fileURLToPath(import.meta.url));
16 |
17 | export default defineConfig({
18 | // 环境变量前缀
19 | envPrefix: 'APP_',
20 | // 路径别名
21 | resolve: {
22 | alias: {
23 | '~': resolve(__dirname, './src'),
24 | '@': resolve(__dirname, './src'),
25 | '@@': resolve(__dirname, './'),
26 | },
27 | },
28 | // 插件
29 | plugins: [
30 | Inject({
31 | requestAnimationFrame: resolve(__dirname, './src/modules/polyfills/requestAnimationFrame.ts'),
32 | exclude: [
33 | 'node_modules/wot-design-uni/**',
34 | ],
35 | }),
36 | // 原子化 CSS 引擎
37 | Unocss(),
38 | // 自动导入使用到的组件
39 | Components({
40 | dts: resolve(__dirname, './types/components.d.ts'),
41 | dirs: [
42 | resolve(__dirname, './src/layouts'),
43 | resolve(__dirname, './src/components'),
44 | resolve(__dirname, './src/components-private'),
45 | ],
46 | resolvers: [
47 | UniUIResolver(),
48 | WotResolver(),
49 | NutResolver(),
50 | ],
51 | }),
52 | // API 自动加载
53 | AutoImport({
54 | dts: resolve(__dirname, './types/auto-imports.d.ts'),
55 | vueTemplate: true,
56 | imports: [
57 | 'vue',
58 | 'uni-app',
59 | '@vueuse/core',
60 | '@vueuse/math',
61 | MixteUseAutoImport({ useWithVueUseCore: true }),
62 | ],
63 | dirs: [
64 | resolve(__dirname, './src/composables'),
65 | resolve(__dirname, './src/stores'),
66 | ],
67 | eslintrc: {
68 | enabled: true,
69 | filepath: resolve(__dirname, './.eslintrc-auto-import.json'),
70 | },
71 | }),
72 | // uni-app
73 | isESModule(Uni) ? Uni.default() : Uni(),
74 | // 插件调试
75 | Inspect(),
76 | ],
77 | });
78 |
--------------------------------------------------------------------------------
/UniApp Base.code-workspace:
--------------------------------------------------------------------------------
1 | {
2 | "folders": [
3 | {
4 | "path": "."
5 | },
6 | {
7 | "path": "src\\static",
8 | "name": "Static"
9 | },
10 | {
11 | "path": "src\\styles",
12 | "name": "Styles"
13 | },
14 | {
15 | "path": "src\\shared",
16 | "name": "Shared"
17 | },
18 | {
19 | "path": "src\\libs",
20 | "name": "Libs"
21 | },
22 | {
23 | "path": "src\\utils",
24 | "name": "Utils"
25 | },
26 | {
27 | "path": "src\\apis",
28 | "name": "Apis"
29 | },
30 | {
31 | "path": "src\\modules",
32 | "name": "Modules"
33 | },
34 | {
35 | "path": "src\\workers",
36 | "name": "Workers"
37 | },
38 | {
39 | "path": "src\\stores",
40 | "name": "Stores"
41 | },
42 | {
43 | "path": "src\\composables",
44 | "name": "Composables"
45 | },
46 | {
47 | "path": "src\\components",
48 | "name": "Components"
49 | },
50 | {
51 | "path": "src\\components-private",
52 | "name": "Components Private"
53 | },
54 | {
55 | "path": "src\\layouts",
56 | "name": "Layouts"
57 | },
58 | {
59 | "path": "src\\pages",
60 | "name": "Pages"
61 | }
62 | ],
63 | "settings": {
64 | // 启用 ESlint 的扁平配置支持
65 | "eslint.experimental.useFlatConfig": true,
66 |
67 | // 禁用默认格式化程序, 使用 ESlint 代替
68 | "prettier.enable": false,
69 | "editor.formatOnSave": false,
70 |
71 | // 自动修复
72 | "editor.codeActionsOnSave": {
73 | "source.fixAll.eslint": "explicit",
74 | "source.organizeImports": "never"
75 | },
76 |
77 | // 为所有支持的语言启用 ESlint
78 | "eslint.validate": [
79 | "javascript",
80 | "javascriptreact",
81 | "typescript",
82 | "typescriptreact",
83 | "vue",
84 | "html",
85 | "markdown",
86 | "json",
87 | "jsonc",
88 | "yaml",
89 | "toml",
90 | "gql",
91 | "graphql"
92 | ],
93 |
94 | "eslint.quiet": true,
95 |
96 | "unocss.autocomplete.matchType": "fuzzy",
97 | "unocss.remToPxPreview": true,
98 |
99 | "files.associations": {
100 | "pages.json": "jsonc",
101 | "manifest.json": "jsonc"
102 | },
103 |
104 | "files.exclude": {
105 | "*.code-workspace": true,
106 | "node_modules": true,
107 | "src/apis": true,
108 | "src/components": true,
109 | "src/components-private": true,
110 | "src/composables": true,
111 | "src/layouts": true,
112 | "src/libs": true,
113 | "src/modules": true,
114 | "src/pages": true,
115 | "src/router": true,
116 | "src/shared": true,
117 | "src/static": true,
118 | "src/stores": true,
119 | "src/styles": true,
120 | "src/utils": true,
121 | "src/workers": true
122 | },
123 | }
124 | }
--------------------------------------------------------------------------------
/.github/copilot-instructions.md:
--------------------------------------------------------------------------------
1 | # AI 智能代理手册
2 | - **技术栈**:UniApp + Vue 3(`
109 |
110 |
120 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 项目快速启动模板
2 | UniApp 项目模板
3 |
4 |
5 |
6 | ## 特性
7 |
8 | - ⚡️ [Vue 3](https://github.com/vuejs/core), [Vite](https://github.com/vitejs/vite), [pnpm](https://pnpm.io) 优先 - 就是快!
9 | - 📲 [组件自动加载](https://github.com/uni-helper/vite-plugin-uni-components)
10 | - 📥 [API 自动加载](https://github.com/antfu/unplugin-auto-import) - 直接使用 Composition API 无需引入
11 | - 🎨 [UnoCSS](https://github.com/unocss/unocss) - 高性能且极具灵活性的原子化 CSS 引擎
12 | - 😃 [各种图标集为你所用](https://github.com/iconify/icon-sets) - 150+ 图标集, 200000+ 图标为你所用, 从不妥协
13 | - 🍍 [使用 Pinia 的状态管理](https://pinia.vuejs.org)
14 | - 🔥 使用 [新的 `