├── .browserslistrc ├── .editorconfig ├── .env ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .npmrc ├── .prettierignore ├── .prettierrc.js ├── README.md ├── babel.config.js ├── config └── index.js ├── global.d.ts ├── mock └── api.ts ├── package.json ├── pnpm-lock.yaml ├── postcss.config.js ├── project.config.json ├── project.private.config.json ├── public ├── favicon.ico └── index.html ├── src ├── api │ ├── http.interceptor.ts │ ├── http.ts │ └── tsg.config.ts ├── app.config.ts ├── app.ts ├── common │ └── decorator │ │ └── catch │ │ └── index.ts ├── component │ └── if │ │ └── index.tsx ├── config │ ├── config.default.ts │ ├── config.develop.ts │ ├── config.release.ts │ ├── config.trial.ts │ └── index.ts ├── index.html ├── main.ts ├── module │ └── demo │ │ ├── first │ │ ├── index.config.ts │ │ ├── index.module.scss │ │ └── index.tsx │ │ └── two │ │ ├── index.config.ts │ │ ├── index.module.scss │ │ └── index.tsx ├── polyfill.ts ├── setup │ └── index.ts └── theme │ └── app.scss ├── tailwind.config.js └── tsconfig.json /.browserslistrc: -------------------------------------------------------------------------------- 1 | chrome >= 51 2 | ios >= 10 3 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | insert_final_newline = false 13 | trim_trailing_whitespace = false -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | VUE_APP_BASE_ROUTE=/ 2 | VUE_APP_BASE_URL=$VUE_APP_BASE_ROUTE 3 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .idea 3 | dist 4 | coverage 5 | public 6 | *.d.ts 7 | *.js 8 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true, 5 | browser: true, 6 | }, 7 | extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended'], 8 | plugins: ['@typescript-eslint'], 9 | parser: '@typescript-eslint/parser', 10 | parserOptions: { 11 | ecmaVersion: 2020, 12 | sourceType: 'module', 13 | ecmaFeatures: { 14 | jsx: true, 15 | }, 16 | }, 17 | rules: { 18 | '@typescript-eslint/unbound-method': 'off', 19 | '@typescript-eslint/ban-ts-comment': 'off', 20 | '@typescript-eslint/explicit-module-boundary-types': 'off', 21 | '@typescript-eslint/no-empty-function': 'warn', 22 | '@typescript-eslint/ban-types': [ 23 | 'error', 24 | { 25 | extendDefaults: true, 26 | types: { 27 | '{}': false, 28 | }, 29 | }, 30 | ], 31 | '@typescript-eslint/no-explicit-any': 'off', 32 | 'no-debugger': 'warn', 33 | 'no-case-declarations': 'off', 34 | '@typescript-eslint/no-unused-vars': 'off', 35 | }, 36 | } 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | deploy_versions/ 3 | .temp/ 4 | .rn_temp/ 5 | node_modules/ 6 | .DS_Store 7 | .gitlab-ci.yml 8 | yarn-error.log 9 | .idea 10 | .swc 11 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry=https://registry.npmmirror.com/ 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .editorconfig 2 | .gitignore 3 | .prettierignore 4 | .idea 5 | node_modules 6 | dist 7 | pnpm-lock.yaml 8 | *.d.ts 9 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | semi: false, 3 | singleQuote: true, 4 | printWidth: 100, 5 | trailingComma: 'all', 6 | arrowParens: 'avoid', 7 | plugins: [require('prettier-plugin-tailwindcss')], 8 | } 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 使用vue3-oop开发小程序 2 | 3 | - vue3 4 | - tsx + vue3-oop 5 | - 依赖注入 6 | 7 | ## 开发 8 | 9 | ```shell 10 | pnpm i 11 | pnpm run dev 12 | ``` 13 | 14 | 然后打开微信开发者工具查看 15 | 16 | ### 开发h5 17 | 18 | ```shell 19 | pnpm run dev:h5 20 | ``` 21 | 22 | ## 打包 23 | 24 | 类似vue-cli一样根据mode来加载环境变量 25 | 26 | ```shell 27 | pnpm run dev --mode master 28 | ``` 29 | 30 | 会加载 `.env.master` 环境变量 31 | 32 | 33 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | // babel-preset-taro 更多选项和默认值: 2 | // https://github.com/NervJS/taro/blob/next/packages/babel-preset-taro/README.md 3 | // 小程序端原生组件 4 | 5 | module.exports = { 6 | presets: [ 7 | [ 8 | 'taro', 9 | { 10 | framework: 'vue3', 11 | ts: false, 12 | }, 13 | ], 14 | ], 15 | plugins: [ 16 | [ 17 | 'import', 18 | { 19 | libraryName: '@nutui/nutui-taro', 20 | customName: (name) => `@nutui/nutui-taro/dist/packages/${name.toLowerCase()}`, 21 | style: (name, file) => name + '/style', 22 | camel2DashComponentName: false, 23 | }, 24 | 'nutui4-taro', 25 | ], 26 | ], 27 | } 28 | -------------------------------------------------------------------------------- /config/index.js: -------------------------------------------------------------------------------- 1 | import { loadEnv } from "@vue3-oop/taro-plugin"; 2 | import { UnifiedWebpackPluginV5 } from "weapp-tailwindcss"; 3 | 4 | const env = loadEnv() 5 | const isH5 = process.env.TARO_ENV === 'h5' 6 | const isWatch = process.env.VUE_APP_WATCH === 'true' 7 | /** 8 | * 9 | * @type {import('@tarojs/taro/types/compile').IProjectConfig} 10 | */ 11 | const config = { 12 | projectName: 'vue3taro', 13 | framework: 'vue3', 14 | designWidth: 375, 15 | deviceRatio: { 16 | 375: 2, 17 | }, 18 | sourceRoot: 'src', 19 | outputRoot: `dist`, 20 | compiler: { 21 | type: 'webpack5', 22 | prebundle: { 23 | enable: false, 24 | }, 25 | }, 26 | plugins: [ 27 | '@tarojs/plugin-html', 28 | '@tarojs/plugin-http', 29 | '@vue3-oop/taro-plugin', 30 | isWatch ? '@tarojs/plugin-mock' : undefined, 31 | ].filter(Boolean), 32 | copy: { 33 | patterns: [ 34 | isH5 35 | ? { 36 | from: 'public/', 37 | to: 'dist/', 38 | ignore: ['**/index.html'], 39 | } 40 | : undefined, 41 | ].filter(Boolean), 42 | }, 43 | sass: { 44 | data: '@import "@nutui/nutui-taro/dist/styles/variables.scss";', 45 | }, 46 | mini: { 47 | optimizeMainPackage: { 48 | enable: true, 49 | }, 50 | postcss: { 51 | pxtransform: { 52 | enable: true, 53 | config: {}, 54 | }, 55 | url: { 56 | enable: true, 57 | config: { 58 | limit: 1024, // 设定转换尺寸上限 59 | }, 60 | }, 61 | cssModules: { 62 | enable: true, // 默认为 false,如需使用 css modules 功能,则设为 true 63 | config: { 64 | namingPattern: 'module', // 转换模式,取值为 global/module 65 | generateScopedName: '[local]--[hash:base64:5]', 66 | }, 67 | }, 68 | }, 69 | webpackChain(chain, webpack) { 70 | chain.merge({ 71 | plugin: { 72 | install: { 73 | plugin: UnifiedWebpackPluginV5, 74 | args: [ 75 | { 76 | appType: 'taro', 77 | // 注意这一行(不传默认 react) 78 | framework: 'vue3', // 'vue2' / 'vue3' 79 | disabled: isH5, 80 | injectAdditionalCssVarScope: true, 81 | }, 82 | ], 83 | }, 84 | }, 85 | }) 86 | }, 87 | }, 88 | h5: { 89 | useDeprecatedAdapterComponent: true, 90 | publicPath: process.env.VUE_APP_BASE_URL, 91 | router: { 92 | basename: process.env.VUE_APP_BASE_ROUTE.replace(/\/$/, ''), 93 | mode: 'browser', 94 | }, 95 | staticDirectory: 'static', 96 | esnextModules: ['nutui-taro', 'icons-vue-taro'], 97 | postcss: { 98 | pxtransform: { 99 | enable: true, 100 | config: {}, 101 | }, 102 | cssModules: { 103 | enable: true, 104 | config: { 105 | namingPattern: 'module', 106 | localsConvention: 'camelCaseOnly', 107 | generateScopedName: '[local]--[hash:base64:5]', 108 | }, 109 | }, 110 | }, 111 | devServer: { 112 | open: false, 113 | }, 114 | }, 115 | } 116 | module.exports = () => config 117 | -------------------------------------------------------------------------------- /global.d.ts: -------------------------------------------------------------------------------- 1 | // 热更新需要用 2 | declare var __VUE_HMR_RUNTIME__: any 3 | // 环境变量定义 4 | declare namespace __WebpackModuleApi { 5 | interface NodeProcess { 6 | env: { 7 | NODE_ENV: 'development' | 'production' 8 | TARO_ENV: 'weapp' | 'swan' | 'alipay' | 'h5' | 'rn' | 'tt' | 'quickapp' | 'qq' | 'jd' 9 | VUE_APP_MODE: string 10 | BASE_URL: string 11 | VUE_APP_BASE_ROUTE: string 12 | VUE_APP_BASE_URL: string 13 | } 14 | } 15 | } 16 | 17 | declare module '@tarojs/components' { 18 | export * from '@tarojs/components/types/index.vue3' 19 | } 20 | -------------------------------------------------------------------------------- /mock/api.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | 'GET /api/abc': { 3 | status: 1, 4 | data: [11111], 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "taro3-vue3-template", 3 | "version": "1.0.0", 4 | "private": true, 5 | "description": "taro3-vue3-template", 6 | "templateInfo": { 7 | "name": "default", 8 | "typescript": true, 9 | "css": "sass" 10 | }, 11 | "scripts": { 12 | "dev": "pnpm run build --watch", 13 | "dev:prod": "NODE_ENV=production pnpm run build --watch", 14 | "build": "taro build --type weapp", 15 | "build:master": "pnpm run build --mode master", 16 | "dev:h5": "pnpm run build:h5 --watch", 17 | "build:h5": "taro build --type h5", 18 | "preview:h5": "serve -s dist", 19 | "type:check": "tsc --noEmit", 20 | "lint": "eslint --fix .", 21 | "api": "tsg -c ./src/api/tsg.config.ts", 22 | "postinstall": "simple-git-hooks && weapp-tw patch" 23 | }, 24 | "dependencies": { 25 | "@abraham/reflection": "^0.12.0", 26 | "@babel/runtime": "^7.23.5", 27 | "@nutui/nutui-taro": "^4.2.3", 28 | "@tarojs/components": "3.6.20", 29 | "@tarojs/helper": "3.6.20", 30 | "@tarojs/plugin-framework-vue3": "3.6.20", 31 | "@tarojs/plugin-html": "3.6.20", 32 | "@tarojs/plugin-platform-h5": "^3.6.20", 33 | "@tarojs/plugin-platform-weapp": "3.6.20", 34 | "@tarojs/runtime": "3.6.20", 35 | "@tarojs/shared": "3.6.20", 36 | "@tarojs/taro": "3.6.20", 37 | "@vue3-oop/taro-hooks": "^1.0.1", 38 | "axios": "^1.6.2", 39 | "injection-js": "^2.4.0", 40 | "path-to-regexp": "^6.2.1", 41 | "ts-essentials": "^9.4.1", 42 | "tslib": "^2.6.2", 43 | "vue": "^3.3.9", 44 | "vue3-oop": "^1.0.6" 45 | }, 46 | "devDependencies": { 47 | "@babel/core": "^7.23.5", 48 | "@commitlint/cli": "^18.4.3", 49 | "@commitlint/config-conventional": "^18.4.3", 50 | "@tarojs/cli": "^3.6.20", 51 | "@tarojs/plugin-http": "^3.6.20", 52 | "@tarojs/plugin-mock": "^0.0.9", 53 | "@tarojs/webpack5-runner": "3.6.20", 54 | "@types/lodash": "^4.14.202", 55 | "@types/node": "^20.10.1", 56 | "@types/webpack-env": "^1.18.4", 57 | "@typescript-eslint/eslint-plugin": "^6.13.1", 58 | "@typescript-eslint/parser": "^6.13.1", 59 | "@vue/babel-plugin-jsx": "^1.1.5", 60 | "@vue/compiler-sfc": "^3.3.9", 61 | "@vue3-oop/taro-plugin": "^1.0.5", 62 | "babel-plugin-import": "^1.13.8", 63 | "babel-preset-taro": "3.6.20", 64 | "cross-env": "^7.0.3", 65 | "css-loader": "6.8.1", 66 | "eslint": "~8.54.0", 67 | "eslint-config-prettier": "^8.9.0", 68 | "eslint-config-taro": "3.6.20", 69 | "eslint-plugin-prettier": "^4.0.0", 70 | "eslint-plugin-vue": "^9.19.2", 71 | "lint-staged": "^15.1.0", 72 | "lodash": "^4.17.21", 73 | "postcss": "^8.4.31", 74 | "postcss-preset-env": "^9.3.0", 75 | "prettier": "^2.8.8", 76 | "prettier-plugin-tailwindcss": "^0.4.1", 77 | "serve": "^14.2.1", 78 | "simple-git-hooks": "^2.9.0", 79 | "style-loader": "3.3.3", 80 | "swagger-schema-official": "2.0.0-bab6bed", 81 | "tailwindcss": "~3.3.5", 82 | "tailwindcss-rem2px-preset": "^1.0.3", 83 | "ts-gear": "^4.11.7", 84 | "ts-node": "^10.9.1", 85 | "typescript": "^5.3.2", 86 | "vite": "^4.4.9", 87 | "vue-loader": "^17.3.1", 88 | "weapp-tailwindcss": "^3.0.3", 89 | "webpack": "^5.89.0" 90 | }, 91 | "commitlint": { 92 | "extends": [ 93 | "@commitlint/config-conventional" 94 | ] 95 | }, 96 | "simple-git-hooks": { 97 | "pre-commit": "pnpm exec lint-staged", 98 | "commit-msg": "pnpm exec commitlint -e \"$@\"" 99 | }, 100 | "lint-staged": { 101 | "*.{ts,tsx}": [ 102 | "eslint --fix" 103 | ], 104 | "*.{css,scss,html,json}": [ 105 | "prettier --write" 106 | ] 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | 'postcss-preset-env': {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /project.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "miniprogramRoot": "dist/", 3 | "projectname": "WechatAppDemo", 4 | "description": "WechatAppDemo", 5 | "appid": "wx3f627368f76b01c7", 6 | "setting": { 7 | "urlCheck": false, 8 | "es6": false, 9 | "postcss": false, 10 | "minified": false, 11 | "checkSiteMap": false, 12 | "babelSetting": { 13 | "ignore": [], 14 | "disablePlugins": [], 15 | "outputPath": "" 16 | }, 17 | "condition": false, 18 | "minifyWXSS": false, 19 | "ignoreUploadUnusedFiles": false, 20 | "ignoreDevUnusedFiles": false 21 | }, 22 | "compileType": "miniprogram", 23 | "libVersion": "3.1.3", 24 | "srcMiniprogramRoot": "dist/", 25 | "packOptions": { 26 | "ignore": [], 27 | "include": [] 28 | }, 29 | "condition": {}, 30 | "editorSetting": { 31 | "tabIndent": "auto", 32 | "tabSize": 4 33 | }, 34 | "simulatorPluginLibVersion": {} 35 | } -------------------------------------------------------------------------------- /project.private.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html", 3 | "projectname": "vue3-taro", 4 | "setting": { 5 | "compileHotReLoad": false, 6 | "bigPackageSizeSupport": true 7 | }, 8 | "libVersion": "3.1.3" 9 | } -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agileago/vue3-taro/6f59b82f6204fc907730c8512d125466c0f0e9f1/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 16 | 17 | 18 |
19 | 20 | 21 | -------------------------------------------------------------------------------- /src/api/http.interceptor.ts: -------------------------------------------------------------------------------- 1 | import { abcRequest } from '@/api/http' 2 | import type { AxiosResponse } from 'axios' 3 | import config from '@/config' 4 | 5 | // 过滤返回数据 6 | function handleResponseSuccess(res: AxiosResponse) { 7 | return res.data 8 | } 9 | // 处理错误 10 | function handleResponseError(error: any) { 11 | // 后端返回401直接到登录 12 | if (error?.response?.status === 401) { 13 | location.href = config.BASE_ROUTE + 'login' 14 | return 15 | } 16 | if (error?.response) { 17 | const data = error.response.data 18 | const responseError = new Error(data?.message || data) 19 | // @ts-ignore 20 | responseError.code = data?.code 21 | // @ts-ignore 22 | responseError.detail = data 23 | 24 | throw responseError 25 | } 26 | 27 | throw error 28 | } 29 | 30 | abcRequest.interceptors.response.use(handleResponseSuccess) 31 | abcRequest.interceptors.response.use(undefined, handleResponseError) 32 | -------------------------------------------------------------------------------- /src/api/http.ts: -------------------------------------------------------------------------------- 1 | import type { AxiosInstance, AxiosRequestConfig } from 'axios' 2 | import axios from 'axios' 3 | import * as pathToRegexp from 'path-to-regexp' 4 | import type { RequestParameter } from 'ts-gear' 5 | import config from '@/config' 6 | 7 | // region 基础方法 基本不需要动 8 | 9 | // 此类型主要用于简化响应类型 10 | type ReturnEntityType = T 11 | export type { AxiosRequestConfig } 12 | /** 13 | * 解析url中的参数 /abc/:id 替换id 14 | * @param url 15 | * @param option 16 | */ 17 | export const parseUrl = (url: string, option?: RequestParameter): string => { 18 | if (option) { 19 | if (option.path) { 20 | Object.getOwnPropertyNames(option.path).forEach(k => { 21 | option.path[k] = encodeURIComponent(String(option.path[k])) 22 | }) 23 | url = pathToRegexp.compile(url)(option.path) 24 | } 25 | } 26 | return url 27 | } 28 | /** 29 | * 转换成axios里面的配置 30 | * @param url 31 | * @param option 32 | */ 33 | export function interceptRequest( 34 | url: string, 35 | option?: RequestParameter, 36 | ): [string, AxiosRequestConfig] { 37 | try { 38 | url = parseUrl(url, option) 39 | } catch (e: any) { 40 | throw new Error(e.message) 41 | } 42 | option = option || {} 43 | const requestOption: AxiosRequestConfig = { 44 | method: option.method || 'get', 45 | } 46 | if (option.header) { 47 | requestOption.headers = option.header 48 | } 49 | if (option.body) { 50 | requestOption.data = option.body 51 | } 52 | if (option.formData) { 53 | const formData = new FormData() 54 | Object.keys(option.formData).forEach(k => { 55 | formData.append(k, option?.formData[k]) 56 | }) 57 | requestOption.data = formData 58 | } 59 | if (option.query) { 60 | requestOption.params = option.query 61 | } 62 | return [url, requestOption] 63 | } 64 | /** 65 | * 创建请求方法 66 | * @param ax 67 | */ 68 | export const createRequester = (ax: AxiosInstance) => { 69 | return function (apiUrl: string, param: RequestParameter, config: AxiosRequestConfig = {}) { 70 | // eslint-disable-next-line prefer-const 71 | let [url, option] = interceptRequest(apiUrl, param) 72 | option = { url, ...option, ...config } 73 | return ax.request(option) as unknown as Promise> 74 | } 75 | } 76 | // endregion 77 | 78 | // 创建request 对request进行拦截各种操作 79 | export const abcRequest = axios.create({ 80 | baseURL: config.API, 81 | }) 82 | 83 | export const abcRequester = createRequester(abcRequest) 84 | -------------------------------------------------------------------------------- /src/api/tsg.config.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * each project will use the "requester" function when request remote api 3 | * so this file would be included into your source file when compile 4 | * */ 5 | import type { Project } from 'ts-gear' 6 | import { generateRequestFunctionName } from 'ts-gear/lib/tool/generateRequestFunctionName' 7 | // @ts-ignore 8 | import prettier from '../../.prettierrc' 9 | import * as _ from 'lodash' 10 | 11 | // 调用的所有api 12 | const projects: Partial[] = [ 13 | { 14 | name: 'abc', 15 | source: 'http://211.154.163.74:21191/we_uc/openapi_json', 16 | }, 17 | ] 18 | 19 | /** 20 | * 生成请求代码样例: 21 | * (option?: RequestType, config?: AxiosRequestConfig) => requester('/url', {method: 'post', ...option}, config) 22 | */ 23 | const requestTemplate: Project['generateRequestFunction'] = function (arg) { 24 | // 适配 fastApi 路径参数需要被path-to-regexp正确解析 25 | const path = arg.pathname.replace(/{(\w+?)}/g, (s, p1) => `:${p1}`) 26 | let parameter = arg.parameterTypeName 27 | ? `option${!arg.parameterRequired ? '?' : ''}: ${arg.parameterTypeName}, ` 28 | : '' 29 | parameter += 'config?: AxiosRequestConfig' 30 | const body = `requester<${arg.responseSuccessTypeName}>('${path}', { method: '${ 31 | arg.httpMethod 32 | }' ${arg.parameterTypeName ? ', ...option' : ''}}, config)` 33 | return `(${parameter}) => ${body}` 34 | } 35 | function createStandardProjects(projects: Partial[]) { 36 | return projects.map(p => { 37 | const { name } = p 38 | return { 39 | dest: '../api', 40 | keepGeneric: false, 41 | shouldExportResponseType: false, 42 | shouldExportRequestOptionType: false, 43 | shouldForceSkipRequestHeaderOption: true, 44 | importRequesterStatement: `import { ${ 45 | _.camelCase(name) + 'Requester' 46 | }, type AxiosRequestConfig } from "../http"`, 47 | prettierConfig: prettier, 48 | // 生成请求函数名称 49 | generateRequestFunctionName(arg) { 50 | return ( 51 | 'api' + _.upperFirst(_.camelCase(name)) + _.upperFirst(generateRequestFunctionName(arg)) 52 | ) 53 | }, 54 | generateRequestFunction: requestTemplate, 55 | ...p, 56 | } as Project 57 | }) 58 | } 59 | 60 | export default createStandardProjects(projects) 61 | -------------------------------------------------------------------------------- /src/app.config.ts: -------------------------------------------------------------------------------- 1 | export default defineAppConfig({ 2 | pages: ['module/demo/first/index', 'module/demo/two/index'], 3 | window: { 4 | navigationBarBackgroundColor: '#fff', 5 | navigationBarTitleText: '小程序Taro1', 6 | navigationBarTextStyle: 'black', 7 | }, 8 | subpackages: [], 9 | }) 10 | -------------------------------------------------------------------------------- /src/app.ts: -------------------------------------------------------------------------------- 1 | import './polyfill' 2 | import '@abraham/reflection' 3 | import './theme/app.scss' 4 | import { createApp } from 'vue' 5 | import { setup } from '@/setup' 6 | import App from './main' 7 | 8 | const app = createApp(App) 9 | setup(app) 10 | export default app 11 | -------------------------------------------------------------------------------- /src/common/decorator/catch/index.ts: -------------------------------------------------------------------------------- 1 | import Taro from '@tarojs/taro' 2 | 3 | /** 4 | * 自动处理异常装饰器,适合异常后无后续处理的请求 5 | * @param msg 自定义提示消息 6 | * @param loadingKey 自定义loading 7 | */ 8 | export function Catch(loading = true, loadingKey?: string | ((obj: any) => void)): MethodDecorator { 9 | return function (target: any, key: string | symbol, desc: PropertyDescriptor) { 10 | const fn = desc.value 11 | desc.value = async function (this: any, ...args: any[]) { 12 | try { 13 | loading && Taro.showLoading() 14 | return await fn.apply(this, args) 15 | } catch (e: any) { 16 | console.error(e) 17 | if (e.message) { 18 | Taro.showToast({ 19 | icon: 'error', 20 | title: e.message, 21 | duration: 4000, 22 | }) 23 | } 24 | } finally { 25 | loading && Taro.hideLoading() 26 | if (loadingKey) { 27 | if (typeof loadingKey === 'string') this[loadingKey] = false 28 | else loadingKey(this) 29 | } 30 | } 31 | } 32 | return desc 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/component/if/index.tsx: -------------------------------------------------------------------------------- 1 | import type { SetupContext } from 'vue' 2 | 3 | export interface IfProps { 4 | /*判断条件*/ 5 | condition: any 6 | } 7 | export function If(props: IfProps, ctx: SetupContext) { 8 | if (!props.condition) return null 9 | return ctx.slots.default?.() 10 | } 11 | -------------------------------------------------------------------------------- /src/config/config.default.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 默认本地开发配置 3 | */ 4 | export default class Config { 5 | env = process.env.VUE_APP_MODE 6 | // 基础路由 /app/ 7 | BASE_ROUTE = process.env.VUE_APP_BASE_ROUTE 8 | // 静态资源路径 9 | BASE_URL = process.env.VUE_APP_BASE_URL 10 | HOST = 'http://localhost:9527' 11 | // 后端API 12 | get API() { 13 | return this.HOST + '/api' 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/config/config.develop.ts: -------------------------------------------------------------------------------- 1 | import Config from '@/config/config.default' 2 | 3 | export default class extends Config {} 4 | -------------------------------------------------------------------------------- /src/config/config.release.ts: -------------------------------------------------------------------------------- 1 | import Config from '@/config/config.default' 2 | 3 | export default class extends Config {} 4 | -------------------------------------------------------------------------------- /src/config/config.trial.ts: -------------------------------------------------------------------------------- 1 | import Config from '@/config/config.default' 2 | 3 | export default class extends Config {} 4 | -------------------------------------------------------------------------------- /src/config/index.ts: -------------------------------------------------------------------------------- 1 | import Config from './config.default' 2 | import Develop from './config.develop' 3 | import Release from './config.release' 4 | import Taro from '@tarojs/taro' 5 | 6 | let TargetConf = Config 7 | 8 | const { miniProgram } = Taro.getAccountInfoSync() 9 | 10 | switch (miniProgram.envVersion) { 11 | case 'develop': 12 | TargetConf = Develop 13 | break 14 | case 'trial': 15 | TargetConf = Develop 16 | break 17 | case 'release': 18 | TargetConf = Release 19 | break 20 | } 21 | 22 | const config = new TargetConf() 23 | 24 | export default config 25 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 16 | 17 | 18 |
19 | 20 | 21 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { VueComponent } from 'vue3-oop' 2 | // 全局服务挂载 3 | export default class App extends VueComponent {} 4 | -------------------------------------------------------------------------------- /src/module/demo/first/index.config.ts: -------------------------------------------------------------------------------- 1 | export default definePageConfig({ 2 | navigationBarBackgroundColor: '#781d1d', 3 | navigationBarTitleText: 'demo第一页', 4 | }) 5 | -------------------------------------------------------------------------------- /src/module/demo/first/index.module.scss: -------------------------------------------------------------------------------- 1 | .title { 2 | color: red; 3 | } 4 | -------------------------------------------------------------------------------- /src/module/demo/first/index.tsx: -------------------------------------------------------------------------------- 1 | import { Hook, Link, VueComponent } from 'vue3-oop' 2 | import { MiniHook } from '@vue3-oop/taro-hooks' 3 | import { abcRequest } from '@/api/http' 4 | import { useRouter } from '@tarojs/taro' 5 | import { Button } from '@tarojs/components' 6 | 7 | export default class First extends VueComponent { 8 | router = useRouter() 9 | @MiniHook('Load') 10 | load() { 11 | abcRequest('/abc', { method: 'get', params: { a: 1 } }).then(res => console.log('res', res)) 12 | } 13 | 14 | @Hook('Mounted') 15 | handlePhone() { 16 | console.log(111, this.abc) 17 | } 18 | 19 | @Link() abc?: HTMLButtonElement 20 | 21 | render() { 22 | return ( 23 |
24 |
aaa
25 | 33 |
34 | ) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/module/demo/two/index.config.ts: -------------------------------------------------------------------------------- 1 | export default definePageConfig({ 2 | navigationBarBackgroundColor: '#56cea0', 3 | navigationBarTitleText: 'webview', 4 | }) 5 | -------------------------------------------------------------------------------- /src/module/demo/two/index.module.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agileago/vue3-taro/6f59b82f6204fc907730c8512d125466c0f0e9f1/src/module/demo/two/index.module.scss -------------------------------------------------------------------------------- /src/module/demo/two/index.tsx: -------------------------------------------------------------------------------- 1 | import { VueComponent } from 'vue3-oop' 2 | import { WebView } from '@tarojs/components' 3 | 4 | export default class Two extends VueComponent { 5 | render() { 6 | return 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/polyfill.ts: -------------------------------------------------------------------------------- 1 | const obj = { 2 | Array: Array, 3 | Date: Date, 4 | Error: Error, 5 | Function: Function, 6 | Math: Math, 7 | Object: Object, 8 | RegExp: RegExp, 9 | String: String, 10 | TypeError: TypeError, 11 | setTimeout: setTimeout, 12 | clearTimeout: clearTimeout, 13 | setInterval: setInterval, 14 | clearInterval: clearInterval, 15 | } 16 | 17 | // @ts-ignore 18 | Object.assign(global, obj) 19 | 20 | // @ts-ignore 21 | if (typeof window === 'object' && typeof window.global === 'object') { 22 | // @ts-ignore 23 | Object.assign(window.global, obj) 24 | } 25 | export {} 26 | -------------------------------------------------------------------------------- /src/setup/index.ts: -------------------------------------------------------------------------------- 1 | import type { App } from 'vue' 2 | import '@/api/http.interceptor' 3 | 4 | export function setup(app: App) {} 5 | -------------------------------------------------------------------------------- /src/theme/app.scss: -------------------------------------------------------------------------------- 1 | @import 'tailwindcss/base'; 2 | @import 'tailwindcss/components'; 3 | @import 'tailwindcss/utilities'; 4 | 5 | * { 6 | margin: 0; 7 | padding: 0; 8 | box-sizing: border-box; 9 | } 10 | 11 | page, 12 | html, 13 | body { 14 | font-family: -apple-system, 'Noto Sans', 'Helvetica Neue', Helvetica, 'Nimbus Sans L', Arial, 15 | 'Liberation Sans', 'PingFang SC', 'Hiragino Sans GB', 'Noto Sans CJK SC', 'Source Han Sans SC', 16 | 'Source Han Sans CN', 'Microsoft YaHei', 'Wenquanyi Micro Hei', 'WenQuanYi Zen Hei', 'ST Heiti', 17 | SimHei, 'WenQuanYi Zen Hei Sharp', sans-serif; 18 | /* prettier-ignore */ 19 | max-width: 750PX; 20 | margin: 0 auto; 21 | height: 100%; 22 | } 23 | 24 | view, 25 | scroll-view { 26 | box-sizing: border-box; 27 | } 28 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ['./src/**/*.{ts,tsx}'], 4 | theme: { 5 | extend: { 6 | }, 7 | }, 8 | corePlugins: { 9 | preflight: false 10 | }, 11 | plugins: [], 12 | presets: [ 13 | // rem space to px 14 | require('tailwindcss-rem2px-preset'), 15 | ], 16 | } 17 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2021", 4 | "module": "ESNext", 5 | "strict": true, 6 | "preserveConstEnums": true, 7 | "moduleResolution": "node", 8 | "experimentalDecorators": true, 9 | "emitDecoratorMetadata": true, 10 | "useDefineForClassFields": false, 11 | "verbatimModuleSyntax": true, 12 | "skipLibCheck": true, 13 | "skipDefaultLibCheck": true, 14 | "noImplicitAny": false, 15 | "allowSyntheticDefaultImports": true, 16 | "esModuleInterop": true, 17 | "resolveJsonModule": true, 18 | "allowJs": false, 19 | "importHelpers": true, 20 | "outDir": "lib", 21 | "sourceMap": true, 22 | "baseUrl": ".", 23 | "rootDir": ".", 24 | "jsx": "preserve", 25 | "types": ["@tarojs/taro", "webpack-env", "vite/client", "vue/jsx"], 26 | "paths": { 27 | "@/*": ["src/*"] 28 | } 29 | }, 30 | "exclude": ["node_modules", "dist"] 31 | } 32 | --------------------------------------------------------------------------------