Browse Source

小程序脚手架初版提交

刘洋 1 year ago
commit
708f4e3c65

+ 15 - 0
.editorconfig

@@ -0,0 +1,15 @@
+# https://editorconfig.org
+
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 2
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.md]
+insert_final_newline = false
+trim_trailing_whitespace = false

+ 3 - 0
.env

@@ -0,0 +1,3 @@
+# https://cli.vuejs.org/guide/mode-and-env.html#environment-variables
+# 全局环境变量
+

+ 8 - 0
.env.development

@@ -0,0 +1,8 @@
+# https://cli.vuejs.org/guide/mode-and-env.html#modes
+NODE_ENV = development
+
+# 接口地址,注意协议,如果你没有配置 ssl,需要将 https 改为 http
+VUE_APP_BASE_API  = 'http://localhost:8100'
+
+# 分享的前缀
+VUE_APP_SHARE_PREFIX = 'http://127.0.0.1:8101'

+ 8 - 0
.env.production

@@ -0,0 +1,8 @@
+# https://cli.vuejs.org/guide/mode-and-env.html#modes
+NODE_ENV = production
+
+# 接口地址,注意协议,如果你没有配置 ssl,需要将 https 改为 http
+VUE_APP_BASE_API  = ''
+
+# 分享的前缀
+VUE_APP_SHARE_PREFIX = 'http://127.0.0.1:8101'

+ 8 - 0
.env.staging

@@ -0,0 +1,8 @@
+# https://cli.vuejs.org/guide/mode-and-env.html#modes
+NODE_ENV = production
+
+# 接口地址,注意协议,如果你没有配置 ssl,需要将 https 改为 http
+VUE_APP_BASE_API  = ''
+
+# 分享的前缀
+VUE_APP_SHARE_PREFIX = 'http://127.0.0.1:8101'

+ 8 - 0
.env.test

@@ -0,0 +1,8 @@
+# https://cli.vuejs.org/guide/mode-and-env.html#modes
+NODE_ENV = production
+
+# 接口地址,注意协议,如果你没有配置 ssl,需要将 https 改为 http
+VUE_APP_BASE_API  = ''
+
+# 分享的前缀
+VUE_APP_SHARE_PREFIX = 'http://127.0.0.1:8101'

+ 14 - 0
.eslintignore

@@ -0,0 +1,14 @@
+# .eslintignore
+
+public
+dist
+node_modules
+docs
+
+src/static
+src/hybrid
+src/plugins/jsencrypt.js
+
+.automator
+.sourcemap
+.md

+ 37 - 0
.eslintrc.js

@@ -0,0 +1,37 @@
+module.exports = {
+  root: true,
+  env: {
+    node: true,
+    browser: true,
+    es6: true,
+    es2017: true,
+    es2020: true,
+    es2021: true
+  },
+  extends: ['plugin:vue/essential', 'eslint:recommended', 'plugin:prettier/recommended'],
+  parserOptions: {
+    parser: '@babel/eslint-parser',
+    sourceType: 'module', // script或者module
+    ecmaVersion: 'latest', // 6 7 8 9
+    ecmaFeatures: {
+      globalReturn: false, // 允许在全局作用域下使用 return 语句
+      jsx: true, // 启用 jsx
+      impliedStrict: false, // 启用全局 strict mode (如果 ecmaVersion 是 5 或更高)
+      experimentalObjectRestSpread: false // 启用实验性的 object rest/spread properties 支持
+    }
+  },
+  rules: {
+    'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
+    'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
+    'vue/multi-word-component-names': 'off'
+  },
+  // 这是全局变量,找不到引用也可以用
+  globals: {
+    vConsole: true,
+    uni: true,
+    plus: true,
+    wx: true,
+    ROUTES: true,
+    getCurrentPages: true
+  }
+}

+ 26 - 0
.gitignore

@@ -0,0 +1,26 @@
+.DS_Store
+node_modules/
+unpackage/
+dist/
+.hbuilderx/
+
+
+*.lock
+# local env files
+.env.local
+.env.*.local
+
+# Log files
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# Editor directories and files
+.project
+.idea
+.vscode
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw*

+ 38 - 0
.prettierrc.js

@@ -0,0 +1,38 @@
+module.exports = {
+  // 在ES5中有效的结尾逗号(对象,数组等)
+  trailingComma: 'none',
+  // 不使用缩进符,而使用空格
+  useTabs: false,
+  // tab 用两个空格代替
+  tabWidth: 2,
+  // 仅在语法可能出现错误的时候才会添加分号
+  semi: false,
+  // 使用单引号
+  singleQuote: true,
+  // 在Vue文件中缩进脚本和样式标签。
+  vueIndentScriptAndStyle: true,
+  // 一行最多 100 字符
+  printWidth: 150,
+  // 对象的 key 仅在必要时用引号
+  quoteProps: 'as-needed',
+  // jsx 不使用单引号,而使用双引号
+  jsxSingleQuote: false,
+  // 大括号内的首尾需要空格
+  bracketSpacing: true,
+  // jsx 标签的反尖括号需要换行
+  jsxBracketSameLine: false,
+  // 箭头函数,只有一个参数的时候,也需要括号
+  arrowParens: 'always',
+  // 每个文件格式化的范围是文件的全部内容
+  rangeStart: 0,
+  // 不需要写文件开头的 @prettier
+  requirePragma: false,
+  // 不需要自动在文件开头插入 @prettier
+  insertPragma: false,
+  // 使用默认的折行标准
+  proseWrap: 'preserve',
+  // 根据显示样式决定 html 要不要折行
+  htmlWhitespaceSensitivity: 'css',
+  // 换行符使用 lf
+  endOfLine: 'lf'
+}

+ 13 - 0
README.md

@@ -0,0 +1,13 @@
+# 质控平台
+
+使用`uniapp`框架,使用`cli`方式运行, ui 使用`uview: 1.8.8`
+
+`terminal`运行 npm, 国内建议改为 `taobao` 源
+
+```shell
+npm config set registry http://registry.npmmirror.com
+npm i && npm start
+```
+
+`hbuliderx` 中运行问题
+1. 拖入 hbuliderx 中后,需要右键选择"重新识别项目类型"

+ 79 - 0
babel.config.js

@@ -0,0 +1,79 @@
+const webpack = require('webpack')
+const plugins = []
+
+if (process.env.UNI_OPT_TREESHAKINGNG) {
+  plugins.push(require('@dcloudio/vue-cli-plugin-uni-optimize/packages/babel-plugin-uni-api/index.js'))
+}
+
+if (
+  (process.env.UNI_PLATFORM === 'app-plus' && process.env.UNI_USING_V8) ||
+  (process.env.UNI_PLATFORM === 'h5' && process.env.UNI_H5_BROWSER === 'builtin')
+) {
+  const path = require('path')
+
+  const isWin = /^win/.test(process.platform)
+
+  const normalizePath = (path) => (isWin ? path.replace(/\\/g, '/') : path)
+
+  const input = normalizePath(process.env.UNI_INPUT_DIR)
+  try {
+    plugins.push([
+      require('@dcloudio/vue-cli-plugin-hbuilderx/packages/babel-plugin-console'),
+      {
+        file(file) {
+          file = normalizePath(file)
+          if (file.indexOf(input) === 0) {
+            return path.relative(input, file)
+          }
+          return false
+        }
+      }
+    ])
+  } catch (e) {
+    console.error(e)
+  }
+}
+
+process.UNI_LIBRARIES = process.UNI_LIBRARIES || ['@dcloudio/uni-ui']
+process.UNI_LIBRARIES.forEach((libraryName) => {
+  plugins.push([
+    'import',
+    {
+      libraryName: libraryName,
+      customName: (name) => {
+        return `${libraryName}/lib/${name}/${name}`
+      }
+    }
+  ])
+})
+
+if (process.env.UNI_PLATFORM !== 'h5') {
+  plugins.push('@babel/plugin-transform-runtime')
+}
+
+const config = {
+  presets: [
+    [
+      '@vue/app',
+      {
+        modules: webpack.version[0] > 4 ? 'auto' : 'commonjs',
+        useBuiltIns: process.env.UNI_PLATFORM === 'h5' ? 'usage' : 'entry'
+      }
+    ]
+  ],
+  plugins
+}
+
+const UNI_H5_TEST = '**/@dcloudio/uni-h5/dist/index.umd.min.js'
+if (process.env.NODE_ENV === 'production') {
+  config.overrides = [
+    {
+      test: UNI_H5_TEST,
+      compact: true
+    }
+  ]
+} else {
+  config.ignore = [UNI_H5_TEST]
+}
+
+module.exports = config

