Procházet zdrojové kódy

feat(): 重置master分支文件。解决冲突

zengzhixiang před 2 roky
rodič
revize
0be88ca8f2
100 změnil soubory, kde provedl 9634 přidání a 2352 odebrání
  1. 7 0
      .env.development
  2. 10 1
      .env.production
  3. 8 1
      .env.test
  4. 9 1
      .env.uat
  5. 7 1
      .eslintrc.js
  6. 12 0
      .gitattributes
  7. 174 0
      Jenkinsfile
  8. binární
      assets/image/activity/invite-user/bg-2@2x.png
  9. binární
      assets/image/activity/invite-user/bg@2x.png
  10. binární
      assets/image/activity/invite-user/box-bg@2x.png
  11. binární
      assets/image/activity/invite-user/box-title@2x.png
  12. binární
      assets/image/activity/invite-user/box@2x.png
  13. binární
      assets/image/activity/invite-user/cybz-content-2@2x.png
  14. binární
      assets/image/logo.png
  15. 76 0
      components/activity/invite-user/box.vue
  16. 194 0
      components/disk/index.vue
  17. 122 0
      components/disk/touch.vue
  18. 348 0
      components/disk/video.vue
  19. 4 2
      jsconfig.json
  20. 2 0
      modules/postcss-px-to-viewport.js
  21. 6 2
      nuxt.config.js
  22. 13 7
      package.json
  23. 469 156
      pages/activity/invite-user/index.vue
  24. 112 106
      pages/activity/invite-user/register.vue
  25. 112 0
      pages/customer-service.vue
  26. 1 9
      pages/index.vue
  27. 5 2
      pages/login.vue
  28. 74 0
      pages/phone/_id.vue
  29. 37 0
      pages/phone/index.vue
  30. 59 0
      pages/test/ios-post-message.vue
  31. 1 0
      plugins/auth.js
  32. 1 0
      plugins/axios.js
  33. 1 1
      plugins/baidu-tongji.js
  34. 5 3
      plugins/callapp.js
  35. 3 1
      plugins/error-captured.js
  36. 17 0
      plugins/file-center.js
  37. 0 31
      plugins/file-upload.js
  38. 1 0
      plugins/jweixin.js
  39. 79 19
      plugins/native.js
  40. 5 2
      plugins/user-agent.js
  41. 973 645
      pnpm-lock.yaml
  42. 52 1
      schemes/password.js
  43. 761 0
      static/microserviceUserH5/static/css/awardActivity.css
  44. binární
      static/microserviceUserH5/static/img/awardActivity/all-bg.png
  45. binární
      static/microserviceUserH5/static/img/awardActivity/bug-phone.png
  46. binární
      static/microserviceUserH5/static/img/awardActivity/card1.png
  47. binární
      static/microserviceUserH5/static/img/awardActivity/card2.jpg
  48. binární
      static/microserviceUserH5/static/img/awardActivity/card3.png
  49. binární
      static/microserviceUserH5/static/img/awardActivity/card4.png
  50. binární
      static/microserviceUserH5/static/img/awardActivity/no-award.png
  51. binární
      static/microserviceUserH5/static/img/awardActivity/no-data.png
  52. binární
      static/microserviceUserH5/static/img/awardActivity/rest-bg.png
  53. binární
      static/microserviceUserH5/static/img/awardActivity/start-award.png
  54. binární
      static/microserviceUserH5/static/img/awardActivity/tab-bg1.png
  55. binární
      static/microserviceUserH5/static/img/awardActivity/tab-bg2.png
  56. binární
      static/microserviceUserH5/static/img/awardActivity/tab-bg3.png
  57. binární
      static/microserviceUserH5/static/img/awardActivity/tab-bg4.png
  58. binární
      static/microserviceUserH5/static/img/awardActivity/top-bg-one.png
  59. binární
      static/microserviceUserH5/static/img/awardActivity/top-bg-two.png
  60. binární
      static/microserviceUserH5/static/img/iosEnter/entry.png
  61. binární
      static/microserviceUserH5/static/img/iosEnter/index-phone.png
  62. binární
      static/microserviceUserH5/static/img/iosEnter/index-what.png
  63. 0 1
      static/microserviceUserH5/static/js/vender/config.js
  64. 1141 0
      static/microserviceUserH5/vcloud/awardActivity.html
  65. 13 4
      static/microserviceUserH5/vcloud/iosEnter.html
  66. 371 151
      static/screenAndroid/WXdraw.js
  67. 386 85
      static/screenAndroid/WXtrialInterface.html
  68. 590 439
      static/screenAndroid/css/WXtrialInterface.css
  69. 470 156
      static/screenIos/WXdraw.js
  70. 402 66
      static/screenIos/WXtrialInterface.html
  71. 520 370
      static/screenIos/css/WXtrialInterface.css
  72. 67 50
      static/screenIos/decoder.js
  73. 62 39
      static/screenIos/helper.js
  74. binární
      static/static/img/phone-size-active.png
  75. 24 0
      static/static/lib/doT-1.1.3/.eslintrc.yml
  76. 33 0
      static/static/lib/doT-1.1.3/.gitignore
  77. 8 0
      static/static/lib/doT-1.1.3/.travis.yml
  78. 24 0
      static/static/lib/doT-1.1.3/LICENSE-DOT.txt
  79. 106 0
      static/static/lib/doT-1.1.3/README.md
  80. 110 0
      static/static/lib/doT-1.1.3/benchmarks/compileBench.js
  81. 22 0
      static/static/lib/doT-1.1.3/benchmarks/genspeed.html
  82. 22 0
      static/static/lib/doT-1.1.3/benchmarks/index.html
  83. 628 0
      static/static/lib/doT-1.1.3/benchmarks/jslitmus.js
  84. 138 0
      static/static/lib/doT-1.1.3/benchmarks/templatesBench.js
  85. 140 0
      static/static/lib/doT-1.1.3/benchmarks/templating/doT.js
  86. 56 0
      static/static/lib/doT-1.1.3/benchmarks/templating/doU.js
  87. 52 0
      static/static/lib/doT-1.1.3/bin/dot-packer
  88. 21 0
      static/static/lib/doT-1.1.3/bower.json
  89. 144 0
      static/static/lib/doT-1.1.3/doT.js
  90. 1 0
      static/static/lib/doT-1.1.3/doT.min.js
  91. 56 0
      static/static/lib/doT-1.1.3/doU.js
  92. 135 0
      static/static/lib/doT-1.1.3/examples/advancedsnippet.txt
  93. 54 0
      static/static/lib/doT-1.1.3/examples/browsersample.html
  94. 19 0
      static/static/lib/doT-1.1.3/examples/customdoT.js
  95. 1 0
      static/static/lib/doT-1.1.3/examples/express/index.js
  96. 25 0
      static/static/lib/doT-1.1.3/examples/express/lib/app.js
  97. 18 0
      static/static/lib/doT-1.1.3/examples/express/lib/render/index.js
  98. 10 0
      static/static/lib/doT-1.1.3/examples/express/package.json
  99. 5 0
      static/static/lib/doT-1.1.3/examples/express/templates/dashboard.jst
  100. 0 0
      static/static/lib/doT-1.1.3/examples/express/templates/login.jst

+ 7 - 0
.env.development

@@ -6,6 +6,7 @@ API_PREFIX = "/api"
 API_HOST = "192.168.31.194"
 API_PORT = 80
 API_HTTPS = false
+API_URL_BROWSER = "/api"
 
 # 文件服务器
 FILE_PREFIX = "/file"
@@ -19,3 +20,9 @@ UMENG_DATASOURCES_APP_KEY = "62ad961905844627b5b8638c"
 
 # 百度统计
 BAIDU_TONGJI_KEY = "fb2fdf29d47485e36ea2ec253bf6ac06"
+
+SZX_APP_IOS_SCHEME = "shuangzixingiOSAppTest"
+
+SZX_APP_ANDROID_SCHEME = "opengeminiapp"
+
+QIYUKF_TEMPLATE_ID = "6635862"

+ 10 - 1
.env.production

@@ -3,11 +3,12 @@ API_PREFIX = "/api"
 API_HOST = "client.phone.androidscloud.com"
 API_PORT = 443
 API_HTTPS = true
+API_URL_BROWSER = "/api"
 
 # 文件服务器
 FILE_PREFIX = "/"
 FILE_HOST = "file.phone.androidscloud.com"
-FILE_PORT = 8210
+FILE_PORT = 8121
 FILE_UPLOAD_KEY = "edv834e74a9c43eaac02"
 FILE_HTTPS = true
 
@@ -16,3 +17,11 @@ UMENG_DATASOURCES_APP_KEY = "62ad961905844627b5b8638c"
 
 # 百度统计
 BAIDU_TONGJI_KEY = "fb2fdf29d47485e36ea2ec253bf6ac06"
+
+
+SZX_APP_IOS_SCHEME = "shuangzixingiOSApp"
+
+SZX_APP_ANDROID_SCHEME = "opengeminiapp"
+
+QIYUKF_TEMPLATE_ID = "6635862"
+

+ 8 - 1
.env.test

@@ -4,11 +4,12 @@ API_PREFIX = "/api"
 API_HOST = "gntest.phone.androidscloud.com"
 API_PORT = 1280
 API_HTTPS = false
+API_URL_BROWSER = "/api"
 
 # 文件服务器
 FILE_PREFIX = "/"
 FILE_HOST = "file.phone.androidscloud.com"
-FILE_PORT = 8210
+FILE_PORT = 8121
 FILE_UPLOAD_KEY = "3dn9b4585511476691c6"
 FILE_HTTPS = true
 
@@ -17,3 +18,9 @@ UMENG_DATASOURCES_APP_KEY = "62ad961905844627b5b8638c"
 
 # 百度统计
 BAIDU_TONGJI_KEY = "fb2fdf29d47485e36ea2ec253bf6ac06"
+
+SZX_APP_IOS_SCHEME = "shuangzixingiOSAppTest"
+
+SZX_APP_ANDROID_SCHEME = "opengeminiapp"
+
+QIYUKF_TEMPLATE_ID = "6635862"

+ 9 - 1
.env.uat

@@ -5,10 +5,12 @@ API_HOST = "prese.phone.androidscloud.com"
 API_PORT = 443
 API_HTTPS = true
 
+API_URL_BROWSER = "/api"
+
 # 文件服务器
 FILE_PREFIX = "/"
 FILE_HOST = "file.phone.androidscloud.com"
-FILE_PORT = 8210
+FILE_PORT = 8121
 FILE_UPLOAD_KEY = "347905r86eb745a1sc38"
 FILE_HTTPS = true
 
@@ -17,3 +19,9 @@ UMENG_DATASOURCES_APP_KEY = "62ad961905844627b5b8638c"
 
 # 百度统计
 BAIDU_TONGJI_KEY = "fb2fdf29d47485e36ea2ec253bf6ac06"
+
+SZX_APP_IOS_SCHEME = "shuangzixingiOSAppTest"
+
+SZX_APP_ANDROID_SCHEME = "opengeminiapp"
+
+QIYUKF_TEMPLATE_ID = "6635862"

+ 7 - 1
.eslintrc.js

@@ -4,11 +4,17 @@ module.exports = {
     browser: true,
     node: true,
   },
+  parser: 'vue-eslint-parser',
   parserOptions: {
     parser: '@babel/eslint-parser',
     requireConfigFile: false,
   },
-  extends: ['@nuxtjs', 'plugin:nuxt/recommended', 'prettier'],
+  extends: [
+    'plugin:vue/recommended',
+    '@nuxtjs',
+    'plugin:nuxt/recommended',
+    'prettier',
+  ],
   plugins: [],
   // add your custom rules here
   rules: {

+ 12 - 0
.gitattributes

@@ -0,0 +1,12 @@
+# core.safecrlf = true
+# core.autocrlf = input
+
+* text=auto eol=lf
+*.js text eol=lf
+*.vue text eol=lf
+*.css text eol=lf
+*.scss text eol=lf
+*.json text eol=lf
+*.jpg -text
+*.png -text
+*.gif -text

+ 174 - 0
Jenkinsfile

@@ -0,0 +1,174 @@
+
+// 所有脚本命令包含在pipeline{}中
+pipeline {
+    // 指定任务在哪个节点执行(Jenkins支持分布式)
+  agent any
+  // 配置全局环境,指定变量名=变量值信息
+  // environment{
+  //   host = '172.17.1.22'
+  // }
+  environment {
+    // docker的包名
+    DOCKER_VERSION = env.BUILD_TAG.replace("%2F","-")
+  }
+  options{
+    disableConcurrentBuilds()
+  }
+  parameters {
+    booleanParam(name: 'auto_run_docker_nginx_build_job', defaultValue: false, description: '自动运行docker构建任务')
+    booleanParam(name: 'auto_run_docker_nginx_pull_job', defaultValue: false, description: '自动运行docker发布任务')
+  }
+
+  triggers {
+    GenericTrigger(
+     genericVariables: [
+      [key: 'ref', value: '$.ref']
+     ],
+     causeString: 'Triggered on $ref',
+     token: 'android-cloud-h5',
+     tokenCredentialId: '',
+     silentResponse: false,
+     printPostContent: true,
+     printContributedVariables: true,
+     regexpFilterExpression: 'refs/heads/' + env.BRANCH_NAME,
+     regexpFilterText: '$ref'
+    )
+  }
+
+
+  // 存放所有任务的合集
+  stages {
+    // 实现任务的具体流程
+    stage('初始化构建环境') {
+      steps {
+        // echo "DOCKER_VERSION: ${env.DOCKER_VERSION}"
+        echo "env.ref: ${env.ref}"
+        script {
+          if (env.ref) {
+            echo "任务来源git Webhooks开启自动构建并发布docker"
+            env.auto_run_docker_nginx_build_job = "true"
+            env.auto_run_docker_nginx_pull_job = "true"
+          }
+        }
+        echo "自动运行docker构建任务: ${env.auto_run_docker_nginx_build_job}"
+        echo "自动运行docker发布任务: ${env.auto_run_docker_nginx_pull_job}"
+
+        // XXX: 此处需注意类型
+        // params.auto_run_docker_nginx_build_job === false
+        // env.auto_run_docker_nginx_build_job === "false"
+
+
+        // echo "params.auto_run_docker_nginx_pull_job: ${params.auto_run_docker_nginx_pull_job}"
+        // echo 'env.BRANCH_NAME: ' + env.BRANCH_NAME
+        // echo 'env.BUILD_NUMBER: ' + env.BUILD_NUMBER
+        // echo 'env.BUILD_ID: ' + env.BUILD_ID
+        // echo 'env.BUILD_DISPLAY_NAME: ' + env.BUILD_DISPLAY_NAME
+        // echo 'env.JOB_NAME: ' + env.JOB_NAME
+        // echo 'env.JOB_BASE_NAME: ' + env.JOB_BASE_NAME
+        // echo 'env.BUILD_TAG: ' + env.BUILD_TAG
+        nodejs('v16') {
+          sh '''
+            node -v
+            npm -v
+            npm install
+          '''
+        }
+      }
+    }
+    stage('构建项目') {
+      steps {
+        script {
+          // if (env.TAG_NAME) {
+          //   nodejs('v16') {
+          //     sh 'npm run build:prod'
+          //   }
+          // } else
+          if (env.BRANCH_NAME == 'master') {
+            nodejs('v16') {
+              sh 'npm run build:prod'
+            }
+          } else if (env.BRANCH_NAME ==~ /^(release\/.+)$/) {
+            nodejs('v16') {
+              sh 'npm run build:uat'
+            }
+          } else {
+            nodejs('v16') {
+              sh 'npm run build:test'
+            }
+          }
+        }
+      }
+    }
+    stage('构建docker镜像') {
+      when {
+        allOf {
+          expression { env.auto_run_docker_nginx_build_job == "true" }
+          anyOf {
+            branch "develop"
+            branch "release/*"
+            // branch 'uat'
+            // branch "master"
+            // expression { BRANCH_NAME ==~ /^(release\/.+)$/ }
+          }
+        }
+      }
+      environment {
+        // 构建docker的任务名
+        BUILD_DOCKER_JOB = """${sh(
+          returnStdout: true,
+          script: """
+            if [[ "$BRANCH_NAME" == "master" ]];then
+              echo "cloud_build_docker_nginx/prod"
+            elif [[ "$BRANCH_NAME" =~ ^release\\/.* ]];then
+              echo "cloud_build_docker_nginx/uat"
+            else
+              echo "cloud_build_docker_nginx/test"
+            fi
+          """
+        ).trim()}"""
+      }
+      steps {
+        echo '开始构建docker镜像'
+        echo "DOCKER_VERSION: ${env.DOCKER_VERSION}"
+        echo "build job: ${env.BUILD_DOCKER_JOB}"
+        build job: env.BUILD_DOCKER_JOB, parameters: [string(name: 'version', value: env.DOCKER_VERSION), extendedChoice(name: 'service', value: 'manage_H5'),  booleanParam(name: 'auto_run_docker_nginx_pull_job', value: false), string(name: 'manage_H5_workspace', value: env.WORKSPACE)]
+      }
+    }
+    stage('发布docker镜像') {
+      when {
+        allOf {
+          expression { env.auto_run_docker_nginx_build_job == "true" }
+          expression { env.auto_run_docker_nginx_pull_job == "true" }
+          anyOf {
+            branch 'develop'
+            branch "release/*"
+            // branch 'uat'
+            // branch 'master'
+            // expression { BRANCH_NAME ==~ /^(release\/.+)$/ }
+          }
+        }
+      }
+      environment {
+        // 发布docker的任务名
+        PULL_DOCKER_JOB = """${sh(
+          returnStdout: true,
+          script: """
+            if [[ "$BRANCH_NAME" == "master" ]];then
+              echo ""
+            elif [[ "$BRANCH_NAME" =~ ^release\\/.* ]];then
+              echo "cloud_publish_docker_pre"
+            else
+              echo "cloud_publish_docker_powerful_test"
+            fi
+          """
+        ).trim()}"""
+      }
+      steps {
+        echo '开始发布docker镜像'
+        echo "build job: ${env.PULL_DOCKER_JOB}"
+        build job: env.PULL_DOCKER_JOB, parameters: [extendedChoice(name: 'service', value: 'cloud-nginx'), string(name: 'version', value: env.DOCKER_VERSION), string(name: 'replicas', value: '3'), string(name: 'memory', value: '2G'), string(name: 'cpu', value: '2')]
+      }
+    }
+  }
+}
+

binární
assets/image/activity/invite-user/bg-2@2x.png


binární
assets/image/activity/invite-user/bg@2x.png


binární
assets/image/activity/invite-user/box-bg@2x.png


binární
assets/image/activity/invite-user/box-title@2x.png


binární
assets/image/activity/invite-user/box@2x.png


binární
assets/image/activity/invite-user/cybz-content-2@2x.png


binární
assets/image/logo.png


+ 76 - 0
components/activity/invite-user/box.vue

@@ -0,0 +1,76 @@
+<template lang="">
+  <div class="box">
+    <div class="box-header">
+      <div class="box-header-content">
+        <slot name="title"></slot>
+      </div>
+    </div>
+
+    <div class="box-main">
+      <slot></slot>
+    </div>
+  </div>
+</template>
+<script>
+export default {
+  name: 'ActivityInviteUserBox',
+};
+</script>
+<style lang="scss">
+.box {
+  width: 373px;
+  box-sizing: border-box;
+  margin: auto;
+
+  // border-image-width: 200px;
+  // border-image-slice: 200%;
+  // border-width: 1px;
+  position: relative;
+  z-index: 0;
+
+  + .box {
+    margin-top: 30px;
+  }
+
+  .box-header {
+    // position: absolute;
+    position: relative;
+    // top: -30px;
+    z-index: 1;
+    // left: 141px;
+    // left: 0;
+    // right: 0;
+    margin-bottom: -34px;
+    // text-align: center;
+    // padding: 0 130px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    flex-direction: column;
+    .box-header-content {
+      color: #fff;
+      border-image-source: url('~/assets/image/activity/invite-user/box-title@2x.png');
+      border-image-slice: 0 140 fill;
+      border-width: 0 70px 0;
+      border-style: solid;
+      height: 38px;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      flex-direction: column;
+      // width: auto;
+    }
+  }
+
+  .box-main {
+    z-index: 0;
+    // padding: 30px 15px 20px;
+    border-image-source: url('~/assets/image/activity/invite-user/box-bg@2x.png');
+    border-image-slice: 50 60 80 fill;
+    border-image-width: 30px 30px 40px;
+    border-width: 50px 20px 40px;
+    border-style: solid;
+    // padding-top: 30px;
+  }
+}
+</style>

+ 194 - 0
components/disk/index.vue

@@ -0,0 +1,194 @@
+<template>
+  <div
+    :data-id="diskInfo && diskInfo.id"
+    class="disk reverse flex flex-col h-full"
+  >
+    <header class="disk-header"></header>
+    <main class="disk-main flex-auto">
+      <disk-video :info="connectInfo"></disk-video>
+      <disk-touch :info="connectInfo" @sizeChange="() => {}"></disk-touch>
+    </main>
+    <footer class="disk-footer h-50rpx"></footer>
+    <v-overlay absolute :value="$fetchState.error || $fetchState.pending">
+      <div
+        v-if="$fetchState.pending"
+        class="flex flex-col justify-center items-center"
+      >
+        <v-progress-circular
+          indeterminate
+          class="w-32rpx h-32rpx"
+        ></v-progress-circular>
+        <div v-if="loadingLabel" class="label mt-2">{{ loadingLabel }}</div>
+      </div>
+      <div v-else-if="$fetchState.error" class="">
+        <v-icon>$error</v-icon>
+        <div class="">{{ $fetchState.error.message }}</div>
+        <div class="">
+          <v-btn @click="$router.back()">退出</v-btn>
+        </div>
+      </div>
+    </v-overlay>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'Disk',
+  props: {
+    userCardId: {
+      type: Number,
+      default: null,
+    },
+  },
+  data() {
+    return {
+      diskInfo: null,
+      connectInfo: null,
+      // loading: false,
+      loadingLabel: null,
+
+      // 当前生效的分辨率
+      currentWidth: 720,
+      currentHeight: 1280,
+      currentDpi: 1,
+
+      // 当前选中的分辨率
+      activeWidth: 720,
+      activeHeight: 1280,
+      activeDpi: 1,
+
+      // 分辨率列表
+      phoneSizeList: [
+        {
+          id: 1,
+          width: 720,
+          height: 1280,
+          dpi: 1,
+        },
+      ],
+
+      // currentPhoneSizeId: 0,
+      // activePhoneSizeId: 0,
+    };
+  },
+  async fetch() {
+    // await this.getDiskInfo();
+    if (this.userCardId) {
+      await this.getDiskInfo(this.userCardId);
+      await this.getPhoneSizeList(this.userCardId);
+      await this.connect(this.userCardId);
+    }
+  },
+  computed: {
+    // currentPhoneSize() {
+    //   // return this.phoneSizeList.find(v=> v.width=== )
+    // },
+  },
+  watch: {
+    userCardId: '$fetch',
+  },
+  methods: {
+    // 查询云机
+    async getDiskInfo(userCardId) {
+      const res = await this.$axios.$get(
+        '/resources/v5/client/disk/info/userCard/single',
+        {
+          params: {
+            userCardId,
+          },
+        },
+      );
+      this.diskInfo = res.data;
+    },
+    async getPhoneSizeList(userCardId) {
+      try {
+        const res = await this.$axios.$get(
+          '/resources/v5/machine/resolution/getResolvingPower',
+          {
+            params: {
+              userCardId,
+            },
+          },
+        );
+        this.phoneSizeList = res.data;
+      } catch (error) {
+        this.phoneSizeList = [];
+      }
+    },
+
+    /**
+     * 轮询连接云机,因为超分需要挂载
+     * @param {number} userCardId
+     * @param {number} 超时
+     */
+    async connect(userCardId) {
+      // 连接云机
+      const getConnectData = () =>
+        this.$axios.$post('/resources/user/cloud/connect', {
+          userCardId,
+        });
+
+      try {
+        const res = await getConnectData();
+        this.connectInfo = res.data;
+        return res;
+      } catch (error) {
+        const res = await new Promise((resolve, reject) => {
+          const startTime = Date.now();
+          const maxTime = 1000 * 60;
+          const interval = 1000 * 1;
+          clearInterval(this._watchConnectInterval);
+          this._watchConnectInterval = setInterval(async () => {
+            try {
+              if (Date.now() - startTime >= maxTime) {
+                throw new Error('云手机挂载超时');
+              }
+              await getConnectData().then(resolve, (error) => {
+                // console.log(
+                //   '🚀 ~ file: index.vue ~ line 149 ~ awaitgetConnectData ~ error',
+                //   error,
+                // );
+                if (error.response.data.status === 5220) {
+                  // console.log('一键修复');
+                  this.loadingLabel = error.response.data.msg;
+                }
+              });
+            } catch (error) {
+              clearInterval(this._watchConnectInterval);
+              reject(error);
+            }
+          }, interval);
+        });
+        this.connectInfo = res.data;
+        return res;
+      }
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+// .disk {
+//   position: relative;
+//   display: flex;
+//   flex-direction: column;
+//   height: 100%;
+// }
+// .disk-header {
+//   position: relative;
+//   height: 0;
+//   flex: none;
+// }
+// .disk-main {
+//   position: relative;
+//   flex: 1;
+// }
+// .disk-footer {
+//   height: 50px;
+// flex: none;
+// }
+.disk-main {
+  position: relative;
+  overflow: hidden;
+}
+</style>

+ 122 - 0
components/disk/touch.vue

@@ -0,0 +1,122 @@
+<template>
+  <div
+    class="disk-touch"
+    @touchstart="ontouchstart"
+    @touchmove="ontouchmove"
+    @touchend="ontouchend"
+  >
+  </div>
+</template>
+
+<script>
+import qs from 'qs';
+export default {
+  name: 'DiskTouch',
+  props: {
+    info: {
+      type: Object,
+      default: null,
+    },
+  },
+  async fetch() {
+    this.info && (await this.initBusinessChannelWebSocket());
+  },
+  watch: {
+    info: '$fetch',
+  },
+  methods: {
+    genSendDataList(event, action) {
+      const width = 720;
+      const height = 1280;
+      return Array.from(event.changedTouches).map((item) => ({
+        data: {
+          action,
+          count: event.touches.length,
+          pointerId: item.identifier,
+          x: (
+            item.clientX *
+            (width / this.$el.getBoundingClientRect().width)
+          ).toFixed(2),
+          y: (
+            item.clientY *
+            (height / this.$el.getBoundingClientRect().height)
+          ).toFixed(2),
+        },
+        type: 'event',
+      }));
+    },
+    initBusinessChannelWebSocket() {
+      this?._wx?.close();
+      const { internetHttps, localIp, sn, cardToken } = this.info;
+      const url = `wss://${internetHttps}/businessChannel${qs.stringify(
+        {
+          cardIp: localIp,
+          token: cardToken,
+          type: 'directives',
+        },
+        { addQueryPrefix: true, encode: false },
+      )}`;
+      const ws = new WebSocket(url);
+      ws.addEventListener('open', (e) => {
+        ws.send(JSON.stringify({ type: 'getVsStatus' }));
+        ws.send(
+          JSON.stringify({ type: 'bitRate', data: { bitRate: 1243000 } }),
+        );
+        ws.send(
+          JSON.stringify({
+            type: 'forwardMsg',
+            data: { code: '3000', desc: '询问是否有在控制' },
+          }),
+        );
+        ws.send(
+          JSON.stringify({
+            type: 'getPhoneSize',
+          }),
+        );
+      });
+      ws.addEventListener('error', (e) => {});
+      ws.addEventListener('message', (event) => {
+        var result =
+          typeof event.data === 'string' ? JSON.parse(event.data) : event.data;
+        console.log(result);
+        if (result.type === 'getPhoneSize') {
+          this.$emit('sizeChange', result.data);
+        }
+      });
+      ws.addEventListener('close', (e) => {});
+      this.$once('hook:beforeDestroy', () => {
+        ws.close();
+      });
+      this._ws = ws;
+    },
+    ontouchstart(event) {
+      this.genSendDataList(event, 0).forEach((item) =>
+        this._ws.send(JSON.stringify(item)),
+      );
+    },
+    ontouchmove(event) {
+      this.genSendDataList(event, 2).forEach((item) =>
+        this._ws.send(JSON.stringify(item)),
+      );
+    },
+    ontouchend(event) {
+      this.genSendDataList(event, 1).forEach((item) =>
+        this._ws.send(JSON.stringify(item)),
+      );
+    },
+  },
+};
+</script>
+
+
+<style lang="scss" scoped>
+.disk-touch {
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  z-index: 1;
+}
+</style>
+

+ 348 - 0
components/disk/video.vue

@@ -0,0 +1,348 @@
+<template>
+  <div class="disk-video">
+    <video
+      v-if="hasMediaSource"
+      ref="video"
+      class="video"
+      autoplay
+      muted
+      controls
+    ></video>
+    <canvas
+      v-else
+      class="canvas"
+      width="720"
+      height="1280"
+    ></canvas>
+  </div>
+</template>
+
+<script>
+import JMuxer from 'jmuxer';
+import qs from 'qs';
+// bcc校验码计算
+// arry: 要计算的数组
+// 返回计算协议中校验位的校验码
+function calBcc(arry) {
+  let bcc = 0;
+  for (let i = 0; i < arry.length; i++) {
+    bcc ^= arry[i];
+  }
+  return bcc;
+}
+
+function makeFrameExtend(sn, dataType, body) {
+  let index = 0;
+  const dataLen = body.length;
+  const frameLen = dataLen + 26;
+  const outPut = new Uint8Array(frameLen);
+  outPut[index++] = 0x68;
+  outPut[index++] = (dataLen & 0xff000000) >> 24;
+  outPut[index++] = (dataLen & 0x00ff0000) >> 16;
+  outPut[index++] = (dataLen & 0x0000ff00) >> 8;
+  outPut[index++] = dataLen & 0x000000ff;
+  outPut[index++] = 0; // 类型为client
+
+  // sn号赋值,string转ascii
+  for (let i = 0; i < sn.length; i++) {
+    outPut[index++] = sn[i].charCodeAt();
+  }
+
+  outPut[index++] = dataType; // 指定数据类型为json
+  // json string转ascii
+  for (let i = 0; i < body.length; i++) {
+    outPut[index++] = body[i];
+  }
+
+  const bccBuffer = outPut.slice(1, frameLen - 3 + 1); // 忽略协议头和协议尾
+  outPut[index++] = calBcc(bccBuffer);
+  outPut[index++] = 0x16;
+  return outPut;
+}
+
+// 生成鉴权报文
+function VerifyCode(sn, code) {
+  const len = code.length + 1;
+  const codeBuffer = new TextEncoder('utf-8').encode(code); // 获取字符串ascii码
+  const buffer = new Uint8Array(len);
+  buffer[0] = 0x04;
+
+  for (let i = 0; i < codeBuffer.length; i++) {
+    buffer[i + 1] = codeBuffer[i];
+  }
+
+  return makeFrameExtend(sn, 6, buffer);
+}
+
+// I 帧请求报文生成
+function RequestIFrame(sn) {
+  // let sn = "RK3923C1201900139";
+  const outPut = new Uint8Array([0x20]);
+  return makeFrameExtend(sn, 6, outPut);
+}
+
+// 数组打印,调试用
+function PrintArry(data) {
+  let str = '';
+
+  for (let i = 0; i < data.length; i++) {
+    str = str + data[i].toString(16).padStart(2, '0');
+  }
+
+  str = str.toUpperCase();
+  return str;
+}
+// 检查鉴权报文
+function CheckVerifyCode(data) {
+  const dataLen = data.length - 26;
+  const body = data.slice(24, 24 + dataLen);
+  console.log('打印:' + PrintArry(body));
+
+  if (body[3] === 0x03) {
+    return true;
+  }
+
+  return false;
+}
+// 通道配置
+function ConfigChannel(sn) {
+  const outPut = new Uint8Array([0x07]);
+  return makeFrameExtend(sn, 6, outPut);
+}
+
+// 查询屏幕方向
+function GetScreenState(sn) {
+  // let sn = "RK3923C1201900139";
+  const outPut = new Uint8Array([
+    0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02,
+  ]);
+  return makeFrameExtend(sn, 5, outPut);
+}
+
+// 根据报文识别屏幕方向, 0横屏,1竖屏
+function CheckScreenDirection(data) {
+  if (data[0] === 0 && data[1] === 0 && data[2] === 0 && data[3] === 1) {
+    if (data[4] === 1 && data[5] === 1) {
+      if (data[6] === 1) {
+        const screen = data[7];
+        return screen;
+      }
+    }
+  }
+}
+
+// 多端登录数据解析
+function checkMultiLoginInfo(input) {
+  const dataLen = input.length - 26; // 得到json 长度
+  const jsonHex = input.slice(24, 24 + dataLen); // 截取json hex二进制数据
+  const jsonStr = new TextDecoder('utf-8').decode(jsonHex);
+  console.log('取得json 字符串:' + jsonStr);
+  const jsonObj = JSON.parse(jsonStr);
+  return jsonObj;
+}
+
+// 切换清晰度
+function makeSharpness(sn, level) {
+  // var sn = "RK3923C1201900139";
+  const jsonObj = {
+    type: 2,
+    data: { definition: level, clientType: 'h5', sceneType: 'cloudPhone' },
+  };
+  const jsonStr = JSON.stringify(jsonObj);
+  const outPut = new TextEncoder('utf-8').encode(jsonStr);
+  return makeFrameExtend(sn, 0xd, outPut);
+}
+export default {
+  name: 'DiskVideo',
+  props: {
+    info: {
+      type: Object,
+      default: null,
+    },
+  },
+  data() {
+    return {
+      hasMediaSource: false,
+    };
+  },
+  async fetch() {
+    this.info && (await this.initPlugflowWebSocket());
+  },
+  computed: {
+    sn() {
+      return this.info.sn;
+    },
+  },
+  watch: {
+    info: '$fetch',
+  },
+  created() {},
+  mounted() {
+    this.initVideo();
+  },
+  methods: {
+    initPlugflowWebSocket() {
+      this?._ws?.close();
+      const { internetHttps, localIp, sn, cardToken } = this.info;
+      const url = `wss://${internetHttps}/plugflow${qs.stringify(
+        {
+          cardIp: localIp,
+          token: cardToken,
+          type: 'business',
+        },
+        { addQueryPrefix: true },
+      )}`;
+      const ws = new WebSocket(url);
+      ws.binaryType = 'arraybuffer';
+      ws.addEventListener('open', (e) => {
+        ws.send('ping');
+        ws._pingInterval = setInterval(() => {
+          ws.send('ping');
+        }, 1000 * 1);
+        ws.send(VerifyCode(sn, cardToken));
+        // ws.send(makeSharpness(sn, 4));
+        // ws.send(RequestIFrame(sn));
+      });
+      ws.addEventListener('error', (e) => {});
+      ws.addEventListener('message', (event) => {
+        const ParseProto = (data) => {
+          const input = new Uint8Array(data);
+          let duration;
+          let video;
+          let frameType;
+          let audio;
+
+          if (
+            input[0] === 0 &&
+            input[1] === 0 &&
+            input[2] === 0 &&
+            input[3] === 1
+          ) {
+            video = input;
+            duration = 24;
+            const nalType = input[4] & 0x1f;
+            frameType = nalType;
+
+            // if (!isFeed) {
+            //   if (nalType == 0x05 && isVisuable) {
+            //     isFeed = true;
+            //   }
+            // }
+          } else if (input[0] === 0xff) {
+            audio = input;
+            duration = 24;
+          } else if (input[0] === 0x68) {
+            if (input[23] === 0x5c) {
+              console.log('收到消息:' + PrintArry(input));
+
+              if (CheckVerifyCode(input)) {
+                ws.send(ConfigChannel(sn));
+                // ws.send(GetScreenState(sn));
+              } else {
+                // connect('update');
+              }
+            }
+            if (input[23] === 0x05) {
+              // 横竖屏标识
+              const state = CheckScreenDirection(input.slice(24, 24 + 8));
+
+              if (state === 1) {
+                console.log('安卓卡此时竖屏');
+                // 竖屏处理
+                // resolving = 1;
+              } else {
+                console.log('安卓卡此时横屏');
+                // 横屏处理
+                // resolving = 0;
+              }
+            }
+            if (input[23] === 0x0b) {
+              // 多端登录处理, 数据从索引24开始取, input 是接收到的原始数据
+              const jsonobj = checkMultiLoginInfo(input);
+              // console.log(
+              //   '🚀 ~ file: disk.vue ~ line 324 ~ ParseProto ~ jsonobj',
+              //   jsonobj,
+              // );
+            }
+          }
+
+          return {
+            audio,
+            video,
+            duration,
+            frameType,
+          };
+        };
+        const data = ParseProto(event.data); // JAVA服务器转发
+        // console.log(
+        //   '🚀 ~ file: disk.vue ~ line 336 ~ ws.addEventListener ~ data',
+        //   data,
+        // );
+        if (data.video) {
+          this.pushVideo(data);
+        }
+      });
+      ws.addEventListener('close', (event) => {
+        clearInterval(event.currentTarget._pingInterval);
+      });
+      this.$once('hook:beforeDestroy', () => {
+        ws.close();
+      });
+      this._ws = ws;
+    },
+    initVideo() {
+      this.hasMediaSource = !!window.MediaSource;
+      // this.hasMediaSource = false;
+      if (this.hasMediaSource) {
+        this._jmuxer = new JMuxer({
+          node: this.$refs.video,
+          flushingTime: 33,
+          fps: 30,
+          mode: 'video',
+          debug: true,
+          onReady :()=>{
+            console.log('JMuxer ready');
+          }
+        });
+      } else {
+        throw new Error('当前设备不支持MediaSource');
+      }
+    },
+    pushVideo(data) {
+      console.log("🚀 ~ file: video.vue ~ line 315 ~ pushVideo ~ data", data)
+      if (this.hasMediaSource) {
+        this?._jmuxer?.feed({
+          video: data.video,
+          // duration: data.duration,
+          // frameType: data.frameType,
+        });
+        this.$refs?.video?.play();
+      }
+    },
+  },
+};
+</script>
+
+
+<style lang="scss" scoped>
+.disk-video {
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  z-index: 0;
+}
+.video {
+  position: relative;
+  width: 100%;
+  height: 100%;
+  object-fit: fill;
+}
+.canvas {
+  position: relative;
+  width: 100%;
+  height: 100%;
+}
+</style>
+

+ 4 - 2
jsconfig.json

@@ -6,7 +6,9 @@
       "@/*": ["./*"],
       "~~/*": ["./*"],
       "@@/*": ["./*"]
-    }
+    },
+    "jsx": "preserve"
   },
