├── 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 | 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 | 7 | 8 | 17 | 18 | 25 | -------------------------------------------------------------------------------- /src/pages/hello/name.vue: -------------------------------------------------------------------------------- 1 | 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 | 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 | 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 | 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 | 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 | - 🔥 使用 [新的 `