+ 55 - 0
jsconfig.json

@@ -0,0 +1,55 @@
+{
+  "compilerOptions": {
+    "target": "esnext",
+    "module": "esnext",
+    "moduleResolution": "node",
+    "lib": [
+      "esnext",
+      "es6",
+      "dom",
+      "dom.iterable",
+      "scripthost"
+    ],
+    "useDefineForClassFields": true,
+    "strict": true,
+    "jsx": "preserve",
+    "sourceMap": false,
+    "allowJs": true,
+    "checkJs": true,
+    "resolveJsonModule": true,
+    "esModuleInterop": true,
+    "noImplicitAny": false,
+    "alwaysStrict": true,
+    "skipLibCheck": true,
+    "types": [
+      "@dcloudio/types",
+      "@types/lodash-es",
+      "vite/client",
+      "miniprogram-api-typings",
+      "mini-types"
+    ],
+    "typeRoots": [
+      "types",
+      "node_modules/@types"
+    ],
+    "paths": {
+      "@/*": [
+        "./src/*"
+      ]
+    }
+  },
+  "include": [
+    "src/**/*.ts",
+    "src/**/*.d.ts",
+    "src/**/*.tsx",
+    "src/**/*.vue",
+    "types/**/*.d.ts",
+    "src/**/*.d.ts"
+  ],
+  "exclude": [
+    "node_modules",
+    "test",
+    "dist",
+    ".history"
+  ]
+}

+ 141 - 0
package.json

@@ -0,0 +1,141 @@
+{
+  "name": "uni-app-uview",
+  "version": "1.0.0",
+  "private": true,
+  "scripts": {
+    "start":"npm run serve",
+    "serve": "npm run dev:mp-weixin",
+    "build": "npm run build:mp-weixin",
+    "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .eslintignore",
+    "lint:lint-staged": "lint-staged",
+    "prettier": "prettier --write .",
+    "build:app-plus:test": "cross-env NODE_ENV=production UNI_PLATFORM=app-plus vue-cli-service uni-build --mode test",
+    "build:app-plus:staging": "cross-env NODE_ENV=production UNI_PLATFORM=app-plus vue-cli-service uni-build --mode staging",
+    "build:app-plus:prod": "cross-env NODE_ENV=production UNI_PLATFORM=app-plus vue-cli-service uni-build --mode production",
+    "build:custom": "cross-env NODE_ENV=production uniapp-cli custom",
+    "build:h5:test": "cross-env NODE_ENV=production UNI_PLATFORM=h5 vue-cli-service uni-build --mode test",
+    "build:h5:staging": "cross-env NODE_ENV=production UNI_PLATFORM=h5 vue-cli-service uni-build --mode staging",
+    "build:h5:prod": "cross-env NODE_ENV=production UNI_PLATFORM=h5 vue-cli-service uni-build --mode production",
+    "build:mp-360": "cross-env NODE_ENV=production UNI_PLATFORM=mp-360 vue-cli-service uni-build",
+    "build:mp-alipay": "cross-env NODE_ENV=production UNI_PLATFORM=mp-alipay vue-cli-service uni-build",
+    "build:mp-baidu": "cross-env NODE_ENV=production UNI_PLATFORM=mp-baidu vue-cli-service uni-build",
+    "build:mp-jd": "cross-env NODE_ENV=production UNI_PLATFORM=mp-jd vue-cli-service uni-build",
+    "build:mp-kuaishou": "cross-env NODE_ENV=production UNI_PLATFORM=mp-kuaishou vue-cli-service uni-build",
+    "build:mp-lark": "cross-env NODE_ENV=production UNI_PLATFORM=mp-lark vue-cli-service uni-build",
+    "build:mp-qq": "cross-env NODE_ENV=production UNI_PLATFORM=mp-qq vue-cli-service uni-build",
+    "build:mp-toutiao": "cross-env NODE_ENV=production UNI_PLATFORM=mp-toutiao vue-cli-service uni-build",
+    "build:mp-weixin:test": "cross-env NODE_ENV=production UNI_PLATFORM=mp-weixin vue-cli-service uni-build --mode test",
+    "build:mp-weixin:staging": "cross-env NODE_ENV=production UNI_PLATFORM=mp-weixin vue-cli-service uni-build --mode staging",
+    "build:mp-weixin:prod": "cross-env NODE_ENV=production UNI_PLATFORM=mp-weixin vue-cli-service uni-build --mode production",
+    "build:quickapp-native": "cross-env NODE_ENV=production UNI_PLATFORM=quickapp-native vue-cli-service uni-build",
+    "build:quickapp-webview": "cross-env NODE_ENV=production UNI_PLATFORM=quickapp-webview vue-cli-service uni-build",
+    "build:quickapp-webview-huawei": "cross-env NODE_ENV=production UNI_PLATFORM=quickapp-webview-huawei vue-cli-service uni-build",
+    "build:quickapp-webview-union": "cross-env NODE_ENV=production UNI_PLATFORM=quickapp-webview-union vue-cli-service uni-build",
+    "dev:app-plus": "cross-env NODE_ENV=development UNI_PLATFORM=app-plus vue-cli-service uni-build --watch",
+    "dev:custom": "cross-env NODE_ENV=development uniapp-cli custom",
+    "dev:h5": "cross-env NODE_ENV=development UNI_PLATFORM=h5 vue-cli-service uni-serve --mode development",
+    "dev:mp-360": "cross-env NODE_ENV=development UNI_PLATFORM=mp-360 vue-cli-service uni-build --watch",
+    "dev:mp-alipay": "cross-env NODE_ENV=development UNI_PLATFORM=mp-alipay vue-cli-service uni-build --watch",
+    "dev:mp-baidu": "cross-env NODE_ENV=development UNI_PLATFORM=mp-baidu vue-cli-service uni-build --watch",
+    "dev:mp-jd": "cross-env NODE_ENV=development UNI_PLATFORM=mp-jd vue-cli-service uni-build --watch",
+    "dev:mp-kuaishou": "cross-env NODE_ENV=development UNI_PLATFORM=mp-kuaishou vue-cli-service uni-build --watch",
+    "dev:mp-lark": "cross-env NODE_ENV=development UNI_PLATFORM=mp-lark vue-cli-service uni-build --watch",
+    "dev:mp-qq": "cross-env NODE_ENV=development UNI_PLATFORM=mp-qq vue-cli-service uni-build --watch",
+    "dev:mp-toutiao": "cross-env NODE_ENV=development UNI_PLATFORM=mp-toutiao vue-cli-service uni-build --watch",
+    "dev:mp-weixin": "cross-env NODE_ENV=development UNI_PLATFORM=mp-weixin vue-cli-service uni-build --watch",
+    "dev:quickapp-native": "cross-env NODE_ENV=development UNI_PLATFORM=quickapp-native vue-cli-service uni-build --watch",
+    "dev:quickapp-webview": "cross-env NODE_ENV=development UNI_PLATFORM=quickapp-webview vue-cli-service uni-build --watch",
+    "dev:quickapp-webview-huawei": "cross-env NODE_ENV=development UNI_PLATFORM=quickapp-webview-huawei vue-cli-service uni-build --watch",
+    "dev:quickapp-webview-union": "cross-env NODE_ENV=development UNI_PLATFORM=quickapp-webview-union vue-cli-service uni-build --watch",
+    "info": "node node_modules/@dcloudio/vue-cli-plugin-uni/commands/info.js",
+    "serve:quickapp-native": "node node_modules/@dcloudio/uni-quickapp-native/bin/serve.js",
+    "test:android": "cross-env UNI_PLATFORM=app-plus UNI_OS_NAME=android jest -i",
+    "test:h5": "cross-env UNI_PLATFORM=h5 jest -i",
+    "test:ios": "cross-env UNI_PLATFORM=app-plus UNI_OS_NAME=ios jest -i",
+    "test:mp-baidu": "cross-env UNI_PLATFORM=mp-baidu jest -i",
+    "test:mp-weixin": "cross-env UNI_PLATFORM=mp-weixin jest -i"
+  },
+  "dependencies": {
+    "@dcloudio/uni-app": "2.0.2-3080420230530001",
+    "@dcloudio/uni-app-plus": "2.0.2-3080420230530001",
+    "@dcloudio/uni-h5": "2.0.2-3080420230530001",
+    "@dcloudio/uni-i18n": "2.0.2-3080420230530001",
+    "@dcloudio/uni-mp-360": "2.0.2-3080420230530001",
+    "@dcloudio/uni-mp-alipay": "2.0.2-3080420230530001",
+    "@dcloudio/uni-mp-baidu": "2.0.2-3080420230530001",
+    "@dcloudio/uni-mp-jd": "2.0.2-3080420230530001",
+    "@dcloudio/uni-mp-kuaishou": "2.0.2-3080420230530001",
+    "@dcloudio/uni-mp-lark": "2.0.2-3080420230530001",
+    "@dcloudio/uni-mp-qq": "2.0.2-3080420230530001",
+    "@dcloudio/uni-mp-toutiao": "2.0.2-3080420230530001",
+    "@dcloudio/uni-mp-vue": "2.0.2-3080420230530001",
+    "@dcloudio/uni-mp-weixin": "2.0.2-3080420230530001",
+    "@dcloudio/uni-mp-xhs": "2.0.2-3080420230530001",
+    "@dcloudio/uni-quickapp-native": "2.0.2-3080420230530001",
+    "@dcloudio/uni-quickapp-webview": "2.0.2-3080420230530001",
+    "@dcloudio/uni-stacktracey": "^2.0.2-3080420230530001",
+    "@dcloudio/uni-stat": "2.0.2-3080420230530001",
+    "@vue/shared": "^3.2.34",
+    "better-mock": "^0.3.6",
+    "core-js": "^3.22.5",
+    "crypto-js": "^4.1.1",
+    "flyio": "^0.6.14",
+    "js-cookie": "^3.0.1",
+    "lodash-es": "^4.17.21",
+    "luch-request": "3.0.8",
+    "qs": "^6.10.1",
+    "uni-simple-router": "^2.0.7",
+    "uview-ui": "^1.8.8",
+    "@dcloudio/uni-ui": "^1.4.28",
+    "vue": "^2.6.14",
+    "vuex": "^3.6.2"
+  },
+  "devDependencies": {
+    "@babel/core": "^7.21.4",
+    "@babel/eslint-parser": "^7.21.3",
+    "@dcloudio/types": "^3.3.2",
+    "@dcloudio/uni-automator": "2.0.2-3080420230530001",
+    "@dcloudio/uni-cli-i18n": "2.0.2-3080420230530001",
+    "@dcloudio/uni-cli-shared": "2.0.2-3080420230530001",
+    "@dcloudio/uni-helper-json": "^1.0.13",
+    "@dcloudio/uni-migration": "2.0.2-3080420230530001",
+    "@dcloudio/uni-template-compiler": "2.0.2-3080420230530001",
+    "@dcloudio/vue-cli-plugin-hbuilderx": "2.0.2-3080420230530001",
+    "@dcloudio/vue-cli-plugin-uni": "2.0.2-3080420230530001",
+    "@dcloudio/vue-cli-plugin-uni-optimize": "2.0.2-3080420230530001",
+    "@dcloudio/webpack-uni-mp-loader": "2.0.2-3080420230530001",
+    "@dcloudio/webpack-uni-pages-loader": "2.0.2-3080420230530001",
+    "@vue/cli-plugin-babel": "~5.0.0",
+    "@vue/cli-service": "~5.0.0",
+    "babel-plugin-import": "^1.11.0",
+    "cross-env": "^7.0.2",
+    "eslint": "^8.37.0",
+    "eslint-config-prettier": "^8.8.0",
+    "eslint-plugin-prettier": "^4.2.1",
+    "eslint-plugin-vue": "^9.10.0",
+    "jest": "^25.4.0",
+    "lint-staged": "^13.2.0",
+    "mini-types": "^0.1.7",
+    "miniprogram-api-typings": "^3.4.6",
+    "postcss-comment": "^2.0.0",
+    "prettier": "^2.8.7",
+    "sass": "^1.60.0",
+    "sass-loader": "^8.0.2",
+    "uni-read-pages": "^1.0.5",
+    "vue-template-compiler": ">= 2.6.14 < 2.7"
+  },
+  "browserslist": [
+    "Android >= 4.4",
+    "ios >= 9"
+  ],
+  "lint-staged": {
+    "./src/**/*.{js,jsx,ts,tsx,vue}": [
+      "prettier --write",
+      "eslint --fix",
+      "git add ."
+    ]
+  },
+  "uni-app": {
+    "scripts": {}
+  }
+}

+ 28 - 0
postcss.config.js

@@ -0,0 +1,28 @@
+const path = require('path')
+const webpack = require('webpack')
+const config = {
+  parser: require('postcss-comment'),
+  plugins: [
+    require('postcss-import')({
+      // eslint-disable-next-line
+      resolve(id, basedir, importOptions) {
+        if (id.startsWith('~@/')) {
+          return path.resolve(process.env.UNI_INPUT_DIR, id.substr(3))
+        } else if (id.startsWith('@/')) {
+          return path.resolve(process.env.UNI_INPUT_DIR, id.substr(2))
+        } else if (id.startsWith('/') && !id.startsWith('//')) {
+          return path.resolve(process.env.UNI_INPUT_DIR, id.substr(1))
+        }
+        return id
+      }
+    }),
+    require('autoprefixer')({
+      remove: process.env.UNI_PLATFORM !== 'h5'
+    }),
+    require('@dcloudio/vue-cli-plugin-uni/packages/postcss')
+  ]
+}
+if (webpack.version[0] > 4) {
+  delete config.parser
+}
+module.exports = config

+ 28 - 0
public/index.html

@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+  <head>
+    <meta charset="utf-8" />
+    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+    <title><%= htmlWebpackPlugin.options.title %></title>
+    <script>
+      document.addEventListener('DOMContentLoaded', function () {
+        document.documentElement.style.fontSize = document.documentElement.clientWidth / 20 + 'px'
+      })
+      var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') || CSS.supports('top: constant(a)'))
+      document.write(
+        '<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
+          (coverSupport ? ', viewport-fit=cover' : '') +
+          '" />'
+      )
+    </script>
+    <link rel="stylesheet" href="<%= BASE_URL %>static/index.<%= VUE_APP_INDEX_CSS_HASH %>.css" />
+  </head>
+
+  <body>
+    <noscript>
+      <strong>Please enable JavaScript to continue.</strong>
+    </noscript>
+    <div id="app"></div>
+    <!-- built files will be auto injected -->
+  </body>
+</html>

+ 18 - 0
src/App.vue

@@ -0,0 +1,18 @@
+<script>
+  import { wxUpgrade } from '@/utils/upgrade.js'
+
+  export default {
+    onLaunch() {
+      // #ifdef MP-WEIXIN
+      wxUpgrade()
+      // #endif
+    },
+    onShow() {},
+    methods: {}
+  }
+</script>
+
+<style lang="scss">
+  /* 注意要写在第一行,同时给style标签加入lang="scss"属性 */
+  @import 'uview-ui/index.scss';
+</style>

+ 6 - 0
src/api/user.js

@@ -0,0 +1,6 @@
+import { http } from '@/service/request.js'
+
+/**
+ * 微信小程序模拟登录
+ */
+export const wxMiniAppLogin = (params) => http.post(`/user/wxMiniAppLogin`, params)

+ 42 - 0
src/main.js

@@ -0,0 +1,42 @@
+import Vue from 'vue'
+import App from '@/App'
+import { router, RouterMount } from '@/router/index.js'
+import LoadingPlugin from '@/plugins/loading.js'
+import '@/utils/filter.js'
+import uView from 'uview-ui'
+
+// 此处为演示vuex使用,非uView的功能部分
+import store from '@/store'
+
+// 引入uView对小程序分享的mixin封装
+const mpShare = require('uview-ui/libs/mixin/mpShare.js')
+// 引入uView提供的对vuex的简写法文件
+import vuexStore from '@/store/$u.mixin.js'
+Vue.mixin(vuexStore)
+Vue.mixin(mpShare)
+
+Vue.use(router)
+Vue.use(uView)
+
+Vue.use(LoadingPlugin)
+
+Vue.config.productionTip = false
+
+// 小程序页面组件和这个 App.vue 组件的写法和引入方式是一致的,为了区分两者,需要设置mpType值
+App.mpType = 'app'
+
+const app = new Vue({
+  store,
+  ...App
+})
+
+//v1.3.5起 H5端 你应该去除原有的app.$mount();使用路由自带的渲染方式
+// #ifdef H5
+RouterMount(app, router, '#app')
+// #endif
+
+// #ifndef H5
+app.$mount() //为了兼容小程序及app端必须这样写才有效果
+// #endif
+
+export default app

+ 141 - 0
src/manifest.json

@@ -0,0 +1,141 @@
+{
+  "name": "uView",
+  "appid": "wx3564271a274bd400",
+  "description": "多平台快速开发的UI框架",
+  "versionName": "1.8.5",
+  "versionCode": "100",
+  "transformPx": false,
+  "app-plus": {
+    "optimization": {
+      "subPackages": true
+    },
+    "compatible": {
+      "ignoreVersion": true
+    },
+    "safearea": {
+      "bottom": {
+        "offset": "none"
+      }
+    },
+    "splashscreen": {
+      "alwaysShowBeforeRender": true,
+      "waiting": true,
+      "autoclose": true,
+      "delay": 0
+    },
+    "usingComponents": true,
+    "nvueCompiler": "uni-app",
+    "compilerVersion": 3,
+    "modules": {},
+    "distribute": {
+      "android": {
+        "permissions": [
+          "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
+          "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
+          "<uses-permission android:name=\"android.permission.READ_CONTACTS\"/>",
+          "<uses-permission android:name=\"android.permission.VIBRATE\"/>",
+          "<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
+          "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
+          "<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
+          "<uses-permission android:name=\"android.permission.WRITE_CONTACTS\"/>",
+          "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
+          "<uses-permission android:name=\"android.permission.CAMERA\"/>",
+          "<uses-permission android:name=\"android.permission.RECORD_AUDIO\"/>",
+          "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
+          "<uses-permission android:name=\"android.permission.MODIFY_AUDIO_SETTINGS\"/>",
+          "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
+          "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
+          "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
+          "<uses-permission android:name=\"android.permission.CALL_PHONE\"/>",
+          "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
+          "<uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\"/>",
+          "<uses-feature android:name=\"android.hardware.camera\"/>",
+          "<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\"/>",
+          "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
+        ],
+        "abiFilters": ["armeabi-v7a", "arm64-v8a"]
+      },
+      "ios": {},
+      "sdkConfigs": {
+        "ad": {}
+      },
+      "icons": {
+        "android": {
+          "hdpi": "unpackage/res/icons/72x72.png",
+          "xhdpi": "unpackage/res/icons/96x96.png",
+          "xxhdpi": "unpackage/res/icons/144x144.png",
+          "xxxhdpi": "unpackage/res/icons/192x192.png"
+        },
+        "ios": {
+          "appstore": "unpackage/res/icons/1024x1024.png",
+          "ipad": {
+            "app": "unpackage/res/icons/76x76.png",
+            "app@2x": "unpackage/res/icons/152x152.png",
+            "notification": "unpackage/res/icons/20x20.png",
+            "notification@2x": "unpackage/res/icons/40x40.png",
+            "proapp@2x": "unpackage/res/icons/167x167.png",
+            "settings": "unpackage/res/icons/29x29.png",
+            "settings@2x": "unpackage/res/icons/58x58.png",
+            "spotlight": "unpackage/res/icons/40x40.png",
+            "spotlight@2x": "unpackage/res/icons/80x80.png"
+          },
+          "iphone": {
+            "app@2x": "unpackage/res/icons/120x120.png",
+            "app@3x": "unpackage/res/icons/180x180.png",
+            "notification@2x": "unpackage/res/icons/40x40.png",
+            "notification@3x": "unpackage/res/icons/60x60.png",
+            "settings@2x": "unpackage/res/icons/58x58.png",
+            "settings@3x": "unpackage/res/icons/87x87.png",
+            "spotlight@2x": "unpackage/res/icons/80x80.png",
+            "spotlight@3x": "unpackage/res/icons/120x120.png"
+          }
+        }
+      }
+    }
+  },
+  "quickapp": {},
+  "mp-weixin": {
+    "appid": "wx3564271a274bd400",
+    "setting": {
+      "urlCheck": true,
+      "es6": true,
+      "minified": true,
+      "postcss": false
+    },
+    "optimization": {
+      "subPackages": true
+    },
+    "usingComponents": true
+  },
+  "mp-alipay": {
+    "usingComponents": true,
+    "component2": true
+  },
+  "mp-qq": {
+    "optimization": {
+      "subPackages": true
+    },
+    "appid": "15646153"
+  },
+  "mp-baidu": {
+    "usingComponents": true,
+    "appid": "17597421"
+  },
+  "mp-toutiao": {
+    "usingComponents": true,
+    "appid": ""
+  },
+  "h5": {
+    "template": "template.h5.html",
+    "router": {
+      "mode": "history",
+      "base": "./"
+    },
+    "optimization": {
+      "treeShaking": {
+        "enable": false
+      }
+    },
+    "title": "uView UI"
+  }
+}

+ 78 - 0
src/pages.json

@@ -0,0 +1,78 @@
+{
+  "easycom": {
+    "^u-(.*)": "uview-ui/components/u-$1/u-$1.vue",
+    "^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue"
+  },
+  "condition": {
+    //模式配置,仅开发期间生效
+    "current": 0,
+    //当前激活的模式(list 的索引项)
+    "list": [
+      {
+        "name": "test",
+        //模式名称
+        "path": "",
+        //启动页面,必选
+        "query": "uuid=c4bba940-f69e-11ea-a419-6bafda9d095e&__id__=1"
+        //启动参数,在页面的onLoad函数里面得到
+      }
+    ]
+  },
+  "pages": [
+    {
+      "path": "pages/index/index",
+      "style": {
+        "navigationBarTitleText": "首页",
+        "enablePullDownRefresh": false
+      }
+    },
+    {
+      "path": "pages/home/home",
+      "style": {
+        "navigationBarTitleText": "我的",
+        "enablePullDownRefresh": false
+      }
+    }
+  ],
+  "subPackages": [
+    {
+      "root": "pages/login",
+      "pages": [
+        {
+          "path": "login",
+          "style": {
+            "navigationBarTitleText": "登录",
+            "enablePullDownRefresh": false
+          }
+        }
+      ]
+    }
+  ],
+  "preloadRule": {},
+  "globalStyle": {
+    "navigationBarTextStyle": "black",
+    "navigationBarTitleText": "uView",
+    "navigationBarBackgroundColor": "#FFFFFF",
+    "backgroundColor": "#FFFFFF"
+  },
+  "tabBar": {
+    "color": "#909399",
+    "selectedColor": "#303133",
+    "backgroundColor": "#FFFFFF",
+    "borderStyle": "black",
+    "list": [
+      {
+        "pagePath": "pages/index/index",
+        "iconPath": "static/icon/tab/icon_home.png",
+        "selectedIconPath": "static/icon/tab/icon_home_c.png",
+        "text": "首页"
+      },
+      {
+        "pagePath": "pages/home/home",
+        "iconPath": "static/icon/tab/icon_wode.png",
+        "selectedIconPath": "static/icon/tab/icon_wode_c.png",
+        "text": "我的"
+      }
+    ]
+  }
+}

+ 16 - 0
src/pages/404/index.vue

@@ -0,0 +1,16 @@
+<template>
+  <view> 找不到该页面! </view>
+</template>
+
+<script>
+  export default {
+    name: 'Page404',
+    data() {
+      return {}
+    },
+    created() {},
+    methods: {}
+  }
+</script>
+
+<style></style>

+ 16 - 0
src/pages/home/home.vue

@@ -0,0 +1,16 @@
+<template>
+  <view> 我的页面 </view>
+</template>
+
+<script>
+  export default {
+    name: 'HomePage',
+    data() {
+      return {}
+    },
+    created() {},
+    methods: {}
+  }
+</script>
+
+<style></style>

+ 37 - 0
src/pages/index/index.vue

@@ -0,0 +1,37 @@
+<template>
+  <view>
+    <u-button @click="doRouter('/pages/login/login')"> 去登录页</u-button>
+  </view>
+</template>
+
+<script>
+  export default {
+    name: 'IndexPage',
+
+    data() {
+      return {}
+    },
+    created() {},
+    mounted() {
+      this.$u.vuex('vuex_token', '123')
+    },
+    onShow() {},
+    methods: {
+      /**
+       * 做路由跳转
+       * @param url
+       * @param params
+       */
+      doRouter(url) {
+        // 路由跳转
+        this.$Router.push({ path: url, query: { name: 'sop' } })
+        // 在onLoad中使用this.$Route.query获取参数
+      },
+      jumpToSwitchTab(url) {
+        this.$Router.pushTab({ path: url, query: { name: 'sop' } })
+      }
+    }
+  }
+</script>
+
+<style></style>

+ 95 - 0
src/pages/login/login.vue