-  "exclude": ["node_modules", ".nuxt", "dist"]
+  "exclude": ["node_modules", ".nuxt", "dist"],
+  "vueCompilerOptions": { "target": 2.7 }
 }

+ 2 - 0
modules/postcss-px-to-viewport.js

@@ -1,5 +1,6 @@
 export default function () {
   this.nuxt.hook('build:before', (nuxt, buildOptions) => {
+    // console.log("🚀 ~ file: postcss-px-to-viewport.js ~ line 3 ~ this.nuxt.hook ~ buildOptions", buildOptions)
     // buildOptions.postcss.plugins;
     // console.log(
     //   '🚀 ~ file: postcss-px-to-viewport.js ~ line 9 ~ this.nuxt.hook ~ buildOptions.postcss.plugins',
@@ -12,6 +13,7 @@ export default function () {
 
     buildOptions.postcss.plugins['postcss-px-to-viewport'] = {
       viewportWidth: 375,
+      unitPrecision: 10,
     };
   });
 }

+ 6 - 2
nuxt.config.js

@@ -70,7 +70,7 @@ export default {
   // Plugins to run before rendering page: https://go.nuxtjs.dev/config-plugins
   plugins: [
     // '~/plugins/api',
-    '~/plugins/file-upload',
+    // '~/plugins/file-upload',
     // '~/plugins/message',
     '~/plugins/toast',
     '~/plugins/error-captured',
@@ -126,6 +126,10 @@ export default {
   ],
   unocss: {
     uno: true,
+    rules: [
+      [/^w-(\d+)rpx$/, ([, d]) => ({ width: `${d * (100 / 375)}vw` })],
+      [/^h-(\d+)rpx$/, ([, d]) => ({ height: `${d * (100 / 375)}vw` })],
+    ],
   },
 
   optimizedImages: {
@@ -140,7 +144,7 @@ export default {
     //   baseURL: '/',
   },
   proxy: {
-    '/api': {
+    [process.env.API_PREFIX]: {
       // target: 'http://gntest.phone.androidscloud.com:1280',
       target: `http${process.env.API_HTTPS === 'true' ? 's' : ''}://${
         process.env.API_HOST

+ 13 - 7
package.json

@@ -1,5 +1,5 @@
 {
-  "name": "android-cloud-H5-nuxt",
+  "name": "android-cloud-h5",
   "version": "1.0.0",
   "private": true,
   "scripts": {
@@ -28,9 +28,11 @@
     "clipboard": "^2.0.11",
     "clipboard-polyfill": "^4.0.0-rc1",
     "clipboardy": "^3.0.0",
+    "copy-to-clipboard": "^3.3.2",
     "core-js": "^3.19.3",
     "dayjs": "^1.11.3",
     "format-number": "^3.0.0",
+    "jmuxer": "^2.0.4",
     "jsencrypt": "^3.2.1",
     "jweixin-module": "^1.6.0",
     "normalize.css": "^8.0.1",
@@ -40,26 +42,27 @@
     "vant": "^2.12.47",
     "vconsole": "^3.14.6",
     "vee-validate": "^3.4.14",
-    "vue": "^2.6.14",
+    "vue": "^2.7.8",
     "vue-data-dict": "^1.0.6",
-    "vue-server-renderer": "^2.6.14",
-    "vue-template-compiler": "^2.6.14",
+    "vue-server-renderer": "^2.7.8",
+    "vue-template-compiler": "^2.7.8",
     "vuetify": "^2.6.1",
     "webpack": "^4.46.0"
   },
   "devDependencies": {
     "@babel/core": "^7",
-    "@babel/eslint-parser": "^7.16.3",
+    "@babel/eslint-parser": "^7.18.2",
     "@commitlint/cli": "^15.0.0",
     "@commitlint/config-conventional": "^15.0.0",
-    "@mdi/font": "^6.7.96",
+    "@mdi/font": "^7.0.96",
     "@nuxtjs/dotenv": "^1.4.1",
     "@nuxtjs/eslint-config": "^10.0.0",
     "@nuxtjs/eslint-module": "^3.0.2",
     "@nuxtjs/style-resources": "^1.2.1",
     "@nuxtjs/stylelint-module": "^4.1.0",
     "@nuxtjs/vuetify": "^1.12.3",
-    "@unocss/nuxt": "^0.39.0",
+    "@unocss/nuxt": "^0.43",
+    "@vue/runtime-dom": "^3.2.37",
     "babel-plugin-import": "^1.13.5",
     "eslint": "^8.4.1",
     "eslint-config-prettier": "^8.3.0",
@@ -68,8 +71,11 @@
     "less": "^4.1.3",
     "less-loader": "^7.3.0",
     "lqip-loader": "^2.2.1",
+    "postcss": "^8",
     "postcss-html": "^1.3.0",
+    "postcss-loader": "^4",
     "postcss-px-to-viewport": "^1.1.1",
+    "postcss-px-to-viewport-8-plugin": "^1.1.5",
     "prettier": "^2.5.1",
     "stylelint": "^14.1.0",
     "stylelint-config-prettier": "^9.0.3",

+ 469 - 156
pages/activity/invite-user/index.vue

@@ -1,65 +1,135 @@
 <template>
-  <v-container class="invite-user pa-0" fluid>
-    <div class="box box1">
-      <div class="box-header">参与步骤</div>
-      <div class="box-main">
-        <v-img
-          class="cybz-content"
-          src="~/assets/image/activity/invite-user/cybz-content@2x.png"
-        />
-        <div class="cybz-content-text">
-          <div class="cybz-content-text-item">分享好友</div>
-          <div class="cybz-content-text-item">邀请购买云机</div>
-          <div class="cybz-content-text-item">返星币换现金</div>
+  <v-container class="invite-user" fluid :class="`bg-${type}`">
+    <activity-invite-user-box class="box1">
+      <template #title>参与步骤</template>
+      <v-img
+        v-if="type === 1"
+        class="cybz-content"
+        src="~/assets/image/activity/invite-user/cybz-content@2x.png"
+      />
+      <v-img
+        v-else-if="type === 2"
+        class="cybz-content"
+        src="~/assets/image/activity/invite-user/cybz-content-2@2x.png"
+      />
+      <div class="cybz-content-text">
+        <div class="cybz-content-text-item">分享好友</div>
+        <div class="cybz-content-text-item">邀请购买云机</div>
+        <div class="cybz-content-text-item">
+          <template v-if="type === 1">返星币换现金</template>
+          <template v-else-if="type === 2">返云手机时长</template>
         </div>
-        <v-btn class="share-button" rounded @click="share()"
-          >分享好友赚星币</v-btn
+      </div>
+      <v-btn class="share-button" rounded @click="share()">
+        <template v-if="!($userAgent.isSzx || $userAgent.isSzxBrowser)"
+          >复制链接</template
+        >分享好友
+      </v-btn>
+      <!-- <button @click="share()">邀请</button> -->
+    </activity-invite-user-box>
+    <activity-invite-user-box class="ox box2">
+      <template #title>收益明细</template>
+      <div class="grid grid-cols-2 gap-x-4 gap-y-8">
+        <div
+          v-for="(item, index) in dataList"
+          :key="index"
+          :class="{
+            'col-span-2': index === 6,
+          }"
         >
-        <!-- <button @click="share()">邀请</button> -->
+          <div class="label text-sm">
+            <span>{{ item.label }}</span>
+          </div>
+          <div class="value text-2xl font-bold">
+            <span>{{ item.value | formatNumber }}</span>
+          </div>
+        </div>
       </div>
-      <!-- <div class="h-40">13</div> -->
-    </div>
-    <div class="box box2">
-      <div class="box-header">收益明细</div>
-      <div class="box-main">
-        <div class="grid grid-cols-2 gap-x-4 gap-y-8">
+    </activity-invite-user-box>
+
+    <activity-invite-user-box v-if="type === 2" class="box3">
+      <template #title>激活码明细</template>
+
+      <div class="code-table">
+        <div class="table-header">
+          <table class="w-full">
+            <colgroup>
+              <col class="time-col" />
+              <col class="code-col" />
+              <col class="status-col" />
+            </colgroup>
+            <thead class="text-left text-sm whitespace-nowrap break-normal">
+              <tr>
+                <th class="">获得的时间</th>
+                <th>激活码编号</th>
+                <th class="">使用状态</th>
+              </tr>
+            </thead>
+          </table>
+        </div>
+        <div class="table-body">
+          <table class="w-full">
+            <colgroup>
+              <col class="time-col" />
+              <col class="code-col" />
+              <col class="status-col" />
+            </colgroup>
+            <tbody class="text-xs">
+              <tr v-for="(item, index) in myActivationCode" :key="index">
+                <td class="whitespace-nowrap break-normal">
+                  {{ item.createTime | formatDate('MM月DD日 HH:mm:ss') }}
+                </td>
+                <td class="whitespace-nowrap break-normal">
+                  <div class="flex">
+                    <span class="font-mono"
+                      >{{ item.activationCode | activationCodeMask }} </span
+                    ><span class="mx-1">|</span
+                    ><v-btn
+                      text
+                      small
+                      color="#991AD2"
+                      class="copy-btn"
+                      @click="copyCode(item)"
+                      >复制</v-btn
+                    >
+                  </div>
+                </td>
+                <td class="whitespace-nowrap break-normal text-right">
+                  <span v-if="item.activationUse" style="color: #dd1b0d"
+                    >已使用</span
+                  >
+                  <span v-else>未使用</span>
+                </td>
+              </tr>
+            </tbody>
+          </table>
           <div
-            v-for="(item, index) in dataList"
-            :key="index"
-            :class="{
-              'col-span-2': index === 6,
-            }"
+            v-if="myActivationCode.length"
+            v-intersect.quiet="codeLoadMoreIntersect"
+            class="flex item-center justify-center"
           >
-            <div class="label text-sm">
-              <span>{{ item.label }}</span>
-            </div>
-            <div class="value text-2xl font-bold">
-              <span>{{ item.value | formatNumber }}</span>
-            </div>
+            <!-- <v-progress-circular indeterminate  ></v-progress-circular> -->
+            <v-btn :loading="codeIsLoading" text small>
+              <template v-if="codeIsLoading">加载中</template>
+              <template v-else-if="codeHasMore">加载更多</template>
+              <template v-else>没有更多</template>
+            </v-btn>
           </div>
         </div>
       </div>
-    </div>
-    <!-- <div class="box box3">
-      <div class="box-header">返利套餐</div>
-      <div class="box-main"></div>
-    </div> -->
-    <div class="box box4">
-      <div class="box-header">活动规则</div>
-      <div class="box-main text-sm" v-html="activityRules">
-        <!-- <p>1、传播过程中有任何疑问,可直接咨询客服</p>
-        <p>
-          2、用户可以通过分享页面或者套餐给想要分享的人,被分享人购买云机套餐后,分享人可获得套餐对应的星币
-        </p>
-        <p>
-          3、星币为双子星云手机平台唯一认可代币,仅能通过充值、赠送和分享获得,其它渠道途径非法获取的星币,不受平台认可
-        </p>
-        <p>4、星币可在双子星购买任一等价值套餐</p>
-        <p>
-          5、请勿利用双子星传播或扩散任何有关于政治、情色等任何违法的信息,一经发现,取消资格,如果触犯任何法律相关问题,平台不负任何责任
-        </p> -->
-      </div>
-    </div>
+      <!-- <div class="code-list">
+          <div
+            v-for="(item, index) in 10"
+            :key="index"
+            class="code-item"
+          ></div>
+        </div> -->
+    </activity-invite-user-box>
+    <activity-invite-user-box class="box4">
+      <template #title>活动规则</template>
+      <!-- eslint-disable-next-line vue/no-v-html -->
+      <div class="text-sm" v-html="activityRules"></div>
+    </activity-invite-user-box>
     <!-- <div class="">invite-user</div> -->
 
     <!-- <button>刷新数据</button> -->
@@ -84,13 +154,32 @@
 <script>
 // import qs from 'qs';
 // import clipboard from 'clipboardy/browser';
-import * as clipboard from 'clipboard-polyfill/text';
-import { getStarCoinOverview } from '~/api/activity/invite-user.js';
-import { getContentByType } from '~/api/public/agreement.js';
+
+// import * as clipboard from 'clipboard-polyfill/text';
+// console.log("🚀 ~ file: index.vue ~ line 176 ~ clipboard", clipboard)
+// import { getStarCoinOverview } from '~/api/activity/invite-user.js';
+// import { getContentByType } from '~/api/public/agreement.js';
 export default {
   // auth: false,
   name: 'InviteUser',
-  filters: {},
+  filters: {
+    activationCodeMask(value) {
+      // 微信环境不支持零宽断言
+      // return value.replace(/(?<=^.{4})(.*)(?=.{4}$)/, '***');
+
+      return value.replace(/(.{4})(.*)(.{4}$)/, '$1***$3');
+    },
+  },
+  // async asyncData({ $axios }) {
+  //   // 页面初始化前触发
+  //   const res = await $axios.$get(
+  //     '/activity/v1/inviteUser/orderRelation/starCoinOverview',
+  //   );
+  //   res.data.type = 2;
+  //   return {
+  //     data: res.data,
+  //   };
+  // },
   data() {
     return {
       data: {
@@ -104,15 +193,28 @@ export default {
         inviteUserName: null,
         activityId: null,
         status: 0,
+        type: 0,
       },
       activityRules: '',
+      myActivationCode: [],
+      myCodeFrom: {
+        activityId: null,
+        pageNum: 1,
+        pageSize: 8,
+      },
+      codeTotal: -1,
+      codeIsLoading: false,
     };
   },
   async fetch() {
-    //
+    // 页面初始化后触发
     try {
-      this.getActivityRules();
-      this.data = (await getStarCoinOverview(this)).data;
+      await this.getActiveInfo();
+      this.$native.setShareInfo(this.shareInfo);
+      await Promise.all([
+        this.getActivityRules(),
+        this.type === 2 && this.getMyActivationCode(this.myCodeFrom),
+      ]);
     } catch (error) {
       this.$toast.error(error.message);
     }
@@ -127,81 +229,209 @@ export default {
     // },
   },
   computed: {
+    codeHasMore() {
+      return (
+        this.codeTotal < 0 ||
+        this.myCodeFrom.pageNum * this.myCodeFrom.pageSize < this.codeTotal
+      );
+    },
     dataList() {
-      return [
-        { label: '今日收益(星币)', value: this.data.todayIncomeStarCoin },
-        { label: '总收益(星币)', value: this.data.totalIncomeStarCoin },
-        { label: '今日购买订单数', value: this.data.todayBuyOrderSuccessCount },
-        { label: '累计推广订单', value: this.data.totalBuyOrderCount },
-        {
-          label: '今日购买成功客户',
-          value: this.data.todayBuyOrderSuccessCount,
-        },
-        {
-          label: '累计购买成功客户',
-          value: this.data.totalBuyOrderSuccessCount,
-        },
-        // { label: '可提现星币数量', value: this.data.withdrawStarCoinNum },
-      ];
+      switch (this.type) {
+        case 1: {
+          return [
+            { label: '今日收益(星币)', value: this.data.todayIncomeStarCoin },
+            { label: '总收益(星币)', value: this.data.totalIncomeStarCoin },
+            {
+              label: '今日购买订单数',
+              value: this.data.todayBuyOrderSuccessCount,
+            },
+            { label: '累计推广订单', value: this.data.totalBuyOrderCount },
+            {
+              label: '今日购买成功客户',
+              value: this.data.todayBuyOrderSuccessCount,
+            },
+            {
+              label: '累计购买成功客户',
+              value: this.data.totalBuyOrderSuccessCount,
+            },
+            // { label: '可提现星币数量', value: this.data.withdrawStarCoinNum },
+          ];
+        }
+        case 2: {
+          return [
+            {
+              label: '今日获取的激活码',
+              value: this.data.todayIncomeActivationCode,
+            },
+            { label: '总激活码', value: this.data.totalIncomeActivationCode },
+            {
+              label: '今日购买订单数',
+              value: this.data.todayBuyOrderSuccessCount,
+            },
+            { label: '累计推广订单', value: this.data.totalBuyOrderCount },
+            {
+              label: '今日购买成功客户',
+              value: this.data.todayBuyOrderSuccessCount,
+            },
+            {
+              label: '累计购买成功客户',
+              value: this.data.totalBuyOrderSuccessCount,
+            },
+            // { label: '可提现星币数量', value: this.data.withdrawStarCoinNum },
+          ];
+        }
+        default: {
+          return [];
+        }
+      }
+    },
+    type() {
+      return this.data.type;
+    },
+    shareUrl() {
+      return (
+        location.origin +
+        this.$router.resolve({
+          path: '/activity/invite-user/register',
+          query: {
+            invitationUserName: this.data.inviteUserName,
+            activityId: this.data.activityId,
+            type: this.type,
+          },
+        }).href
+      );
+    },
+    shareInfo() {
+      if (this.$userAgent.isMiniProgram) {
+        return {
+          title: '双子星APP',
+          path: '/pages/home/home',
+          imgUrl: location.origin + require('~/assets/image/logo.png'),
+        };
+      }
+      return {
+        title: '双子星APP',
+        desc: '分享好友购买云机套餐,返星币换现金',
+        link: this.shareUrl,
+        imgUrl: location.origin + require('~/assets/image/logo.png'),
+      };
     },
   },
   mounted() {
-    this.$userAgent.isMiniProgram &&
-      this.$wx.miniProgram.postMessage({
-        data: {
-          action: 'updateAppMessageShareData',
-          params: {
-            title: '双子星云手机',
-            path: '/pages/home/home',
-            imageUrl:
-              location.origin +
-              require('~/assets/image/activity/invite-user/bg@2x.png'),
-            // promise: null,
-          },
-        },
-      });
+    // this.$userAgent.isMiniProgram &&
+    //   this.$wx.miniProgram.postMessage({
+    //     data: {
+    //       action: 'updateAppMessageShareData',
+    //       params: {
+    //         title: '双子星云手机',
+    //         path: '/pages/home/home',
+    //         imageUrl:
+    //           location.origin +
+    //           require('~/assets/image/activity/invite-user/bg@2x.png'),
+    //         // promise: null,
+    //       },
+    //     },
+    //   });
   },
   methods: {
+    async getActiveInfo() {
+      const res = await this.$axios.$get(
+        '/activity/v1/inviteUser/orderRelation/starCoinOverview',
+      );
+      // res.data.type = 2;
+      this.data = res.data;
+      this.myCodeFrom.activityId = res.data.activityId;
+    },
+    async getMyActivationCode(params) {
+      try {
+        this.codeIsLoading = true;
+
+        const res = await this.$axios.$get(
+          '/activity/v1/inviteUser/orderRelation/myActivationCode',
+          {
+            params,
+          },
+        );
+        this.codeTotal = res.data.total;
+
+        this.myActivationCode.push(
+          ...(res.data.list ?? []),
+          // ...Array.from({ length: 8 }).fill({
+          //   createTime: '2022-08-01 14:33:11',
+          //   activationCode: 'XXXXXXXXXXXXXXXXXXX',
+          //   activationUse: 1,
+          // }),
+        );
+
+        this.myCodeFrom = params;
+      } finally {
+        this.codeIsLoading = false;
+      }
+    },
     async share() {
       // console.log(this);
       if (this.data.status !== 1) {
-        throw new Error('当前活动已失效');
+        if (this.data.status === 0) {
+          throw new Error('当前活动已过期');
+        }
+        if (this.data.status === 2) {
+          throw new Error('当前活动未开启');
+        }
+        if (this.data.status === 3) {
+          throw new Error('当前活动已结束');
+        }
+        throw new Error('未知活动状态');
       }
+
       this.$tongji.trackEvent('活动', '分享', '', 0);
-      const url =
-        location.origin +
-        this.$router.resolve({
-          path: '/activity/invite-user/register',
-          query: {
-            invitationUserName: this.data.inviteUserName,
-            activityId: this.data.activityId,
-          },
-        }).href;
 
-      if (this.$userAgent.isMiniProgram) {
-        // 小程序环境
-        await clipboard.writeText(url);
-        this.$toast.success('链接复制成功');
-      } else if (this.$userAgent.isApp) {
+      // if (this.$userAgent.isMiniProgram) {
+      //   // 小程序环境
+      //   await clipboard.writeText(`${this.shareInfo.gotoUrl} 唔即云手机`);
+      //   this.$toast.success('链接复制成功');
+      // } else
+      if (this.$userAgent.isSzx || this.$userAgent.isSzxBrowser) {
         // app环境
-        this.$native.share({
-          title: '双子星APP',
-          content: '分享好友购买云机套餐,返星币换现金',
-          gotoUrl: url,
-          shareImg: 'sdsa.png',
-        });
+        this.$native.share(this.shareInfo);
       } else {
         // 浏览器环境
-        await clipboard.writeText(url);
+
+        await this.$native.clipboard.writeText(`${this.shareUrl} 双子星云手机`);
         // throw new Error('1231');
         this.$toast.success('链接复制成功');
       }
     },
     async getActivityRules() {
-      const res = await getContentByType(this, 'iuserrules01');
-      this.activityRules = res.data.content
-        .replace(/[\d\D]*<body>([\d\D]+)<\/body>[\d\D]*/i, '$1')
-        .replace(/<div class="phone-container">([\d\D]+)<\/div>/g, '$1');
+      const agreementCoding = { 1: 'iuserrules01', 2: 'iuserrules02' }[
+        this.type
+      ];
+      if (agreementCoding) {
+        const res = await this.$axios.$get(
+          '/public/v5/agreementApi/content/getContentByType',
+          {
+            params: {
+              agreementCoding,
+              type: 1,
+            },
+          },
+        );
+
+        this.activityRules = res.data.content
+          .replace(/[\d\D]*<body>([\d\D]+)<\/body>[\d\D]*/i, '$1')
+          .replace(/<div class="phone-container">([\d\D]+)<\/div>/g, '$1');
+      }
+    },
+    async copyCode(item) {
+      await this.$native.clipboard.writeText(item.activationCode);
+      this.$toast.success('复制成功');
+    },
+    codeLoadMoreIntersect(event) {
+      if (event[0].isIntersecting && this.codeHasMore && !this.codeIsLoading) {
+        this.getMyActivationCode({
+          ...this.myCodeFrom,
+          pageNum: this.myCodeFrom.pageNum + 1,
+        });
+      }
     },
   },
 };
@@ -210,49 +440,89 @@ export default {
 <style lang="scss" scoped>
 .invite-user {
   color: #333;
-  background-image: url('~/assets/image/activity/invite-user/bg@2x.png');
+  // background-image: url('~/assets/image/activity/invite-user/bg@2x.png');
   background-size: 100% auto;
-  background-position-y: -44px;
+  // background-position-y: -44px;
   overflow: hidden;
-  padding-bottom: 30px;
-}
-.box {
-  width: 373px;
-  box-sizing: border-box;
-  margin: auto;
-  border-image-source: url('~/assets/image/activity/invite-user/box@2x.png');
-  border-image-slice: 38 * 2 20 * 2 30 * 2 fill;
-
-  // border-image-width: 200px;
-  // border-image-slice: 200%;
-  border-width: 38px 15px 15px;
-  // border-width: 1px;
-  border-style: solid;
-  position: relative;
-  + .box {
-    margin-top: 30px;
-  }
-  .box-header {
-    position: absolute;
-    top: -30px;
-    // left: 141px;
-    left: 0;
-    right: 0;
-    text-align: center;
-    padding: 0 130px;
-    color: #fff;
+  padding: 0;
+  // padding-bottom: 30px;
+  padding-bottom: env(safe-area-inset-bottom, 30px);
+  background-color: #9525e3;
+
+  &.bg-1 {
+    background-image: url('~/assets/image/activity/invite-user/bg@2x.png');
   }
-  .box-main {
-    padding: 30px 15px 20px;
+  &.bg-2 {
+    background-image: url('~/assets/image/activity/invite-user/bg-2@2x.png');
   }
 }
+
+// .box {
+//   width: 373px;
+//   box-sizing: border-box;
+//   margin: auto;
+
+//   // border-image-width: 200px;
+//   // border-image-slice: 200%;
+//   // border-width: 1px;
+//   position: relative;
+//   z-index: 0;
+
+//   + .box {
+//     margin-top: 30px;
+//   }
+
+//   .box-header {
+//     // position: absolute;
+//     position: relative;
+//     // top: -30px;
+//     z-index: 1;
+//     // left: 141px;
+//     // left: 0;
+//     // right: 0;
+//     margin-bottom: -34px;
+//     // text-align: center;
+//     // padding: 0 130px;
+//     display: flex;
+//     align-items: center;
+//     justify-content: center;
+//     flex-direction: column;
+//     .box-header-content {
+//       color: #fff;
+//       border-image-source: url('~/assets/image/activity/invite-user/box-title@2x.png');
+//       border-image-slice: 0 140 fill;
+//       border-width: 0 70px 0;
+//       border-style: solid;
+//       height: 38px;
+//       display: flex;
+//       align-items: center;
+//       justify-content: center;
+//       flex-direction: column;
+//       // width: auto;
+//     }
+//   }
+
+//   .box-main {
+//     z-index: 0;
+//     // padding: 30px 15px 20px;
+//     border-image-source: url('~/assets/image/activity/invite-user/box-bg@2x.png');
+//     border-image-slice: 50 60 80 fill;
+//     border-image-width: 30px 30px 40px;
+//     border-width: 50px 20px 40px;
+//     border-style: solid;
+//     // padding-top: 30px;
+//   }
+// }
+
 .box1 {
   margin-top: 275px;
-  .box-main {
-    // padding: 30px 15px 20px;
-    padding-left: 0;
-    padding-right: 0;
-  }
+
+  // ::v-deep .box-main {
+  //   // padding: 30px 15px 20px;
+  //   padding-left: 0;
+  //   padding-right: 0;
+  // }
+
   .cybz-content {
     width: 305px;
     height: 75px;
@@ -260,6 +530,7 @@ export default {
     display: block;
     // margin-top: 30px;
   }
+
   .cybz-content-text {
     // display: flex;
     display: grid;
@@ -271,12 +542,14 @@ export default {
     grid-template-columns: 1fr 1fr 1fr;
     font-size: 12px;
     color: #333;
+
     .cybz-content-text-item {
       // width: 0;
       // flex: auto;
       text-align: center;
     }
   }
+
   .share-button {
     display: block;
     margin: auto;
@@ -291,25 +564,65 @@ export default {
     font-weight: bold;
   }
 }
+
 .box2 {
   .label {
     // font-size: 14px;
   }
+
   .value {
     color: #ff6600;
     // font-size: 24px;
   }
-  .box-main {
+
+  ::v-deep .box-main {
     padding-left: 20px;
     padding-right: 20px;
   }
 }
-.box4 {
-  .box-main {
+
+.box3 {
+  ::v-deep .box-main {
     // padding-left: 20px;
     // padding-top: 0;
+    padding-left: 10px;
+    padding-right: 10px;
   }
 }
-</style>
-
+.code-table {
+  .table-body {
+    max-height: 30px * 8;
+    overflow-y: auto;
+  }
+  .time-col {
+    width: 120px;
+  }
+  .status-col {
+    width: 60px;
+  }
 
+  // width: 100%;
+  tr {
+    height: 30px;
+    color: #666;
+  }
+  td {
+    height: 30px;
+    color: #333;
+  }
+  // tbody {
+  //   // max-height: 200px;
+  //   // overflow-y: auto;
+  //   // display: block;
+  // }
+  .copy-btn {
+    min-width: 0 !important;
+    height: auto !important;
+    padding: 0 !important;
+  }
+}
+.box4::v-deep .box-main {
+  padding-left: 10px;
+  padding-right: 10px;
+}
+</style>

+ 112 - 106
pages/activity/invite-user/register.vue

@@ -1,80 +1,78 @@
 <template>
-  <v-container class="register-for-invite pa-0" fluid>
-    <div class="box box1">
-      <div class="box-header">快速注册</div>
-      <div class="box-main">
-        <validation-observer ref="observer" slim>
-          <v-form @submit.prevent="submit()">
-            <validation-provider
-              v-slot="{ errors }"
-              ref="providerPhome"
-              name="手机号码"
-              rules="phone"
-              slim
+  <v-container class="register-for-invite pa-0" fluid :class="`type-${type}`">
+    <activity-invite-user-box class="box1">
+      <template #title>快速注册</template>
+      <validation-observer ref="observer" slim>
+        <v-form @submit.prevent="submit()">
+          <validation-provider
+            v-slot="{ errors }"
+            ref="providerPhome"
+            name="手机号码"
+            rules="phone"
+            slim
+          >
+            <v-text-field
+              v-model="form.phone"
+              v-mask="$mask.phone"
+              label=""
+              name="phone"
+              required
+              :error-messages="errors"
+              placeholder="请输入11位手机号"
+              solo
+              type="tel"
+              flat
+              maxlength="11"
+            />
+          </validation-provider>
+          <validation-provider
+            v-slot="{ errors }"
+            name="短信验证码"
+            rules="code"
+            slim
+          >
+            <v-text-field
+              v-model="form.code"
+              v-mask="'######'"
+              label=""
+              :error-messages="errors"
+              name="code"
+              maxlength="6"
+              required
+              placeholder="请输入验证码"
+              solo
+              flat
+              type="tel"
             >
-              <v-text-field
-                v-model="form.phone"
-                v-mask="$mask.phone"
-                label=""
-                name="phone"
-                required
-                :error-messages="errors"
-                placeholder="请输入11位手机号"
-                solo
-                type="tel"
-                flat
-                maxlength="11"
-              />
-            </validation-provider>
-            <validation-provider
-              v-slot="{ errors }"
-              name="短信验证码"
-              rules="code"
-              slim
+              <template #append>
+                <v-btn
+                  :loading="codeSending"
+                  :disabled="codeTime > 0"
+                  small
+                  depressed
+                  color="#8027E5"
+                  plain
+                  class="text-base"
+                  @click="sendSmsCode()"
+                >
+                  <template v-if="codeTime > 0">{{ codeTime }}s</template>
+                  <template v-else>发送验证码</template>
+                </v-btn>
+              </template>
+            </v-text-field>
+          </validation-provider>
+          <div class="submit-btn-box">
+            <v-btn
+              type="submit"
+              :loading="submitting"
+              class="submit-btn"
+              rounded
+              >注册并下载APP</v-btn
             >
-              <v-text-field
-                v-model="form.code"
-                v-mask="'######'"
-                label=""
-                :error-messages="errors"
-                name="code"
-                maxlength="6"
-                required
-                placeholder="请输入验证码"
-                solo
-                flat
-                type="tel"
-              >
-                <template #append>
-                  <v-btn
-                    :loading="codeSending"
-                    :disabled="codeTime > 0"
-                    small
-                    depressed
-                    color="#8027E5"
-                    plain
-                    class="text-base"
-                    @click="sendSmsCode()"
-                  >
-                    <template v-if="codeTime > 0">{{ codeTime }}s</template>
-                    <template v-else>发送验证码</template>
-                  </v-btn>
-                </template>
-              </v-text-field>
-            </validation-provider>
-            <div class="submit-btn-box">
-              <v-btn
-                type="submit"
-                :loading="submitting"
-                class="submit-btn"
-                rounded
-                >注册并下载APP</v-btn
-              >
-            </div>
-          </v-form>
-        </validation-observer>
-      </div>
-    </div>
+          </div>
+        </v-form>
+      </validation-observer>
+    </activity-invite-user-box>
   </v-container>
 </template>
 
@@ -104,6 +102,11 @@ export default {
   head: {
     title: '注册',
   },
+  computed: {
+    type() {
+      return Number(this.$route.query.type);
+    },
+  },
   watch: {},
   methods: {
     maskPhone(value) {
@@ -153,47 +156,51 @@ export default {
 };
 </script>
 
-
 <style lang="scss" scoped>
 .register-for-invite {
   color: #333;
-  background-image: url('~/assets/image/activity/invite-user/bg@2x.png');
   background-size: 100% auto;
-  background-position-y: -44px;
+  // background-position-y: -44px;
   overflow: hidden;
   padding-bottom: 30px;
   min-height: 100vh;
-}
-.box {
-  width: 373px;
-  box-sizing: border-box;
-  margin: auto;
-  border-image-source: url('~/assets/image/activity/invite-user/box@2x.png');
-  border-image-slice: 38 * 2 20 * 2 30 * 2 fill;
-
-  // border-image-width: 200px;
-  // border-image-slice: 200%;
-  border-width: 38px 15px 15px;
-  // border-width: 1px;
-  border-style: solid;
-  position: relative;
-  + .box {
-    margin-top: 30px;
-  }
-  .box-header {
-    position: absolute;
-    top: -30px;
-    // left: 141px;
-    left: 0;
-    right: 0;
-    text-align: center;
-    padding: 0 130px;
-    color: #fff;
+  &.type-1 {
+    background-image: url('~/assets/image/activity/invite-user/bg@2x.png');
   }
-  .box-main {
-    padding: 30px 15px 20px;
+  &.type-2 {
+    background-image: url('~/assets/image/activity/invite-user/bg-2@2x.png');
   }
 }
+// .box {
+//   width: 373px;
+//   box-sizing: border-box;
+//   margin: auto;
+//   // border-image-source: url('~/assets/image/activity/invite-user/box@2x.png');
+//   border-image-slice: 38 * 2 20 * 2 30 * 2 fill;
+
+//   // border-image-width: 200px;
+//   // border-image-slice: 200%;
+//   border-width: 38px 15px 15px;
+//   // border-width: 1px;
+//   border-style: solid;
+//   position: relative;
+//   + .box {
+//     margin-top: 30px;
+//   }
+//   .box-header {
+//     position: absolute;
+//     top: -30px;
+//     // left: 141px;
+//     left: 0;
+//     right: 0;
+//     text-align: center;
+//     padding: 0 130px;
+//     color: #fff;
+//   }
+//   .box-main {
+//     padding: 30px 15px 20px;
+//   }
+// }
 .box1 {
   margin-top: 275px;
 }
@@ -216,4 +223,3 @@ export default {
   font-weight: bold !important;
 }
 </style>
-

+ 112 - 0
pages/customer-service.vue

@@ -0,0 +1,112 @@
+<template lang="">
+  <div>
+    <!-- <v-btn @click="open({ templateId: 6635862 })">双子星模板</v-btn>
+    <v-btn @click="open({ templateId: 6634867 })">唔即云模板</v-btn> -->
+  </div>
+</template>
+<script>
+import qs from 'qs';
+import { fileKeyToUrl } from '@/plugins/file-center.js';
+console.log(
+  '🚀 ~ file: customer-service.vue ~ line 10 ~ fileKeyToUrl',
+  fileKeyToUrl,
+);
+
+export default {
+  head() {
+    return {
+      script: [
+        {
+          src:
+            'https://qiyukf.com/script/98112bcf552907c28ee450c6a58269c3.js' +
+            qs.stringify(
+              {
+                hidden: 1,
+                sdkTemplateId:
+                  this.$route.query?.templateId ??
+                  process.env.QIYUKF_TEMPLATE_ID,
+              },
+              {
+                addQueryPrefix: true,
+              },
+            ),
+          async: true,
+          callback: () => {
+            // console.log(window.ysf);
+            this.init(window.ysf);
+          },
+        },
+      ],
+    };
+  },
+  computed: {
+    isAutoOpen() {
+      return this.$route.query.autoopen === 'true';
+    },
+    isOnunread() {
+      return this.$route.query.onunread === 'true';
+    },
+  },
+  mounted() {},
+  methods: {
+    async init(ysf) {
+      this.ysf = ysf;
+      // console.log(ysf);
+
+      await new Promise((resolve, reject) => {
+        ysf('onready', resolve);
+      });
+
+      await new Promise((resolve, reject) => {
+        ysf('config', {
+          uid: String(this.$auth.user.id),
+          name: this.$auth.user.userName,
+          mobile: this.$auth.user.phone,
+          email: this.$auth.user.email,
+          data: JSON.stringify([
+            { key: 'real_name', value: this.$auth.user.userName },
+            { key: 'mobile_phone', hidden: true, value: this.$auth.user.phone },
+            { key: 'email', value: this.$auth.user.email },
+            {
+              key: 'avatar',
+              label: '头像',
+              value:
+                this.$auth.user.headImgFileId &&
+                fileKeyToUrl(this.$auth.user.headImgFileId),
+            }, // 访客头像
+            {
+              index: 0,
+              key: 'account',
+              label: '账号',
+              value: this.$auth.user.phone,
+            },
+          ]),
+          language: 'zh-cn',
+          success: resolve,
+          error: reject,
+        });
+      });
+      if (this.isAutoOpen) {
+        this.open();
+      }
+      if (this.isOnunread) {
+        this.onunread();
+      }
+    },
+    open() {
+      const url = this.ysf('url');
+      location.replace(url);
+      // this.ysf('open', data);
+    },
+    getUnreadMsg() {
+      const result = this.ysf('getUnreadMsg');
+      console.log(result.total, result.type, result.message);
+    },
+    onunread() {
+      this.ysf('onunread', (result) => {
+        console.log(result.total, result.type, result.message);
+      });
+    },
+  },
+};
+</script>

+ 1 - 9
pages/index.vue

@@ -3,18 +3,10 @@
     <div>
       <div>
         <v-btn to="/inspire" nuxt>inspire</v-btn>
+        <v-btn to="/phone" nuxt>phoneList</v-btn>
       </div>
     </div>
     <div class="">{{ FILE_PREFIX }}</div>
-    <img
-      :src="
-        $file.getUrl({
-          fileKey: 'LowLevelMultipartUpload_18706407955405619211',
-          type: 'popup',
-        })
-      "
-      alt=""
-    />
   </div>
 </template>
 

+ 5 - 2
pages/login.vue

@@ -11,10 +11,11 @@
         >
           <v-text-field
             v-model="form.phone"
-            label="手机号"
+            label="手机号"
             name="phone"
             required
             :error-messages="errors"
+            placeholder="请输入"
             maxlength="11"
             type="tel"
           />
@@ -28,7 +29,8 @@
         >
           <v-text-field
             v-model="form.password"
-            label="手机号码"
+            label="密码"
+            placeholder="请输入"
             name="phone"
             required
             :error-messages="errors"
@@ -47,6 +49,7 @@
 
 <script>
 export default {
+  auth: 'guest',
   name: 'LoginPage',
   data() {
     return {

+ 74 - 0
pages/phone/_id.vue

@@ -0,0 +1,74 @@
+<template>
+  <div class="disk-page">
+    <disk :user-card-id="userCardId"></disk>
+  </div>
+</template>
+
+<script>
+import qs from 'qs';
+const isNew = false;
+export default {
+  name: 'DiskPage',
+  middleware: [
+    async ({ base, route, $axios, $auth, redirect, $userAgent }) => {
+      if (isNew) {
+        return;
+      }
+      const res = await $axios.$get(
+        '/resources/v5/client/disk/info/userCard/single',
+        {
+          params: {
+            userCardId: route.params.id,
+          },
+        },
+      );
+      const {
+        userCardId,
+        userName,
+        room,
+        buyVipType,
+        sourceType,
+        authPhone,
+        validTime,
+        id,
+      } = res.data;
+      const token = $auth.strategy.token.get();
+      const url = `${location.origin}${base}${
+        $userAgent.isIos ? 'screenIos' : 'screenAndroid'
+      }/WXtrialInterface.html${qs.stringify(
+        {
+          userCardId,
+          username: $auth.user.username,
+          token,
+          rm: room,
+          mealType: buyVipType,
+          sourceType,
+          authPhone: authPhone ? 'huo' : 'none',
+          validTime,
+          record: id,
+        },
+        {
+          addQueryPrefix: true,
+        },
+      )}`;
+      redirect(url);
+    },
+  ],
+  data() {
+    return {
+      userCardId: null,
+    };
+  },
+  fetch() {
+    this.userCardId = +this.$route.params.id;
+  },
+  methods: {},
+};
+</script>
+
+<style lang="scss" scoped>
+.disk-page {
+  height: 100vh;
+  position: relative;
+}
+</style>

+ 37 - 0
pages/phone/index.vue

@@ -0,0 +1,37 @@
+<template>
+  <div class="">
+    <div class="disk-list">
+      <div v-for="item in diskList" :key="item.id" class="disk-item">
+        <v-btn
+          :to="{
+            name: 'phone-id',
+            params: {
+              id: item.id,
+            },
+          }"
+          >{{ item.diskName }}</v-btn
+        >
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  data() {
+    return {
+      diskList: [],
+    };
+  },
+  async fetch() {
+    await this.getDiskList();
+  },
+  methods: {
+    async getDiskList() {
+      const res = await this.$axios.$get('/resources/v5/client/disk/info');
+      this.diskList = res.data.diskInfo;
+    },
+  },
+};
+</script>
+

+ 59 - 0
pages/test/ios-post-message.vue

@@ -0,0 +1,59 @@
+<template lang="">
+  <div>
+    <div class="">
+      <div class="">window.webkit: {{ hasWebkit }}</div>
+      <div class="">
+        window.webkit.messageHandlers: {{ hasMessageHandlers }}
+      </div>
+      <div class="">
+        window.webkit.messageHandlers.setShareInfo: {{ hasSetShareInfo }}
+      </div>
+      <div class="">
+        window.webkit.messageHandlers.openShare: {{ hasOpenShare }}
+      </div>
+      <div class="">
+        <div class="">messageHandlersKeys {{ messageHandlersKeys.length }}</div>
+        <div v-for="item in messageHandlersKeys" :key="item">
+          {{ item }}
+        </div>
+      </div>
+    </div>
+    <v-btn @click="setShareInfo">setShareInfo</v-btn>
+    <v-btn @click="openShare">openShare</v-btn>
+  </div>
+</template>
+<script>
+export default {
+  auth: false,
+  data() {
+    return {
+      hasWebkit: !!window?.webkit,
+      hasMessageHandlers: !!window?.webkit?.messageHandlers,
+      hasSetShareInfo: !!window?.webkit?.messageHandlers?.setShareInfo,
+      hasOpenShare: !!window?.webkit?.messageHandlers?.openShare,
+      messageHandlersKeys: Object.keys(window?.webkit?.messageHandlers ?? {}),
+    };
+  },
+  fetch() {
+    try {
+      this.setShareInfo();
+    } catch (error) {}
+  },
+  mounted() {
+    // this.
+  },
+  methods: {
+    setShareInfo() {
+      window.webkit.messageHandlers.setShareInfo.postMessage({
+        title: '邀请好友赚星币',
+        gotoUrl: 'http://www.baidu.com',
+        shareImg:
+          'http://gntest.phone.androidscloud.com:1280/cloud/phone/web/static/img/logo.c898a0c6.png',
+      });
+    },
+    openShare() {
+      window.webkit.messageHandlers.openShare.postMessage();
+    },
+  },
+};
+</script>

+ 1 - 0
plugins/auth.js

@@ -1,5 +1,6 @@
 export default async function ({ $auth, query, error }) {
   if (query.token) {
+    console.log("🚀 ~ file: auth.js ~ line 3 ~ query.token", query.token)
     // 请求携带了token
     try {
       await $auth.setUserToken(query.token);

+ 1 - 0
plugins/axios.js

@@ -8,6 +8,7 @@ export default function ({ $axios, $auth, redirect }) {
     return config;
   });
   $axios.onResponse(async (response) => {
+    // console.log("🚀 ~ file: axios.js ~ line 11 ~ $axios.onResponse ~ response", response)
     if (Object.prototype.toString.call(response.data) === '[object Object]') {
       if ([6013, 6014].includes(response.data.status)) {
         // await $auth.logout();

+ 1 - 1
plugins/baidu-tongji.js

@@ -6,7 +6,7 @@ export default function (c, i) {
   window._hmt.push(['_setCustomVar', 1, 'MODE', process.env.MODE, 1]);
   app.router.afterEach((to, from) => {
     // 上报PV
-    window._hmt.push(['_trackPageview', to.fullPath]);
+    window._hmt.push(['_trackPageview', c.base + to.path.slice(1)]);
   });
 
   app.head.script = app.head.script || [];

+ 5 - 3
plugins/callapp.js

@@ -1,12 +1,14 @@
 import CallApp from 'callapp-lib';
 
 export default function ({ $userAgent }, i) {
-  const scheme = $userAgent.isIos ? 'szxiOSApp' : 'opengeminiapp';
+  const szxScheme = $userAgent.isIos
+    ? process.env.SZX_APP_IOS_SCHEME
+    : process.env.SZX_APP_ANDROID_SCHEME;
   i(
     'callSzx',
     new CallApp({
-      scheme: { protocol: scheme, host: '' },
-      intent: { package: 'com.gemini.cloud.client', scheme },
+      scheme: { protocol: szxScheme, host: '' },
+      intent: { package: 'com.gemini.cloud.client', szxScheme },
       timeout: 2000,
       yingyongbao:
         'https://a.app.qq.com/o/simple.jsp?pkgname=com.gemini.cloud.client',

+ 3 - 1
plugins/error-captured.js

@@ -3,7 +3,9 @@
 export default function ({ app, $message }) {
   app.errorCaptured = (err, vm, info) => {
     if (process.client) {
-      // console.error(err);
+      if (process.env.NODE_ENV === 'development') {
+        console.error(err);
+      }
       // $message.error({ content: err.message });
       // vm.$toast.fail({ message: err.message, position: 'bottom' });
       vm.$toast.error({ message: err.message });

+ 17 - 0
plugins/file-center.js

@@ -0,0 +1,17 @@
+import qs from 'qs';
+
+let baseURL;
+if (process.env.NODE_ENV === 'development') {
+  // 开发环境通过本地代理
+  baseURL = process.env.FILE_PREFIX;
+} else {
+  // 部署环境通过域名直连
+  baseURL = `http${process.env.FILE_PORT === '443' ? 's' : ''}://${
+    process.env.FILE_HOST
+  }:${process.env.FILE_PORT}/${process.env.FILE_PREFIX}`;
+}
+
+export const fileKeyToUrl = (fileKey, params = {}) =>
+  `${baseURL}/document/newFile/download/0/${
+    process.env.FILE_UPLOAD_KEY
+  }${qs.stringify({ fileKey, ...params }, { addQueryPrefix: true })}`;

+ 0 - 31
plugins/file-upload.js

@@ -1,31 +0,0 @@
-import qs from 'qs';
-export default function ({ $axios }, inject) {
-  let baseURL;
-  if (process.env.NODE_ENV === 'development') {
-    // 开发环境通过本地代理
-    baseURL = process.env.FILE_PREFIX;
-  } else {
-    // 部署环境通过域名直连
-    baseURL = `http${process.env.FILE_PORT === '443' ? 's' : ''}://${
-      process.env.FILE_HOST
-    }:${process.env.FILE_PORT}/${process.env.FILE_PREFIX}`;
-  }
-  const fileUploadServer = $axios.create({
-    baseURL,
-    headers: {},
-  });
-
-  // fileUploadServer.setBaseURL(process.env.FILE_PREFIX);
-  // console.dir(fileUploadServer);
-
-  inject('file', {
-    upload(file) {
-      throw new Error('开发中');
-    },
-    getUrl(params) {
-      return `${baseURL}/document/newFile/download/0/${
-        process.env.FILE_UPLOAD_KEY
-      }${qs.stringify(params, { addQueryPrefix: true })}`;
-    },
-  });
-}

+ 1 - 0
plugins/jweixin.js

@@ -1,4 +1,5 @@
 import wx from 'jweixin-module';
+// console.log("🚀 ~ file: jweixin.js ~ line 2 ~ wx", wx)
 
 // import Vue from 'vue';
 // import { getConfig } from '~/api/wx/index.js';

+ 79 - 19
plugins/native.js

@@ -1,27 +1,87 @@
-export default function ({ $userAgent }, i) {
-  const before = () => {
-    if ($userAgent.isApp) {
-      return;
-    }
-    //  else if ($userAgent.isMiniProgram) {
-    //   throw new Error('小程序环境不支持');
-    // }
-    throw new Error('非App环境');
-  };
+// import * as clipboard from 'clipboard-polyfill/text';
+import copy from 'copy-to-clipboard';
+console.log("🚀 ~ file: native.js ~ line 3 ~ copy", copy)
+export default function ({ $userAgent, $wx }, i) {
+  // const before = () => {
+  //   if ($userAgent.isApp) {
+  //     return;
+  //   }
+  //   //  else if ($userAgent.isMiniProgram) {
+  //   //   throw new Error('小程序环境不支持');
+  //   // }
+  //   throw new Error('非App环境');
+  // };
 
   i('native', {
-    share({ title, content, gotoUrl, shareImg }) {
-      before();
-      if ($userAgent.isIos) {
-        window.webkit.messageHandlers.share.postMessage({
+    async share({ title, desc, link, imgUrl }) {
+      // before();
+      if ($userAgent.isSzx) {
+        if ($userAgent.isIos) {
+          await window.webkit.messageHandlers.share.postMessage({
+            title,
+            content: desc,
+            gotoUrl: link,
+            shareImg: imgUrl,
+          });
+          return;
+        }
+
+        if ($userAgent.isAndroid) {
+          await window.native.share(title, desc, link, imgUrl);
+          return;
+        }
+        return;
+      }
+
+      if ($userAgent.isSzxBrowser) {
+        await this.setShareInfo({ title, desc, link, imgUrl });
+        await window.webkit.messageHandlers.openShare.postMessage({});
+      }
+    },
+    async setShareInfo({ title, desc, link, imgUrl, path }) {
+      if ($userAgent.isSzxBrowser) {
+        // before();
+        await window.webkit.messageHandlers.setShareInfo.postMessage({
           title,
-          content,
-          gotoUrl,
-          shareImg,
+          // content,
+          gotoUrl: link,
+          shareImg: imgUrl,
         });
-      } else {
-        window.native.share(title, content, gotoUrl, shareImg);
+        return;
       }
+
+      if ($userAgent.isMiniProgram) {
+        await $wx?.miniProgram?.postMessage({
+          data: {
+            action: 'updateAppMessageShareData',
+            params: {
+              title,
+              path,
+              imageUrl: imgUrl,
+            },
+          },
+        });
+      }
+      // if ($userAgent.isIos) {
+      // } else {
+      //   window.native.setShareUrl(url);
+      // }
+    },
+    clipboard: {
+      /**
+       *
+       * @param {string} text
+       * @returns {Promise<any>}
+       */
+      async writeText(text) {
+        if ($userAgent.isSzx && $userAgent.isAndroid) {
+          return await window.native.copyToClipboard(text);
+        }
+        return await copy(text, {
+          format: 'text/plain',
+        });
+        // return await clipboard.writeText(text);
+      },
     },
   });
 }

+ 5 - 2
plugins/user-agent.js

@@ -6,8 +6,8 @@ export default function (c, i) {
     get isAndroid() {
       return /Android/i.test(navigator.userAgent);
     },
-    get isApp() {
-      return /Szx/i.test(navigator.userAgent);
+    get isSzx() {
+      return /Szx\//i.test(navigator.userAgent);
     },
     get isMobile() {
       return /Mobile/i.test(navigator.userAgent);
@@ -18,5 +18,8 @@ export default function (c, i) {
     get isMiniProgram() {
       return /miniProgram/i.test(navigator.userAgent);
     },
+    get isSzxBrowser() {
+      return /SZXBrowser\//i.test(navigator.userAgent);
+    },
   });
 }

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 973 - 645
pnpm-lock.yaml


+ 52 - 1
schemes/password.js

@@ -4,7 +4,22 @@ import { LocalScheme } from '~auth/runtime';
 // const { createHash } = await import('node:crypto');
 let publicKey;
 const encrypt = new JSEncrypt();
-
+function getProp(holder, propName) {
+  if (!propName || !holder || typeof holder !== 'object') {
+    return holder;
+  }
+  if (propName in holder) {
+    return holder[propName];
+  }
+  const propParts = Array.isArray(propName)
+    ? propName
+    : (propName + '').split('.');
+  let result = holder;
+  while (propParts.length && result) {
+    result = result[propParts.shift()];
+  }
+  return result;
+}
 export default class CustomScheme extends LocalScheme {
   async login(data, { reset = true } = {}) {
     const endpoint = { data: Object.assign({}, data) };
@@ -53,6 +68,8 @@ export default class CustomScheme extends LocalScheme {
       this.options.endpoints.login,
     );
     this.updateTokens(response);
+    // localStorage.setItem('auth.username', response.data.data.username);
+
     if (!this.requestHandler.interceptor) {
       this.initializeRequestInterceptor();
     }
@@ -61,4 +78,38 @@ export default class CustomScheme extends LocalScheme {
     }
     return response;
   }
+
+  // fetchUser(endpoint) {
+  //   console.log(
+  //     '🚀 ~ file: password.js ~ line 83 ~ CustomScheme ~ fetchUser ~ endpoint',
+  //     endpoint,
+  //   );
+  //   if (!this.check().valid) {
+  //     return Promise.resolve();
+  //   }
+  //   if (!this.options.endpoints.user) {
+  //     this.$auth.setUser({});
+  //     return Promise.resolve();
+  //   }
+  //   return this.$auth
+  //     .requestWith(this.name, endpoint, this.options.endpoints.user)
+  //     .then((response) => {
+  //       const userData = getProp(response.data, this.options.user.property);
+
+  //       userData.username = localStorage.getItem('auth.username');
+
+  //       if (!userData) {
+  //         const error = new Error(
+  //           `User Data response does not contain field ${this.options.user.property}`,
+  //         );
+  //         return Promise.reject(error);
+  //       }
+  //       this.$auth.setUser(userData);
+  //       return response;
+  //     })
+  //     .catch((error) => {
+  //       this.$auth.callOnError(error, { method: 'fetchUser' });
+  //       return Promise.reject(error);
+  //     });
+  // }
 }

+ 761 - 0
static/microserviceUserH5/static/css/awardActivity.css

@@ -0,0 +1,761 @@
+@media screen and (max-width: 750px) {
+  html {
+    font-size: calc(100vw / 7.5);
+  }
+}
+
+@media screen and (min-width: 750px) {
+  html {
+    font-size: calc(450px / 7.5);
+  }
+}
+
+html, body, div, ul, li, p {
+  padding: 0;
+  margin: 0;
+}
+
+ul, li {
+  list-style: none;
+}
+
+.container{
+  position: relative;
+}
+
+.all-bg-wrap{
+  width: 7.5rem;
+  position: absolute;
+  top: 0;
+  left: 50%;
+  transform: translateX(-50%);
+  z-index: -1;
+}
+
+.all-bg-img{
+  width: 100%;
+}
+
+.rule-wrap{
+  position: absolute;
+  top: 0.6rem;
+  right: 0;
+  width: 0.44rem;
+  height: 1.22rem;
+  line-height: 0.28rem;
+  text-align: center;
+  padding-top: 0.1rem;
+  background: #FFFFFF;
+  opacity: 0.5;
+  border-radius: 0.16rem 0 0 0.16rem;
+  font-size: 0.24rem;
+  font-weight: 600;
+  color: #5C96FF;
+}
+
+.time-position{
+  height: 1.73rem;
+}
+
+.time-wrap{
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  width: 4.92rem;
+  height: 0.54rem;
+  margin: 0 auto;
+  border-image: linear-gradient(0deg, #A9CFFD, #97C8FF, #ABEFF2) 10 10;
+  background: linear-gradient(90deg, #8FB5FB 0%, #61AAF9 49%, #59C8DD 100%);
+  border-radius: 0.26rem;
+  font-size: 0.24rem;
+  font-weight: 800;
+  color: #FFFFFF;
+}
+
+
+.top-container{
+  display: flex;
+  justify-content: center;
+  position: relative;
+}
+
+.top-bg-one{
+  width: 7.12rem;
+  height: 8.44rem;
+  margin-top: 0.32rem;
+  display: none;
+}
+
+.top-bg-two{
+  width: 7.12rem;
+  height: 7.34rem;
+  margin-top: 0.32rem;
+}
+
+.get-award-container{
+  position: absolute;
+  top: 1.2rem;
+  left: 50%;
+  transform: translate(-50%, -50%);
+  width: 5.84rem;
+  height: 0.5rem;
+  line-height: 0.5rem;
+  text-align: center;
+  border-radius: 0.3rem;
+  font-size: 0.24rem;
+  font-family: PingFang SC;
+  font-weight: 500;
+  color: #FFF8E5;
+  overflow: hidden;
+}
+
+.award-lists{
+  width: 5.84rem;
+  position: absolute;
+  top: 0;
+  left: 50%;
+  transform: translateX(-50%);
+  height: 6rem;
+}
+
+.award-text{
+  height: 0.5rem;
+  font-size: 0.24rem;
+  color: #ffffff;
+}
+
+.all-award-wrap{
+  width: 6.2rem;
+  position: absolute;
+  top: 2.36rem;
+  left: 50%;
+  transform: translateX(-50%);
+  display: flex;
+  flex-wrap: wrap;
+  margin-left: 0.12rem;
+}
+
+.curr-award-item{
+  width: 1.32rem;
+  height: 1.34rem;
+  background: #FDFCFA;
+  border-radius: 0.08rem;
+  display: flex;
+  flex-direction: column;
+  /* justify-content: center; */
+  align-items: center;
+  padding-top: 0.1rem;
+  margin-left: 0.12rem;
+  margin-bottom: 0.12rem;
+}
+
+.award-img{
+  width: 1.08rem;
+  height: 0.8rem;
+  /* background: #FFE3B7; */
+  border-radius: 0.08rem;
+}
+
+.curr-award-name{
+  margin-top: 0.08rem;
+  padding: 0 0.12rem;
+  font-size: 0.16rem;
+  color: #E4402F;
+  text-align: center;
+}
+
+.curr-award-id{
+  display: none;
+}
+
+.curr-award-item-one{
+  width: 1.78rem;
+  height: 1.72rem;
+  margin-left: 0.16rem;
+}
+
+.curr-award-img-one{
+  width: 1.46rem;
+  height: 1.08rem;
+}
+
+.rest-wrap{
+  width: 7.5rem;
+  /* min-height: 66vh; */
+  margin: 0 auto;
+  margin-top: -0.04rem;
+  padding-bottom: 2rem;
+  background-image: url('../img/awardActivity/rest-bg.png');
+  background-repeat: repeat-y;
+  background-size: cover;
+  position: relative;
+}
+
+.award-btn-wrap{
+  width: 5rem;
+  height: 1.58rem;
+  margin: 0 auto;
+  padding-top: 0.34rem;
+}
+
+.start-award{
+  width: 100%;
+  height: 100%;
+}
+
+.get-time-wrap{
+  display: flex;
+  justify-content: space-between;
+  margin: 0 0.56rem;
+  margin-top: 1.2rem;
+}
+
+.rest-time{
+  font-size: 0.28rem;
+  color: #F9F0D4;
+}
+
+.get-time-btn{
+  margin-right: 0.1rem;
+  font-size: 0.32rem;
+  font-weight: 600;
+  color: #DC0622;
+}
+
+.tab-wrap{
+  width: 6.82rem;
+  height: 5.75rem;
+  margin: 0 auto;
+  margin-top: 0.6rem;
+  position: relative;
+}
+
+.tab-bg1{
+  width: 100%;
+  height: 100%;
+}
+
+.tab-bg2{
+  width: 100%;
+  height: 100%;
+  display: none;
+}
+
+.tab-bg3{
+  width: 100%;
+  height: 100%;
+  display: none;
+}
+
+.tab-bg4{
+  width: 100%;
+  height: 100%;
+  display: none;
+}
+
+.no-award-data{
+  width: 3.65rem;
+  height: 1.67rem;
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%, -50%);
+  display: none;
+}
+
+.no-award-img{
+  width: 100%;
+  height: 100%;
+  margin-left: 0.2rem;
+}
+
+.no-award-text{
+  width: 4rem;
+  font-size: 0.28rem;
+  color: #999999;
+}
+
+.no-distribution-data{
+  width: 3.65rem;
+  height: 1.67rem;
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%, -50%);
+  text-align: center;
+  display: none;
+}
+
+.no-distribution-img{
+  width: 1.76rem;
+  height: 1.68rem;
+}
+
+.no-distribution-text{
+  width: 4rem;
+  font-size: 0.28rem;
+  color: #999999;
+}
+
+.award-title-wrap{
+  width: 100%;
+  display: flex;
+  position: absolute;
+  top: 0.3rem;
+  font-size: 0.32rem;
+  font-weight: 500;
+  color: #010101;
+}
+
+.award-title{
+  flex: 1;
+  text-align: center;
+  opacity: 0.5;
+}
+
+.active-title{
+  font-weight: 600;
+  opacity: 1;
+}
+
+.award-list{
+  position: absolute;
+  top: 1.3rem;
+}
+
+.award-list-title-wrap{
+  width: 6.22rem;
+  padding-left: 0.6rem;
+  display: flex;
+  justify-content: space-between;
+  font-size: 0.28rem;
+  color: #333333;
+  border-bottom: 0.02rem solid #F4EDDC;
+  padding-bottom: 0.23rem;
+}
+
+.red{
+  color: #FF482E;
+}
+
+.distribution-total-wrap{
+  width: 100%;
+  padding: 0.46rem 0;
+  font-size: 0.24rem;
+  color: #999999;
+  display: flex;
+  justify-content: center;
+}
+
+.award-total-data:not(:first-child){
+  margin: 0 0.12rem;
+  padding-left: 0.12rem;
+  border-left: 0.01rem solid rgba(135,135,135,1);
+}
+
+.award-list-title, .award-list-content{
+  flex: 1;
+}
+
+.award-list-wrap{
+  margin-top: 0.32rem;
+  height: 3.48rem;
+  overflow-y: scroll;
+}
+
+.distribution-container{
+  height: 5.28rem;
+  overflow-y: scroll;
+}
+
+.award-list-item{
+  width: 6.02rem;
+  padding-left: 0.6rem;
+  display: flex;
+  justify-content: space-between;
+  font-size: 0.28rem;
+  color: #333333;
+  margin-bottom: 0.34rem;
+}
+
+.distribution-list-wrap{
+  width: 100%;
+  position: absolute;
+  top: 1rem;
+  display: none;
+}
+
+.distribution-list{
+  background: rgba(255,255,255,1);
+  margin: 0 0.1rem;
+  padding-bottom: 0.42rem;
+}
+
+.distribution-item{
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 0.38rem;
+}
+
+.distribution-main-content{
+  display: flex;
+  align-items: center;
+}
+
+.phone-logo{
+  width: 0.74rem;
+  height: 0.74rem;
+  margin-left: 0.46rem;
+}
+
+.distribution-info{
+  margin-left: 0.14rem;
+}
+
+.distribution-name{
+  font-size: 0.32rem;
+  font-weight: 500;
+  color: #000000;
+}
+
+.distribution-time{
+  margin-top: 0.22rem;
+  font-size: 0.22rem;
+  color: #9E9693;
+}
+
+.change-num-wrap{
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  width: 1.84rem;
+  height: 0.56rem;
+  background: #FFFFFF;
+  border: 0.01rem solid #DDDDDD;
+  border-radius: 0.1rem;
+  margin-right: 0.46rem;
+}
+
+.cut, .add{
+  width: 0.56rem;
+  height: 0.56rem;
+  text-align: center;
+  font-size: 0.32rem;
+  color: #999999;
+  position: relative;
+  z-index: 99;
+}
+
+.cut{
+  border-right: 0.01rem solid #DDDDDD;
+}
+
+.add{
+  border-left: 0.01rem solid #DDDDDD;
+}
+
+.num-ipt{
+  width: 0.36rem;
+  height: 0.38rem;
+  margin: 0 0.1rem;
+  border-radius: 0.04rem;
+  text-align: center;
+  border: none;
+}
+
+input:focus{ 
+  outline:none; 
+}
+
+.distribution-award-btn{
+  width: 5.7rem;
+  height: 0.88rem;
+  line-height: 0.88rem;
+  margin: 0 auto;
+  text-align: center;
+  background: linear-gradient(90deg, #FFDD71 0%, #FEC047 100%);
+  border-radius: 0.44rem;
+  font-size: 0.36rem;
+  font-weight: bold;
+  color: #DC0622;
+}
+
+.award-record-list{
+  min-height: 5.6rem;
+  padding: 0 0.36rem;
+  margin: 0 0.1rem;
+  margin-top: 0.24rem;
+  background: rgba(255,255,255,1);
+  border-radius: 0.16rem;
+  display: none;
+}
+
+.record-list-title{
+  width: 3.71rem;
+  height: 0.72rem;
+  line-height: 0.72rem;
+  text-align: center;
+  margin: 0 auto;
+  background: #FEDFB3;
+  border-radius: 0 0 0.24rem 0.24rem;
+  font-size: 0.32rem;
+  font-weight: 500;
+  color: #7D271A;
+}
+
+.award-record-list-title-wrap{
+  width: 100%;
+  display: flex;
+  justify-content: space-between;
+  font-size: 0.32rem;
+  font-weight: 500;
+  color: #010101;
+  opacity: 0.5;
+  margin-top: 0.3rem;
+}
+
+.award-record-list-wrap{
+  margin-top: 0.3rem;
+  height: 3.48rem;
+  overflow-y: scroll;
+}
+
+.award-record-list-item{
+  display: flex;
+  font-size: 0.28rem;
+  color: #666666;
+  padding-bottom: 0.4rem;
+}
+
+.award-record-list-name{
+  flex: 0.3;
+}
+
+.award-record-list-time{
+  flex: 0.6;
+  text-align: center;
+}
+
+.award-record-list-day{
+  flex: 0.1;
+  text-align: right;
+}
+
+.result-mask,.rule-mask{
+  width: 100%;
+  height: 100vh;
+  position: fixed;
+  z-index: 9999;
+  top: 0;
+  left: 0;
+  background: rgba(0, 0, 0, 0.5);
+  display: none;
+}
+
+.result-dialog,.rule-result-dialog{
+  width: 5.86rem;
+  height: 6.49rem;
+  background: linear-gradient(0deg, #FFFFFF 74%, #FFEDEE 100%);
+  border-radius: 0.32rem;
+  position: absolute;
+  left: 50%;
+  top: -10.86rem;
+  transform: translateX(-50%);
+}
+
+.rule-title-wrap{
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+
+.rule-title-icon{
+  width: 0.56rem;
+  height: 0.08rem;
+  background: linear-gradient(90deg, #FFE2E0 0%, #FFF4F4 100%);
+}
+
+.rule-title{
+  margin: 0 0.2rem;
+  text-align: center;
+  font-size: 0.36rem;
+  font-weight: bold;
+  color: #333333;
+}
+
+.rule-content{
+  width: 5.86rem;
+  padding: 0.2rem 0;
+  border-radius: 0.1rem;
+  position: absolute;
+  top: 0.2rem;
+}
+
+.rule-point-wrap{
+  height: 4.9rem;
+  margin: 0 0.4rem;
+  margin-top: 0.34rem;
+  font-size: 0.28rem;
+  line-height: 0.4rem;
+  overflow-y: scroll;
+}
+
+.rule-point{
+  margin: 0 0.3rem;
+  font-size: 0.24rem;
+  line-height: 0.48rem;
+}
+
+.card-close,.rule-close{
+  width: 0.68rem;
+  height: 0.68rem;
+  position: absolute;
+  left: 50%;
+  bottom: -1rem;
+  transform: translateX(-50%);
+}
+
+.sure-distribution-wrap{
+  width: 100vw;
+  height: 100vh;
+  position: fixed;
+  top: 0;
+  left: 0;
+  background-color: rgba(0, 0, 0, 0.24);
+  z-index: 999;
+  display: none;
+}
+.sure-distribution-box{
+  width: 6.22rem;
+  height: 2.28rem;
+  background: #FFFFFF;
+  border-radius: 0.2rem;
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%,-50%);
+}
+.sure-distribution-title{
+  text-align: center;
+  padding: 0.48rem;
+  font-size: 0.32rem;
+        color: #333333;
+}
+.sure-distribution-btn-list{
+  width: 100%;
+  height: 0.86rem;
+  display: flex;
+  justify-content: space-between;
+  border-top: 0.01rem solid #E5E5E5;
+  font-size: 0.28rem;
+}
+.sure-distribution-btn{
+  height: 0.86rem;
+  line-height: 0.86rem;
+  text-align: center;
+  flex: 1;
+}
+.sure-distribution-sure{
+  border-left: 0.01rem solid #b4bbc5;
+  color: #3B7FFF;
+}
+.sure-distribution-cannel:active{
+  background-color: #E5E5E5;
+  border-bottom-left-radius: 0.2rem;
+}
+.sure-distribution-sure:active{
+  background-color: #E5E5E5;
+  border-bottom-right-radius: 0.2rem;
+}
+
+.result-title-wrap{
+  margin-top: 0.5rem;
+}
+
+.main-contain{
+  margin-top: 0.48rem;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+}
+
+.card-img{
+  width: 1.52rem;
+  height: 1.52rem;
+  /* background: #FFF0DB; */
+  border-radius: 0.24rem;
+}
+
+.card-img1{
+  width: 1.46rem;
+  height: 1.08rem;
+}
+
+.card-img2{
+  width: 1.08rem;
+  height: 0.8rem;
+}
+
+.card-title{
+  margin-top: 0.24rem;
+  font-size: 0.32rem;
+  color: #333333;
+}
+
+.card-tips{
+  margin-top: 0.14rem;
+  font-size: 0.24rem;
+  color: #888888;
+}
+
+.card-btn{
+  width: 4rem;
+  height: 0.96rem;
+  line-height: 0.96rem;
+  text-align: center;
+  margin-top: 0.56rem;
+  background: linear-gradient(90deg, #FF0200 0%, #FF7900 100%);
+  border-radius: 0.48rem;
+  font-size: 0.32rem;
+  color: #FFFFFF;
+}
+
+.card-rest-times{
+  margin-top: 0.26rem;
+  font-size: 0.24rem;
+  color: #B5B5B5;
+}
+
+#toast-container>.toast-error {
+  background-image: none !important;
+}
+
+.toast-error {
+  background-color: rgba(0, 0, 0, 0.8);
+}
+
+#toast-container>div {
+  font-size: 14px;
+  min-width: 80px !important;
+  padding: 12px !important;
+  box-shadow: none;
+}
+
+.toast-center-center {
+  position: fixed;
+  min-width: 220px;
+  top: 50%;
+  left: 50%;
+  text-align: center;
+  transform: translateX(-50%);
+  color: #FFFFFF;
+  border-radius: 0.1rem;
+}
+
+@media (max-width: 480px) and (min-width: 241px) {
+  #toast-container>div {
+    min-width: 80px !important;
+    width: auto;
+  }
+}

binární
static/microserviceUserH5/static/img/awardActivity/all-bg.png


binární
static/microserviceUserH5/static/img/awardActivity/bug-phone.png


binární
static/microserviceUserH5/static/img/awardActivity/card1.png


binární
static/microserviceUserH5/static/img/awardActivity/card2.jpg


binární
static/microserviceUserH5/static/img/awardActivity/card3.png


binární
static/microserviceUserH5/static/img/awardActivity/card4.png


binární
static/microserviceUserH5/static/img/awardActivity/no-award.png


binární
static/microserviceUserH5/static/img/awardActivity/no-data.png


binární
static/microserviceUserH5/static/img/awardActivity/rest-bg.png


binární
static/microserviceUserH5/static/img/awardActivity/start-award.png


binární
static/microserviceUserH5/static/img/awardActivity/tab-bg1.png


binární
static/microserviceUserH5/static/img/awardActivity/tab-bg2.png


binární
static/microserviceUserH5/static/img/awardActivity/tab-bg3.png


binární
static/microserviceUserH5/static/img/awardActivity/tab-bg4.png


binární
static/microserviceUserH5/static/img/awardActivity/top-bg-one.png


binární
static/microserviceUserH5/static/img/awardActivity/top-bg-two.png


binární
static/microserviceUserH5/static/img/iosEnter/entry.png


binární
static/microserviceUserH5/static/img/iosEnter/index-phone.png


binární
static/microserviceUserH5/static/img/iosEnter/index-what.png


+ 0 - 1
static/microserviceUserH5/static/js/vender/config.js

@@ -5,7 +5,6 @@ var baseUrl = url[0] + '//' + url[2];
 // var baseUrl = 'http://prese.phone.androidscloud.com'
 // var baseUrl = 'http://vclusters.imwork.net:2221'
 
-
 function GetRequest() {
   var url = location.search; // 获取url中"?"符后的字串
   var obj= new Object();

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1141 - 0
static/microserviceUserH5/vcloud/awardActivity.html


+ 13 - 4
static/microserviceUserH5/vcloud/iosEnter.html

@@ -18,11 +18,12 @@
 	</script>
 </head>
 <div class="container">
-	<div class="index-icon" id="index-what">
-		<img class="img" src="../static/img/iosEnter/index-what.png" />
-	</div>
 	<div class="index-icon" id="index-phone">
 		<img class="img" src="../static/img/iosEnter/index-phone.png" />
+		<img class="entry" src="../static/img/iosEnter/entry.png" />
+	</div>
+	<div class="index-icon" id="index-what">
+		<img class="img" src="../static/img/iosEnter/index-what.png" />
 	</div>
 </div>
 <script type="text/javascript" th:inline="javascript">
@@ -91,7 +92,8 @@
 	.index-icon{
 		width: 6.5rem;
 		height: 2.28rem;
-		padding-top: 0.36rem;
+		margin-top: 0.36rem;
+		position: relative;
 	}
 	.img{
 		width: 100%;
@@ -100,6 +102,13 @@
 	#index-phone{
 		display: none;
 	}
+	.entry{
+		width: 2.08rem;
+		height: 1.28rem;
+		position: absolute;
+		top: 0.32rem;
+		right: 0.4rem;
+	}
 	#toast-container>.toast-error {
 		background-image: none !important;
 	}

+ 371 - 151
static/screenAndroid/WXdraw.js

@@ -1,5 +1,5 @@
 //蒙版
-var canvas_bak = document.getElementById("box");
+var canvas_bak = document.getElementById('box');
 
 var winHeight = window.screen.height - window.innerHeight;
 var vowidth = window.screen.width;
@@ -8,9 +8,9 @@ var numse = window.screen.height;
 
 // 计算title top 头部 连接断开,是否准备重连?
 if (numse <= 70) {
-  var voheight = window.screen.height - winHeight - 34 - 20
+  var voheight = window.screen.height - winHeight - 34 - 20;
 } else {
-  var voheight = window.screen.height - topwinHeightDraw - 20
+  var voheight = window.screen.height - topwinHeightDraw - 20;
 }
 
 // 画笔大小
@@ -21,17 +21,25 @@ url = url.split('/');
 
 var parameters = GetRequest();
 var videoWidth, videoHeight;
-var isControl = true; // 是否是观看模式
+var isControl = false; // 是否是观看模式
+changIsControl(true);
 var isAuth = parameters['authPhone']; // 是否是获取的云手机
 
-var wsss, errorTime = 0;
+var wsss,
+  errorTime = 0;
 var first = true;
+function changIsControl(value) {
+  isControl = value;
+  // if(){}
+  $('#open-set-phone-size-dialog-btn').attr('hidden', !value);
+}
+
 function throttle(fn, delay) {
   var flag = true;
-  errorTime += delay;
   return () => {
     if (!flag) return;
     flag = false;
+    errorTime += delay;
     timer = setTimeout(() => {
       fn();
       flag = true;
@@ -39,72 +47,186 @@ function throttle(fn, delay) {
   };
 }
 
+const throttleDoConnectDirectives = throttle(() => {
+  doConnectDirectives();
+}, 100);
+
 function doConnectDirectives() {
-  videoWidth = Number(resolvingPower) ? Number(resolvingPower) : 720
-  videoHeight = videoWidth === 720 ? 1280 : 1920
+  videoWidth = Number(resolvingPower) ? Number(resolvingPower) : 720;
+  videoHeight = videoWidth === 720 ? 1280 : 1920;
   wsss = new WebSocket(cUrl);
   wsss.binaryType = 'arraybuffer';
 
   wsss.onopen = function () {
+    errorTime = 0;
     // 进入发起询问
     var pings2 = {
-      "type": "forwardMsg",
-      "data": {
-        "code": "3000",
-        "desc": "询问是否有在控制" // 可选
-      }
-    }
+      type: 'forwardMsg',
+      data: {
+        code: '3000',
+        username,
+        desc: '询问是否有在控制', // 可选
+      },
+    };
     wsss.send(JSON.stringify(pings2));
+
+    wsss.send(
+      JSON.stringify({
+        type: 'getPhoneSize',
+      }),
+    );
   };
-  wsss.onerror = function (evt) {
-    wsss.close();
-    throttle(doConnectDirectives, 100);
-    if (errorTime > 1000) {
-      quit();
+  wsss.onerror = function (e) {
+    // console.log('🚀 ~ file: WXdraw.js ~ line 82 ~ onerror ~ e', e);
+    wsss.close(1006);
+    // throttle(doConnectDirectives, 100);
+    // if (errorTime > 1000) {
+    //   quit();
+    // }
+  };
+  wsss.onclose = function (e) {
+    // console.log('🚀 ~ file: WXdraw.js ~ line 93 ~ onclose ~ e', e);
+    // new WebSocket(e.)
+    // doConnectDirectives();
+    if (e.code === 1006) {
+      // 异常关闭,重连
+      throttleDoConnectDirectives();
+      // doConnectDirectives();
+      // throttle(doConnectDirectives, 100);
+      if (errorTime > 1000) {
+        quit();
+      }
     }
   };
   wsss.onmessage = function (res) {
-    console.log('res', res)
-    var result = typeof res.data === 'string' ? JSON.parse(res.data) : res.data
-    console.log('result.type', result.type)
+    var result = typeof res.data === 'string' ? JSON.parse(res.data) : res.data;
+    console.log(
+      '🚀 ~ file: WXdraw.js ~ line 78 ~ doConnectDirectives ~ result',
+      result,
+    );
+
     if (result.type === 'cutting') {
       if (result.data.status === 0) {
-        $.toast('复制成功', "text");
+        $.toast('复制成功', 'text');
       } else {
-        $.toast(result.msg, "text");
+        $.toast(result.msg, 'text');
       }
-      return
+      return;
     }
-    if (result.type === 'forwardMsg' && isAuth !== 'none') {
-      if (result.data.code === 4000 || result.data.code === 3000) {
-        if (isAuth === 'huo') {
-          $.confirm("授权方已收回控制权,您进入观看屏幕模式", function () {
-            //点击确认后的回调函数
-            isControl = false;
-          }, function () {
-            isControl = false;
-            //点击取消后的回调函数
-            quit();
-          });
-        } else {
-          $.confirm("当前云手机正在授控,是否请求获取云手机控制权?", function () {
-            //点击确认后的回调函数
-            var ping = {
-              "type": "forwardMsg",
-              "data": {
-                "code": "5000",
-                "desc": "控制权限收回" // 可选
-              }
+    if (result.type === 'forwardMsgRep') {
+      // 当前云机无其他终端在线,获得控制权
+      changIsControl(true);
+    }
+    if (result.type === 'forwardMsg') {
+      /**
+       * @type {boolean} isControl 当前是否拥有控制权,初始化时为false
+       * @type {string} isAuth 当前云机类型 - huo: 获取的云机,none: 自己的云机
+       * @type {string} username 当前登录的双子星账号username
+       */
+
+      if (result.data.username !== username) {
+        switch (String(result.data.code)) {
+          case '3000': {
+            if (isControl) {
+              // 回复有控制
+              wsss.send(
+                JSON.stringify({
+                  type: 'forwardMsg',
+                  data: {
+                    code: '4000',
+                    username,
+                    desc: '有控制', // 可选
+                  },
+                }),
+              );
+              return;
             }
-            wsss.send(JSON.stringify(ping));
-            isControl = true;
-          }, function () {
-            //点击取消后的回调函数
-            isControl = false;
-          });
+            // 回复有观看
+            wsss.send(
+              JSON.stringify({
+                type: 'forwardMsg',
+                data: {
+                  code: '4100',
+                  username,
+                  desc: '有观看', // 可选
+                },
+              }),
+            );
+            return;
+          }
+          case '4000': {
+            // 当前是获取方
+            if (isAuth === 'huo' && isControl) {
+              $.confirm(
+                '授权方已收回控制权,您进入观看屏幕模式',
+                function () {
+                  //点击确认后的回调函数
+                  changIsControl(false);
+                },
+                function () {
+                  changIsControl(false);
+                  //点击取消后的回调函数
+                  quit();
+                },
+              );
+              return;
+            }
+            if (!isControl && isAuth === 'shou') {
+              // 当前是授权方切没有控制权
+              $.confirm(
+                '当前云手机正在授控,是否请求获取云手机控制权?',
+                function () {
+                  //点击确认后的回调函数
+                  wsss.send(
+                    JSON.stringify({
+                      type: 'forwardMsg',
+                      data: {
+                        code: '5000',
+                        username,
+                        desc: '控制权限收回', // 可选
+                      },
+                    }),
+                  );
+                  changIsControl(true);
+                },
+                function () {
+                  //点击取消后的回调函数
+                  changIsControl(false);
+                },
+              );
+
+              return;
+            }
+          }
+          case '5000': {
+            // if (result.data.username === username) {
+            //   changIsControl(true);
+            //   return;
+            // }
+            if (isAuth === 'huo' && isControl) {
+              $.confirm(
+                '授权方已收回控制权,您进入观看屏幕模式',
+                function () {
+                  //点击确认后的回调函数
+                  changIsControl(false);
+                },
+                function () {
+                  changIsControl(false);
+                  //点击取消后的回调函数
+                  quit();
+                },
+              );
+              return;
+            }
+
+            return;
+          }
+          default: {
+            return;
+          }
         }
       }
-      return
+      return;
     }
     if (result.type === 'payInitiateEvent') {
       var url = window.location.href;
@@ -121,144 +243,242 @@ function doConnectDirectives() {
         dataType: 'json',
         contentType: 'application/json;charset=UTF-8',
         success: function (res) {
-          if(result.data.payType === 1) { // 微信
-            if (window.__wxjs_environment === 'miniprogram') { // 小程序
+          if (result.data.payType === 1) {
+            // 微信
+            if (window.__wxjs_environment === 'miniprogram') {
+              // 小程序
               // copyUrl(result.data.payUrl);
             } else {
-              window.location.href = result.data.payUrl
+              window.location.href = result.data.payUrl;
             }
           } else {
-            window.location.href = result.data.payUrl
+            window.location.href = result.data.payUrl;
           }
         },
       });
-      return
+      return;
     }
-  }
+    if (result.type === 'getPhoneSize' || result.type === 'setPhoneSize') {
+      // console.log(result);
+      if (
+        window.currentPhoneSize &&
+        (window.currentPhoneSize.width !==
+          Math.min(result.data.width, result.data.height) ||
+          window.currentPhoneSize.height !==
+            Math.max(result.data.width, result.data.height) ||
+          window.currentPhoneSize.dpi !== result.data.dpi)
+      ) {
+        // 获取到的分辨率与当前分辨率不符
+
+        const data = window.phoneSizeList.find(function (v) {
+          return (
+            v.width === Math.min(result.data.width, result.data.height) &&
+            v.height === Math.max(result.data.width, result.data.height) &&
+            v.dpi === result.data.dpi
+          );
+        });
+        window.currentPhoneSize = data || {
+          width: Math.min(result.data.width, result.data.height),
+          height: Math.max(result.data.width, result.data.height),
+          dpi: result.data.dpi,
+        };
+
+        if (result.type === 'setPhoneSize') {
+          lastSetPhone = Date.now();
+        }
+        // if (result.type === 'getPhoneSize') {
+        // 上报给后端
+        $.ajax({
+          url:
+            baseUrl +
+            '/api/resources/v5/machine/resolution/operationResolvingPower',
+          headers: {
+            Authorization: token,
+          },
+          type: 'post',
+          dataType: 'json',
+          contentType: 'application/json; charset=UTF-8',
+          data: JSON.stringify({
+            userCardId: window.userCardId,
+            resolvingPowerId: window.currentPhoneSize.id,
+          }),
+        });
+      }
+      // }
+
+      updateDB(db, storeName, {
+        id: userCardId,
+        socketURL: socketURL,
+        cUrl: cUrl,
+        cardToken: cardToken,
+        resolvingPower: resolvingPower,
+        width: window.currentPhoneSize.width,
+        height: window.currentPhoneSize.height,
+        dpi: window.currentPhoneSize.dpi,
+      });
+
+      return;
+    }
+  };
 }
-$('body').on("click", function () {
+$('body').on('click', function () {
   draw_graph('pencil');
-})
+});
 //剪切板
-$(".upload").on("click", function () {
-  var texts = $(this).attr("data-text")
-  if (texts == "uploads") {
-    $(".mainbox").css({
-      "display": "block"
-    })
-    $(".sbox").css({
-      "display": "none"
-    })
+$('.upload').on('click', function () {
+  var texts = $(this).attr('data-text');
+  if (texts == 'uploads') {
+    $('.mainbox').css({
+      display: 'block',
+    });
+    $('.sbox').css({
+      display: 'none',
+    });
   }
-})
+});
 
 //home 控制home
-$(".botmat1img").on("click", function () {
-  var codes = $(this).attr("data-text")
-  if (codes == "home" && isControl) {
+$('.botmat1img').on('click', function () {
+  var codes = $(this).attr('data-text');
+  if (codes == 'home' && isControl) {
     wsss.send(ExexuteKeyBoard(3));
-  } else if (codes == "return" && isControl) {
+  } else if (codes == 'return' && isControl) {
     wsss.send(ExexuteKeyBoard(4));
-  } else if (codes == "gengduo" && isControl) {
+  } else if (codes == 'gengduo' && isControl) {
     wsss.send(ExexuteKeyBoard(187));
   }
-
-})
+});
 // 高清控制
-$(".PictureQuality").on("click", function () {
+$('.PictureQuality').on('click', function () {
   if (!isControl) {
-    return
+    return;
   }
-  $(this).addClass("avit").siblings().removeClass('avit')
-  var id = $(this).attr("data-id")
+  $(this).addClass('avit').siblings().removeClass('avit');
+  var id = $(this).attr('data-id');
   var buffer = makeSharpness(Number(id));
   ws.send(buffer);
-})
+});
 //画图形
 var draw_graph = function (graphType, obj) {
   //把蒙版放于画板上面
-  $("#container").css("z-index", 30);
-  $("#dedit").css("z-index", 20);
+  $('#container').css('z-index', 30);
+  $('#dedit').css('z-index', 20);
   var canDraw = false;
   //鼠标按下获取 开始xy开始画图
-  var ongoingTouches = [];
+  // var ongoingTouches = [];
   var touchstart = function (e) {
     if (!isControl) {
-      return
-    }
-    $('.control-right-img').attr({
-      "data-id": "2"
-    })
-    $(".leftmains").css({
-      "right": "-4rem"
-    })
-    var touchfor = e.originalEvent.changedTouches; //for 的手指数组
-    //是否横屏
-    for (var i = 0; i < touchfor.length; i++) {
-      var acrossWidthX = touchfor[i].pageY * (videoHeight / voheight);
-      var acrossHeightY = videoWidth - touchfor[i].pageX * (videoWidth / vowidth);
-      var verticalWidthX = touchfor[i].pageX * (videoWidth / vowidth);
-      var verticalHeightY = touchfor[i].pageY * (videoHeight / voheight);
-      var idx = ongoingTouches.findIndex(function (ele) {
-        return ele.identifier === touchfor[i].identifier
-      })
-      if (idx < 0) {
-        ongoingTouches.push(touchfor[i]);
-      }
-      var ping = resolving == 0 ?
-        { "data": { "action": 0, "count": ongoingTouches.length, "pointerId": touchfor[i].identifier, "x": acrossWidthX.toFixed(2), "y": acrossHeightY.toFixed(2) }, "type": "event" } :
-        { "data": { "action": 0, "count": ongoingTouches.length, "pointerId": touchfor[i].identifier, "x": verticalWidthX.toFixed(2), "y": verticalHeightY.toFixed(2) }, "type": "event" };
-      wsss.send(JSON.stringify(ping));
+      return;
     }
+    const action = 0;
+    Array.from(e.originalEvent.changedTouches).forEach(function (item, index) {
+      const x = item.clientX - item.target.getBoundingClientRect().x;
+      const y = item.clientY - item.target.getBoundingClientRect().y;
+      return wsss.send(
+        JSON.stringify({
+          type: 'event',
+          data: {
+            action,
+            count: e.originalEvent.touches.length,
+            pointerId: item.identifier,
+            x: (function () {
+              return (
+                resolving
+                  ? x * (window.currentPhoneSize.width / vowidth)
+                  : y * (window.currentPhoneSize.height / voheight)
+              ).toFixed(2);
+            })(),
+            y: (function () {
+              return (
+                resolving
+                  ? y * (window.currentPhoneSize.height / voheight)
+                  : (vowidth - x) * (window.currentPhoneSize.width / vowidth)
+              ).toFixed(2);
+            })(),
+          },
+        }),
+      );
+    });
     canDraw = true;
   };
 
   //鼠标离开 把蒙版canvas的图片生成到canvas中
   var touchend = function (e) {
     if (!isControl) {
-      return
-    }
-    var touchfor = e.originalEvent.changedTouches; //for 的手指数组
-    //是否横屏
-    for (var i = 0; i < touchfor.length; i++) {
-      var acrossWidthX = touchfor[i].pageY * (videoHeight / voheight);
-      var acrossHeightY = videoWidth - touchfor[i].pageX * (videoWidth / vowidth);
-      var verticalWidthX = touchfor[i].pageX * (videoWidth / vowidth);
-      var verticalHeightY = touchfor[i].pageY * (videoHeight / voheight);
-      var ping = resolving == 0 ?
-        { "data": { "action": 1, "count": ongoingTouches.length, "pointerId": touchfor[i].identifier, "x": acrossWidthX.toFixed(2), "y": acrossHeightY.toFixed(2) }, "type": "event" } :
-        { "data": { "action": 1, "count": ongoingTouches.length, "pointerId": touchfor[i].identifier, "x": verticalWidthX.toFixed(2), "y": verticalHeightY.toFixed(2) }, "type": "event" };
-      wsss.send(JSON.stringify(ping));
-      ongoingTouches.forEach(function (item, index) {
-        if (item.identifier === touchfor[i].identifier) {
-          ongoingTouches.splice(index, 1)
-        }
-      })
+      return;
     }
+    const action = 1;
+    Array.from(e.originalEvent.changedTouches).forEach(function (item, index) {
+      const x = item.clientX - item.target.getBoundingClientRect().x;
+      const y = item.clientY - item.target.getBoundingClientRect().y;
+      return wsss.send(
+        JSON.stringify({
+          type: 'event',
+          data: {
+            action,
+            count: e.originalEvent.touches.length,
+            pointerId: item.identifier,
+            x: (function () {
+              return (
+                resolving
+                  ? x * (window.currentPhoneSize.width / vowidth)
+                  : y * (window.currentPhoneSize.height / voheight)
+              ).toFixed(2);
+            })(),
+            y: (function () {
+              return (
+                resolving
+                  ? y * (window.currentPhoneSize.height / voheight)
+                  : (vowidth - x) * (window.currentPhoneSize.width / vowidth)
+              ).toFixed(2);
+            })(),
+          },
+        }),
+      );
+    });
     canDraw = false;
   };
 
   //清空层 云手机超出屏幕的开关
   var clearContext = function () {
     canDraw = false;
-  }
+  };
 
   // 鼠标移动
   var touchmove = function (e) {
+    e.preventDefault();
     if (!isControl) {
-      return
-    }
-    var touchfor = e.originalEvent.targetTouches; //for 的手指数组
-    for (var i = 0; i < touchfor.length; i++) {
-      var acrossWidthX = touchfor[i].pageY * (videoHeight / voheight);
-      var acrossHeightY = videoWidth - touchfor[i].pageX * (videoWidth / vowidth);
-      var verticalWidthX = touchfor[i].pageX * (videoWidth / vowidth);
-      var verticalHeightY = touchfor[i].pageY * (videoHeight / voheight);
-      var ping = resolving == 0 ?
-        { "data": { "action": 2, "count": touchfor.length, "pointerId": touchfor[i].identifier, "x": acrossWidthX.toFixed(2), "y": acrossHeightY.toFixed(2) }, "type": "event" } :
-        { "data": { "action": 2, "count": touchfor.length, "pointerId": touchfor[i].identifier, "x": verticalWidthX.toFixed(2), "y": verticalHeightY.toFixed(2) }, "type": "event" };
-      wsss.send(JSON.stringify(ping));
+      return;
     }
+    const action = 2;
+    Array.from(e.originalEvent.changedTouches).forEach(function (item, index) {
+      const x = item.clientX - item.target.getBoundingClientRect().x;
+      const y = item.clientY - item.target.getBoundingClientRect().y;
+      return wsss.send(
+        JSON.stringify({
+          type: 'event',
+          data: {
+            action,
+            count: e.originalEvent.touches.length,
+            pointerId: item.identifier,
+            x: (function () {
+              return (
+                resolving
+                  ? x * (window.currentPhoneSize.width / vowidth)
+                  : y * (window.currentPhoneSize.height / voheight)
+              ).toFixed(2);
+            })(),
+            y: (function () {
+              return (
+                resolving
+                  ? y * (window.currentPhoneSize.height / voheight)
+                  : (vowidth - x) * (window.currentPhoneSize.width / vowidth)
+              ).toFixed(2);
+            })(),
+          },
+        }),
+      );
+    });
   };
 
   //鼠标离开区域以外 除了涂鸦 都清空
@@ -266,24 +486,24 @@ var draw_graph = function (graphType, obj) {
     if (graphType != 'handwriting') {
       clearContext();
     }
-  }
+  };
 
-  $(canvas_bak).unbind();
-  $(canvas_bak).bind('touchstart', touchstart);
-  $(canvas_bak).bind('touchmove', touchmove);
-  $(canvas_bak).bind('touchend', touchend);
-  $(canvas_bak).bind('mouseout', mouseout);
-}
+  $(canvas_bak).off();
+  $(canvas_bak).on('touchstart', touchstart);
+  $(canvas_bak).on('touchmove', touchmove);
+  $(canvas_bak).on('touchend', touchend);
+  $(canvas_bak).on('mouseout', mouseout);
+};
 
 // 获取url中"?"符后的字串
 function GetRequest() {
   var url = location.search;
   var obj = new Object();
-  if (url.indexOf("?") != -1) {
+  if (url.indexOf('?') != -1) {
     var str = url.substr(1);
-    strs = str.split("&");
+    strs = str.split('&');
     for (var i = 0; i < strs.length; i++) {
-      obj[strs[i].split("=")[0]] = (strs[i].split("=")[1]);
+      obj[strs[i].split('=')[0]] = strs[i].split('=')[1];
     }
   }
   return obj;

+ 386 - 85
static/screenAndroid/WXtrialInterface.html

@@ -42,9 +42,54 @@
       rel="stylesheet"
       href="https://cdn.bootcss.com/jquery-weui/1.2.1/css/jquery-weui.min.css"
     />
+    <!-- <link rel="stylesheet" href="../static/lib/swiper/swiper-bundle.min.css" />
+    <script src="../static/lib/swiper/swiper-bundle.js"></script> -->
+    <script src="../static/lib/doT-1.1.3/doT.min.js"></script>
+    <script src="../static/lib/qs.js"></script>
   </head>
 
   <body class="scroll h-player" style="overscroll-behavior: contain">
+    <template id="template-phone-size-item">
+      {{~ it.list :value:index }}
+      <div
+        class="phone-size-item {{? value.width === it.active.width && value.height === it.active.height }}active{{?}}"
+        data-id="{{= value.id }}"
+        data-dpi="{{= value.dpi }}"
+        data-width="{{= value.width }}"
+        data-height="{{= value.height }}"
+      >
+        <span>{{= value.width }}x{{= value.height }}</span>
+      </div>
+      {{~}}
+    </template>
+    <template id="template-shear">
+      {{? it.length}}
+      <div class="title">
+        剪贴板
+        <div class="btn-clear">清空</div>
+      </div>
+      <div class="slide-wrapper-content">
+        {{~ it :item:index }}
+        <div class="slide-wrapper" data-id="{{= item.id}}">
+          <div class="slide-scroll animate-slide-start">
+            <div
+              class="slide-content"
+              data-content="{{= encodeHtml(item.content)}}"
+            >
+              <div>{{= encodeHtml(item.content)}}</div>
+            </div>
+            <div class="slide-content-button" data-id="{{= item.id}}">
+              <button>删除</button>
+            </div>
+          </div>
+        </div>
+        {{~}}
+      </div>
+      {{??}}
+      <img class="empty" src="img/jianqieban_pic@2x.png" alt="" />
+      <div class="empty-txt">剪贴板为空</div>
+      {{?}}
+    </template>
     <div class="container" id="player">
       <div class="muted" id="btnMuted">
         <div class="control-right-img" data-id="1">
@@ -80,10 +125,15 @@
       </div>
       <div class="leftmains">
         <div class="PictureQualityMain">
-          <div class="PictureQuality" data-id="4">高清</div>
-          <div class="PictureQuality avit" data-id="3">标清</div>
-          <div class="PictureQuality" data-id="2">极速</div>
+          <div class="menu-btn PictureQuality" data-id="4">高清</div>
+          <div class="menu-btn PictureQuality avit" data-id="3">标清</div>
+          <div class="menu-btn PictureQuality" data-id="2">极速</div>
         </div>
+        <!-- <div class="">
+          <div class="menu-btn" id="open-set-phone-size-dialog-btn">
+            <span>分辨率</span>
+          </div>
+        </div> -->
         <div class="operation">
           <div class="upload" id="showsuss" data-text="uploads">
             <img src="../static/img/wx/shangchuan_icon.png" />
@@ -172,7 +222,26 @@
       </div>
     </div>
 
-    <body oncontextmenu="Back()"></body>
+    <!-- <body oncontextmenu="Back()"></body> -->
+    <div id="set-phone-size-dialog" class="dialog">
+      <div class="dialog-mask"></div>
+      <!-- 设置分辨率的弹窗 -->
+      <div class="dialog-content-border">
+        <div class="dialog-content">
+          <div class="dialog-header">
+            <div class="dialog-btn cancel">
+              <span>取消</span>
+            </div>
+            <div class="dialog-btn confirm">
+              <span>确定</span>
+            </div>
+          </div>
+          <div class="dialog-main">
+            <div id="phone-size-list"></div>
+          </div>
+        </div>
+      </div>
+    </div>
     <script
       type="text/javascript"
       src="https://res.wx.qq.com/open/js/jweixin-1.3.2.js"
@@ -202,8 +271,9 @@
 
       var parameters = GetRequest();
       var token = parameters['token'];
-      var userCardId = parameters['userCardId'];
       var mealType = parameters['mealType'];
+      var userCardId = parameters['userCardId'];
+      var username = parameters['username'];
       var videoTimer = null,
         videoTime = 0,
         adType = 0,
@@ -217,10 +287,10 @@
         /^192\.168\./.test(location.host) ||
         /^127\.0\.0\.1/.test(location.host) ||
         /^localhost/.test(location.host);
-      if (isDev) {
-        baseUrl = 'http://gntest.phone.androidscloud.com:1280';
-        sourceType = 2;
-      }
+      // if (isDev) {
+      //   baseUrl = 'http://gntest.phone.androidscloud.com:1280';
+      //   sourceType = 2;
+      // }
       if (parameters['mealType'] === 'VIP') {
         $('.loading_sceen_pic').attr('src', '../static/img/home_bg_VIP.png');
       } else if (parameters['mealType'] === 'SVIP') {
@@ -289,6 +359,13 @@
             cUrl = request.result.cUrl;
             cardToken = request.result.cardToken;
             resolvingPower = request.result.resolvingPower;
+
+            window.currentPhoneSize = {
+              width: request.result.width,
+              height: request.result.height,
+              dpi: request.result.dpi,
+            };
+
             doConnectBusiness();
             doConnectDirectives();
           } else {
@@ -376,7 +453,8 @@
       Module.onRuntimeInitialized = function () {
         isFinish = true;
       };
-      var timerInterval = null, timeInterval = 0;
+      var timerInterval = null,
+        timeInterval = 0;
       function connect(type) {
         $.ajax({
           url: baseUrl + '/api/resources/user/cloud/connect',
@@ -423,6 +501,9 @@
                     cUrl: cUrl,
                     cardToken: res.data.cardToken,
                     resolvingPower: res.data.resolvingPower,
+                    width: res.data.width,
+                    height: res.data.high,
+                    dpi: res.data.dpi,
                   });
                 } else {
                   updateDB(db, storeName, {
@@ -431,8 +512,23 @@
                     cUrl: cUrl,
                     cardToken: res.data.cardToken,
                     resolvingPower: res.data.resolvingPower,
+                    width: res.data.width,
+                    height: res.data.high,
+                    dpi: res.data.dpi,
                   });
                 }
+                const data = window.phoneSizeList.find(function (v) {
+                  return (
+                    v.width === res.data.width &&
+                    v.height === res.data.height &&
+                    v.dpi === res.data.dpi
+                  );
+                });
+                window.currentPhoneSize = data || {
+                  width: res.data.width,
+                  height: res.data.height,
+                  dpi: res.data.dpi,
+                };
               } else {
                 $.toast('网络异常,请稍后重试', 'text');
                 setTimeout(() => {
@@ -440,27 +536,36 @@
                   quit();
                 }, 3000);
               }
-            } else if (res.status === 5200) {
+              return;
+            }
+            if (res.status === 5200) {
               if (timeInterval > 7) {
                 $.toast('网络异常,请稍后重试', 'text');
                 setTimeout(() => {
                   clearInterval(intervaler);
                   quit();
                 }, 3000);
-                timerInterval = clearTimeout()
-                return
+                timerInterval = clearTimeout();
+                return;
               }
               timerInterval = setTimeout(() => {
                 connect(type);
                 timeInterval += 1;
-              }, 3000)
-            } else {
-              $.toast('画面异常,请重新进入', 'text');
+              }, 3000);
+              return;
+            }
+            if (res.status === 5220) {
+              $.toast('云手机正在一键修复中', 'text');
               setTimeout(() => {
-                clearInterval(intervaler);
                 quit();
               }, 3000);
+              return;
             }
+            $.toast('画面异常,请重新进入', 'text');
+            setTimeout(() => {
+              clearInterval(intervaler);
+              quit();
+            }, 3000);
           },
         });
       }
@@ -505,11 +610,11 @@
           if (ws.readyState === 1) {
             ws.send('ping');
           } else {
-            clearInterval(intervaler);
             $.toast('画面异常,请重新进入', 'text');
             setTimeout(() => {
+              clearInterval(intervaler);
               quit();
-            }, 3000)
+            }, 3000);
           }
         }, 3000);
 
@@ -529,7 +634,7 @@
             setTimeout(() => {
               wsss.close();
               quit();
-            }, 3000)
+            }, 3000);
           }
         });
         ws.addEventListener('message', function (event) {
@@ -618,7 +723,7 @@
               ws.close();
               wsss.close();
               uni.webView.navigateBack({
-                delta: 1
+                delta: 1,
               });
             }
           }
@@ -713,18 +818,44 @@
             }
           }
           if (input[23] == 0x05) {
-            //横竖屏标识
-            var state = CheckScreenDirection(input.slice(24, 24 + 8));
+            if (input[28] == 0x01 && input[29] == 0x01) {
+              //横竖屏标识
+              var state = CheckScreenDirection(input.slice(24, 24 + 8));
 
-            if (state == 1) {
-              console.log('安卓卡此时竖屏');
-              //竖屏处理
-              resolving = 1;
-            } else {
-              console.log('安卓卡此时横屏');
-              //横屏处理
-              resolving = 0;
+              if (state == 1) {
+                console.log('安卓卡此时竖屏');
+                //竖屏处理
+                resolving = 1;
+              } else {
+                console.log('安卓卡此时横屏');
+                //横屏处理
+                resolving = 0;
+              }
             }
+            // window.phoneSizeList = window.phoneSizeListBack.map(function (
+            //   item,
+            // ) {
+            //   return resolving
+            //     ? item
+            //     : Object.assign({}, item, {
+            //         width: item.height,
+            //         height: item.width,
+            //       });
+            // });
+
+            // wsss.send(
+            //   JSON.stringify({
+            //     type: 'getPhoneSize',
+            //   }),
+            // );
+            // 横竖屏变更时获取分辨率要延迟,不然会获取到变更前的分辨率
+            // setTimeout(function () {
+            //   wsss.send(
+            //     JSON.stringify({
+            //       type: 'getPhoneSize',
+            //     }),
+            //   );
+            // }, 500);
           }
           if (input[23] == 0x0b) {
             //多端登录处理, 数据从索引24开始取, input 是接收到的原始数据
@@ -767,11 +898,43 @@
       var cutList = [];
       let timer,
         isFlag = true;
+      const shearTemplate = doT.template(
+        $('#template-shear').html().replace(/&amp;/g, '&'),
+      );
+
+      function updateShearHtml(list) {
+        $('.box-shear-plate').html(shearTemplate(list));
+      }
+      // 对字符串进行html转义
+      function encodeHtml(content) {
+        return [
+          ['<', '&lt;'],
+          ['>', '&gt;'],
+          ['&', '&amp;'],
+          ['"', '&quot;'],
+        ].reduce(function (previousValue, currentValue) {
+          return previousValue.replace(
+            new RegExp(currentValue[0], 'g'),
+            currentValue[1],
+          );
+        }, content);
+      }
+
+      $('.box-shear-plate').on('click', '.slide-content', function (e) {
+        handleCopy(e.currentTarget.dataset.content);
+      });
+      $('.box-shear-plate').on('click', '.btn-clear', function (e) {
+        handleClear();
+      });
+      $('.box-shear-plate').on('click', '.slide-content-button', function (e) {
+        handleDelete(e.currentTarget.dataset.id);
+      });
+
       function showShearPlate() {
         if (!isControl) {
           return;
         }
-        stopManyClick(() => {
+        stopManyClick(function () {
           new Promise((resolve, reject) => {
             if (window.navigator.clipboard) {
               window.navigator.clipboard
@@ -801,7 +964,7 @@
             }
           }).then(function () {
             $('.box-shear-plate').empty();
-            $.ajax({
+            return $.ajax({
               url: baseUrl + '/api/public/v5/shear/content',
               headers: {
                 Authorization: token,
@@ -809,36 +972,9 @@
               type: 'get',
               dataType: 'json',
               success: function (res) {
-                if (res.status === 0) {
-                  if (res.data.length) {
-                    cutList = array_unique(res.data);
-                    var str =
-                      '<div class="title">剪贴板<div onclick="handleClear()" class="btn-clear">清空</div></div><div class="slide-wrapper-content">';
-                    cutList.forEach(function (item) {
-                      str +=
-                        "<div class='slide-wrapper'><div class='slide-scroll animate-slide-start'><div class='slide-content'><div onclick='handleCopy(\"" +
-                        item.content +
-                        '")\'>' +
-                        item.content +
-                        "</div></div><div class='slide-content-button'><button onclick='handleDelete(" +
-                        item.id +
-                        ")'>删除</button></div></div></div>";
-                    });
-                    str += '</div>';
-                    $('.box-shear-plate').append(str);
-                  } else {
-                    $('.box-shear-plate').append(
-                      '<img class="empty" src="img/jianqieban_pic@2x.png" alt="" /><div class="empty-txt">剪贴板为空</div>',
-                    );
-                  }
-                  $('.mask').show();
-                  initSlider();
-                } else {
-                  $('.box-shear-plate').append(
-                    '<img class="empty" src="img/jianqieban_pic@2x.png" alt="" /><div class="empty-txt">剪贴板为空</div>',
-                  );
-                  $('.mask').show();
-                }
+                updateShearHtml(res.status === 0 ? res.data : []);
+                $('.mask').show();
+                initSlider();
               },
             });
           });
@@ -859,14 +995,24 @@
       }
       // 清空剪贴板
       function handleClear() {
-        var ids = '';
-        cutList.forEach(function (item) {
-          ids += 'ids=' + item.id + '&';
-        });
-        ids = ids.substring(0, ids.lastIndexOf('&'));
+        // var ids = '';
+        // cutList.forEach(function (item) {
+        //   ids += 'ids=' + item.id + '&';
+        // });
+
+        // ids = ids.substring(0, ids.lastIndexOf('&'));
+
         $.confirm('确定清空剪贴板?', function () {
           $.ajax({
-            url: baseUrl + '/api/public/v5/shear/content?' + ids,
+            url:
+              baseUrl +
+              '/api/public/v5/shear/content' +
+              Qs.stringify(
+                {
+                  ids: Array.from($('.slide-wrapper')).map((v) => v.dataset.id),
+                },
+                { arrayFormat: 'repeat', addQueryPrefix: true },
+              ),
             headers: {
               Authorization: token,
             },
@@ -995,6 +1141,16 @@
           });
       }
 
+      $('#wine').on('click', function (e) {
+        console.log('🚀 ~ file: WXdraw.js ~ line 4 ~ e', e);
+        $('.control-right-img').attr({
+          'data-id': '1',
+        });
+        $('.leftmains').css({
+          right: '-4rem',
+        });
+      });
+
       var btnMuted = document.querySelector('#btnMuted');
       btnMuted &&
         (function () {
@@ -1349,6 +1505,19 @@
           getAD();
         });
       }
+      // 观看广告次数上报
+      function reportFrequency() {
+        $.ajax({
+          url: baseUrl + '/api/resoures/v1/trial/reportFrequency/' + userCardId,
+          headers: {
+            Authorization: token,
+          },
+          type: 'post',
+          contentType: 'application/json',
+          dataType: 'json',
+          success: function (res) {},
+        });
+      }
       //广告信息
       // function adInit() {
       //   sourceType = parameters['sourceType'];
@@ -1449,23 +1618,10 @@
           success: function (res) {},
         });
       }
-      // 观看广告次数上报
-      function reportFrequency() {
-        $.ajax({
-          url: baseUrl + '/api/resoures/v1/trial/reportFrequency/' + form.userCardId,
-          headers: {
-            Authorization: form.token
-          },
-          type: 'post',
-          contentType: 'application/json',
-          dataType: 'json',
-          success: function (res) {},
-        });
-      }
       //关闭广告
       $('.time-close-wrap')[0].addEventListener('click', () => {
-        if(videoTime == 0) {
-          reportFrequency()
+        if (videoTime == 0) {
+          reportFrequency();
         }
         $('.buy-phone-wrap').eq(0).show();
       });
@@ -1573,7 +1729,7 @@
           });
         } else {
           uni.webView.navigateBack({
-	          delta: 1
+            delta: 1,
           });
         }
       }
@@ -1581,6 +1737,151 @@
         ws.close();
         wsss.close();
       };
+      function updatePhoneSizeListHtml() {
+        const templatePhoneSizeItem = doT.template(
+          $('#template-phone-size-item').html().replace(/&amp;/g, '&'),
+        );
+        // console.log({
+        //   list: window.phoneSizeList,
+        //   active: window.activePhoneSize,
+        //   resolving,
+        // });
+        const phoneSizeListItemsHtml = templatePhoneSizeItem({
+          list: window.phoneSizeList,
+          active: window.activePhoneSize,
+          resolving,
+        });
+        $('#phone-size-list').html(phoneSizeListItemsHtml);
+      }
+      $('#open-set-phone-size-dialog-btn').on('click', function (e) {
+        window.activePhoneSize = window.currentPhoneSize;
+        updatePhoneSizeListHtml();
+        $('#set-phone-size-dialog').addClass('show');
+      });
+      // $('#set-phone-size-dialog').addClass('show');
+      $('.dialog .dialog-mask')
+        .add('.dialog .dialog-btn.cancel')
+        .on('click', function (e) {
+          $(e.currentTarget).parents('.dialog').removeClass('show');
+        });
+
+      // 分辨率列表
+      window.phoneSizeList = [];
+      // 当前生效的分辨率
+      // 选中的分辨率
+      // window.activePhoneSize = window.currentPhoneSize;
+
+      function getPhoneSizeList() {
+        return $.ajax({
+          url:
+            baseUrl + '/api/resources/v5/machine/resolution/getResolvingPower',
+          headers: {
+            Authorization: token,
+          },
+          type: 'get',
+          dataType: 'json',
+          data: {
+            userCardId,
+          },
+        }).then(function (response) {
+          return response.data.map(function (v) {
+            return {
+              id: v.id,
+              dpi: v.dpi,
+              width: v.width,
+              height: v.high,
+            };
+          });
+        });
+      }
+
+      /*
+      getPhoneSizeList().then(function (phoneSizeList) {
+        window.phoneSizeList = phoneSizeList;
+        // window.phoneSizeListBack = phoneSizeList; // 备份下数据,用户横竖切换时。
+        const currentPhoneSize = window.phoneSizeList.find(function (v) {
+          return (
+            window.currentPhoneSize &&
+            v.width === window.currentPhoneSize.width &&
+            v.height === window.currentPhoneSize.height &&
+            v.dpi === window.currentPhoneSize.dpi
+          );
+        });
+
+        window.currentPhoneSize = currentPhoneSize || window.currentPhoneSize;
+        // return updatePhoneSizeListHtml();
+      });
+      */
+
+      // const phoneSizeListSwiper = new Swiper('#phone-size-list-swiper', {
+      //   direction: 'vertical',
+      //   slidesPerView: 'auto',
+      //   loop: false,
+      //   centeredSlides: true,
+      // });
+      let lastSetPhone = 0;
+      function setPhoneSize(config) {
+        if (config.id === currentPhoneSize.id) {
+          return;
+        }
+
+        if (Date.now() <= lastSetPhone + 1000 * 5) {
+          throw new Error('请勿频繁操作');
+        }
+        lastSetPhone = Date.now();
+        // config = Object.assign({}, config, {
+        //   width: config.width,
+        //   height: config.height,
+        // });
+        // 修改云机分辨率
+        wsss.send(
+          JSON.stringify({
+            type: 'setPhoneSize',
+            data: {
+              id: config.id,
+              width: config.width,
+              height: config.height,
+              dpi: config.dpi,
+            },
+          }),
+        );
+        // 通知其他在线端
+        // wsss.send(
+        //   JSON.stringify({
+        //     type: 'forwardMsg',
+        //     data: {
+        //       code: 'phoneSizeChange',
+        //       id: config.id,
+        //       width: config.width,
+        //       height: config.height,
+        //       dpi: config.dpi,
+        //       desc: '分辨率修改', // 可选
+        //     },
+        //   }),
+        // );
+        // 上报分辨率
+        window.currentPhoneSize = config;
+      }
+      // wsss.addEventListener('message', function (event) {
+      //   console.log(
+      //     '🚀 ~ file: WXtrialInterface.html ~ line 1476 ~ event',
+      //     event,
+      //   );
+      // });
+      $('#phone-size-list').on('click', '.phone-size-item', function (e) {
+        const data = $(e.currentTarget).data();
+        window.activePhoneSize = data;
+        updatePhoneSizeListHtml();
+        // setPhoneSize(data.width, data.height);
+      });
+      $('#set-phone-size-dialog .dialog-btn.confirm').on('click', function (e) {
+        try {
+          setPhoneSize(window.activePhoneSize);
+          $('#set-phone-size-dialog').removeClass('show');
+        } catch (error) {
+          $.toast(error.message, 'text');
+        }
+      });
     </script>
     <script type="text/javascript" src="WXdraw.js"></script>
     <script type="text/javascript" src="aac.js"></script>

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 590 - 439
static/screenAndroid/css/WXtrialInterface.css


+ 470 - 156
static/screenIos/WXdraw.js

@@ -1,15 +1,15 @@
 // 蒙版
-var canvas_bak = document.getElementById("box");;
+var canvas_bak = document.getElementById('box');
 
 var winHeight = window.screen.height - window.innerHeight;
 var vowidth = window.screen.width;
 var topwinHeightDraw = window.screen.height - window.innerHeight + 30; //计算title top 头部
-var numse = window.screen.height // -winHeight
+var numse = window.screen.height; // -winHeight
 //计算title top 头部
 if (numse <= 70) {
-  var voheight = window.screen.height - winHeight - 34 - 20
+  var voheight = window.screen.height - winHeight - 34 - 20;
 } else {
-  var voheight = window.screen.height - topwinHeightDraw - 20
+  var voheight = window.screen.height - topwinHeightDraw - 20;
 }
 
 //画笔大小
@@ -20,18 +20,24 @@ url = url.split('/');
 var parameters = GetRequest();
 
 var videoWidth, videoHeight;
-var isControl = true; // 是否是观看模式
+var isControl = false; // 是否是观看模式
+changIsControl(true);
 var isAuth = parameters['authPhone']; // 是否是获取的云手机
 var wsss;
 var errorTime = 0;
 var first = true;
+function changIsControl(value) {
+  isControl = value;
+  // if(){}
+  $('#open-set-phone-size-dialog-btn').attr('hidden', !value);
+}
 
 function throttle(fn, delay) {
   var flag = true;
-  errorTime += delay;
   return () => {
     if (!flag) return;
     flag = false;
+    errorTime += delay;
     timer = setTimeout(() => {
       fn();
       flag = true;
@@ -39,80 +45,196 @@ function throttle(fn, delay) {
   };
 }
 
+const throttleDoConnectDirectives = throttle(() => {
+  doConnectDirectives();
+}, 100);
+
 function doConnectDirectives() {
-  videoWidth = Number(resolvingPower) ? Number(resolvingPower) : 720
-  videoHeight = videoWidth === 720 ? 1280 : 1920
+  videoWidth = Number(resolvingPower) ? Number(resolvingPower) : 720;
+  videoHeight = videoWidth === 720 ? 1280 : 1920;
   wsss = new WebSocket(cUrl);
   wsss.binaryType = 'arraybuffer';
 
   wsss.onopen = function () {
     // 获取虚拟场景状态
-    var pings = { "type": "getVsStatus" }
+    errorTime = 0;
+    var pings = { type: 'getVsStatus' };
     wsss.send(JSON.stringify(pings));
     var bitRate = {
-      "data": {
-        "bitRate": 1243000
+      data: {
+        bitRate: 1243000,
       },
-      "type": "bitRate"
-    }
+      type: 'bitRate',
+    };
     wsss.send(JSON.stringify(bitRate));
     // 进入发起询问
     var pings2 = {
-      "type": "forwardMsg",
-      "data": {
-        "code": "3000",
-        "desc": "询问是否有在控制" // 可选
-      }
-    }
+      type: 'forwardMsg',
+      data: {
+        code: '3000',
+        username,
+        desc: '询问是否有在控制', // 可选
+      },
+    };
     wsss.send(JSON.stringify(pings2));
+
+    wsss.send(
+      JSON.stringify({
+        type: 'getPhoneSize',
+      }),
+    );
+  };
+  wsss.onerror = function (e) {
+    // console.log('🚀 ~ file: WXdraw.js ~ line 82 ~ onerror ~ e', e);
+    wsss.close(1006);
+    // throttle(doConnectDirectives, 100);
+    // if (errorTime > 1000) {
+    //   quit();
+    // }
   };
-  wsss.onerror = function () {
-    wsss.close();
-    throttle(doConnectDirectives, 100);
-    if (errorTime > 1000) {
-      quit();
+  wsss.onclose = function (e) {
+    // console.log('🚀 ~ file: WXdraw.js ~ line 93 ~ onclose ~ e', e);
+    // new WebSocket(e.)
+    // doConnectDirectives();
+    if (e.code === 1006) {
+      // 异常关闭,重连
+      throttleDoConnectDirectives();
+      // doConnectDirectives();
+      // throttle(doConnectDirectives, 100);
+      if (errorTime > 1000) {
+        quit();
+      }
     }
   };
   wsss.onmessage = function (res) {
-    var result = typeof res.data === 'string' ? JSON.parse(res.data) : res.data
+    var result = typeof res.data === 'string' ? JSON.parse(res.data) : res.data;
+    console.log(
+      '🚀 ~ file: WXdraw.js ~ line 78 ~ doConnectDirectives ~ result',
+      result,
+    );
+
     if (result.type === 'cutting') {
       if (result.data.status === 0) {
-        $.toast('复制成功', "text");
+        $.toast('复制成功', 'text');
       } else {
-        $.toast(result.msg, "text");
+        $.toast(result.msg, 'text');
       }
-      return
+      return;
+    }
+    if (result.type === 'forwardMsgRep') {
+      // 当前云机无其他终端在线,获得控制权
+      changIsControl(true);
     }
-    if (result.type === 'forwardMsg' && isAuth !== 'none') {
-      if (result.data.code === 4000 || result.data.code === 3000) {
-        if (isAuth === 'huo') {
-          $.confirm("授权方已收回控制权,您进入观看屏幕模式", function () {
-            //点击确认后的回调函数
-            isControl = false;
-          }, function () {
-            isControl = false;
-            //点击取消后的回调函数
-            quit();
-          });
-        } else {
-          $.confirm("当前云手机正在授控,是否请求获取云手机控制权?", function () {
-            //点击确认后的回调函数
-            var ping = {
-              "type": "forwardMsg",
-              "data": {
-                "code": "5000",
-                "desc": "控制权限收回" // 可选
-              }
+    if (result.type === 'forwardMsg') {
+      /**
+       * @type {boolean} isControl 当前是否拥有控制权,初始化时为false
+       * @type {string} isAuth 当前云机类型 - huo: 获取的云机,none: 自己的云机
+       * @type {string} username 当前登录的双子星账号username
+       */
+
+      if (result.data.username !== username) {
+        switch (String(result.data.code)) {
+          case '3000': {
+            if (isControl) {
+              // 回复有控制
+              wsss.send(
+                JSON.stringify({
+                  type: 'forwardMsg',
+                  data: {
+                    code: '4000',
+                    username,
+                    desc: '有控制', // 可选
+                  },
+                }),
+              );
+              return;
             }
-            wsss.send(JSON.stringify(ping));
-            isControl = true;
-          }, function () {
-            //点击取消后的回调函数
-            isControl = false;
-          });
+            // 回复有观看
+            wsss.send(
+              JSON.stringify({
+                type: 'forwardMsg',
+                data: {
+                  code: '4100',
+                  username,
+                  desc: '有观看', // 可选
+                },
+              }),
+            );
+            return;
+          }
+          case '4000': {
+            // 当前是获取方
+            if (isAuth === 'huo' && isControl) {
+              $.confirm(
+                '授权方已收回控制权,您进入观看屏幕模式',
+                function () {
+                  //点击确认后的回调函数
+                  changIsControl(false);
+                },
+                function () {
+                  changIsControl(false);
+                  //点击取消后的回调函数
+                  quit();
+                },
+              );
+              return;
+            }
+            if (!isControl && isAuth === 'shou') {
+              // 当前是授权方切没有控制权
+              $.confirm(
+                '当前云手机正在授控,是否请求获取云手机控制权?',
+                function () {
+                  //点击确认后的回调函数
+                  wsss.send(
+                    JSON.stringify({
+                      type: 'forwardMsg',
+                      data: {
+                        code: '5000',
+                        username,
+                        desc: '控制权限收回', // 可选
+                      },
+                    }),
+                  );
+                  changIsControl(true);
+                },
+                function () {
+                  //点击取消后的回调函数
+                  changIsControl(false);
+                },
+              );
+
+              return;
+            }
+          }
+          case '5000': {
+            // if (result.data.username === username) {
+            //   changIsControl(true);
+            //   return;
+            // }
+            if (isAuth === 'huo' && isControl) {
+              $.confirm(
+                '授权方已收回控制权,您进入观看屏幕模式',
+                function () {
+                  //点击确认后的回调函数
+                  changIsControl(false);
+                },
+                function () {
+                  changIsControl(false);
+                  //点击取消后的回调函数
+                  quit();
+                },
+              );
+              return;
+            }
+
+            return;
+          }
+          default: {
+            return;
+          }
         }
       }
-      return
+      return;
     }
     if (result.type === 'payInitiateEvent') {
       var url = window.location.href;
@@ -129,149 +251,341 @@ function doConnectDirectives() {
         dataType: 'json',
         contentType: 'application/json;charset=UTF-8',
         success: function (res) {
-          if(result.data.payType === 1) { // 微信
-            if (window.__wxjs_environment === 'miniprogram') { // 小程序
+          if (result.data.payType === 1) {
+            // 微信
+            if (window.__wxjs_environment === 'miniprogram') {
+              // 小程序
               // copyUrl(result.data.payUrl);
             } else {
-              window.location.href = result.data.payUrl
+              window.location.href = result.data.payUrl;
             }
           } else {
-            window.location.href = result.data.payUrl
+            window.location.href = result.data.payUrl;
           }
         },
       });
-      return
+      return;
     }
-  }
+    if (result.type === 'getPhoneSize' || result.type === 'setPhoneSize') {
+      // console.log(result);
+      if (
+        window.currentPhoneSize &&
+        (window.currentPhoneSize.width !==
+          Math.min(result.data.width, result.data.height) ||
+          window.currentPhoneSize.height !==
+            Math.max(result.data.width, result.data.height) ||
+          window.currentPhoneSize.dpi !== result.data.dpi)
+      ) {
+        // 获取到的分辨率与当前分辨率不符
+
+        const data = window.phoneSizeList.find(function (v) {
+          return (
+            v.width === Math.min(result.data.width, result.data.height) &&
+            v.height === Math.max(result.data.width, result.data.height) &&
+            v.dpi === result.data.dpi
+          );
+        });
+        window.currentPhoneSize = data || {
+          width: Math.min(result.data.width, result.data.height),
+          height: Math.max(result.data.width, result.data.height),
+          dpi: result.data.dpi,
+        };
+
+        if (result.type === 'setPhoneSize') {
+          lastSetPhone = Date.now();
+        }
+        // if (result.type === 'getPhoneSize') {
+        // 上报给后端
+        $.ajax({
+          url:
+            baseUrl +
+            '/api/resources/v5/machine/resolution/operationResolvingPower',
+          headers: {
+            Authorization: token,
+          },
+          type: 'post',
+          dataType: 'json',
+          contentType: 'application/json; charset=UTF-8',
+          data: JSON.stringify({
+            userCardId: window.userCardId,
+            resolvingPowerId: window.currentPhoneSize.id,
+          }),
+        });
+      }
+      // }
+
+      updateDB(db, storeName, {
+        id: userCardId,
+        socketURL: socketURL,
+        cUrl: cUrl,
+        cardToken: cardToken,
+        resolvingPower: resolvingPower,
+        width: window.currentPhoneSize.width,
+        height: window.currentPhoneSize.height,
+        dpi: window.currentPhoneSize.dpi,
+      });
+
+      return;
+    }
+  };
 }
-$('body').on("click", function () {
-  draw_graph('pencil', this)
-})
+$('body').on('click', function () {
+  draw_graph('pencil', this);
+});
 //剪切板
-$(".upload").on("click", function () {
-  var texts = $(this).attr("data-text")
-  if (texts == "uploads") {
-    $(".mainbox").css({
-      "display": "block"
-    })
-    $(".sbox").css({
-      "display": "none"
-    })
+$('.upload').on('click', function () {
+  var texts = $(this).attr('data-text');
+  if (texts == 'uploads') {
+    $('.mainbox').css({
+      display: 'block',
+    });
+    $('.sbox').css({
+      display: 'none',
+    });
   }
-})
+});
 
 //home 控制home
-$(".botmat1img").on("click", function () {
-  var codes = $(this).attr("data-text")
-  if (codes == "home" && isControl) {
+$('.botmat1img').on('click', function () {
+  var codes = $(this).attr('data-text');
+  if (codes == 'home' && isControl) {
     wsss.send(ExexuteKeyBoard(3));
-  } else if (codes == "return" && isControl) {
+  } else if (codes == 'return' && isControl) {
     wsss.send(ExexuteKeyBoard(4));
-  } else if (codes == "gengduo" && isControl) {
+  } else if (codes == 'gengduo' && isControl) {
     wsss.send(ExexuteKeyBoard(187));
   }
-})
+});
 // 高清控制
-$(".PictureQuality").on("click", function () {
+$('.PictureQuality').on('click', function () {
   if (!isControl) {
-    return
+    return;
   }
-  $(this).addClass("avit").siblings().removeClass('avit')
-  var id = $(this).attr("data-id")
+  $(this).addClass('avit').siblings().removeClass('avit');
+  var id = $(this).attr('data-id');
   var cmd = {
-    type: "switchSharpness",
+    type: 'switchSharpness',
   };
 
-  decodeWoker.postMessage(cmd);//通知解码器worker切换分辨率		
+  decodeWoker.postMessage(cmd); //通知解码器worker切换分辨率
   var buffer = makeSharpness(Number(id));
   webSocketWorker.postMessage(buffer);
-})
+});
 var canDraw = false;
 //画图形
 var draw_graph = function (graphType) {
   //把蒙版放于画板上面
-  $("#container").css("z-index", 30);
-  $("#dedit").css("z-index", 20);
+  $('#container').css('z-index', 30);
+  $('#dedit').css('z-index', 20);
   // 先画在蒙版上 再复制到画布上
   //鼠标按下获取 开始xy开始画图
-  var ongoingTouches = [];
+  // var ongoingTouches = [];
   var touchstart = function (e) {
+    // console.log('🚀 ~ file: WXdraw.js ~ line 244 ~ touchstart ~ e', e);
+
     if (!isControl) {
-      return
-    }
-    $('.control-right-img').attr({
-      "data-id": "2"
-    })
-    $(".leftmains").css({
-      "right": "-4rem"
-    })
-    var touchfor = e.originalEvent.changedTouches; //for 的手指数组
-    //是否横屏
-    for (var i = 0; i < touchfor.length; i++) {
-      var acrossWidthX = touchfor[i].pageY * (videoHeight / voheight);
-      var acrossHeightY = videoWidth - touchfor[i].pageX * (videoWidth / vowidth);
-      var verticalWidthX = touchfor[i].pageX * (videoWidth / vowidth);
-      var verticalHeightY = touchfor[i].pageY * (videoHeight / voheight);
-      var idx = ongoingTouches.findIndex(function (ele) {
-        return ele.identifier === touchfor[i].identifier
-      })
-      if (idx < 0) {
-        ongoingTouches.push(touchfor[i]);
-      }
-      var ping = resolving == 0 ?
-        { "data": { "action": 0, "count": ongoingTouches.length, "pointerId": touchfor[i].identifier, "x": acrossWidthX.toFixed(2), "y": acrossHeightY.toFixed(2) }, "type": "event" } :
-        { "data": { "action": 0, "count": ongoingTouches.length, "pointerId": touchfor[i].identifier, "x": verticalWidthX.toFixed(2), "y": verticalHeightY.toFixed(2) }, "type": "event" };
-      wsss.send(JSON.stringify(ping));
+      return;
     }
+    const action = 0;
+    Array.from(e.originalEvent.changedTouches).forEach(function (item, index) {
+      const x = item.clientX - item.target.getBoundingClientRect().x;
+      const y = item.clientY - item.target.getBoundingClientRect().y;
+      return wsss.send(
+        JSON.stringify({
+          type: 'event',
+          data: {
+            action,
+            count: e.originalEvent.touches.length,
+            pointerId: item.identifier,
+            x: (function () {
+              return (
+                resolving
+                  ? x * (window.currentPhoneSize.width / vowidth)
+                  : y * (window.currentPhoneSize.height / voheight)
+              ).toFixed(2);
+            })(),
+            y: (function () {
+              return (
+                resolving
+                  ? y * (window.currentPhoneSize.height / voheight)
+                  : (vowidth - x) * (window.currentPhoneSize.width / vowidth)
+              ).toFixed(2);
+            })(),
+          },
+        }),
+      );
+    });
     canDraw = true;
   };
 
   //鼠标离开 把蒙版canvas的图片生成到canvas中
   var touchend = function (e) {
     if (!isControl) {
-      return
-    }
-    var touchfor = e.originalEvent.changedTouches; //for 的手指数组
-    //是否横屏
-    for (var i = 0; i < touchfor.length; i++) {
-      var acrossWidthX = touchfor[i].pageY * (videoHeight / voheight);
-      var acrossHeightY = videoWidth - touchfor[i].pageX * (videoWidth / vowidth);
-      var verticalWidthX = touchfor[i].pageX * (videoWidth / vowidth);
-      var verticalHeightY = touchfor[i].pageY * (videoHeight / voheight);
-      var ping = resolving == 0 ?
-        { "data": { "action": 1, "count": ongoingTouches.length, "pointerId": touchfor[i].identifier, "x": acrossWidthX.toFixed(2), "y": acrossHeightY.toFixed(2) }, "type": "event" } :
-        { "data": { "action": 1, "count": ongoingTouches.length, "pointerId": touchfor[i].identifier, "x": verticalWidthX.toFixed(2), "y": verticalHeightY.toFixed(2) }, "type": "event" };
-      wsss.send(JSON.stringify(ping));
-      ongoingTouches.forEach(function (item, index) {
-        if (item.identifier === touchfor[i].identifier) {
-          ongoingTouches.splice(index, 1)
-        }
-      })
+      return;
     }
+    const action = 1;
+    Array.from(e.originalEvent.changedTouches).forEach(function (item, index) {
+      const x = item.clientX - item.target.getBoundingClientRect().x;
+      const y = item.clientY - item.target.getBoundingClientRect().y;
+      return wsss.send(
+        JSON.stringify({
+          type: 'event',
+          data: {
+            action,
+            count: e.originalEvent.touches.length,
+            pointerId: item.identifier,
+            x: (function () {
+              return (
+                resolving
+                  ? x * (window.currentPhoneSize.width / vowidth)
+                  : y * (window.currentPhoneSize.height / voheight)
+              ).toFixed(2);
+            })(),
+            y: (function () {
+              return (
+                resolving
+                  ? y * (window.currentPhoneSize.height / voheight)
+                  : (vowidth - x) * (window.currentPhoneSize.width / vowidth)
+              ).toFixed(2);
+            })(),
+          },
+        }),
+      );
+    });
+    // var touchfor = e.originalEvent.changedTouches; //for 的手指数组
+    // //是否横屏
+    // for (var i = 0; i < touchfor.length; i++) {
+    //   // var acrossWidthX = touchfor[i].pageY * (videoHeight / voheight);
+    //   // var acrossHeightY =
+    //   //   videoWidth - touchfor[i].pageX * (videoWidth / vowidth);
+    //   // if (resolving) {
+    //   //   var verticalWidthX =
+    //   //     touchfor[i].pageX * (window.currentPhoneSize.width / vowidth);
+    //   //   var verticalHeightY =
+    //   //     touchfor[i].pageY * (window.currentPhoneSize.height / voheight);
+    //   // } else {
+    //   //   var verticalWidthX =
+    //   //     touchfor[i].pageX * (window.currentPhoneSize.width / vowidth);
+    //   //   var verticalHeightY =
+    //   //     touchfor[i].pageY * (window.currentPhoneSize.height / voheight);
+    //   // }
+    //   var ping =
+    //     // resolving == 0 ?
+    //     //   { "data": { "action": 1, "count": ongoingTouches.length, "pointerId": touchfor[i].identifier, "x": acrossWidthX.toFixed(2), "y": acrossHeightY.toFixed(2) }, "type": "event" } :
+    //     {
+    //       data: {
+    //         action: 1,
+    //         count: ongoingTouches.length,
+    //         pointerId: touchfor[i].identifier,
+    //         x: (() =>
+    //           (resolving
+    //             ? touchfor[i].pageX * (window.currentPhoneSize.width / vowidth)
+    //             : touchfor[i].pageY * (window.currentPhoneSize.width / voheight)
+    //           ).toFixed(2))(),
+    //         y: (() =>
+    //           (resolving
+    //             ? touchfor[i].pageY *
+    //               (window.currentPhoneSize.height / voheight)
+    //             : (vowidth - touchfor[i].pageX) *
+    //               (window.currentPhoneSize.height / vowidth)
+    //           ).toFixed(2))(),
+    //       },
+    //       type: 'event',
+    //     };
+    //   wsss.send(JSON.stringify(ping));
+    //   ongoingTouches.forEach(function (item, index) {
+    //     if (item.identifier === touchfor[i].identifier) {
+    //       ongoingTouches.splice(index, 1);
+    //     }
+    //   });
+    // }
     canDraw = false;
   };
 
   //清空层 云手机超出屏幕的开关
   var clearContext = function () {
     canDraw = false;
-  }
+  };
 
   // 鼠标移动
   var touchmove = function (e) {
+    e.preventDefault();
     if (!isControl) {
-      return
-    }
-    var touchfor = e.originalEvent.targetTouches; //for 的手指数组
-    for (var i = 0; i < touchfor.length; i++) {
-      var acrossWidthX = touchfor[i].pageY * (videoHeight / voheight);
-      var acrossHeightY = videoWidth - touchfor[i].pageX * (videoWidth / vowidth);
-      var verticalWidthX = touchfor[i].pageX * (videoWidth / vowidth);
-      var verticalHeightY = touchfor[i].pageY * (videoHeight / voheight);
-      var ping = resolving == 0 ?
-        { "data": { "action": 2, "count": touchfor.length, "pointerId": touchfor[i].identifier, "x": acrossWidthX.toFixed(2), "y": acrossHeightY.toFixed(2) }, "type": "event" } :
-        { "data": { "action": 2, "count": touchfor.length, "pointerId": touchfor[i].identifier, "x": verticalWidthX.toFixed(2), "y": verticalHeightY.toFixed(2) }, "type": "event" };
-      wsss.send(JSON.stringify(ping));
+      return;
     }
+    const action = 2;
+    Array.from(e.originalEvent.changedTouches).forEach(function (item, index) {
+      const x = item.clientX - item.target.getBoundingClientRect().x;
+      const y = item.clientY - item.target.getBoundingClientRect().y;
+      return wsss.send(
+        JSON.stringify({
+          type: 'event',
+          data: {
+            action,
+            count: e.originalEvent.touches.length,
+            pointerId: item.identifier,
+            x: (function () {
+              return (
+                resolving
+                  ? x * (window.currentPhoneSize.width / vowidth)
+                  : y * (window.currentPhoneSize.height / voheight)
+              ).toFixed(2);
+            })(),
+            y: (function () {
+              return (
+                resolving
+                  ? y * (window.currentPhoneSize.height / voheight)
+                  : (vowidth - x) * (window.currentPhoneSize.width / vowidth)
+              ).toFixed(2);
+            })(),
+          },
+        }),
+      );
+    });
+    // var touchfor = e.originalEvent.targetTouches; //for 的手指数组
+    // for (var i = 0; i < touchfor.length; i++) {
+    //   // var acrossWidthX = touchfor[i].pageY * (videoHeight / voheight);
+    //   // var acrossHeightY =
+    //   //   videoWidth - touchfor[i].pageX * (videoWidth / vowidth);
+
+    //   // let verticalWidthX = 0;
+    //   // let verticalHeightY = 0;
+    //   // if (resolving) {
+    //   //   verticalWidthX =
+    //   //     touchfor[i].pageX * (window.currentPhoneSize.width / vowidth);
+    //   //   verticalHeightY =
+    //   //     touchfor[i].pageY * (window.currentPhoneSize.height / voheight);
+    //   // } else {
+    //   //   verticalWidthX =
+    //   //     touchfor[i].pageX * (window.currentPhoneSize.width / vowidth);
+    //   //   verticalHeightY =
+    //   //     touchfor[i].pageY * (window.currentPhoneSize.height / voheight);
+    //   // }
+    //   var ping =
+    //     // resolving == 0 ?
+    //     //   { "data": { "action": 2, "count": touchfor.length, "pointerId": touchfor[i].identifier, "x": acrossWidthX.toFixed(2), "y": acrossHeightY.toFixed(2) }, "type": "event" } :
+    //     {
+    //       data: {
+    //         action: 2,
+    //         count: touchfor.length,
+    //         pointerId: touchfor[i].identifier,
+    //         x: (() =>
+    //           (resolving
+    //             ? touchfor[i].pageX * (window.currentPhoneSize.width / vowidth)
+    //             : touchfor[i].pageY * (window.currentPhoneSize.width / voheight)
+    //           ).toFixed(2))(),
+    //         y: (() =>
+    //           (resolving
+    //             ? touchfor[i].pageY *
+    //               (window.currentPhoneSize.height / voheight)
+    //             : (vowidth - touchfor[i].pageX) *
+    //               (window.currentPhoneSize.height / vowidth)
+    //           ).toFixed(2))(),
+    //       },
+    //       type: 'event',
+    //     };
+    //   wsss.send(JSON.stringify(ping));
+    // }
   };
 
   //鼠标离开区域以外 除了涂鸦 都清空
@@ -279,22 +593,22 @@ var draw_graph = function (graphType) {
     if (graphType != 'handwriting') {
       clearContext();
     }
-  }
-  $(canvas_bak).unbind();
-  $(canvas_bak).bind('touchstart', touchstart);
-  $(canvas_bak).bind('touchmove', touchmove);
-  $(canvas_bak).bind('touchend', touchend);
-  $(canvas_bak).bind('mouseout', mouseout);
-}
+  };
+  $(canvas_bak).off();
+  $(canvas_bak).on('touchstart', touchstart);
+  $(canvas_bak).on('touchmove', touchmove);
+  $(canvas_bak).on('touchend', touchend);
+  $(canvas_bak).on('mouseout', mouseout);
+};
 
 function GetRequest() {
   var url = location.search; // 获取url中"?"符后的字串
   var obj = new Object();
-  if (url.indexOf("?") != -1) {
+  if (url.indexOf('?') != -1) {
     var str = url.substr(1);
-    strs = str.split("&");
+    strs = str.split('&');
     for (var i = 0; i < strs.length; i++) {
-      obj[strs[i].split("=")[0]] = (strs[i].split("=")[1]);
+      obj[strs[i].split('=')[0]] = strs[i].split('=')[1];
     }
   }
   return obj;

+ 402 - 66
static/screenIos/WXtrialInterface.html

@@ -43,9 +43,54 @@
       rel="stylesheet"
       href="https://cdn.bootcss.com/jquery-weui/1.2.1/css/jquery-weui.min.css"
     />
+    <!-- <link rel="stylesheet" href="../static/lib/swiper/swiper-bundle.min.css" />
+    <script src="../static/lib/swiper/swiper-bundle.js"></script> -->
+    <script src="../static/lib/doT-1.1.3/doT.min.js"></script>
+    <script src="../static/lib/qs.js"></script>
   </head>
 
   <body class="scroll h-player" style="overscroll-behavior: contain">
+    <template id="template-phone-size-item">
+      {{~ it.list :value:index }}
+      <div
+        class="phone-size-item {{? value.width === it.active.width && value.height === it.active.height }}active{{?}}"
+        data-id="{{= value.id }}"
+        data-dpi="{{= value.dpi }}"
+        data-width="{{= value.width }}"
+        data-height="{{= value.height }}"
+      >
+        <span>{{= value.width }}x{{= value.height }}</span>
+      </div>
+      {{~}}
+    </template>
+    <template id="template-shear">
+      {{? it.length }}
+      <div class="title">
+        剪贴板
+        <div class="btn-clear">清空</div>
+      </div>
+      <div class="slide-wrapper-content">
+        {{~ it :item:index }}
+        <div class="slide-wrapper" data-id="{{= item.id}}">
+          <div class="slide-scroll animate-slide-start">
+            <div
+              class="slide-content"
+              data-content="{{= encodeHtml(item.content)}}"
+            >
+              <div>{{= encodeHtml(item.content)}}</div>
+            </div>
+            <div class="slide-content-button" data-id="{{= item.id}}">
+              <button>删除</button>
+            </div>
+          </div>
+        </div>
+        {{~}}
+      </div>
+      {{??}}
+      <img class="empty" src="img/jianqieban_pic@2x.png" alt="" />
+      <div class="empty-txt">剪贴板为空</div>
+      {{?}}
+    </template>
     <div class="container" id="player">
       <div class="muted" id="btnMuted">
         <div class="control-right-img" data-id="1">
@@ -59,10 +104,15 @@
       </div>
       <div class="leftmains">
         <div class="PictureQualityMain">
-          <div class="PictureQuality" data-id="4">高清</div>
-          <div class="PictureQuality avit" data-id="3">标清</div>
-          <div class="PictureQuality" data-id="2">极速</div>
+          <div class="menu-btn PictureQuality" data-id="4">高清</div>
+          <div class="menu-btn PictureQuality avit" data-id="3">标清</div>
+          <div class="menu-btn PictureQuality" data-id="2">极速</div>
         </div>
+        <!-- <div class="open-set-phone-size-dialog-btn-wrap">
+          <div class="menu-btn" id="open-set-phone-size-dialog-btn">
+            <span>分辨率</span>
+          </div>
+        </div> -->
         <div class="operation">
           <div class="upload" id="showsuss" data-text="uploads">
             <img src="../static/img/wx/shangchuan_icon.png" />
@@ -149,6 +199,26 @@
         </div>
       </div>
     </div>
+
+    <div id="set-phone-size-dialog" class="dialog">
+      <div class="dialog-mask"></div>
+      <!-- 设置分辨率的弹窗 -->
+      <div class="dialog-content-border">
+        <div class="dialog-content">
+          <div class="dialog-header">
+            <div class="dialog-btn cancel">
+              <span>取消</span>
+            </div>
+            <div class="dialog-btn confirm">
+              <span>确定</span>
+            </div>
+          </div>
+          <div class="dialog-main">
+            <div id="phone-size-list"></div>
+          </div>
+        </div>
+      </div>
+    </div>
     <script
       type="text/javascript"
       src="https://res.wx.qq.com/open/js/jweixin-1.3.2.js"
@@ -254,10 +324,10 @@
       // 开发环境
       var isDev =
         /^192\.168\./.test(location.host) || /^localhost/.test(location.host);
-      if (isDev) {
-        baseUrl = 'http://gntest.phone.androidscloud.com:1280';
-        sourceType = 2;
-      }
+      // if (isDev) {
+      //   baseUrl = 'http://gntest.phone.androidscloud.com:1280';
+      //   sourceType = 2;
+      // }
 
       var topwinHeight = window.screen.height - window.innerHeight + 30; //计算title top 头部
       var url = window.location.href;
@@ -269,6 +339,7 @@
       var token = parameters['token'];
       var mealType = parameters['mealType'];
       var userCardId = parameters['userCardId'];
+      var username = parameters['username'];
 
       var videoTimer = null,
         videoTime = 0,
@@ -361,6 +432,13 @@
             cUrl = request.result.cUrl;
             cardToken = request.result.cardToken;
             resolvingPower = request.result.resolvingPower;
+
+            window.currentPhoneSize = {
+              width: request.result.width,
+              height: request.result.height,
+              dpi: request.result.dpi,
+            };
+
             doConnectBusiness();
             doConnectDirectives();
           } else {
@@ -390,7 +468,8 @@
         getDataByKey(db, storeName, userCardId);
       }, 1000);
 
-      var timerInterval = null, timeInterval = 0;
+      var timerInterval = null,
+        timeInterval = 0;
       function connect(type) {
         $.ajax({
           url: baseUrl + '/api/resources/user/cloud/connect',
@@ -406,6 +485,9 @@
           async: false,
           success: function (res) {
             if (res.status === 0) {
+              if (timerInterval) {
+                timerInterval = clearTimeout();
+              }
               if (res.data.internetHttps) {
                 socketURL =
                   'wss://' +
@@ -434,6 +516,9 @@
                     cUrl: cUrl,
                     cardToken: res.data.cardToken,
                     resolvingPower: res.data.resolvingPower,
+                    width: res.data.width,
+                    height: res.data.high,
+                    dpi: res.data.dpi,
                   });
                 } else {
                   updateDB(db, storeName, {
@@ -442,8 +527,23 @@
                     cUrl: cUrl,
                     cardToken: res.data.cardToken,
                     resolvingPower: res.data.resolvingPower,
+                    width: res.data.width,
+                    height: res.data.high,
+                    dpi: res.data.dpi,
                   });
                 }
+                const data = window.phoneSizeList.find(function (v) {
+                  return (
+                    v.width === res.data.width &&
+                    v.height === res.data.height &&
+                    v.dpi === res.data.dpi
+                  );
+                });
+                window.currentPhoneSize = data || {
+                  width: res.data.width,
+                  height: res.data.height,
+                  dpi: res.data.dpi,
+                };
               } else {
                 $.toast('网络异常,请稍后重试', 'text');
                 setTimeout(() => {
@@ -451,25 +551,34 @@
                   quit();
                 }, 3000);
               }
-            } else if (res.status === 5200) {
+              return;
+            }
+            if (res.status === 5200) {
               if (timeInterval > 7) {
                 $.toast('网络异常,请稍后重试', 'text');
-                timerInterval = clearTimeout()
+                timerInterval = clearTimeout();
                 setTimeout(() => {
                   quit();
-                }, 3000)
-                return
+                }, 3000);
+                return;
               }
               timerInterval = setTimeout(() => {
                 connect(type);
                 timeInterval += 1;
-              }, 3000)
-            }  else {
-              $.toast('画面异常,请重新进入', 'text');
+              }, 3000);
+              return;
+            }
+            if (res.status === 5220) {
+              $.toast('云手机正在一键修复中', 'text');
               setTimeout(() => {
                 quit();
-              }, 3000)
+              }, 3000);
+              return;
             }
+            $.toast('画面异常,请重新进入', 'text');
+            setTimeout(() => {
+              quit();
+            }, 3000);
           },
         });
       }
@@ -502,7 +611,43 @@
       var cutList = [];
       let timer,
         isFlag = true;
+
+      const shearTemplate = doT.template(
+        $('#template-shear').html().replace(/&amp;/g, '&'),
+      );
+
+      function updateShearHtml(list) {
+        $('.box-shear-plate').html(shearTemplate(list));
+      }
+      // 对字符串进行html转义
+      function encodeHtml(content) {
+        return [
+          ['<', '&lt;'],
+          ['>', '&gt;'],
+          ['&', '&amp;'],
+          ['"', '&quot;'],
+        ].reduce(function (previousValue, currentValue) {
+          return previousValue.replace(
+            new RegExp(currentValue[0], 'g'),
+            currentValue[1],
+          );
+        }, content);
+      }
+
+      $('.box-shear-plate').on('click', '.slide-content', function (e) {
+        handleCopy(e.currentTarget.dataset.content);
+      });
+      $('.box-shear-plate').on('click', '.btn-clear', function (e) {
+        handleClear();
+      });
+      $('.box-shear-plate').on('click', '.slide-content-button', function (e) {
+        handleDelete(e.currentTarget.dataset.id);
+      });
+
       function showShearPlate() {
+        if (!isControl) {
+          return;
+        }
         stopManyClick(function () {
           new Promise((resolve, reject) => {
             if (window.navigator.clipboard) {
@@ -541,36 +686,9 @@
               type: 'get',
               dataType: 'json',
               success: function (res) {
-                if (res.status === 0) {
-                  if (res.data.length) {
-                    cutList = array_unique(res.data);
-                    var str =
-                      '<div class="title">剪贴板<div onclick="handleClear()" class="btn-clear">清空</div></div><div class="slide-wrapper-content">';
-                    cutList.forEach(function (item) {
-                      str +=
-                        "<div class='slide-wrapper'><div class='slide-scroll animate-slide-start'><div class='slide-content'><div onclick='handleCopy(\"" +
-                        item.content +
-                        '")\'>' +
-                        item.content +
-                        "</div></div><div class='slide-content-button'><button onclick='handleDelete(" +
-                        item.id +
-                        ")'>删除</button></div></div></div>";
-                    });
-                    str += '</div>';
-                    $('.box-shear-plate').append(str);
-                  } else {
-                    $('.box-shear-plate').append(
-                      '<img class="empty" src="img/jianqieban_pic@2x.png" alt="" /><div class="empty-txt">剪贴板为空</div>',
-                    );
-                  }
-                  $('.mask').show();
-                  initSlider();
-                } else {
-                  $('.box-shear-plate').append(
-                    '<img class="empty" src="img/jianqieban_pic@2x.png" alt="" /><div class="empty-txt">剪贴板为空</div>',
-                  );
-                  $('.mask').show();
-                }
+                updateShearHtml(res.status === 0 ? res.data : []);
+                $('.mask').show();
+                initSlider();
               },
             });
           });
@@ -591,14 +709,24 @@
       }
       // 清空剪贴板
       function handleClear() {
-        var ids = '';
-        cutList.forEach(function (item) {
-          ids += 'ids=' + item.id + '&';
-        });
-        ids = ids.substring(0, ids.lastIndexOf('&'));
+        // var ids = '';
+        // cutList.forEach(function (item) {
+        //   ids += 'ids=' + item.id + '&';
+        // });
+
+        // ids = ids.substring(0, ids.lastIndexOf('&'));
+
         $.confirm('确定清空剪贴板?', function () {
           $.ajax({
-            url: baseUrl + '/api/public/v5/shear/content?' + ids,
+            url:
+              baseUrl +
+              '/api/public/v5/shear/content' +
+              Qs.stringify(
+                {
+                  ids: Array.from($('.slide-wrapper')).map((v) => v.dataset.id),
+                },
+                { arrayFormat: 'repeat', addQueryPrefix: true },
+              ),
             headers: {
               Authorization: token,
             },
@@ -726,6 +854,16 @@
           });
       }
 
+      $('#wine').on('click', function (e) {
+        console.log('🚀 ~ file: WXdraw.js ~ line 4 ~ e', e);
+        $('.control-right-img').attr({
+          'data-id': '1',
+        });
+        $('.leftmains').css({
+          right: '-4rem',
+        });
+      });
+
       var btnMuted = document.querySelector('#btnMuted');
       btnMuted &&
         (function () {
@@ -932,10 +1070,13 @@
           case 0:
             break;
           case 1:
+            // console.log(objData);
             if (logicWidth != objData.width || logicHeight != objData.height) {
               logicWidth = objData.width;
               logicHeight = objData.height;
             }
+            // logicWidth = 375
+            // logicHeight = 812
             webglPlayer.renderFrame(
               objData.data,
               logicWidth,
@@ -977,6 +1118,7 @@
             '&cardToken=' +
             encodeURIComponent(cardToken),
         );
+        window.webSocketWorker = webSocketWorker;
         webSocketWorker.onmessage = function (event) {
           var input = event.data;
           if (input[0] == 0xff && isAudioPlay) {
@@ -1006,17 +1148,45 @@
             }
             if (input[23] == 0x05) {
               //横竖屏标识
-              var state = CheckScreenDirection(input.slice(24, 24 + 8));
+              if (input[28] == 0x01 && input[29] == 0x01) {
+                var state = CheckScreenDirection(input.slice(24, 24 + 8));
 
-              if (state == 1) {
-                console.log('安卓卡此时竖屏');
-                //竖屏处理
-                resolving = 1;
-              } else {
-                console.log('安卓卡此时横屏');
-                //横屏处理
-                resolving = 0;
+                if (state == 1) {
+                  console.log('安卓卡此时竖屏');
+                  //竖屏处理
+                  resolving = 1;
+                  // $('#playCanvas').removeClass('horizontal').addClass('vertical');
+                } else {
+                  console.log('安卓卡此时横屏');
+                  //横屏处理
+                  resolving = 0;
+                  // $('#playCanvas').removeClass('vertical').addClass('horizontal');
+                }
               }
+              // window.phoneSizeList = window.phoneSizeListBack.map(function (
+              //   item,
+              // ) {
+              //   return resolving
+              //     ? item
+              //     : Object.assign({}, item, {
+              //         width: item.height,
+              //         height: item.width,
+              //       });
+              // });
+
+              // wsss.send(
+              //   JSON.stringify({
+              //     type: 'getPhoneSize',
+              //   }),
+              // );
+              // 横竖屏变更时获取分辨率要延迟,不然会获取到变更前的分辨率
+              // setTimeout(function () {
+              //   wsss.send(
+              //     JSON.stringify({
+              //       type: 'getPhoneSize',
+              //     }),
+              //   );
+              // }, 500);
             }
             if (input[23] == 0x0b) {
               console.log('多端登陆');
@@ -1226,9 +1396,9 @@
       // 观看广告次数上报1
       function reportFrequency() {
         $.ajax({
-          url: baseUrl + '/api/resoures/v1/trial/reportFrequency/' + form.userCardId,
+          url: baseUrl + '/api/resoures/v1/trial/reportFrequency/' + userCardId,
           headers: {
-            Authorization: form.token
+            Authorization: token,
           },
           type: 'post',
           contentType: 'application/json',
@@ -1255,8 +1425,8 @@
       }
       //关闭广告
       $('.time-close-wrap')[0].addEventListener('click', () => {
-        if(videoTime == 0) {
-          reportFrequency()
+        if (videoTime == 0) {
+          reportFrequency();
         }
         $('.buy-phone-wrap').eq(0).show();
       });
@@ -1335,6 +1505,22 @@
         decodeWoker.postMessage('close');
         decodeWoker.terminate();
       };
+      // 埋点
+      function systemBuriedPoint(pointName) {
+        $.ajax({
+          url: baseUrl + '/api/public/v1/systemBuriedPoint/stat',
+          headers: {
+            Authorization: token,
+          },
+          type: 'post',
+          data: JSON.stringify({
+            pointName: pointName,
+          }),
+          contentType: 'application/json',
+          dataType: 'json',
+          success: function (res) {},
+        });
+      }
       function quit() {
         if (navigator.userAgent.toLowerCase().includes('toutiaomicroapp')) {
           tt.miniProgram.switchTab({
@@ -1346,10 +1532,160 @@
           });
         } else {
           uni.webView.navigateBack({
-	          delta: 1
+            delta: 1,
+          });
+        }
+      }
+
+      function updatePhoneSizeListHtml() {
+        const templatePhoneSizeItem = doT.template(
+          $('#template-phone-size-item').html().replace(/&amp;/g, '&'),
+        );
+        // console.log({
+        //   list: window.phoneSizeList,
+        //   active: window.activePhoneSize,
+        //   resolving,
+        // });
+        const phoneSizeListItemsHtml = templatePhoneSizeItem({
+          list: window.phoneSizeList,
+          active: window.activePhoneSize,
+          resolving,
+        });
+        $('#phone-size-list').html(phoneSizeListItemsHtml);
+      }
+
+      $('#open-set-phone-size-dialog-btn').on('click', function (e) {
+        window.activePhoneSize = window.currentPhoneSize;
+        updatePhoneSizeListHtml();
+        $('#set-phone-size-dialog').addClass('show');
+      });
+      // $('#set-phone-size-dialog').addClass('show');
+      $('.dialog .dialog-mask')
+        .add('.dialog .dialog-btn.cancel')
+        .on('click', function (e) {
+          $(e.currentTarget).parents('.dialog').removeClass('show');
+        });
+
+      // 分辨率列表
+      window.phoneSizeList = [];
+      // 当前生效的分辨率
+      // 选中的分辨率
+      // window.activePhoneSize = window.currentPhoneSize;
+
+      function getPhoneSizeList() {
+        return $.ajax({
+          url:
+            baseUrl + '/api/resources/v5/machine/resolution/getResolvingPower',
+          headers: {
+            Authorization: token,
+          },
+          type: 'get',
+          dataType: 'json',
+          data: {
+            userCardId,
+          },
+        }).then(function (response) {
+          return response.data.map(function (v) {
+            return {
+              id: v.id,
+              dpi: v.dpi,
+              width: v.width,
+              height: v.high,
+            };
           });
+        });
+      }
+      /*
+      getPhoneSizeList().then(function (phoneSizeList) {
+        window.phoneSizeList = phoneSizeList;
+        // window.phoneSizeListBack = phoneSizeList; // 备份下数据,用户横竖切换时。
+        const currentPhoneSize = window.phoneSizeList.find(function (v) {
+          return (
+            window.currentPhoneSize &&
+            v.width === window.currentPhoneSize.width &&
+            v.height === window.currentPhoneSize.height &&
+            v.dpi === window.currentPhoneSize.dpi
+          );
+        });
+
+        window.currentPhoneSize = currentPhoneSize || window.currentPhoneSize;
+        // return updatePhoneSizeListHtml();
+      });
+      */
+      // const phoneSizeListSwiper = new Swiper('#phone-size-list-swiper', {
+      //   direction: 'vertical',
+      //   slidesPerView: 'auto',
+      //   loop: false,
+      //   centeredSlides: true,
+      // });
+      let lastSetPhone = 0;
+      function setPhoneSize(config) {
+        if (config.id === currentPhoneSize.id) {
+          return;
         }
+
+        if (Date.now() <= lastSetPhone + 1000 * 5) {
+          throw new Error('请勿频繁操作');
+        }
+        lastSetPhone = Date.now();
+        // config = Object.assign({}, config, {
+        //   width: config.width,
+        //   height: config.height,
+        // });
+        // 修改云机分辨率
+        wsss.send(
+          JSON.stringify({
+            type: 'setPhoneSize',
+            data: {
+              id: config.id,
+              width: config.width,
+              height: config.height,
+              dpi: config.dpi,
+            },
+          }),
+        );
+        // XXX: 去除了,改为中台发送
+        // 通知其他在线端
+        // wsss.send(
+        //   JSON.stringify({
+        //     type: 'forwardMsg',
+        //     data: {
+        //       code: 'phoneSizeChange',
+        //       id: config.id,
+        //       width: config.width,
+        //       height: config.height,
+        //       dpi: config.dpi,
+        //       desc: '分辨率修改', // 可选
+        //     },
+        //   }),
+        // );
+        // 上报分辨率
+        window.currentPhoneSize = config;
       }
+      // wsss.addEventListener('message', function (event) {
+      //   console.log(
+      //     '🚀 ~ file: WXtrialInterface.html ~ line 1476 ~ event',
+      //     event,
+      //   );
+      // });
+      $('#phone-size-list').on('click', '.phone-size-item', function (e) {
+        const data = $(e.currentTarget).data();
+        // console.log(
+        //   '🚀 ~ file: WXtrialInterface.html ~ line 1494 ~ data',
+        //   data,
+        // );
+        window.activePhoneSize = data;
+        updatePhoneSizeListHtml();
+        // setPhoneSize(data.width, data.height);
+      });
+      $('#set-phone-size-dialog .dialog-btn.confirm').on('click', function (e) {
+        try {
+          setPhoneSize(window.activePhoneSize);
+          $('#set-phone-size-dialog').removeClass('show');
+        } catch (error) {
+          $.toast(error.message, 'text');
+        }
+      });
     </script>
     <script type="text/javascript" src="WXdraw.js"></script>
     <script type="text/javascript" src="aac.js"></script>

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 520 - 370
static/screenIos/css/WXtrialInterface.css


+ 67 - 50
static/screenIos/decoder.js

@@ -6,18 +6,17 @@ var ret;
 var maxWidth = 1080;
 var maxHeight = 1920;
 var globalYuvPtr = undefined;
-var golbalYuvData;//全局,只分配一次
+var golbalYuvData; //全局,只分配一次
 var renderCount = 0;
 var curFrameWidth = undefined;
 var curFrameHeight = undefined;
 
-
 function doSomeInit() {
   var allocSize = maxWidth * maxHeight * 3;
   golbalYuvData = new Uint8Array(allocSize);
 }
-self.importScripts("ffmpeghelper.js");
-self.importScripts("spsParser.js");
+self.importScripts('ffmpeghelper.js');
+self.importScripts('spsParser.js');
 
 self.Module.onRuntimeInitialized = function () {
   isFinish = true;
@@ -25,43 +24,49 @@ self.Module.onRuntimeInitialized = function () {
   ret = Module._openDecoder();
 
   if (!ret) {
-    console.log("打开编码器成功");
+    console.log('打开编码器成功');
   }
-}
-
-self.addEventListener('message', function (e) {
-  var msg = e.data;
-  if (msg.type == "rawData") {
-    var buffer = e.data.data;
-
-    if (buffer[0] !== 0xff) { // 音频解码
-      var type = buffer[4] & 0x1f;
-      if (type == 7) {
-        let info = spsParser(buffer);
-
-        if (curFrameWidth != undefined && curFrameHeight != undefined) {
-          if (info.width != curFrameWidth || info.height != curFrameHeight) { // 分辨率发生改变,切换		
-            switchNewStream();
+};
+
+self.addEventListener(
+  'message',
+  function (e) {
+    var msg = e.data;
+    if (msg.type == 'rawData') {
+      var buffer = e.data.data;
+
+      if (buffer[0] !== 0xff) {
+        // 音频解码
+        var type = buffer[4] & 0x1f;
+        if (type == 7) {
+          let info = spsParser(buffer);
+
+          if (curFrameWidth != undefined && curFrameHeight != undefined) {
+            if (info.width != curFrameWidth || info.height != curFrameHeight) {
+              // 分辨率发生改变,切换
+              console.log('🚀 ~ file: decoder.js ~ line 44 ~ 分辨率发生改变');
+              switchNewStream();
+            }
           }
-        }
 
-        curFrameWidth = info.width;
-        curFrameHeight = info.height;
-        h264Queue.push(buffer);
-      } else {
-        h264Queue.push(buffer);
+          curFrameWidth = info.width;
+          curFrameHeight = info.height;
+          h264Queue.push(buffer);
+        } else {
+          h264Queue.push(buffer);
+        }
       }
-
     }
-  }
-}, false);
+  },
+  false,
+);
 
 function PrintfLog(str) {
   var curTime = new Date().getTime();
   var objData = {
     cmd: 0,
     data: str,
-    time: curTime
+    time: curTime,
   };
   self.postMessage(objData);
 }
@@ -73,23 +78,27 @@ function doRequestIFrame() {
   self.postMessage(objData);
 }
 
-
 function decodeVideo() {
   if (h264Queue.length > 0 && isFinish) {
     decodeH264(h264Queue.shift());
   }
 }
 
-
 var timeFlag = setInterval(decodeVideo, 1);
 
 //实现C 端到js yuv数据赋值, 并分配给opengl 渲染
 /*yuvData, 存放yuv数据的数组
-* inputYuvPtr C++ 
-*/
-function dispatchYuvData(yuvData, inputYuvPtr, videoWidth, videoHeight, copyLen) {
+ * inputYuvPtr C++
+ */
+function dispatchYuvData(
+  yuvData,
+  inputYuvPtr,
+  videoWidth,
+  videoHeight,
+  copyLen,
+) {
   for (i = 0; i < copyLen; i++) {
-    yuvData[i] = Module.HEAPU8[(inputYuvPtr) + i];
+    yuvData[i] = Module.HEAPU8[inputYuvPtr + i];
   }
 
   var curTime = new Date().getTime();
@@ -98,7 +107,7 @@ function dispatchYuvData(yuvData, inputYuvPtr, videoWidth, videoHeight, copyLen)
     data: yuvData,
     time: curTime,
     width: videoWidth,
-    height: videoHeight
+    height: videoHeight,
   };
   self.postMessage(objData);
 }
@@ -109,23 +118,32 @@ function decodeH264(data) {
   var inputPtr = Module._malloc(data.length); //输入数据
 
   for (var i = 0; i < data.length; i++) {
-    Module.HEAPU8[(inputPtr) + i] = data[i]; //转换为堆数据
+    Module.HEAPU8[inputPtr + i] = data[i]; //转换为堆数据
   }
 
-  var allocSize = maxWidth * maxHeight * 3 / 2;
+  var allocSize = (maxWidth * maxHeight * 3) / 2;
   if (globalYuvPtr == undefined) {
     globalYuvPtr = Module._malloc(allocSize);
   }
 
   var ret = Module._feedData(inputPtr, data.length, globalYuvPtr);
 
-  if (ret >= 0) { //解码成功才考虑渲染
-    frameWidth = Module._getVideoWidth();//拿到解码器宽、高
+  if (ret >= 0) {
+    //解码成功才考虑渲染
+    frameWidth = Module._getVideoWidth(); //拿到解码器宽、高
     frameHeight = Module._getVideoHeight();
-    var copyLen = frameWidth * frameHeight * 3 / 2;//只拷贝必须的长度
-
-    if (renderCount > 1) { //第一帧因为画面时全绿色的不渲染
-      dispatchYuvData(golbalYuvData, globalYuvPtr, frameWidth, frameHeight, copyLen);
+    // console.log("🚀 ~ file: decoder.js ~ line 134 ~ decodeH264 ~ frameWidth", frameWidth,frameHeight)
+    var copyLen = (frameWidth * frameHeight * 3) / 2; //只拷贝必须的长度
+
+    if (renderCount > 1) {
+      //第一帧因为画面时全绿色的不渲染
+      dispatchYuvData(
+        golbalYuvData,
+        globalYuvPtr,
+        frameWidth,
+        frameHeight,
+        copyLen,
+      );
     } else {
       renderCount++;
     }
@@ -135,15 +153,14 @@ function decodeH264(data) {
 }
 
 function switchNewStream() {
-
   closeDecoder();
-  var ret = Module._openDecoder();//再次开启解码器	
+  var ret = Module._openDecoder(); //再次开启解码器
   var timeFlag = setInterval(decodeVideo, 1);
-  console.log("切换解码器成功");
+  console.log('切换解码器成功');
 }
 
 function closeDecoder() {
-  clearInterval(timeFlag);//关闭原有定时器
+  clearInterval(timeFlag); //关闭原有定时器
   Module._closeDecoder();
   renderCount = 0;
 
@@ -151,7 +168,7 @@ function closeDecoder() {
     Module._free(globalYuvPtr);
     globalYuvPtr = undefined;
   }
-  console.log("此时buffer长度: %d", h264Queue.length);
+  console.log('此时buffer长度: %d', h264Queue.length);
   while (h264Queue.lengh > 0) {
     h264Queue.shift();
   }

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 62 - 39
static/screenIos/helper.js


binární
static/static/img/phone-size-active.png


+ 24 - 0
static/static/lib/doT-1.1.3/.eslintrc.yml

@@ -0,0 +1,24 @@
+env:
+  node: true
+extends: 'eslint:recommended'
+globals:
+  define: false
+parserOptions:
+  ecmaVersion: 5
+rules:
+  no-trailing-spaces: 2
+  linebreak-style: [ 2, unix ]
+  semi: [ 2, always ]
+  valid-jsdoc: [ 2, { requireReturn: false } ]
+  no-unused-vars: [ 2, { args: none } ]
+  no-console: 0
+  block-scoped-var: 2
+  dot-location: [ 2, property ]
+  dot-notation: 2
+  no-else-return: 2
+  no-eq-null: 2
+  no-fallthrough: 2
+  no-return-assign: 2
+  no-use-before-define: [ 2, nofunc ]
+  no-path-concat: 2
+  no-useless-escape: 0

+ 33 - 0
static/static/lib/doT-1.1.3/.gitignore

@@ -0,0 +1,33 @@
+# Logs
+logs
+*.log
+
+# Runtime data
+pids
+*.pid
+*.seed
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+.nyc_output
+
+# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (http://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directory
+# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
+node_modules
+
+# MacOS directory info files
+.DS_Store
+
+package-lock.json

+ 8 - 0
static/static/lib/doT-1.1.3/.travis.yml

@@ -0,0 +1,8 @@
+language: node_js
+node_js:
+  - "8"
+  - "10"
+  - "12"
+  - "13"
+after_script:
+  - coveralls < coverage/lcov.info

+ 24 - 0
static/static/lib/doT-1.1.3/LICENSE-DOT.txt

@@ -0,0 +1,24 @@
+
+Copyright (c) 2011 Laura Doktorova
+
+Software includes portions from jQote2 Copyright (c) 2010 aefxx,
+http://aefxx.com/ licensed under the MIT license.
+
+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.

+ 106 - 0
static/static/lib/doT-1.1.3/README.md

@@ -0,0 +1,106 @@
+# doT
+
+Created in search of the fastest and concise JavaScript templating function with emphasis on performance under V8 and nodejs. It shows great performance for both nodejs and browsers.
+
+doT.js is fast, small and has no dependencies.
+
+[![Build Status](https://travis-ci.org/olado/doT.svg?branch=master)](https://travis-ci.org/olado/doT)
+[![npm version](https://badge.fury.io/js/dot.svg)](https://www.npmjs.com/package/dot)
+[![Coverage Status](http://coveralls.io/repos/github/olado/doT/badge.svg?branch=master)](https://coveralls.io/github/olado/doT?branch=master)
+
+
+## Features
+    custom delimiters
+    runtime evaluation
+    runtime interpolation
+    compile-time evaluation
+    partials support
+    conditionals support
+    array iterators
+    encoding
+    control whitespace - strip or preserve
+    streaming friendly
+    use it as logic-less or with logic, it is up to you
+
+## Docs, live playground and samples
+
+http://olado.github.com/doT (todo: update docs with new features added in version 1.0.0)
+
+## New in version 1.0.0
+
+#### Added parameters support in partials
+
+```html
+{{##def.macro:param:
+	<div>{{=param.foo}}</div>
+#}}
+
+{{#def.macro:myvariable}}
+```
+
+#### Node module now supports auto-compilation of dot templates from specified path
+
+```js
+var dots = require("dot").process({ path: "./views"});
+```
+
+This will compile .def, .dot, .jst files found under the specified path.
+Details
+   * It ignores sub-directories.
+   * Template files can have multiple extensions at the same time.
+   * Files with .def extension can be included in other files via {{#def.name}}
+   * Files with .dot extension are compiled into functions with the same name and
+   can be accessed as renderer.filename
+   * Files with .jst extension are compiled into .js files. Produced .js file can be
+   loaded as a commonJS, AMD module, or just installed into a global variable (default is set to window.render)
+   * All inline defines defined in the .jst file are
+   compiled into separate functions and are available via _render.filename.definename
+ 
+   Basic usage:
+ ```js
+        var dots = require("dot").process({path: "./views"});
+        dots.mytemplate({foo:"hello world"});
+ ```
+   The above snippet will:
+	* Compile all templates in views folder (.dot, .def, .jst)
+  	* Place .js files compiled from .jst templates into the same folder
+     	   These files can be used with require, i.e. require("./views/mytemplate")
+  	* Return an object with functions compiled from .dot templates as its properties
+  	* Render mytemplate template
+ 
+#### CLI tool to compile dot templates into js files
+
+	./bin/dot-packer -s examples/views -d out/views
+
+## Example for express
+	Many people are using doT with express. I added an example of the best way of doing it examples/express:
+
+[doT with express](examples/express)
+
+## Notes
+    doU.js is here only so that legacy external tests do not break. Use doT.js.
+    doT.js with doT.templateSettings.append=false provides the same performance as doU.js.
+
+## Security considerations
+
+doT allows arbitrary JavaScript code in templates, making it one of the most flexible and powerful templating engines. It means that doT security model assumes that you only use trusted templates and you don't use any  user input as any part of the template, as otherwise it can lead to code injection.
+
+It is strongly recommended to compile all templates to JS code as early as possible. Possible options:
+
+- using doT as dev-dependency only and compiling templates to JS files, for example, as described above or using a custom script, during the build. This is the most performant and secure approach and it is strongly recommended.
+- if the above approach is not possible for some reason (e.g. templates are dynamically generated using some run-time data), it is recommended to compile templates to in-memory functions during application start phase, before any external input is processed.
+- compiling templates lazily, on demand, is less safe. Even though the possibility of the code injection via prototype pollution was patched (#291), there may be some other unknown vulnerabilities that could lead to code injection.
+
+Please report any found vulnerabilities to npm, not via issue tracker.
+
+## Author
+Laura Doktorova [@olado](http://twitter.com/olado)
+
+## License
+doT is licensed under the MIT License. (See LICENSE-DOT)
+
+<p align="center">
+  <img src="http://olado.github.io/doT/doT-js-100@2x.png" alt="logo by Kevin Kirchner"/>
+</p>
+
+Thank you [@KevinKirchner](https://twitter.com/kevinkirchner) for the logo.

+ 110 - 0
static/static/lib/doT-1.1.3/benchmarks/compileBench.js

@@ -0,0 +1,110 @@
+(function() {
+	var jslitmus, _, doU, doT,
+		data = { f1: 1, f2: 2, f3: 3, f4: "http://bebedo.com/laura"},
+		snippet = "<h1>Just static text</h1>\
+		<p>Here is a simple {{=it.f1}} </p>\
+		<div>test {{=it.f2}}\
+		<div>{{=it.f3}}</div>\
+		<div>{{!it.f4}}</div>\
+		</div>";
+
+	if (typeof module !== 'undefined' && module.exports) {
+		runTests();
+	} else {
+		window.onload = runTestsInBrowser;
+	}
+
+	function testsetup(snippet) {
+
+		jslitmus.test('doU.js', function() {
+			doU.template(snippet);
+		});
+
+		jslitmus.test('doU.js - looping', function(count) {
+			while (count--) {
+				doU.template(snippet);
+			}
+		});
+
+		jslitmus.test('doT.js - using this', function() {
+			doT.template(snippet);
+		});
+
+		jslitmus.test('doT.js - using this - looping', function(count) {
+			while (count--) {
+				doT.template(snippet);
+			}
+		});
+	}
+
+	function runTests() {
+		//var util = require('util');
+		jslitmus = require('./jslitmus.js');
+		doU = require('./templating/doU.js');
+		doT = require('./templating/doT.js');
+		var passOne = 0;
+		console.log("*** Compilation speed test");
+		console.log("*** Small template length: " + snippet.length);
+		testsetup(snippet);
+		// Log the test results
+		jslitmus.on('complete', function(test) {
+			//console.log(util.inspect(process.memoryUsage()));
+			console.log(test.toString());
+		});
+		// 'all_complete' fires when all tests have finished.
+		jslitmus.on('all_complete', function() {
+			switch (passOne) {
+			case 0:
+				passOne++;
+				for(var i=0; i<5; i++) { snippet += snippet; }
+				console.log("*** Medium template length: " + snippet.length);
+				break;
+			case 1:
+				passOne++;
+				for(var i=0; i<3; i++) { snippet += snippet; }
+				console.log("*** Large template length: " + snippet.length);
+				break;
+			default:
+				return;
+			}
+
+			jslitmus.clearAll();
+			testsetup(snippet);
+			jslitmus.runAll();
+		});
+		// Run it!
+		jslitmus.runAll();
+	}
+
+	function runTestsInBrowser() {
+		jslitmus = window.jslitmus;doU = window.doU;doT = window.doT;
+
+		var resultTmpl = doT.template("<h3>Template length : {{=it.size}} </h3>	<img src='{{=it.url}}'/>");
+		var currentSet = document.getElementById('small');
+		testsetup(snippet);
+		// 'complete' fires for each test when it finishes.
+		jslitmus.on('complete', function(test) {
+		// Output test results
+			currentSet.innerHTML += test + '<br/>';
+		});
+		// 'all_complete' fires when all tests have finished.
+		jslitmus.on('all_complete', function() {
+			// Get the results image URL
+			var url = jslitmus.getGoogleChart();
+			if (currentSet.id === 'small') {
+				currentSet.innerHTML += resultTmpl({size: snippet.length, url: url});
+				setTimeout(function() {
+					jslitmus.clearAll();
+					currentSet = document.getElementById('large');
+					for(var i=0; i<8; i++) { snippet += snippet; }
+					testsetup(snippet);
+					jslitmus.runAll();
+				}, 10);
+			} else {
+				currentSet.innerHTML += resultTmpl({size: snippet.length, url: url});
+			}
+		});
+		// Run it!
+		jslitmus.runAll();
+	}
+})();

+ 22 - 0
static/static/lib/doT-1.1.3/benchmarks/genspeed.html

@@ -0,0 +1,22 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
+
+<title>Templating Test Suite</title>
+
+<script type="text/javascript" src="jslitmus.js"></script>
+<script type="text/javascript" src="templating/doU.js"></script>
+<script type="text/javascript" src="templating/doT.js"></script>
+<script type="text/javascript" src="compileBench.js"></script>
+
+</head>
+
+<body>
+  <h1>Comparing doU.js and doT.js compilation speed</h1>
+  <div id='small' style="float:left;min-width:400px;"><h3>Small template</h3></div>
+  <div id='large' style="float:left;"><h3>Large template</h3></div>
+</body>
+
+</html>

+ 22 - 0
static/static/lib/doT-1.1.3/benchmarks/index.html

@@ -0,0 +1,22 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
+
+<title>Templating Test Suite</title>
+
+<script type="text/javascript" src="jslitmus.js"></script>
+<script type="text/javascript" src="templating/doU.js"></script>
+<script type="text/javascript" src="templating/doT.js"></script>
+<script type="text/javascript" src="templatesBench.js"></script>
+
+</head>
+
+<body>
+  <h1>Comparing doU.js and doT.js</h1>
+  <div id='small' style="float:left;min-width:400px;"><h3>Small template</h3></div>
+  <div id='large' style="float:left;"><h3>Large template</h3></div>
+</body>
+
+</html>

+ 628 - 0
static/static/lib/doT-1.1.3/benchmarks/jslitmus.js

@@ -0,0 +1,628 @@
+// jslitmus.js
+//
+// Copyright (c) 2010, Robert Kieffer, http://broofa.com
+// Available under MIT license (http://en.wikipedia.org/wiki/MIT_License)
+
+(function() {
+  var root = this;
+
+  //
+  // Platform detect
+  //
+
+  var platform = (function() {
+    // Platform info object
+    var p = {
+      name: null,
+      version: null,
+      os: null,
+      description: 'unknown platform',
+      toString: function() {return this.description;}
+    };
+
+    if (root.navigator) {
+      var ua = navigator.userAgent;
+
+      // Detect OS
+      var oses = 'Windows|iPhone OS|(?:Intel |PPC )?Mac OS X|Linux';
+      p.os = new RegExp('((' + oses + ') +[^ \);]*)').test(ua) ? RegExp.$1.replace(/_/g, '.') : null;
+
+      // Detect expected names
+      p.name = /(Chrome|MSIE|Safari|Opera|Firefox|Minefield)/.test(ua) ? RegExp.$1 : null;
+
+      // Detect version
+      if (p.name == 'Opera') {
+        p.version = opera.name;
+      } else if (p.name) {
+        var vre = new RegExp('(Version|' + p.name + ')[ \/]([^ ;]*)');
+        p.version = vre.test(ua) ? RegExp.$2 : null;
+      }
+    } else if (root.process && process.platform) {
+      // Support node.js (see http://nodejs.org)
+      p.name = 'node';
+      p.version = process.version;
+      p.os = process.platform;
+    }
+
+    // Set the description
+    var d = [];
+    if (p.name) d.push(p.name);
+    if (p.version) d.push(' ' + p.version);
+    if (p.os) d.push(' on ' + p.os);
+    if (d.length) p.description = d.join('');
+
+    return p;
+  })();
+
+  //
+  // Context-specific initialization
+  //
+
+  var sys = null, querystring = null;
+  if (platform.name == 'node') {
+    util = require('util');
+    querystring = require('querystring');
+  }
+
+  //
+  // Misc convenience methods
+  //
+
+  function log(msg) {
+    if (typeof(console) != 'undefined') {
+      console.log(msg);
+    } else if (sys) {
+      util.log(msg);
+    }
+  }
+
+  // nil function
+  function nilf(x) {
+    return x;
+  }
+
+  // Copy properties
+  function extend(dst, src) {
+    for (var k in src) {
+      dst[k] = src[k];
+    }
+    return dst;
+  }
+
+  // Array: apply f to each item in a
+  function forEach(a, f) {
+    for (var i = 0, il = (a && a.length); i < il; i++) {
+      var o = a[i];
+      f(o, i);
+    }
+  }
+
+  // Array: return array of all results of f(item)
+  function map(a, f) {
+    var o, res = [];
+    for (var i = 0, il = (a && a.length); i < il; i++) {
+      var o = a[i];
+      res.push(f(o, i));
+    }
+    return res;
+  }
+
+  // Array: filter out items for which f(item) is falsy
+  function filter(a, f) {
+    var o, res = [];
+    for (var i = 0, il = (a && a.length); i < il; i++) {
+      var o = a[i];
+      if (f(o, i)) res.push(o);
+    }
+    return res;
+  }
+
+  // Array: IE doesn't have indexOf in some cases
+  function indexOf(a, o) {
+    if (a.indexOf) return a.indexOf(o);
+    for (var i = 0, l = a.length; i < l; i++) if (a[i] === o) return i;
+    return -1;
+  }
+
+  // Enhanced escape()
+  function escape2(s) {
+    s = s.replace(/,/g, '\\,');
+    s = querystring ? querystring.escape(s) : escape(s);
+    s = s.replace(/\+/g, '%2b');
+    s = s.replace(/ /g, '+');
+    return s;
+  }
+
+  // join(), for objects. Creates url query param-style strings by default
+  function join(o, delimit1, delimit2) {
+    var asQuery = !delimit1 && !delimit2;
+    if (asQuery) {
+      delimit1 = '&';
+      delimit2 = '=';
+    }
+
+    var pairs = [];
+    for (var key in o) {
+      var value = o[key];
+      if (asQuery) value = escape2(value);
+      pairs.push(key + delimit2 + o[key]);
+    }
+    return pairs.join(delimit1);
+  }
+
+  // split(), for object strings. Parses url query param strings by default
+  function split(s, delimit1, delimit2) {
+    var asQuery = !delimit1 && !delimit2;
+    if (asQuery) {
+      s = s.replace(/.*[?#]/, '');
+      delimit1 = '&';
+      delimit2 = '=';
+    }
+
+    if (match) {
+      var o = query.split(delimit1);
+      for (var i = 0; i < o.length; i++) {
+        var pair = o[i].split(new RegExp(delimit2 + '+'));
+        var key = pair.shift();
+        var value = (asQuery && pair.length > 1) ? pair.join(delimit2) : pair[0];
+        o[key] = value;
+      }
+    }
+
+    return o;
+  }
+
+  // Round x to d significant digits
+  function sig(x, d) {
+    var exp = Math.ceil(Math.log(Math.abs(x))/Math.log(10)),
+        f = Math.pow(10, exp-d);
+    return Math.round(x/f)*f;
+  }
+
+  // Convert x to a readable string version
+  function humanize(x, sd) {
+    var ax = Math.abs(x), res;
+    sd = sd | 4;  // significant digits
+    if (ax == Infinity) {
+      res = ax > 0 ? 'Infinity' : '-Infinity';
+    } else if (ax > 1e9) {
+      res = sig(x/1e9, sd) + 'G';
+    } else if (ax > 1e6) {
+      res = sig(x/1e6, sd) + 'M';
+    } else if (ax > 1e3) {
+      res = sig(x/1e3, sd) + 'k';
+    } else if (ax > .01) {
+      res = sig(x, sd);
+    } else if (ax > 1e-3) {
+      res = sig(x/1e-3, sd) + 'm';
+    } else if (ax > 1e-6) {
+      res = sig(x/1e-6, sd) + '\u00b5'; // Greek mu
+    } else if (ax > 1e-9) {
+      res = sig(x/1e-9, sd) + 'n';
+    } else {
+      res = x ? sig(x, sd) : 0;
+    }
+    // Turn values like "1.1000000000005" -> "1.1"
+    res = (res + '').replace(/0{5,}\d*/, '');
+
+    return res;
+  }
+
+  // Node.js-inspired event emitter API, with some enhancements.
+  function EventEmitter() {
+    var ee = this;
+    var listeners = {};
+    extend(ee, {
+      on: function(e, f) {
+        if (!listeners[e]) listeners[e] = [];
+        listeners[e].push(f);
+      },
+      removeListener: function(e, f) {
+        listeners[e] = filter(listeners[e], function(l) {
+          return l != f;
+        });
+      },
+      removeAllListeners: function(e) {
+        listeners[e] = [];
+      },
+      emit: function(e) {
+        var args = Array.prototype.slice.call(arguments, 1);
+        forEach([].concat(listeners[e], listeners['*']), function(l) {
+          ee._emitting = e;
+          if (l) l.apply(ee, args);
+        });
+        delete ee._emitting;
+      }
+    });
+  }
+
+  //
+  // Test class
+  //
+
+  /**
+   * Test manages a single test (created with JSLitmus.test())
+   */
+  function Test(name, f) {
+    var test = this;
+
+    // Test instances get EventEmitter API
+    EventEmitter.call(test);
+
+    if (!f) throw new Error('Undefined test function');
+    if (!/function[^\(]*\(([^,\)]*)/.test(f)) {
+      throw new Error('"' + name + '" test: Invalid test function');
+    }
+
+    // If the test function takes an argument, we assume it does the iteration
+    // for us
+    var isLoop = !!RegExp.$1;
+
+    /**
+     * Reset test state
+     */
+    function reset() {
+      delete test.count;
+      delete test.time;
+      delete test.running;
+      test.emit('reset', test);
+      return test;
+    }
+
+    function clone() {
+      var test = extend(new Test(name, f), test);
+      return test.reset();
+    }
+
+    /**
+     * Run the test n times, and use the best results
+     */
+    function bestOf(n) {
+      var best = null;
+      while (n--) {
+        var t = clone();
+        t.run(null, true);
+        if (!best || t.period < best.period) {
+          best = t;
+        }
+      }
+      extend(test, best);
+    }
+
+    /**
+     * Start running a test.  Default is to run the test asynchronously (via
+     * setTimeout).  Can be made synchronous by passing true for 2nd param
+     */
+    function run(count, synchronous) {
+      count = count || test.INIT_COUNT;
+      test.running = true;
+
+      if (synchronous) {
+        _run(count, synchronous);
+      } else {
+        setTimeout(function() {
+          _run(count);
+        }, 1);
+      }
+      return test;
+    }
+
+    /**
+     * Run, for real
+     */
+    function _run(count, noTimeout) {
+
+      try {
+        var start, f = test.f, now, i = count;
+
+        // Start the timer
+        start = new Date();
+
+        // Run the test code
+        test.count = count;
+        test.time = 0;
+        test.period = 0;
+
+        test.emit('start', test);
+
+        if (isLoop) {
+          // Test code does it's own iteration
+          f(count);
+        } else {
+          // Do the iteration ourselves
+          while (i--) f();
+        }
+
+        // Get time test took (in secs)
+        test.time = Math.max(1,new Date() - start)/1000;
+
+        // Store iteration count and per-operation time taken
+        test.count = count;
+        test.period = test.time/count;
+
+        // Do we need to keep running?
+        test.running = test.time < test.MIN_TIME;
+
+        // Publish results
+        test.emit('results', test);
+
+        // Set up for another run, if needed
+        if (test.running) {
+          // Use an iteration count that will (we hope) get us close to the
+          // MAX_COUNT time.
+          var x = test.MIN_TIME/test.time;
+          var pow = Math.pow(2, Math.max(1, Math.ceil(Math.log(x)/Math.log(2))));
+          count *= pow;
+          if (count > test.MAX_COUNT) {
+            throw new Error('Max count exceeded.  If this test uses a looping function, make sure the iteration loop is working properly.');
+          }
+
+          if (noTimeout) {
+            _run(count, noTimeout);
+          } else {
+            run(count);
+          }
+        } else {
+          test.emit('complete', test);
+        }
+      } catch (err) {
+        log(err);
+        // Exceptions are caught and displayed in the test UI
+        test.emit('error', err);
+      }
+
+      return test;
+    }
+
+    /**
+    * Get the number of operations per second for this test.
+    *
+    * @param normalize if true, iteration loop overhead taken into account.
+    *                  Note that normalized tests may return Infinity if the
+    *                  test time is of the same order as the calibration time.
+    */
+    function getHz(normalize) {
+      var p = test.period;
+
+      // Adjust period based on the calibration test time
+      if (normalize) {
+        var cal = test.isLoop ? Test.LOOP_CAL : Test.NOLOOP_CAL;
+        if (!cal.period) {
+          // Run calibration if needed
+          cal.MIN_TIME = .3;
+          cal.bestOf(3);
+        }
+
+        // Subtract off calibration time.  In theory this should never be
+        // negative, but in practice the calibration times are affected by a
+        // variety of factors so just clip to zero and let users test for
+        // getHz() == Infinity
+        p = Math.max(0, p - cal.period);
+      }
+
+      return sig(1/p, 4);
+    }
+
+    // Set properties that are specific to this instance
+    extend(test, {
+      // Test name
+      name: name,
+
+      // Test function
+      f: f,
+
+      // True if the test function does it's own looping (i.e. takes an arg)
+      isLoop: isLoop,
+
+      clone: clone,
+      run: run,
+      bestOf: bestOf,
+      getHz: getHz,
+      reset: reset
+    });
+
+    // IE7 doesn't do 'toString' or 'toValue' in object enumerations, so set
+    // it explicitely here.
+    test.toString = function() {
+      if (this.time) {
+        return this.name + ', f = '  +
+        humanize(this.getHz()) + 'hz (' +
+        humanize(this.count) + '/' + humanize(this.time) + 'secs)';
+      } else {
+        return this.name + ', count = '  + humanize(this.count);
+      }
+    };
+  };
+
+  // Set static properties
+  extend(Test, {
+    LOOP_CAL: new Test('loop cal', function(count) {while (count--) {}}),
+    NOLOOP_CAL: new Test('noloop cal', nilf)
+  });
+
+  // Set default property values
+  extend(Test.prototype, {
+    // Initial number of iterations
+    INIT_COUNT: 10,
+
+    // Max iterations allowed (used to detect bad looping functions)
+    MAX_COUNT: 1e9,
+
+    // Minimum time test should take to get valid results (secs)
+    MIN_TIME: 1.0
+  });
+
+  //
+  // jslitmus
+  //
+
+  // Set up jslitmus context
+  var jslitmus;
+  if (platform.name == 'node') {
+    jslitmus = exports;
+  } else {
+    jslitmus = root.jslitmus = {};
+  }
+
+  var tests = [], // test store (all tests added w/ jslitmus.test())
+      queue = [], // test queue (to be run)
+      currentTest; // currently running test
+
+  // jslitmus gets EventEmitter API
+  EventEmitter.call(jslitmus);
+
+  /**
+    * Create a new test
+    */
+  function test(name, f) {
+    // Create the Test object
+    var test = new Test(name, f);
+    tests.push(test);
+
+    // Run the next test if this one finished
+    test.on('*', function() {
+      // Forward test events to jslitmus listeners
+      var args = Array.prototype.slice.call(arguments);
+      args.unshift(test._emitting);
+      jslitmus.emit.apply(jslitmus, args);
+
+      // Auto-run the next test
+      if (test._emitting == 'complete') {
+        currentTest = null;
+        _nextTest();
+      }
+    });
+
+    jslitmus.emit('added', test);
+
+    return test;
+  }
+
+  /**
+    * Add all tests to the run queue
+    */
+  function runAll(e) {
+    forEach(tests, _queueTest);
+  }
+
+  /**
+    * Remove all tests from the run queue.  The current test has to finish on
+    * it's own though
+    */
+  function stop() {
+    while (queue.length) {
+      var test = queue.shift();
+    }
+  }
+
+  /**
+    * Run the next test in the run queue
+    */
+  function _nextTest() {
+    if (!currentTest) {
+      var test = queue.shift();
+      if (test) {
+        currentTest = test;
+        test.run();
+      } else {
+        jslitmus.emit('all_complete');
+      }
+    }
+  }
+
+  /**
+    * Add a test to the run queue
+    */
+  function _queueTest(test) {
+    if (indexOf(queue, test) >= 0) return;
+    queue.push(test);
+    _nextTest();
+  }
+
+  function clearAll() {
+	tests = [];
+  }
+
+  /**
+    * Generate a Google Chart URL that shows the data for all tests
+    */
+  function getGoogleChart(normalize) {
+    var chart_title = [
+      'Operations/second on ' + platform.name,
+      '(' + platform.version + ' / ' + platform.os + ')'
+    ];
+
+    var n = tests.length, markers = [], data = [];
+    var d, min = 0, max = -1e10;
+
+    // Gather test data
+
+    var markers = map(tests, function(test, i) {
+      if (test.count) {
+        var hz = test.getHz();
+        var v = hz != Infinity ? hz : 0;
+        data.push(v);
+        var label = test.name + '(' + humanize(hz)+ ')';
+        var marker = 't' + escape2(label) + ',000000,0,' + i + ',10';
+        max = Math.max(v, max);
+
+        return marker;
+      }
+    });
+
+    if (markers.length <= 0) return null;
+
+    // Build labels
+    var labels = [humanize(min), humanize(max)];
+
+    var w = 250, bw = 15;
+    var bs = 5;
+    var h = markers.length*(bw + bs) + 30 + chart_title.length*20;
+
+    var params = {
+      chtt: escape(chart_title.join('|')),
+      chts: '000000,10',
+      cht: 'bhg',                     // chart type
+      chd: 't:' + data.join(','),     // data set
+      chds: min + ',' + max,          // max/min of data
+      chxt: 'x',                      // label axes
+      chxl: '0:|' + labels.join('|'), // labels
+      chsp: '0,1',
+      chm: markers.join('|'),         // test names
+      chbh: [bw, 0, bs].join(','),    // bar widths
+      // chf: 'bg,lg,0,eeeeee,0,eeeeee,.5,ffffff,1', // gradient
+      chs: w + 'x' + h
+    };
+
+    var url = 'http://chart.apis.google.com/chart?' + join(params);
+
+    return url;
+  }
+
+  // Public API
+  extend(jslitmus, {
+    Test: Test,
+    platform: platform,
+    test: test,
+    runAll: runAll,
+    getGoogleChart: getGoogleChart,
+	clearAll: clearAll
+  });
+
+  // Expose code goodness we've got here, since it's useful, but do so in a way
+  // that doesn't commit us to supporting it in future versions.
+  jslitmus.unsupported = {
+    nilf: nilf,
+    log: log,
+    extend: extend,
+    forEach: forEach,
+    filter: filter,
+    map: map,
+    indexOf: indexOf,
+    escape2: escape2,
+    join: join,
+    split: split,
+    sig: sig,
+    humanize: humanize
+  };
+})();

+ 138 - 0
static/static/lib/doT-1.1.3/benchmarks/templatesBench.js

@@ -0,0 +1,138 @@
+(function() {
+	var jslitmus, _, doU, doT,
+		data = { f1: 1, f2: 2, f3: 3, f4: "http://bebedo.com/laura"},
+		snippet = "<h1>Just static text</h1>\
+		<p>Here is a simple {{=it.f1}} </p>\
+		<div>test {{=it.f2}}\
+		<div>{{=it.f3}}</div>\
+		<div>{{!it.f4}}</div>\
+		</div>";
+
+	if (typeof module !== 'undefined' && module.exports) {
+		runTests();
+	} else {
+		window.onload = runTestsInBrowser;
+	}
+
+	function testsetup(snippet) {
+		// doU with 'it'
+		var doUCompiled = doU.template(snippet);
+		// doT with 'it'
+		var doTCompiledParam = doT.template(snippet);
+		// doT with 'this'
+		var doTCompiled = doT.template(snippet.replace(/=it\./g, '=this.').replace(/{{!it\./g, '{{!this.'));
+		// doT with 'it' and append = false
+		doT.templateSettings.append = false;
+		var doTCompiledNoAppend = doT.template(snippet);
+
+		jslitmus.test('doU.js', function() {
+			doUCompiled(data);
+		});
+
+		jslitmus.test('doU.js - looping', function(count) {
+			while (count--) {
+				doUCompiled(data);
+			}
+		});
+
+		jslitmus.test('doT.js - using this', function() {
+			doTCompiled.call(data);
+		});
+
+		jslitmus.test('doT.js - using this - looping', function(count) {
+			while (count--) {
+				doTCompiled.call(data);
+			}
+		});
+
+		jslitmus.test('doT.js - using it', function() {
+			doTCompiledParam(data);
+		});
+
+		jslitmus.test('doT.js - using it - looping', function(count) {
+			while (count--) {
+				doTCompiledParam(data);
+			}
+		});
+
+		jslitmus.test('doT.js - append off', function() {
+			doTCompiledNoAppend(data);
+		});
+
+		jslitmus.test('doT.js - append off - looping', function(count) {
+			while (count--) {
+				doTCompiledNoAppend(data);
+			}
+		});
+}
+
+	function runTests() {
+		//var util = require('util');
+		jslitmus = require('./jslitmus.js');
+		doU = require('./templating/doU.js');
+		doT = require('./templating/doT.js');
+		var passOne = 0;
+		console.log("*** Small template length: " + snippet.length);
+		testsetup(snippet);
+		// Log the test results
+		jslitmus.on('complete', function(test) {
+			//console.log(util.inspect(process.memoryUsage()));
+			console.log(test.toString());
+		});
+		// 'all_complete' fires when all tests have finished.
+		jslitmus.on('all_complete', function() {
+			switch (passOne) {
+			case 0:
+				passOne++;
+				for(var i=0; i<5; i++) { snippet += snippet; }
+				console.log("*** Medium template length: " + snippet.length);
+				break;
+			case 1:
+				passOne++;
+				for(var i=0; i<3; i++) { snippet += snippet; }
+				console.log("*** Large template length: " + snippet.length);
+				break;
+			default:
+				return;
+			}
+
+			jslitmus.clearAll();
+			testsetup(snippet);
+			jslitmus.runAll();
+		});
+		// Run it!
+		jslitmus.runAll();
+	}
+
+	function runTestsInBrowser() {
+		jslitmus = window.jslitmus;doU = window.doU;doT = window.doT;
+
+		var resultTmpl = doT.template("<h3>Template length : {{=it.size}} </h3>	<img src='{{=it.url}}'/>");
+		var currentSet = document.getElementById('small');
+		testsetup(snippet);
+		// 'complete' fires for each test when it finishes.
+		jslitmus.on('complete', function(test) {
+		// Output test results
+			currentSet.innerHTML += test + '<br/>';
+		});
+		// 'all_complete' fires when all tests have finished.
+		jslitmus.on('all_complete', function() {
+			// Get the results image URL
+			var url = jslitmus.getGoogleChart();
+			if (currentSet.id === 'small') {
+				currentSet.innerHTML += resultTmpl({size: snippet.length, url: url});
+				setTimeout(function() {
+					jslitmus.clearAll();
+					currentSet = document.getElementById('large');
+					for(var i=0; i<8; i++) { snippet += snippet; }
+					testsetup(snippet);
+					jslitmus.runAll();
+				}, 10);
+			} else {
+				currentSet.innerHTML += resultTmpl({size: snippet.length, url: url});
+			}
+		});
+		// Run it!
+		jslitmus.runAll();
+	}
+})();

+ 140 - 0
static/static/lib/doT-1.1.3/benchmarks/templating/doT.js

@@ -0,0 +1,140 @@
+// doT.js
+// 2011-2014, Laura Doktorova, https://github.com/olado/doT
+// Licensed under the MIT license.
+
+(function() {
+	"use strict";
+
+	var doT = {
+		version: "1.0.3",
+		templateSettings: {
+			evaluate:    /\{\{([\s\S]+?(\}?)+)\}\}/g,
+			interpolate: /\{\{=([\s\S]+?)\}\}/g,
+			encode:      /\{\{!([\s\S]+?)\}\}/g,
+			use:         /\{\{#([\s\S]+?)\}\}/g,
+			useParams:   /(^|[^\w$])def(?:\.|\[[\'\"])([\w$\.]+)(?:[\'\"]\])?\s*\:\s*([\w$\.]+|\"[^\"]+\"|\'[^\']+\'|\{[^\}]+\})/g,
+			define:      /\{\{##\s*([\w\.$]+)\s*(\:|=)([\s\S]+?)#\}\}/g,
+			defineParams:/^\s*([\w$]+):([\s\S]+)/,
+			conditional: /\{\{\?(\?)?\s*([\s\S]*?)\s*\}\}/g,
+			iterate:     /\{\{~\s*(?:\}\}|([\s\S]+?)\s*\:\s*([\w$]+)\s*(?:\:\s*([\w$]+))?\s*\}\})/g,
+			varname:	"it",
+			strip:		true,
+			append:		true,
+			selfcontained: false,
+			doNotSkipEncoded: false
+		},
+		template: undefined, //fn, compile template
+		compile:  undefined  //fn, for express
+	}, _globals;
+
+	doT.encodeHTMLSource = function(doNotSkipEncoded) {
+		var encodeHTMLRules = { "&": "&#38;", "<": "&#60;", ">": "&#62;", '"': "&#34;", "'": "&#39;", "/": "&#47;" },
+			matchHTML = doNotSkipEncoded ? /[&<>"'\/]/g : /&(?!#?\w+;)|<|>|"|'|\//g;
+		return function(code) {
+			return code ? code.toString().replace(matchHTML, function(m) {return encodeHTMLRules[m] || m;}) : "";
+		};
+	};
+
+	_globals = (function(){ return this || (0,eval)("this"); }());
+
+	if (typeof module !== "undefined" && module.exports) {
+		module.exports = doT;
+	} else if (typeof define === "function" && define.amd) {
+		define(function(){return doT;});
+	} else {
+		_globals.doT = doT;
+	}
+
+	var startend = {
+		append: { start: "'+(",      end: ")+'",      startencode: "'+encodeHTML(" },
+		split:  { start: "';out+=(", end: ");out+='", startencode: "';out+=encodeHTML(" }
+	}, skip = /$^/;
+
+	function resolveDefs(c, block, def) {
+		return ((typeof block === "string") ? block : block.toString())
+		.replace(c.define || skip, function(m, code, assign, value) {
+			if (code.indexOf("def.") === 0) {
+				code = code.substring(4);
+			}
+			if (!(code in def)) {
+				if (assign === ":") {
+					if (c.defineParams) value.replace(c.defineParams, function(m, param, v) {
+						def[code] = {arg: param, text: v};
+					});
+					if (!(code in def)) def[code]= value;
+				} else {
+					new Function("def", "def['"+code+"']=" + value)(def);
+				}
+			}
+			return "";
+		})
+		.replace(c.use || skip, function(m, code) {
+			if (c.useParams) code = code.replace(c.useParams, function(m, s, d, param) {
+				if (def[d] && def[d].arg && param) {
+					var rw = (d+":"+param).replace(/'|\\/g, "_");
+					def.__exp = def.__exp || {};
+					def.__exp[rw] = def[d].text.replace(new RegExp("(^|[^\\w$])" + def[d].arg + "([^\\w$])", "g"), "$1" + param + "$2");
+					return s + "def.__exp['"+rw+"']";
+				}
+			});
+			var v = new Function("def", "return " + code)(def);
+			return v ? resolveDefs(c, v, def) : v;
+		});
+	}
+
+	function unescape(code) {
+		return code.replace(/\\('|\\)/g, "$1").replace(/[\r\t\n]/g, " ");
+	}
+
+	doT.template = function(tmpl, c, def) {
+		c = c || doT.templateSettings;
+		var cse = c.append ? startend.append : startend.split, needhtmlencode, sid = 0, indv,
+			str  = (c.use || c.define) ? resolveDefs(c, tmpl, def || {}) : tmpl;
+
+		str = ("var out='" + (c.strip ? str.replace(/(^|\r|\n)\t* +| +\t*(\r|\n|$)/g," ")
+					.replace(/\r|\n|\t|\/\*[\s\S]*?\*\//g,""): str)
+			.replace(/'|\\/g, "\\$&")
+			.replace(c.interpolate || skip, function(m, code) {
+				return cse.start + unescape(code) + cse.end;
+			})
+			.replace(c.encode || skip, function(m, code) {
+				needhtmlencode = true;
+				return cse.startencode + unescape(code) + cse.end;
+			})
+			.replace(c.conditional || skip, function(m, elsecase, code) {
+				return elsecase ?
+					(code ? "';}else if(" + unescape(code) + "){out+='" : "';}else{out+='") :
+					(code ? "';if(" + unescape(code) + "){out+='" : "';}out+='");
+			})
+			.replace(c.iterate || skip, function(m, iterate, vname, iname) {
+				if (!iterate) return "';} } out+='";
+				sid+=1; indv=iname || "i"+sid; iterate=unescape(iterate);
+				return "';var arr"+sid+"="+iterate+";if(arr"+sid+"){var "+vname+","+indv+"=-1,l"+sid+"=arr"+sid+".length-1;while("+indv+"<l"+sid+"){"
+					+vname+"=arr"+sid+"["+indv+"+=1];out+='";
+			})
+			.replace(c.evaluate || skip, function(m, code) {
+				return "';" + unescape(code) + "out+='";
+			})
+			+ "';return out;")
+			.replace(/\n/g, "\\n").replace(/\t/g, '\\t').replace(/\r/g, "\\r")
+			.replace(/(\s|;|\}|^|\{)out\+='';/g, '$1').replace(/\+''/g, "");
+			//.replace(/(\s|;|\}|^|\{)out\+=''\+/g,'$1out+=');
+
+		if (needhtmlencode) {
+			if (!c.selfcontained && _globals && !_globals._encodeHTML) _globals._encodeHTML = doT.encodeHTMLSource(c.doNotSkipEncoded);
+			str = "var encodeHTML = typeof _encodeHTML !== 'undefined' ? _encodeHTML : ("
+				+ doT.encodeHTMLSource.toString() + "(" + (c.doNotSkipEncoded || '') + "));"
+				+ str;
+		}
+		try {
+			return new Function(c.varname, str);
+		} catch (e) {
+			if (typeof console !== "undefined") console.log("Could not create a template function: " + str);
+			throw e;
+		}
+	};
+
+	doT.compile = function(tmpl, def) {
+		return doT.template(tmpl, null, def);
+	};
+}());

+ 56 - 0
static/static/lib/doT-1.1.3/benchmarks/templating/doU.js

@@ -0,0 +1,56 @@
+// doU.js
+// (c) 2011, Laura Doktorova
+// https://github.com/olado/doT
+//
+// doU is an extraction and slight modification of an excellent
+// templating function from jQote2.js (jQuery plugin) by aefxx
+// (http://aefxx.com/jquery-plugins/jqote2/).
+//
+// Modifications:
+// 1. nodejs support
+// 2. allow for custom template markers
+// 3. only allow direct invocation of the compiled function
+//
+// Licensed under the MIT license.
+
+(function() {
+	var doU = { version : '0.1.2' };
+
+	if (typeof module !== 'undefined' && module.exports) {
+		module.exports = doU;
+	} else {
+		this.doU = doU;
+	}
+
+	doU.templateSettings = {
+		begin : '{{',
+		end : '}}',
+		varname : 'it'
+	};
+
+	doU.template = function(tmpl, conf) {
+		conf = conf || doU.templateSettings;
+		var str = '', tb = conf.begin, te = conf.end, m, l,
+			arr = tmpl.replace(/\s*<!\[CDATA\[\s*|\s*\]\]>\s*|[\r\n\t]|(\/\*[\s\S]*?\*\/)/g, '')
+				.split(tb).join(te +'\x1b')
+				.split(te);
+
+		for (m=0,l=arr.length; m < l; m++) {
+			str += arr[m].charAt(0) !== '\x1b' ?
+			"out+='" + arr[m].replace(/(\\|["'])/g, '\\$1') + "'" : (arr[m].charAt(1) === '=' ?
+			';out+=(' + arr[m].substr(2) + ');' : (arr[m].charAt(1) === '!' ?
+			';out+=(' + arr[m].substr(2) + ").toString().replace(/&(?!\\w+;)/g, '&#38;').split('<').join('&#60;').split('>').join('&#62;').split('" + '"' + "').join('&#34;').split(" + '"' + "'" + '"' + ").join('&#39;').split('/').join('&#x2F;');" : ';' + arr[m].substr(1)));
+		}
+
+		str = ('var out="";'+str+';return out;')
+			.split("out+='';").join('')
+			.split('var out="";out+=').join('var out=');
+
+		try {
+			return new Function(conf.varname, str);
+		} catch (e) {
+			if (typeof console !== 'undefined') console.log("Could not create a template function: " + str);
+			throw e;
+		}
+	};
+}());

+ 52 - 0
static/static/lib/doT-1.1.3/bin/dot-packer

@@ -0,0 +1,52 @@
+#!/usr/bin/env node
+/* Continuation of https://github.com/Katahdin/dot-packer */
+
+var program = require('commander'),
+	dot = require('../');
+
+program
+	.version('0.0.1')
+	.usage('dottojs')
+	.option('-s, --source [value]', 'source folder/file path')
+	.option('-d, --dest [value]', 'destination folder')
+	.option('-g, --global [value]', 'the global variable to install the templates in',"window.render")
+	.option('-p, --package [value]', 'if specified, package all templates from destination folder into specified file')
+	.parse(process.argv);
+
+if (program.dest) mkdirordie(program.dest);
+if (program.package) {
+	var li = program.package.lastIndexOf('/');
+	if (li>0) mkdirordie(program.package.substring(0, li));
+}
+
+function mkdirordie(path) {
+	require("mkdirp")(path, function (err) {
+    	if (err) {
+    		console.error(err);
+    		process.exit(1);
+    	}
+	});
+}
+
+var render = dot.process({
+		path: program.source,
+		destination: program.dest,
+		global: program.global
+	});
+
+if (program.package) {
+	console.log("Packaging all files into " + program.package);
+	var fs = require("fs");
+	var files = [];
+	var dest = program.dest || './';
+	if (dest[dest.length-1] !== '/') dest += '/';
+	var sources = fs.readdirSync(dest);
+	for(k = 0; k < sources.length; k++) {
+		name = sources[k];
+		if (/\.js$/.test(name)) {
+			files.push(dest + name);
+		}
+	}
+	var result = require("uglify-js").minify(files);
+	fs.writeFileSync(program.package, result.code);
+}

+ 21 - 0
static/static/lib/doT-1.1.3/bower.json

@@ -0,0 +1,21 @@
+{
+  "name": "dot",
+  "description": "Concise and fast javascript templating compatible with nodejs and other javascript environments",
+  "main": "doT.js",
+  "authors": [
+    "Laura Doktorova <ldoktorova@gmail.com>"
+  ],
+  "license": "MIT",
+  "keywords": [
+    "template",
+    "fast",
+    "simple",
+    "templating"
+  ],
+  "homepage": "https://github.com/olado/doT",
+  "ignore": [
+    "node_modules",
+    "bower_components",
+    "test"
+  ]
+}

+ 144 - 0
static/static/lib/doT-1.1.3/doT.js

@@ -0,0 +1,144 @@
+// doT.js
+// 2011-2014, Laura Doktorova, https://github.com/olado/doT
+// Licensed under the MIT license.
+
+(function () {
+	"use strict";
+
+	var doT = {
+		name: "doT",
+		version: "1.1.1",
+		templateSettings: {
+			evaluate:    /\{\{([\s\S]+?(\}?)+)\}\}/g,
+			interpolate: /\{\{=([\s\S]+?)\}\}/g,
+			encode:      /\{\{!([\s\S]+?)\}\}/g,
+			use:         /\{\{#([\s\S]+?)\}\}/g,
+			useParams:   /(^|[^\w$])def(?:\.|\[[\'\"])([\w$\.]+)(?:[\'\"]\])?\s*\:\s*([\w$\.]+|\"[^\"]+\"|\'[^\']+\'|\{[^\}]+\})/g,
+			define:      /\{\{##\s*([\w\.$]+)\s*(\:|=)([\s\S]+?)#\}\}/g,
+			defineParams:/^\s*([\w$]+):([\s\S]+)/,
+			conditional: /\{\{\?(\?)?\s*([\s\S]*?)\s*\}\}/g,
+			iterate:     /\{\{~\s*(?:\}\}|([\s\S]+?)\s*\:\s*([\w$]+)\s*(?:\:\s*([\w$]+))?\s*\}\})/g,
+			varname:	"it",
+			strip:		true,
+			append:		true,
+			selfcontained: false,
+			doNotSkipEncoded: false
+		},
+		template: undefined, //fn, compile template
+		compile:  undefined, //fn, for express
+		log: true
+	}, _globals;
+
+	doT.encodeHTMLSource = function(doNotSkipEncoded) {
+		var encodeHTMLRules = { "&": "&#38;", "<": "&#60;", ">": "&#62;", '"': "&#34;", "'": "&#39;", "/": "&#47;" },
+			matchHTML = doNotSkipEncoded ? /[&<>"'\/]/g : /&(?!#?\w+;)|<|>|"|'|\//g;
+		return function(code) {
+			return code ? code.toString().replace(matchHTML, function(m) {return encodeHTMLRules[m] || m;}) : "";
+		};
+	};
+
+	_globals = (function(){ return this || (0,eval)("this"); }());
+
+	/* istanbul ignore else */
+	if (typeof module !== "undefined" && module.exports) {
+		module.exports = doT;
+	} else if (typeof define === "function" && define.amd) {
+		define(function(){return doT;});
+	} else {
+		_globals.doT = doT;
+	}
+
+	var startend = {
+		append: { start: "'+(",      end: ")+'",      startencode: "'+encodeHTML(" },
+		split:  { start: "';out+=(", end: ");out+='", startencode: "';out+=encodeHTML(" }
+	}, skip = /$^/;
+
+	function resolveDefs(c, block, def) {
+		return ((typeof block === "string") ? block : block.toString())
+		.replace(c.define || skip, function(m, code, assign, value) {
+			if (code.indexOf("def.") === 0) {
+				code = code.substring(4);
+			}
+			if (!(code in def)) {
+				if (assign === ":") {
+					if (c.defineParams) value.replace(c.defineParams, function(m, param, v) {
+						def[code] = {arg: param, text: v};
+					});
+					if (!(code in def)) def[code]= value;
+				} else {
+					new Function("def", "def['"+code+"']=" + value)(def);
+				}
+			}
+			return "";
+		})
+		.replace(c.use || skip, function(m, code) {
+			if (c.useParams) code = code.replace(c.useParams, function(m, s, d, param) {
+				if (def[d] && def[d].arg && param) {
+					var rw = (d+":"+param).replace(/'|\\/g, "_");
+					def.__exp = def.__exp || {};
+					def.__exp[rw] = def[d].text.replace(new RegExp("(^|[^\\w$])" + def[d].arg + "([^\\w$])", "g"), "$1" + param + "$2");
+					return s + "def.__exp['"+rw+"']";
+				}
+			});
+			var v = new Function("def", "return " + code)(def);
+			return v ? resolveDefs(c, v, def) : v;
+		});
+	}
+
+	function unescape(code) {
+		return code.replace(/\\('|\\)/g, "$1").replace(/[\r\t\n]/g, " ");
+	}
+
+	doT.template = function(tmpl, c, def) {
+		c = c || doT.templateSettings;
+		var cse = c.append ? startend.append : startend.split, needhtmlencode, sid = 0, indv,
+			str  = (c.use || c.define) ? resolveDefs(c, tmpl, def || {}) : tmpl;
+
+		str = ("var out='" + (c.strip ? str.replace(/(^|\r|\n)\t* +| +\t*(\r|\n|$)/g," ")
+					.replace(/\r|\n|\t|\/\*[\s\S]*?\*\//g,""): str)
+			.replace(/'|\\/g, "\\$&")
+			.replace(c.interpolate || skip, function(m, code) {
+				return cse.start + unescape(code) + cse.end;
+			})
+			.replace(c.encode || skip, function(m, code) {
+				needhtmlencode = true;
+				return cse.startencode + unescape(code) + cse.end;
+			})
+			.replace(c.conditional || skip, function(m, elsecase, code) {
+				return elsecase ?
+					(code ? "';}else if(" + unescape(code) + "){out+='" : "';}else{out+='") :
+					(code ? "';if(" + unescape(code) + "){out+='" : "';}out+='");
+			})
+			.replace(c.iterate || skip, function(m, iterate, vname, iname) {
+				if (!iterate) return "';} } out+='";
+				sid+=1; indv=iname || "i"+sid; iterate=unescape(iterate);
+				return "';var arr"+sid+"="+iterate+";if(arr"+sid+"){var "+vname+","+indv+"=-1,l"+sid+"=arr"+sid+".length-1;while("+indv+"<l"+sid+"){"
+					+vname+"=arr"+sid+"["+indv+"+=1];out+='";
+			})
+			.replace(c.evaluate || skip, function(m, code) {
+				return "';" + unescape(code) + "out+='";
+			})
+			+ "';return out;")
+			.replace(/\n/g, "\\n").replace(/\t/g, '\\t').replace(/\r/g, "\\r")
+			.replace(/(\s|;|\}|^|\{)out\+='';/g, '$1').replace(/\+''/g, "");
+			//.replace(/(\s|;|\}|^|\{)out\+=''\+/g,'$1out+=');
+
+		if (needhtmlencode) {
+			if (!c.selfcontained && _globals && !_globals._encodeHTML) _globals._encodeHTML = doT.encodeHTMLSource(c.doNotSkipEncoded);
+			str = "var encodeHTML = typeof _encodeHTML !== 'undefined' ? _encodeHTML : ("
+				+ doT.encodeHTMLSource.toString() + "(" + (c.doNotSkipEncoded || '') + "));"
+				+ str;
+		}
+		try {
+			return new Function(c.varname, str);
+		} catch (e) {
+			/* istanbul ignore else */
+			if (typeof console !== "undefined") console.log("Could not create a template function: " + str);
+			throw e;
+		}
+	};
+
+	doT.compile = function(tmpl, def) {
+		return doT.template(tmpl, null, def);
+	};
+}());

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 0
static/static/lib/doT-1.1.3/doT.min.js


+ 56 - 0
static/static/lib/doT-1.1.3/doU.js

@@ -0,0 +1,56 @@
+// doU.js
+// (c) 2011, Laura Doktorova
+// https://github.com/olado/doT
+//
+// doU is an extraction and slight modification of an excellent
+// templating function from jQote2.js (jQuery plugin) by aefxx
+// (http://aefxx.com/jquery-plugins/jqote2/).
+//
+// Modifications:
+// 1. nodejs support
+// 2. allow for custom template markers
+// 3. only allow direct invocation of the compiled function
+//
+// Licensed under the MIT license.
+
+(function() {
+	var doU = { version : '0.1.2' };
+
+	if (typeof module !== 'undefined' && module.exports) {
+		module.exports = doU;
+	} else {
+		this.doU = doU;
+	}
+
+	doU.templateSettings = {
+		begin : '{{',
+		end : '}}',
+		varname : 'it'
+	};
+
+	doU.template = function(tmpl, conf) {
+		conf = conf || doU.templateSettings;
+		var str = '', tb = conf.begin, te = conf.end, m, l,
+			arr = tmpl.replace(/\s*<!\[CDATA\[\s*|\s*\]\]>\s*|[\r\n\t]|(\/\*[\s\S]*?\*\/)/g, '')
+				.split(tb).join(te +'\x1b')
+				.split(te);
+
+		for (m=0,l=arr.length; m < l; m++) {
+			str += arr[m].charAt(0) !== '\x1b' ?
+			"out+='" + arr[m].replace(/(\\|["'])/g, '\\$1') + "'" : (arr[m].charAt(1) === '=' ?
+			';out+=(' + arr[m].substr(2) + ');' : (arr[m].charAt(1) === '!' ?
+			';out+=(' + arr[m].substr(2) + ").toString().replace(/&(?!\\w+;)/g, '&#38;').split('<').join('&#60;').split('>').join('&#62;').split('" + '"' + "').join('&#34;').split(" + '"' + "'" + '"' + ").join('&#39;').split('/').join('&#x2F;');" : ';' + arr[m].substr(1)));
+		}
+
+		str = ('var out="";'+str+';return out;')
+			.split("out+='';").join('')
+			.split('var out="";out+=').join('var out=');
+
+		try {
+			return new Function(conf.varname, str);
+		} catch (e) {
+			if (typeof console !== 'undefined') console.log("Could not create a template function: " + str);
+			throw e;
+		}
+	};
+}());

+ 135 - 0
static/static/lib/doT-1.1.3/examples/advancedsnippet.txt

@@ -0,0 +1,135 @@
+Advanced templating: illustrates defines and includes.
+
+Include external snippet defined in a variable:
+{{#def.externalsnippet}}
+
+Load external template from a file:
+{{#def.loadfile('/snippet.txt')}}
+
+Load external template from a file and cache in a variable:
+{{#def['snippet.txt'] || (def['snippet.txt'] = def.loadfile('/snippet.txt'))}}
+
+Use cached file again:
+{{#def['snippet.txt']}}
+
+Here is a def block that will be used later. This snippet can be referenced from external templates too:
+{{##def.snippet1:
+	Some snippet that will be included {{#def.a}} later {{=it.f1}}
+#}}
+
+First use of snippet1:
+{{#def.snippet1}}
+
+Second use of snippet1:
+{{#def.snippet1}}
+
+Include snippet1 if true:
+{{# true && def.snippet1 }}
+
+Runtime and Compile time evaluation used together:
+{{= it.f3 + {{#def.a + def.b}} }}
+
+Include xyz or insert 'not found':
+{{#def.xyz || 'not found'}}
+
+Set xyz to 1 and exclude result from output:
+{{##def.xyz=1#}} is identical to {{#(def.xyz=1) && ""}}
+
+Compare xyz to 1, show 'xyz is not 1' if false:
+{{#def.xyz === 1 || 'xyz is not 1'}}
+
+{{ if ({{#!def.abc}}) { }}
+	{{#def.abc}} is falsy
+{{ } }}
+
+{{ if ({{#def.xyz === 1}}) { }}
+	if(true) block
+{{ } }}
+
+{{##def.fntest = function() {
+	return "Function test worked!";
+}
+#}}
+
+{{#def.fntest()}}
+
+Conditionals:
+{{? !it.altEmail }}
+	<p>
+	second email: {{= it.altEmail }}
+	</p>
+{{?? true }}
+	else case worked
+{{?}}
+
+Array iterators
+{{~ it.farray :p }}
+	<h1>{{=p.farray}}<h1>
+	{{~ p.farray :value:i }}
+		<h2>{{=i}}: {{=value}}</h2>
+		{{~ value :w }}
+			<h3>{{=w}}</h3>
+		{{~}}
+	{{~}}
+{{~}}
+
+{{~ ["apple", "banana", "orange"] :k}}
+	{{=k}}
+{{~}}
+
+{{~ (function(){ return [1,2,3]})() :k}}
+	{{=k}}
+{{~}}
+
+{{ function children(it) { }}
+
+{{?it.Nodes.length}}
+<ul>
+    {{~ it.Nodes :p}}
+    <li>
+        {{=p.title}}
+		{{children(p);}}
+    </li>
+    {{~}}
+</ul>
+{{?}}
+
+{{ } }}
+
+{{ children( {Nodes:[ {title:"1.1", Nodes:[ {title:"1.1.1", Nodes:[]}, {title:"1.1.2", Nodes:[]}] }, { title:"1.2", Nodes:[]}, { title:"1.3", Nodes:[]}], title:"1" } ); }}
+
+
+{{##def.block:param:
+	<div>{{=param}}</div>
+#}}
+
+{{##def.block1:param:
+	<div>{{=param.a}}</div>
+#}}
+
+
+{{#(def.block:'text' || '') + def.block:5}}
+
+{{#def.block:it.f3 || ''}}
+
+{{#def.block:"lala tralala" || ''}}
+
+{{#def.block1:{a:1, b:2} || ''}}
+
+{{##def.testFunctionWithParam = function(str) {
+		return "My name is: " + str;
+	}
+#}}
+
+{{##def.mytestparam: {{=it.name}} #}}
+{{#def.testFunctionWithParam(def.mytestparam)}}
+
+{{#def.testFunctionWithParam("\{\{=it.name\}\}")}}
+
+{{##def.testParamDef:myparam:
+My name is: {{=myparam}}
+#}}
+
+{{#def.testParamDef:it.name}}
+
+The end

+ 54 - 0
static/static/lib/doT-1.1.3/examples/browsersample.html

@@ -0,0 +1,54 @@
+<html>
+	<head>
+
+	<script id="headertmpl" type="text/x-dot-template">
+		<h1>{{=it.title}}</h1>
+	</script>
+
+	<script id="pagetmpl" type="text/x-dot-template">
+		<h2>Here is the page using a header template</h2>
+		{{#def.header}}
+		{{=it.name}}
+	</script>
+
+	<script id="customizableheadertmpl" type="text/x-dot-template">
+		 {{#def.header}}
+		 {{#def.mycustominjectionintoheader || ''}}
+	 </script>
+
+	<script id="pagetmplwithcustomizableheader" type="text/x-dot-template">
+		<h2>Here is the page with customized header template</h2>
+		{{##def.mycustominjectionintoheader:
+			<div>{{=it.title}} is not {{=it.name}}</div>
+		#}}
+		{{#def.customheader}}
+		{{=it.name}}
+	</script>
+
+	<script src="../doT.min.js" type="text/javascript"></script>
+	</head>
+
+	<body>
+		<div id="content"></div>
+		<div id="contentcustom"></div>
+	</body>
+
+	<script type="text/javascript">
+		var def = {
+			header: document.getElementById('headertmpl').text,
+			customheader: document.getElementById('customizableheadertmpl').text
+		};
+		var data = {
+			title: "My title",
+			name: "My name"
+		};
+
+		var pagefn = doT.template(document.getElementById('pagetmpl').text, undefined, def);
+		document.getElementById('content').innerHTML = pagefn(data);
+
+		pagefn = doT.template(document.getElementById('pagetmplwithcustomizableheader').text, undefined, def);
+		document.getElementById('contentcustom').innerHTML = pagefn(data);
+
+	</script>
+
+</html>

+ 19 - 0
static/static/lib/doT-1.1.3/examples/customdoT.js

@@ -0,0 +1,19 @@
+(function() {
+	var doT = require('../doT.js'),
+		data = { f1: 1, f2: 2, f3: 3},
+		snippet = '<h1>Just static text</h1> <p>Here is a simple case <?=it.f1+it.f3?> </p> <div class="<?=it.f1?>"> Next we will use a JavaScript block: </div> <? for(var i=0; i < it.f2; i++) { ?>	<div><?=it.f3?></div> <? }; ?>';
+
+	doT.templateSettings = {
+		evaluate : /\<\?([\s\S]+?)\?\>/g,
+		interpolate : /\<\?=([\s\S]+?)\?\>/g,
+		varname     : 'it',
+		strip: true
+	};
+
+
+
+	var doTCompiled = doT.template(snippet);
+
+	console.log("Generated function: \n" + doTCompiled.toString());
+	console.log("Result of calling with " + JSON.stringify(data) + " :\n" + doTCompiled(data));
+}());

+ 1 - 0
static/static/lib/doT-1.1.3/examples/express/index.js

@@ -0,0 +1 @@
+require("./lib/app");

+ 25 - 0
static/static/lib/doT-1.1.3/examples/express/lib/app.js

@@ -0,0 +1,25 @@
+require("dot").process({
+	global: "_page.render"
+	, destination: __dirname + "/render/"
+	, path: (__dirname + "/../templates")
+});
+
+var express = require('express')
+, http = require('http')
+, app = express()
+, render = require('./render')
+;
+
+app.get('/', function(req, res){
+  res.send(render.dashboard({text:"Good morning!"}));
+});
+
+app.use(function(err, req, res, next) {
+	console.error(err.stack);
+	res.status(500).send('Something broke!');
+});
+
+var httpServer = http.createServer(app);
+httpServer.listen(3000, function() {
+	console.log('Listening on port %d', httpServer.address().port);
+});

+ 18 - 0
static/static/lib/doT-1.1.3/examples/express/lib/render/index.js

@@ -0,0 +1,18 @@
+var path = require('path')
+	, fs = require('fs');
+
+function req(name) {
+	var module = require("./" + name);
+	delete exports[name];
+	return exports[name] = module;
+}
+
+fs.readdirSync(__dirname).forEach(function(file) {
+	if ((file === 'index.js') || (file[0] === '_')) { return; }
+	var ext = path.extname(file);
+	var stats = fs.statSync(__dirname + '/' + file);
+	if (stats.isFile() && !(ext in require.extensions)) { return; }
+	var basename = path.basename(file, '.js');
+  	exports.__defineGetter__(basename, function(){ return req(basename); });
+});
+

+ 10 - 0
static/static/lib/doT-1.1.3/examples/express/package.json

@@ -0,0 +1,10 @@
+{
+  "name": "dot-express-example",
+  "description": "doT express example",
+  "version": "0.0.1",
+  "private": true,
+  "dependencies": {
+    "express": "4.x",
+    "dot": "*"
+  }
+}

+ 5 - 0
static/static/lib/doT-1.1.3/examples/express/templates/dashboard.jst

@@ -0,0 +1,5 @@
+{{##def.test:
+	{{=it.text}}
+#}}
+
+{{#def.test}}

+ 0 - 0
static/static/lib/doT-1.1.3/examples/express/templates/login.jst


Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů