wenningning 3 years ago
commit
6f38c88981
92 changed files with 41776 additions and 0 deletions
  1. 23 0
      .gitignore
  2. 19 0
      README.md
  3. 63 0
      babel.config.js
  4. 15863 0
      package-lock.json
  5. 100 0
      package.json
  6. 22 0
      postcss.config.js
  7. 28 0
      public/index.html
  8. 419 0
      public/tinymce/langs/zh_CN.js
  9. 59 0
      public/tinymce/skins/content/dark/content.css
  10. 7 0
      public/tinymce/skins/content/dark/content.min.css
  11. 49 0
      public/tinymce/skins/content/default/content.css
  12. 7 0
      public/tinymce/skins/content/default/content.min.css
  13. 53 0
      public/tinymce/skins/content/document/content.css
  14. 7 0
      public/tinymce/skins/content/document/content.min.css
  15. 50 0
      public/tinymce/skins/content/writer/content.css
  16. 7 0
      public/tinymce/skins/content/writer/content.min.css
  17. 600 0
      public/tinymce/skins/ui/oxide-dark/content.css
  18. 612 0
      public/tinymce/skins/ui/oxide-dark/content.inline.css
  19. 7 0
      public/tinymce/skins/ui/oxide-dark/content.inline.min.css
  20. 7 0
      public/tinymce/skins/ui/oxide-dark/content.min.css
  21. 29 0
      public/tinymce/skins/ui/oxide-dark/content.mobile.css
  22. 7 0
      public/tinymce/skins/ui/oxide-dark/content.mobile.min.css
  23. BIN
      public/tinymce/skins/ui/oxide-dark/fonts/tinymce-mobile.woff
  24. 2884 0
      public/tinymce/skins/ui/oxide-dark/skin.css
  25. 7 0
      public/tinymce/skins/ui/oxide-dark/skin.min.css
  26. 673 0
      public/tinymce/skins/ui/oxide-dark/skin.mobile.css
  27. 7 0
      public/tinymce/skins/ui/oxide-dark/skin.mobile.min.css
  28. 618 0
      public/tinymce/skins/ui/oxide/content.css
  29. 612 0
      public/tinymce/skins/ui/oxide/content.inline.css
  30. 7 0
      public/tinymce/skins/ui/oxide/content.inline.min.css
  31. 7 0
      public/tinymce/skins/ui/oxide/content.min.css
  32. 29 0
      public/tinymce/skins/ui/oxide/content.mobile.css
  33. 7 0
      public/tinymce/skins/ui/oxide/content.mobile.min.css
  34. BIN
      public/tinymce/skins/ui/oxide/fonts/tinymce-mobile.woff
  35. 2884 0
      public/tinymce/skins/ui/oxide/skin.css
  36. 7 0
      public/tinymce/skins/ui/oxide/skin.min.css
  37. 673 0
      public/tinymce/skins/ui/oxide/skin.mobile.css
  38. 7 0
      public/tinymce/skins/ui/oxide/skin.mobile.min.css
  39. 230 0
      src/App.vue
  40. 21 0
      src/LICENSE
  41. 14 0
      src/README.md
  42. 184 0
      src/colorui/animation.css
  43. 69 0
      src/colorui/components/cu-custom.vue
  44. 1226 0
      src/colorui/icon.css
  45. 3912 0
      src/colorui/main.css
  46. 97 0
      src/common/graceChecker.js
  47. 352 0
      src/common/html-parser.js
  48. 0 0
      src/common/ning
  49. 245 0
      src/common/permission.js
  50. 120 0
      src/common/uni-nvue.css
  51. 1448 0
      src/common/uni.css
  52. 73 0
      src/common/util.js
  53. 0 0
      src/components/tki-qrcode/my-qrcode.vue
  54. 1201 0
      src/components/tki-qrcode/qrcode.js
  55. 212 0
      src/components/tki-qrcode/tki-qrcode.vue
  56. 546 0
      src/components/uni-calendar/calendar.js
  57. 152 0
      src/components/uni-calendar/uni-calendar-item.vue
  58. 434 0
      src/components/uni-calendar/uni-calendar.vue
  59. 327 0
      src/components/uni-calendar/util.js
  60. 88 0
      src/hybrid/html/local.html
  61. 21 0
      src/main.js
  62. 130 0
      src/manifest.json
  63. 15 0
      src/package.json
  64. 86 0
      src/pages.json
  65. 311 0
      src/pages/index/QsCode.vue
  66. 755 0
      src/pages/index/aike.vue
  67. 208 0
      src/pages/index/bloodPressure.vue
  68. 76 0
      src/pages/index/drug.vue
  69. 15 0
      src/pages/index/external_page.vue
  70. 220 0
      src/pages/index/heartRate.vue
  71. 533 0
      src/pages/index/home.vue
  72. 254 0
      src/pages/index/index.vue
  73. 188 0
      src/pages/index/myCode.vue
  74. 85 0
      src/pages/index/test.vue
  75. 514 0
      src/pages/index/test1.vue
  76. 87 0
      src/pages/index/wifi_page.vue
  77. 202 0
      src/platforms/app-plus/feedback/feedback.vue
  78. 61 0
      src/platforms/app-plus/orientation/orientation.vue
  79. 69 0
      src/platforms/app-plus/proximity/proximity.vue
  80. 120 0
      src/platforms/app-plus/push/push.vue
  81. 91 0
      src/platforms/app-plus/shake/shake.vue
  82. 105 0
      src/platforms/app-plus/speech/speech.vue
  83. BIN
      src/static/bg.png
  84. BIN
      src/static/doctor.png
  85. BIN
      src/static/logo.png
  86. BIN
      src/static/uni.ttf
  87. 73 0
      src/store/index.js
  88. 53 0
      src/template.h5.html
  89. 76 0
      src/uni.scss
  90. 6 0
      src/utils/bluetest.js
  91. 2 0
      src/utils/main.js
  92. 9 0
      tsconfig.json

+ 23 - 0
.gitignore

@@ -0,0 +1,23 @@
+.DS_Store
+node_modules/
+unpackage/
+dist/
+
+# 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*

+ 19 - 0
README.md

@@ -0,0 +1,19 @@
+# my-project
+
+## Project setup
+```
+npm install
+```
+
+### Compiles and hot-reloads for development
+```
+npm run serve
+```
+
+### Compiles and minifies for production
+```
+npm run build
+```
+
+### Customize configuration
+See [Configuration Reference](https://cli.vuejs.org/config/).

+ 63 - 0
babel.config.js

@@ -0,0 +1,63 @@
+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) {}
+}
+
+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}`
+      }
+    }
+  ])
+})
+module.exports = {
+  presets: [
+    [
+      '@vue/app',
+      {
+        modules: 'commonjs',
+        useBuiltIns: process.env.UNI_PLATFORM === 'h5' ? 'usage' : 'entry'
+      }
+    ]
+  ],
+  plugins
+}

File diff suppressed because it is too large
+ 15863 - 0
package-lock.json


+ 100 - 0
package.json

@@ -0,0 +1,100 @@
+{
+  "name": "my-project",
+  "version": "0.1.0",
+  "private": true,
+  "scripts": {
+    "serve": "npm run dev:h5",
+    "build": "npm run build:h5",
+    "build:app-plus": "cross-env NODE_ENV=production UNI_PLATFORM=app-plus vue-cli-service uni-build",
+    "build:custom": "cross-env NODE_ENV=production uniapp-cli custom",
+    "build:h5": "cross-env NODE_ENV=production UNI_PLATFORM=h5 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-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": "cross-env NODE_ENV=production UNI_PLATFORM=mp-weixin vue-cli-service uni-build",
+    "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",
+    "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",
+    "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-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",
+    "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-plus": "^2.0.0-27520200518001",
+    "@dcloudio/uni-h5": "^2.0.0-27520200518001",
+    "@dcloudio/uni-helper-json": "*",
+    "@dcloudio/uni-mp-alipay": "^2.0.0-27520200518001",
+    "@dcloudio/uni-mp-baidu": "^2.0.0-27520200518001",
+    "@dcloudio/uni-mp-qq": "^2.0.0-27520200518001",
+    "@dcloudio/uni-mp-toutiao": "^2.0.0-27520200518001",
+    "@dcloudio/uni-mp-weixin": "^2.0.0-27520200518001",
+    "@dcloudio/uni-quickapp-native": "^2.0.0-27520200518001",
+    "@dcloudio/uni-quickapp-webview": "^2.0.0-27520200518001",
+    "@dcloudio/uni-stat": "^2.0.0-27520200518001",
+    "@tinymce/tinymce-vue": "^3.2.2",
+    "@uni-plugs/uni-code": "^1.3.0",
+    "@uni-ui/code-plugs": "^1.0.0",
+    "@uni-ui/code-ui": "^1.0.3",
+    "@uni-ui/nvue-canvas-plugs": "^1.0.0",
+    "core-js": "^3.6.4",
+    "flyio": "^0.6.2",
+    "jscomp": "^1.0.1",
+    "less-loader": "^5.0.0",
+    "node-sass": "^4.14.1",
+    "regenerator-runtime": "^0.12.1",
+    "sass-loader": "^9.0.2",
+    "tinymce": "^5.4.1",
+    "vant": "^2.8.7",
+    "vue": "^2.6.11",
+    "vuex": "^3.2.0"
+  },
+  "devDependencies": {
+    "@dcloudio/types": "*",
+    "@dcloudio/uni-automator": "^2.0.0-27520200518001",
+    "@dcloudio/uni-cli-shared": "^2.0.0-27520200518001",
+    "@dcloudio/uni-migration": "^2.0.0-27520200518001",
+    "@dcloudio/uni-template-compiler": "^2.0.0-27520200518001",
+    "@dcloudio/vue-cli-plugin-hbuilderx": "^2.0.0-27520200518001",
+    "@dcloudio/vue-cli-plugin-uni": "^2.0.0-27520200518001",
+    "@dcloudio/vue-cli-plugin-uni-optimize": "^2.0.0-27520200518001",
+    "@dcloudio/webpack-uni-mp-loader": "^2.0.0-27520200518001",
+    "@dcloudio/webpack-uni-pages-loader": "^2.0.0-27520200518001",
+    "@vue/cli-plugin-babel": "~4.3.0",
+    "@vue/cli-service": "~4.3.0",
+    "babel-plugin-import": "^1.13.0",
+    "cross-env": "^7.0.2",
+    "jest": "^25.4.0",
+    "less": "^4.1.1",
+    "mini-types": "*",
+    "miniprogram-api-typings": "*",
+    "postcss-comment": "^2.0.0",
+    "sass-loader": "^9.0.2",
+    "vue-template-compiler": "^2.6.11"
+  },
+  "browserslist": [
+    "Android >= 4",
+    "ios >= 8"
+  ],
+  "uni-app": {
+    "scripts": {}
+  },
+  "description": "## Project setup ``` npm install ```",
+  "main": "babel.config.js",
+  "keywords": [],
+  "author": "",
+  "license": "ISC"
+}

+ 22 - 0
postcss.config.js

@@ -0,0 +1,22 @@
+const path = require('path')
+module.exports = {
+  parser: require('postcss-comment'),
+  plugins: [
+    require('postcss-import')({
+      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')
+  ]
+}

+ 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>

+ 419 - 0
public/tinymce/langs/zh_CN.js

@@ -0,0 +1,419 @@
+tinymce.addI18n('zh_CN',{
+"Redo": "\u91cd\u505a",
+"Undo": "\u64a4\u9500",
+"Cut": "\u526a\u5207",
+"Copy": "\u590d\u5236",
+"Paste": "\u7c98\u8d34",
+"Select all": "\u5168\u9009",
+"New document": "\u65b0\u6587\u4ef6",
+"Ok": "\u786e\u5b9a",
+"Cancel": "\u53d6\u6d88",
+"Visual aids": "\u7f51\u683c\u7ebf",
+"Bold": "\u7c97\u4f53",
+"Italic": "\u659c\u4f53",
+"Underline": "\u4e0b\u5212\u7ebf",
+"Strikethrough": "\u5220\u9664\u7ebf",
+"Superscript": "\u4e0a\u6807",
+"Subscript": "\u4e0b\u6807",
+"Clear formatting": "\u6e05\u9664\u683c\u5f0f",
+"Align left": "\u5de6\u8fb9\u5bf9\u9f50",
+"Align center": "\u4e2d\u95f4\u5bf9\u9f50",
+"Align right": "\u53f3\u8fb9\u5bf9\u9f50",
+"Justify": "\u4e24\u7aef\u5bf9\u9f50",
+"Bullet list": "\u9879\u76ee\u7b26\u53f7",
+"Numbered list": "\u7f16\u53f7\u5217\u8868",
+"Decrease indent": "\u51cf\u5c11\u7f29\u8fdb",
+"Increase indent": "\u589e\u52a0\u7f29\u8fdb",
+"Close": "\u5173\u95ed",
+"Formats": "\u683c\u5f0f",
+"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "\u4f60\u7684\u6d4f\u89c8\u5668\u4e0d\u652f\u6301\u6253\u5f00\u526a\u8d34\u677f\uff0c\u8bf7\u4f7f\u7528Ctrl+X\/C\/V\u7b49\u5feb\u6377\u952e\u3002",
+"Headers": "\u6807\u9898",
+"Header 1": "\u6807\u98981",
+"Header 2": "\u6807\u98982",
+"Header 3": "\u6807\u98983",
+"Header 4": "\u6807\u98984",
+"Header 5": "\u6807\u98985",
+"Header 6": "\u6807\u98986",
+"Headings": "\u6807\u9898",
+"Heading 1": "\u6807\u98981",
+"Heading 2": "\u6807\u98982",
+"Heading 3": "\u6807\u98983",
+"Heading 4": "\u6807\u98984",
+"Heading 5": "\u6807\u98985",
+"Heading 6": "\u6807\u98986",
+"Preformatted": "\u9884\u5148\u683c\u5f0f\u5316\u7684",
+"Div": "Div",
+"Pre": "Pre",
+"Code": "\u4ee3\u7801",
+"Paragraph": "\u6bb5\u843d",
+"Blockquote": "\u5f15\u6587\u533a\u5757",
+"Inline": "\u6587\u672c",
+"Blocks": "\u57fa\u5757",
+"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "\u5f53\u524d\u4e3a\u7eaf\u6587\u672c\u7c98\u8d34\u6a21\u5f0f\uff0c\u518d\u6b21\u70b9\u51fb\u53ef\u4ee5\u56de\u5230\u666e\u901a\u7c98\u8d34\u6a21\u5f0f\u3002",
+"Fonts": "\u5b57\u4f53",
+"Font Sizes": "\u5b57\u53f7",
+"Class": "\u7c7b\u578b",
+"Browse for an image": "\u6d4f\u89c8\u56fe\u50cf",
+"OR": "\u6216",
+"Drop an image here": "\u62d6\u653e\u4e00\u5f20\u56fe\u50cf\u81f3\u6b64",
+"Upload": "\u4e0a\u4f20",
+"Block": "\u5757",
+"Align": "\u5bf9\u9f50",
+"Default": "\u9ed8\u8ba4",
+"Circle": "\u7a7a\u5fc3\u5706",
+"Disc": "\u5b9e\u5fc3\u5706",
+"Square": "\u65b9\u5757",
+"Lower Alpha": "\u5c0f\u5199\u82f1\u6587\u5b57\u6bcd",
+"Lower Greek": "\u5c0f\u5199\u5e0c\u814a\u5b57\u6bcd",
+"Lower Roman": "\u5c0f\u5199\u7f57\u9a6c\u5b57\u6bcd",
+"Upper Alpha": "\u5927\u5199\u82f1\u6587\u5b57\u6bcd",
+"Upper Roman": "\u5927\u5199\u7f57\u9a6c\u5b57\u6bcd",
+"Anchor...": "\u951a\u70b9...",
+"Name": "\u540d\u79f0",
+"Id": "\u6807\u8bc6\u7b26",
+"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "\u6807\u8bc6\u7b26\u5e94\u8be5\u4ee5\u5b57\u6bcd\u5f00\u5934\uff0c\u540e\u8ddf\u5b57\u6bcd\u3001\u6570\u5b57\u3001\u7834\u6298\u53f7\u3001\u70b9\u3001\u5192\u53f7\u6216\u4e0b\u5212\u7ebf\u3002",
+"You have unsaved changes are you sure you want to navigate away?": "\u4f60\u8fd8\u6709\u6587\u6863\u5c1a\u672a\u4fdd\u5b58\uff0c\u786e\u5b9a\u8981\u79bb\u5f00\uff1f",
+"Restore last draft": "\u6062\u590d\u4e0a\u6b21\u7684\u8349\u7a3f",
+"Special character...": "\u7279\u6b8a\u5b57\u7b26...",
+"Source code": "\u6e90\u4ee3\u7801",
+"Insert\/Edit code sample": "\u63d2\u5165\/\u7f16\u8f91\u4ee3\u7801\u793a\u4f8b",
+"Language": "\u8bed\u8a00",
+"Code sample...": "\u793a\u4f8b\u4ee3\u7801...",
+"Color Picker": "\u9009\u8272\u5668",
+"R": "R",
+"G": "G",
+"B": "B",
+"Left to right": "\u4ece\u5de6\u5230\u53f3",
+"Right to left": "\u4ece\u53f3\u5230\u5de6",
+"Emoticons...": "\u8868\u60c5\u7b26\u53f7...",
+"Metadata and Document Properties": "\u5143\u6570\u636e\u548c\u6587\u6863\u5c5e\u6027",
+"Title": "\u6807\u9898",
+"Keywords": "\u5173\u952e\u8bcd",
+"Description": "\u63cf\u8ff0",
+"Robots": "\u673a\u5668\u4eba",
+"Author": "\u4f5c\u8005",
+"Encoding": "\u7f16\u7801",
+"Fullscreen": "\u5168\u5c4f",
+"Action": "\u64cd\u4f5c",
+"Shortcut": "\u5feb\u6377\u952e",
+"Help": "\u5e2e\u52a9",
+"Address": "\u5730\u5740",
+"Focus to menubar": "\u79fb\u52a8\u7126\u70b9\u5230\u83dc\u5355\u680f",
+"Focus to toolbar": "\u79fb\u52a8\u7126\u70b9\u5230\u5de5\u5177\u680f",
+"Focus to element path": "\u79fb\u52a8\u7126\u70b9\u5230\u5143\u7d20\u8def\u5f84",
+"Focus to contextual toolbar": "\u79fb\u52a8\u7126\u70b9\u5230\u4e0a\u4e0b\u6587\u83dc\u5355",
+"Insert link (if link plugin activated)": "\u63d2\u5165\u94fe\u63a5 (\u5982\u679c\u94fe\u63a5\u63d2\u4ef6\u5df2\u6fc0\u6d3b)",
+"Save (if save plugin activated)": "\u4fdd\u5b58(\u5982\u679c\u4fdd\u5b58\u63d2\u4ef6\u5df2\u6fc0\u6d3b)",
+"Find (if searchreplace plugin activated)": "\u67e5\u627e(\u5982\u679c\u67e5\u627e\u66ff\u6362\u63d2\u4ef6\u5df2\u6fc0\u6d3b)",
+"Plugins installed ({0}):": "\u5df2\u5b89\u88c5\u63d2\u4ef6 ({0}):",
+"Premium plugins:": "\u4f18\u79c0\u63d2\u4ef6\uff1a",
+"Learn more...": "\u4e86\u89e3\u66f4\u591a...",
+"You are using {0}": "\u4f60\u6b63\u5728\u4f7f\u7528 {0}",
+"Plugins": "\u63d2\u4ef6",
+"Handy Shortcuts": "\u5feb\u6377\u952e",
+"Horizontal line": "\u6c34\u5e73\u5206\u5272\u7ebf",
+"Insert\/edit image": "\u63d2\u5165\/\u7f16\u8f91\u56fe\u7247",
+"Image description": "\u56fe\u7247\u63cf\u8ff0",
+"Source": "\u5730\u5740",
+"Dimensions": "\u5927\u5c0f",
+"Constrain proportions": "\u4fdd\u6301\u7eb5\u6a2a\u6bd4",
+"General": "\u666e\u901a",
+"Advanced": "\u9ad8\u7ea7",
+"Style": "\u6837\u5f0f",
+"Vertical space": "\u5782\u76f4\u8fb9\u8ddd",
+"Horizontal space": "\u6c34\u5e73\u8fb9\u8ddd",
+"Border": "\u8fb9\u6846",
+"Insert image": "\u63d2\u5165\u56fe\u7247",
+"Image...": "\u56fe\u7247...",
+"Image list": "\u56fe\u7247\u5217\u8868",
+"Rotate counterclockwise": "\u9006\u65f6\u9488\u65cb\u8f6c",
+"Rotate clockwise": "\u987a\u65f6\u9488\u65cb\u8f6c",
+"Flip vertically": "\u5782\u76f4\u7ffb\u8f6c",
+"Flip horizontally": "\u6c34\u5e73\u7ffb\u8f6c",
+"Edit image": "\u7f16\u8f91\u56fe\u7247",
+"Image options": "\u56fe\u7247\u9009\u9879",
+"Zoom in": "\u653e\u5927",
+"Zoom out": "\u7f29\u5c0f",
+"Crop": "\u88c1\u526a",
+"Resize": "\u8c03\u6574\u5927\u5c0f",
+"Orientation": "\u65b9\u5411",
+"Brightness": "\u4eae\u5ea6",
+"Sharpen": "\u9510\u5316",
+"Contrast": "\u5bf9\u6bd4\u5ea6",
+"Color levels": "\u989c\u8272\u5c42\u6b21",
+"Gamma": "\u4f3d\u9a6c\u503c",
+"Invert": "\u53cd\u8f6c",
+"Apply": "\u5e94\u7528",
+"Back": "\u540e\u9000",
+"Insert date\/time": "\u63d2\u5165\u65e5\u671f\/\u65f6\u95f4",
+"Date\/time": "\u65e5\u671f\/\u65f6\u95f4",
+"Insert\/Edit Link": "\u63d2\u5165\/\u7f16\u8f91\u94fe\u63a5",
+"Insert\/edit link": "\u63d2\u5165\/\u7f16\u8f91\u94fe\u63a5",
+"Text to display": "\u663e\u793a\u6587\u5b57",
+"Url": "\u5730\u5740",
+"Open link in...": "\u94fe\u63a5\u6253\u5f00\u4f4d\u7f6e...",
+"Current window": "\u5f53\u524d\u7a97\u53e3",
+"None": "\u65e0",
+"New window": "\u5728\u65b0\u7a97\u53e3\u6253\u5f00",
+"Remove link": "\u5220\u9664\u94fe\u63a5",
+"Anchors": "\u951a\u70b9",
+"Link...": "\u94fe\u63a5...",
+"Paste or type a link": "\u7c98\u8d34\u6216\u8f93\u5165\u94fe\u63a5",
+"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "\u4f60\u6240\u586b\u5199\u7684URL\u5730\u5740\u4e3a\u90ae\u4ef6\u5730\u5740\uff0c\u9700\u8981\u52a0\u4e0amailto:\u524d\u7f00\u5417\uff1f",
+"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "\u4f60\u6240\u586b\u5199\u7684URL\u5730\u5740\u5c5e\u4e8e\u5916\u90e8\u94fe\u63a5\uff0c\u9700\u8981\u52a0\u4e0ahttp:\/\/:\u524d\u7f00\u5417\uff1f",
+"Link list": "\u94fe\u63a5\u5217\u8868",
+"Insert video": "\u63d2\u5165\u89c6\u9891",
+"Insert\/edit video": "\u63d2\u5165\/\u7f16\u8f91\u89c6\u9891",
+"Insert\/edit media": "\u63d2\u5165\/\u7f16\u8f91\u5a92\u4f53",
+"Alternative source": "\u955c\u50cf",
+"Alternative source URL": "\u66ff\u4ee3\u6765\u6e90\u7f51\u5740",
+"Media poster (Image URL)": "\u5c01\u9762(\u56fe\u7247\u5730\u5740)",
+"Paste your embed code below:": "\u5c06\u5185\u5d4c\u4ee3\u7801\u7c98\u8d34\u5728\u4e0b\u9762:",
+"Embed": "\u5185\u5d4c",
+"Media...": "\u591a\u5a92\u4f53...",
+"Nonbreaking space": "\u4e0d\u95f4\u65ad\u7a7a\u683c",
+"Page break": "\u5206\u9875\u7b26",
+"Paste as text": "\u7c98\u8d34\u4e3a\u6587\u672c",
+"Preview": "\u9884\u89c8",
+"Print...": "\u6253\u5370...",
+"Save": "\u4fdd\u5b58",
+"Find": "\u67e5\u627e",
+"Replace with": "\u66ff\u6362\u4e3a",
+"Replace": "\u66ff\u6362",
+"Replace all": "\u5168\u90e8\u66ff\u6362",
+"Previous": "\u4e0a\u4e00\u4e2a",
+"Next": "\u4e0b\u4e00\u4e2a",
+"Find and replace...": "\u67e5\u627e\u5e76\u66ff\u6362...",
+"Could not find the specified string.": "\u672a\u627e\u5230\u641c\u7d22\u5185\u5bb9.",
+"Match case": "\u533a\u5206\u5927\u5c0f\u5199",
+"Find whole words only": "\u5168\u5b57\u5339\u914d",
+"Spell check": "\u62fc\u5199\u68c0\u67e5",
+"Ignore": "\u5ffd\u7565",
+"Ignore all": "\u5168\u90e8\u5ffd\u7565",
+"Finish": "\u5b8c\u6210",
+"Add to Dictionary": "\u6dfb\u52a0\u5230\u5b57\u5178",
+"Insert table": "\u63d2\u5165\u8868\u683c",
+"Table properties": "\u8868\u683c\u5c5e\u6027",
+"Delete table": "\u5220\u9664\u8868\u683c",
+"Cell": "\u5355\u5143\u683c",
+"Row": "\u884c",
+"Column": "\u5217",
+"Cell properties": "\u5355\u5143\u683c\u5c5e\u6027",
+"Merge cells": "\u5408\u5e76\u5355\u5143\u683c",
+"Split cell": "\u62c6\u5206\u5355\u5143\u683c",
+"Insert row before": "\u5728\u4e0a\u65b9\u63d2\u5165",
+"Insert row after": "\u5728\u4e0b\u65b9\u63d2\u5165",
+"Delete row": "\u5220\u9664\u884c",
+"Row properties": "\u884c\u5c5e\u6027",
+"Cut row": "\u526a\u5207\u884c",
+"Copy row": "\u590d\u5236\u884c",
+"Paste row before": "\u7c98\u8d34\u5230\u4e0a\u65b9",
+"Paste row after": "\u7c98\u8d34\u5230\u4e0b\u65b9",
+"Insert column before": "\u5728\u5de6\u4fa7\u63d2\u5165",
+"Insert column after": "\u5728\u53f3\u4fa7\u63d2\u5165",
+"Delete column": "\u5220\u9664\u5217",
+"Cols": "\u5217",
+"Rows": "\u884c",
+"Width": "\u5bbd",
+"Height": "\u9ad8",
+"Cell spacing": "\u5355\u5143\u683c\u5916\u95f4\u8ddd",
+"Cell padding": "\u5355\u5143\u683c\u5185\u8fb9\u8ddd",
+"Show caption": "\u663e\u793a\u6807\u9898",
+"Left": "\u5de6\u5bf9\u9f50",
+"Center": "\u5c45\u4e2d",
+"Right": "\u53f3\u5bf9\u9f50",
+"Cell type": "\u5355\u5143\u683c\u7c7b\u578b",
+"Scope": "\u8303\u56f4",
+"Alignment": "\u5bf9\u9f50\u65b9\u5f0f",
+"H Align": "\u6c34\u5e73\u5bf9\u9f50",
+"V Align": "\u5782\u76f4\u5bf9\u9f50",
+"Top": "\u9876\u90e8\u5bf9\u9f50",
+"Middle": "\u5782\u76f4\u5c45\u4e2d",
+"Bottom": "\u5e95\u90e8\u5bf9\u9f50",
+"Header cell": "\u8868\u5934\u5355\u5143\u683c",
+"Row group": "\u884c\u7ec4",
+"Column group": "\u5217\u7ec4",
+"Row type": "\u884c\u7c7b\u578b",
+"Header": "\u8868\u5934",
+"Body": "\u8868\u4f53",
+"Footer": "\u8868\u5c3e",
+"Border color": "\u8fb9\u6846\u989c\u8272",
+"Insert template...": "\u63d2\u5165\u6a21\u677f...",
+"Templates": "\u6a21\u677f",
+"Template": "\u6a21\u677f",
+"Text color": "\u6587\u5b57\u989c\u8272",
+"Background color": "\u80cc\u666f\u8272",
+"Custom...": "\u81ea\u5b9a\u4e49...",
+"Custom color": "\u81ea\u5b9a\u4e49\u989c\u8272",
+"No color": "\u65e0",
+"Remove color": "\u79fb\u9664\u989c\u8272",
+"Table of Contents": "\u5185\u5bb9\u5217\u8868",
+"Show blocks": "\u663e\u793a\u533a\u5757\u8fb9\u6846",
+"Show invisible characters": "\u663e\u793a\u4e0d\u53ef\u89c1\u5b57\u7b26",
+"Word count": "\u5b57\u6570",
+"Count": "\u8ba1\u6570",
+"Document": "\u6587\u6863",
+"Selection": "\u9009\u62e9",
+"Words": "\u5355\u8bcd",
+"Words: {0}": "\u5b57\u6570\uff1a{0}",
+"{0} words": "{0} \u5b57",
+"File": "\u6587\u4ef6",
+"Edit": "\u7f16\u8f91",
+"Insert": "\u63d2\u5165",
+"View": "\u89c6\u56fe",
+"Format": "\u683c\u5f0f",
+"Table": "\u8868\u683c",
+"Tools": "\u5de5\u5177",
+"Powered by {0}": "\u7531{0}\u9a71\u52a8",
+"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "\u5728\u7f16\u8f91\u533a\u6309ALT-F9\u6253\u5f00\u83dc\u5355\uff0c\u6309ALT-F10\u6253\u5f00\u5de5\u5177\u680f\uff0c\u6309ALT-0\u67e5\u770b\u5e2e\u52a9",
+"Image title": "\u56fe\u7247\u6807\u9898",
+"Border width": "\u8fb9\u6846\u5bbd\u5ea6",
+"Border style": "\u8fb9\u6846\u6837\u5f0f",
+"Error": "\u9519\u8bef",
+"Warn": "\u8b66\u544a",
+"Valid": "\u6709\u6548",
+"To open the popup, press Shift+Enter": "\u6309Shitf+Enter\u952e\u6253\u5f00\u5bf9\u8bdd\u6846",
+"Rich Text Area. Press ALT-0 for help.": "\u7f16\u8f91\u533a\u3002\u6309Alt+0\u952e\u6253\u5f00\u5e2e\u52a9\u3002",
+"System Font": "\u7cfb\u7edf\u5b57\u4f53",
+"Failed to upload image: {0}": "\u56fe\u7247\u4e0a\u4f20\u5931\u8d25: {0}",
+"Failed to load plugin: {0} from url {1}": "\u63d2\u4ef6\u52a0\u8f7d\u5931\u8d25: {0} \u6765\u81ea\u94fe\u63a5 {1}",
+"Failed to load plugin url: {0}": "\u63d2\u4ef6\u52a0\u8f7d\u5931\u8d25 \u94fe\u63a5: {0}",
+"Failed to initialize plugin: {0}": "\u63d2\u4ef6\u521d\u59cb\u5316\u5931\u8d25: {0}",
+"example": "\u793a\u4f8b",
+"Search": "\u641c\u7d22",
+"All": "\u5168\u90e8",
+"Currency": "\u8d27\u5e01",
+"Text": "\u6587\u5b57",
+"Quotations": "\u5f15\u7528",
+"Mathematical": "\u6570\u5b66",
+"Extended Latin": "\u62c9\u4e01\u8bed\u6269\u5145",
+"Symbols": "\u7b26\u53f7",
+"Arrows": "\u7bad\u5934",
+"User Defined": "\u81ea\u5b9a\u4e49",
+"dollar sign": "\u7f8e\u5143\u7b26\u53f7",
+"currency sign": "\u8d27\u5e01\u7b26\u53f7",
+"euro-currency sign": "\u6b27\u5143\u7b26\u53f7",
+"colon sign": "\u5192\u53f7",
+"cruzeiro sign": "\u514b\u9c81\u8d5b\u7f57\u5e01\u7b26\u53f7",
+"french franc sign": "\u6cd5\u90ce\u7b26\u53f7",
+"lira sign": "\u91cc\u62c9\u7b26\u53f7",
+"mill sign": "\u5bc6\u5c14\u7b26\u53f7",
+"naira sign": "\u5948\u62c9\u7b26\u53f7",
+"peseta sign": "\u6bd4\u585e\u5854\u7b26\u53f7",
+"rupee sign": "\u5362\u6bd4\u7b26\u53f7",
+"won sign": "\u97e9\u5143\u7b26\u53f7",
+"new sheqel sign": "\u65b0\u8c22\u514b\u5c14\u7b26\u53f7",
+"dong sign": "\u8d8a\u5357\u76fe\u7b26\u53f7",
+"kip sign": "\u8001\u631d\u57fa\u666e\u7b26\u53f7",
+"tugrik sign": "\u56fe\u683c\u91cc\u514b\u7b26\u53f7",
+"drachma sign": "\u5fb7\u62c9\u514b\u9a6c\u7b26\u53f7",
+"german penny symbol": "\u5fb7\u56fd\u4fbf\u58eb\u7b26\u53f7",
+"peso sign": "\u6bd4\u7d22\u7b26\u53f7",
+"guarani sign": "\u74dc\u62c9\u5c3c\u7b26\u53f7",
+"austral sign": "\u6fb3\u5143\u7b26\u53f7",
+"hryvnia sign": "\u683c\u91cc\u592b\u5c3c\u4e9a\u7b26\u53f7",
+"cedi sign": "\u585e\u5730\u7b26\u53f7",
+"livre tournois sign": "\u91cc\u5f17\u5f17\u5c14\u7b26\u53f7",
+"spesmilo sign": "spesmilo\u7b26\u53f7",
+"tenge sign": "\u575a\u6208\u7b26\u53f7",
+"indian rupee sign": "\u5370\u5ea6\u5362\u6bd4",
+"turkish lira sign": "\u571f\u8033\u5176\u91cc\u62c9",
+"nordic mark sign": "\u5317\u6b27\u9a6c\u514b",
+"manat sign": "\u9a6c\u7eb3\u7279\u7b26\u53f7",
+"ruble sign": "\u5362\u5e03\u7b26\u53f7",
+"yen character": "\u65e5\u5143\u5b57\u6837",
+"yuan character": "\u4eba\u6c11\u5e01\u5143\u5b57\u6837",
+"yuan character, in hong kong and taiwan": "\u5143\u5b57\u6837\uff08\u6e2f\u53f0\u5730\u533a\uff09",
+"yen\/yuan character variant one": "\u5143\u5b57\u6837\uff08\u5927\u5199\uff09",
+"Loading emoticons...": "\u52a0\u8f7d\u8868\u60c5\u7b26\u53f7...",
+"Could not load emoticons": "\u4e0d\u80fd\u52a0\u8f7d\u8868\u60c5\u7b26\u53f7",
+"People": "\u4eba\u7c7b",
+"Animals and Nature": "\u52a8\u7269\u548c\u81ea\u7136",
+"Food and Drink": "\u98df\u7269\u548c\u996e\u54c1",
+"Activity": "\u6d3b\u52a8",
+"Travel and Places": "\u65c5\u6e38\u548c\u5730\u70b9",
+"Objects": "\u7269\u4ef6",
+"Flags": "\u65d7\u5e1c",
+"Characters": "\u5b57\u7b26",
+"Characters (no spaces)": "\u5b57\u7b26(\u65e0\u7a7a\u683c)",
+"{0} characters": "{0} \u4e2a\u5b57\u7b26",
+"Error: Form submit field collision.": "\u9519\u8bef: \u8868\u5355\u63d0\u4ea4\u5b57\u6bb5\u51b2\u7a81\u3002",
+"Error: No form element found.": "\u9519\u8bef: \u6ca1\u6709\u8868\u5355\u63a7\u4ef6\u3002",
+"Update": "\u66f4\u65b0",
+"Color swatch": "\u989c\u8272\u6837\u672c",
+"Turquoise": "\u9752\u7eff\u8272",
+"Green": "\u7eff\u8272",
+"Blue": "\u84dd\u8272",
+"Purple": "\u7d2b\u8272",
+"Navy Blue": "\u6d77\u519b\u84dd",
+"Dark Turquoise": "\u6df1\u84dd\u7eff\u8272",
+"Dark Green": "\u6df1\u7eff\u8272",
+"Medium Blue": "\u4e2d\u84dd\u8272",
+"Medium Purple": "\u4e2d\u7d2b\u8272",
+"Midnight Blue": "\u6df1\u84dd\u8272",
+"Yellow": "\u9ec4\u8272",
+"Orange": "\u6a59\u8272",
+"Red": "\u7ea2\u8272",
+"Light Gray": "\u6d45\u7070\u8272",
+"Gray": "\u7070\u8272",
+"Dark Yellow": "\u6697\u9ec4\u8272",
+"Dark Orange": "\u6df1\u6a59\u8272",
+"Dark Red": "\u6df1\u7ea2\u8272",
+"Medium Gray": "\u4e2d\u7070\u8272",
+"Dark Gray": "\u6df1\u7070\u8272",
+"Light Green": "\u6d45\u7eff\u8272",
+"Light Yellow": "\u6d45\u9ec4\u8272",
+"Light Red": "\u6d45\u7ea2\u8272",
+"Light Purple": "\u6d45\u7d2b\u8272",
+"Light Blue": "\u6d45\u84dd\u8272",
+"Dark Purple": "\u6df1\u7d2b\u8272",
+"Dark Blue": "\u6df1\u84dd\u8272",
+"Black": "\u9ed1\u8272",
+"White": "\u767d\u8272",
+"Switch to or from fullscreen mode": "\u5207\u6362\u5168\u5c4f\u6a21\u5f0f",
+"Open help dialog": "\u6253\u5f00\u5e2e\u52a9\u5bf9\u8bdd\u6846",
+"history": "\u5386\u53f2",
+"styles": "\u6837\u5f0f",
+"formatting": "\u683c\u5f0f\u5316",
+"alignment": "\u5bf9\u9f50",
+"indentation": "\u7f29\u8fdb",
+"permanent pen": "\u8bb0\u53f7\u7b14",
+"comments": "\u5907\u6ce8",
+"Format Painter": "\u683c\u5f0f\u5237",
+"Insert\/edit iframe": "\u63d2\u5165\/\u7f16\u8f91\u6846\u67b6",
+"Capitalization": "\u5927\u5199",
+"lowercase": "\u5c0f\u5199",
+"UPPERCASE": "\u5927\u5199",
+"Title Case": "\u9996\u5b57\u6bcd\u5927\u5199",
+"Permanent Pen Properties": "\u6c38\u4e45\u7b14\u5c5e\u6027",
+"Permanent pen properties...": "\u6c38\u4e45\u7b14\u5c5e\u6027...",
+"Font": "\u5b57\u4f53",
+"Size": "\u5b57\u53f7",
+"More...": "\u66f4\u591a...",
+"Spellcheck Language": "\u62fc\u5199\u68c0\u67e5\u8bed\u8a00",
+"Select...": "\u9009\u62e9...",
+"Preferences": "\u9996\u9009\u9879",
+"Yes": "\u662f",
+"No": "\u5426",
+"Keyboard Navigation": "\u952e\u76d8\u6307\u5f15",
+"Version": "\u7248\u672c",
+"Anchor": "\u951a\u70b9",
+"Special character": "\u7279\u6b8a\u7b26\u53f7",
+"Code sample": "\u4ee3\u7801\u793a\u4f8b",
+"Color": "\u989c\u8272",
+"Emoticons": "\u8868\u60c5",
+"Document properties": "\u6587\u6863\u5c5e\u6027",
+"Image": "\u56fe\u7247",
+"Insert link": "\u63d2\u5165\u94fe\u63a5",
+"Target": "\u6253\u5f00\u65b9\u5f0f",
+"Link": "\u94fe\u63a5",
+"Poster": "\u5c01\u9762",
+"Media": "\u5a92\u4f53",
+"Print": "\u6253\u5370",
+"Prev": "\u4e0a\u4e00\u4e2a",
+"Find and replace": "\u67e5\u627e\u548c\u66ff\u6362",
+"Whole words": "\u5168\u5b57\u5339\u914d",
+"Spellcheck": "\u62fc\u5199\u68c0\u67e5",
+"Caption": "\u6807\u9898",
+"Insert template": "\u63d2\u5165\u6a21\u677f"
+});

+ 59 - 0
public/tinymce/skins/content/dark/content.css

@@ -0,0 +1,59 @@
+/**
+ * Copyright (c) Tiny Technologies, Inc. All rights reserved.
+ * Licensed under the LGPL or a commercial license.
+ * For LGPL see License.txt in the project root for license information.
+ * For commercial licenses see https://www.tiny.cloud/
+ */
+body {
+  background-color: #2f3742;
+  color: #dfe0e4;
+  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
+  line-height: 1.4;
+  margin: 1rem;
+}
+a {
+  color: #4099ff;
+}
+table {
+  border-collapse: collapse;
+}
+table th,
+table td {
+  border: 1px solid #6d737b;
+  padding: 0.4rem;
+}
+figure {
+  display: table;
+  margin: 1rem auto;
+}
+figure figcaption {
+  color: #8a8f97;
+  display: block;
+  margin-top: 0.25rem;
+  text-align: center;
+}
+hr {
+  border-color: #6d737b;
+  border-style: solid;
+  border-width: 1px 0 0 0;
+}
+code {
+  background-color: #6d737b;
+  border-radius: 3px;
+  padding: 0.1rem 0.2rem;
+}
+/* Make text in selected cells in tables dark and readable */
+td[data-mce-selected],
+th[data-mce-selected] {
+  color: #333;
+}
+.mce-content-body:not([dir=rtl]) blockquote {
+  border-left: 2px solid #6d737b;
+  margin-left: 1.5rem;
+  padding-left: 1rem;
+}
+.mce-content-body[dir=rtl] blockquote {
+  border-right: 2px solid #6d737b;
+  margin-right: 1.5rem;
+  padding-right: 1rem;
+}

File diff suppressed because it is too large
+ 7 - 0
public/tinymce/skins/content/dark/content.min.css


+ 49 - 0
public/tinymce/skins/content/default/content.css

@@ -0,0 +1,49 @@
+/**
+ * Copyright (c) Tiny Technologies, Inc. All rights reserved.
+ * Licensed under the LGPL or a commercial license.
+ * For LGPL see License.txt in the project root for license information.
+ * For commercial licenses see https://www.tiny.cloud/
+ */
+body {
+  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
+  line-height: 1.4;
+  margin: 1rem;
+}
+table {
+  border-collapse: collapse;
+}
+table th,
+table td {
+  border: 1px solid #ccc;
+  padding: 0.4rem;
+}
+figure {
+  display: table;
+  margin: 1rem auto;
+}
+figure figcaption {
+  color: #999;
+  display: block;
+  margin-top: 0.25rem;
+  text-align: center;
+}
+hr {
+  border-color: #ccc;
+  border-style: solid;
+  border-width: 1px 0 0 0;
+}
+code {
+  background-color: #e8e8e8;
+  border-radius: 3px;
+  padding: 0.1rem 0.2rem;
+}
+.mce-content-body:not([dir=rtl]) blockquote {
+  border-left: 2px solid #ccc;
+  margin-left: 1.5rem;
+  padding-left: 1rem;
+}
+.mce-content-body[dir=rtl] blockquote {
+  border-right: 2px solid #ccc;
+  margin-right: 1.5rem;
+  padding-right: 1rem;
+}

File diff suppressed because it is too large
+ 7 - 0
public/tinymce/skins/content/default/content.min.css


+ 53 - 0
public/tinymce/skins/content/document/content.css

@@ -0,0 +1,53 @@
+/**
+ * Copyright (c) Tiny Technologies, Inc. All rights reserved.
+ * Licensed under the LGPL or a commercial license.
+ * For LGPL see License.txt in the project root for license information.
+ * For commercial licenses see https://www.tiny.cloud/
+ */
+@media screen {
+  html {
+    background: #f4f4f4;
+  }
+}
+body {
+  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
+}
+@media screen {
+  body {
+    background-color: #fff;
+    box-shadow: 0 0 4px rgba(0, 0, 0, 0.15);
+    box-sizing: border-box;
+    margin: 1rem auto 0;
+    max-width: 820px;
+    min-height: calc(100vh - 1rem);
+    padding: 4rem 6rem 6rem 6rem;
+  }
+}
+table {
+  border-collapse: collapse;
+}
+table th,
+table td {
+  border: 1px solid #ccc;
+  padding: 0.4rem;
+}
+figure figcaption {
+  color: #999;
+  margin-top: 0.25rem;
+  text-align: center;
+}
+hr {
+  border-color: #ccc;
+  border-style: solid;
+  border-width: 1px 0 0 0;
+}
+.mce-content-body:not([dir=rtl]) blockquote {
+  border-left: 2px solid #ccc;
+  margin-left: 1.5rem;
+  padding-left: 1rem;
+}
+.mce-content-body[dir=rtl] blockquote {
+  border-right: 2px solid #ccc;
+  margin-right: 1.5rem;
+  padding-right: 1rem;
+}

File diff suppressed because it is too large
+ 7 - 0
public/tinymce/skins/content/document/content.min.css


+ 50 - 0
public/tinymce/skins/content/writer/content.css

@@ -0,0 +1,50 @@
+/**
+ * Copyright (c) Tiny Technologies, Inc. All rights reserved.
+ * Licensed under the LGPL or a commercial license.
+ * For LGPL see License.txt in the project root for license information.
+ * For commercial licenses see https://www.tiny.cloud/
+ */
+body {
+  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
+  line-height: 1.4;
+  margin: 1rem auto;
+  max-width: 900px;
+}
+table {
+  border-collapse: collapse;
+}
+table th,
+table td {
+  border: 1px solid #ccc;
+  padding: 0.4rem;
+}
+figure {
+  display: table;
+  margin: 1rem auto;
+}
+figure figcaption {
+  color: #999;
+  display: block;
+  margin-top: 0.25rem;
+  text-align: center;
+}
+hr {
+  border-color: #ccc;
+  border-style: solid;
+  border-width: 1px 0 0 0;
+}
+code {
+  background-color: #e8e8e8;
+  border-radius: 3px;
+  padding: 0.1rem 0.2rem;
+}
+.mce-content-body:not([dir=rtl]) blockquote {
+  border-left: 2px solid #ccc;
+  margin-left: 1.5rem;
+  padding-left: 1rem;
+}
+.mce-content-body[dir=rtl] blockquote {
+  border-right: 2px solid #ccc;
+  margin-right: 1.5rem;
+  padding-right: 1rem;
+}

File diff suppressed because it is too large
+ 7 - 0
public/tinymce/skins/content/writer/content.min.css


File diff suppressed because it is too large
+ 600 - 0
public/tinymce/skins/ui/oxide-dark/content.css


File diff suppressed because it is too large
+ 612 - 0
public/tinymce/skins/ui/oxide-dark/content.inline.css


File diff suppressed because it is too large
+ 7 - 0
public/tinymce/skins/ui/oxide-dark/content.inline.min.css


File diff suppressed because it is too large
+ 7 - 0
public/tinymce/skins/ui/oxide-dark/content.min.css


+ 29 - 0
public/tinymce/skins/ui/oxide-dark/content.mobile.css

@@ -0,0 +1,29 @@
+/**
+ * Copyright (c) Tiny Technologies, Inc. All rights reserved.
+ * Licensed under the LGPL or a commercial license.
+ * For LGPL see License.txt in the project root for license information.
+ * For commercial licenses see https://www.tiny.cloud/
+ */
+.tinymce-mobile-unfocused-selections .tinymce-mobile-unfocused-selection {
+  /* Note: this file is used inside the content, so isn't part of theming */
+  background-color: green;
+  display: inline-block;
+  opacity: 0.5;
+  position: absolute;
+}
+body {
+  -webkit-text-size-adjust: none;
+}
+body img {
+  /* this is related to the content margin */
+  max-width: 96vw;
+}
+body table img {
+  max-width: 95%;
+}
+body {
+  font-family: sans-serif;
+}
+table {
+  border-collapse: collapse;
+}

+ 7 - 0
public/tinymce/skins/ui/oxide-dark/content.mobile.min.css

@@ -0,0 +1,7 @@
+/**
+ * Copyright (c) Tiny Technologies, Inc. All rights reserved.
+ * Licensed under the LGPL or a commercial license.
+ * For LGPL see License.txt in the project root for license information.
+ * For commercial licenses see https://www.tiny.cloud/
+ */
+.tinymce-mobile-unfocused-selections .tinymce-mobile-unfocused-selection{background-color:green;display:inline-block;opacity:.5;position:absolute}body{-webkit-text-size-adjust:none}body img{max-width:96vw}body table img{max-width:95%}body{font-family:sans-serif}table{border-collapse:collapse}

BIN
public/tinymce/skins/ui/oxide-dark/fonts/tinymce-mobile.woff


File diff suppressed because it is too large
+ 2884 - 0
public/tinymce/skins/ui/oxide-dark/skin.css


File diff suppressed because it is too large
+ 7 - 0
public/tinymce/skins/ui/oxide-dark/skin.min.css


+ 673 - 0
public/tinymce/skins/ui/oxide-dark/skin.mobile.css

@@ -0,0 +1,673 @@
+/**
+ * Copyright (c) Tiny Technologies, Inc. All rights reserved.
+ * Licensed under the LGPL or a commercial license.
+ * For LGPL see License.txt in the project root for license information.
+ * For commercial licenses see https://www.tiny.cloud/
+ */
+/* RESET all the things! */
+.tinymce-mobile-outer-container {
+  all: initial;
+  display: block;
+}
+.tinymce-mobile-outer-container * {
+  border: 0;
+  box-sizing: initial;
+  cursor: inherit;
+  float: none;
+  line-height: 1;
+  margin: 0;
+  outline: 0;
+  padding: 0;
+  -webkit-tap-highlight-color: transparent;
+  /* TBIO-3691, stop the gray flicker on touch. */
+  text-shadow: none;
+  white-space: nowrap;
+}
+.tinymce-mobile-icon-arrow-back::before {
+  content: "\e5cd";
+}
+.tinymce-mobile-icon-image::before {
+  content: "\e412";
+}
+.tinymce-mobile-icon-cancel-circle::before {
+  content: "\e5c9";
+}
+.tinymce-mobile-icon-full-dot::before {
+  content: "\e061";
+}
+.tinymce-mobile-icon-align-center::before {
+  content: "\e234";
+}
+.tinymce-mobile-icon-align-left::before {
+  content: "\e236";
+}
+.tinymce-mobile-icon-align-right::before {
+  content: "\e237";
+}
+.tinymce-mobile-icon-bold::before {
+  content: "\e238";
+}
+.tinymce-mobile-icon-italic::before {
+  content: "\e23f";
+}
+.tinymce-mobile-icon-unordered-list::before {
+  content: "\e241";
+}
+.tinymce-mobile-icon-ordered-list::before {
+  content: "\e242";
+}
+.tinymce-mobile-icon-font-size::before {
+  content: "\e245";
+}
+.tinymce-mobile-icon-underline::before {
+  content: "\e249";
+}
+.tinymce-mobile-icon-link::before {
+  content: "\e157";
+}
+.tinymce-mobile-icon-unlink::before {
+  content: "\eca2";
+}
+.tinymce-mobile-icon-color::before {
+  content: "\e891";
+}
+.tinymce-mobile-icon-previous::before {
+  content: "\e314";
+}
+.tinymce-mobile-icon-next::before {
+  content: "\e315";
+}
+.tinymce-mobile-icon-large-font::before,
+.tinymce-mobile-icon-style-formats::before {
+  content: "\e264";
+}
+.tinymce-mobile-icon-undo::before {
+  content: "\e166";
+}
+.tinymce-mobile-icon-redo::before {
+  content: "\e15a";
+}
+.tinymce-mobile-icon-removeformat::before {
+  content: "\e239";
+}
+.tinymce-mobile-icon-small-font::before {
+  content: "\e906";
+}
+.tinymce-mobile-icon-readonly-back::before,
+.tinymce-mobile-format-matches::after {
+  content: "\e5ca";
+}
+.tinymce-mobile-icon-small-heading::before {
+  content: "small";
+}
+.tinymce-mobile-icon-large-heading::before {
+  content: "large";
+}
+.tinymce-mobile-icon-small-heading::before,
+.tinymce-mobile-icon-large-heading::before {
+  font-family: sans-serif;
+  font-size: 80%;
+}
+.tinymce-mobile-mask-edit-icon::before {
+  content: "\e254";
+}
+.tinymce-mobile-icon-back::before {
+  content: "\e5c4";
+}
+.tinymce-mobile-icon-heading::before {
+  /* TODO: Translate */
+  content: "Headings";
+  font-family: sans-serif;
+  font-size: 80%;
+  font-weight: bold;
+}
+.tinymce-mobile-icon-h1::before {
+  content: "H1";
+  font-weight: bold;
+}
+.tinymce-mobile-icon-h2::before {
+  content: "H2";
+  font-weight: bold;
+}
+.tinymce-mobile-icon-h3::before {
+  content: "H3";
+  font-weight: bold;
+}
+.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask {
+  align-items: center;
+  display: flex;
+  justify-content: center;
+  background: rgba(51, 51, 51, 0.5);
+  height: 100%;
+  position: absolute;
+  top: 0;
+  width: 100%;
+}
+.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container {
+  align-items: center;
+  border-radius: 50%;
+  display: flex;
+  flex-direction: column;
+  font-family: sans-serif;
+  font-size: 1em;
+  justify-content: space-between;
+}
+.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .mixin-menu-item {
+  align-items: center;
+  display: flex;
+  justify-content: center;
+  border-radius: 50%;
+  height: 2.1em;
+  width: 2.1em;
+}
+.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section {
+  align-items: center;
+  display: flex;
+  justify-content: center;
+  flex-direction: column;
+  font-size: 1em;
+}
+@media only screen and (min-device-width:700px) {
+  .tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section {
+    font-size: 1.2em;
+  }
+}
+.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section .tinymce-mobile-mask-tap-icon {
+  align-items: center;
+  display: flex;
+  justify-content: center;
+  border-radius: 50%;
+  height: 2.1em;
+  width: 2.1em;
+  background-color: white;
+  color: #207ab7;
+}
+.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section .tinymce-mobile-mask-tap-icon::before {
+  content: "\e900";
+  font-family: 'tinymce-mobile', sans-serif;
+}
+.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section:not(.tinymce-mobile-mask-tap-icon-selected) .tinymce-mobile-mask-tap-icon {
+  z-index: 2;
+}
+.tinymce-mobile-android-container.tinymce-mobile-android-maximized {
+  background: #ffffff;
+  border: none;
+  bottom: 0;
+  display: flex;
+  flex-direction: column;
+  left: 0;
+  position: fixed;
+  right: 0;
+  top: 0;
+}
+.tinymce-mobile-android-container:not(.tinymce-mobile-android-maximized) {
+  position: relative;
+}
+.tinymce-mobile-android-container .tinymce-mobile-editor-socket {
+  display: flex;
+  flex-grow: 1;
+}
+.tinymce-mobile-android-container .tinymce-mobile-editor-socket iframe {
+  display: flex !important;
+  flex-grow: 1;
+  height: auto !important;
+}
+.tinymce-mobile-android-scroll-reload {
+  overflow: hidden;
+}
+:not(.tinymce-mobile-readonly-mode) > .tinymce-mobile-android-selection-context-toolbar {
+  margin-top: 23px;
+}
+.tinymce-mobile-toolstrip {
+  background: #fff;
+  display: flex;
+  flex: 0 0 auto;
+  z-index: 1;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar {
+  align-items: center;
+  background-color: #fff;
+  border-bottom: 1px solid #cccccc;
+  display: flex;
+  flex: 1;
+  height: 2.5em;
+  width: 100%;
+  /* Make it no larger than the toolstrip, so that it needs to scroll */
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group {
+  align-items: center;
+  display: flex;
+  height: 100%;
+  flex-shrink: 1;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group > div {
+  align-items: center;
+  display: flex;
+  height: 100%;
+  flex: 1;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group.tinymce-mobile-exit-container {
+  background: #f44336;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group.tinymce-mobile-toolbar-scrollable-group {
+  flex-grow: 1;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item {
+  padding-left: 0.5em;
+  padding-right: 0.5em;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item.tinymce-mobile-toolbar-button {
+  align-items: center;
+  display: flex;
+  height: 80%;
+  margin-left: 2px;
+  margin-right: 2px;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item.tinymce-mobile-toolbar-button.tinymce-mobile-toolbar-button-selected {
+  background: #c8cbcf;
+  color: #cccccc;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group:first-of-type,
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group:last-of-type {
+  background: #207ab7;
+  color: #eceff1;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar {
+  /* Note, this file is imported inside .tinymce-mobile-context-toolbar, so that prefix is on everything here. */
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group {
+  align-items: center;
+  display: flex;
+  height: 100%;
+  flex: 1;
+  padding-bottom: 0.4em;
+  padding-top: 0.4em;
+  /* Make any buttons appearing on the left and right display in the centre (e.g. color edges) */
+  /* For widgets like the colour picker, use the whole height */
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog {
+  display: flex;
+  min-height: 1.5em;
+  overflow: hidden;
+  padding-left: 0;
+  padding-right: 0;
+  position: relative;
+  width: 100%;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain {
+  display: flex;
+  height: 100%;
+  transition: left cubic-bezier(0.4, 0, 1, 1) 0.15s;
+  width: 100%;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen {
+  display: flex;
+  flex: 0 0 auto;
+  justify-content: space-between;
+  width: 100%;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen input {
+  font-family: Sans-serif;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-input-container {
+  display: flex;
+  flex-grow: 1;
+  position: relative;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-input-container .tinymce-mobile-input-container-x {
+  -ms-grid-row-align: center;
+      align-self: center;
+  background: inherit;
+  border: none;
+  border-radius: 50%;
+  color: #888;
+  font-size: 0.6em;
+  font-weight: bold;
+  height: 100%;
+  padding-right: 2px;
+  position: absolute;
+  right: 0;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-input-container.tinymce-mobile-input-container-empty .tinymce-mobile-input-container-x {
+  display: none;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-previous,
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-next {
+  align-items: center;
+  display: flex;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-previous::before,
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-next::before {
+  align-items: center;
+  display: flex;
+  font-weight: bold;
+  height: 100%;
+  padding-left: 0.5em;
+  padding-right: 0.5em;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-previous.tinymce-mobile-toolbar-navigation-disabled::before,
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-next.tinymce-mobile-toolbar-navigation-disabled::before {
+  visibility: hidden;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-dot-item {
+  color: #cccccc;
+  font-size: 10px;
+  line-height: 10px;
+  margin: 0 2px;
+  padding-top: 3px;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-dot-item.tinymce-mobile-dot-active {
+  color: #c8cbcf;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-icon-large-font::before,
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-icon-large-heading::before {
+  margin-left: 0.5em;
+  margin-right: 0.9em;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-icon-small-font::before,
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-icon-small-heading::before {
+  margin-left: 0.9em;
+  margin-right: 0.5em;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider {
+  display: flex;
+  flex: 1;
+  margin-left: 0;
+  margin-right: 0;
+  padding: 0.28em 0;
+  position: relative;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider .tinymce-mobile-slider-size-container {
+  align-items: center;
+  display: flex;
+  flex-grow: 1;
+  height: 100%;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider .tinymce-mobile-slider-size-container .tinymce-mobile-slider-size-line {
+  background: #cccccc;
+  display: flex;
+  flex: 1;
+  height: 0.2em;
+  margin-bottom: 0.3em;
+  margin-top: 0.3em;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container {
+  padding-left: 2em;
+  padding-right: 2em;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container .tinymce-mobile-slider-gradient-container {
+  align-items: center;
+  display: flex;
+  flex-grow: 1;
+  height: 100%;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container .tinymce-mobile-slider-gradient-container .tinymce-mobile-slider-gradient {
+  background: linear-gradient(to right, hsl(0, 100%, 50%) 0%, hsl(60, 100%, 50%) 17%, hsl(120, 100%, 50%) 33%, hsl(180, 100%, 50%) 50%, hsl(240, 100%, 50%) 67%, hsl(300, 100%, 50%) 83%, hsl(0, 100%, 50%) 100%);
+  display: flex;
+  flex: 1;
+  height: 0.2em;
+  margin-bottom: 0.3em;
+  margin-top: 0.3em;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container .tinymce-mobile-hue-slider-black {
+  /* Not part of theming */
+  background: black;
+  height: 0.2em;
+  margin-bottom: 0.3em;
+  margin-top: 0.3em;
+  width: 1.2em;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container .tinymce-mobile-hue-slider-white {
+  /* Not part of theming */
+  background: white;
+  height: 0.2em;
+  margin-bottom: 0.3em;
+  margin-top: 0.3em;
+  width: 1.2em;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider .tinymce-mobile-slider-thumb {
+  /* vertically centering trick (margin: auto, top: 0, bottom: 0). On iOS and Safari, if you leave
+     * out these values, then it shows the thumb at the top of the spectrum. This is probably because it is
+     * absolutely positioned with only a left value, and not a top. Note, on Chrome it seems to be fine without
+     * this approach.
+    */
+  align-items: center;
+  background-clip: padding-box;
+  background-color: #455a64;
+  border: 0.5em solid rgba(136, 136, 136, 0);
+  border-radius: 3em;
+  bottom: 0;
+  color: #fff;
+  display: flex;
+  height: 0.5em;
+  justify-content: center;
+  left: -10px;
+  margin: auto;
+  position: absolute;
+  top: 0;
+  transition: border 120ms cubic-bezier(0.39, 0.58, 0.57, 1);
+  width: 0.5em;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider .tinymce-mobile-slider-thumb.tinymce-mobile-thumb-active {
+  border: 0.5em solid rgba(136, 136, 136, 0.39);
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serializer-wrapper,
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group > div {
+  align-items: center;
+  display: flex;
+  height: 100%;
+  flex: 1;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serializer-wrapper {
+  flex-direction: column;
+  justify-content: center;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item {
+  align-items: center;
+  display: flex;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item:not(.tinymce-mobile-serialised-dialog) {
+  height: 100%;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-dot-container {
+  display: flex;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group input {
+  background: #ffffff;
+  border: none;
+  border-radius: 0;
+  color: #455a64;
+  flex-grow: 1;
+  font-size: 0.85em;
+  padding-bottom: 0.1em;
+  padding-left: 5px;
+  padding-top: 0.1em;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group input::-webkit-input-placeholder {
+  /* WebKit, Blink, Edge */
+  color: #888;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group input::placeholder {
+  /* WebKit, Blink, Edge */
+  color: #888;
+}
+/* dropup */
+.tinymce-mobile-dropup {
+  background: white;
+  display: flex;
+  overflow: hidden;
+  width: 100%;
+}
+.tinymce-mobile-dropup.tinymce-mobile-dropup-shrinking {
+  transition: height 0.3s ease-out;
+}
+.tinymce-mobile-dropup.tinymce-mobile-dropup-growing {
+  transition: height 0.3s ease-in;
+}
+.tinymce-mobile-dropup.tinymce-mobile-dropup-closed {
+  flex-grow: 0;
+}
+.tinymce-mobile-dropup.tinymce-mobile-dropup-open:not(.tinymce-mobile-dropup-growing) {
+  flex-grow: 1;
+}
+/* TODO min-height for device size and orientation */
+.tinymce-mobile-ios-container .tinymce-mobile-dropup:not(.tinymce-mobile-dropup-closed) {
+  min-height: 200px;
+}
+@media only screen and (orientation: landscape) {
+  .tinymce-mobile-dropup:not(.tinymce-mobile-dropup-closed) {
+    min-height: 200px;
+  }
+}
+@media only screen and (min-device-width : 320px) and (max-device-width : 568px) and (orientation : landscape) {
+  .tinymce-mobile-ios-container .tinymce-mobile-dropup:not(.tinymce-mobile-dropup-closed) {
+    min-height: 150px;
+  }
+}
+/* styles menu */
+.tinymce-mobile-styles-menu {
+  font-family: sans-serif;
+  outline: 4px solid black;
+  overflow: hidden;
+  position: relative;
+  width: 100%;
+}
+.tinymce-mobile-styles-menu [role="menu"] {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+  position: absolute;
+  width: 100%;
+}
+.tinymce-mobile-styles-menu [role="menu"].transitioning {
+  transition: transform 0.5s ease-in-out;
+}
+.tinymce-mobile-styles-menu .tinymce-mobile-styles-item {
+  border-bottom: 1px solid #ddd;
+  color: #455a64;
+  cursor: pointer;
+  display: flex;
+  padding: 1em 1em;
+  position: relative;
+}
+.tinymce-mobile-styles-menu .tinymce-mobile-styles-collapser .tinymce-mobile-styles-collapse-icon::before {
+  color: #455a64;
+  content: "\e314";
+  font-family: 'tinymce-mobile', sans-serif;
+}
+.tinymce-mobile-styles-menu .tinymce-mobile-styles-item.tinymce-mobile-styles-item-is-menu::after {
+  color: #455a64;
+  content: "\e315";
+  font-family: 'tinymce-mobile', sans-serif;
+  padding-left: 1em;
+  padding-right: 1em;
+  position: absolute;
+  right: 0;
+}
+.tinymce-mobile-styles-menu .tinymce-mobile-styles-item.tinymce-mobile-format-matches::after {
+  font-family: 'tinymce-mobile', sans-serif;
+  padding-left: 1em;
+  padding-right: 1em;
+  position: absolute;
+  right: 0;
+}
+.tinymce-mobile-styles-menu .tinymce-mobile-styles-separator,
+.tinymce-mobile-styles-menu .tinymce-mobile-styles-collapser {
+  align-items: center;
+  background: #fff;
+  border-top: #455a64;
+  color: #455a64;
+  display: flex;
+  min-height: 2.5em;
+  padding-left: 1em;
+  padding-right: 1em;
+}
+.tinymce-mobile-styles-menu [data-transitioning-destination="before"][data-transitioning-state],
+.tinymce-mobile-styles-menu [data-transitioning-state="before"] {
+  transform: translate(-100%);
+}
+.tinymce-mobile-styles-menu [data-transitioning-destination="current"][data-transitioning-state],
+.tinymce-mobile-styles-menu [data-transitioning-state="current"] {
+  transform: translate(0%);
+}
+.tinymce-mobile-styles-menu [data-transitioning-destination="after"][data-transitioning-state],
+.tinymce-mobile-styles-menu [data-transitioning-state="after"] {
+  transform: translate(100%);
+}
+@font-face {
+  font-family: 'tinymce-mobile';
+  font-style: normal;
+  font-weight: normal;
+  src: url('fonts/tinymce-mobile.woff?8x92w3') format('woff');
+}
+@media (min-device-width: 700px) {
+  .tinymce-mobile-outer-container,
+  .tinymce-mobile-outer-container input {
+    font-size: 25px;
+  }
+}
+@media (max-device-width: 700px) {
+  .tinymce-mobile-outer-container,
+  .tinymce-mobile-outer-container input {
+    font-size: 18px;
+  }
+}
+.tinymce-mobile-icon {
+  font-family: 'tinymce-mobile', sans-serif;
+}
+.mixin-flex-and-centre {
+  align-items: center;
+  display: flex;
+  justify-content: center;
+}
+.mixin-flex-bar {
+  align-items: center;
+  display: flex;
+  height: 100%;
+}
+.tinymce-mobile-outer-container .tinymce-mobile-editor-socket iframe {
+  background-color: #fff;
+  width: 100%;
+}
+.tinymce-mobile-editor-socket .tinymce-mobile-mask-edit-icon {
+  /* Note, on the iPod touch in landscape, this isn't visible when the navbar appears */
+  background-color: #207ab7;
+  border-radius: 50%;
+  bottom: 1em;
+  color: white;
+  font-size: 1em;
+  height: 2.1em;
+  position: fixed;
+  right: 2em;
+  width: 2.1em;
+  align-items: center;
+  display: flex;
+  justify-content: center;
+}
+@media only screen and (min-device-width:700px) {
+  .tinymce-mobile-editor-socket .tinymce-mobile-mask-edit-icon {
+    font-size: 1.2em;
+  }
+}
+.tinymce-mobile-outer-container:not(.tinymce-mobile-fullscreen-maximized) .tinymce-mobile-editor-socket {
+  height: 300px;
+  overflow: hidden;
+}
+.tinymce-mobile-outer-container:not(.tinymce-mobile-fullscreen-maximized) .tinymce-mobile-editor-socket iframe {
+  height: 100%;
+}
+.tinymce-mobile-outer-container:not(.tinymce-mobile-fullscreen-maximized) .tinymce-mobile-toolstrip {
+  display: none;
+}
+/*
+  Note, that if you don't include this (::-webkit-file-upload-button), the toolbar width gets
+  increased and the whole body becomes scrollable. It's important!
+ */
+input[type="file"]::-webkit-file-upload-button {
+  display: none;
+}
+@media only screen and (min-device-width : 320px) and (max-device-width : 568px) and (orientation : landscape) {
+  .tinymce-mobile-ios-container .tinymce-mobile-editor-socket .tinymce-mobile-mask-edit-icon {
+    bottom: 50%;
+  }
+}

File diff suppressed because it is too large
+ 7 - 0
public/tinymce/skins/ui/oxide-dark/skin.mobile.min.css


File diff suppressed because it is too large
+ 618 - 0
public/tinymce/skins/ui/oxide/content.css


File diff suppressed because it is too large
+ 612 - 0
public/tinymce/skins/ui/oxide/content.inline.css


File diff suppressed because it is too large
+ 7 - 0
public/tinymce/skins/ui/oxide/content.inline.min.css


File diff suppressed because it is too large
+ 7 - 0
public/tinymce/skins/ui/oxide/content.min.css


+ 29 - 0
public/tinymce/skins/ui/oxide/content.mobile.css

@@ -0,0 +1,29 @@
+/**
+ * Copyright (c) Tiny Technologies, Inc. All rights reserved.
+ * Licensed under the LGPL or a commercial license.
+ * For LGPL see License.txt in the project root for license information.
+ * For commercial licenses see https://www.tiny.cloud/
+ */
+.tinymce-mobile-unfocused-selections .tinymce-mobile-unfocused-selection {
+  /* Note: this file is used inside the content, so isn't part of theming */
+  background-color: green;
+  display: inline-block;
+  opacity: 0.5;
+  position: absolute;
+}
+body {
+  -webkit-text-size-adjust: none;
+}
+body img {
+  /* this is related to the content margin */
+  max-width: 96vw;
+}
+body table img {
+  max-width: 95%;
+}
+body {
+  font-family: sans-serif;
+}
+table {
+  border-collapse: collapse;
+}

+ 7 - 0
public/tinymce/skins/ui/oxide/content.mobile.min.css

@@ -0,0 +1,7 @@
+/**
+ * Copyright (c) Tiny Technologies, Inc. All rights reserved.
+ * Licensed under the LGPL or a commercial license.
+ * For LGPL see License.txt in the project root for license information.
+ * For commercial licenses see https://www.tiny.cloud/
+ */
+.tinymce-mobile-unfocused-selections .tinymce-mobile-unfocused-selection{background-color:green;display:inline-block;opacity:.5;position:absolute}body{-webkit-text-size-adjust:none}body img{max-width:96vw}body table img{max-width:95%}body{font-family:sans-serif}table{border-collapse:collapse}

BIN
public/tinymce/skins/ui/oxide/fonts/tinymce-mobile.woff


File diff suppressed because it is too large
+ 2884 - 0
public/tinymce/skins/ui/oxide/skin.css


File diff suppressed because it is too large
+ 7 - 0
public/tinymce/skins/ui/oxide/skin.min.css


+ 673 - 0
public/tinymce/skins/ui/oxide/skin.mobile.css

@@ -0,0 +1,673 @@
+/**
+ * Copyright (c) Tiny Technologies, Inc. All rights reserved.
+ * Licensed under the LGPL or a commercial license.
+ * For LGPL see License.txt in the project root for license information.
+ * For commercial licenses see https://www.tiny.cloud/
+ */
+/* RESET all the things! */
+.tinymce-mobile-outer-container {
+  all: initial;
+  display: block;
+}
+.tinymce-mobile-outer-container * {
+  border: 0;
+  box-sizing: initial;
+  cursor: inherit;
+  float: none;
+  line-height: 1;
+  margin: 0;
+  outline: 0;
+  padding: 0;
+  -webkit-tap-highlight-color: transparent;
+  /* TBIO-3691, stop the gray flicker on touch. */
+  text-shadow: none;
+  white-space: nowrap;
+}
+.tinymce-mobile-icon-arrow-back::before {
+  content: "\e5cd";
+}
+.tinymce-mobile-icon-image::before {
+  content: "\e412";
+}
+.tinymce-mobile-icon-cancel-circle::before {
+  content: "\e5c9";
+}
+.tinymce-mobile-icon-full-dot::before {
+  content: "\e061";
+}
+.tinymce-mobile-icon-align-center::before {
+  content: "\e234";
+}
+.tinymce-mobile-icon-align-left::before {
+  content: "\e236";
+}
+.tinymce-mobile-icon-align-right::before {
+  content: "\e237";
+}
+.tinymce-mobile-icon-bold::before {
+  content: "\e238";
+}
+.tinymce-mobile-icon-italic::before {
+  content: "\e23f";
+}
+.tinymce-mobile-icon-unordered-list::before {
+  content: "\e241";
+}
+.tinymce-mobile-icon-ordered-list::before {
+  content: "\e242";
+}
+.tinymce-mobile-icon-font-size::before {
+  content: "\e245";
+}
+.tinymce-mobile-icon-underline::before {
+  content: "\e249";
+}
+.tinymce-mobile-icon-link::before {
+  content: "\e157";
+}
+.tinymce-mobile-icon-unlink::before {
+  content: "\eca2";
+}
+.tinymce-mobile-icon-color::before {
+  content: "\e891";
+}
+.tinymce-mobile-icon-previous::before {
+  content: "\e314";
+}
+.tinymce-mobile-icon-next::before {
+  content: "\e315";
+}
+.tinymce-mobile-icon-large-font::before,
+.tinymce-mobile-icon-style-formats::before {
+  content: "\e264";
+}
+.tinymce-mobile-icon-undo::before {
+  content: "\e166";
+}
+.tinymce-mobile-icon-redo::before {
+  content: "\e15a";
+}
+.tinymce-mobile-icon-removeformat::before {
+  content: "\e239";
+}
+.tinymce-mobile-icon-small-font::before {
+  content: "\e906";
+}
+.tinymce-mobile-icon-readonly-back::before,
+.tinymce-mobile-format-matches::after {
+  content: "\e5ca";
+}
+.tinymce-mobile-icon-small-heading::before {
+  content: "small";
+}
+.tinymce-mobile-icon-large-heading::before {
+  content: "large";
+}
+.tinymce-mobile-icon-small-heading::before,
+.tinymce-mobile-icon-large-heading::before {
+  font-family: sans-serif;
+  font-size: 80%;
+}
+.tinymce-mobile-mask-edit-icon::before {
+  content: "\e254";
+}
+.tinymce-mobile-icon-back::before {
+  content: "\e5c4";
+}
+.tinymce-mobile-icon-heading::before {
+  /* TODO: Translate */
+  content: "Headings";
+  font-family: sans-serif;
+  font-size: 80%;
+  font-weight: bold;
+}
+.tinymce-mobile-icon-h1::before {
+  content: "H1";
+  font-weight: bold;
+}
+.tinymce-mobile-icon-h2::before {
+  content: "H2";
+  font-weight: bold;
+}
+.tinymce-mobile-icon-h3::before {
+  content: "H3";
+  font-weight: bold;
+}
+.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask {
+  align-items: center;
+  display: flex;
+  justify-content: center;
+  background: rgba(51, 51, 51, 0.5);
+  height: 100%;
+  position: absolute;
+  top: 0;
+  width: 100%;
+}
+.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container {
+  align-items: center;
+  border-radius: 50%;
+  display: flex;
+  flex-direction: column;
+  font-family: sans-serif;
+  font-size: 1em;
+  justify-content: space-between;
+}
+.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .mixin-menu-item {
+  align-items: center;
+  display: flex;
+  justify-content: center;
+  border-radius: 50%;
+  height: 2.1em;
+  width: 2.1em;
+}
+.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section {
+  align-items: center;
+  display: flex;
+  justify-content: center;
+  flex-direction: column;
+  font-size: 1em;
+}
+@media only screen and (min-device-width:700px) {
+  .tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section {
+    font-size: 1.2em;
+  }
+}
+.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section .tinymce-mobile-mask-tap-icon {
+  align-items: center;
+  display: flex;
+  justify-content: center;
+  border-radius: 50%;
+  height: 2.1em;
+  width: 2.1em;
+  background-color: white;
+  color: #207ab7;
+}
+.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section .tinymce-mobile-mask-tap-icon::before {
+  content: "\e900";
+  font-family: 'tinymce-mobile', sans-serif;
+}
+.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section:not(.tinymce-mobile-mask-tap-icon-selected) .tinymce-mobile-mask-tap-icon {
+  z-index: 2;
+}
+.tinymce-mobile-android-container.tinymce-mobile-android-maximized {
+  background: #ffffff;
+  border: none;
+  bottom: 0;
+  display: flex;
+  flex-direction: column;
+  left: 0;
+  position: fixed;
+  right: 0;
+  top: 0;
+}
+.tinymce-mobile-android-container:not(.tinymce-mobile-android-maximized) {
+  position: relative;
+}
+.tinymce-mobile-android-container .tinymce-mobile-editor-socket {
+  display: flex;
+  flex-grow: 1;
+}
+.tinymce-mobile-android-container .tinymce-mobile-editor-socket iframe {
+  display: flex !important;
+  flex-grow: 1;
+  height: auto !important;
+}
+.tinymce-mobile-android-scroll-reload {
+  overflow: hidden;
+}
+:not(.tinymce-mobile-readonly-mode) > .tinymce-mobile-android-selection-context-toolbar {
+  margin-top: 23px;
+}
+.tinymce-mobile-toolstrip {
+  background: #fff;
+  display: flex;
+  flex: 0 0 auto;
+  z-index: 1;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar {
+  align-items: center;
+  background-color: #fff;
+  border-bottom: 1px solid #cccccc;
+  display: flex;
+  flex: 1;
+  height: 2.5em;
+  width: 100%;
+  /* Make it no larger than the toolstrip, so that it needs to scroll */
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group {
+  align-items: center;
+  display: flex;
+  height: 100%;
+  flex-shrink: 1;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group > div {
+  align-items: center;
+  display: flex;
+  height: 100%;
+  flex: 1;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group.tinymce-mobile-exit-container {
+  background: #f44336;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group.tinymce-mobile-toolbar-scrollable-group {
+  flex-grow: 1;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item {
+  padding-left: 0.5em;
+  padding-right: 0.5em;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item.tinymce-mobile-toolbar-button {
+  align-items: center;
+  display: flex;
+  height: 80%;
+  margin-left: 2px;
+  margin-right: 2px;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item.tinymce-mobile-toolbar-button.tinymce-mobile-toolbar-button-selected {
+  background: #c8cbcf;
+  color: #cccccc;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group:first-of-type,
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group:last-of-type {
+  background: #207ab7;
+  color: #eceff1;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar {
+  /* Note, this file is imported inside .tinymce-mobile-context-toolbar, so that prefix is on everything here. */
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group {
+  align-items: center;
+  display: flex;
+  height: 100%;
+  flex: 1;
+  padding-bottom: 0.4em;
+  padding-top: 0.4em;
+  /* Make any buttons appearing on the left and right display in the centre (e.g. color edges) */
+  /* For widgets like the colour picker, use the whole height */
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog {
+  display: flex;
+  min-height: 1.5em;
+  overflow: hidden;
+  padding-left: 0;
+  padding-right: 0;
+  position: relative;
+  width: 100%;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain {
+  display: flex;
+  height: 100%;
+  transition: left cubic-bezier(0.4, 0, 1, 1) 0.15s;
+  width: 100%;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen {
+  display: flex;
+  flex: 0 0 auto;
+  justify-content: space-between;
+  width: 100%;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen input {
+  font-family: Sans-serif;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-input-container {
+  display: flex;
+  flex-grow: 1;
+  position: relative;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-input-container .tinymce-mobile-input-container-x {
+  -ms-grid-row-align: center;
+      align-self: center;
+  background: inherit;
+  border: none;
+  border-radius: 50%;
+  color: #888;
+  font-size: 0.6em;
+  font-weight: bold;
+  height: 100%;
+  padding-right: 2px;
+  position: absolute;
+  right: 0;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-input-container.tinymce-mobile-input-container-empty .tinymce-mobile-input-container-x {
+  display: none;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-previous,
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-next {
+  align-items: center;
+  display: flex;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-previous::before,
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-next::before {
+  align-items: center;
+  display: flex;
+  font-weight: bold;
+  height: 100%;
+  padding-left: 0.5em;
+  padding-right: 0.5em;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-previous.tinymce-mobile-toolbar-navigation-disabled::before,
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-next.tinymce-mobile-toolbar-navigation-disabled::before {
+  visibility: hidden;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-dot-item {
+  color: #cccccc;
+  font-size: 10px;
+  line-height: 10px;
+  margin: 0 2px;
+  padding-top: 3px;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-dot-item.tinymce-mobile-dot-active {
+  color: #c8cbcf;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-icon-large-font::before,
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-icon-large-heading::before {
+  margin-left: 0.5em;
+  margin-right: 0.9em;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-icon-small-font::before,
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-icon-small-heading::before {
+  margin-left: 0.9em;
+  margin-right: 0.5em;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider {
+  display: flex;
+  flex: 1;
+  margin-left: 0;
+  margin-right: 0;
+  padding: 0.28em 0;
+  position: relative;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider .tinymce-mobile-slider-size-container {
+  align-items: center;
+  display: flex;
+  flex-grow: 1;
+  height: 100%;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider .tinymce-mobile-slider-size-container .tinymce-mobile-slider-size-line {
+  background: #cccccc;
+  display: flex;
+  flex: 1;
+  height: 0.2em;
+  margin-bottom: 0.3em;
+  margin-top: 0.3em;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container {
+  padding-left: 2em;
+  padding-right: 2em;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container .tinymce-mobile-slider-gradient-container {
+  align-items: center;
+  display: flex;
+  flex-grow: 1;
+  height: 100%;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container .tinymce-mobile-slider-gradient-container .tinymce-mobile-slider-gradient {
+  background: linear-gradient(to right, hsl(0, 100%, 50%) 0%, hsl(60, 100%, 50%) 17%, hsl(120, 100%, 50%) 33%, hsl(180, 100%, 50%) 50%, hsl(240, 100%, 50%) 67%, hsl(300, 100%, 50%) 83%, hsl(0, 100%, 50%) 100%);
+  display: flex;
+  flex: 1;
+  height: 0.2em;
+  margin-bottom: 0.3em;
+  margin-top: 0.3em;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container .tinymce-mobile-hue-slider-black {
+  /* Not part of theming */
+  background: black;
+  height: 0.2em;
+  margin-bottom: 0.3em;
+  margin-top: 0.3em;
+  width: 1.2em;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container .tinymce-mobile-hue-slider-white {
+  /* Not part of theming */
+  background: white;
+  height: 0.2em;
+  margin-bottom: 0.3em;
+  margin-top: 0.3em;
+  width: 1.2em;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider .tinymce-mobile-slider-thumb {
+  /* vertically centering trick (margin: auto, top: 0, bottom: 0). On iOS and Safari, if you leave
+     * out these values, then it shows the thumb at the top of the spectrum. This is probably because it is
+     * absolutely positioned with only a left value, and not a top. Note, on Chrome it seems to be fine without
+     * this approach.
+    */
+  align-items: center;
+  background-clip: padding-box;
+  background-color: #455a64;
+  border: 0.5em solid rgba(136, 136, 136, 0);
+  border-radius: 3em;
+  bottom: 0;
+  color: #fff;
+  display: flex;
+  height: 0.5em;
+  justify-content: center;
+  left: -10px;
+  margin: auto;
+  position: absolute;
+  top: 0;
+  transition: border 120ms cubic-bezier(0.39, 0.58, 0.57, 1);
+  width: 0.5em;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider .tinymce-mobile-slider-thumb.tinymce-mobile-thumb-active {
+  border: 0.5em solid rgba(136, 136, 136, 0.39);
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serializer-wrapper,
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group > div {
+  align-items: center;
+  display: flex;
+  height: 100%;
+  flex: 1;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serializer-wrapper {
+  flex-direction: column;
+  justify-content: center;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item {
+  align-items: center;
+  display: flex;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item:not(.tinymce-mobile-serialised-dialog) {
+  height: 100%;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-dot-container {
+  display: flex;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group input {
+  background: #ffffff;
+  border: none;
+  border-radius: 0;
+  color: #455a64;
+  flex-grow: 1;
+  font-size: 0.85em;
+  padding-bottom: 0.1em;
+  padding-left: 5px;
+  padding-top: 0.1em;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group input::-webkit-input-placeholder {
+  /* WebKit, Blink, Edge */
+  color: #888;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group input::placeholder {
+  /* WebKit, Blink, Edge */
+  color: #888;
+}
+/* dropup */
+.tinymce-mobile-dropup {
+  background: white;
+  display: flex;
+  overflow: hidden;
+  width: 100%;
+}
+.tinymce-mobile-dropup.tinymce-mobile-dropup-shrinking {
+  transition: height 0.3s ease-out;
+}
+.tinymce-mobile-dropup.tinymce-mobile-dropup-growing {
+  transition: height 0.3s ease-in;
+}
+.tinymce-mobile-dropup.tinymce-mobile-dropup-closed {
+  flex-grow: 0;
+}
+.tinymce-mobile-dropup.tinymce-mobile-dropup-open:not(.tinymce-mobile-dropup-growing) {
+  flex-grow: 1;
+}
+/* TODO min-height for device size and orientation */
+.tinymce-mobile-ios-container .tinymce-mobile-dropup:not(.tinymce-mobile-dropup-closed) {
+  min-height: 200px;
+}
+@media only screen and (orientation: landscape) {
+  .tinymce-mobile-dropup:not(.tinymce-mobile-dropup-closed) {
+    min-height: 200px;
+  }
+}
+@media only screen and (min-device-width : 320px) and (max-device-width : 568px) and (orientation : landscape) {
+  .tinymce-mobile-ios-container .tinymce-mobile-dropup:not(.tinymce-mobile-dropup-closed) {
+    min-height: 150px;
+  }
+}
+/* styles menu */
+.tinymce-mobile-styles-menu {
+  font-family: sans-serif;
+  outline: 4px solid black;
+  overflow: hidden;
+  position: relative;
+  width: 100%;
+}
+.tinymce-mobile-styles-menu [role="menu"] {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+  position: absolute;
+  width: 100%;
+}
+.tinymce-mobile-styles-menu [role="menu"].transitioning {
+  transition: transform 0.5s ease-in-out;
+}
+.tinymce-mobile-styles-menu .tinymce-mobile-styles-item {
+  border-bottom: 1px solid #ddd;
+  color: #455a64;
+  cursor: pointer;
+  display: flex;
+  padding: 1em 1em;
+  position: relative;
+}
+.tinymce-mobile-styles-menu .tinymce-mobile-styles-collapser .tinymce-mobile-styles-collapse-icon::before {
+  color: #455a64;
+  content: "\e314";
+  font-family: 'tinymce-mobile', sans-serif;
+}
+.tinymce-mobile-styles-menu .tinymce-mobile-styles-item.tinymce-mobile-styles-item-is-menu::after {
+  color: #455a64;
+  content: "\e315";
+  font-family: 'tinymce-mobile', sans-serif;
+  padding-left: 1em;
+  padding-right: 1em;
+  position: absolute;
+  right: 0;
+}
+.tinymce-mobile-styles-menu .tinymce-mobile-styles-item.tinymce-mobile-format-matches::after {
+  font-family: 'tinymce-mobile', sans-serif;
+  padding-left: 1em;
+  padding-right: 1em;
+  position: absolute;
+  right: 0;
+}
+.tinymce-mobile-styles-menu .tinymce-mobile-styles-separator,
+.tinymce-mobile-styles-menu .tinymce-mobile-styles-collapser {
+  align-items: center;
+  background: #fff;
+  border-top: #455a64;
+  color: #455a64;
+  display: flex;
+  min-height: 2.5em;
+  padding-left: 1em;
+  padding-right: 1em;
+}
+.tinymce-mobile-styles-menu [data-transitioning-destination="before"][data-transitioning-state],
+.tinymce-mobile-styles-menu [data-transitioning-state="before"] {
+  transform: translate(-100%);
+}
+.tinymce-mobile-styles-menu [data-transitioning-destination="current"][data-transitioning-state],
+.tinymce-mobile-styles-menu [data-transitioning-state="current"] {
+  transform: translate(0%);
+}
+.tinymce-mobile-styles-menu [data-transitioning-destination="after"][data-transitioning-state],
+.tinymce-mobile-styles-menu [data-transitioning-state="after"] {
+  transform: translate(100%);
+}
+@font-face {
+  font-family: 'tinymce-mobile';
+  font-style: normal;
+  font-weight: normal;
+  src: url('fonts/tinymce-mobile.woff?8x92w3') format('woff');
+}
+@media (min-device-width: 700px) {
+  .tinymce-mobile-outer-container,
+  .tinymce-mobile-outer-container input {
+    font-size: 25px;
+  }
+}
+@media (max-device-width: 700px) {
+  .tinymce-mobile-outer-container,
+  .tinymce-mobile-outer-container input {
+    font-size: 18px;
+  }
+}
+.tinymce-mobile-icon {
+  font-family: 'tinymce-mobile', sans-serif;
+}
+.mixin-flex-and-centre {
+  align-items: center;
+  display: flex;
+  justify-content: center;
+}
+.mixin-flex-bar {
+  align-items: center;
+  display: flex;
+  height: 100%;
+}
+.tinymce-mobile-outer-container .tinymce-mobile-editor-socket iframe {
+  background-color: #fff;
+  width: 100%;
+}
+.tinymce-mobile-editor-socket .tinymce-mobile-mask-edit-icon {
+  /* Note, on the iPod touch in landscape, this isn't visible when the navbar appears */
+  background-color: #207ab7;
+  border-radius: 50%;
+  bottom: 1em;
+  color: white;
+  font-size: 1em;
+  height: 2.1em;
+  position: fixed;
+  right: 2em;
+  width: 2.1em;
+  align-items: center;
+  display: flex;
+  justify-content: center;
+}
+@media only screen and (min-device-width:700px) {
+  .tinymce-mobile-editor-socket .tinymce-mobile-mask-edit-icon {
+    font-size: 1.2em;
+  }
+}
+.tinymce-mobile-outer-container:not(.tinymce-mobile-fullscreen-maximized) .tinymce-mobile-editor-socket {
+  height: 300px;
+  overflow: hidden;
+}
+.tinymce-mobile-outer-container:not(.tinymce-mobile-fullscreen-maximized) .tinymce-mobile-editor-socket iframe {
+  height: 100%;
+}
+.tinymce-mobile-outer-container:not(.tinymce-mobile-fullscreen-maximized) .tinymce-mobile-toolstrip {
+  display: none;
+}
+/*
+  Note, that if you don't include this (::-webkit-file-upload-button), the toolbar width gets
+  increased and the whole body becomes scrollable. It's important!
+ */
+input[type="file"]::-webkit-file-upload-button {
+  display: none;
+}
+@media only screen and (min-device-width : 320px) and (max-device-width : 568px) and (orientation : landscape) {
+  .tinymce-mobile-ios-container .tinymce-mobile-editor-socket .tinymce-mobile-mask-edit-icon {
+    bottom: 50%;
+  }
+}

File diff suppressed because it is too large
+ 7 - 0
public/tinymce/skins/ui/oxide/skin.mobile.min.css


+ 230 - 0
src/App.vue

@@ -0,0 +1,230 @@
+<script>
+    import Vue from 'vue'
+    export default {
+        onLaunch: function() {
+            Vue.prototype.ColorList = [{
+                title: '嫣红',
+                name: 'red',
+                color: '#e54d42'
+            },
+                {
+                    title: '桔橙',
+                    name: 'orange',
+                    color: '#f37b1d'
+                },
+                {
+                    title: '明黄',
+                    name: 'yellow',
+                    color: '#fbbd08'
+                },
+                {
+                    title: '橄榄',
+                    name: 'olive',
+                    color: '#8dc63f'
+                },
+                {
+                    title: '森绿',
+                    name: 'green',
+                    color: '#39b54a'
+                },
+                {
+                    title: '天青',
+                    name: 'cyan',
+                    color: '#1cbbb4'
+                },
+                {
+                    title: '海蓝',
+                    name: 'blue',
+                    color: '#0081ff'
+                },
+                {
+                    title: '姹紫',
+                    name: 'purple',
+                    color: '#6739b6'
+                },
+                {
+                    title: '木槿',
+                    name: 'mauve',
+                    color: '#9c26b0'
+                },
+                {
+                    title: '桃粉',
+                    name: 'pink',
+                    color: '#e03997'
+                },
+                {
+                    title: '棕褐',
+                    name: 'brown',
+                    color: '#a5673f'
+                },
+                {
+                    title: '玄灰',
+                    name: 'grey',
+                    color: '#8799a3'
+                },
+                {
+                    title: '草灰',
+                    name: 'gray',
+                    color: '#aaaaaa'
+                },
+                {
+                    title: '墨黑',
+                    name: 'black',
+                    color: '#333333'
+                },
+                {
+                    title: '雅白',
+                    name: 'white',
+                    color: '#ffffff'
+                },
+            ]
+        },
+        onShow: function() {
+            console.log('App Show')
+        },
+        onHide: function() {
+            console.log('App Hide')
+        },
+		globalData: {
+			test: ''
+		}
+    }
+</script>
+
+<style>
+    /* #ifndef APP-PLUS-NVUE */
+    /* uni.css - 通用组件、模板样式库,可以当作一套ui库应用 */
+    @import './common/uni.css';
+    @import "colorui/main.css";
+    @import "colorui/icon.css";
+
+    .nav-list {
+        display: flex;
+        flex-wrap: wrap;
+        padding: 0px 40upx 0px;
+        justify-content: space-between;
+    }
+
+    .nav-li {
+        padding: 30upx;
+        border-radius: 12upx;
+        width: 45%;
+        margin: 0 2.5% 40upx;
+        background-image: url(https://cdn.nlark.com/yuque/0/2019/png/280374/1552996358352-assets/web-upload/cc3b1807-c684-4b83-8f80-80e5b8a6b975.png);
+        background-size: cover;
+        background-position: center;
+        position: relative;
+        z-index: 1;
+    }
+
+    .nav-li::after {
+        content: "";
+        position: absolute;
+        z-index: -1;
+        background-color: inherit;
+        width: 100%;
+        height: 100%;
+        left: 0;
+        bottom: -10%;
+        border-radius: 10upx;
+        opacity: 0.2;
+        transform: scale(0.9, 0.9);
+    }
+
+    .nav-li.cur {
+        color: #fff;
+        background: rgb(94, 185, 94);
+        box-shadow: 4upx 4upx 6upx rgba(94, 185, 94, 0.4);
+    }
+
+    .nav-title {
+        font-size: 32upx;
+        font-weight: 300;
+    }
+
+    .nav-title::first-letter {
+        font-size: 40upx;
+        margin-right: 4upx;
+    }
+
+    .nav-name {
+        font-size: 28upx;
+        text-transform: Capitalize;
+        margin-top: 20upx;
+        position: relative;
+    }
+
+    .nav-name::before {
+        content: "";
+        position: absolute;
+        display: block;
+        width: 40upx;
+        height: 6upx;
+        background: #fff;
+        bottom: 0;
+        right: 0;
+        opacity: 0.5;
+    }
+
+    .nav-name::after {
+        content: "";
+        position: absolute;
+        display: block;
+        width: 100upx;
+        height: 1px;
+        background: #fff;
+        bottom: 0;
+        right: 40upx;
+        opacity: 0.3;
+    }
+
+    .nav-name::first-letter {
+        font-weight: bold;
+        font-size: 36upx;
+        margin-right: 1px;
+    }
+
+    .nav-li text {
+        position: absolute;
+        right: 30upx;
+        top: 30upx;
+        font-size: 52upx;
+        width: 60upx;
+        height: 60upx;
+        text-align: center;
+        line-height: 60upx;
+    }
+
+    .text-light {
+        font-weight: 300;
+    }
+
+    @keyframes show {
+        0% {
+            transform: translateY(-50px);
+        }
+
+        60% {
+            transform: translateY(40upx);
+        }
+
+        100% {
+            transform: translateY(0px);
+        }
+    }
+
+    @-webkit-keyframes show {
+        0% {
+            transform: translateY(-50px);
+        }
+
+        60% {
+            transform: translateY(40upx);
+        }
+
+        100% {
+            transform: translateY(0px);
+        }
+    }
+    /*http://www.88dushu.cc/shu/7417-1*/
+</style>

+ 21 - 0
src/LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018 DCloud
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 14 - 0
src/README.md

@@ -0,0 +1,14 @@
+# hello-uniapp
+
+`uni-app`框架示例,一套代码,同时发行到iOS、Android、H5、小程序等多个平台,请使用手机扫码快速体验`uni-app`的强大功能。
+
+<p align="center">
+    <a href="https://m3w.cn/uniapp" target="blank">
+        <img src="https://img-cdn-qiniu.dcloud.net.cn/uni-app-qr-all.jpg"/>
+    </a>
+</p>
+
+`uni-app`官网文档详见[https://uniapp.dcloud.io](https://uniapp.dcloud.io)
+
+更多uni-app的模板、示例详见[插件市场](https://ext.dcloud.net.cn/)
+

+ 184 - 0
src/colorui/animation.css

@@ -0,0 +1,184 @@
+/* 
+  Animation 微动画  
+  基于ColorUI组建库的动画模块 by 文晓港 2019年3月26日19:52:28
+ */
+
+/* css 滤镜 控制黑白底色gif的 */
+.gif-black{  
+  mix-blend-mode: screen;  
+}
+.gif-white{  
+  mix-blend-mode: multiply; 
+}
+
+
+/* Animation css */
+[class*=animation-] {
+    animation-duration: .5s;
+    animation-timing-function: ease-out;
+    animation-fill-mode: both
+}
+
+.animation-fade {
+    animation-name: fade;
+    animation-duration: .8s;
+    animation-timing-function: linear
+}
+
+.animation-scale-up {
+    animation-name: scale-up
+}
+
+.animation-scale-down {
+    animation-name: scale-down
+}
+
+.animation-slide-top {
+    animation-name: slide-top
+}
+
+.animation-slide-bottom {
+    animation-name: slide-bottom
+}
+
+.animation-slide-left {
+    animation-name: slide-left
+}
+
+.animation-slide-right {
+    animation-name: slide-right
+}
+
+.animation-shake {
+    animation-name: shake
+}
+
+.animation-reverse {
+    animation-direction: reverse
+}
+
+@keyframes fade {
+    0% {
+        opacity: 0
+    }
+
+    100% {
+        opacity: 1
+    }
+}
+
+@keyframes scale-up {
+    0% {
+        opacity: 0;
+        transform: scale(.2)
+    }
+
+    100% {
+        opacity: 1;
+        transform: scale(1)
+    }
+}
+
+@keyframes scale-down {
+    0% {
+        opacity: 0;
+        transform: scale(1.8)
+    }
+
+    100% {
+        opacity: 1;
+        transform: scale(1)
+    }
+}
+
+@keyframes slide-top {
+    0% {
+        opacity: 0;
+        transform: translateY(-100%)
+    }
+
+    100% {
+        opacity: 1;
+        transform: translateY(0)
+    }
+}
+
+@keyframes slide-bottom {
+    0% {
+        opacity: 0;
+        transform: translateY(100%)
+    }
+
+    100% {
+        opacity: 1;
+        transform: translateY(0)
+    }
+}
+
+@keyframes shake {
+
+    0%,
+    100% {
+        transform: translateX(0)
+    }
+
+    10% {
+        transform: translateX(-9px)
+    }
+
+    20% {
+        transform: translateX(8px)
+    }
+
+    30% {
+        transform: translateX(-7px)
+    }
+
+    40% {
+        transform: translateX(6px)
+    }
+
+    50% {
+        transform: translateX(-5px)
+    }
+
+    60% {
+        transform: translateX(4px)
+    }
+
+    70% {
+        transform: translateX(-3px)
+    }
+
+    80% {
+        transform: translateX(2px)
+    }
+
+    90% {
+        transform: translateX(-1px)
+    }
+}
+
+@keyframes slide-left {
+    0% {
+        opacity: 0;
+        transform: translateX(-100%)
+    }
+
+    100% {
+        opacity: 1;
+        transform: translateX(0)
+    }
+}
+
+@keyframes slide-right {
+    0% {
+        opacity: 0;
+        transform: translateX(100%)
+    }
+
+    100% {
+        opacity: 1;
+        transform: translateX(0)
+    }
+}

+ 69 - 0
src/colorui/components/cu-custom.vue

@@ -0,0 +1,69 @@
+<template>
+	<view>
+		<view class="cu-custom" :style="[{height:CustomBar + 'px'}]">
+			<view class="cu-bar fixed" :style="style" :class="[bgImage!=''?'none-bg text-white bg-img':'',bgColor]">
+				<view class="action" @tap="BackPage" v-if="isBack">
+					<text class="cuIcon-back"></text>
+					<slot name="backText"></slot>
+				</view>
+				<view class="content" :style="[{top:StatusBar + 'px'}]">
+					<slot name="content"></slot>
+				</view>
+				<slot name="right"></slot>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				StatusBar: this.StatusBar,
+				CustomBar: this.CustomBar
+			};
+		},
+		name: 'cu-custom',
+		computed: {
+			style() {
+				var StatusBar= this.StatusBar;
+				var CustomBar= this.CustomBar;
+				var bgImage = this.bgImage;
+				var style = `height:${CustomBar}px;padding-top:${StatusBar}px;`;
+				if (this.bgImage) {
+					style = `${style}background-image:url(${bgImage});`;
+				}
+				return style
+			}
+		},
+		props: {
+			bgColor: {
+				type: String,
+				default: ''
+			},
+			isBack: {
+				type: [Boolean, String],
+				default: false
+			},
+			bgImage: {
+				type: String,
+				default: ''
+			},
+		},
+		methods: {
+			BackPage() {
+				if (getCurrentPages().length < 2 && 'undefined' !== typeof __wxConfig) {
+					let url = '/' + __wxConfig.pages[0]
+					return uni.redirectTo({url})
+				}
+				uni.navigateBack({
+					delta: 1
+				});
+			}
+		}
+	}
+</script>
+
+<style>
+
+</style>

File diff suppressed because it is too large
+ 1226 - 0
src/colorui/icon.css


File diff suppressed because it is too large
+ 3912 - 0
src/colorui/main.css


+ 97 - 0
src/common/graceChecker.js

@@ -0,0 +1,97 @@
+/**
+数据验证(表单验证)
+来自 grace.hcoder.net 
+作者 hcoder 深海
+*/
+module.exports = {
+	error:'',
+	check : function (data, rule){
+		for(var i = 0; i < rule.length; i++){
+			if (!rule[i].checkType){return true;}
+			if (!rule[i].name) {return true;}
+			if (!rule[i].errorMsg) {return true;}
+			if (!data[rule[i].name]) {this.error = rule[i].errorMsg; return false;}
+			switch (rule[i].checkType){
+				case 'string':
+					var reg = new RegExp('^.{' + rule[i].checkRule + '}$');
+					if(!reg.test(data[rule[i].name])) {this.error = rule[i].errorMsg; return false;}
+				break;
+				case 'int':
+					var reg = new RegExp('^(-[1-9]|[1-9])[0-9]{' + rule[i].checkRule + '}$');
+					if(!reg.test(data[rule[i].name])) {this.error = rule[i].errorMsg; return false;}
+					break;
+				break;
+				case 'between':
+					if (!this.isNumber(data[rule[i].name])){
+						this.error = rule[i].errorMsg;
+						return false;
+					}
+					var minMax = rule[i].checkRule.split(',');
+					minMax[0] = Number(minMax[0]);
+					minMax[1] = Number(minMax[1]);
+					if (data[rule[i].name] > minMax[1] || data[rule[i].name] < minMax[0]) {
+						this.error = rule[i].errorMsg;
+						return false;
+					}
+				break;
+				case 'betweenD':
+					var reg = /^-?[1-9][0-9]?$/;
+					if (!reg.test(data[rule[i].name])) { this.error = rule[i].errorMsg; return false; }
+					var minMax = rule[i].checkRule.split(',');
+					minMax[0] = Number(minMax[0]);
+					minMax[1] = Number(minMax[1]);
+					if (data[rule[i].name] > minMax[1] || data[rule[i].name] < minMax[0]) {
+						this.error = rule[i].errorMsg;
+						return false;
+					}
+				break;
+				case 'betweenF': 
+					var reg = /^-?[0-9][0-9]?.+[0-9]+$/;
+					if (!reg.test(data[rule[i].name])){this.error = rule[i].errorMsg; return false;}
+					var minMax = rule[i].checkRule.split(',');
+					minMax[0] = Number(minMax[0]);
+					minMax[1] = Number(minMax[1]);
+					if (data[rule[i].name] > minMax[1] || data[rule[i].name] < minMax[0]) {
+						this.error = rule[i].errorMsg;
+						return false;
+					}
+				break;
+				case 'same':
+					if (data[rule[i].name] != rule[i].checkRule) { this.error = rule[i].errorMsg; return false;}
+				break;
+				case 'notsame':
+					if (data[rule[i].name] == rule[i].checkRule) { this.error = rule[i].errorMsg; return false; }
+				break;
+				case 'email':
+					var reg = /^\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/;
+					if (!reg.test(data[rule[i].name])) { this.error = rule[i].errorMsg; return false; }
+				break;
+				case 'phoneno':
+					var reg = /^1[0-9]{10,10}$/;
+					if (!reg.test(data[rule[i].name])) { this.error = rule[i].errorMsg; return false; }
+				break;
+				case 'zipcode':
+					var reg = /^[0-9]{6}$/;
+					if (!reg.test(data[rule[i].name])) { this.error = rule[i].errorMsg; return false; }
+				break;
+				case 'reg':
+					var reg = new RegExp(rule[i].checkRule);
+					if (!reg.test(data[rule[i].name])) { this.error = rule[i].errorMsg; return false; }
+				break;
+				case 'in':
+					if(rule[i].checkRule.indexOf(data[rule[i].name]) == -1){
+						this.error = rule[i].errorMsg; return false;
+					}
+				break;
+				case 'notnull':
+					if(data[rule[i].name] == null || data[rule[i].name].length < 1){this.error = rule[i].errorMsg; return false;}
+				break;
+			}
+		}
+		return true;
+	},
+	isNumber : function (checkVal){
+		var reg = /^-?[1-9][0-9]?.?[0-9]*$/;
+		return reg.test(checkVal);
+	}
+}

+ 352 - 0
src/common/html-parser.js

@@ -0,0 +1,352 @@
+/*
+ * HTML5 Parser By Sam Blowes
+ *
+ * Designed for HTML5 documents
+ *
+ * Original code by John Resig (ejohn.org)
+ * http://ejohn.org/blog/pure-javascript-html-parser/
+ * Original code by Erik Arvidsson, Mozilla Public License
+ * http://erik.eae.net/simplehtmlparser/simplehtmlparser.js
+ *
+ * ----------------------------------------------------------------------------
+ * License
+ * ----------------------------------------------------------------------------
+ *
+ * This code is triple licensed using Apache Software License 2.0,
+ * Mozilla Public License or GNU Public License
+ *
+ * ////////////////////////////////////////////////////////////////////////////
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License.  You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ////////////////////////////////////////////////////////////////////////////
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+ * License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * The Original Code is Simple HTML Parser.
+ *
+ * The Initial Developer of the Original Code is Erik Arvidsson.
+ * Portions created by Erik Arvidssson are Copyright (C) 2004. All Rights
+ * Reserved.
+ *
+ * ////////////////////////////////////////////////////////////////////////////
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ * ----------------------------------------------------------------------------
+ * Usage
+ * ----------------------------------------------------------------------------
+ *
+ * // Use like so:
+ * HTMLParser(htmlString, {
+ *     start: function(tag, attrs, unary) {},
+ *     end: function(tag) {},
+ *     chars: function(text) {},
+ *     comment: function(text) {}
+ * });
+ *
+ * // or to get an XML string:
+ * HTMLtoXML(htmlString);
+ *
+ * // or to get an XML DOM Document
+ * HTMLtoDOM(htmlString);
+ *
+ * // or to inject into an existing document/DOM node
+ * HTMLtoDOM(htmlString, document);
+ * HTMLtoDOM(htmlString, document.body);
+ *
+ */
+// Regular Expressions for parsing tags and attributes
+var startTag = /^<([-A-Za-z0-9_]+)((?:\s+[a-zA-Z_:][-a-zA-Z0-9_:.]*(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/;
+var endTag = /^<\/([-A-Za-z0-9_]+)[^>]*>/;
+var attr = /([a-zA-Z_:][-a-zA-Z0-9_:.]*)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g; // Empty Elements - HTML 5
+
+var empty = makeMap('area,base,basefont,br,col,frame,hr,img,input,link,meta,param,embed,command,keygen,source,track,wbr'); // Block Elements - HTML 5
+// fixed by xxx 将 ins 标签从块级名单中移除
+
+var block = makeMap('a,address,article,applet,aside,audio,blockquote,button,canvas,center,dd,del,dir,div,dl,dt,fieldset,figcaption,figure,footer,form,frameset,h1,h2,h3,h4,h5,h6,header,hgroup,hr,iframe,isindex,li,map,menu,noframes,noscript,object,ol,output,p,pre,section,script,table,tbody,td,tfoot,th,thead,tr,ul,video'); // Inline Elements - HTML 5
+
+var inline = makeMap('abbr,acronym,applet,b,basefont,bdo,big,br,button,cite,code,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,textarea,tt,u,var'); // Elements that you can, intentionally, leave open
+// (and which close themselves)
+
+var closeSelf = makeMap('colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr'); // Attributes that have their values filled in disabled="disabled"
+
+var fillAttrs = makeMap('checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected'); // Special Elements (can contain anything)
+
+var special = makeMap('script,style');
+function HTMLParser(html, handler) {
+  var index;
+  var chars;
+  var match;
+  var stack = [];
+  var last = html;
+
+  stack.last = function () {
+    return this[this.length - 1];
+  };
+
+  while (html) {
+    chars = true; // Make sure we're not in a script or style element
+
+    if (!stack.last() || !special[stack.last()]) {
+      // Comment
+      if (html.indexOf('<!--') == 0) {
+        index = html.indexOf('-->');
+
+        if (index >= 0) {
+          if (handler.comment) {
+            handler.comment(html.substring(4, index));
+          }
+
+          html = html.substring(index + 3);
+          chars = false;
+        } // end tag
+
+      } else if (html.indexOf('</') == 0) {
+        match = html.match(endTag);
+
+        if (match) {
+          html = html.substring(match[0].length);
+          match[0].replace(endTag, parseEndTag);
+          chars = false;
+        } // start tag
+
+      } else if (html.indexOf('<') == 0) {
+        match = html.match(startTag);
+
+        if (match) {
+          html = html.substring(match[0].length);
+          match[0].replace(startTag, parseStartTag);
+          chars = false;
+        }
+      }
+
+      if (chars) {
+        index = html.indexOf('<');
+        var text = index < 0 ? html : html.substring(0, index);
+        html = index < 0 ? '' : html.substring(index);
+
+        if (handler.chars) {
+          handler.chars(text);
+        }
+      }
+    } else {
+      html = html.replace(new RegExp('([\\s\\S]*?)<\/' + stack.last() + '[^>]*>'), function (all, text) {
+        text = text.replace(/<!--([\s\S]*?)-->|<!\[CDATA\[([\s\S]*?)]]>/g, '$1$2');
+
+        if (handler.chars) {
+          handler.chars(text);
+        }
+
+        return '';
+      });
+      parseEndTag('', stack.last());
+    }
+
+    if (html == last) {
+      throw 'Parse Error: ' + html;
+    }
+
+    last = html;
+  } // Clean up any remaining tags
+
+
+  parseEndTag();
+
+  function parseStartTag(tag, tagName, rest, unary) {
+    tagName = tagName.toLowerCase();
+
+    if (block[tagName]) {
+      while (stack.last() && inline[stack.last()]) {
+        parseEndTag('', stack.last());
+      }
+    }
+
+    if (closeSelf[tagName] && stack.last() == tagName) {
+      parseEndTag('', tagName);
+    }
+
+    unary = empty[tagName] || !!unary;
+
+    if (!unary) {
+      stack.push(tagName);
+    }
+
+    if (handler.start) {
+      var attrs = [];
+      rest.replace(attr, function (match, name) {
+        var value = arguments[2] ? arguments[2] : arguments[3] ? arguments[3] : arguments[4] ? arguments[4] : fillAttrs[name] ? name : '';
+        attrs.push({
+          name: name,
+          value: value,
+          escaped: value.replace(/(^|[^\\])"/g, '$1\\\"') // "
+
+        });
+      });
+
+      if (handler.start) {
+        handler.start(tagName, attrs, unary);
+      }
+    }
+  }
+
+  function parseEndTag(tag, tagName) {
+    // If no tag name is provided, clean shop
+    if (!tagName) {
+      var pos = 0;
+    } // Find the closest opened tag of the same type
+    else {
+        for (var pos = stack.length - 1; pos >= 0; pos--) {
+          if (stack[pos] == tagName) {
+            break;
+          }
+        }
+      }
+
+    if (pos >= 0) {
+      // Close all the open elements, up the stack
+      for (var i = stack.length - 1; i >= pos; i--) {
+        if (handler.end) {
+          handler.end(stack[i]);
+        }
+      } // Remove the open elements from the stack
+
+
+      stack.length = pos;
+    }
+  }
+}
+
+function makeMap(str) {
+  var obj = {};
+  var items = str.split(',');
+
+  for (var i = 0; i < items.length; i++) {
+    obj[items[i]] = true;
+  }
+
+  return obj;
+}
+
+function removeDOCTYPE(html) {
+  return html.replace(/<\?xml.*\?>\n/, '').replace(/<!doctype.*>\n/, '').replace(/<!DOCTYPE.*>\n/, '');
+}
+
+function parseAttrs(attrs) {
+  return attrs.reduce(function (pre, attr) {
+    var value = attr.value;
+    var name = attr.name;
+
+    if (pre[name]) {
+			pre[name] = pre[name] + " " + value;
+    } else {
+			pre[name] = value;
+    }
+
+    return pre;
+  }, {});
+}
+
+function parseHtml(html) {
+  html = removeDOCTYPE(html);
+  var stacks = [];
+  var results = {
+    node: 'root',
+    children: []
+  };
+  HTMLParser(html, {
+    start: function start(tag, attrs, unary) {
+      var node = {
+        name: tag
+      };
+
+      if (attrs.length !== 0) {
+        node.attrs = parseAttrs(attrs);
+      }
+
+      if (unary) {
+        var parent = stacks[0] || results;
+
+        if (!parent.children) {
+          parent.children = [];
+        }
+
+        parent.children.push(node);
+      } else {
+        stacks.unshift(node);
+      }
+    },
+    end: function end(tag) {
+      var node = stacks.shift();
+      if (node.name !== tag) console.error('invalid state: mismatch end tag');
+
+      if (stacks.length === 0) {
+        results.children.push(node);
+      } else {
+        var parent = stacks[0];
+
+        if (!parent.children) {
+          parent.children = [];
+        }
+
+        parent.children.push(node);
+      }
+    },
+    chars: function chars(text) {
+      var node = {
+        type: 'text',
+        text: text
+      };
+
+      if (stacks.length === 0) {
+        results.children.push(node);
+      } else {
+        var parent = stacks[0];
+
+        if (!parent.children) {
+          parent.children = [];
+        }
+
+        parent.children.push(node);
+      }
+    },
+    comment: function comment(text) {
+      var node = {
+        node: 'comment',
+        text: text
+      };
+      var parent = stacks[0];
+
+      if (!parent.children) {
+        parent.children = [];
+      }
+
+      parent.children.push(node);
+    }
+  });
+  return results.children;
+}
+
+export default parseHtml;

+ 0 - 0
src/common/ning


+ 245 - 0
src/common/permission.js

@@ -0,0 +1,245 @@
+/// null = 未请求,1 = 已允许,0 = 拒绝|受限, 2 = 系统未开启
+
+var isIOS
+
+function album() {
+    var result = 0;
+    var PHPhotoLibrary = plus.ios.import("PHPhotoLibrary");
+    var authStatus = PHPhotoLibrary.authorizationStatus();
+    if (authStatus === 0) {
+        result = null;
+    } else if (authStatus == 3) {
+        result = 1;
+    } else {
+        result = 0;
+    }
+    plus.ios.deleteObject(PHPhotoLibrary);
+    return result;
+}
+
+function camera() {
+    var result = 0;
+    var AVCaptureDevice = plus.ios.import("AVCaptureDevice");
+    var authStatus = AVCaptureDevice.authorizationStatusForMediaType('vide');
+    if (authStatus === 0) {
+        result = null;
+    } else if (authStatus == 3) {
+        result = 1;
+    } else {
+        result = 0;
+    }
+    plus.ios.deleteObject(AVCaptureDevice);
+    return result;
+}
+
+function location() {
+    var result = 0;
+    var cllocationManger = plus.ios.import("CLLocationManager");
+    var enable = cllocationManger.locationServicesEnabled();
+    var status = cllocationManger.authorizationStatus();
+    if (!enable) {
+        result = 2;
+    } else if (status === 0) {
+        result = null;
+    } else if (status === 3 || status === 4) {
+        result = 1;
+    } else {
+        result = 0;
+    }
+    plus.ios.deleteObject(cllocationManger);
+    return result;
+}
+
+function push() {
+    var result = 0;
+    var UIApplication = plus.ios.import("UIApplication");
+    var app = UIApplication.sharedApplication();
+    var enabledTypes = 0;
+    if (app.currentUserNotificationSettings) {
+        var settings = app.currentUserNotificationSettings();
+        enabledTypes = settings.plusGetAttribute("types");
+        if (enabledTypes == 0) {
+            result = 0;
+            console.log("推送权限没有开启");
+        } else {
+            result = 1;
+            console.log("已经开启推送功能!")
+        }
+        plus.ios.deleteObject(settings);
+    } else {
+        enabledTypes = app.enabledRemoteNotificationTypes();
+        if (enabledTypes == 0) {
+            result = 3;
+            console.log("推送权限没有开启!");
+        } else {
+            result = 4;
+            console.log("已经开启推送功能!")
+        }
+    }
+    plus.ios.deleteObject(app);
+    plus.ios.deleteObject(UIApplication);
+    return result;
+}
+
+function contact() {
+    var result = 0;
+    var CNContactStore = plus.ios.import("CNContactStore");
+    var cnAuthStatus = CNContactStore.authorizationStatusForEntityType(0);
+    if (cnAuthStatus === 0) {
+        result = null;
+    } else if (cnAuthStatus == 3) {
+        result = 1;
+    } else {
+        result = 0;
+    }
+    plus.ios.deleteObject(CNContactStore);
+    return result;
+}
+
+function record() {
+    var result = null;
+    var avaudiosession = plus.ios.import("AVAudioSession");
+    var avaudio = avaudiosession.sharedInstance();
+    var status = avaudio.recordPermission();
+    console.log("permissionStatus:" + status);
+    if (status === 1970168948) {
+        result = null;
+    } else if (status === 1735552628) {
+        result = 1;
+    } else {
+        result = 0;
+    }
+    plus.ios.deleteObject(avaudiosession);
+    return result;
+}
+
+function calendar() {
+    var result = null;
+    var EKEventStore = plus.ios.import("EKEventStore");
+    var ekAuthStatus = EKEventStore.authorizationStatusForEntityType(0);
+    if (ekAuthStatus == 3) {
+        result = 1;
+        console.log("日历权限已经开启");
+    } else {
+        console.log("日历权限没有开启");
+    }
+    plus.ios.deleteObject(EKEventStore);
+    return result;
+}
+
+function memo() {
+    var result = null;
+    var EKEventStore = plus.ios.import("EKEventStore");
+    var ekAuthStatus = EKEventStore.authorizationStatusForEntityType(1);
+    if (ekAuthStatus == 3) {
+        result = 1;
+        console.log("备忘录权限已经开启");
+    } else {
+        console.log("备忘录权限没有开启");
+    }
+    plus.ios.deleteObject(EKEventStore);
+    return result;
+}
+
+
+function requestIOS(permissionID) {
+    return new Promise((resolve, reject) => {
+        switch (permissionID) {
+            case "push":
+                resolve(push());
+                break;
+            case "location":
+                resolve(location());
+                break;
+            case "record":
+                resolve(record());
+                break;
+            case "camera":
+                resolve(camera());
+                break;
+            case "album":
+                resolve(album());
+                break;
+            case "contact":
+                resolve(contact());
+                break;
+            case "calendar":
+                resolve(calendar());
+                break;
+            case "memo":
+                resolve(memo());
+                break;
+            default:
+                resolve(0);
+                break;
+        }
+    });
+}
+
+function requestAndroid(permissionID) {
+    return new Promise((resolve, reject) => {
+        plus.android.requestPermissions(
+            [permissionID],
+            function(resultObj) {
+                var result = 0;
+                for (var i = 0; i < resultObj.granted.length; i++) {
+                    var grantedPermission = resultObj.granted[i];
+                    console.log('已获取的权限:' + grantedPermission);
+                    result = 1
+                }
+                for (var i = 0; i < resultObj.deniedPresent.length; i++) {
+                    var deniedPresentPermission = resultObj.deniedPresent[i];
+                    console.log('拒绝本次申请的权限:' + deniedPresentPermission);
+                    result = 0
+                }
+                for (var i = 0; i < resultObj.deniedAlways.length; i++) {
+                    var deniedAlwaysPermission = resultObj.deniedAlways[i];
+                    console.log('永久拒绝申请的权限:' + deniedAlwaysPermission);
+                    result = -1
+                }
+                resolve(result);
+            },
+            function(error) {
+                console.log('result error: ' + error.message)
+                resolve({
+                    code: error.code,
+                    message: error.message
+                });
+            }
+        );
+    });
+}
+
+function gotoAppPermissionSetting() {
+    if (permission.isIOS) {
+        var UIApplication = plus.ios.import("UIApplication");
+        var application2 = UIApplication.sharedApplication();
+        var NSURL2 = plus.ios.import("NSURL");
+        var setting2 = NSURL2.URLWithString("app-settings:");
+        application2.openURL(setting2);
+        plus.ios.deleteObject(setting2);
+        plus.ios.deleteObject(NSURL2);
+        plus.ios.deleteObject(application2);
+    } else {
+        var Intent = plus.android.importClass("android.content.Intent");
+        var Settings = plus.android.importClass("android.provider.Settings");
+        var Uri = plus.android.importClass("android.net.Uri");
+        var mainActivity = plus.android.runtimeMainActivity();
+        var intent = new Intent();
+        intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+        var uri = Uri.fromParts("package", mainActivity.getPackageName(), null);
+        intent.setData(uri);
+        mainActivity.startActivity(intent);
+    }
+}
+
+const permission = {
+    get isIOS(){
+        return typeof isIOS === 'boolean' ? isIOS : (isIOS = uni.getSystemInfoSync().platform === 'ios')
+    },
+    requestIOS: requestIOS,
+    requestAndroid: requestAndroid,
+    gotoAppSetting: gotoAppPermissionSetting
+}
+
+module.exports = permission

+ 120 - 0
src/common/uni-nvue.css

@@ -0,0 +1,120 @@
+/* #ifndef APP-PLUS-NVUE */
+page {
+    min-height: 100%;
+    height: auto;
+}
+/* #endif */
+
+/* 解决头条小程序字体图标不显示问题,因为头条运行时自动插入了span标签,且有全局字体 */
+/* #ifdef MP-TOUTIAO */
+/* text :not(view) {
+    font-family: uniicons;
+} */
+/* #endif */
+
+.uni-icon {
+    font-family: uniicons;
+    font-weight: normal;
+}
+
+.uni-container {
+    padding: 15px;
+    background-color: #f8f8f8;
+}
+
+.uni-header-logo {
+    padding: 15px 15px;
+    flex-direction: column;
+    justify-content: center;
+    align-items: center;
+    margin-top: 10rpx;
+}
+
+.uni-header-image {
+    width: 80px;
+    height: 80px;
+}
+
+.uni-hello-text {
+    margin-bottom: 20px;
+}
+
+.hello-text {
+    color: #7A7E83;
+    font-size: 14px;
+    line-height: 20px;
+}
+
+.hello-link {
+    color: #7A7E83;
+    font-size: 14px;
+    line-height: 20px;
+}
+
+.uni-panel {
+    margin-bottom: 12px;
+}
+
+.uni-panel-h {
+    background-color: #ffffff;
+    flex-direction: row;
+    align-items: center;
+    padding: 12px;
+}
+/*
+.uni-panel-h:active {
+    background-color: #f8f8f8;
+}
+ */
+.uni-panel-h-on {
+    background-color: #f0f0f0;
+}
+
+.uni-panel-text {
+    flex: 1;
+    color: #000000;
+    font-size: 14px;
+    font-weight: normal;
+}
+
+.uni-panel-icon {
+    margin-left: 15px;
+    color: #999999;
+    font-size: 14px;
+    font-weight: normal;
+    transform: rotate(0deg);
+    transition-duration: 0s;
+    transition-property: transform;
+}
+
+.uni-panel-icon-on {
+    transform: rotate(180deg);
+}
+
+.uni-navigate-item {
+    flex-direction: row;
+    align-items: center;
+    background-color: #FFFFFF;
+    border-top-style: solid;
+    border-top-color: #f0f0f0;
+    border-top-width: 1px;
+    padding: 12px;
+}
+
+.uni-navigate-item:active {
+    background-color: #f8f8f8;
+}
+
+.uni-navigate-text {
+    flex: 1;
+    color: #000000;
+    font-size: 14px;
+    font-weight: normal;
+}
+
+.uni-navigate-icon {
+    margin-left: 15px;
+    color: #999999;
+    font-size: 14px;
+    font-weight: normal;
+}

File diff suppressed because it is too large
+ 1448 - 0
src/common/uni.css


+ 73 - 0
src/common/util.js

@@ -0,0 +1,73 @@
+function formatTime(time) {
+	if (typeof time !== 'number' || time < 0) {
+		return time
+	}
+
+	var hour = parseInt(time / 3600)
+	time = time % 3600
+	var minute = parseInt(time / 60)
+	time = time % 60
+	var second = time
+
+	return ([hour, minute, second]).map(function (n) {
+		n = n.toString()
+		return n[1] ? n : '0' + n
+	}).join(':')
+}
+
+function formatLocation(longitude, latitude) {
+	if (typeof longitude === 'string' && typeof latitude === 'string') {
+		longitude = parseFloat(longitude)
+		latitude = parseFloat(latitude)
+	}
+
+	longitude = longitude.toFixed(2)
+	latitude = latitude.toFixed(2)
+
+	return {
+		longitude: longitude.toString().split('.'),
+		latitude: latitude.toString().split('.')
+	}
+}
+var dateUtils = {
+	UNITS: {
+		'年': 31557600000,
+		'月': 2629800000,
+		'天': 86400000,
+		'小时': 3600000,
+		'分钟': 60000,
+		'秒': 1000
+	},
+	humanize: function (milliseconds) {
+		var humanize = '';
+		for (var key in this.UNITS) {
+			if (milliseconds >= this.UNITS[key]) {
+				humanize = Math.floor(milliseconds / this.UNITS[key]) + key + '前';
+				break;
+			}
+		}
+		return humanize || '刚刚';
+	},
+	format: function (dateStr) {
+		var date = this.parse(dateStr)
+		var diff = Date.now() - date.getTime();
+		if (diff < this.UNITS['天']) {
+			return this.humanize(diff);
+		}
+		var _format = function (number) {
+			return (number < 10 ? ('0' + number) : number);
+		};
+		return date.getFullYear() + '/' + _format(date.getMonth() + 1) + '/' + _format(date.getDate()) + '-' +
+			_format(date.getHours()) + ':' + _format(date.getMinutes());
+	},
+	parse: function (str) { //将"yyyy-mm-dd HH:MM:ss"格式的字符串,转化为一个Date对象
+		var a = str.split(/[^0-9]/);
+		return new Date(a[0], a[1] - 1, a[2], a[3], a[4], a[5]);
+	}
+};
+
+module.exports = {
+	formatTime: formatTime,
+	formatLocation: formatLocation,
+	dateUtils: dateUtils
+}

+ 0 - 0
src/components/tki-qrcode/my-qrcode.vue


File diff suppressed because it is too large
+ 1201 - 0
src/components/tki-qrcode/qrcode.js


+ 212 - 0
src/components/tki-qrcode/tki-qrcode.vue

@@ -0,0 +1,212 @@
+<template xlang="wxml" minapp="mpvue">
+  <view class="tki-qrcode">
+    <!-- #ifndef MP-ALIPAY -->
+    <canvas class="tki-qrcode-canvas" :canvas-id="cid" :style="{width:cpSize+'px',height:cpSize+'px'}" />
+    <!-- #endif -->
+    <!-- #ifdef MP-ALIPAY -->
+    <canvas :id="cid" :width="cpSize" :height="cpSize" class="tki-qrcode-canvas" />
+    <!-- #endif -->
+    <image v-show="show" :src="result" :style="{width:cpSize+'px',height:cpSize+'px'}" />
+  </view>
+</template>
+
+<script>
+import QRCode from "./qrcode.js"
+let qrcode
+export default {
+  name: "tki-qrcode",
+  props: {
+    cid: {
+      type: String,
+      default: 'tki-qrcode-canvas'
+    },
+    size: {
+      type: Number,
+      default: 200
+    },
+    unit: {
+      type: String,
+      default: 'upx'
+    },
+    show: {
+      type: Boolean,
+      default: true
+    },
+    val: {
+      type: String,
+      default: ''
+    },
+    background: {
+      type: String,
+      default: '#ffffff'
+    },
+    foreground: {
+      type: String,
+      default: '#000000'
+    },
+    pdground: {
+      type: String,
+      default: '#000000'
+    },
+    icon: {
+      type: String,
+      default: ''
+    },
+    iconSize: {
+      type: Number,
+      default: 40
+    },
+    lv: {
+      type: Number,
+      default: 3
+    },
+    onval: {
+      type: Boolean,
+      default: false
+    },
+    loadMake: {
+      type: Boolean,
+      default: false
+    },
+    usingComponents: {
+      type: Boolean,
+      default: true
+    },
+    showLoading: {
+      type: Boolean,
+      default: true
+    },
+    loadingText: {
+      type: String,
+      default: '二维码生成中'
+    },
+  },
+  data() {
+    return {
+      result: '',
+    }
+  },
+  methods: {
+    _makeCode() {
+      let that = this
+      if (!this._empty(this.val)) {
+        qrcode = new QRCode({
+          context: that, // 上下文环境
+          canvasId:that.cid, // canvas-id
+          usingComponents: that.usingComponents, // 是否是自定义组件
+          showLoading: that.showLoading, // 是否显示loading
+          loadingText: that.loadingText, // loading文字
+          text: that.val, // 生成内容
+          size: that.cpSize, // 二维码大小
+          background: that.background, // 背景色
+          foreground: that.foreground, // 前景色
+          pdground: that.pdground, // 定位角点颜色
+          correctLevel: that.lv, // 容错级别
+          image: that.icon, // 二维码图标
+          imageSize: that.iconSize,// 二维码图标大小
+          cbResult: function (res) { // 生成二维码的回调
+            that._result(res)
+          },
+        });
+      } else {
+        uni.showToast({
+          title: '二维码内容不能为空',
+          icon: 'none',
+          duration: 2000
+        });
+      }
+    },
+    _clearCode() {
+      this._result('')
+      qrcode.clear()
+    },
+    _saveCode() {
+      let that = this;
+      if (this.result != "") {
+        uni.saveImageToPhotosAlbum({
+          filePath: that.result,
+          success: function () {
+            uni.showToast({
+              title: '二维码保存成功',
+              icon: 'success',
+              duration: 2000
+            });
+          }
+        });
+      }
+    },
+    _result(res) {
+      this.result = res;
+      this.$emit('result', res)
+    },
+    _empty(v) {
+      let tp = typeof v,
+          rt = false;
+      if (tp == "number" && String(v) == "") {
+        rt = true
+      } else if (tp == "undefined") {
+        rt = true
+      } else if (tp == "object") {
+        if (JSON.stringify(v) == "{}" || JSON.stringify(v) == "[]" || v == null) rt = true
+      } else if (tp == "string") {
+        if (v == "" || v == "undefined" || v == "null" || v == "{}" || v == "[]") rt = true
+      } else if (tp == "function") {
+        rt = false
+      }
+      return rt
+    }
+  },
+  watch: {
+    size: function (n, o) {
+      if (n != o && !this._empty(n)) {
+        this.cSize = n
+        if (!this._empty(this.val)) {
+          setTimeout(() => {
+            this._makeCode()
+          }, 100);
+        }
+      }
+    },
+    val: function (n, o) {
+      if (this.onval) {
+        if (n != o && !this._empty(n)) {
+          setTimeout(() => {
+            this._makeCode()
+          }, 0);
+        }
+      }
+    }
+  },
+  computed: {
+    cpSize() {
+      if(this.unit == "upx"){
+        return uni.upx2px(this.size)
+      }else{
+        return this.size
+      }
+    }
+  },
+  mounted: function () {
+    if (this.loadMake) {
+      if (!this._empty(this.val)) {
+        setTimeout(() => {
+          this._makeCode()
+        }, 0);
+      }
+    }
+  },
+}
+</script>
+<style>
+.tki-qrcode {
+  position: relative;
+  display: flex;
+  text-align: center;
+}
+.tki-qrcode-canvas {
+  position: fixed;
+  top: -99999upx;
+  left: -99999upx;
+  z-index: -99999;
+}
+</style>

+ 546 - 0
src/components/uni-calendar/calendar.js

@@ -0,0 +1,546 @@
+/**
+* @1900-2100区间内的公历、农历互转
+* @charset UTF-8
+* @github  https://github.com/jjonline/calendar.js
+* @Author  Jea杨(JJonline@JJonline.Cn)
+* @Time    2014-7-21
+* @Time    2016-8-13 Fixed 2033hex、Attribution Annals
+* @Time    2016-9-25 Fixed lunar LeapMonth Param Bug
+* @Time    2017-7-24 Fixed use getTerm Func Param Error.use solar year,NOT lunar year
+* @Version 1.0.3
+* @公历转农历:calendar.solar2lunar(1987,11,01); //[you can ignore params of prefix 0]
+* @农历转公历:calendar.lunar2solar(1987,09,10); //[you can ignore params of prefix 0]
+*/
+/* eslint-disable */
+var calendar = {
+
+  /**
+      * 农历1900-2100的润大小信息表
+      * @Array Of Property
+      * @return Hex
+      */
+  lunarInfo: [0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2, // 1900-1909
+    0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977, // 1910-1919
+    0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, // 1920-1929
+    0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950, // 1930-1939
+    0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, // 1940-1949
+    0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0, // 1950-1959
+    0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, // 1960-1969
+    0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0, 0x195a6, // 1970-1979
+    0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570, // 1980-1989
+    0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x05ac0, 0x0ab60, 0x096d5, 0x092e0, // 1990-1999
+    0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5, // 2000-2009
+    0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, // 2010-2019
+    0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530, // 2020-2029
+    0x05aa0, 0x076a3, 0x096d0, 0x04afb, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, // 2030-2039
+    0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0, // 2040-2049
+    /** Add By JJonline@JJonline.Cn**/
+    0x14b63, 0x09370, 0x049f8, 0x04970, 0x064b0, 0x168a6, 0x0ea50, 0x06b20, 0x1a6c4, 0x0aae0, // 2050-2059
+    0x0a2e0, 0x0d2e3, 0x0c960, 0x0d557, 0x0d4a0, 0x0da50, 0x05d55, 0x056a0, 0x0a6d0, 0x055d4, // 2060-2069
+    0x052d0, 0x0a9b8, 0x0a950, 0x0b4a0, 0x0b6a6, 0x0ad50, 0x055a0, 0x0aba4, 0x0a5b0, 0x052b0, // 2070-2079
+    0x0b273, 0x06930, 0x07337, 0x06aa0, 0x0ad50, 0x14b55, 0x04b60, 0x0a570, 0x054e4, 0x0d160, // 2080-2089
+    0x0e968, 0x0d520, 0x0daa0, 0x16aa6, 0x056d0, 0x04ae0, 0x0a9d4, 0x0a2d0, 0x0d150, 0x0f252, // 2090-2099
+    0x0d520], // 2100
+
+  /**
+      * 公历每个月份的天数普通表
+      * @Array Of Property
+      * @return Number
+      */
+  solarMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
+
+  /**
+      * 天干地支之天干速查表
+      * @Array Of Property trans["甲","乙","丙","丁","戊","己","庚","辛","壬","癸"]
+      * @return Cn string
+      */
+  Gan: ['\u7532', '\u4e59', '\u4e19', '\u4e01', '\u620a', '\u5df1', '\u5e9a', '\u8f9b', '\u58ec', '\u7678'],
+
+  /**
+      * 天干地支之地支速查表
+      * @Array Of Property
+      * @trans["子","丑","寅","卯","辰","巳","午","未","申","酉","戌","亥"]
+      * @return Cn string
+      */
+  Zhi: ['\u5b50', '\u4e11', '\u5bc5', '\u536f', '\u8fb0', '\u5df3', '\u5348', '\u672a', '\u7533', '\u9149', '\u620c', '\u4ea5'],
+
+  /**
+      * 天干地支之地支速查表<=>生肖
+      * @Array Of Property
+      * @trans["鼠","牛","虎","兔","龙","蛇","马","羊","猴","鸡","狗","猪"]
+      * @return Cn string
+      */
+  Animals: ['\u9f20', '\u725b', '\u864e', '\u5154', '\u9f99', '\u86c7', '\u9a6c', '\u7f8a', '\u7334', '\u9e21', '\u72d7', '\u732a'],
+
+  /**
+      * 24节气速查表
+      * @Array Of Property
+      * @trans["小寒","大寒","立春","雨水","惊蛰","春分","清明","谷雨","立夏","小满","芒种","夏至","小暑","大暑","立秋","处暑","白露","秋分","寒露","霜降","立冬","小雪","大雪","冬至"]
+      * @return Cn string
+      */
+  solarTerm: ['\u5c0f\u5bd2', '\u5927\u5bd2', '\u7acb\u6625', '\u96e8\u6c34', '\u60ca\u86f0', '\u6625\u5206', '\u6e05\u660e', '\u8c37\u96e8', '\u7acb\u590f', '\u5c0f\u6ee1', '\u8292\u79cd', '\u590f\u81f3', '\u5c0f\u6691', '\u5927\u6691', '\u7acb\u79cb', '\u5904\u6691', '\u767d\u9732', '\u79cb\u5206', '\u5bd2\u9732', '\u971c\u964d', '\u7acb\u51ac', '\u5c0f\u96ea', '\u5927\u96ea', '\u51ac\u81f3'],
+
+  /**
+      * 1900-2100各年的24节气日期速查表
+      * @Array Of Property
+      * @return 0x string For splice
+      */
+  sTermInfo: ['9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f',
+    '97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
+    '97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f',
+    'b027097bd097c36b0b6fc9274c91aa', '9778397bd19801ec9210c965cc920e', '97b6b97bd19801ec95f8c965cc920f',
+    '97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2', '9778397bd197c36c9210c9274c91aa',
+    '97b6b97bd19801ec95f8c965cc920e', '97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2',
+    '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec95f8c965cc920e', '97bcf97c3598082c95f8e1cfcc920f',
+    '97bd097bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e',
+    '97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f',
+    '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
+    '97bcf97c359801ec95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd097bd07f595b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9210c8dc2', '9778397bd19801ec9210c9274c920e', '97b6b97bd19801ec95f8c965cc920f',
+    '97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e',
+    '97b6b97bd19801ec95f8c965cc920f', '97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2',
+    '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bd07f1487f595b0b0bc920fb0722',
+    '7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
+    '97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f531b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
+    '97bcf7f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
+    '9778397bd097c36b0b6fc9210c91aa', '97b6b97bd197c36c9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e',
+    '97b6b7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2',
+    '9778397bd097c36b0b70c9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b7f0e47f531b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
+    '9778397bd097c36b0b6fc9210c91aa', '97b6b7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '977837f0e37f149b0723b0787b0721',
+    '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c35b0b6fc9210c8dc2',
+    '977837f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc9210c8dc2', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '977837f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
+    '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
+    '977837f0e37f14998082b0723b06bd', '7f07e7f0e37f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b0721',
+    '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f595b0b0bb0b6fb0722', '7f0e37f0e37f14898082b0723b02d5',
+    '7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f531b0b0bb0b6fb0722',
+    '7f0e37f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e37f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35',
+    '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
+    '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f149b0723b0787b0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0723b06bd',
+    '7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', '7f0e37f0e366aa89801eb072297c35',
+    '7ec967f0e37f14998082b0723b06bd', '7f07e7f0e37f14998083b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
+    '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14898082b0723b02d5', '7f07e7f0e37f14998082b0787b0721',
+    '7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66aa89801e9808297c35', '665f67f0e37f14898082b0723b02d5',
+    '7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66a449801e9808297c35',
+    '665f67f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e36665b66a449801e9808297c35', '665f67f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f531b0723b0b6fb0721', '7f0e26665b66a449801e9808297c35', '665f67f0e37f1489801eb072297c35',
+    '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722'],
+
+  /**
+      * 数字转中文速查表
+      * @Array Of Property
+      * @trans ['日','一','二','三','四','五','六','七','八','九','十']
+      * @return Cn string
+      */
+  nStr1: ['\u65e5', '\u4e00', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d', '\u5341'],
+
+  /**
+      * 日期转农历称呼速查表
+      * @Array Of Property
+      * @trans ['初','十','廿','卅']
+      * @return Cn string
+      */
+  nStr2: ['\u521d', '\u5341', '\u5eff', '\u5345'],
+
+  /**
+      * 月份转农历称呼速查表
+      * @Array Of Property
+      * @trans ['正','一','二','三','四','五','六','七','八','九','十','冬','腊']
+      * @return Cn string
+      */
+  nStr3: ['\u6b63', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d', '\u5341', '\u51ac', '\u814a'],
+
+  /**
+      * 返回农历y年一整年的总天数
+      * @param lunar Year
+      * @return Number
+      * @eg:var count = calendar.lYearDays(1987) ;//count=387
+      */
+  lYearDays: function (y) {
+    var i; var sum = 348
+    for (i = 0x8000; i > 0x8; i >>= 1) { sum += (this.lunarInfo[y - 1900] & i) ? 1 : 0 }
+    return (sum + this.leapDays(y))
+  },
+
+  /**
+      * 返回农历y年闰月是哪个月;若y年没有闰月 则返回0
+      * @param lunar Year
+      * @return Number (0-12)
+      * @eg:var leapMonth = calendar.leapMonth(1987) ;//leapMonth=6
+      */
+  leapMonth: function (y) { // 闰字编码 \u95f0
+    return (this.lunarInfo[y - 1900] & 0xf)
+  },
+
+  /**
+      * 返回农历y年闰月的天数 若该年没有闰月则返回0
+      * @param lunar Year
+      * @return Number (0、29、30)
+      * @eg:var leapMonthDay = calendar.leapDays(1987) ;//leapMonthDay=29
+      */
+  leapDays: function (y) {
+    if (this.leapMonth(y)) {
+      return ((this.lunarInfo[y - 1900] & 0x10000) ? 30 : 29)
+    }
+    return (0)
+  },
+
+  /**
+      * 返回农历y年m月(非闰月)的总天数,计算m为闰月时的天数请使用leapDays方法
+      * @param lunar Year
+      * @return Number (-1、29、30)
+      * @eg:var MonthDay = calendar.monthDays(1987,9) ;//MonthDay=29
+      */
+  monthDays: function (y, m) {
+    if (m > 12 || m < 1) { return -1 }// 月份参数从1至12,参数错误返回-1
+    return ((this.lunarInfo[y - 1900] & (0x10000 >> m)) ? 30 : 29)
+  },
+
+  /**
+      * 返回公历(!)y年m月的天数
+      * @param solar Year
+      * @return Number (-1、28、29、30、31)
+      * @eg:var solarMonthDay = calendar.leapDays(1987) ;//solarMonthDay=30
+      */
+  solarDays: function (y, m) {
+    if (m > 12 || m < 1) { return -1 } // 若参数错误 返回-1
+    var ms = m - 1
+    if (ms == 1) { // 2月份的闰平规律测算后确认返回28或29
+      return (((y % 4 == 0) && (y % 100 != 0) || (y % 400 == 0)) ? 29 : 28)
+    } else {
+      return (this.solarMonth[ms])
+    }
+  },
+
+  /**
+     * 农历年份转换为干支纪年
+     * @param  lYear 农历年的年份数
+     * @return Cn string
+     */
+  toGanZhiYear: function (lYear) {
+    var ganKey = (lYear - 3) % 10
+    var zhiKey = (lYear - 3) % 12
+    if (ganKey == 0) ganKey = 10// 如果余数为0则为最后一个天干
+    if (zhiKey == 0) zhiKey = 12// 如果余数为0则为最后一个地支
+    return this.Gan[ganKey - 1] + this.Zhi[zhiKey - 1]
+  },
+
+  /**
+     * 公历月、日判断所属星座
+     * @param  cMonth [description]
+     * @param  cDay [description]
+     * @return Cn string
+     */
+  toAstro: function (cMonth, cDay) {
+    var s = '\u9b54\u7faf\u6c34\u74f6\u53cc\u9c7c\u767d\u7f8a\u91d1\u725b\u53cc\u5b50\u5de8\u87f9\u72ee\u5b50\u5904\u5973\u5929\u79e4\u5929\u874e\u5c04\u624b\u9b54\u7faf'
+    var arr = [20, 19, 21, 21, 21, 22, 23, 23, 23, 23, 22, 22]
+    return s.substr(cMonth * 2 - (cDay < arr[cMonth - 1] ? 2 : 0), 2) + '\u5ea7'// 座
+  },
+
+  /**
+      * 传入offset偏移量返回干支
+      * @param offset 相对甲子的偏移量
+      * @return Cn string
+      */
+  toGanZhi: function (offset) {
+    return this.Gan[offset % 10] + this.Zhi[offset % 12]
+  },
+
+  /**
+      * 传入公历(!)y年获得该年第n个节气的公历日期
+      * @param y公历年(1900-2100);n二十四节气中的第几个节气(1~24);从n=1(小寒)算起
+      * @return day Number
+      * @eg:var _24 = calendar.getTerm(1987,3) ;//_24=4;意即1987年2月4日立春
+      */
+  getTerm: function (y, n) {
+    if (y < 1900 || y > 2100) { return -1 }
+    if (n < 1 || n > 24) { return -1 }
+    var _table = this.sTermInfo[y - 1900]
+    var _info = [
+      parseInt('0x' + _table.substr(0, 5)).toString(),
+      parseInt('0x' + _table.substr(5, 5)).toString(),
+      parseInt('0x' + _table.substr(10, 5)).toString(),
+      parseInt('0x' + _table.substr(15, 5)).toString(),
+      parseInt('0x' + _table.substr(20, 5)).toString(),
+      parseInt('0x' + _table.substr(25, 5)).toString()
+    ]
+    var _calday = [
+      _info[0].substr(0, 1),
+      _info[0].substr(1, 2),
+      _info[0].substr(3, 1),
+      _info[0].substr(4, 2),
+
+      _info[1].substr(0, 1),
+      _info[1].substr(1, 2),
+      _info[1].substr(3, 1),
+      _info[1].substr(4, 2),
+
+      _info[2].substr(0, 1),
+      _info[2].substr(1, 2),
+      _info[2].substr(3, 1),
+      _info[2].substr(4, 2),
+
+      _info[3].substr(0, 1),
+      _info[3].substr(1, 2),
+      _info[3].substr(3, 1),
+      _info[3].substr(4, 2),
+
+      _info[4].substr(0, 1),
+      _info[4].substr(1, 2),
+      _info[4].substr(3, 1),
+      _info[4].substr(4, 2),
+
+      _info[5].substr(0, 1),
+      _info[5].substr(1, 2),
+      _info[5].substr(3, 1),
+      _info[5].substr(4, 2)
+    ]
+    return parseInt(_calday[n - 1])
+  },
+
+  /**
+      * 传入农历数字月份返回汉语通俗表示法
+      * @param lunar month
+      * @return Cn string
+      * @eg:var cnMonth = calendar.toChinaMonth(12) ;//cnMonth='腊月'
+      */
+  toChinaMonth: function (m) { // 月 => \u6708
+    if (m > 12 || m < 1) { return -1 } // 若参数错误 返回-1
+    var s = this.nStr3[m - 1]
+    s += '\u6708'// 加上月字
+    return s
+  },
+
+  /**
+      * 传入农历日期数字返回汉字表示法
+      * @param lunar day
+      * @return Cn string
+      * @eg:var cnDay = calendar.toChinaDay(21) ;//cnMonth='廿一'
+      */
+  toChinaDay: function (d) { // 日 => \u65e5
+    var s
+    switch (d) {
+      case 10:
+        s = '\u521d\u5341'; break
+      case 20:
+        s = '\u4e8c\u5341'; break
+        break
+      case 30:
+        s = '\u4e09\u5341'; break
+        break
+      default :
+        s = this.nStr2[Math.floor(d / 10)]
+        s += this.nStr1[d % 10]
+    }
+    return (s)
+  },
+
+  /**
+      * 年份转生肖[!仅能大致转换] => 精确划分生肖分界线是“立春”
+      * @param y year
+      * @return Cn string
+      * @eg:var animal = calendar.getAnimal(1987) ;//animal='兔'
+      */
+  getAnimal: function (y) {
+    return this.Animals[(y - 4) % 12]
+  },
+
+  /**
+      * 传入阳历年月日获得详细的公历、农历object信息 <=>JSON
+      * @param y  solar year
+      * @param m  solar month
+      * @param d  solar day
+      * @return JSON object
+      * @eg:console.log(calendar.solar2lunar(1987,11,01));
+      */
+  solar2lunar: function (y, m, d) { // 参数区间1900.1.31~2100.12.31
+    // 年份限定、上限
+    if (y < 1900 || y > 2100) {
+      return -1// undefined转换为数字变为NaN
+    }
+    // 公历传参最下限
+    if (y == 1900 && m == 1 && d < 31) {
+      return -1
+    }
+    // 未传参  获得当天
+    if (!y) {
+      var objDate = new Date()
+    } else {
+      var objDate = new Date(y, parseInt(m) - 1, d)
+    }
+    var i; var leap = 0; var temp = 0
+    // 修正ymd参数
+    var y = objDate.getFullYear()
+    var m = objDate.getMonth() + 1
+    var d = objDate.getDate()
+    var offset = (Date.UTC(objDate.getFullYear(), objDate.getMonth(), objDate.getDate()) - Date.UTC(1900, 0, 31)) / 86400000
+    for (i = 1900; i < 2101 && offset > 0; i++) {
+      temp = this.lYearDays(i)
+      offset -= temp
+    }
+    if (offset < 0) {
+      offset += temp; i--
+    }
+
+    // 是否今天
+    var isTodayObj = new Date()
+    var isToday = false
+    if (isTodayObj.getFullYear() == y && isTodayObj.getMonth() + 1 == m && isTodayObj.getDate() == d) {
+      isToday = true
+    }
+    // 星期几
+    var nWeek = objDate.getDay()
+    var cWeek = this.nStr1[nWeek]
+    // 数字表示周几顺应天朝周一开始的惯例
+    if (nWeek == 0) {
+      nWeek = 7
+    }
+    // 农历年
+    var year = i
+    var leap = this.leapMonth(i) // 闰哪个月
+    var isLeap = false
+
+    // 效验闰月
+    for (i = 1; i < 13 && offset > 0; i++) {
+      // 闰月
+      if (leap > 0 && i == (leap + 1) && isLeap == false) {
+        --i
+        isLeap = true; temp = this.leapDays(year) // 计算农历闰月天数
+      } else {
+        temp = this.monthDays(year, i)// 计算农历普通月天数
+      }
+      // 解除闰月
+      if (isLeap == true && i == (leap + 1)) { isLeap = false }
+      offset -= temp
+    }
+    // 闰月导致数组下标重叠取反
+    if (offset == 0 && leap > 0 && i == leap + 1) {
+      if (isLeap) {
+        isLeap = false
+      } else {
+        isLeap = true; --i
+      }
+    }
+    if (offset < 0) {
+      offset += temp; --i
+    }
+    // 农历月
+    var month = i
+    // 农历日
+    var day = offset + 1
+    // 天干地支处理
+    var sm = m - 1
+    var gzY = this.toGanZhiYear(year)
+
+    // 当月的两个节气
+    // bugfix-2017-7-24 11:03:38 use lunar Year Param `y` Not `year`
+    var firstNode = this.getTerm(y, (m * 2 - 1))// 返回当月「节」为几日开始
+    var secondNode = this.getTerm(y, (m * 2))// 返回当月「节」为几日开始
+
+    // 依据12节气修正干支月
+    var gzM = this.toGanZhi((y - 1900) * 12 + m + 11)
+    if (d >= firstNode) {
+      gzM = this.toGanZhi((y - 1900) * 12 + m + 12)
+    }
+
+    // 传入的日期的节气与否
+    var isTerm = false
+    var Term = null
+    if (firstNode == d) {
+      isTerm = true
+      Term = this.solarTerm[m * 2 - 2]
+    }
+    if (secondNode == d) {
+      isTerm = true
+      Term = this.solarTerm[m * 2 - 1]
+    }
+    // 日柱 当月一日与 1900/1/1 相差天数
+    var dayCyclical = Date.UTC(y, sm, 1, 0, 0, 0, 0) / 86400000 + 25567 + 10
+    var gzD = this.toGanZhi(dayCyclical + d - 1)
+    // 该日期所属的星座
+    var astro = this.toAstro(m, d)
+
+    return { 'lYear': year, 'lMonth': month, 'lDay': day, 'Animal': this.getAnimal(year), 'IMonthCn': (isLeap ? '\u95f0' : '') + this.toChinaMonth(month), 'IDayCn': this.toChinaDay(day), 'cYear': y, 'cMonth': m, 'cDay': d, 'gzYear': gzY, 'gzMonth': gzM, 'gzDay': gzD, 'isToday': isToday, 'isLeap': isLeap, 'nWeek': nWeek, 'ncWeek': '\u661f\u671f' + cWeek, 'isTerm': isTerm, 'Term': Term, 'astro': astro }
+  },
+
+  /**
+      * 传入农历年月日以及传入的月份是否闰月获得详细的公历、农历object信息 <=>JSON
+      * @param y  lunar year
+      * @param m  lunar month
+      * @param d  lunar day
+      * @param isLeapMonth  lunar month is leap or not.[如果是农历闰月第四个参数赋值true即可]
+      * @return JSON object
+      * @eg:console.log(calendar.lunar2solar(1987,9,10));
+      */
+  lunar2solar: function (y, m, d, isLeapMonth) { // 参数区间1900.1.31~2100.12.1
+    var isLeapMonth = !!isLeapMonth
+    var leapOffset = 0
+    var leapMonth = this.leapMonth(y)
+    var leapDay = this.leapDays(y)
+    if (isLeapMonth && (leapMonth != m)) { return -1 }// 传参要求计算该闰月公历 但该年得出的闰月与传参的月份并不同
+    if (y == 2100 && m == 12 && d > 1 || y == 1900 && m == 1 && d < 31) { return -1 }// 超出了最大极限值
+    var day = this.monthDays(y, m)
+    var _day = day
+    // bugFix 2016-9-25
+    // if month is leap, _day use leapDays method
+    if (isLeapMonth) {
+      _day = this.leapDays(y, m)
+    }
+    if (y < 1900 || y > 2100 || d > _day) { return -1 }// 参数合法性效验
+
+    // 计算农历的时间差
+    var offset = 0
+    for (var i = 1900; i < y; i++) {
+      offset += this.lYearDays(i)
+    }
+    var leap = 0; var isAdd = false
+    for (var i = 1; i < m; i++) {
+      leap = this.leapMonth(y)
+      if (!isAdd) { // 处理闰月
+        if (leap <= i && leap > 0) {
+          offset += this.leapDays(y); isAdd = true
+        }
+      }
+      offset += this.monthDays(y, i)
+    }
+    // 转换闰月农历 需补充该年闰月的前一个月的时差
+    if (isLeapMonth) { offset += day }
+    // 1900年农历正月一日的公历时间为1900年1月30日0时0分0秒(该时间也是本农历的最开始起始点)
+    var stmap = Date.UTC(1900, 1, 30, 0, 0, 0)
+    var calObj = new Date((offset + d - 31) * 86400000 + stmap)
+    var cY = calObj.getUTCFullYear()
+    var cM = calObj.getUTCMonth() + 1
+    var cD = calObj.getUTCDate()
+
+    return this.solar2lunar(cY, cM, cD)
+  }
+}
+
+export default calendar

+ 152 - 0
src/components/uni-calendar/uni-calendar-item.vue

@@ -0,0 +1,152 @@
+<template>
+	<view class="uni-calendar-item__weeks-box" :class="{
+		'uni-calendar-item--disable':weeks.disable,
+		'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
+		'uni-calendar-item--checked':(calendar.fullDate === weeks.fullDate && !weeks.isDay) ,
+		'uni-calendar-item--multiple': weeks.multiple
+		}"
+	 @click="choiceDate(weeks)">
+		<view class="uni-calendar-item__weeks-box-item">
+			<text v-if="selected&&weeks.extraInfo" class="uni-calendar-item__weeks-box-circle"></text>
+			<text class="uni-calendar-item__weeks-box-text" :class="{
+				'uni-calendar-item--isDay-text': weeks.isDay,
+				'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
+				'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay,
+				'uni-calendar-item--multiple': weeks.multiple,
+				'uni-calendar-item--disable':weeks.disable,
+				}">{{weeks.date}}</text>
+			<text v-if="!lunar&&!weeks.extraInfo && weeks.isDay" class="uni-calendar-item__weeks-lunar-text" :class="{
+				'uni-calendar-item--isDay-text':weeks.isDay,
+				'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
+				'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay,
+				'uni-calendar-item--multiple': weeks.multiple,
+				}">今天</text>
+			<text v-if="lunar&&!weeks.extraInfo" class="uni-calendar-item__weeks-lunar-text" :class="{
+				'uni-calendar-item--isDay-text':weeks.isDay,
+				'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
+				'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay,
+				'uni-calendar-item--multiple': weeks.multiple,
+				'uni-calendar-item--disable':weeks.disable,
+				}">{{weeks.isDay?'今天': (weeks.lunar.IDayCn === '初一'?weeks.lunar.IMonthCn:weeks.lunar.IDayCn)}}</text>
+			<text v-if="weeks.extraInfo&&weeks.extraInfo.info" class="uni-calendar-item__weeks-lunar-text" :class="{
+				'uni-calendar-item--extra':weeks.extraInfo.info,
+				'uni-calendar-item--isDay-text':weeks.isDay,
+				'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
+				'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay,
+				'uni-calendar-item--multiple': weeks.multiple,
+				'uni-calendar-item--disable':weeks.disable,
+				}">{{weeks.extraInfo.info}}</text>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		props: {
+			weeks: {
+				type: Object,
+				default () {
+					return {}
+				}
+			},
+			calendar: {
+				type: Object,
+				default: () => {
+					return {}
+				}
+			},
+			selected: {
+				type: Array,
+				default: () => {
+					return []
+				}
+			},
+			lunar: {
+				type: Boolean,
+				default: false
+			}
+		},
+		methods: {
+			choiceDate(weeks) {
+				this.$emit('change', weeks)
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.uni-calendar-item__weeks-box {
+		flex: 1;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: column;
+		justify-content: center;
+		align-items: center;
+	}
+
+	.uni-calendar-item__weeks-box-text {
+		font-size: $uni-font-size-base;
+		color: $uni-text-color;
+	}
+
+	.uni-calendar-item__weeks-lunar-text {
+		font-size: $uni-font-size-sm;
+		color: $uni-text-color;
+	}
+
+	.uni-calendar-item__weeks-box-item {
+		position: relative;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: column;
+		justify-content: center;
+		align-items: center;
+		width: 100rpx;
+		height: 100rpx;
+	}
+
+	.uni-calendar-item__weeks-box-circle {
+		position: absolute;
+		top: 5px;
+		right: 5px;
+		width: 8px;
+		height: 8px;
+		border-radius: 8px;
+		background-color: $uni-color-error;
+
+	}
+
+	.uni-calendar-item--disable {
+		background-color: rgba(249, 249, 249, $uni-opacity-disabled);
+		color: $uni-text-color-disable;
+	}
+
+	.uni-calendar-item--isDay-text {
+		color: $uni-color-primary;
+	}
+
+	.uni-calendar-item--isDay {
+		background-color: $uni-color-primary;
+		opacity: 0.8;
+		color: #fff;
+	}
+
+	.uni-calendar-item--extra {
+		color: $uni-color-error;
+		opacity: 0.8;
+	}
+
+	.uni-calendar-item--checked {
+		background-color: $uni-color-primary;
+		color: #fff;
+		opacity: 0.8;
+	}
+
+	.uni-calendar-item--multiple {
+		background-color: $uni-color-primary;
+		color: #fff;
+		opacity: 0.8;
+	}
+</style>

+ 434 - 0
src/components/uni-calendar/uni-calendar.vue

@@ -0,0 +1,434 @@
+<template>
+	<view class="uni-calendar" @touchmove.stop.prevent="clean">
+		<view v-if="!insert&&show" class="uni-calendar__mask" :class="{'uni-calendar--mask-show':aniMaskShow}" @click="clean"></view>
+		<view v-if="insert || show" class="uni-calendar__content" :class="{'uni-calendar--fixed':!insert,'uni-calendar--ani-show':aniMaskShow}">
+			<view v-if="!insert" class="uni-calendar__header uni-calendar--fixed-top">
+				<view class="uni-calendar__header-btn-box" @click="close">
+					<text class="uni-calendar__header-text uni-calendar--fixed-width">取消</text>
+				</view>
+				<view class="uni-calendar__header-btn-box" @click="confirm">
+					<text class="uni-calendar__header-text uni-calendar--fixed-width">确定</text>
+				</view>
+			</view>
+			<view class="uni-calendar__header">
+				<view class="uni-calendar__header-btn-box" @click="pre">
+					<view class="uni-calendar__header-btn uni-calendar--left"></view>
+				</view>
+				<text class="uni-calendar__header-text">{{ (nowDate.year||'') +'年'+( nowDate.month||'') +'月'}}</text>
+				<view class="uni-calendar__header-btn-box" @click="next">
+					<view class="uni-calendar__header-btn uni-calendar--right"></view>
+				</view>
+				<text class="uni-calendar__backtoday" @click="backtoday">回到今天</text>
+			</view>
+			<view class="uni-calendar__box">
+				<view v-if="showMonth" class="uni-calendar__box-bg">
+					<text class="uni-calendar__box-bg-text">{{nowDate.month}}</text>
+				</view>
+				<view class="uni-calendar__weeks">
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">日</text>
+					</view>
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">一</text>
+					</view>
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">二</text>
+					</view>
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">三</text>
+					</view>
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">四</text>
+					</view>
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">五</text>
+					</view>
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">六</text>
+					</view>
+				</view>
+				<view class="uni-calendar__weeks" v-for="(item,weekIndex) in weeks" :key="weekIndex">
+					<view class="uni-calendar__weeks-item" v-for="(weeks,weeksIndex) in item" :key="weeksIndex">
+						<uni-calendar-item :weeks="weeks" :calendar="calendar" :selected="selected" :lunar="lunar" @change="choiceDate"></uni-calendar-item>
+					</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import Calendar from './util.js';
+	import uniCalendarItem from './uni-calendar-item.vue'
+	export default {
+		components: {
+			uniCalendarItem
+		},
+		props: {
+			/**
+			 * 当前日期
+			 */
+			date: {
+				type: String,
+				default: ''
+			},
+			/**
+			 * 打点日期
+			 */
+			selected: {
+				type: Array,
+				default () {
+					return []
+				}
+			},
+			/**
+			 * 是否开启阴历日期
+			 */
+			lunar: {
+				type: Boolean,
+				default: false
+			},
+			/**
+			 * 开始时间
+			 */
+			startDate: {
+				type: String,
+				default: ''
+			},
+			/**
+			 * 结束时间
+			 */
+			endDate: {
+				type: String,
+				default: ''
+			},
+			/**
+			 * 范围
+			 */
+			range: {
+				type: Boolean,
+				default: false
+			},
+			/**
+			 * 插入
+			 */
+			insert: {
+				type: Boolean,
+				default: true
+			},
+			/**
+			 * 是否显示月份背景
+			 */
+			showMonth: {
+				type: Boolean,
+				default: true
+			}
+		},
+		data() {
+			return {
+				show: false,
+				weeks: [],
+				calendar: {},
+				nowDate: '',
+				aniMaskShow: false
+			}
+		},
+		watch: {
+			selected(newVal) {
+				this.cale.setSelectInfo(this.nowDate.fullDate, newVal)
+				this.weeks = this.cale.weeks
+			}
+		},
+		created() {
+			// 获取日历方法实例
+			this.cale = new Calendar({
+				date: this.date,
+				selected: this.selected,
+				startDate: this.startDate,
+				endDate: this.endDate,
+				range: this.range,
+			})
+			this.init(this.cale.date.fullDate)
+		},
+		methods: {
+			// 取消穿透
+			clean() {},
+			init(date) {
+				this.weeks = this.cale.weeks
+				this.nowDate = this.calendar = this.cale.getInfo(date)
+			},
+			open() {
+				this.show = true
+				this.$nextTick(() => {
+					setTimeout(()=>{
+						this.aniMaskShow = true
+					},50)
+				})
+			},
+			close() {
+				this.aniMaskShow = false
+				this.$nextTick(() => {
+					setTimeout(() => {
+						this.show = false
+					}, 300)
+				})
+			},
+			confirm() {
+				this.setEmit('confirm')
+				this.close()
+			},
+			change() {
+				if (!this.insert) return
+				this.setEmit('change')
+			},
+			monthSwitch() {
+				let {
+					year,
+					month
+				} = this.nowDate
+				this.$emit('monthSwitch', {
+					year,
+					month: Number(month)
+				})
+			},
+			setEmit(name) {
+				let {
+					year,
+					month,
+					date,
+					fullDate,
+					lunar,
+					extraInfo
+				} = this.calendar
+				this.$emit(name, {
+					range: this.cale.multipleStatus,
+					year,
+					month,
+					date,
+					fulldate: fullDate,
+					lunar,
+					extraInfo: extraInfo || {}
+				})
+			},
+			choiceDate(weeks) {
+				if (weeks.disable) return
+				this.calendar = weeks
+				// 设置多选
+				this.cale.setMultiple(this.calendar.fullDate)
+				this.weeks = this.cale.weeks
+				this.change()
+			},
+			backtoday() {
+				this.cale.setDate(this.date)
+				this.weeks = this.cale.weeks
+				this.nowDate = this.calendar = this.cale.getInfo(this.date)
+				this.change()
+			},
+			pre() {
+				const preDate = this.cale.getDate(this.nowDate.fullDate, -1, 'month').fullDate
+				this.setDate(preDate)
+				this.monthSwitch()
+
+			},
+			next() {
+				const nextDate = this.cale.getDate(this.nowDate.fullDate, +1, 'month').fullDate
+				this.setDate(nextDate)
+				this.monthSwitch()
+			},
+			setDate(date) {
+				this.cale.setDate(date)
+				this.weeks = this.cale.weeks
+				this.nowDate = this.cale.getInfo(date)
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.uni-calendar {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: column;
+	}
+
+	.uni-calendar__mask {
+		position: fixed;
+		bottom: 0;
+		top: 0;
+		left: 0;
+		right: 0;
+		background-color: $uni-bg-color-mask;
+		transition-property: opacity;
+		transition-duration: 0.3s;
+		opacity: 0;
+		/* #ifndef APP-NVUE */
+		z-index: 99;
+		/* #endif */
+	}
+
+	.uni-calendar--mask-show {
+		opacity: 1
+	}
+
+	.uni-calendar--fixed {
+		position: fixed;
+		bottom: 0;
+		left: 0;
+		right: 0;
+		transition-property: transform;
+		transition-duration: 0.3s;
+		transform: translateY(460px);
+		/* #ifndef APP-NVUE */
+		z-index: 99;
+		/* #endif */
+	}
+
+	.uni-calendar--ani-show {
+		transform: translateY(0);
+	}
+
+	.uni-calendar__content {
+		background-color: #fff;
+	}
+
+	.uni-calendar__header {
+		position: relative;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		justify-content: center;
+		align-items: center;
+		height: 50px;
+		border-bottom-color: $uni-border-color;
+		border-bottom-style: solid;
+		border-bottom-width: 1px;
+	}
+
+	.uni-calendar--fixed-top {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		justify-content: space-between;
+		border-top-color: $uni-border-color;
+		border-top-style: solid;
+		border-top-width: 1px;
+	}
+
+	.uni-calendar--fixed-width {
+		width: 50px;
+		// padding: 0 15px;
+	}
+
+	.uni-calendar__backtoday {
+		position: absolute;
+		right: 0;
+		top: 25rpx;
+		padding: 0 5px;
+		padding-left: 10px;
+		height: 25px;
+		line-height: 25px;
+		font-size: 12px;
+		border-top-left-radius: 25px;
+		border-bottom-left-radius: 25px;
+		color: $uni-text-color;
+		background-color: $uni-bg-color-hover;
+	}
+
+	.uni-calendar__header-text {
+		text-align: center;
+		width: 100px;
+		font-size: $uni-font-size-base;
+		color: $uni-text-color;
+	}
+
+	.uni-calendar__header-btn-box {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		align-items: center;
+		justify-content: center;
+		width: 50px;
+		height: 50px;
+	}
+
+	.uni-calendar__header-btn {
+		width: 10px;
+		height: 10px;
+		border-left-color: $uni-text-color-placeholder;
+		border-left-style: solid;
+		border-left-width: 2px;
+		border-top-color: $uni-color-subtitle;
+		border-top-style: solid;
+		border-top-width: 2px;
+	}
+
+	.uni-calendar--left {
+		transform: rotate(-45deg);
+	}
+
+	.uni-calendar--right {
+		transform: rotate(135deg);
+	}
+
+
+	.uni-calendar__weeks {
+		position: relative;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+	}
+
+	.uni-calendar__weeks-item {
+		flex: 1;
+	}
+
+	.uni-calendar__weeks-day {
+		flex: 1;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: column;
+		justify-content: center;
+		align-items: center;
+		height: 45px;
+		border-bottom-color: #F5F5F5;
+		border-bottom-style: solid;
+		border-bottom-width: 1px;
+	}
+	.uni-calendar__weeks-day-text {
+		font-size: 14px;
+	}
+
+	.uni-calendar__box {
+		position: relative;
+	}
+
+	.uni-calendar__box-bg {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		justify-content: center;
+		align-items: center;
+		position: absolute;
+		top: 0;
+		left: 0;
+		right: 0;
+		bottom: 0;
+	}
+
+	.uni-calendar__box-bg-text {
+		font-size: 200px;
+		font-weight: bold;
+		color: $uni-text-color-grey;
+		opacity: 0.1;
+		text-align: center;
+		/* #ifndef APP-NVUE */
+		line-height: 1;
+		/* #endif */
+	}
+</style>

+ 327 - 0
src/components/uni-calendar/util.js

@@ -0,0 +1,327 @@
+import CALENDAR from './calendar.js'
+
+class Calendar {
+	constructor({
+		date,
+		selected,
+		startDate,
+		endDate,
+		range
+	} = {}) {
+		// 当前日期
+		this.date = this.getDate(date) // 当前初入日期
+		// 打点信息
+		this.selected = selected || [];
+		// 范围开始
+		this.startDate = startDate
+		// 范围结束
+		this.endDate = endDate
+		this.range = range
+		// 多选状态
+		this.multipleStatus = {
+			before: '',
+			after: '',
+			data: []
+		}
+		// 每周日期
+		this.weeks = {}
+
+		this._getWeek(this.date.fullDate)
+	}
+
+	/**
+	 * 获取任意时间
+	 */
+	getDate(date, AddDayCount = 0, str = 'day') {
+		if (!date) {
+			date = new Date()
+		}
+		if (typeof date !== 'object') {
+			date = date.replace(/-/g, '/')
+		}
+		const dd = new Date(date)
+		switch (str) {
+			case 'day':
+				dd.setDate(dd.getDate() + AddDayCount) // 获取AddDayCount天后的日期
+				break
+			case 'month':
+				if (dd.getDate() === 31) {
+					dd.setDate(dd.getDate() + AddDayCount)
+				} else {
+					dd.setMonth(dd.getMonth() + AddDayCount) // 获取AddDayCount天后的日期
+				}
+				break
+			case 'year':
+				dd.setFullYear(dd.getFullYear() + AddDayCount) // 获取AddDayCount天后的日期
+				break
+		}
+		const y = dd.getFullYear()
+		const m = dd.getMonth() + 1 < 10 ? '0' + (dd.getMonth() + 1) : dd.getMonth() + 1 // 获取当前月份的日期,不足10补0
+		const d = dd.getDate() < 10 ? '0' + dd.getDate() : dd.getDate() // 获取当前几号,不足10补0
+		return {
+			fullDate: y + '-' + m + '-' + d,
+			year: y,
+			month: m,
+			date: d,
+			day: dd.getDay()
+		}
+	}
+
+
+	/**
+	 * 获取上月剩余天数
+	 */
+	_getLastMonthDays(firstDay, full) {
+		let dateArr = []
+		for (let i = firstDay; i > 0; i--) {
+			const beforeDate = new Date(full.year, full.month - 1, -i + 1).getDate()
+			dateArr.push({
+				date: beforeDate,
+				month: full.month - 1,
+				lunar: this.getlunar(full.year, full.month - 1, beforeDate),
+				disable: true
+			})
+		}
+		return dateArr
+	}
+	/**
+	 * 获取本月天数
+	 */
+	_currentMonthDys(dateData, full) {
+		let dateArr = []
+		let fullDate = this.date.fullDate
+		for (let i = 1; i <= dateData; i++) {
+			let isinfo = false
+			let nowDate = full.year + '-' + (full.month < 10 ?
+				full.month : full.month) + '-' + (i < 10 ?
+				'0' + i : i)
+			// 是否今天
+			let isDay = fullDate === nowDate
+			// 获取打点信息
+			let info = this.selected && this.selected.find((item) => {
+				if (this.dateEqual(nowDate, item.date)) {
+					return item
+				}
+			})
+
+			// 日期禁用
+			let disableBefore = true
+			let disableAfter = true
+			if (this.startDate) {
+				let dateCompBefore = this.dateCompare(this.startDate, fullDate)
+				disableBefore = this.dateCompare(dateCompBefore ? this.startDate : fullDate, nowDate)
+			}
+
+			if (this.endDate) {
+				let dateCompAfter = this.dateCompare(fullDate, this.endDate)
+				disableAfter = this.dateCompare(nowDate, dateCompAfter ? this.endDate : fullDate)
+			}
+
+			let multiples = this.multipleStatus.data
+			let checked = false
+			let multiplesStatus = -1
+			if (this.range) {
+				if (multiples) {
+					multiplesStatus = multiples.findIndex((item) => {
+						return this.dateEqual(item, nowDate)
+					})
+				}
+				if (multiplesStatus !== -1) {
+					checked = true
+				}
+			}
+
+			let data = {
+				fullDate: nowDate,
+				year: full.year,
+				date: i,
+				multiple: this.range ? checked : false,
+				month: full.month,
+				lunar: this.getlunar(full.year, full.month, i),
+				disable: !disableBefore || !disableAfter,
+				isDay
+			}
+			if (info) {
+				data.extraInfo = info
+			}
+
+			dateArr.push(data)
+		}
+		return dateArr
+	}
+	/**
+	 * 获取下月天数
+	 */
+	_getNextMonthDays(surplus, full) {
+		let dateArr = []
+		for (let i = 1; i < surplus + 1; i++) {
+			dateArr.push({
+				date: i,
+				month: Number(full.month) + 1,
+				lunar: this.getlunar(full.year, Number(full.month) + 1, i),
+				disable: true
+			})
+		}
+		return dateArr
+	}
+	/**
+	 * 设置日期
+	 * @param {Object} date
+	 */
+	setDate(date) {
+		this._getWeek(date)
+	}
+	/**
+	 * 获取当前日期详情
+	 * @param {Object} date
+	 */
+	getInfo(date) {
+		if (!date) {
+			date = new Date()
+		}
+		const dateInfo = this.canlender.find(item => item.fullDate === this.getDate(date).fullDate)
+		return dateInfo
+	}
+
+	/**
+	 * 比较时间大小
+	 */
+	dateCompare(startDate, endDate) {
+		// 计算截止时间
+		startDate = new Date(startDate.replace('-', '/').replace('-', '/'))
+		// 计算详细项的截止时间
+		endDate = new Date(endDate.replace('-', '/').replace('-', '/'))
+		if (startDate <= endDate) {
+			return true
+		} else {
+			return false
+		}
+	}
+
+	/**
+	 * 比较时间是否相等
+	 */
+	dateEqual(before, after) {
+		// 计算截止时间
+		before = new Date(before.replace('-', '/').replace('-', '/'))
+		// 计算详细项的截止时间
+		after = new Date(after.replace('-', '/').replace('-', '/'))
+		if (before.getTime() - after.getTime() === 0) {
+			return true
+		} else {
+			return false
+		}
+	}
+
+
+	/**
+	 * 获取日期范围内所有日期
+	 * @param {Object} begin
+	 * @param {Object} end
+	 */
+	geDateAll(begin, end) {
+		var arr = []
+		var ab = begin.split('-')
+		var ae = end.split('-')
+		var db = new Date()
+		db.setFullYear(ab[0], ab[1] - 1, ab[2])
+		var de = new Date()
+		de.setFullYear(ae[0], ae[1] - 1, ae[2])
+		var unixDb = db.getTime() - 24 * 60 * 60 * 1000
+		var unixDe = de.getTime() - 24 * 60 * 60 * 1000
+		for (var k = unixDb; k <= unixDe;) {
+			k = k + 24 * 60 * 60 * 1000
+			arr.push(this.getDate(new Date(parseInt(k))).fullDate)
+		}
+		return arr
+	}
+	/**
+	 * 计算阴历日期显示
+	 */
+	getlunar(year, month, date) {
+		return CALENDAR.solar2lunar(year, month, date)
+	}
+	/**
+	 * 设置打点
+	 */
+	setSelectInfo(data, value) {
+		this.selected = value
+		this._getWeek(data)
+	}
+
+	/**
+	 *  获取多选状态
+	 */
+	setMultiple(fullDate) {
+		let {
+			before,
+			after
+		} = this.multipleStatus
+		if (!this.range) return
+		if (before && after) {
+			this.multipleStatus.before = ''
+			this.multipleStatus.after = ''
+			this.multipleStatus.data = []
+			this._getWeek(fullDate)
+		} else {
+			if (!before) {
+				this.multipleStatus.before = fullDate
+			} else {
+				this.multipleStatus.after = fullDate
+				if (this.dateCompare(this.multipleStatus.before, this.multipleStatus.after)) {
+					this.multipleStatus.data = this.geDateAll(this.multipleStatus.before, this.multipleStatus.after);
+				} else {
+					this.multipleStatus.data = this.geDateAll(this.multipleStatus.after, this.multipleStatus.before);
+				}
+				this._getWeek(fullDate)
+			}
+		}
+	}
+
+	/**
+	 * 获取每周数据
+	 * @param {Object} dateData
+	 */
+	_getWeek(dateData) {
+		const {
+			fullDate,
+			year,
+			month,
+			date,
+			day
+		} = this.getDate(dateData)
+		let firstDay = new Date(year, month - 1, 1).getDay()
+		let currentDay = new Date(year, month, 0).getDate()
+		let dates = {
+			lastMonthDays: this._getLastMonthDays(firstDay, this.getDate(dateData)), // 上个月末尾几天
+			currentMonthDys: this._currentMonthDys(currentDay, this.getDate(dateData)), // 本月天数
+			nextMonthDays: [], // 下个月开始几天
+			weeks: []
+		}
+		let canlender = []
+		const surplus = 42 - (dates.lastMonthDays.length + dates.currentMonthDys.length)
+		dates.nextMonthDays = this._getNextMonthDays(surplus, this.getDate(dateData))
+		canlender = canlender.concat(dates.lastMonthDays, dates.currentMonthDys, dates.nextMonthDays)
+		let weeks = {}
+		// 拼接数组  上个月开始几天 + 本月天数+ 下个月开始几天
+		for (let i = 0; i < canlender.length; i++) {
+			if (i % 7 === 0) {
+				weeks[parseInt(i / 7)] = new Array(7)
+			}
+			weeks[parseInt(i / 7)][i % 7] = canlender[i]
+		}
+		this.canlender = canlender
+		this.weeks = weeks
+	}
+
+	//静态方法
+	// static init(date) {
+	// 	if (!this.instance) {
+	// 		this.instance = new Calendar(date);
+	// 	}
+	// 	return this.instance;
+	// }
+}
+
+
+export default Calendar

+ 88 - 0
src/hybrid/html/local.html

@@ -0,0 +1,88 @@
+<!DOCTYPE html>
+<html>
+	<head>
+		<meta charset="utf-8" />
+		<meta name="viewport" content="width=device-width, initial-scale=1">
+		<title>本地网页</title>
+		<style type="text/css">
+			.btn {
+				display: block;
+				margin: 20px auto;
+				padding: 5px;
+				background-color: #007aff;
+				border: 0;
+				color: #ffffff;
+				height: 40px;
+				width: 200px;
+			}
+
+			.btn-red {
+				background-color: #dd524d;
+			}
+
+			.btn-yellow {
+				background-color: #f0ad4e;
+			}
+
+			.desc {
+				padding: 10px;
+				color: #999999;
+			}
+		</style>
+	</head>
+	<body>
+		<p class="desc">web-view 组件加载本地 html 示例,仅在 App 环境下生效。点击下列按钮,跳转至其它页面。</p>
+		<div class="btn-list">
+			<button class="btn" type="button" data-action="navigateTo">navigateTo</button>
+			<button class="btn" type="button" data-action="redirectTo">redirectTo</button>
+			<button class="btn" type="button" data-action="navigateBack">navigateBack</button>
+			<button class="btn" type="button" data-action="reLaunch">reLaunch</button>
+			<button class="btn" type="button" data-action="switchTab">switchTab</button>
+		</div>
+		<p class="desc">网页向应用发送消息。注意:小程序端应用会在此页面后退时接收到消息。</p>
+		<div class="btn-list">
+			<button class="btn btn-red" type="button" id="postMessage">postMessage</button>
+		</div>
+		<!-- uni 的 SDK -->
+		<script type="text/javascript" src="https://js.cdn.aliyun.dcloud.net.cn/dev/uni-app/uni.webview.1.5.2.js"></script>
+		<script type="text/javascript">
+			document.addEventListener('UniAppJSBridgeReady', function() {
+				document.querySelector('.btn-list').addEventListener('click', function(evt) {
+					var target = evt.target;
+					if (target.tagName === 'BUTTON') {
+						var action = target.getAttribute('data-action');
+						switch (action) {
+							case 'switchTab':
+								uni.switchTab({
+									url: '/pages/tabBar/API/API'
+								});
+								break;
+							case 'reLaunch':
+								uni.reLaunch({
+									url: '/pages/tabBar/API/API'
+								});
+								break;
+							case 'navigateBack':
+								uni.navigateBack({
+									delta: 1
+								});
+								break;
+							default:
+								uni[action]({
+									url: '/pages/component/button/button'
+								});
+								break;
+						}
+					}
+				});
+				document.querySelector("#postMessage").addEventListener('click', function() {
+					uni.postMessage({
+						data: {
+							action: 'message'
+						}
+					});
+				})
+			});
+		</script>
+	</body>
+</html>

+ 21 - 0
src/main.js

@@ -0,0 +1,21 @@
+import Vue from 'vue'
+import App from './App'
+
+import store from './store'
+
+Vue.config.productionTip = false
+
+Vue.prototype.$store = store
+Vue.prototype.$backgroundAudioData = {
+	playing: false,
+	playTime: 0,
+	formatedPlayTime: '00:00:00'
+}
+
+App.mpType = 'app'
+
+const app = new Vue({
+	store,
+	...App
+})
+app.$mount()

+ 130 - 0
src/manifest.json

@@ -0,0 +1,130 @@
+{
+    "name" : "uniappTest",
+    "appid" : "",
+    "description" : "应用描述",
+    "versionName" : "1.0.0",
+    "versionCode" : "100",
+    "transformPx" : false,
+    /* 5+App特有相关 */
+    "app-plus" : {
+        "usingComponents" : true,
+        "nvueCompiler" : "uni-app",
+        "compilerVersion" : 3,
+        "nvueLaunchMode" : "fast",
+        "splashscreen" : {
+            "alwaysShowBeforeRender" : true,
+            "waiting" : true,
+            "autoclose" : true,
+            "delay" : 0
+        },
+        "modules" : {
+            "OAuth" : {},
+            "Payment" : {},
+            "Push" : {},
+            "Share" : {},
+            "Speech" : {},
+            "VideoPlayer" : {},
+            "Canvas" : "nvue canvas"
+        },
+        /* 应用发布信息 */
+        "distribute" : {
+            /* android打包配置 */
+            "android" : {
+                "permissions" : [
+                    "<uses-feature android:name=\"android.hardware.camera\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\"/>",
+                    "<uses-permission android:name=\"android.permission.VIBRATE\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_MOCK_LOCATION\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CALL_PHONE\"/>",
+                    "<uses-permission android:name=\"android.permission.CAMERA\"/>",
+                    "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
+                    "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
+                    "<uses-permission android:name=\"android.permission.GET_TASKS\"/>",
+                    "<uses-permission android:name=\"android.permission.INTERNET\"/>",
+                    "<uses-permission android:name=\"android.permission.MODIFY_AUDIO_SETTINGS\"/>",
+                    "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_CONTACTS\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_SMS\"/>",
+                    "<uses-permission android:name=\"android.permission.RECEIVE_BOOT_COMPLETED\"/>",
+                    "<uses-permission android:name=\"android.permission.RECORD_AUDIO\"/>",
+                    "<uses-permission android:name=\"android.permission.SEND_SMS\"/>",
+                    "<uses-permission android:name=\"android.permission.SYSTEM_ALERT_WINDOW\"/>",
+                    "<uses-permission android:name=\"android.permission.VIBRATE\"/>",
+                    "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
+                    "<uses-permission android:name=\"android.permission.WRITE_CONTACTS\"/>",
+                    "<uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"/>",
+                    "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>",
+                    "<uses-permission android:name=\"android.permission.WRITE_SMS\"/>",
+                    "<uses-permission android:name=\"android.permission.RECEIVE_USER_PRESENT\"/>"
+                ]
+            },
+            /* ios打包配置 */
+            "ios" : {
+                "UIBackgroundModes" : [ "audio" ],
+                "urlschemewhitelist" : [ "baidumap", "iosamap" ]
+            },
+            /* SDK配置 */
+            "sdkConfigs" : {
+                "speech" : {
+                    "ifly" : {}
+                }
+            },
+            "orientation" : [ "portrait-primary" ]
+        }
+    },
+    /* 快应用特有相关 */
+    "quickapp" : {},
+    /* 小程序特有相关 */
+    "mp-weixin" : {
+        "appid" : "wx39e390ff4635fc8e",
+        "setting" : {
+            "urlCheck" : false
+        },
+        "plugins": {
+            "btutil-plugin": {
+                "version": "0.0.13",
+                "provider": "wxe4c3965f1d95d9f9"
+            }
+        },
+        "usingComponents" : true,
+        "permission" : {
+            "scope.userLocation" : {
+                "desc" : "演示定位能力"
+            }
+        }
+    },
+    "mp-alipay" : {
+        "usingComponents" : true
+    },
+    "mp-baidu" : {
+        "usingComponents" : true
+    },
+    "mp-toutiao" : {
+        "usingComponents" : true
+    },
+    "h5" : {
+        "template" : "template.h5.html",
+        "router" : {
+            "mode" : "history",
+            "base" : "/h5/"
+        },
+        "sdkConfigs" : {
+            "maps" : {
+                "qqmap" : {
+                    "key" : ""
+                }
+            }
+        },
+        "async" : {
+            "timeout" : 20000
+        }
+    }
+}

+ 15 - 0
src/package.json

@@ -0,0 +1,15 @@
+{
+    "uni-app": {
+        "scripts": {
+            "mp-dingtalk": { 
+                "title":"钉钉小程序", 
+                "env": { 
+                    "UNI_PLATFORM": "mp-alipay" 
+                },
+                "define": { 
+                    "MP-DINGTALK": true 
+                }
+            }
+        }
+    }
+}

+ 86 - 0
src/pages.json

@@ -0,0 +1,86 @@
+{
+  "pages": [
+
+    {
+      "path": "pages/index/index",
+      "style": {
+        "navigationBarTitleText": "uni-app"
+      }
+    },
+    {
+      "path": "pages/index/QsCode",
+      "style": {
+        "navigationBarTitleText": "二维码"
+      }
+    },
+    {
+      "path": "pages/index/myCode",
+      "style": {
+        "navigationBarTitleText": "我的二维码"
+      }
+    },
+    {
+      "path": "pages/index/home",
+      "style": {
+        "navigationBarTitleText": "首页"
+      }
+    },
+    {
+      "path": "pages/index/heartRate",
+      "style": {
+        "navigationBarTitleText": "心率"
+      }
+    },
+    {
+      "path": "pages/index/bloodPressure",
+      "style": {
+        "navigationBarTitleText": "血压"
+      }
+    },
+    {
+      "path": "pages/index/test",
+      "style": {
+        "navigationBarTitleText": "测试"
+      }
+    },
+    {
+      "path": "pages/index/aike",
+      "style": {
+        "navigationBarTitleText": "艾科血糖仪"
+      }
+    },
+    {
+      "path": "pages/index/test1",
+      "style": {
+        "navigationBarTitleText": "测试页面"
+      }
+    },
+    {
+      "path": "pages/index/external_page",
+      "style": {
+        "navigationBarTitleText": "外部页面"
+      }
+    },
+    {
+      "path": "pages/index/wifi_page",
+      "style": {
+        "navigationBarTitleText": "WiFi页面"
+      }
+    },
+    {
+      "path": "pages/index/drug",
+      "style": {
+        "navigationBarTitleText": "用药打卡"
+      }
+    }
+  ],
+  "globalStyle": {
+    "navigationBarTextStyle": "black",
+    "navigationBarTitleText": "uni-app",
+    "navigationBarBackgroundColor": "#F8F8F8",
+    "backgroundColor": "#F8F8F8"
+  },
+  "easycom": {
+    "^u-(.*)": "uview-ui/components/u-$1/u-$1.vue"
+  }
+}

+ 311 - 0
src/pages/index/QsCode.vue

@@ -0,0 +1,311 @@
+<template>
+  <view class="container-page">
+    <view class="header" @click="saveQrcode">{{header}}</view>
+    <view class="qrcode-view">
+        <tki-qrcode v-if="ifShow" cid="qrcode1" ref="qrcode" :val="val" :size="size" :unit="unit" :icon="icon" :iconSize="iconsize" :lv="lv" :onval="onval" :loadMake="loadMake" :usingComponents="true" @result="qrR" />
+    </view>
+    <view class="bottom-name">{{Name}}</view>
+    <view class="uni-padding-wrap">
+      <view class="btns">
+        <button :disabled="myDis" type="primary" @tap="saveQrcode">保存二维码</button>
+      </view>
+    </view>
+    <canvas canvas-id="shareCanvas" :style="'width:'+myWid+'px;height:'+myHei+'px;position: fixed;top: -10000px'" @tap="openSetting"></canvas>
+  </view>
+</template>
+<script>
+import tkiQrcode from '@/components/tki-qrcode/tki-qrcode.vue'
+export default {
+  data() {
+    return {
+      ifShow: true,
+      val: 'http://m.wdklian.com/care/apk/care.user?type=BIND_PART&no=486&doctorId=50289', // 要生成的二维码值
+      size: 200, // 二维码大小
+      unit: 'px', // 单位
+      icon: '/static/doctor.png', // 二维码图标
+      iconsize: 40, // 二维码图标大小
+      lv: 3, // 二维码容错级别 , 一般不用设置,默认就行
+      onval: false, // val值变化时自动重新生成二维码
+      loadMake: true, // 组件加载完成后自动生成二维码
+      src: '', // 二维码生成后的图片地址或base64
+      header: "微信扫一扫二维码,绑定您的专属医生",
+      background:{
+        backgroundImage: 'linear-gradient(to left, #8E54E9, #4776E6)',
+      },
+      Name: '张医生',
+      imgSrc: '',
+      myWid: wx.getSystemInfoSync().windowWidth,
+      myHei: wx.getSystemInfoSync().windowHeight,
+      myDis: false
+    }
+  },
+  methods: {
+    creatQrcode() {
+      this.$refs.qrcode._makeCode()
+    },
+    saveQrcode() {
+      this.myDis = true
+      uni.showToast({
+        title: '二维码保存成功中...',
+        icon: 'none',
+        duration: 5000
+      });
+      const _this = this
+      const ctx = wx.createCanvasContext('shareCanvas')
+      // 设置背景图
+      ctx.drawImage('/static/bg.png', 0, 0, this.myWid, this.myHei)
+      // 设置白色背景
+      // ctx.fillStyle = '#ffffff';
+      // ctx.fillRect(10,70,this.myWid - 20, this.myHei - (this.myHei - 100 - this.size - 50));
+      this.roundRect(ctx, 10,70,this.myWid - 20, this.myHei - (this.myHei - 100 - this.size - 50), 25)
+      // 设置抬头
+      ctx.setTextAlign('center')    // 文字居中
+      ctx.setFillStyle('#8E54E9')  // 文字颜色:紫色
+      ctx.setFontSize(16)         // 文字字号:16px
+      ctx.fillText("微信扫一扫二维码,绑定您的专属医生", this.myWid / 2, 110)
+      // 二维码图
+      ctx.drawImage(_this.src, (this.myWid - this.size) / 2, 150, this.size, this.size)
+      // 设置边框
+      ctx.beginPath()
+      ctx.strokeStyle = '#e9547e'  // 边框颜色:红色
+      ctx.strokeRect((this.myWid - this.size) / 2 - 20, 130, this.size + 40, this.size + 40)
+      ctx.stroke()
+      ctx.fill()
+      ctx.closePath()
+      // 设置抬头
+      ctx.setTextAlign('center')    // 文字居中
+      ctx.setFillStyle('#180101')  // 文字颜色:黑色
+      ctx.setFontSize(22)         // 文字字号:22px
+      ctx.fillText("专属医生:", this.myWid / 2 - 45, 150 + this.size + 50)
+
+      ctx.font = 'normal normal 30px sans-serif';
+      ctx.setFillStyle('#ff6648')
+      ctx.fillText('张医生', this.myWid / 2 + 45, 150 + this.size + 50);
+
+      ctx.font = 'normal 20px sans-serif';
+      ctx.setFillStyle('#ffffff')
+      ctx.fillText('慢病指导', this.myWid / 2 - 60, 150 + this.size + 50 + 50);
+
+      ctx.font = 'normal 20px sans-serif';
+      ctx.setFillStyle('#ffffff')
+      ctx.fillText('饮食健康', this.myWid / 2 + 60, 150 + this.size + 50 + 50);
+
+      const hei = 150 + this.size + 50 + 85
+      ctx.font = 'normal 20px sans-serif';
+      ctx.setFillStyle('#ffffff')
+      ctx.fillText('体征数据', this.myWid / 2 - 60, hei);
+
+      ctx.font = 'normal 20px sans-serif';
+      ctx.setFillStyle('#ffffff')
+      ctx.fillText('健康宣教', this.myWid / 2 + 60, hei);
+      if (this.myHei - hei > 50) {
+        this.myHei = hei + 40
+      }
+      ctx.stroke()
+      // 完成作画
+      ctx.draw(false, (() => {
+        setTimeout(()=>{
+          uni.canvasToTempFilePath({
+            canvasId: 'shareCanvas',
+            destWidth: _this.myWid * 0.5 * 2, //分享图片尺寸=画布尺寸1*缩放比0.5*像素比2
+            destHeight: _this.myHei * 0.5 * 2,
+            quality:1,
+            fileType:'jpg',
+            success: (res) => {
+              uni.compressImage({
+                src: res.tempFilePath,
+                quality: 100,
+                success: r => {
+                  const share_img = r.tempFilePath
+                  uni.canvasToTempFilePath({
+                    canvasId: 'shareCanvas',
+                    destWidth: _this.myWid * 2,   //展示图片尺寸=画布尺寸1*像素比2
+                    destHeight: _this.myHei * 2,
+                    quality:1,
+                    fileType:'jpg',
+                    success: (e) => {
+                      console.log(e.tempFilePath)
+                      _this.imgSrc = e.tempFilePath
+                      _this.openSetting()
+                    },
+                  }, _this);
+                }
+              })
+            },
+          }, _this);
+        },500)
+      })())
+    },
+    // 绘制圆角矩形
+    roundRect(ctx, x, y, w, h, r, c = '#fff') {
+      if (w < 2 * r) { r = w / 2; }
+      if (h < 2 * r) { r = h / 2; }
+      ctx.beginPath();
+      ctx.fillStyle = c;
+      ctx.strokeStyle = '#fff'
+      ctx.stroke();
+      ctx.arc(x + r, y + r, r, Math.PI, Math.PI * 1.5);
+      ctx.moveTo(x + r, y);
+      ctx.lineTo(x + w - r, y);
+      ctx.lineTo(x + w, y + r);
+
+      ctx.arc(x + w - r, y + r, r, Math.PI * 1.5, Math.PI * 2);
+      ctx.lineTo(x + w, y + h - r);
+      ctx.lineTo(x + w - r, y + h);
+
+      ctx.arc(x + w - r, y + h - r, r, 0, Math.PI * 0.5);
+      ctx.lineTo(x + r, y + h);
+      ctx.lineTo(x, y + h - r);
+
+      ctx.arc(x + r, y + h - r, r, Math.PI * 0.5, Math.PI);
+      ctx.lineTo(x, y + r);
+      ctx.lineTo(x + r, y);
+
+      ctx.fill();
+      ctx.closePath();
+    },
+    qrR(res) {
+      this.src = res
+    },
+    getTx() {
+      let that = this;
+      // 直接设置 options.image 的值,在手机上logo会绘制失败
+      new Promise((resolve, reject) => {
+        // 绘制网络地址的logo时需要先使用 wx.getImageInfo 获取到图片信息
+        // 注意网络图片需先配置download域名 wx.getImageInfo 才能生效。
+        wx.getImageInfo({
+          src: 'http://wdklmall.oss-cn-shenzhen.aliyuncs.com/mallgoods/normal/0B1CD5C4488447D6BF94F1DCDCB070F9.png',
+          success: (res) => {
+            resolve(res.path);
+          },
+          fail: (e) => {
+            resolve();
+          }
+        })
+      }).then((path) => {
+        that.icon = path
+        setTimeout(() => {
+          that.creatQrcode()
+          // that.saveQrcode()
+        }, 100);
+      })
+    },
+    openSetting() {
+      const _this = this
+      wx.getSetting({
+        success(res) {
+          if (!res.authSetting['scope.writePhotosAlbum']) {
+            wx.openSetting({
+              success(res){
+                _this.saveImageToPhotosAlbum()
+              },
+              fail() {
+                _this.myDis = false
+              }
+            })
+          } else {
+            _this.saveImageToPhotosAlbum()
+          }
+        }
+      })
+    },
+    saveImageToPhotosAlbum() {
+      const _this = this
+      wx.saveImageToPhotosAlbum({
+        filePath: _this.imgSrc,
+        success: function () {
+          _this.myDis = false
+          uni.showToast({
+            title: '二维码保存成功',
+            icon: 'success',
+            duration: 2000
+          });
+        },
+        fail: function (res) {
+          _this.myDis = false
+        }
+      });
+    }
+  },
+  components: {
+    tkiQrcode
+  },
+  onLoad: function () {
+    // this.getTx()
+    this.creatQrcode()
+  },
+}
+</script>
+<style>
+page{
+  background: -webkit-linear-gradient(to left, #8E54E9, #4776E6);  /* Chrome 10-25, Safari 5.1-6 */
+  background: linear-gradient(to left, #8E54E9, #4776E6); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */
+}
+</style>
+<style lang="less" scoped>
+// #ifdef APP-PLUS
+.background{
+  width: 100%;
+  height: 100%;
+  position: absolute;
+  z-index: 0;
+  background: -webkit-linear-gradient(to left, #8E54E9, #4776E6);  /* Chrome 10-25, Safari 5.1-6 */
+  background: linear-gradient(to left, #8E54E9, #4776E6); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */
+}
+// #endif
+.container-page{
+  width: 90%;
+  z-index: 1;
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  // #ifdef APP-PLUS
+  bottom: 256rpx;
+  // #endif
+  margin: auto;
+  margin-top: 200rpx;
+  background-color: #fff;
+  border-radius: 10rpx;
+  margin-bottom: 50rpx;
+  // #ifdef MP-WEIXIN
+  // margin-bottom: 100rpx;
+  // #endif
+  .header {
+    text-align: center;
+    padding: 30rpx 0;
+    color: #1b82d2;
+    font-weight: bold;
+    letter-spacing: 2px;
+    background-color: #f0f0f0;
+    border-radius: 10rpx 10rpx 0 0;
+    margin-bottom: 30rpx;
+    overflow: hidden;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+  }
+  .qrcode-view{
+    display: flex;
+    justify-content: center;  /*水平方向居中*/
+    margin-top: 50rpx;
+  }
+  .bottom-name{
+    text-align: center;
+    margin-top: 50rpx;
+    letter-spacing: 2px;
+    font-weight: 400;
+    font-size: 35rpx;
+  }
+  .btns {
+    display: flex;
+    flex-direction: column;
+    width: 100%;
+  }
+
+  button {
+    width: 100%;
+    margin-top: 50upx;
+  }
+}
+</style>

+ 755 - 0
src/pages/index/aike.vue

@@ -0,0 +1,755 @@
+<template name="components">
+    <view style="position: relative;">
+        <view v-if="isBanding">
+            <view class="cu-bar bg-white solid-bottom margin-top">
+                <view class="action">
+                    <text class="cuIcon-title text-orange"></text>
+                    血糖记录
+                </view>
+            </view>
+            <view class="cu-list menu">
+                <view class="cu-item" v-for="(item,index) in xtList" :key="index">
+                    <view class="content">
+                        <text class="text-grey">{{item.show_time}}</text>
+                        <text v-if="item.show_name === '空腹'" class="cu-tag round bg-pink light">{{item.show_name}}
+                        </text>
+                        <text v-if="item.show_name === '餐后2小时'" class="cu-tag round bg-cyan light">{{item.show_name}}
+                        </text>
+                        <text v-if="item.show_name === '随机'" class="cu-tag round bg-green light">{{item.show_name}}
+                        </text>
+                    </view>
+                    <view class="action">
+                        <view class="cu-tag round bg-blue light">{{item.vs_value}}</view>
+                        mmol/L
+                    </view>
+                </view>
+            </view>
+            <view class="text-white text-white" style="bottom:0;margin-top: 120px">.</view>
+            <view class="myfoot" style="bottom:0">
+                <button v-if="xtList.length > 0" class="cu-btn block bg-gradual-green margin-tb-sm lg shadow-blur"
+                        @click="saveList">
+                    上传
+                </button>
+                <button v-if="myDeviceId" class="cu-btn block bg-gradual-orange margin-tb-sm lg shadow-blur"
+                        @click="closeBLEConnection">
+                    取消绑定
+                </button>
+            </view>
+        </view>
+
+        <view v-else>
+            <view class="cu-bar bg-white solid-bottom margin-top">
+                <view class="action">
+                    <text class="cuIcon-title text-orange"></text>
+                    蓝牙列表
+                </view>
+            </view>
+            <view class="cu-load bg-blue loading"></view>
+            <view class="cu-list menu">
+                <view class="cu-item arrow" v-for="(item,index) in devicesList" :key="index"
+                      @click="createBLEConnection(item.deviceId,item.name)">
+                    <view class="content">
+                        <text class="cuIcon-warn text-green"></text>
+                        <text class="text-grey">{{item.name}}</text>
+                    </view>
+                </view>
+                <view class="cu-item arrow" v-for="(item,index) in laList" :key="index"
+                      @click="createBLEConnection(item.deviceId,item.name)">
+                    <view class="content">
+                        <text class="cuIcon-warn text-green"></text>
+                        <text class="text-grey">{{item.name}}</text>
+                    </view>
+                </view>
+            </view>
+            <view>
+                <button class="cu-btn block bg-gradual-green margin-tb-sm lg shadow-blur gd" @click="cancel">
+                    <text class="text-Abc cuIconfont-spin"></text>
+                    取消
+                </button>
+            </view>
+        </view>
+        <view class="cu-load load-modal" v-if="loadModal">
+            <view class="cuIcon-emojifill text-orange"></view>
+            <view class="gray-text">{{loadMsg}}</view>
+        </view>
+    </view>
+</template>
+
+<script>
+    export default {
+        name: "aike",
+        data() {
+            return {
+                isOpen: false,
+                loadModal: true,
+                loadMsg: '加载中...',
+                myDeviceId: null,
+                laList: [],
+                devicesList: [],
+                isBanding: true,
+                platform: null,
+                hasNo: 1, // 是否保存绑定到数据库
+                serviceId: null,
+                characteristicId: null,
+                myData: '',
+                xtList: [],
+                writeCmd: {
+                    read: '26445A20063336380D',
+                    del: '26444A200634393533330D',
+                    ok: '2644312031200632373838340D'
+                },
+                isChange: 'read',
+                bleServer: null,
+                isPostAdvertising: false
+            };
+        },
+        onLoad() {
+            wx.openBluetoothAdapter({
+                mode: 'peripheral',
+                success: (res) => {
+                    wx.createBLEPeripheralServer({
+                        success: (res) => {
+                            this.bleServer = res.server
+                            console.log(this.bleServer)
+                        },
+                        fail: (res) => {}
+                    })
+                },
+                fail: (res) => {
+                    wx.showToast({
+                        title: '请打开蓝牙',
+                    })
+                }
+            })
+            let _this = this
+            setTimeout(function () {
+                _this.valueChange()
+            }, 1000)
+            // this.loadModal = true
+            // this.myDeviceId = '4C:E1:73:C3:67:EA'
+            // this.myDeviceId = 'CDCEDFEE-3D60-0830-5DAC-069C31946B25'
+            // let _this = this
+            // uni.getSystemInfo({
+            //     success(res) {
+            //         console.log('platform', res.platform) // 客户端平台,值域为:ios、android
+            //         _this.platform = res.platform
+            //         _this.openBluetoothAdapter()
+            //     }
+            // })
+        },
+        methods: {
+            valueChange: function (event) {
+                console.log('我进来了',this.isPostAdvertising)
+                if (this.bleServer != null) {
+                    //这里为了方便就先停止再发送,这个对测试结果没影响
+                    if(!this.isPostAdvertising){
+                        this.bleServer.stopAdvertising({
+                            complete: (res) => {
+                                console.log("stop success", res)
+                                if(!this.isPostAdvertising){
+                                    console.log("invoke startAdvertising")
+                                    this.isPostAdvertising = true;
+                                    this.bleServer.startAdvertising({
+                                        advertiseRequest: {
+                                            connectable: true,
+                                            deviceName:"测试蓝牙广播",
+                                            serviceUuids: ['00001812-0000-1000-8000-00805F9B34FB'],
+                                        },
+                                        powerLevel: 'high',
+                                        success: (res)=> {
+                                            console.log("start success", res)
+                                        },
+                                        fail: (res)=> {
+                                            console.log("start fail", res)
+                                        },
+                                        complete:(res)=>{
+                                            console.log("start complete", res)
+                                            this.isPostAdvertising = false;
+                                        }
+                                    })
+
+                                }else{
+                                    console.log("isAdvertising now");
+                                }
+                            },
+                            success: (res) => {
+                                console.log('stopAdvertising success', res)
+                            },
+                            fail: (res) => {
+                                console.log('stopAdvertising fail', res)
+                            }
+                        })
+                    }
+                } else {
+                    wx.showToast({
+                        title: '异常',
+                    })
+                }
+            },
+            // 初始化蓝牙模块
+            openBluetoothAdapter() {
+                let _this = this
+                uni.openBluetoothAdapter({
+                    success: function (r) {
+                        console.log("openBluetoothAdapter success", r)
+                        _this.isOpen = true
+                        if (_this.myDeviceId !== null) {
+                            _this.getConnectedBluetoothDevices()
+                        } else {
+                            _this.isBanding = false
+                            _this.startBluetoothDevicesDiscovery()
+                            if (_this.platform === 'android') {
+                                _this.getBluetoothDevices()
+                            }
+                        }
+                    },
+                    fail: function (r) {
+                        _this.loadModal = false
+                        if (r.errCode === 10001) {
+                            uni.showToast({
+                                title: '蓝牙未开启,请先打开蓝牙',
+                                icon: 'none',
+                                duration: 3000
+                            })
+                            _this.isOpen = false
+                        } else {
+                            uni.showToast({
+                                title: '初始化蓝牙失败,错误码:' + (r.errCode || r.errMsg),
+                                icon: 'none',
+                                duration: 3000
+                            })
+                        }
+                        console.log("openBluetoothAdapter fail", r)
+                    }
+                })
+            },
+            // 获取处于已连接状态的设备
+            getConnectedBluetoothDevices() {
+                let _this = this
+                setTimeout(function () {
+                    if (_this.isOpen) {
+                        uni.getConnectedBluetoothDevices({
+                            success: function (r) {
+                                if (r.devices.length > 0) {
+                                    if (_this.myDeviceId !== r.devices[0].deviceId) {
+                                        uni.showToast({
+                                            title: '您连接的设备不是您绑定的设备请重连',
+                                            icon: 'none',
+                                            duration: 3000
+                                        })
+                                    }
+                                    _this.isOpen = true
+                                    _this.isBanding = true
+                                } else {
+                                    if ((_this.myDeviceId.length > 20 && _this.platform === 'android') || (_this.myDeviceId.length <= 20 && _this.platform === 'ios')) {
+                                        console.log('nononon')
+                                        uni.showToast({
+                                            title: '请您手动连接',
+                                            icon: 'none',
+                                            duration: 4000
+                                        })
+                                        _this.isBanding = false
+                                        _this.hasNo = 1
+                                        _this.myDeviceId = null
+                                        if (_this.platform === 'android') {
+                                            _this.getBluetoothDevices()
+                                        }
+                                        _this.startBluetoothDevicesDiscovery()
+                                    } else {
+                                        _this.startBluetoothDevicesDiscovery()
+                                    }
+                                }
+                                console.log("获取处于已连接状态的设备 success", r)
+                            },
+                            fail: function (r) {
+                                console.log("未初始化蓝牙是配饰器 fail", r)
+                            }
+                        })
+                    }
+                }, 100);
+
+            },
+            // 开始搜寻附近的蓝牙外围设备
+            startBluetoothDevicesDiscovery() {
+                let _this = this
+                this.laList = []
+                setTimeout(function () {
+                    console.log(_this.myDeviceId, _this.isBanding)
+                    if (_this.isOpen) {
+                        _this.loadModal = true
+                        _this.loadMsg = '加载中...'
+                        // 开始搜寻附近的蓝牙外围设备
+                        uni.startBluetoothDevicesDiscovery({
+                            success: function (r) {
+                                console.log("开始搜寻附近的蓝牙外围设备 success", r)
+                                // 找到新设备的事件
+                                uni.onBluetoothDeviceFound(function callback(r) {
+                                    console.log(r)
+                                    if (_this.myDeviceId !== null && _this.isBanding) {
+                                        if (r.devices[0].deviceId === _this.myDeviceId) {
+                                            _this.createBLEConnection(r.devices[0].deviceId, null)
+                                        }
+                                    } else {
+                                        _this.loadModal = false
+                                        if (undefined !== r.devices[0].name && '' !== r.devices[0].name && r.devices[0].name !== '未知设备') {
+                                            console.log(_this.ab2hex2(r.devices[0].advertisData))
+                                            if (_this.laList.length > 0) {
+                                                let isCF = 0
+                                                _this.laList.forEach(item => {
+                                                    if (item.deviceId === r.devices[0].deviceId) {
+                                                        isCF = 1
+                                                    }
+                                                })
+                                                if (isCF === 0) {
+                                                    console.log(r.devices[0].name, r)
+                                                    _this.laList.push(r.devices[0])
+                                                }
+                                            } else {
+                                                _this.laList.push(r.devices[0])
+                                            }
+                                        }
+                                    }
+                                })
+                            },
+                            fail: function (r) {
+                                _this.loadModal = false
+                                uni.showToast({
+                                    title: r.errMsg,
+                                    icon: 'none',
+                                    duration: 3000
+                                })
+                                console.log("未初始化蓝牙配饰器 fail", r)
+                            }
+                        })
+                    } else {
+                        uni.showToast({
+                            title: '未初始化蓝牙配饰器',
+                            icon: 'none',
+                            duration: 3000
+                        })
+                    }
+                }, 200);
+
+            },
+            // 停止搜寻附近的蓝牙外围设备
+            stopBluetoothDevicesDiscovery() {
+                uni.stopBluetoothDevicesDiscovery({
+                    success: function (r) {
+                        console.log("停止搜寻附近的蓝牙外围设备 success", r)
+                    },
+                    fail: function (r) {
+                        console.log("停止搜寻附近的蓝牙外围设备 fail", r)
+                    }
+                })
+            },
+            // 连接低功耗蓝牙设备
+            createBLEConnection(deviceId, name) {
+                let _this = this
+                if (name !== null) {
+                    if (name.indexOf('OGM_') === -1) {
+                        uni.showToast({
+                            title: '连接的设备不是艾科血糖仪,请重新连接!',
+                            icon: 'none',
+                            duration: 3000
+                        })
+                        return
+                    }
+                }
+                _this.loadMsg = '正在连接...'
+                _this.loadModal = true
+                uni.createBLEConnection({
+                    deviceId: deviceId,
+                    success: function (r) {
+                        if (_this.hasNo === 1) {
+                            // 当myDeviceId为null时加入设备绑定用户操作
+                            // 请求地址:/care/care_device_member_bind/add
+                            // 参数:
+                            // const params = {
+                            //     imei: deviceId,
+                            //     deviceSkuId: 7,
+                            //     memberId: Storage.getItem('uid')
+                            // }
+                            // API_bindDevice.bindDevice(params).then(res => {
+                            //     if (res.success) {
+                            //         _this.hasNo = 2
+                            //         _this.$message.success("绑定成功")
+                            //         _this.bindedDevice = res.data
+                            //     } else {
+                            //         _this.$message.error(res.message)
+                            //     }
+                            // })
+                        }
+                        //延迟0.5s获取设备的services
+                        setTimeout(function () {
+                            console.log("获取设备的services");
+                            _this.getBLEDeviceServices();
+                        }, 300);
+                        _this.isOpen = true
+                        _this.isBanding = true
+                        _this.myDeviceId = deviceId
+                        _this.onBLEConnectionStateChange()
+                        console.log("连接低功耗蓝牙设备 success", r)
+                    },
+                    fail: function (r) {
+                        if (r.errCode === -1) {
+                            uni.showToast({
+                                title: '蓝牙已连接,请重启蓝牙重连',
+                                icon: 'none',
+                                duration: 3000
+                            })
+                        } else {
+                            uni.showToast({
+                                title: '连接失败,请重试',
+                                icon: 'none',
+                                duration: 3000
+                            })
+                        }
+                        console.log("连接低功耗蓝牙设备 fail", r)
+                    },
+                    complete(res) {
+                        _this.loadModal = false
+                        _this.stopBluetoothDevicesDiscovery()
+                    }
+                })
+            },
+            // 断开与低功耗蓝牙设备的连接
+            closeBLEConnection() {
+                let _this = this
+                this.xtList = []
+                uni.closeBLEConnection({
+                    deviceId: _this.myDeviceId,
+                    success: function (r) {
+                        if (_this.isChange === 'read') {
+                            // API_bindDevice.deleteDevice(_this.bindedDevice.id).then(res => {
+                            //     _this.hasNo = 1
+                            //     console.log('设备用户绑定删除', res)
+                            // })
+                            _this.hasNo = 1
+                            _this.myDeviceId = null
+                        }
+                        console.log("断开与低功耗蓝牙设备的连接 success", r)
+                    },
+                    fail: function (r) {
+                        _this.isBanding = true
+                        console.log("断开与低功耗蓝牙设备的连接 fail", r)
+                    },
+                    complete: function () {
+                        _this.isBanding = false
+                    }
+                })
+            },
+            // 低功耗蓝牙连接状态的改变事件
+            onBLEConnectionStateChange() {
+                let _this = this
+                uni.onBLEConnectionStateChange(function callback(r) {
+                    console.log('低功耗蓝牙连接状态的改变事件', r)
+                    if (!r.connected) {
+                        _this.isBanding = false
+                        if (_this.platform === 'android') {
+                            _this.getBluetoothDevices()
+                        }
+                        _this.startBluetoothDevicesDiscovery()
+                    }
+                })
+            },
+            // 已发现的蓝牙设备
+            getBluetoothDevices() {
+                let _this = this
+                uni.getBluetoothDevices({
+                    success: function (r) {
+                        _this.devicesList = []
+                        if (r.devices.length > 0) {
+                            r.devices.forEach(value => {
+                                if (value.name !== '未知设备' && value.deviceId !== '00:00:00:00:00:00') {
+                                    _this.devicesList.push(value)
+                                }
+                            })
+                        }
+                        console.log("已发现的蓝牙设备 success", r)
+                    },
+                    fail: function (r) {
+                        console.log("已发现的蓝牙设备 fail", r)
+                    }
+                })
+            },
+            // 取消
+            cancel() {
+                this.isBanding = true
+                this.stopBluetoothDevicesDiscovery()
+            },
+            // 获取设备的服务ID
+            getBLEDeviceServices() {
+                let _this = this
+                _this.loadModal = true
+                _this.loadMsg = '正在获取血糖数据...'
+                uni.getBLEDeviceServices({
+                    // 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
+                    deviceId: this.myDeviceId,
+                    success(res) {
+                        console.log('获取设备的服务ID success:', res.services)
+                        _this.serviceId = res.services[0].uuid
+                        _this.getBLEDeviceCharacteristics()
+                    },
+                    fail(res) {
+                        _this.loadModal = false
+                        console.log('获取设备的服务 failed:' + res);
+                    }
+                })
+            },
+            // 获取指定服务的特征值
+            getBLEDeviceCharacteristics() {
+                let _this = this
+                uni.getBLEDeviceCharacteristics({
+                    // 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
+                    deviceId: this.myDeviceId,
+                    // 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取
+                    serviceId: this.serviceId,
+                    success(res) {
+                        console.log('获取指定服务的特征值 success:', res.characteristics);
+                        _this.characteristicId = res.characteristics[0].uuid
+                        _this.notifyBLECharacteristicValueChange()
+                    },
+                    fail(res) {
+                        _this.loadModal = false
+                        console.log('获取指定服务的特征值 failed:', res);
+                    }
+                })
+            },
+            // 开启订阅特征值
+            notifyBLECharacteristicValueChange() {
+                let _this = this
+                console.log(this.serviceId, this.characteristicId)
+                uni.notifyBLECharacteristicValueChange({
+                    state: true, // 启用 notify 功能
+                    // 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
+                    deviceId: this.myDeviceId,
+                    // 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取
+                    serviceId: this.serviceId,
+                    // 这里的 characteristicId 需要在 getBLEDeviceCharacteristics 接口中获取
+                    characteristicId: this.characteristicId,
+                    success(res) {
+                        console.log('开启订阅特征值 success', JSON.stringify(res))
+
+                        // ArrayBuffer转16进度字符串示例
+                        function ab2hex(buffer) {
+                            const hexArr = Array.prototype.map.call(
+                                new Uint8Array(buffer),
+                                function (bit) {
+                                    return ('00' + bit.toString(16)).slice(-2)
+                                }
+                            )
+                            return hexArr.join('')
+                        }
+
+                        uni.onBLECharacteristicValueChange(function (res) {
+                            console.log(`监听成功 ${res.characteristicId} has changed, now is ${JSON.stringify(res.value)}`)
+                            console.log(ab2hex(res.value))
+                            if (_this.isChange === 'read') {
+                                _this.myData += ab2hex(res.value)
+                            } else {
+                                _this.loadModal = false
+                            }
+                        })
+                    },
+                    fail(res) {
+                        _this.loadModal = false
+                        console.log('开启订阅特征值 failed:', res)
+                    }
+                })
+                _this.myData = ''
+                setTimeout(function () {
+                    if (_this.laList.length === 0) {
+                        _this.isChange = 'read'
+                        _this.writeBLECharacteristicValue()
+                    } else {
+                        _this.loadModal = false
+                        uni.showToast({
+                            title: '没有数据',
+                            icon: 'none',
+                            duration: 3000
+                        })
+                    }
+                }, 1300)
+            },
+            // 写入特征值
+            writeBLECharacteristicValue() {
+                let _this = this
+                let writeCode
+                if (this.isChange === 'read') {
+                    writeCode = this.writeCmd.read
+                } else if (this.isChange === 'del') {
+                    writeCode = this.writeCmd.del
+                } else {
+                    console.log('我是ok》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》')
+                    writeCode = this.writeCmd.ok
+                }
+                // let writeCode = '264445200631323636340D'
+                let deviceId = this.myDeviceId;
+                let serviceId = this.serviceId;
+                let characteristicId = this.characteristicId;
+                //因为协议文档中,一个字节两个字符的控制命令,codeLength为命令字节数
+                let codeLength = writeCode.length / 2;
+                const buffer = new ArrayBuffer(codeLength);
+                const dataView = new DataView(buffer)
+                //在这里解析将要写入的值
+                for (let i = 0; i < codeLength; i++) {
+                    dataView.setUint8(i, '0X' + writeCode.substring(i * 2, i * 2 + 2));
+                    console.log("次数:" + i + "-----0X" + writeCode.substring(2 * i, 2 * i + 2));
+                }
+                console.log("写入数据中deviceId:" + deviceId);
+                console.log("写入数据中serviceId:" + serviceId);
+                console.log("写入数据中characteristicId:" + characteristicId);
+                console.log("分割线************************************");
+                uni.writeBLECharacteristicValue({
+                    // 这里的 deviceId 需要在 getBluetoothDevices 或 onBluetoothDeviceFound 接口中获取
+                    deviceId,
+                    // 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取
+                    serviceId,
+                    // 这里的 characteristicId 需要在 getBLEDeviceCharacteristics 接口中获取
+                    characteristicId,
+                    // 这里的value是ArrayBuffer类型
+                    value: buffer,
+                    success(res) {
+                        console.log('writeBLECharacteristicValue success', JSON.stringify(res))
+                    },
+                    fail(res) {
+                        _this.loadModal = false
+                        console.log("写入数据失败", res.errMsg)
+                    }
+                });
+            },
+            // 获取血糖
+            getXtList() {
+                let _this = this
+                const data = this.myData.split('1e')
+                _this.myData = ''
+                console.log('data=', data)
+                data.forEach(item => {
+                    if (item.length > 20) {
+                        let codeLength = item.length / 2
+                        let str2 = ''
+                        for (let i = 0; i < codeLength; i++) {
+                            str2 += String.fromCharCode(('0X' + item.substring(2 * i, 2 * i + 2)))
+                        }
+                        const t = str2.split(' ')
+                        let param_id, param_name, setting_id
+                        if (t[2] == 1) {
+                            param_id = 13
+                            param_name = '餐后2小时'
+                            setting_id = 2
+                        } else if (t[2] == 2) {
+                            param_id = 12
+                            param_name = '空腹'
+                            setting_id = 1
+                        } else {
+                            param_id = 14
+                            param_name = '随机'
+                            setting_id = 3
+                        }
+                        let time = _this.qige(t[0])
+                        const params = {
+                            setting_id: setting_id,
+                            log_time: _this.getUnixTime(time),
+                            vs_value: _this.formatDecimal(t[1] / 18 + 0.05, 1),
+                            param_id: param_id,
+                            imei: _this.myDeviceId,
+                            show_name: param_name,
+                            show_time: time
+                        }
+                        _this.xtList.push(params)
+                    }
+                })
+                _this.loadModal = false
+            },
+            qige(str) {
+                return '20' + str.substr(4, 2) + '-' + str.substr(0, 2) + '-' + str.substr(2, 2) + ' ' + str.substr(6, 2) + ':' + str.substr(8, 2)
+            },
+            formatDecimal(num, decimal) {
+                num = num.toString();
+                let index = num.indexOf('.');
+                if (index !== -1) {
+                    num = num.substring(0, decimal + index + 1)
+                } else {
+                    num = num.substring(0)
+                }
+                return parseFloat(num).toFixed(decimal)
+            },
+            getUnixTime(dateStr) {
+                let newstr = dateStr.replace(/-/g, '/');
+                let date = new Date(newstr);
+                let time_str = date.getTime().toString();
+                return time_str.substr(0, 10);
+            },
+            saveList() {
+                this.isChange = 'del'
+                this.writeBLECharacteristicValue()
+            },
+            ab2hex2(buffer) {
+                const hexArr = Array.prototype.map.call(
+                    new Uint8Array(buffer),
+                    function (bit) {
+                        return ('00' + bit.toString(16)).slice(-2)
+                    }
+                )
+                return hexArr.join('')
+            }
+        },
+        onUnload() {
+            this.stopBluetoothDevicesDiscovery()
+        },
+        watch: {
+            myData: function (newData) {
+                let _this = this
+                if (newData !== '' && _this.isChange === 'read' && newData.length > 30) {
+                    if (newData.indexOf('1e06') !== -1) {
+                        _this.xtList = []
+                        _this.isChange = 'del'
+                        _this.getXtList()
+                    } else if (newData.length < 30) {
+                        _this.loadModal = false
+                        uni.showToast({
+                            title: '没有数据',
+                            icon: 'none',
+                            duration: 3000
+                        })
+                        _this.isChange = 'ok'
+                        _this.writeBLECharacteristicValue()
+                    }
+                }
+            },
+            isChange: function (data) {
+                let _this = this
+                console.log('isChange=', data)
+                if (data === 'ok') {
+                    setTimeout(function () {
+                        _this.laList = []
+                        _this.writeBLECharacteristicValue()
+                        setTimeout(function () {
+                            console.log('2222222222')
+                            _this.writeBLECharacteristicValue()
+                        }, 500)
+                        setTimeout(function () {
+                            console.log('33333333')
+                            _this.writeBLECharacteristicValue()
+                            _this.isChange = 'read'
+                        }, 1000)
+                    }, 500)
+                } else if (data === 'del') {
+                    setTimeout(function () {
+                        _this.writeBLECharacteristicValue()
+                        _this.isChange = 'ok'
+                    }, 1000)
+                }
+            }
+        }
+    }
+</script>
+
+<style scoped>
+    .myfoot {
+        position: fixed;
+        width: 100%;
+        bottom: 0;
+        z-index: 1024
+    }
+</style>

+ 208 - 0
src/pages/index/bloodPressure.vue

@@ -0,0 +1,208 @@
+<template>
+    <view style="margin-bottom: 60px">
+        <view class="cu-bar bg-white solid-bottom margin-top">
+            <view class="action">
+                <text class="cuIcon-title text-orange"></text>
+                血压
+            </view>
+        </view>
+        <view class="cu-list menu">
+            <view class="cu-item" v-for="(item,index) in reverseData" :key="index">
+                <view class="content">
+                    <text class="text-grey">{{changeTime(item.time)}}</text>
+                </view>
+                <view class="action">
+                    <view class="cu-tag round bg-blue light">{{item.maxVal}}~{{item.minVal}}</view>
+                    mmHg
+                </view>
+            </view>
+        </view>
+        <button class="cu-btn block bg-gradual-green margin-tb-sm lg shadow-blur gd" @click="reqSynData">
+            <text class="text-Abc cuIconfont-spin" :class="isChange ? 'cuIcon-loading2' : ''"></text>
+            检查血压
+        </button>
+    </view>
+
+</template>
+
+<script>
+    import {HBraclete} from '@/utils/bluetest.js'
+
+    export default {
+        name: "heartRate",
+        data() {
+            return {
+                isOpen: false,
+                myDeviceId: null,
+                heartRateList: [
+                    {'time': 257, 'minVal': 73, 'maxVal': 109},
+                    {'time': 258, 'minVal': 63, 'maxVal': 100},
+                    {'time': 259, 'minVal': 70, 'maxVal': 101},
+                    {'time': 260, 'minVal': 71, 'maxVal': 119},
+                    {'time': 261, 'minVal': 67, 'maxVal': 102},
+                    {'time': 1000, 'minVal': 87, 'maxVal': 103},
+                    {'time': 1010, 'minVal': 87, 'maxVal': 104},
+                    {'time': 1020, 'minVal': 87, 'maxVal': 105},
+                    {'time': 787, 'minVal': 87, 'maxVal': 106},
+                    {'time': 878, 'minVal': 87, 'maxVal': 107},
+                    {'time': 876, 'minVal': 87, 'maxVal': 104},
+                    {'time': 989, 'minVal': 87, 'maxVal': 101}
+                ],
+                countTime: 0,
+                isChange:false
+            };
+        },
+        computed: {
+            reverseData() {
+                return this.sortByKey(this.heartRateList, 'time')
+            }
+        },
+        mounted() {
+            // 加入根据member_id、deviceId(imei)、今天的时间查询血压数据
+            // 请求地址:/care/care_vital_signs_log/getLogsByDay
+            // const today = (new Date().setHours(0, 0, 0, 0)) / 1000
+            // const todayend = today + 86400
+            // const param = {
+            //     fixedCondition : 'log_time>=' + today + ' and log_time<=' + todayend + ' and member_id = ' + Storage.getItem('uid') + ' and imei = ' + Storage.getItem('deviceId') + ' and group_id = 7'
+            // }
+            this.myDeviceId = 'ED:D5:D9:7B:8E:A1'
+            this.countTime = this.heartRateList[this.heartRateList.length - 1].time
+        },
+        methods: {
+            //  请求同步数据(app主动查询内容)
+            reqSynData() {
+                if (!this.isChange){
+                    this.isChange = true
+                    let _this = this
+                    HBraclete.synOper.reqSynData({
+                        data: [0, 0, 0, 1, 0, 0, 0, 0],
+                        deviceId: this.myDeviceId,
+                        success: function (r) {
+                            console.log("请求同步数据(app主动查询内容) success", r)
+                            _this.healthDataSwitch(1)
+                            _this.synBloodPressureData()
+                            setTimeout(function () {
+                                _this.isChange = false
+                                // 加入批量保存血压功能this.heartRateList
+                                // 请求地址:/care/care_vital_signs_log
+                                // 参数: careVitalSignsLogList
+                                _this.healthDataSwitch(0)
+                            }, 120000)
+                        },
+                        fail: function (r) {
+                            _this.isChange = false
+                            console.log("请求同步数据(app主动查询内容) fail", r)
+                            if (r.errCode === 10006) {
+                                _this.createBLEConnection(_this.myDeviceId)
+                            }
+                        }
+                    })
+                }
+            },
+            // 同步血压数据
+            synBloodPressureData(){
+                let _this = this
+                HBraclete.synOper.synBloodPressureData(function callback(res) {
+                    console.log('同步血压数据',res)
+                    console.log(res.data[0].time)
+                    if (res.data.length > 0) {
+                        let i = _this.heartRateList.length - 1
+                        if (_this.countTime === res.data[0].time) {
+                            console.log('更新')
+                            _this.heartRateList[i].minVal = res.data[0].minVal
+                            _this.heartRateList[i].maxVal = res.data[0].maxVal
+                        } else if (res.data[0].time > _this.countTime) {
+                            console.log('添加')
+                            _this.heartRateList.push(res.data[0])
+                            _this.sortByKey(_this.heartRateList, 'time')
+                            _this.countTime = res.data[0].time
+                        }
+
+                    }
+                })
+            },
+            // 健康数据开关
+            healthDataSwitch(type) {
+                HBraclete.synOper.healthDataSwitch({
+                    data: [[0, 1, 0], type],
+                    deviceId: this.myDeviceId,
+                    success: function (r) {
+                        console.log("健康数据开关 success", r)
+                    },
+                    fail: function (r) {
+                        console.log("健康数据开关 fail", r)
+                    }
+                })
+            },
+            // 连接低功耗蓝牙设备
+            createBLEConnection(item) {
+                console.log(item)
+                let _this = this
+                HBraclete.createBLEConnection({
+                    deviceId: item,
+                    success: function (r) {
+                        _this.myDeviceId = item
+                        _this.reqSynData()
+                        _this.onBLEConnectionStateChange()
+                        console.log("连接低功耗蓝牙设备 success", r)
+                    },
+                    fail: function (r) {
+                        uni.showToast({
+                            title: r.errMsg,
+                            icon: 'none',
+                            duration: 3000
+                        })
+                        console.log("连接低功耗蓝牙设备 fail", r)
+                    }
+                })
+            },
+            // 低功耗蓝牙连接状态的改变事件
+            onBLEConnectionStateChange() {
+                let _this = this
+                HBraclete.onBLEConnectionStateChange(function callback(r) {
+                    console.log('低功耗蓝牙连接状态的改变事件', r)
+                    if (r.connected === false) {
+                        _this.createBLEConnection()
+                    }
+                })
+            },
+            changeTime(chaneTime) {
+                let timeStr = ''
+                let time = parseInt(chaneTime)
+                let a = time / 60 >> 0
+                if (a === 0) {
+                    timeStr += '00:'
+                } else if (a > 0 && a < 10) {
+                    timeStr += "0" + a + ":"
+                } else if (a >= 10) {
+                    timeStr += a + ":"
+                }
+                let t = time % 60
+                if (t === 0) {
+                    timeStr += "00"
+                } else if (t > 0 && t < 10) {
+                    timeStr += "0" + t
+                } else if (t >= 10) {
+                    timeStr += t
+                }
+                return timeStr
+            },
+            sortByKey(array, key) {  //(数组、排序的列)
+                return array.sort(function (a, b) {
+                    let x = a[key]
+                    let y = b[key]
+                    return ((x > y) ? -1 : ((x < y) ? 1 : 0));
+                });
+            }
+        }
+    }
+</script>
+
+<style scoped>
+    .gd {
+        position: fixed;
+        z-index: 3;
+        bottom: 5px;
+        width: 100%;
+    }
+</style>

+ 76 - 0
src/pages/index/drug.vue

@@ -0,0 +1,76 @@
+<template>
+    <view>
+        <view class="cu-list menu bg-white">
+            <view class="cu-item arrow solid-bottom" @click="go('drug-details')">
+                <text>
+                    <text class="cuIcon-edit text-red margin-right" style="font-size: 50upx;"></text>
+                    <text>用药档案</text>
+                </text>
+            </view>
+            <view class="cu-item arrow solid-bottom"  @click="go">
+                <text>
+                    <text class="cuIcon-rank text-orange margin-right" style="font-size: 50upx;"></text>
+                    <text>统计</text>
+                </text>
+            </view>
+        </view>
+        <view class="padding">
+            <view class="grid margin-bottom col-2 bg-white padding">
+                <view v-for="(item, index) in list" :key="index" class="myborder padding">
+                    <view class="text-lg">
+                        <text class="text-blue margin-right-xs"></text> {{item.named}}
+                    </view>
+                    <view class="text-gray text-lg">
+                        <view v-if="item.punch_clock_time">
+                            <text class="cuIcon-check bg-blue round"></text>
+                            <text class="margin-right-sm">{{item.punch_clock_time}}已打卡</text>
+                        </view>
+                        <view v-else>未打卡</view>
+                    </view>
+                </view>
+            </view>
+        </view>
+        <view class="padding" style="height: 200px;">
+            <button style="width: 150px; height: 150px; border-radius: 50%" class="cu-btn bg-blue juzhong">
+                <span style="font-size: 36upx;">打卡</span>
+            </button>
+        </view>
+    </view>
+</template>
+
+<script>
+    export default {
+        data() {
+            return {
+                list: [
+                    { named: '早餐前09:00',  punch_clock_time: '09:01'},
+                    { named: '午餐前12:00',  punch_clock_time: '12:03'},
+                    { named: '晚餐前18:00',  punch_clock_time: ''},
+                    { named: '睡前22:00',  punch_clock_time: ''},
+                    { named: '一周一次22:00',  punch_clock_time: ''}
+                ]
+            }
+        },
+        methods: {
+            go() {
+                uni.navigateTo({
+                    url: './drug-chat'
+                })
+            }
+        }
+    }
+</script>
+
+<style>
+    .juzhong {
+        position: fixed;
+        left: 0;
+        right: 0;
+        margin: 0 auto;
+    }
+    .myborder {
+        border: 1px solid rgba(0, 0, 0, .1);
+        border-radius: inherit;
+        text-align: center;
+    }
+</style>

+ 15 - 0
src/pages/index/external_page.vue

@@ -0,0 +1,15 @@
+<template>
+    <view>
+        <web-view src="https://appdx.eddbrain.cn/img/userfiles/medicalHtml/medical-h5/index.html"></web-view>
+    </view>
+</template>
+
+<script>
+    export default {
+        name: "external_page"
+    }
+</script>
+
+<style scoped>
+
+</style>

+ 220 - 0
src/pages/index/heartRate.vue

@@ -0,0 +1,220 @@
+<template>
+    <view style="margin-bottom: 60px">
+        <view class="cu-bar bg-white solid-bottom margin-top">
+            <view class="action">
+                <text class="cuIcon-title text-orange"></text>
+                心率
+            </view>
+        </view>
+        <view class="cu-list menu">
+            <view class="cu-item" v-for="(item,index) in reverseData" :key="index">
+                <view class="content">
+                    <text class="text-grey">{{changeTime(item.heartTime)}}</text>
+                </view>
+                <view class="action">
+                    <view class="cu-tag round bg-blue light">{{item.heartVal}}</view>
+                    次/分钟
+                </view>
+            </view>
+        </view>
+        <button class="cu-btn block bg-gradual-green margin-tb-sm lg shadow-blur gd" @click="reqSynData">
+            <text class="text-Abc cuIconfont-spin" :class="isChange ? 'cuIcon-loading2' : ''"></text>
+            检查心率
+        </button>
+    </view>
+
+</template>
+
+<script>
+    import {HBraclete} from '@/utils/bluetest.js'
+
+    export default {
+        name: "heartRate",
+        data() {
+            return {
+                isOpen: false,
+                myDeviceId: null,
+                heartRateList: [
+                    {'heartTime': 257, 'heartVal': 73},
+                    {'heartTime': 258, 'heartVal': 63},
+                    {'heartTime': 259, 'heartVal': 70},
+                    {'heartTime': 260, 'heartVal': 71},
+                    {'heartTime': 261, 'heartVal': 67},
+                    {'heartTime': 1000, 'heartVal': 87},
+                    {'heartTime': 1010, 'heartVal': 87},
+                    {'heartTime': 1020, 'heartVal': 87},
+                    {'heartTime': 787, 'heartVal': 87},
+                    {'heartTime': 878, 'heartVal': 87},
+                    {'heartTime': 876, 'heartVal': 87},
+                    {'heartTime': 989, 'heartVal': 87},
+                    {'heartTime': 999, 'heartVal': 77},
+                    {'heartTime': 968, 'heartVal': 88},
+                    {'heartTime': 935, 'heartVal': 99},
+                    {'heartTime': 915, 'heartVal': 101},
+                    {'heartTime': 925, 'heartVal': 111},
+                    {'heartTime': 923, 'heartVal': 84},
+                    {'heartTime': 916, 'heartVal': 81},
+                    {'heartTime': 921, 'heartVal': 83},
+                    {'heartTime': 943, 'heartVal': 66},
+                    {'heartTime': 945, 'heartVal': 68},
+                    {'heartTime': 952, 'heartVal': 76}
+                ],
+                countTime: 0,
+                isChange:false
+            };
+        },
+        computed: {
+            reverseData() {
+                return this.sortByKey(this.heartRateList, 'heartTime')
+            }
+        },
+        mounted() {
+            // 加入根据member_id、deviceId(imei)、今天的时间查询心率数据
+            // 请求地址:/care/care_vital_signs_log/getLogsByDay
+            // const today = (new Date().setHours(0, 0, 0, 0)) / 1000
+            // const todayend = today + 86400
+            // const param = {
+            //     fixedCondition : 'log_time>=' + today + ' and log_time<=' + todayend + ' and member_id = ' + Storage.getItem('uid') + ' and imei = ' + Storage.getItem('deviceId') + ' and group_id = 7'
+            // }
+            this.myDeviceId = 'ED:D5:D9:7B:8E:A1'
+            this.countTime = this.heartRateList[this.heartRateList.length - 1].heartTime
+            console.log(this.countTime)
+        },
+        methods: {
+            //  请求同步数据(app主动查询内容)
+            reqSynData() {
+                if (!this.isChange) {
+                    this.isChange = true
+                    let _this = this
+                    HBraclete.synOper.reqSynData({
+                        data: [0, 0, 1, 0, 0, 0, 0, 0],
+                        deviceId: this.myDeviceId,
+                        success: function (r) {
+                            console.log("请求同步数据(app主动查询内容) success", r)
+                            _this.healthDataSwitch(1)
+                            _this.synHeartData()
+                            setTimeout(function () {
+                                _this.isChange = false
+                                // 加入批量保存心率功能this.heartRateList
+                                // 请求地址:/care/care_vital_signs_log
+                                // 参数: careVitalSignsLogList
+                                _this.healthDataSwitch(0)
+                            }, 60000)
+                        },
+                        fail: function (r) {
+                            _this.isChange = false
+                            console.log("请求同步数据(app主动查询内容) fail", r)
+                            if (r.errCode === 10006) {
+                                _this.createBLEConnection(_this.myDeviceId)
+                            }
+                        }
+                    })
+                }
+            },
+            // 同步心率数据
+            synHeartData() {
+                let _this = this
+                HBraclete.synOper.synHeartData(function callback(res) {
+                    console.log('同步心率数据', res)
+                    console.log(res.data[0].heartTime)
+                    if (res.data.length > 0) {
+                        let i = _this.heartRateList.length - 1
+                        if (_this.countTime === res.data[0].heartTime) {
+                            console.log('更新')
+                            _this.heartRateList[i].heartVal = res.data[0].heartVal
+                        } else if (res.data[0].heartTime > _this.countTime) {
+                            console.log('添加')
+                            _this.heartRateList.push(res.data[0])
+                            _this.sortByKey(_this.heartRateList, 'heartTime')
+                            _this.countTime = res.data[0].heartTime
+                        }
+
+                    }
+
+                })
+            },
+            // 健康数据开关
+            healthDataSwitch(type) {
+                HBraclete.synOper.healthDataSwitch({
+                    data: [[1, 0, 0], type],
+                    deviceId: this.myDeviceId,
+                    success: function (r) {
+                        console.log("健康数据开关 success", r)
+                    },
+                    fail: function (r) {
+                        console.log("健康数据开关 fail", r)
+                    }
+                })
+            },
+            // 连接低功耗蓝牙设备
+            createBLEConnection(item) {
+                console.log(item)
+                let _this = this
+                HBraclete.createBLEConnection({
+                    deviceId: item,
+                    success: function (r) {
+                        _this.myDeviceId = item
+                        _this.reqSynData()
+                        _this.onBLEConnectionStateChange()
+                        console.log("连接低功耗蓝牙设备 success", r)
+                    },
+                    fail: function (r) {
+                        uni.showToast({
+                            title: r.errMsg,
+                            icon: 'none',
+                            duration: 3000
+                        })
+                        console.log("连接低功耗蓝牙设备 fail", r)
+                    }
+                })
+            },
+            // 低功耗蓝牙连接状态的改变事件
+            onBLEConnectionStateChange() {
+                let _this = this
+                HBraclete.onBLEConnectionStateChange(function callback(r) {
+                    console.log('低功耗蓝牙连接状态的改变事件', r)
+                    if (r.connected === false) {
+                        _this.createBLEConnection()
+                    }
+                })
+            },
+            changeTime(chaneTime) {
+                let timeStr = ''
+                let time = parseInt(chaneTime)
+                let a = time / 60 >> 0
+                if (a === 0) {
+                    timeStr += '00:'
+                } else if (a > 0 && a < 10) {
+                    timeStr += "0" + a + ":"
+                } else if (a >= 10) {
+                    timeStr += a + ":"
+                }
+                let t = time % 60
+                if (t === 0) {
+                    timeStr += "00"
+                } else if (t > 0 && t < 10) {
+                    timeStr += "0" + t
+                } else if (t >= 10) {
+                    timeStr += t
+                }
+                return timeStr
+            },
+            sortByKey(array, key) {  //(数组、排序的列)
+                return array.sort(function (a, b) {
+                    let x = a[key]
+                    let y = b[key]
+                    return ((x > y) ? -1 : ((x < y) ? 1 : 0));
+                });
+            }
+        }
+    }
+</script>
+
+<style scoped>
+    .gd {
+        position: fixed;
+        z-index: 3;
+        bottom: 0;
+        width: 100%;
+    }
+</style>

+ 533 - 0
src/pages/index/home.vue

@@ -0,0 +1,533 @@
+<template name="components">
+    <view style="top:50px;position: relative;">
+        <scroll-view scroll-y class="page" v-if="isBanding">
+            <view class="nav-list">
+                <navigator hover-class='none' class="nav-li bg-orange" @tap="changePage(1)"
+                           :style="[{animation: 'show ' + ((1)*0.2+1) + 's 1'}]">
+                    <view class="nav-title">设备</view>
+                    <view class="nav-name">{{type}}</view>
+                    <text class="cuIcon-timefill"></text>
+                </navigator>
+                <navigator hover-class='none' class="nav-li bg-olive" @tap="changePage(2)"
+                           :style="[{animation: 'show ' + ((1+1)*0.2+1) + 's 1'}]">
+                    <view class="nav-title">血氧</view>
+                    <view class="nav-name">血氧1</view>
+                    <text class="cuIcon-album"></text>
+                </navigator>
+                <navigator hover-class='none' class="nav-li bg-red" @tap="changePage(3)"
+                           :style="[{animation: 'show ' + ((2+1)*0.2+1) + 's 1'}]">
+                    <view class="nav-title">心率</view>
+                    <view class="nav-name">心率1</view>
+                    <text class="cuIcon-like"></text>
+                </navigator>
+                <navigator hover-class='none' class="nav-li bg-purple" @tap="changePage(4)"
+                           :style="[{animation: 'show ' + ((3+1)*0.2+1) + 's 1'}]">
+                    <view class="nav-title">血压</view>
+                    <view class="nav-name">血压1</view>
+                    <text class="cuIcon-roundcheckfill"></text>
+                </navigator>
+            </view>
+            <view class="cu-tabbar-height"></view>
+        </scroll-view>
+
+        <view v-else>
+            <view class="cu-bar bg-white solid-bottom margin-top">
+                <view class="action">
+                    <text class="cuIcon-title text-orange"></text>
+                    蓝牙列表
+                </view>
+            </view>
+            <view class="cu-list menu-avatar">
+                <view class="cu-item arrow" v-for="(item,index) in devicesList" :key="index"
+                      @click="createBLEConnection(item.deviceId)">
+                    <view class="content">
+                        <text class="cuIcon-warn text-green"></text>
+                        <text class="text-grey" v-if="item.name===''">{{item.mac}}</text>
+                        <text class="text-grey" v-else>{{item.name}}</text>
+                    </view>
+                </view>
+                <view class="cu-item arrow" v-for="(item,index) in laList" :key="index"
+                      @click="createBLEConnection(item.deviceId)">
+                    <view class="content">
+                        <text class="cuIcon-warn text-green"></text>
+                        <text class="text-grey" v-if="item.name===''">{{item.mac}}</text>
+                        <text class="text-grey" v-else>{{item.name}}</text>
+                    </view>
+                </view>
+            </view>
+            <button class="cu-btn block bg-gradual-green margin-tb-sm lg shadow-blur gd" @click="cancel">
+                <text class="text-Abc cuIconfont-spin"></text>
+                取消
+            </button>
+        </view>
+        <view class="cu-load load-modal" v-if="loadModal">
+            <view class="cuIcon-emojifill text-orange"></view>
+            <view class="gray-text">{{loadMsg}}</view>
+        </view>
+    </view>
+</template>
+
+<script>
+    import {HBraclete} from '@/utils/bluetest.js'
+
+    export default {
+        data() {
+            return {
+                modalName: '',
+                isOpen: false,
+                loadModal: false,
+                loadMsg: '加载中...',
+                myDeviceId: null,
+                laList: [],
+                devicesList: [],
+                isBanding: true,
+                type: '未绑定',
+                yesTerDayTime: null,
+                logXyList: [],
+                logXlList: [],
+                countXyTime: 0,
+                countXlTime: 0
+            };
+        },
+        onLoad() {
+            // 加入根据member_id查询是否绑定用
+            // 请求地址: /care/care_device_member_bind/list
+            // 参数:
+            // const bindDeviceParams = {
+            //     fixedCondition: ' member_id = ' + Storage.getItem('uid')
+            // }
+            // this.myDeviceId = 'ED:D5:D9:7B:8E:A1'
+            this.openBluetoothAdapter()
+            let timeStamp = new Date(new Date().setHours(0, 0, 0, 0)) / 1000
+            //一天是86400秒   故n天前的时间戳为
+            this.yesTerDayTime = timeStamp - 86400
+            console.log('昨天时间戳为',this.yesTerDayTime)
+        },
+        methods: {
+            // 初始化蓝牙模块
+            openBluetoothAdapter() {
+                let _this = this
+                HBraclete.openBluetoothAdapter({
+                    success: function (r) {
+                        console.log("openBluetoothAdapter success", r)
+                        _this.isOpen = true
+                        if (_this.myDeviceId !== null) {
+                            _this.getConnectedBluetoothDevices()
+                        }
+                    },
+                    fail: function (r) {
+                        if (r.errCode === 10001) {
+                            uni.showToast({
+                                title: '蓝牙未开启,请先打开蓝牙',
+                                icon: 'none',
+                                duration: 3000
+                            })
+                            _this.isOpen = false
+                        } else {
+                            uni.showToast({
+                                title: '初始化蓝牙失败,错误码:' + (r.errCode || r.errMsg),
+                                icon: 'none',
+                                duration: 3000
+                            })
+                        }
+                        console.log("openBluetoothAdapter fail", r)
+                    }
+                })
+            },
+            // 获取处于已连接状态的设备
+            getConnectedBluetoothDevices() {
+                let _this = this
+                setTimeout(function () {
+                    if (_this.isOpen) {
+                        HBraclete.getConnectedBluetoothDevices({
+                            success: function (r) {
+                                if (r.devices.length > 0) {
+                                    if (_this.myDeviceId !== r.devices[0].deviceId) {
+                                        uni.showToast({
+                                            title: '您连接的设备不是您绑定的设置请重连',
+                                            icon: 'none',
+                                            duration: 3000
+                                        })
+                                    }
+                                    _this.isOpen = true
+                                    _this.isBanding = true
+                                    _this.type = '已绑定'
+                                } else {
+                                    _this.startBluetoothDevicesDiscovery()
+                                }
+                                console.log("获取处于已连接状态的设备 success", r)
+                            },
+                            fail: function (r) {
+                                console.log("未初始化蓝牙是配饰器 fail", r)
+                            }
+                        })
+                    }
+                }, 100);
+
+            },
+            // 开始搜寻附近的蓝牙外围设备
+            startBluetoothDevicesDiscovery() {
+                let _this = this
+                setTimeout(function () {
+                    console.log(_this.myDeviceId)
+                    if (_this.isOpen) {
+                        _this.loadModal = true
+                        _this.loadMsg = '加载中...'
+                        // 开始搜寻附近的蓝牙外围设备
+                        HBraclete.startBluetoothDevicesDiscovery({
+                            success: function (r) {
+                                console.log("开始搜寻附近的蓝牙外围设备 success", r)
+                                _this.loadModal = false
+                                // 找到新设备的事件
+                                HBraclete.onBluetoothDeviceFound(function callback(r) {
+                                    if (_this.myDeviceId !== null && _this.isBanding) {
+                                        if (r.devices[0].deviceId === _this.myDeviceId) {
+                                            _this.createBLEConnection(_this.myDeviceId)
+                                        }
+                                    } else {
+                                        if (r.devices[0].name !== '') {
+                                            _this.laList.push(r.devices[0])
+                                            console.log(r)
+                                        }
+                                    }
+
+                                })
+                            },
+                            fail: function (r) {
+                                _this.loadModal = false
+                                uni.showToast({
+                                    title: r.errMsg,
+                                    icon: 'none',
+                                    duration: 3000
+                                })
+                                console.log("未初始化蓝牙是配饰器 fail", r)
+                            }
+                        })
+                    } else {
+                        uni.showToast({
+                            title: '未初始化蓝牙是配饰器',
+                            icon: 'none',
+                            duration: 3000
+                        })
+                    }
+                }, 300);
+
+            },
+            // 停止搜寻附近的蓝牙外围设备
+            stopBluetoothDevicesDiscovery() {
+                HBraclete.stopBluetoothDevicesDiscovery({
+                    success: function (r) {
+                        console.log("停止搜寻附近的蓝牙外围设备 success", r)
+                    },
+                    fail: function (r) {
+                        console.log("停止搜寻附近的蓝牙外围设备 fail", r)
+                    }
+                })
+            },
+            // 连接低功耗蓝牙设备
+            createBLEConnection(deviceId) {
+                let _this = this
+                _this.loadMsg = '正在连接...'
+                _this.loadModal = true
+                _this.type = '绑定中'
+                HBraclete.createBLEConnection({
+                    deviceId: deviceId,
+                    success: function (r) {
+                        // 当myDeviceId为null时加入设备绑定用户操作
+                        // 请求地址:/care/care_device_member_bind/add
+                        // 参数:
+                        // const params = {
+                        //     imei: deviceId,
+                        //     deviceSkuId:设备id 3,
+                        //     memberId: Storage.getItem('uid')
+                        // }
+                        // Storage.setItem('deviceId',deviceId)
+                        _this.isOpen = true
+                        _this.isBanding = true
+                        _this.myDeviceId = deviceId
+                        _this.type = '已绑定'
+                        _this.synRealData(1)
+                        _this.onBLEConnectionStateChange()
+                        console.log("连接低功耗蓝牙设备 success", r)
+                    },
+                    fail: function (r) {
+                        uni.showToast({
+                            title: '连接失败',
+                            icon: 'none',
+                            duration: 3000
+                        })
+                        console.log("连接低功耗蓝牙设备 fail", r)
+                    },
+                    complete(res) {
+                        _this.loadModal = false
+                        _this.stopBluetoothDevicesDiscovery()
+                    }
+                })
+            },
+            // 断开与低功耗蓝牙设备的连接
+            closeBLEConnection(deviceId) {
+                let _this = this
+                HBraclete.closeBLEConnection({
+                    deviceId: deviceId,
+                    success: function (r) {
+                        // 加入取消设备绑定用户操作
+                        // 请求地址:/care/care_device_member_bind?设备用户绑定Id
+                        _this.myDeviceId = null
+                        _this.type = '未绑定'
+                        _this.getBluetoothDevices()
+                        _this.startBluetoothDevicesDiscovery()
+                        console.log("断开与低功耗蓝牙设备的连接 success", r)
+                    },
+                    fail: function (r) {
+                        _this.isBanding = true
+                        console.log("断开与低功耗蓝牙设备的连接 fail", r)
+                    }
+                })
+            },
+            // 低功耗蓝牙连接状态的改变事件
+            onBLEConnectionStateChange() {
+                let _this = this
+                HBraclete.onBLEConnectionStateChange(function callback(r) {
+                    console.log('低功耗蓝牙连接状态的改变事件', r)
+                    if (r.connected === false && _this.isBanding) {
+                        _this.isOpen = false
+                        _this.createBLEConnection(_this.myDeviceId)
+                    }
+                })
+            },
+            // 已发现的蓝牙设备
+            getBluetoothDevices() {
+                let _this = this
+                HBraclete.getBluetoothDevices({
+                    success: function (r) {
+                        _this.devicesList = []
+                        if (r.devices.length > 0) {
+                            r.devices.forEach(function (value, key, iterable) {
+                                console.log(value.name)
+                                if (value.name !== '未知设备') {
+                                    _this.devicesList.push(value)
+                                }
+                            })
+                        }
+                        console.log("已发现的蓝牙设备 success", r)
+                    },
+                    fail: function (r) {
+                        console.log("已发现的蓝牙设备 fail", r)
+                    }
+                })
+            },
+            // 设置实时数据同步
+            synRealData(num) {
+                let data
+                if(num === 1) {
+                    data = [0xFF, [0, 0, 1, 1, 0, 0]]
+                } else {
+                    data = [0x00]
+                }
+                let _this = this
+                HBraclete.synOper.synRealData({
+                    data: data,
+                    deviceId: _this.myDeviceId,
+                    success: function (r) {
+                        console.log("设置实时数据同步 success", r)
+                        if (num === 1) {
+                            _this.synHeartData()
+                            _this.synBloodPressureData()
+                            setTimeout(function () {
+                                _this.saveList(1)
+                            }, 4000)
+                            setTimeout(function () {
+                                _this.saveList(2)
+                            }, 5000)
+                            setTimeout(function () {
+                                _this.synRealData(2)
+                            }, 10000)
+                        }
+                    },
+                    fail: function (r) {
+                        console.log("设置实时数据同步 fail", r)
+                    }
+                })
+            },
+            // 同步心率数据
+            synHeartData() {
+                let _this = this
+                HBraclete.synOper.synHeartData(function callback(res) {
+                    console.log('同步心率数据', res)
+                    let data = {}
+                    if (res.data.length > 0) {
+                        let tt = _this.changeTime(res.data[0].heartTime)
+                        let yesTime = _this.getUnixTime( res.date + ' ' + tt + ':00')
+                        console.log(yesTime, _this.yesTerDayTime)
+                        if (yesTime > _this.yesTerDayTime) {
+                            res.data.forEach(item=>{
+                                if (_this.countXlTime === item.heartTime) {
+                                    console.log('更新')
+                                    _this.logXlList[i].vs_value = item.heartVal
+                                } else if (item.heartTime > _this.countXlTime) {
+                                    console.log('添加')
+                                    _this.countXlTime = item.heartTime
+                                    let t = _this.changeTime(item.heartTime)
+                                    let time = _this.getUnixTime( res.date + ' ' + t + ':00')
+                                    data = {
+                                        group_id: 8,
+                                        param_id: 20,
+                                        vs_value: item.heartVal,
+                                        log_time: time,
+                                    }
+                                    _this.logXlList.push(data)
+                                }
+                            })
+                        }
+                    }
+                })
+            },
+            // 同步血压数据
+            synBloodPressureData() {
+                let _this = this
+                HBraclete.synOper.synBloodPressureData(function callback(res) {
+                    console.log('同步血压数据', res)
+                    let data = {}
+                    if (res.data.length > 0) {
+                        let tt = _this.changeTime(res.data[0].time)
+                        let yesTime = _this.getUnixTime( res.date + ' ' + tt + ':00')
+                        console.log(yesTime, _this.yesTerDayTime)
+                        if (yesTime > _this.yesTerDayTime) {
+                            res.data.forEach(item=>{
+                                if (item.time > _this.countXyTime) {
+                                    _this.countXyTime = item.time
+                                    let t = _this.changeTime(item.time)
+                                    let time = _this.getUnixTime( res.date + ' ' + t + ':00')
+                                    data = {
+                                        group_id: 2,
+                                        param_id: 15,
+                                        vs_value: item.minVal,
+                                        log_time: time,
+                                    }
+                                    _this.logXyList.push(data)
+                                    data = {}
+                                    data = {
+                                        group_id: 2,
+                                        param_id: 16,
+                                        vs_value: item.maxVal,
+                                        log_time: time,
+                                    }
+                                    _this.logXyList.push(data)
+                                }
+                            })
+                        }
+                    }
+                })
+            },
+            // 切换页面
+            changePage(page) {
+                if (this.isOpen) {
+                    if (page === 1) {
+                        if (this.myDeviceId === null) {
+                            this.isBanding = false
+                            this.startBluetoothDevicesDiscovery()
+                            this.getBluetoothDevices()
+                        } else {
+                            console.log("设备已绑定,是否解除绑定")
+                            this.isBanding = false
+                            this.closeBLEConnection(this.myDeviceId)
+                        }
+
+                    } else if (page === 2) {
+                        uni.showToast({
+                            title: '该功能正在拼命开发中...',
+                            icon: 'none',
+                            duration: 3000
+                        })
+                    } else if (page === 3) {
+                        uni.navigateTo({
+                            url: '/pages/index/heartRate'
+                        })
+                    } else if (page === 4) {
+                        uni.navigateTo({
+                            url: '/pages/index/bloodPressure'
+                        })
+                    }
+
+                } else {
+                    uni.showToast({
+                        title: '你还未绑定设备',
+                        icon: 'none',
+                        duration: 3000
+                    })
+                }
+
+            },
+            // 批量保存
+            saveList(num) {
+                let _this = this
+                let data
+                if (this.logXlList.length > 0 || this.logXyList.length > 0) {
+                    if (num === 1) {
+                        console.log('this.logXlList===',this.logXlList)
+                        data = JSON.parse(JSON.stringify(this.logXlList))
+                    } else {
+                        console.log('this.logXyList===',this.logXyList)
+                        data = JSON.parse(JSON.stringify(this.logXyList))
+                    }
+                    if (data.length > 0) {
+                        if (num === 1) {
+                            console.log('心率进来了》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》')
+                            this.countXlTime = 0
+                            this.logXlList = []
+                        } else {
+                            console.log('血压进来了》》》》》》》》》》》》》》》》》》》》》》')
+                            this.countXyTime = 0
+                            this.logXyList = []
+                        }
+                    }
+                }
+            },
+            cancel() {
+                this.isBanding = true
+                this.stopBluetoothDevicesDiscovery()
+            },
+            changeTime(chaneTime) {
+                let timeStr = ''
+                let time = parseInt(chaneTime)
+                let a = time / 60 >> 0
+                if (a === 0) {
+                    timeStr += '00:'
+                } else if (a > 0 && a < 10) {
+                    timeStr += "0" + a + ":"
+                } else if (a >= 10) {
+                    timeStr += a + ":"
+                }
+                let t = time % 60
+                if (t === 0) {
+                    timeStr += "00"
+                } else if (t > 0 && t < 10) {
+                    timeStr += "0" + t
+                } else if (t >= 10) {
+                    timeStr += t
+                }
+                return timeStr
+            },
+            // 讲日期格式转化为时间戳 2019-06-17 23:11:54 》》1560784314
+            getUnixTime(dateStr) {
+                let newstr = dateStr.replace(/-/g, '/');
+                let date = new Date(newstr);
+                let time_str = date.getTime().toString();
+                return time_str.substr(0, 10);
+            },
+        }
+    }
+</script>
+
+<style>
+    .page {
+        height: 100vh;
+    }
+
+    .gd {
+        position: fixed;
+        z-index: 3;
+        bottom: 5px;
+        width: 100%;
+    }
+</style>

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

@@ -0,0 +1,254 @@
+<template>
+    <view>
+        <view class="cu-bar bg-white solid-bottom margin-top">
+            <view class="action">
+                <text class="cuIcon-title text-orange"></text>
+                功能列表
+            </view>
+        </view>
+        <button @click="getoNext('aike')">艾科血糖仪</button>
+        <button @click="getoNext('test')">测试</button>
+        <button @click="getoNext('test1')">网关配网</button>
+        <button @click="getoNext('home')">设置</button>
+        <button @click="getoNext('external_page')">外部页面</button>
+        <button @click="getoNext('wifi_pages')">wifi页面</button>
+        <button @click="getoNext('drug')">打卡用药</button>
+        <button @click="getoNext('myCode')">二维码</button>
+        <button @click="startBluetoothDevicesDiscovery">开始搜寻附近的蓝牙外围设备</button>
+        <button @click="bindDevice">绑定用户</button>
+        <button @click="unbindDevice">解绑用户</button>
+        <button @click="qryReminder">查询久坐提醒设置</button>
+        <button @click="qryHeartMeasurement">查询心率全天检测</button>
+        <button @click="reqSynData">请求同步数据(app主动查询内容)</button>
+        <button @click="synRealData">设置实时数据同步</button>
+
+        <scroll-view scroll-x class="bg-white nav">
+            <view class="flex text-center">
+                <view class="cu-item" :class="0==TabCur?'text-orange cur':''" @tap="tabSelect" data-id="0">
+                    新蓝牙
+                </view>
+                <view class="cu-item" :class="1==TabCur?'text-orange cur':''" @tap="tabSelect" data-id="1">
+                    已发现蓝牙
+                </view>
+                <view class="cu-item" :class="2==TabCur?'text-orange cur':''" @tap="tabSelect" data-id="2">
+                    已连接蓝牙
+                </view>
+            </view>
+        </scroll-view>
+
+        <view class="cu-bar bg-white solid-bottom margin-top">
+            <view class="action">
+                <text class="cuIcon-title text-orange"></text>
+                蓝牙列表
+            </view>
+        </view>
+        <view class="cu-list menu" v-if="TabCur==0">
+            <view class="cu-item arrow" v-for="(item,index) in laList" :key="index" @click="createBLEConnection(item)">
+                <view class="content">
+                    <text class="cuIcon-warn text-green"></text>
+                    <text class="text-grey" v-if="item.name===''">{{item.mac}}</text>
+                    <text class="text-grey" v-else>{{item.name}}</text>
+                </view>
+            </view>
+        </view>
+
+        <view class="cu-list menu" v-if="TabCur==1">
+            <view class="cu-item arrow" v-for="(item,index) in devicesList" :key="index" @click="createBLEConnection(item)">
+                <view class="content">
+                    <text class="cuIcon-warn text-green"></text>
+                    <text class="text-grey" v-if="item.name===''">{{item.mac}}</text>
+                    <text class="text-grey" v-else>{{item.name}}</text>
+                </view>
+            </view>
+        </view>
+
+        <view class="cu-list menu" v-if="TabCur==2">
+            <view class="cu-item arrow" v-for="(item,index) in myDevicesList" :key="index">
+                <view class="content">
+                    <text class="cuIcon-warn text-green"></text>
+                    <text class="text-grey" v-if="item.name===''">{{item.mac}}</text>
+                    <text class="text-grey" v-else>{{item.name}}</text>
+                </view>
+            </view>
+        </view>
+    </view>
+</template>
+
+<script>
+    export default {
+        data() {
+            return {
+                title: 'Hello',
+                laList: [],
+                devicesList: [],
+                myDevicesList: [],
+                TabCur: 0,
+                myDeviceId: '',
+                platform: null,
+                language: null,
+                timer: null,
+                heartRateList: [
+                    // {id: 11, 'time': 1594034940, 'minVal': 70},
+                    // {id: 12, 'time': 1594034940, 'minVal': 70},
+                    // {id: 11, 'time': 1594034940, 'minVal': 102},
+                    // {id: 12, 'time': 1594034940, 'minVal': 102},
+                    {id: 13, 'time': 1594034140, 'minVal': 66},
+                    {id: 14, 'time': 1594034140, 'minVal': 105},
+                    {id: 13, 'time': 1594036553, 'minVal': 66},
+                    {id: 14, 'time': 1594036553, 'minVal': 105},
+                    {id: 13, 'time': 1594034340, 'minVal': 73},
+                    {id: 14, 'time': 1594034340, 'minVal': 135}
+                ],
+            }
+        },
+        onLoad() {
+            const str = encodeURI('p0宁'), a = this.strToHexCharCode(str), b = this.hexToBuffer(a)
+            // console.log(a)
+            // console.log(b)
+            // console.log(this.ab2hex2(b))
+            // console.log('1111=',this.utf8to16(unescape("ning12121%E5%AE%81%E7%81%AC")));
+            // console.log('4444=',encodeURI("ning12121宁灬"));
+            // console.log('5555555=',encodeURIComponent("宁灬"));
+
+            let date = new Date(1623902400 * 1000)
+            console.log(this.unixToDate(date/1000, 'hh')+':00')
+            let min= date.getMinutes()
+            console.log(min)
+            date.setMinutes(min + 30)
+            console.log(date / 1000)
+            console.log(this.unixToDate(date/1000))
+            date = new Date(1623902400 * 1000)
+            date.setMinutes(min - 30)
+            console.log(this.unixToDate(date/1000))
+        },
+        methods: {
+            utf8to16(str) {
+                var out, i, len, c;
+                var char2, char3;
+                out = "";
+                len = str.length;
+                i = 0;
+                while(i < len) {
+                    c = str.charCodeAt(i++);
+                    switch(c >> 4)
+                    {
+                        case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
+                        out += str.charAt(i-1);
+                        break;
+                        case 12: case 13:
+                        char2 = str.charCodeAt(i++);
+                        out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
+                        break;
+                        case 14:
+                            char2 = str.charCodeAt(i++);
+                            char3 = str.charCodeAt(i++);
+                            out += String.fromCharCode(((c & 0x0F) << 12) |
+                                ((char2 & 0x3F) << 6) |
+                                ((char3 & 0x3F) << 0));
+                            break;
+                    }
+                }
+
+                return out;
+            },
+            strToHexCharCode(r) {
+                if ("" === r) return "";
+                let t = []
+                for (let e = 0; e < r.length; e++) t.push(r.charCodeAt(e).toString(16));
+                return t.join("")
+            },
+            hexToBuffer(r) {
+                let t = r.length / 2, e = new ArrayBuffer(t), n = new DataView(e)
+                for (let o = 0; o < t; o++) n.setUint8(o, "0X" + r.substring(2 * o, 2 * o + 2));
+                return e
+            },
+            ab2hex2(buffer) {
+                const hexArr = Array.prototype.map.call(
+                    new Uint8Array(buffer),
+                    function (bit) {
+                        return ('00' + bit.toString(16)).slice(-2)
+                    }
+                )
+                return hexArr.join('')
+            },
+            qige(str) {
+                return '20' + str.substr(4,2) + '-' + str.substr(0,2) + '-' + str.substr(2,2) + ' ' + str.substr(6,2) + ':' + str.substr(8,2)
+            },
+            formatDecimal(num, decimal) {
+                num = num.toString();
+                var index = num.indexOf('.');
+                if (index !== -1) {
+                    num = num.substring(0, decimal + index + 1)
+                } else {
+                    num = num.substring(0)
+                }
+                return parseFloat(num).toFixed(decimal)
+            },
+            unique(arr, field) {
+                let map = {}
+                let res = []
+                let len = arr.length
+                for (let i = 0; i < len; i++) {
+                    if ((i + 1) < len) {
+                        if (!map[arr[i][field]]) {
+                            console.log(map[arr[i][field]])
+                            map[arr[i][field]] = 1;
+                            if (arr[i][field] === arr[i + 1][field]) {
+                                arr[i].maxVal = arr[i + 1].minVal
+                                res.push(arr[i])
+                            }
+                        }
+                    }
+                }
+                return res
+            },
+            getUnixTime(dateStr) {
+                var newstr = dateStr.replace(/-/g, '/');
+                var date = new Date(newstr);
+                var time_str = date.getTime().toString();
+                return time_str.substr(0, 10);
+            },
+            tabSelect(e) {
+                uni.showToast({
+                    title: '你好吗',
+                    icon: 'none'
+                })
+                this.TabCur = e.currentTarget.dataset.id;
+                if (this.TabCur == 1) {
+                    this.getBluetoothDevices()
+                } else if (this.TabCur == 2) {
+                    this.getConnectedBluetoothDevices()
+                }
+            },
+            getoNext(path) {
+                uni.navigateTo({
+                    url: '/pages/index/'+path
+                })
+            },
+            unixToDate(unix, format) {
+                if (!unix) return unix
+                let _format = format || 'yyyy-MM-dd hh:mm:ss'
+                const d = new Date(unix * 1000)
+                const o = {
+                    'M+': d.getMonth() + 1,
+                    'd+': d.getDate(),
+                    'h+': d.getHours(),
+                    'm+': d.getMinutes(),
+                    's+': d.getSeconds(),
+                    'q+': Math.floor((d.getMonth() + 3) / 3),
+                    S: d.getMilliseconds()
+                }
+                if (/(y+)/.test(_format)) _format = _format.replace(RegExp.$1, (d.getFullYear() + '').substr(4 - RegExp.$1.length))
+                for (const k in o) if (new RegExp('(' + k + ')').test(_format)) _format = _format.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length)))
+                return _format
+            }
+        }
+    }
+</script>
+
+<style>
+    .page {
+        height: 100Vh;
+        width: 100vw;
+    }
+</style>

+ 188 - 0
src/pages/index/myCode.vue

@@ -0,0 +1,188 @@
+<template>
+  <view>
+    <view class="background"></view>
+    <view class="container-page">
+      <view class="header" @click="getoNext('QsCode')">{{header}}</view>
+      <view class="qrcode-view">
+        <canvas canvas-id="qrcode" id="qrcode" style="width:460rpx;height: 460rpx" />
+      </view>
+      <view class="bottom-name" @click="baocun">{{Name}}</view>
+    </view>
+  </view>
+</template>
+<script>
+import CODE from '@uni-plugs/uni-code';
+export default {
+  data() {
+    return {
+      header: "微信扫一扫二维码,绑定您的专属医生",
+      background:{
+        backgroundImage: 'linear-gradient(to left, #8E54E9, #4776E6)',
+      },
+      Name: '张医生',
+      codeConfig:{
+        code: 'https://qm.qq.com/cgi-bin/qm/qr?k=LKqML292dD2WvwQfAJXBUmvgbiB_TZWF&noverify=0',
+        level: 4, //纠错等级
+        border:{
+          color: ['#ec008c','#fc6767'], //边框颜色支持渐变色
+          lineWidth: 6, //边框宽度
+        },
+        color: ['#11998e','#38ef7d'],
+        bgColor: "#FFFFFF",
+        size: 460,
+        img: '',
+        result: ''
+      },
+    }
+  },
+  onReady() {
+    this.getTx()
+  },
+  methods: {
+    async findCan(obj) {
+      const _this = this
+      if (Object.keys(this.codeConfig).length) {
+        CODE.QRCode(obj);
+        const res = await CODE.SaveImg(obj)
+        console.log('res=', res)
+        _this.result = res.tempFilePath
+      }
+    },
+    generateCode () {
+      this.changeCanvas();
+    },
+    changeCanvas () {
+      const obj = {...this.codeConfig,code: this.codeConfig.code ,id: 'qrcode', size: this.codeConfig.size, img: this.codeConfig.img};
+      console.log(obj.img)
+      this.findCan(obj);
+    },
+    getTx() {
+      let that = this;
+      // 直接设置 options.image 的值,在手机上logo会绘制失败
+      new Promise((resolve, reject) => {
+        // 绘制网络地址的logo时需要先使用 wx.getImageInfo 获取到图片信息
+        // 注意网络图片需先配置download域名 wx.getImageInfo 才能生效。
+        wx.getImageInfo({
+          src: 'http://wdklmall.oss-cn-shenzhen.aliyuncs.com/mallgoods/normal/0B1CD5C4488447D6BF94F1DCDCB070F9.png',
+          success: (res) => {
+            resolve(res.path);
+          },
+          fail: (e) => {
+            // 获取logo失败也不应该影响正确生成二维码图片
+            // 这里可以给出失败提示
+            resolve();
+          }
+        })
+      }).then((path) => {
+        that.codeConfig.img = path
+        setTimeout(() => {
+          that.changeCanvas()
+        }, 200);
+      })
+    },
+    baocun() {
+      this.findCan()
+      const _this = this
+      console.log('result=', this.result)
+      wx.getSetting({
+        success(res) {
+          console.log('setting', res)
+          if (!res.authSetting['scope.writePhotosAlbum']) {
+            wx.openSetting({
+              success(res){
+                _this.saveImageToPhotosAlbum()
+              }
+            })
+          }else{
+            _this.saveImageToPhotosAlbum()
+          }
+        }
+      })
+    },
+    saveImageToPhotosAlbum() {
+      const _this = this
+      wx.saveImageToPhotosAlbum({
+        filePath: _this.result,
+        success: function () {
+          uni.showToast({
+            title: '二维码保存成功',
+            icon: 'success',
+            duration: 2000
+          });
+        },
+        fail: function (res) {
+          console.log('555555555', res)
+        }
+      });
+    },
+    getoNext(path) {
+      uni.navigateTo({
+        url: '/pages/index/'+path
+      })
+    }
+  }
+}
+</script>
+<style>
+page{
+  background: -webkit-linear-gradient(to left, #8E54E9, #4776E6);  /* Chrome 10-25, Safari 5.1-6 */
+  background: linear-gradient(to left, #8E54E9, #4776E6); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */
+}
+</style>
+<style lang="less" scoped>
+// #ifdef APP-PLUS
+.background{
+  width: 100%;
+  height: 100%;
+  position: absolute;
+  z-index: 0;
+  background: -webkit-linear-gradient(to left, #8E54E9, #4776E6);  /* Chrome 10-25, Safari 5.1-6 */
+  background: linear-gradient(to left, #8E54E9, #4776E6); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */
+}
+// #endif
+.container-page{
+  width: 90%;
+  z-index: 1;
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  // #ifdef APP-PLUS
+  bottom: 256rpx;
+  // #endif
+  margin: auto;
+  margin-top: 200rpx;
+  background-color: #fff;
+  border-radius: 10rpx;
+  margin-bottom: 50rpx;
+  // #ifdef MP-WEIXIN
+  // margin-bottom: 100rpx;
+  // #endif
+  .header {
+    text-align: center;
+    padding: 30rpx 0;
+    color: #1b82d2;
+    font-weight: bold;
+    letter-spacing: 2px;
+    background-color: #f0f0f0;
+    border-radius: 10rpx 10rpx 0 0;
+    margin-bottom: 30rpx;
+    overflow: hidden;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+  }
+  .qrcode-view{
+    display: flex;
+    justify-content: center;  /*水平方向居中*/
+    margin-top: 50rpx;
+  }
+  .bottom-name{
+    text-align: center;
+    margin-top: 50rpx;
+    letter-spacing: 2px;
+    font-weight: 400;
+    font-size: 35rpx;
+  }
+}
+</style>

+ 85 - 0
src/pages/index/test.vue

@@ -0,0 +1,85 @@
+<template>
+    <view>
+        <view class="cu-bar bg-white solid-bottom margin-top">
+            <view class="action">
+                <text class="cuIcon-title text-orange"></text>
+                设备列表
+            </view>
+        </view>
+        <view class="cu-list menu">
+            <view class="cu-item">
+                <view class="content padding-tb-sm">
+                    <view>
+                        <text class="text-black margin-right-xs text-xl">血氧仪</text>
+                    </view>
+                    <view class="text-gray text-sm">
+                        默认自己使用
+                    </view>
+                </view>
+                <view class="action">
+                    <button class="cu-btn bg-green shadow">绑定</button>
+                </view>
+            </view>
+            <view class="cu-item">
+                <view class="content padding-tb-sm">
+                    <view>
+                        <text class="text-black margin-right-xs text-xl">血糖仪</text>
+                    </view>
+                    <view class="text-gray text-sm">
+                        默认自己使用
+                    </view>
+                </view>
+                <view class="action">
+                    <button class="cu-btn bg-green shadow">绑定</button>
+                </view>
+            </view>
+            <view class="cu-item">
+                <view class="content padding-tb-sm">
+                    <view>
+                        <text class="text-black margin-right-xs text-xl">血压计</text>
+                    </view>
+                    <view class="text-gray text-sm">
+                        <text class="text-green text-lg">绑定者:</text>张三
+                    </view>
+                </view>
+                <view class="action">
+                    <button class="cu-btn bg-green shadow">绑定</button>
+                </view>
+            </view>
+            <view class="cu-item">
+                <view class="content padding-tb-sm">
+                    <view>
+                        <text class="text-black margin-right-xs text-xl">体温枪</text>
+                    </view>
+                    <view class="text-gray text-sm">
+                        <text class="text-green text-lg">绑定者:</text>李四
+                    </view>
+                </view>
+                <view class="action">
+                    <button class="cu-btn bg-green shadow">绑定</button>
+                </view>
+            </view>
+
+        </view>
+    </view>
+</template>
+
+<script>
+    export default {
+        name: "test",
+        data() {
+            return {
+                laList: [],
+            }
+        },
+        onLoad() {
+        },
+        methods: {
+
+        }
+    }
+</script>
+
+<style scoped>
+
+</style>

+ 514 - 0
src/pages/index/test1.vue

@@ -0,0 +1,514 @@
+<template>
+    <view class="bg-white padding">
+        <view class="cu-bar bg-white solid-bottom margin-top">
+            <view class="action">
+                <text class="cuIcon-title text-orange"></text>
+                蓝牙列表
+            </view>
+        </view>
+        <view class="cu-list menu">
+            <view class="cu-item arrow" v-for="(item,index) in devicesList" :key="index"
+                  @click="createBLEConnection(item)">
+                <view class="content">
+                    <text class="cuIcon-btn text-green"></text>
+                    <text class="text-grey">{{item.name}}</text>
+                </view>
+            </view>
+            <view class="cu-item arrow" v-for="(item,index) in laList" :key="index"
+                  @click="createBLEConnection(item)">
+                <view class="content">
+                    <text class="cuIcon-btn text-green"></text>
+                    <text class="text-grey">{{item.name}}</text>
+                </view>
+            </view>
+        </view>
+        <view v-if="formShow" class="cu-modal" :class="formShow?'show':''">
+            <view class="cu-dialog">
+                <view class="cu-bar bg-white justify-end">
+                    <view class="content">连接WiFi</view>
+                    <view class="action" @tap="closeBLEConnection">
+                        <text class="cuIcon-close text-red"></text>
+                    </view>
+                </view>
+                <view class="">
+                    <form>
+                        <view class="cu-form-group margin-top">
+                            <view class="title">WiFi名称:</view>
+                            <picker @change="PickerChange" :value="index" :range="wifiList">
+                                <view v-if="wifiList.length > 0" class="picker" style="text-align: center;">
+                                    {{wifiList[index]}}
+                                </view>
+                            </picker>
+                        </view>
+                        <view class="cu-form-group margin-top">
+                            <view class="title">WiFi密码:</view>
+                            <input v-model="wifiPass" placeholder="输入WiFi密码" />
+                        </view>
+                    </form>
+                </view>
+                <view class="cu-bar bg-white justify-end">
+                    <view class="action">
+                        <button class="cu-btn line-green text-green" @tap="closeBLEConnection">取消</button>
+                        <button class="cu-btn bg-green margin-left" @tap="submitModel">确定</button>
+                    </view>
+                </view>
+            </view>
+        </view>
+
+        <view class="cu-load load-modal" v-if="loadModal">
+            <view class="cuIcon-emojifill text-orange"></view>
+            <view class="gray-text">{{loadMsg}}</view>
+        </view>
+    </view>
+
+</template>
+
+<script>
+    const plugin = requirePlugin('btutil-plugin')
+
+    export default {
+        name: "test1",
+        data() {
+            return {
+                laList: [],
+                devicesList: [],
+                platform: '',
+                isOpen: false,
+                loadModal: true,
+                loadMsg: '加载中...',
+                formShow: false,
+                wifiName: '',
+                wifiPass: '',
+                myDeviceId: null,
+                serviceId: null,
+                characteristicId: null,
+                index: 0,
+                wifiList: []
+            }
+        },
+        onLoad() {
+            // 正式环境建议关闭log,开发环境打开log方便调试
+            // plugin.setLogDebug(true) // 是否打印log
+            // plugin.setBluetoothNamePrefix(['YK', 'wu', '170'])
+            // this.searchDevices()
+            const _this = this
+            uni.getSystemInfo({
+                success(res) {
+                    _this.platform = res.platform
+                    _this.openBluetoothAdapter()
+                    if (_this.platform === 'android') {
+                        setTimeout(function () {
+                            _this.authorize()
+                        }, 700);
+                    } else {
+                        setTimeout(function () {
+                            _this.startWifi()
+                        }, 700);
+                    }
+                }
+            })
+        },
+        onUnload() {
+            // 关闭页面的时候停止搜索外围蓝牙
+            this.stopBluetoothDevicesDiscovery()
+            this.closeBluetoothAdapter()
+        },
+        methods: {
+            // 获取用户授权设置
+            authorize() {
+                const _this = this
+                wx.getSetting({
+                    success(res) {
+                        console.log('setting', res)
+                        if (!res.authSetting['scope.userLocation']) {
+                            // 申请获取地理位置权限
+                            wx.authorize({
+                                scope: 'scope.userLocation',
+                                success: (res) => {
+                                    console.log('authorize succ', res)
+                                    _this.startWifi()
+                                },
+                                fail: (e) => {
+                                    console.log('authorize fail', e)
+                                }
+                            })
+                        }else{
+                            // 开始wifi模块使用
+                            _this.startWifi()
+                        }
+                    }
+                })
+            },
+            /**
+             * 搜索蓝牙设备,并将搜索到的蓝牙设备设置到data,用于列表展示
+             */
+            searchDevices() {
+                const _this = this
+                plugin.searchDevices(res => {
+                    console.log('devices=', res)
+                    if (res.length > 0) {
+                        _this.loadModal = false
+                    }
+                    _this.devicesList = res
+                })
+            },
+            // 初始化蓝牙模块
+            openBluetoothAdapter() {
+                let _this = this
+                uni.openBluetoothAdapter({
+                    success: function (r) {
+                        console.log("openBluetoothAdapter success", r)
+                        _this.isOpen = true
+                        _this.startBluetoothDevicesDiscovery()
+                        if (_this.platform === 'android') {
+                            _this.getBluetoothDevices()
+                        }
+                    },
+                    fail: function (r) {
+                        _this.loadModal = false
+                        if (r.errCode === 10001) {
+                            uni.showToast({
+                                title: '蓝牙未开启,请先打开蓝牙',
+                                icon: 'none',
+                                duration: 3000
+                            })
+                            _this.isOpen = false
+                        } else {
+                            uni.showToast({
+                                title: '初始化蓝牙失败,错误码:' + (r.errCode || r.errMsg),
+                                icon: 'none',
+                                duration: 3000
+                            })
+                        }
+                        console.log("openBluetoothAdapter fail", r)
+                    }
+                })
+            },
+            // 开始搜寻附近的蓝牙外围设备
+            startBluetoothDevicesDiscovery() {
+                let _this = this
+                this.laList = []
+                setTimeout(function () {
+                    if (_this.isOpen) {
+                        _this.loadModal = true
+                        _this.loadMsg = '加载中...'
+                        // 开始搜寻附近的蓝牙外围设备
+                        uni.startBluetoothDevicesDiscovery({
+                            success: function (r) {
+                                console.log("开始搜寻附近的蓝牙外围设备 success", r)
+                                // 找到新设备的事件
+                                uni.onBluetoothDeviceFound(function callback(r) {
+                                    if (r.devices[0].name.indexOf("170") !== -1  && r.devices[0].deviceId !== '00:00:00:00:00:00') {
+                                        _this.loadModal = false
+                                        if (_this.laList.length > 0) {
+                                            let isCF = 0
+                                            _this.laList.forEach(item => {
+                                                if (item.deviceId === r.devices[0].deviceId) {
+                                                    isCF = 1
+                                                }
+                                            })
+                                            if (isCF === 0) {
+                                                console.log(r.devices[0].name, r)
+                                                _this.laList.push(r.devices[0])
+                                            }
+                                        } else {
+                                            _this.laList.push(r.devices[0])
+                                        }
+                                    }
+                                })
+                            },
+                            fail: function (r) {
+                                _this.loadModal = false
+                                uni.showToast({
+                                    title: r.errMsg,
+                                    icon: 'none',
+                                    duration: 3000
+                                })
+                                console.log("未初始化蓝牙配饰器 fail", r)
+                            }
+                        })
+                    } else {
+                        uni.showToast({
+                            title: '未初始化蓝牙配饰器',
+                            icon: 'none',
+                            duration: 3000
+                        })
+                    }
+                }, 200);
+
+            },
+            // 停止搜寻附近的蓝牙外围设备
+            stopBluetoothDevicesDiscovery() {
+                uni.stopBluetoothDevicesDiscovery({
+                    success: function (r) {
+                        console.log("停止搜寻附近的蓝牙外围设备 success", r)
+                    },
+                    fail: function (r) {
+                        console.log("停止搜寻附近的蓝牙外围设备 fail", r)
+                    }
+                })
+            },
+            closeBluetoothAdapter() {
+                uni.closeBluetoothAdapter({
+                    success(res) {
+                        console.log(res)
+                    }
+                })
+            },
+            // 连接低功耗蓝牙设备
+            createBLEConnection(item) {
+                if (this.loadModal) {
+                    return
+                }
+                const { deviceId, name } = item
+                let _this = this
+                _this.loadMsg = '正在连接...'
+                _this.loadModal = true
+                uni.createBLEConnection({
+                    deviceId: deviceId,
+                    success: function (r) {
+                        console.log("连接低功耗蓝牙设备 success", r)
+                        _this.loadModal = false
+                        _this.formShow = true
+                        setTimeout(function () {
+                            _this.getBLEDeviceServices();
+                        }, 300);
+                        _this.isOpen = true
+                        _this.myDeviceId = deviceId
+                        _this.onBLEConnectionStateChange()
+                    },
+                    fail: function (r) {
+                        if (r.errCode === -1) {
+                            uni.showToast({
+                                title: '蓝牙已连接,请重启蓝牙重连',
+                                icon: 'none',
+                                duration: 3000
+                            })
+                        } else {
+                            uni.showToast({
+                                title: '连接失败,请重试',
+                                icon: 'none',
+                                duration: 3000
+                            })
+                        }
+                        _this.loadModal = false
+                        console.log("连接低功耗蓝牙设备 fail", r)
+                    },
+                    complete(res) {
+                        _this.stopBluetoothDevicesDiscovery()
+                    }
+                })
+            },
+            // 断开与低功耗蓝牙设备的连接
+            closeBLEConnection() {
+                let _this = this
+                uni.closeBLEConnection({
+                    deviceId: _this.myDeviceId,
+                    success: function (r) {
+                        console.log("断开与低功耗蓝牙设备的连接 success", r)
+                    },
+                    fail: function (r) {
+                        console.log("断开与低功耗蓝牙设备的连接 fail", r)
+                    },
+                    complete() {
+                        _this.formShow = false
+                    }
+                })
+            },
+            // 低功耗蓝牙连接状态的改变事件
+            onBLEConnectionStateChange() {
+                let _this = this
+                uni.onBLEConnectionStateChange(function callback(r) {
+                    console.log('低功耗蓝牙连接状态的改变事件', r)
+                    if (!r.connected) {
+                        _this.startBluetoothDevicesDiscovery()
+                        if (_this.platform === 'android') {
+                            _this.getBluetoothDevices()
+                        }
+                        setTimeout(function () {
+                            _this.loadModal = false;
+                        }, 2000);
+                    }
+                })
+            },
+            // 已发现的蓝牙设备
+            getBluetoothDevices() {
+                let _this = this
+                uni.getBluetoothDevices({
+                    success: function (r) {
+                        _this.devicesList = []
+                        if (r.devices.length > 0) {
+                            let data = []
+                            _this.loadModal = false
+                            r.devices.forEach(value => {
+                                if (value.name.indexOf("170") !== -1 && value.deviceId !== '00:00:00:00:00:00') {
+                                    data.push({deviceId: value.deviceId, name: value.name})
+                                }
+                            })
+                            const data2 = []
+                            data.forEach(item => {
+                                if (data2.indexOf(item.deviceId) === -1) {
+                                    data2.push(item.deviceId)
+                                    _this.devicesList.push(item)
+                                }
+                            })
+                        }
+                        console.log("已发现的蓝牙设备 success", r)
+                    },
+                    fail: function (r) {
+                        console.log("已发现的蓝牙设备 fail", r)
+                    }
+                })
+            },
+            // 获取设备的服务ID
+            getBLEDeviceServices() {
+                let _this = this
+                uni.getBLEDeviceServices({
+                    // 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
+                    deviceId: this.myDeviceId,
+                    success(res) {
+                        console.log('获取设备的服务ID success:', res.services)
+                        for (let r = function (r) {
+                            if (res.services[r].isPrimary) {
+                                console.log('serviceId===', res.services[r].uuid)
+                                return uni.getBLEDeviceCharacteristics({
+                                    // 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
+                                    deviceId: _this.myDeviceId,
+                                    // 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取
+                                    serviceId: res.services[r].uuid,
+                                    success(s) {
+                                        console.log('获取指定服务的特征值 success:', s.characteristics);
+                                        for (let j = 0; j < s.characteristics.length; j++) {
+                                            if (s.characteristics[j].properties.write) {
+                                                console.log('characteristicId===', s.characteristics[j].uuid)
+                                                _this.serviceId = res.services[r].uuid
+                                                _this.characteristicId = s.characteristics[j].uuid
+                                                break
+                                            }
+                                        }
+                                    },
+                                    fail(r) {
+                                        console.log('获取指定服务的特征值 failed:', r);
+                                    }
+                                }), 'break'
+                            }
+                        }, o = 0; o < res.services.length; o ++) {
+                            if ('break' === r(o)) break
+                        }
+                    },
+                    fail(res) {
+                        // _this.loadModal = false
+                        console.log('获取设备的服务 failed:' + res);
+                    }
+                })
+            },
+            submitModel() {
+                this.wifiName = this.wifiList[this.index]
+                if (this.wifiName === '') {
+                    wx.showToast({
+                        title: '请选择WiFi名称',
+                        icon: 'none',
+                        duration: 3000
+                    })
+                    return
+                }
+                if (this.wifiPass === '') {
+                    wx.showToast({
+                        title: '请输入WiFi名称密码',
+                        icon: 'none',
+                        duration: 3000
+                    })
+                    return
+                }
+                this.loadModal = true
+                this.loadMsg = '正在发送WiFi信息给网关,请稍后'
+                console.log(this.serviceId, this.characteristicId)
+                let o = this.hexToBuffer(this.strToHexCharCode("p0" + this.wifiPass)),
+                    a = {
+                        deviceId: this.myDeviceId,
+                        serviceId: this.serviceId,
+                        characteristicId: this.characteristicId
+                    // serviceId: '6E400001-B5A3-F393-E0A9-E50E24DCCA9E',
+                    // characteristicId: '6E400002-B5A3-F393-E0A9-E50E24DCCA9E'
+                }
+                a.value = this.hexToBuffer(this.strToHexCharCode("s0" + this.wifiName))
+                const _this = this
+                uni.writeBLECharacteristicValue(a).then(res =>{
+                    if (res.length > 1) {
+                        _this.loadMsg = '发送成功,请注意网关指示灯'
+                        setTimeout(function () {
+                            _this.loadModal = false
+                        }, 5000);
+                    } else {
+                        _this.loadModal = false
+                        wx.showToast({
+                            title: '发送失败,请重试或重新连接WiFi',
+                            icon: 'none',
+                            duration: 6000
+                        })
+                    }
+                    console.log('11111', res)
+                    a.value = o
+                    console.log(a)
+                    uni.writeBLECharacteristicValue(a).then(r => {
+                        console.log('22222', r)
+                    }).catch(e => {
+                        console.log('3333', e)
+                    })
+                }).catch(e =>{
+                    console.log('444444444', e)
+                })
+            },
+            strToHexCharCode(r) {
+                console.log('strToHexCharCode====', r)
+                if ("" === r) return "";
+                let t = []
+                for (let e = 0; e < r.length; e++) t.push(r.charCodeAt(e).toString(16));
+                return t.join("")
+            },
+            hexToBuffer(r) {
+                let t = r.length / 2, e = new ArrayBuffer(t), n = new DataView(e)
+                for (let o = 0; o < t; o++) n.setUint8(o, "0X" + r.substring(2 * o, 2 * o + 2));
+                return e
+            },
+            PickerChange(e) {
+                this.index = e.detail.value
+            },
+            onGetWifiList() {
+                const _this = this
+                wx.onGetWifiList(function(res) {
+                    console.log(res)
+                    res.wifiList.forEach(item => {
+                        if (item.SSID !== "" && _this.wifiList.indexOf(item.SSID) === -1) {
+                            _this.wifiList.push(item.SSID)
+                        }
+                    })
+                })
+                wx.getWifiList()
+            },
+            startWifi() {
+                const _this = this
+                wx.startWifi({
+                    success (res) {
+                        console.log('startWifi success=', res.errMsg)
+                        _this.onGetWifiList()
+                    },
+                    fail(res) {
+                        console.log('startWifi fail=', res.errMsg)
+                    }
+                })
+            },
+            stopWifi() {
+                wx.stopWifi({
+                    success (res) {
+                        console.log(res.errMsg)
+                    }
+                })
+            }
+        }
+    }
+</script>
+
+<style scoped>
+
+</style>

+ 87 - 0
src/pages/index/wifi_page.vue

@@ -0,0 +1,87 @@
+<template>
+    <view class="bg-white padding">
+        <view class="cu-bar bg-white solid-bottom margin-top">
+            <view class="action">
+                <text class="cuIcon-title text-orange"></text>
+                蓝牙列表
+            </view>
+        </view>
+        <view class="cu-list menu">
+            <view class="cu-item arrow" v-for="(item,index) in wifiList" :key="index">
+                <view class="content">
+                    <text class="cuIcon-btn text-green"></text>
+                    <text class="text-grey">{{item}}</text>
+                </view>
+            </view>
+        </view>
+    </view>
+</template>
+
+<script>
+    export default {
+        name: "wifi_page",
+        data() {
+            return {
+                wifiList: [],
+            }
+        },
+        onLoad() {
+            this.startWifi()
+        },
+        onUnload() {
+            // 关闭页面的时候关闭WiFi模块
+            this.stopWifi()
+        },
+        methods: {
+            onGetWifiList() {
+                const _this = this
+                wx.onGetWifiList(function(res) {
+                    console.log(res)
+                    if (res.wifiList.length) {
+                        wx.setWifiList({
+                            wifiList: [{
+                                SSID: res.wifiList[0].SSID,
+                                BSSID: res.wifiList[0].BSSID,
+                                password: '123456'
+                            }]
+                        })
+                    } else {
+                        wx.setWifiList({
+                            wifiList: []
+                        })
+                    }
+                    res.wifiList.forEach(item => {
+                        if (item.SSID !== "" && _this.wifiList.indexOf(item.SSID) === -1) {
+                            _this.wifiList.push(item.SSID)
+                        }
+                    })
+                })
+                wx.getWifiList()
+            },
+            startWifi() {
+                const _this = this
+                wx.startWifi({
+                    success (res) {
+                        console.log('startWifi success=', res.errMsg)
+                        _this.onGetWifiList()
+                    },
+                    fail(res) {
+                        console.log('startWifi fail=', res.errMsg)
+                    }
+                })
+            },
+            stopWifi() {
+                wx.stopWifi({
+                    success (res) {
+                        console.log(res.errMsg)
+                    }
+                })
+            }
+
+        }
+    }
+</script>
+
+<style scoped>
+
+</style>

+ 202 - 0
src/platforms/app-plus/feedback/feedback.vue

@@ -0,0 +1,202 @@
+<template>
+    <view class="page">
+        <view class='feedback-title'>
+            <text>问题和意见</text>
+            <text class="feedback-quick" @tap="chooseMsg">快速键入</text>
+        </view>
+        <view class="feedback-body">
+            <textarea placeholder="请详细描述你的问题和意见..." v-model="sendDate.content" class="feedback-textare"></textarea>
+        </view>
+        <view class='feedback-title'>
+            <text>图片(选填,提供问题截图,总大小10M以下)</text>
+        </view>
+        <view class="feedback-body feedback-uploader">
+            <view class="uni-uploader">
+                <view class="uni-uploader-head">
+                    <view class="uni-uploader-title">点击预览图片</view>
+                    <view class="uni-uploader-info">{{imageList.length}}/8</view>
+                </view>
+                <view class="uni-uploader-body">
+                    <view class="uni-uploader__files">
+                        <block v-for="(image,index) in imageList" :key="index">
+                            <view class="uni-uploader__file" style="position: relative;">
+                                <image class="uni-uploader__img" :src="image" @tap="previewImage(index)"></image>
+                                <view class="close-view" @click="close(index)">x</view>
+                            </view>
+                        </block>
+                        <view class="uni-uploader__input-box" v-show="imageList.length < 8">
+                            <view class="uni-uploader__input" @tap="chooseImg"></view>
+                        </view>
+                    </view>
+                </view>
+            </view>
+        </view>
+        <view class='feedback-title'>
+            <text>QQ/邮箱</text>
+        </view>
+        <view class="feedback-body">
+            <input class="feedback-input" v-model="sendDate.contact" placeholder="(选填,方便我们联系你 )" />
+        </view>
+        <view class='feedback-title feedback-star-view'>
+            <text>应用评分</text>
+            <view class="feedback-star-view">
+                <text class="feedback-star" v-for="(value,key) in stars" :key="key" :class="key < sendDate.score ? 'active' : ''"
+                    @tap="chooseStar(value)"></text>
+            </view>
+        </view>
+        <button type="default" class="feedback-submit" @tap="send">提交</button>
+        <view class='feedback-title'>
+            <text>用户反馈的结果可在app打包后于DCloud开发者中心查看</text>
+        </view>
+    </view>
+</template>
+
+<script>
+    export default {
+        data() {
+            return {
+                msgContents: ["界面显示错乱", "启动缓慢,卡出翔了", "UI无法直视,丑哭了", "偶发性崩溃"],
+                stars: [1, 2, 3, 4, 5],
+                imageList: [],
+                sendDate: {
+                    score: 0,
+                    content: "",
+                    contact: ""
+                }
+            }
+        },
+        onLoad() {
+            let deviceInfo = {
+                appid: plus.runtime.appid,
+                imei: plus.device.imei, //设备标识
+                p: plus.os.name === "Android" ? "a" : "i", //平台类型,i表示iOS平台,a表示Android平台。
+                md: plus.device.model, //设备型号
+                app_version: plus.runtime.version,
+                plus_version: plus.runtime.innerVersion, //基座版本号
+                os: plus.os.version,
+                net: "" + plus.networkinfo.getCurrentType()
+            }
+            this.sendDate = Object.assign(deviceInfo, this.sendDate);
+        },
+        methods: {
+            close(e) {
+                this.imageList.splice(e, 1);
+            },
+            chooseMsg() { //快速输入
+                uni.showActionSheet({
+                    itemList: this.msgContents,
+                    success: (res) => {
+                        this.sendDate.content = this.msgContents[res.tapIndex];
+                    }
+                })
+            },
+            chooseImg() { //选择图片
+                uni.chooseImage({
+                    sourceType: ["camera", "album"],
+                    sizeType: "compressed",
+                    count: 8 - this.imageList.length,
+                    success: (res) => {
+                        this.imageList = this.imageList.concat(res.tempFilePaths);
+                    }
+                })
+            },
+            chooseStar(e) { //点击评星
+                this.sendDate.score = e;
+            },
+            previewImage(index) { //预览图片
+                uni.previewImage({
+                    urls: this.imageList,
+                    current: this.imageList[index]
+                });
+            },
+            send() { //发送反馈
+                console.log(JSON.stringify(this.sendDate));
+                if (this.imageList.length === 0) {
+                    uni.showModal({
+                        content: '至少选择一张图片',
+                        showCancel: false
+                    })
+                    return
+                }
+                if (this.sendDate.content.length === 0) {
+                    uni.showModal({
+                        content: '请输入问题和意见',
+                        showCancel: false
+                    })
+                    return
+                }
+                uni.showLoading({
+                    title: '上传中...'
+                })
+                let imgs = this.imageList.map((value, index) => {
+                    return {
+                        name: "image" + index,
+                        uri: value
+                    }
+                })
+                uni.uploadFile({
+                    url: "https://service.dcloud.net.cn/feedback",
+                    files: imgs,
+                    formData: this.sendDate,
+                    success: (res) => {
+                        if (typeof res.data === 'string') {
+                            res.data = JSON.parse(res.data)
+                        }
+                        if (res.statusCode === 200 && res.data && res.data.ret === 0) {
+                            uni.showModal({
+                                content: '反馈成功',
+                                showCancel: false
+                            })
+                            this.imageList = [];
+                            this.sendDate = {
+                                score: 0,
+                                content: "",
+                                contact: ""
+                            }
+                        } else if (res.statusCode !== 200){
+                            uni.showModal({
+                                content: '反馈失败,错误码为:' + res.statusCode,
+                                showCancel: false
+                            })
+                        } else {
+                            uni.showModal({
+                                content: '反馈失败',
+                                showCancel: false
+                            })
+                        }
+                    },
+                    fail: (res) => {
+                        console.log(JSON.stringify(res))
+                    },
+                    complete() {
+                        uni.hideLoading()
+                    }
+                });
+            }
+        }
+    }
+</script>
+
+<style>
+    page {
+        background-color: #EFEFF4;
+    }
+
+    .input-view {
+        font-size: 28rpx;
+    }
+
+    .close-view {
+        text-align: center;
+        line-height: 14px;
+        height: 16px;
+        width: 16px;
+        border-radius: 50%;
+        background: #FF5053;
+        color: #FFFFFF;
+        position: absolute;
+        top: -6px;
+        right: -4px;
+        font-size: 12px;
+    }
+</style>

+ 61 - 0
src/platforms/app-plus/orientation/orientation.vue

@@ -0,0 +1,61 @@
+<template>
+	<view>
+		<page-head :title="title"></page-head>
+		<view class="uni-padding-wrap uni-common-mt">
+			<view class="uni-btn-v">
+				<button type="primary" @tap="getOrient">获取设备的方向信息</button>
+				<button type="primary" @tap="watchOrient">监听设备的方向变化</button>
+				<button type="primary" @tap="watchStop">停止监听</button>
+			</view>
+			<view class="uni-textarea">
+				<textarea :value="value" />
+			</view>
+		</view>
+	</view>
+</template>
+<script>
+	var id = null
+	export default {
+		data() {
+			return {
+				title: 'orientation',
+				value: ''
+			}
+		},
+		methods: {
+			getOrient: function () {
+				var that = this;
+				plus.orientation.getCurrentOrientation(function (o) {
+					that.value = "alpha:" + o.alpha + "\nbeta:" + o.beta + "\ngamma:" + o.gamma;
+				}, function (e) {
+					console.log("获取失败:" + e.message);
+				});
+			},
+			watchOrient: function () {
+				var that = this;
+				if (id) {
+					return;
+				}
+				id = plus.orientation.watchOrientation(function (o) {
+					that.value = "监听设备方向变化信息\n" + "alpha:" + o.alpha + "\nbeta:" + o.beta + "\ngamma:" + o.gamma;
+				}, function (e) {
+					plus.orientation.clearWatch(id);
+					id = null;
+					console.log("监听失败:" + e.message);
+				});
+			},
+			watchStop: function () {
+				if (id) {
+					plus.orientation.clearWatch(id);
+					id = null;
+				} else {
+					console.log("没有监听设备方向变化");
+				}
+			}
+		}
+	}
+</script>
+
+<style>
+	
+</style>

+ 69 - 0
src/platforms/app-plus/proximity/proximity.vue

@@ -0,0 +1,69 @@
+<template>
+	<view>
+		<page-head :title="title"></page-head>
+		<view class="uni-padding-wrap uni-common-mt">
+			<view class="uni-hello-text">
+				手机顶部听筒处有传感器监听距离手机屏幕的障碍物,覆盖该传感器会触发本事件变化
+			</view>
+			<view class="uni-btn-v uni-common-mt">
+				<button type="primary" @tap="getProximity">获取距离传感器信息</button>
+				<button type="primary" @tap="watchProximity">监听距离传感器变化</button>
+				<button type="primary" @tap="watchStop">停止监听</button>
+			</view>
+			<view class="uni-textarea uni-common-mt">
+				<textarea :value="value" />
+			</view>
+		</view>
+	</view>
+</template>
+<script>
+	var id = null
+	var bright = null
+	export default {
+		data() {
+			return {
+				title: 'proximity',
+				value: ''
+			}
+		},
+		methods: {
+			getProximity: function () {
+				var that = this;
+				plus.proximity.getCurrentProximity(function (d) {
+					that.value = "距离为:" + d;
+				}, function (e) {
+					that.value = "获取失败:" + e.message;
+				});
+			},
+			watchProximity: function () {
+				var that = this;
+				if (id) {
+					return;
+				}
+				bright = plus.screen.getBrightness();
+				id = plus.proximity.watchProximity(function (d) {
+					that.value = "距离变化:" + d;
+					plus.screen.setBrightness((d < 1) ? 0.01 : bright);
+				}, function (e) {
+					plus.proximity.clearWatch(id);
+					id = null;
+					that.value = "监听失败:" + e.message;
+				});
+			},
+			watchStop: function () {
+				var that = this;
+				if (id) {
+					that.value = "停止监听设备距离传感器信息";
+					plus.proximity.clearWatch(id);
+					id = null;
+				} else {
+					that.value = "没有监听设备距离传感器";
+				}
+			}
+		}
+	}
+</script>
+
+<style>
+	
+</style>

+ 120 - 0
src/platforms/app-plus/push/push.vue

@@ -0,0 +1,120 @@
+<template>
+	<view>
+		<page-head :title="title"></page-head>
+		<view class="uni-padding-wrap" v-if="provider[0]">
+			<view class="uni-btn-v uni-common-mt">
+				<button type="primary" @tap="openPush">开启push</button>
+				<button type="primary" @tap="closePush">关闭push</button>
+				<button type="primary" @tap="listenTranMsg">监听透传数据</button>
+				<button type="primary" @tap="removeTranMsg">移除监听透传数据</button>
+			</view>
+			<view class="uni-btn-v uni-common-mt">
+				<button type="primary" @tap="requireTranMsg">发送"透传数据"消息</button>
+			</view>
+			<view class="uni-title uni-common-mt">透传内容:</view>
+			<view class="uni-textarea">
+				<textarea v-model="tranMsg" />
+			</view>
+		</view>
+	</view>
+</template>
+<script>
+	export default {
+		data() {
+			return {
+				title: 'push',
+				provider: [],
+				pushServer: 'http://demo.dcloud.net.cn/push/?',
+				tranMsg:''
+			}
+		},
+		onLoad: function () {
+			uni.getProvider({
+				service: "push",
+				success: (e) => {
+					console.log("success", e);
+					this.provider = e.provider;
+				},
+				fail: (e) => {
+					console.log("获取推送通道失败", e);
+				}
+			});
+		},
+		onUnload:function(){
+			this.tranMsg = ''
+		},
+		methods: {
+			openPush() {
+				uni.subscribePush({
+					provider: this.provider[0],
+					success: (e) => {
+						uni.showToast({
+							title: "已开启push接收"
+						})
+					}
+				})
+			},
+			closePush() {
+				uni.unsubscribePush({
+					provider: this.provider[0],
+					success: (e) => {
+						uni.showToast({
+							title: "已关闭push接收"
+						})
+					}
+				})
+			},
+			listenTranMsg() {
+				uni.onPush({
+					provider: this.provider[0],
+					success: (e) => {
+						uni.showToast({
+							title: "开始监听透传数据"
+						})
+					},
+					callback: (e) => {
+						uni.showToast({
+							title: "接收到透传数据"
+						});
+						
+						this.tranMsg = JSON.stringify(e.data);
+					}
+				})
+			},
+			removeTranMsg() {
+				uni.offPush({
+					provider: this.provider[0],
+					success: (e) => {
+						console.log("移除监听透传数据");
+						uni.showToast({
+							title: "移除监听透传数据"
+						})
+					}
+				})
+			},
+			requireTranMsg() { //请求‘透传数据’推送消息
+				var inf = plus.push.getClientInfo();
+				var url = this.pushServer + 'type=tran&appid=' + encodeURIComponent(plus.runtime.appid);
+				inf.id && (url += '&id=' + inf.id);
+				url += ('&cid=' + encodeURIComponent(inf.clientid));
+				if (plus.os.name == 'iOS') {
+					url += ('&token=' + encodeURIComponent(inf.token));
+				}
+				url += ('&title=' + encodeURIComponent('Hello uniapp'));
+				url += ('&content=' + encodeURIComponent('带透传数据推送通知!'));
+				if(plus.os.name === 'iOS'){
+					url += ('&payload=' + encodeURIComponent('{"title":"Hello uniapp Test","content":"test content"}'));
+				}else{
+					url += ('&payload=' + encodeURIComponent('\'{"title":"Hello uniapp Test","content":"test content"}\''));
+				}
+				url += ('&version=' + encodeURIComponent(plus.runtime.version));
+				plus.runtime.openURL(url);
+			}
+		}
+	}
+</script>
+
+<style>
+
+</style>
+

+ 91 - 0
src/platforms/app-plus/shake/shake.vue

@@ -0,0 +1,91 @@
+<template>
+	<view class="root" :style="{backgroundImage:'url('+img+')'}">
+		<view :class="[show ? 'up' : '','shake-up']">
+			<image mode="aspectFit" src="https://img-cdn-qiniu.dcloud.net.cn/uniapp/shake/shakeup.png"></image>
+		</view>
+		<view :class="[show ? 'down' : '','shake-down']">
+			<image mode="aspectFit" src="https://img-cdn-qiniu.dcloud.net.cn/uniapp/shake/shakedown.png"></image>
+		</view>
+	</view>
+</template>
+<script>
+	export default {
+		data() {
+			return {
+				img: 'https://img-cdn-qiniu.dcloud.net.cn/uniapp/shake/1.jpg',
+				show: false,
+				isOpened: false
+			}
+		},
+		onLoad: function () {
+			this.music = uni.createInnerAudioContext();
+			this.music.src = 'https://img-cdn-qiniu.dcloud.net.cn/uniapp/shake/shake.wav';
+
+			let index = 1,
+				t = null;
+			uni.onAccelerometerChange((res) => {
+				if (Math.abs(res.x) + Math.abs(res.y) + Math.abs(res.z) > 20 && !this.show && this.isOpened) {
+					this.music.play();
+					setTimeout(() => {
+						index++;
+						if (index > 4) {
+							index = 1
+						}
+						this.img = 'https://img-cdn-qiniu.dcloud.net.cn/uniapp/shake/' + index + '.jpg';
+					}, 2000);
+					this.show = true;
+					if (t) {
+						clearTimeout(t);
+					}
+					t = setTimeout(() => {
+						t = null;
+						this.show = false;
+					}, 600)
+				}
+			})
+		},
+		onShow() {
+			this.isOpened = true;
+		},
+		onUnload() {
+			this.show = false;
+			this.isOpened = false;
+			uni.stopAccelerometer();
+			this.music.destroy();
+		}
+	}
+</script>
+
+<style>
+	.root {
+		height: 100%;
+		display: flex;
+		flex-direction: column;
+		background-position: center center;
+		background-repeat: no-repeat;
+	}
+
+	.shake-up,
+	.shake-down {
+		height: 50%;
+		overflow: hidden;
+		transition: all .5s ease-in-out;
+		-webkit-transition: all .5s ease-in-out;
+		background: #333;
+	}
+
+	.up {
+		transform: translateY(-50%);
+		-webkit-transform: translateY(-50%);
+	}
+
+	.down {
+		transform: translateY(50%);
+		-webkit-transform: translateY(50%);
+	}
+
+	image {
+		height: 100%;
+		width: 100%;
+	}
+</style>

+ 105 - 0
src/platforms/app-plus/speech/speech.vue

@@ -0,0 +1,105 @@
+<template>
+    <view>
+        <page-head :title="title"></page-head>
+        <view class="uni-padding-wrap uni-common-mt">
+            <view class="uni-textarea">
+                <textarea :value="value" placeholder="语音识别内容展示区域" disabled />
+                </view>
+			<view class="uni-common-mt uni-btn-v">
+				<button type="primary" @tap="startRecognize">开始语音识别</button>
+				<button type="primary" @tap="startRecognizeEnglish">开始语音识别(英语)</button>
+			</view>
+		</view>
+	</view>
+</template>
+<script>
+    import permision from "@/common/permission.js"
+	export default {
+		data() {
+			return {
+				title: 'speech',
+				value: ''
+			}
+		},
+		onUnload(){
+			this.value = ""
+		},
+		methods: {
+			async startRecognize () {
+                // #ifdef APP-PLUS
+                let status = await this.checkPermission();
+                if (status !== 1) {
+                    return;
+                }
+                // #endif
+
+                // TODO ios 在没有请求过权限之前无法得知是否有相关权限,这种状态下需要直接调用语音,会弹出正在识别的toast
+				var options = {};
+				var that = this;
+				options.engine = 'baidu';
+				that.value = "";
+				plus.speech.startRecognize(options, function (s) {
+					console.log(s);
+					that.value += s;
+				}, function (e) {
+					console.log("语音识别失败:" + e.message);
+				});
+			},
+			async startRecognizeEnglish () {
+                // #ifdef APP-PLUS
+                let status = await this.checkPermission();
+                if (status !== 1) {
+                    return;
+                }
+                // #endif
+
+                // TODO ios 在没有请求过权限之前无法得知是否有相关权限,这种状态下需要直接调用语音,会弹出正在识别的toast
+				var options = {};
+				var that = this;
+				options.engine = 'baidu';
+				options.lang = 'en-us';
+				that.value = "";
+				plus.speech.startRecognize(options, function (s) {
+					console.log(s);
+					that.value += s;
+				}, function (e) {
+					console.log("语音识别失败:" + e.message);
+				});
+			}
+            // #ifdef APP-PLUS
+            ,
+            async checkPermission() {
+                let status = permision.isIOS ? await permision.requestIOS('record') :
+                    await permision.requestAndroid('android.permission.RECORD_AUDIO');
+
+                if (status === null || status === 1) {
+                    status = 1;
+                } else if (status === 2) {
+                    uni.showModal({
+                        content: "系统麦克风已关闭",
+                        confirmText: "确定",
+                        showCancel: false,
+                        success: function(res) {
+                        }
+                    })
+                } else {
+                    uni.showModal({
+                        content: "需要麦克风权限",
+                        confirmText: "设置",
+                        success: function(res) {
+                            if (res.confirm) {
+                                permision.gotoAppSetting();
+                            }
+                        }
+                    })
+                }
+                return status;
+            }
+            // #endif
+		}
+	}
+</script>
+
+<style>
+
+</style>

BIN
src/static/bg.png


BIN
src/static/doctor.png


BIN
src/static/logo.png


BIN
src/static/uni.ttf


+ 73 - 0
src/store/index.js

@@ -0,0 +1,73 @@
+import Vue from 'vue'
+import Vuex from 'vuex'
+
+Vue.use(Vuex)
+
+const store = new Vuex.Store({
+	state: {
+		hasLogin: false,
+		loginProvider: "",
+		openid: null,
+		testvuex:false,
+        colorIndex: 0,
+        colorList: ['#FF0000','#00FF00','#0000FF']
+	},
+	mutations: {
+		login(state, provider) {
+			state.hasLogin = true;
+			state.loginProvider = provider;
+		},
+		logout(state) {
+			state.hasLogin = false
+			state.openid = null
+		},
+		setOpenid(state, openid) {
+			state.openid = openid
+		},
+		setTestTrue(state){
+			state.testvuex = true
+		},
+		setTestFalse(state){
+			state.testvuex = false
+		},
+        setColorIndex(state,index){
+            state.colorIndex = index
+        }
+	},
+    getters:{
+        currentColor(state){
+            return state.colorList[state.colorIndex]
+        }
+    },
+	actions: {
+		// lazy loading openid
+		getUserOpenId: async function ({
+			commit,
+			state
+		}) {
+			return await new Promise((resolve, reject) => {
+				if (state.openid) {
+					resolve(state.openid)
+				} else {
+					uni.login({
+						success: (data) => {
+							commit('login')
+							setTimeout(function () { //模拟异步请求服务器获取 openid
+								const openid = '123456789'
+								console.log('uni.request mock openid[' + openid + ']');
+								commit('setOpenid', openid)
+								resolve(openid)
+							}, 1000)
+						},
+						fail: (err) => {
+							console.log('uni.login 接口调用失败,将无法正常使用开放接口等服务', err)
+							reject(err)
+						}
+					})
+				}
+			})
+		}
+	}
+})
+
+export default store

+ 53 - 0
src/template.h5.html

@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+	<head>
+		<meta charset="utf-8">
+		<meta http-equiv="X-UA-Compatible" content="IE=edge">
+		<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
+		<title>
+			<%= htmlWebpackPlugin.options.title %>
+		</title>
+		<!-- 正式发布的时候使用,开发期间不启用。↓ -->
+        <!-- <script src="/h5/touch-emulator.js"></script>
+		<script>
+            TouchEmulator();
+			if (document.documentElement.clientWidth > 1024) {
+				window.location.href = '/h5/pcguide.html#'+location.pathname+location.search;
+			}
+		</script>
+        <style>
+            ::-webkit-scrollbar{
+                display: none;
+            }
+        </style>
+        <script>
+            var _hmt = _hmt || [];
+            (function() {
+                var hm = document.createElement("script");
+                hm.src = "https://hm.baidu.com/hm.js?";// 百度统计key
+                var s = document.getElementsByTagName("script")[0];
+                s.parentNode.insertBefore(hm, s);
+            })();
+        </script> -->
+        <!-- 正式发布的时候使用,开发期间不启用。↑ -->
+		<script>
+			document.addEventListener('DOMContentLoaded', function() {
+				document.documentElement.style.fontSize = document.documentElement.clientWidth / 20 + 'px'
+			})
+		</script>
+		<link rel="stylesheet" href="<%= BASE_URL %>static/index.css" />
+	</head>
+	<body>
+		<!-- 该文件为 H5 平台的模板 HTML,并非应用入口。 -->
+		<!-- 请勿在此文件编写页面代码或直接运行此文件。 -->
+		<!-- 详见文档:https://uniapp.dcloud.io/collocation/manifest?id=h5-template -->
+		<noscript>
+			<strong>Please enable JavaScript to continue.</strong>
+		</noscript>
+		<div id="app"></div>
+		<!-- built files will be auto injected -->
+		<script>
+			/*BAIDU_STAT*/
+		</script>
+	</body>
+</html>

+ 76 - 0
src/uni.scss

@@ -0,0 +1,76 @@
+/**
+ * 这里是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:#ffffff;
+$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:24rpx;
+$uni-font-size-base:28rpx;
+$uni-font-size-lg:32rpx;
+
+/* 图片尺寸 */
+$uni-img-size-sm:40rpx;
+$uni-img-size-base:52rpx;
+$uni-img-size-lg:80rpx;
+
+/* Border Radius */
+$uni-border-radius-sm: 4rpx;
+$uni-border-radius-base: 6rpx;
+$uni-border-radius-lg: 12rpx;
+$uni-border-radius-circle: 50%;
+
+/* 水平间距 */
+$uni-spacing-row-sm: 10px;
+$uni-spacing-row-base: 20rpx;
+$uni-spacing-row-lg: 30rpx;
+
+/* 垂直间距 */
+$uni-spacing-col-sm: 8rpx;
+$uni-spacing-col-base: 16rpx;
+$uni-spacing-col-lg: 24rpx;
+
+/* 透明度 */
+$uni-opacity-disabled: 0.3; // 组件禁用态的透明度
+
+/* 文章场景相关 */
+$uni-color-title: #2C405A; // 文章标题颜色
+$uni-font-size-title:40rpx;
+$uni-color-subtitle: #555555; // 二级标题颜色
+$uni-font-size-subtitle:36rpx;
+$uni-color-paragraph: #3F536E; // 文章段落颜色
+$uni-font-size-paragraph:30rpx;

+ 6 - 0
src/utils/bluetest.js

@@ -0,0 +1,6 @@
+ import './main.js'
+ const app=getApp();
+ const HBraclete = app.globalData.HBraclete;
+ export {
+     HBraclete
+ }

File diff suppressed because it is too large
+ 2 - 0
src/utils/main.js


+ 9 - 0
tsconfig.json

@@ -0,0 +1,9 @@
+{
+  "compilerOptions": {
+    "types": [
+      "@dcloudio/types",
+      "miniprogram-api-typings",
+      "mini-types"
+    ]
+  }
+}