@@ -0,0 +1,95 @@
+<template>
+  <view>
+    <!-- 微信登陆 -->
+    <view class="login-button">
+      <view key="2" class="wxbuton">
+        <u-button type="success" shape="circle" hover-class="none" open-type="getPhoneNumber" @getphonenumber="toWxLogin">
+          <text class="login-wxfont">微信用户一键登录</text>
+        </u-button>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script>
+  import { getWxLoginCode, login } from '@/utils/login'
+
+  export default {
+    name: 'LoginPage',
+    data() {
+      return {
+        // 微信登录需要
+        wxCode: null
+      }
+    },
+    onLoad() {
+      // 如果是微信的话,就去获取登录的wxcode
+    },
+    async onShow() {
+      try {
+        // #ifdef MP-WEIXIN
+        await this.getWxCode()
+        // #endif
+      } catch (e) {
+        this.$u.toast(e)
+      }
+    },
+    methods: {
+      /**
+       * 路由跳转
+       * @param url /
+       * @param query /
+       */
+      doRouter(url, query) {
+        // 路由跳转
+        this.$Router.push({ path: url, query: query })
+      },
+      /**
+       * 获取微信授权code
+       * @returns {Promise<void>}
+       */
+      async getWxCode() {
+        this.wxCode = await getWxLoginCode()
+      },
+      /**
+       * 微信登录
+       */
+      async toWxLogin(wxLoginInfo) {
+        const params = {
+          wxCode: this.wxCode,
+          ...wxLoginInfo.detail
+        }
+        await this.toLogin(params)
+      },
+      /**
+       * 去首页
+       */
+      toIndexPage() {
+        setTimeout(() => {
+          uni.switchTab({
+            url: '/pages/tabbar/index'
+          })
+        }, 800)
+      },
+      /**
+       * 去登录
+       * @param user 登录用户信息
+       * @returns {Promise<void>}
+       */
+      async toLogin(user) {
+        const loginSuccess = await login(user)
+        if (loginSuccess) {
+          // 登录成功跳转路径
+          setTimeout(() => {
+            this.$u.toast('登录成功')
+          }, 800)
+        } else {
+          // 登录失败,对应事件
+          await this.getWxCode()
+        }
+      }
+    }
+  }
+</script>
+
+<style lang="scss" scoped></style>

+ 14 - 0
src/plugins/loading.js

@@ -0,0 +1,14 @@
+export default {
+  install(Vue, options) {
+    Vue.prototype.$showLoading = function (title = '加载中') {
+      uni.showLoading({
+        title: title,
+        mask: true
+      })
+    }
+
+    Vue.prototype.$hideLoading = function () {
+      uni.hideLoading()
+    }
+  }
+}

+ 47 - 0
src/router/index.js

@@ -0,0 +1,47 @@
+
+import { RouterMount, createRouter } from 'uni-simple-router'
+import permission from './permission'
+const router = createRouter({
+  platform: process.env.VUE_APP_PLATFORM,
+  routes: [
+    ...ROUTES,
+    {
+      path: '*',
+      // eslint-disable-next-line
+      redirect: (to) => {
+        return { path: 'pages/404/index' }
+      }
+    }
+  ],
+  debugger: false
+  // 前置路由函数 每次触发跳转前先会触发此函数,你可以在这里面做写什么。可能有、动画、记录等等操作
+  // routerBeforeEach: (to, from, next) => {
+  //   // console.log('***routerBeforeEach***')
+  //   next()
+  // }
+  // // 后置路由函数 每次触发跳转后会触发此函数。
+  // routerAfterEach: (to, from) => {
+  //   // console.log('***routerAfterEach***')
+  // }
+  // 每次跳转失败时都会触发此方法并传递失败原因,详细信息如下:
+  // routerErrorEach: (error, router) => {
+  //   // console.log('***routerErrorEach***')
+  //   // err(error, router, true)
+  // }
+  // detectBeforeLock: (router, to, navType) => {
+  //   // console.log('***detectBeforeLock***')
+  // }
+  // resolveQuery: (jsonQuery) => {
+  //   // console.log('***resolveQuery***')
+  //   jsonQuery
+  // }
+  // 读取参数之前把参数传递给此函数,返回最终的数据!有此函数则不走默认方法。
+  // parseQuery: (jsonQuery) => {
+  //   // console.log('***parseQuery***')
+  //   jsonQuery
+  // }
+})
+
+permission(router)
+
+export { router, RouterMount }

+ 56 - 0
src/router/permission.js

@@ -0,0 +1,56 @@
+import store from '@/store'
+
+export default (router) => {
+  // 路由白名单,可以考虑不同环境配置不同白名单
+  const whiteList = ['/pages/index/index', '/pages/home/home', '/pages/login/login'] // no redirect whitelist
+
+  // 全局路由前置守卫
+  router.beforeEach(async (to, from, next) => {
+    const token = store.state.vuex_access_token
+    const userId = store.state.vuex_user_info
+
+    if (token) {
+      if (to.path === '/pages/login/login') {
+        next()
+      } else if (!userId) {
+        try {
+          // await store.dispatch('user/getUserData')
+          next()
+        } catch (error) {
+          // await store.dispatch('user/logout')
+          next({
+            path: '/pages/login/index',
+            query: {
+              redirect: JSON.stringify({
+                path: to.path,
+                query: to.query
+              })
+            }
+          })
+        }
+      } else {
+        next()
+      }
+    } else if (whiteList.indexOf(to.path) !== -1) {
+      // 在免登录白名单,直接进入
+      next()
+    } else {
+      next({
+        path: '/pages/account/login/index',
+        query: {
+          redirectView: JSON.stringify({
+            path: to.path,
+            query: to.query
+          })
+        }
+      })
+    }
+  })
+
+  // 全局路由后置守卫
+  // eslint-disable-next-line
+  router.afterEach((to, from) => {
+    // console.log('router.afterEach.to', to)
+    // console.log('router.afterEach.from', from)
+  })
+}

+ 97 - 0
src/service/request.js

@@ -0,0 +1,97 @@
+/**
+ * 文档: https://www.quanzhan.co/luch-request/
+ * github: https://github.com/lei-mu/luch-request
+ * DCloud: http://ext.dcloud.net.cn/plugin?id=392
+ */
+
+import Request from 'luch-request'
+import Vue from '@/main'
+const getTokenStorage = () => {
+  let token = ''
+  try {
+    token = uni.getStorageSync('token')
+  } catch (e) {
+    console.log(e)
+  }
+  return token
+}
+
+const http = new Request()
+http.setConfig((config) => {
+  /* 设置全局配置 */
+  config.baseURL = process.env.VUE_APP_BASE_API
+  config.header = {
+    ...config.header,
+    a: 1, // 演示
+    b: 2 // 演示
+  }
+  return config
+})
+
+http.interceptors.request.use(
+  (config) => {
+    /* 请求之前拦截器。可以使用async await 做异步操作 */
+    config.header = {
+      ...config.header,
+      // token: getTokenStorage()
+      Authorization: `Bearer ${Vue.vuex_token}`
+    }
+    /*
+       if (!token) { // 如果token不存在,return Promise.reject(config) 会取消本次请求
+         return Promise.reject(config)
+       }
+       */
+    /**
+     * custom {Object} - 自定义参数
+     */
+    // if (config.custom.auth) {
+    //   config.header.token = '123456'
+    // }
+    // if (config.custom.loading) {
+    //   uni.showLoading()
+    // }
+    return config
+  },
+  (config) => {
+    return Promise.reject(config)
+  }
+)
+
+http.interceptors.response.use(
+  async (response) => {
+    // if (response.config.custom.loading) {
+    //    uni.hideLoading()
+    //  }
+    // if (response.data.code !== 200) { // 服务端返回的状态码不等于200,则reject()
+    //   return Promise.reject(response)
+    // }
+    console.log('response', response)
+    if (response.statusCode === 200) {
+      return response.data
+    }
+    if (response.statusCode === 401) {
+      Vue.$u.vuex('vuex_token', '')
+      Vue.$u.vuex('vuex_user_info', {})
+      Vue.$u.toast('登陆过期')
+      Vue.$u.route({
+        url: 'pages/login/login'
+      })
+    } else if (response.statusCode === 500) {
+      if (response.data.message) {
+        Vue.$u.toast(response.data.message)
+      } else {
+        Vue.$u.toast('请求异常')
+      }
+    }
+    return response
+  },
+  (err) => {
+    console.log(err)
+    // if (response.config.custom.loading) {
+    //    uni.hideLoading()
+    //  }
+    return Promise.reject(err)
+  }
+)
+
+export { http }

BIN
src/static/icon/favicon.ico


BIN
src/static/icon/logo.png


BIN
src/static/icon/tab/icon_home.png


BIN
src/static/icon/tab/icon_home_c.png


BIN
src/static/icon/tab/icon_wode.png


BIN
src/static/icon/tab/icon_wode_c.png


+ 28 - 0
src/store/$u.mixin.js

@@ -0,0 +1,28 @@
+import { mapState } from 'vuex'
+import store from '@/store'
+
+// 尝试将用户在根目录中的store/index.js的vuex的state变量,全部加载到全局变量中
+let $uStoreKey = []
+try {
+  $uStoreKey = store.state ? Object.keys(store.state) : []
+} catch (e) {
+  console.error(e)
+}
+
+export default {
+  beforeCreate() {
+    // 将vuex方法挂在到$u中
+    // 使用方法为:如果要修改vuex的state中的user.name变量为"史诗" => this.$u.vuex('user.name', '史诗')
+    // 如果要修改vuex的state的version变量为1.0.1 => this.$u.vuex('version', '1.0.1')
+    this.$u.vuex = (name, value) => {
+      this.$store.commit('$uStore', {
+        name,
+        value
+      })
+    }
+  },
+  computed: {
+    // 将vuex的state中的所有变量,解构到全局混入的mixin中
+    ...mapState($uStoreKey)
+  }
+}

+ 61 - 0
src/store/index.js

@@ -0,0 +1,61 @@
+import Vue from 'vue'
+import Vuex from 'vuex'
+
+Vue.use(Vuex)
+
+const files = require.context('./modules', false, /\.js$/)
+
+const modules = {
+  state: {},
+  mutations: {},
+  actions: {}
+}
+
+// 需要永久存储,且下次APP启动需要取出的,在state中的变量名
+const saveStateKeys = ['vuex_user_info', 'vuex_token']
+
+// 保存变量到本地存储中
+const saveLifeData = function (key, value) {
+  // 判断变量名是否在需要存储的数组中
+  if (saveStateKeys.indexOf(key) !== -1) {
+    // 获取本地存储的lifeData对象,将变量添加到对象中
+    let tmp = uni.getStorageSync('lifeData')
+    // 第一次打开APP,不存在lifeData变量,故放一个{}空对象
+    tmp = tmp || {}
+    tmp[key] = value
+    // 执行这一步后,所有需要存储的变量,都挂载在本地的lifeData对象中
+    uni.setStorageSync('lifeData', tmp)
+  }
+}
+
+const mutations = {
+  $uStore(state, payload) {
+    // 判断是否多层级调用,state中为对象存在的情况,诸如user.info.score = 1
+    const nameArr = payload.name.split('.')
+    let saveKey = ''
+    const len = nameArr.length
+    if (len >= 2) {
+      let obj = state[nameArr[0]]
+      for (let i = 1; i < len - 1; i++) {
+        obj = obj[nameArr[i]]
+      }
+      obj[nameArr[len - 1]] = payload.value
+      saveKey = nameArr[0]
+    } else {
+      state[payload.name] = payload.value
+      saveKey = payload.name
+    }
+    // 保存变量到本地,见顶部函数定义
+    saveLifeData(saveKey, state[saveKey])
+  }
+}
+
+files.keys().forEach((key) => {
+  Object.assign(modules.state, files(key).state)
+  // 因为做了代理 这一步要做处理
+  Object.assign(modules.mutations, mutations)
+  Object.assign(modules.actions, files(key).actions)
+})
+
+const store = new Vuex.Store(modules)
+export default store

+ 14 - 0
src/store/modules/user.js

@@ -0,0 +1,14 @@
+const lifeData = uni.getStorageSync('lifeData')
+
+export const state = {
+  // 如果上面从本地获取的lifeData对象下有对应的属性,就赋值给state中对应的变量
+  // 加上vuex_前缀,是防止变量名冲突,也让人一目了然
+  vuex_user_info: lifeData.vuex_user_info ? lifeData.vuex_user_info : { id: '888' },
+  vuex_token: lifeData.vuex_refresh_token ? lifeData.vuex_refresh_token : '',
+  // 自定义tabbar数据
+  vuex_tabbar: []
+}
+
+export const mutations = {}
+
+export const actions = {}

+ 79 - 0
src/uni.scss

@@ -0,0 +1,79 @@
+
+/**
+ * 这里是uni-app内置的常用样式变量
+ *
+ * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量
+ * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App
+ *
+ */
+
+/**
+ * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能
+ *
+ * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
+ */
+
+/* 颜色变量 */
+
+/* 行为相关颜色 */
+$uni-color-primary: #007aff;
+$uni-color-success: #4cd964;
+$uni-color-warning: #f0ad4e;
+$uni-color-error: #dd524d;
+
+/* 文字基本颜色 */
+$uni-text-color: #333; // 基本色
+$uni-text-color-inverse: #fff; // 反色
+$uni-text-color-grey: #999; // 辅助灰色,如加载更多的提示信息
+$uni-text-color-placeholder: #808080;
+$uni-text-color-disable: #c0c0c0;
+
+/* 背景颜色 */
+$uni-bg-color: #fff;
+$uni-bg-color-grey: #f8f8f8;
+$uni-bg-color-hover: #f1f1f1; // 点击状态颜色
+$uni-bg-color-mask: rgba(0, 0, 0, 0.4); // 遮罩颜色
+
+/* 边框颜色 */
+$uni-border-color: #c8c7cc;
+
+/* 尺寸变量 */
+
+/* 文字尺寸 */
+$uni-font-size-sm: 12px;
+$uni-font-size-base: 14px;
+$uni-font-size-lg: 16;
+
+/* 图片尺寸 */
+$uni-img-size-sm: 20px;
+$uni-img-size-base: 26px;
+$uni-img-size-lg: 40px;
+
+/* Border Radius */
+$uni-border-radius-sm: 2px;
+$uni-border-radius-base: 3px;
+$uni-border-radius-lg: 6px;
+$uni-border-radius-circle: 50%;
+
+/* 水平间距 */
+$uni-spacing-row-sm: 5px;
+$uni-spacing-row-base: 10px;
+$uni-spacing-row-lg: 15px;
+
+/* 垂直间距 */
+$uni-spacing-col-sm: 4px;
+$uni-spacing-col-base: 8px;
+$uni-spacing-col-lg: 12px;
+
+/* 透明度 */
+$uni-opacity-disabled: 0.3; // 组件禁用态的透明度
+
+/* 文章场景相关 */
+$uni-color-title: #2c405a; // 文章标题颜色
+$uni-font-size-title: 20px;
+$uni-color-subtitle: #555; // 二级标题颜色
+$uni-font-size-subtitle: 18px;
+$uni-color-paragraph: #3f536e; // 文章段落颜色
+$uni-font-size-paragraph: 15px;
+
+@import 'uview-ui/theme.scss';

+ 65 - 0
src/utils/filter.js

@@ -0,0 +1,65 @@
+import Vue from 'vue'
+
+/**
+ * 时间戳转换为想要的时间格式
+ */
+//时间戳转换为时间 format('yyyy-MM-dd hh:mm:ss')
+//时间格式转换
+Date.prototype.format = function (fmt = 'yyyy-MM-dd hh:mm:ss') {
+  //author: meizz
+  var o = {
+    'M+': this.getMonth() + 1, //月份
+    'd+': this.getDate(), //日
+    'h+': this.getHours(), //小时
+    'm+': this.getMinutes(), //分
+    's+': this.getSeconds(), //秒
+    'q+': Math.floor((this.getMonth() + 3) / 3), //季度
+    S: this.getMilliseconds() //毫秒
+  }
+  if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + '').substr(4 - RegExp.$1.length))
+  for (var k in o)
+    if (new RegExp('(' + k + ')').test(fmt)) fmt = fmt.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length))
+  return fmt
+}
+//金额过滤
+Vue.filter('money', function (val) {
+  console.log(val)
+  if (val) {
+    let value = Math.round(parseFloat(val) * 100) / 100
+    let valMoney = value.toString().split('.')
+    if (valMoney.length == 1) {
+      value = value.toString() + '.00'
+      return value
+    }
+    if (valMoney.length > 1) {
+      if (valMoney[1].length < 2) {
+        value = value.toString() + '0'
+      }
+      return value
+    }
+    return value
+  } else {
+    return '0.00'
+  }
+})
+//时间距离现在多少天前
+Vue.filter('dateDiff', function (val) {
+  if (val) {
+    return clickDateDiff(val)
+  } else {
+    return ''
+  }
+})
+// 时间格式化
+Vue.filter('timeFormat', function (val, fmt = 'yyyy-MM-dd hh:mm:ss') {
+  if (val) {
+    if (typeof val == 'string') {
+      let timeText = val.replace(/-/g, '/')
+      return new Date(timeText).format(fmt)
+    } else if (typeof val == 'number') {
+      return new Date(val).format(fmt)
+    }
+  } else {
+    return ''
+  }
+})

+ 58 - 0
src/utils/login.js

@@ -0,0 +1,58 @@
+import { wxMiniAppLogin } from '@/api/user'
+import Vue from 'vue'
+/**
+ * 获取微信登录的code
+ * @returns {Promise<unknown>}
+ */
+export const getWxLoginCode = () => {
+  return new Promise((resolve, reject) => {
+    uni.login({
+      provider: 'weixin',
+      success: (res) => {
+        if (res?.code) {
+          resolve(res.code)
+        } else {
+          reject(res.errMsg)
+        }
+      },
+      fail: (e) => {
+        reject(e)
+      }
+    })
+  })
+}
+
+/**
+ * 登录
+ * @param params 登录入参
+ * @param type 0 微信一键登录 1 密码登录 2 手机验证码登录
+ * @returns {Promise<boolean>}
+ */
+export const login = async (params) => {
+  try {
+    let result = await wxMiniAppLogin(params)
+    //模拟登录接口返回
+    if (result.code === '00000') {
+      const data = result?.data
+      // save token
+      saveToken(data)
+    } else {
+      this.$u.toast(result.message)
+      return false
+    }
+    return true
+  } catch (e) {
+    console.error(e)
+    return false
+  }
+}
+
+/**
+ * 保存token
+ */
+export const saveToken = (data) => {
+  //模拟后台字段,后期修改
+  Vue.$u.vuex('vuex_token', data.token)
+  Vue.$u.vuex('vuex_user_info', data.user_info)
+  Vue.$u.toast('登录成功!')
+}

+ 33 - 0
src/utils/upgrade.js

@@ -0,0 +1,33 @@
+/**
+ * 微信平台需要更新
+ */
+export const wxUpgrade = () => {
+  const updateManager = uni.getUpdateManager()
+  updateManager.onCheckForUpdate((res) => {
+    console.log(res)
+  })
+
+  updateManager.onUpdateReady((res) => {
+    console.log(res)
+    uni.showModal({
+      title: '更新提示',
+      content: '新版本已经准备好,是否重启应用?',
+      success(res) {
+        if (res.confirm) {
+          // 新的版本已经下载好,调用 applyUpdate 应用新版本并重启
+          updateManager.applyUpdate()
+        }
+      }
+    })
+  })
+
+  updateManager.onUpdateFailed((res) => {
+    console.log(res)
+    // 新的版本下载失败
+    uni.showModal({
+      title: '提示',
+      content: '新版小程序下载失败\n请自行退出程序,手动卸载本程序,再运行',
+      confirmText: '知道了'
+    })
+  })
+}

+ 494 - 0
src/utils/utils.js

@@ -0,0 +1,494 @@
+import Vue from 'vue'
+function tip(data) {
+  setTimeout(() => {
+    uni.showToast(data)
+  }, 500)
+}
+
+// 身份证格式校验
+export const checkIdCard = function (sIdCard) {
+  //Wi 加权因子 Xi 余数0~10对应的校验码 Pi省份代码
+  let Wi = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2],
+    Xi = [1, 0, 'X', 9, 8, 7, 6, 5, 4, 3, 2],
+    Pi = [11, 12, 13, 14, 15, 21, 22, 23, 31, 32, 33, 34, 35, 36, 37, 41, 42, 43, 44, 45, 46, 50, 51, 52, 53, 54, 61, 62, 63, 64, 65, 71, 81, 82, 91],
+    checkStatus = 0
+  // 检查身份证长度
+  if (sIdCard.length == 18) {
+    checkStatus += 1
+  }
+  //检验输入的省份编码是否有效
+  if (checkStatus >= 1) {
+    let p2 = sIdCard.substr(0, 2)
+    for (let i = 0; i < Pi.length; i++) {
+      if (Pi[i] == p2) {
+        checkStatus += 1
+      }
+    }
+  }
+  //检验18位身份证号码出生日期是否有效
+  //parseFloat过滤前导零,年份必需大于等于1900且小于等于当前年份,用Date()对象判断日期是否有效。
+  if (checkStatus >= 2) {
+    let year = parseFloat(sIdCard.substr(6, 4))
+    let month = parseFloat(sIdCard.substr(10, 2))
+    let day = parseFloat(sIdCard.substr(12, 2))
+    let checkDay = new Date(year, month - 1, day)
+    let nowDay = new Date()
+    if (1900 <= year && year <= nowDay.getFullYear() && month == checkDay.getMonth() + 1 && day == checkDay.getDate()) {
+      checkStatus += 1
+    }
+  }
+  //检验校验码是否有效
+  if (checkStatus >= 3) {
+    let aIdCard = sIdCard.split('')
+    let sum = 0
+    for (let j = 0; j < Wi.length; j++) {
+      sum += Wi[j] * aIdCard[j] //线性加权求和
+    }
+    let index = sum % 11 //求模,可能为0~10,可求对应的校验码是否于身份证的校验码匹配
+    if (Xi[index] == aIdCard[17].toUpperCase()) {
+      checkStatus += 1
+    }
+  }
+  if (checkStatus == 4) {
+    return true
+  } else {
+    return false
+  }
+}
+/**
+ * 时间转换为XX前
+ */
+export const clickDateDiff = function (value) {
+  let dataValue = value
+  if (typeof value == 'string') {
+    dataValue = new Date(value.replace(/-/g, '/')).getTime()
+  }
+  var result
+  var minute = 1000 * 60
+  var hour = minute * 60
+  var day = hour * 24
+  var month = day * 30
+  var now = new Date().getTime()
+  var diffValue = parseInt(now) - parseInt(dataValue)
+  if (diffValue < 0) {
+    return
+  }
+  var monthC = diffValue / month
+  var weekC = diffValue / (7 * day)
+  var dayC = diffValue / day
+  var hourC = diffValue / hour
+  var minC = diffValue / minute
+  if (monthC >= 1) {
+    result = '' + parseInt(monthC) + '月前'
+  } else if (weekC >= 1) {
+    result = '' + parseInt(weekC) + '周前'
+  } else if (dayC >= 1) {
+    result = '' + parseInt(dayC) + '天前'
+  } else if (hourC >= 1) {
+    result = '' + parseInt(hourC) + '小时前'
+  } else if (minC >= 1) {
+    result = '' + parseInt(minC) + '分钟前'
+  } else {
+    result = '刚刚'
+  }
+  return result
+}
+
+// 保存图片
+let settingWritePhotosAlbum = false
+export const saveImg = function (url, callback) {
+  if (url) {
+    // #ifdef MP-WEIXIN
+    if (settingWritePhotosAlbum) {
+      uni.getSetting({
+        success: (res) => {
+          if (res.authSetting['scope.writePhotosAlbum']) {
+            uni.showLoading({
+              title: '正在下载'
+            })
+            uni.downloadFile({
+              url: url,
+              success: (data) => {
+                if (data.statusCode == 200) {
+                  uni.saveImageToPhotosAlbum({
+                    filePath: data.tempFilePath,
+                    success: () => {
+                      uni.hideLoading()
+                      callback && callback()
+                      uni.showToast({
+                        title: '保存成功'
+                      })
+                    },
+                    fail(e) {
+                      uni.hideLoading()
+                      tip({
+                        title: '下载失败,错误原因:' + e.errMsg,
+                        icon: 'none'
+                      })
+                    }
+                  })
+                } else {
+                  uni.hideLoading()
+                  uni.showToast({
+                    title: '下载失败',
+                    icon: 'none'
+                  })
+                }
+              },
+              fail(e) {
+                uni.hideLoading()
+                uni.showToast({
+                  title: '下载失败,错误原因:' + e.errMsg,
+                  icon: 'none'
+                })
+              }
+            })
+          } else {
+            uni.showModal({
+              title: '提示',
+              content: '请先在设置页面打开“保存相册”使用权限',
+              confirmText: '去设置',
+              cancelText: '算了',
+              success: (data) => {
+                if (data.confirm) {
+                  uni.openSetting()
+                }
+              }
+            })
+          }
+        }
+      })
+    } else {
+      settingWritePhotosAlbum = true
+      uni.authorize({
+        scope: 'scope.writePhotosAlbum',
+        success: () => {
+          uni.showLoading({
+            title: '正在下载'
+          })
+          uni.downloadFile({
+            url: url,
+            success: (data) => {
+              if (data.statusCode == 200) {
+                uni.saveImageToPhotosAlbum({
+                  filePath: data.tempFilePath,
+                  success: () => {
+                    uni.hideLoading()
+                    callback && callback()
+                    uni.showToast({
+                      title: '保存成功'
+                    })
+                  },
+                  fail(e) {
+                    uni.hideLoading()
+                    tip({
+                      title: '下载失败,错误原因:' + e.errMsg,
+                      icon: 'none'
+                    })
+                  }
+                })
+              } else {
+                uni.hideLoading()
+                uni.showToast({
+                  title: '下载失败',
+                  icon: 'none'
+                })
+              }
+            },
+            fail(e) {
+              uni.hideLoading()
+              uni.showToast({
+                title: '下载失败,错误原因:' + e.errMsg,
+                icon: 'none'
+              })
+            }
+          })
+        }
+      })
+    }
+    // #endif
+    // #ifdef H5
+    uni.showLoading({
+      title: '正在下载'
+    })
+    uni.downloadFile({
+      url: url,
+      success: (data) => {
+        uni.hideLoading()
+        if (data.statusCode == 200) {
+          callback && callback()
+          window.open(data.tempFilePath)
+        } else {
+          uni.showToast({
+            title: '下载失败',
+            icon: 'none'
+          })
+        }
+      },
+      fail(e) {
+        uni.hideLoading()
+        uni.showToast({
+          title: '下载失败,错误原因:' + e.errMsg,
+          icon: 'none'
+        })
+      }
+    })
+    // #endif
+    // #ifdef APP-PLUS
+    uni.showLoading({
+      title: '正在下载'
+    })
+    uni.saveImageToPhotosAlbum({
+      filePath: url,
+      success: () => {
+        uni.hideLoading()
+        callback && callback()
+        uni.showToast({
+          title: '保存成功'
+        })
+      },
+      fail(e) {
+        uni.hideLoading()
+        uni.showToast({
+          title: '下载失败,错误原因:' + e.errMsg,
+          icon: 'none'
+        })
+      }
+    })
+    // #endif
+  } else {
+    uni.showToast({
+      title: '未找到图片',
+      icon: 'none'
+    })
+  }
+}
+// 保存视频
+
+export const saveVideo = function (url, callback) {
+  if (url) {
+    // #ifdef MP-WEIXIN
+    if (settingWritePhotosAlbum) {
+      uni.getSetting({
+        success: (res) => {
+          if (res.authSetting['scope.writePhotosAlbum']) {
+            // let urlArr = url.split("/");
+            // let updateUrl = urlArr[urlArr.length - 1];
+            // let  filePath = wx.env.USER_DATA_PATH + '/' + updateUrl;
+            uni.showLoading({
+              title: '正在下载'
+            })
+            uni.downloadFile({
+              url: url,
+              // filePath: filePath,
+              success: (data) => {
+                if (data.statusCode == 200) {
+                  uni.saveVideoToPhotosAlbum({
+                    filePath: data.tempFilePath,
+                    success: () => {
+                      uni.hideLoading()
+                      callback && callback()
+                      tip({
+                        title: '保存成功'
+                      })
+                    },
+                    fail(e) {
+                      uni.hideLoading()
+                      tip({
+                        title: '下载失败,错误原因:' + e.errMsg,
+                        icon: 'none'
+                      })
+                    }
+                  })
+                } else {
+                  uni.hideLoading()
+                  tip({
+                    title: '下载失败',
+                    icon: 'none'
+                  })
+                }
+              },
+              fail(e) {
+                uni.hideLoading()
+                tip({
+                  title: '下载失败,错误原因:' + e.errMsg,
+                  icon: 'none'
+                })
+              }
+            })
+          } else {
+            uni.showModal({
+              title: '提示',
+              content: '请先在设置页面打开“保存相册”使用权限',
+              confirmText: '去设置',
+              cancelText: '算了',
+              success: (data) => {
+                if (data.confirm) {
+                  uni.openSetting()
+                }
+              }
+            })
+          }
+        }
+      })
+    } else {
+      settingWritePhotosAlbum = true
+      uni.authorize({
+        scope: 'scope.writePhotosAlbum',
+        success: () => {
+          // let urlArr = url.split("/");
+          // let updateUrl = urlArr[urlArr.length - 1];
+          // let filePath = wx.env.USER_DATA_PATH + '/' + updateUrl;
+          uni.showLoading({
+            title: '正在下载'
+          })
+          uni.downloadFile({
+            url: url,
+            // filePath: filePath,
+            success: (data) => {
+              if (data.statusCode == 200) {
+                uni.saveVideoToPhotosAlbum({
+                  filePath: data.tempFilePath,
+                  success: () => {
+                    uni.hideLoading()
+                    callback && callback()
+                    tip({
+                      title: '保存成功'
+                    })
+                  },
+                  fail(e) {
+                    uni.hideLoading()
+                    tip({
+                      title: '下载失败,错误原因:' + e.errMsg,
+                      icon: 'none'
+                    })
+                  }
+                })
+              } else {
+                uni.hideLoading()
+                tip({
+                  title: '下载失败,错误原因:' + data.errMsg,
+                  icon: 'none'
+                })
+              }
+            },
+            fail(e) {
+              uni.hideLoading()
+              tip({
+                title: '下载失败,错误原因:' + e.errMsg,
+                icon: 'none'
+              })
+            }
+          })
+        }
+      })
+    }
+    // #endif
+    // #ifdef H5
+    uni.showLoading({
+      title: '正在下载'
+    })
+    uni.downloadFile({
+      url: url,
+      success: (data) => {
+        uni.hideLoading()
+        if (data.statusCode == 200) {
+          callback && callback()
+          window.open(data.tempFilePath)
+        } else {
+          tip({
+            title: '下载失败',
+            icon: 'none'
+          })
+        }
+      },
+      fail(e) {
+        uni.hideLoading()
+        tip({
+          title: '下载失败,错误原因:' + e.errMsg,
+          icon: 'none'
+        })
+      }
+    })
+    // #endif
+    // #ifdef APP-PLUS
+    uni.showLoading({
+      title: '正在下载'
+    })
+    uni.saveVideoToPhotosAlbum({
+      filePath: url,
+      success: () => {
+        uni.hideLoading()
+        callback && callback()
+        tip({
+          title: '保存成功'
+        })
+      },
+      fail(e) {
+        uni.hideLoading()
+        tip({
+          title: '下载失败,错误原因:' + e.errMsg,
+          icon: 'none'
+        })
+      }
+    })
+    // #endif
+  } else {
+    tip({
+      title: '未找到视频',
+      icon: 'none'
+    })
+  }
+}
+// 微信小程序获取定位权限判断
+function wxAppletsLocation(successCallback, errCallback) {
+  uni.getSetting({
+    success: (res) => {
+      if (res.authSetting['scope.userLocation']) {
+        uni.getLocation({
+          type: 'gcj02',
+          success: (res) => {
+            successCallback(res)
+          },
+          fail: (err) => {
+            console.log('位置信息错误', err)
+            errCallback('位置信息获取失败')
+          }
+        })
+      } else {
+        errCallback('“位置信息”未授权')
+        uni.showModal({
+          title: '提示',
+          content: '请先在设置页面打开“位置信息”使用权限',
+          confirmText: '去设置',
+          cancelText: '再逛会',
+          success: (res) => {
+            if (res.confirm) {
+              uni.openSetting()
+            }
+          }
+        })
+      }
+    }
+  })
+}
+// 获取地址信息
+let locationAuthorize = true
+export const getAppWxLatLon = function (successCallback, errCallback) {
+  // #ifdef MP
+  if (locationAuthorize) {
+    uni.authorize({
+      scope: 'scope.userLocation',
+      success: () => {
+        wxAppletsLocation(successCallback, errCallback)
+        locationAuthorize = false
+      },
+      fail: () => {
+        locationAuthorize = false
+      }
+    })
+  } else {
+    wxAppletsLocation(successCallback, errCallback)
+  }
+  // #endif
+}

+ 19 - 0
vue.config.js

@@ -0,0 +1,19 @@
+const TransformPages = require('uni-read-pages')
+const { webpack } = new TransformPages()
+module.exports = {
+  // productionSourceMap: false,
+  lintOnSave: process.env.NODE_ENV === 'development',
+  transpileDependencies: ['uview-ui', 'luch-request', '@dcloudio/uni-ui'], // 需要babel-loader转义的第三方依赖, 指定需要编译的依赖
+  configureWebpack: {
+    plugins: [
+      new webpack.DefinePlugin({
+        ROUTES: webpack.DefinePlugin.runtimeValue(() => {
+          const tfPages = new TransformPages({
+            includes: ['path', 'name', 'meta', 'aliasPath']
+          })
+          return JSON.stringify(tfPages.routes)
+        }, true)
+      })
+    ]
+  }
+}