瀏覽代碼

Merge branch 'feat-international' into master

xzh 4 年之前
父節點
當前提交
afc9e30ee9
共有 65 個文件被更改,包括 1375 次插入945 次删除
  1. 65 0
      common/data/news-en.json
  2. 173 0
      common/data/news.js
  3. 173 0
      common/data/news.json
  4. 41 0
      mobile/build/build.en.js
  5. 1 1
      mobile/build/webpack.base.conf.js
  6. 101 0
      mobile/build/webpack.dev.conf.en.js
  7. 149 0
      mobile/build/webpack.prod.conf.en.js
  8. 8 0
      mobile/config/dev.env.en.js
  9. 5 0
      mobile/config/prod.env.en.js
  10. 4 1
      mobile/package.json
  11. 1 1
      mobile/src/components/editInvoice/edit.vue
  12. 10 4
      mobile/src/components/toast/index.vue
  13. 2 2
      mobile/src/lang/en/modules/agent.js
  14. 4 3
      mobile/src/lang/en/modules/login.js
  15. 1 0
      mobile/src/lang/zh/modules/login.js
  16. 1 1
      mobile/src/main.js
  17. 22 3
      mobile/src/pages/account/forget/components/emailForget.vue
  18. 1 1
      mobile/src/pages/account/forget/index.vue
  19. 7 6
      mobile/src/pages/account/login/index.vue
  20. 25 20
      mobile/src/pages/account/mailRegister/index.vue
  21. 1 0
      mobile/src/pages/account/mailRegister/style.scss
  22. 11 5
      mobile/src/pages/account/manage/change/index.vue
  23. 13 3
      mobile/src/pages/home/components/plate5.vue
  24. 1 1
      mobile/src/pages/home/index.vue
  25. 11 2
      mobile/src/pages/news/index.vue
  26. 3 0
      mobile/src/store/index.js
  27. 1 1
      mobile/src/store/language/cn/toast.js
  28. 1 1
      mobile/src/store/language/en/toast.js
  29. 9 3
      mobile/src/store/user.js
  30. 41 0
      pc/build/build.en.js
  31. 3 1
      pc/build/webpack.base.conf.js
  32. 104 0
      pc/build/webpack.dev.conf.en.js
  33. 10 1
      pc/build/webpack.dev.conf.js
  34. 166 0
      pc/build/webpack.prod.conf.en.js
  35. 52 3
      pc/build/wepack.dev.server.js
  36. 8 0
      pc/config/dev.env.en.js
  37. 5 0
      pc/config/prod.env.en.js
  38. 3 0
      pc/package.json
  39. 二進制
      pc/src/assets/images/refactor/mall/tag-icon.png
  40. 1 1
      pc/src/components/dev/Configuration.vue
  41. 56 37
      pc/src/components/dev/components/News.vue
  42. 1 1
      pc/src/components/toast/extendtoast.vue
  43. 1 1
      pc/src/lang/en/modules/agent.js
  44. 8 0
      pc/src/lang/en/modules/conduct.js
  45. 1 1
      pc/src/lang/en/modules/login.js
  46. 2 2
      pc/src/main.js
  47. 13 3
      pc/src/page/home2/index.vue
  48. 1 1
      pc/src/page/layout/header/index.vue
  49. 5 1
      pc/src/page/login/components/forget/emailForm.vue
  50. 1 1
      pc/src/page/login/components/forget/index.vue
  51. 1 1
      pc/src/page/login/components/forget/selectType.vue
  52. 7 6
      pc/src/page/login/components/login.vue
  53. 10 6
      pc/src/page/login/components/register/index.vue
  54. 1 1
      pc/src/page/mall/kankanPro/style.scss
  55. 0 133
      pc/src/page/manage/style.scss
  56. 10 4
      pc/src/page/manage/temp/change.vue
  57. 1 1
      pc/src/page/manage/temp/scene.vue
  58. 11 2
      pc/src/page/news/index.vue
  59. 0 266
      pc/src/page/purchase/index.vue
  60. 0 402
      pc/src/page/purchase/style.scss
  61. 0 5
      pc/src/router/index.js
  62. 3 0
      pc/src/store/index.js
  63. 1 1
      pc/src/store/language/en/toast.js
  64. 2 3
      pc/src/store/user.js
  65. 1 1
      pc/src/util/http.js

File diff suppressed because it is too large
+ 65 - 0
common/data/news-en.json


File diff suppressed because it is too large
+ 173 - 0
common/data/news.js


File diff suppressed because it is too large
+ 173 - 0
common/data/news.json


+ 41 - 0
mobile/build/build.en.js

@@ -0,0 +1,41 @@
+'use strict'
+require('./check-versions')()
+
+process.env.NODE_ENV = 'production'
+
+const ora = require('ora')
+const rm = require('rimraf')
+const path = require('path')
+const chalk = require('chalk')
+const webpack = require('webpack')
+const config = require('../config')
+const webpackConfig = require('./webpack.prod.conf.en')
+
+const spinner = ora('building for production...')
+spinner.start()
+
+// rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
+//   if (err) throw err
+  webpack(webpackConfig, (err, stats) => {
+    spinner.stop()
+    if (err) throw err
+    process.stdout.write(stats.toString({
+      colors: true,
+      modules: false,
+      children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build.
+      chunks: false,
+      chunkModules: false
+    }) + '\n\n')
+
+    if (stats.hasErrors()) {
+      console.log(chalk.red('  Build failed with errors.\n'))
+      process.exit(1)
+    }
+
+    console.log(chalk.cyan('  Build complete.\n'))
+    console.log(chalk.yellow(
+      '  Tip: built files are meant to be served over an HTTP server.\n' +
+      '  Opening index.html over file:// won\'t work.\n'
+    ))
+  })
+// })

+ 1 - 1
mobile/build/webpack.base.conf.js

@@ -57,7 +57,7 @@ module.exports = {
         options: {
           limit: 10000,
           name: utils.assetsPath('v2/mobile/img/[path][name].[ext]'),
-          publicPath: isProd ? 'https://4dscene.4dage.com/new4dkk/' : ''
+          publicPath: isProd ? process.env.IS_INTERNATIONAL ? 'https://eurs3.4dkankan.com/website/' : 'https://4dscene.4dage.com/new4dkk/' : ''
         }
       },
       {

+ 101 - 0
mobile/build/webpack.dev.conf.en.js

@@ -0,0 +1,101 @@
+'use strict'
+const utils = require('./utils')
+const webpack = require('webpack')
+const config = require('../config')
+const merge = require('webpack-merge')
+const path = require('path')
+const baseWebpackConfig = require('./webpack.base.conf')
+const CopyWebpackPlugin = require('copy-webpack-plugin')
+const HtmlWebpackPlugin = require('html-webpack-plugin')
+const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
+const portfinder = require('portfinder')
+
+const HOST = '0.0.0.0'
+const PORT = process.env.PORT && Number(process.env.PORT)
+
+const devWebpackConfig = merge(baseWebpackConfig, {
+  module: {
+    rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
+  },
+  // cheap-module-eval-source-map is faster for development
+  devtool: config.dev.devtool,
+
+  // these devServer options should be customized in /config/index.js
+  devServer: {
+    clientLogLevel: 'warning',
+    historyApiFallback: {
+      rewrites: [
+        { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },
+      ],
+    },
+    hot: true,
+    contentBase: false, // since we use CopyWebpackPlugin.
+    compress: true,
+    host: HOST || config.dev.host,
+    port: PORT || config.dev.port,
+    open: config.dev.autoOpenBrowser,
+    overlay: config.dev.errorOverlay
+      ? { warnings: false, errors: true }
+      : false,
+    publicPath: config.dev.assetsPublicPath,
+    proxy: config.dev.proxyTable,
+    quiet: true, // necessary for FriendlyErrorsPlugin
+    watchOptions: {
+      poll: config.dev.poll,
+    },
+    proxy: {
+      '/api': {
+        target: 'https://test.4dkankan.com',
+        changeOrigin: true,
+      }
+    }
+  },
+  plugins: [
+    new webpack.DefinePlugin({
+      'process.env': require('../config/dev.env.en')
+    }),
+    new webpack.HotModuleReplacementPlugin(),
+    new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.
+    new webpack.NoEmitOnErrorsPlugin(),
+    // https://github.com/ampedandwired/html-webpack-plugin
+    new HtmlWebpackPlugin({
+      filename: 'index.html',
+      template: 'index.html',
+      inject: true
+    }),
+    // copy custom static assets
+    new CopyWebpackPlugin([
+      {
+        from: path.resolve(__dirname, '../static'),
+        to: config.dev.assetsSubDirectory,
+        ignore: ['.*']
+      }
+    ])
+  ]
+})
+
+module.exports = new Promise((resolve, reject) => {
+  portfinder.basePort = process.env.PORT || config.dev.port
+  portfinder.getPort((err, port) => {
+    if (err) {
+      reject(err)
+    } else {
+      // publish the new Port, necessary for e2e tests
+      process.env.PORT = port
+      // add port to devServer config
+      devWebpackConfig.devServer.port = port
+
+      // Add FriendlyErrorsPlugin
+      devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
+        compilationSuccessInfo: {
+          messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
+        },
+        onErrors: config.dev.notifyOnErrors
+        ? utils.createNotifierCallback()
+        : undefined
+      }))
+
+      resolve(devWebpackConfig)
+    }
+  })
+})

+ 149 - 0
mobile/build/webpack.prod.conf.en.js

@@ -0,0 +1,149 @@
+'use strict'
+const path = require('path')
+const utils = require('./utils')
+const webpack = require('webpack')
+const config = require('../config')
+const merge = require('webpack-merge')
+const baseWebpackConfig = require('./webpack.base.conf')
+const CopyWebpackPlugin = require('copy-webpack-plugin')
+const HtmlWebpackPlugin = require('html-webpack-plugin')
+const ExtractTextPlugin = require('extract-text-webpack-plugin')
+const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
+const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
+
+const env = process.env.NODE_ENV === 'testing'
+  ? require('../config/test.env')
+  : require('../config/prod.env.en')
+
+const webpackConfig = merge(baseWebpackConfig, {
+  module: {
+    rules: utils.styleLoaders({
+      sourceMap: config.build.productionSourceMap,
+      extract: true,
+      usePostCSS: true
+    })
+  },
+  devtool: config.build.productionSourceMap ? config.build.devtool : false,
+  output: {
+    path: config.build.assetsRoot,
+    filename: utils.assetsPath('js/[name].[chunkhash].js'),
+    chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
+  },
+  plugins: [
+    // http://vuejs.github.io/vue-loader/en/workflow/production.html
+    new webpack.DefinePlugin({
+      'process.env': env
+    }),
+    new UglifyJsPlugin({
+      uglifyOptions: {
+        compress: {
+          warnings: false
+        }
+      },
+      sourceMap: config.build.productionSourceMap,
+      parallel: true
+    }),
+    // extract css into its own file
+    new ExtractTextPlugin({
+      filename: utils.assetsPath('css/[name].[contenthash].css'),
+      // Setting the following option to `false` will not extract CSS from codesplit chunks.
+      // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack.
+      // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`, 
+      // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110
+      allChunks: true,
+    }),
+    // Compress extracted CSS. We are using this plugin so that possible
+    // duplicated CSS from different components can be deduped.
+    new OptimizeCSSPlugin({
+      cssProcessorOptions: config.build.productionSourceMap
+        ? { safe: true, map: { inline: false } }
+        : { safe: true }
+    }),
+    // generate dist index.html with correct asset hash for caching.
+    // you can customize output by editing /index.html
+    // see https://github.com/ampedandwired/html-webpack-plugin
+    new HtmlWebpackPlugin({
+      filename: process.env.NODE_ENV === 'testing'
+        ? 'index.html'
+        : config.build.index,
+      template: 'index.html',
+      inject: true,
+      minify: {
+        removeComments: true,
+        collapseWhitespace: true,
+        removeAttributeQuotes: true
+        // more options:
+        // https://github.com/kangax/html-minifier#options-quick-reference
+      },
+      // necessary to consistently work with multiple chunks via CommonsChunkPlugin
+      chunksSortMode: 'dependency'
+    }),
+    // keep module.id stable when vendor modules does not change
+    new webpack.HashedModuleIdsPlugin(),
+    // enable scope hoisting
+    new webpack.optimize.ModuleConcatenationPlugin(),
+    // split vendor js into its own file
+    new webpack.optimize.CommonsChunkPlugin({
+      name: 'vendor',
+      minChunks (module) {
+        // any required modules inside node_modules are extracted to vendor
+        return (
+          module.resource &&
+          /\.js$/.test(module.resource) &&
+          module.resource.indexOf(
+            path.join(__dirname, '../node_modules')
+          ) === 0
+        )
+      }
+    }),
+    // extract webpack runtime and module manifest to its own file in order to
+    // prevent vendor hash from being updated whenever app bundle is updated
+    new webpack.optimize.CommonsChunkPlugin({
+      name: 'manifest',
+      minChunks: Infinity
+    }),
+    // This instance extracts shared chunks from code splitted chunks and bundles them
+    // in a separate chunk, similar to the vendor chunk
+    // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
+    new webpack.optimize.CommonsChunkPlugin({
+      name: 'app',
+      async: 'vendor-async',
+      children: true,
+      minChunks: 3
+    }),
+
+    // copy custom static assets
+    new CopyWebpackPlugin([
+      {
+        from: path.resolve(__dirname, '../static'),
+        to: config.build.assetsSubDirectory,
+        ignore: ['.*']
+      }
+    ])
+  ]
+})
+
+if (config.build.productionGzip) {
+  const CompressionWebpackPlugin = require('compression-webpack-plugin')
+
+  webpackConfig.plugins.push(
+    new CompressionWebpackPlugin({
+      asset: '[path].gz[query]',
+      algorithm: 'gzip',
+      test: new RegExp(
+        '\\.(' +
+        config.build.productionGzipExtensions.join('|') +
+        ')$'
+      ),
+      threshold: 10240,
+      minRatio: 0.8
+    })
+  )
+}
+
+if (config.build.bundleAnalyzerReport) {
+  const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
+  webpackConfig.plugins.push(new BundleAnalyzerPlugin())
+}
+
+module.exports = webpackConfig

+ 8 - 0
mobile/config/dev.env.en.js

@@ -0,0 +1,8 @@
+'use strict'
+const merge = require('webpack-merge')
+const prodEnv = require('./prod.env')
+
+module.exports = merge(prodEnv, {
+  NODE_ENV: '"development"',
+  IS_INTERNATIONAL: '"true"'
+})

+ 5 - 0
mobile/config/prod.env.en.js

@@ -0,0 +1,5 @@
+'use strict'
+module.exports = {
+  NODE_ENV: '"production"',
+  IS_INTERNATIONAL: '"true"'
+}

+ 4 - 1
mobile/package.json

@@ -6,12 +6,14 @@
   "private": true,
   "scripts": {
     "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
+    "dev:en": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.en.js",
     "start": "npm run dev",
     "unit": "jest --config test/unit/jest.conf.js --coverage",
     "e2e": "node test/e2e/runner.js",
     "test": "npm run unit && npm run e2e",
     "lint": "eslint --ext .js,.vue src test/unit test/e2e/specs",
-    "build": "node build/build.js"
+    "build": "node build/build.js",
+    "build:en": "node build/build.en.js"
   },
   "dependencies": {
     "axios": "^0.18.0",
@@ -50,6 +52,7 @@
     "chalk": "^2.0.1",
     "chromedriver": "^2.27.2",
     "copy-webpack-plugin": "^4.0.1",
+    "cross-env": "^7.0.3",
     "cross-spawn": "^5.0.1",
     "css-loader": "^0.28.0",
     "eslint": "^4.15.0",

+ 1 - 1
mobile/src/components/editInvoice/edit.vue

@@ -203,7 +203,7 @@ export default {
           },
           {
             name: '电子邮箱',
-            En: 'Email',
+            En: 'E-mail',
             val: emailAddress
           }
         ]

+ 10 - 4
mobile/src/components/toast/index.vue

@@ -48,7 +48,7 @@
           </div>
         </div>
         <div class="bottom" v-if="toastType==='comfirm'">
-          <span @click="visible=false" class="btn primary">{{lang==='en'?'Cancel':'取消'}}</span>
+          <span @click="visible=false" class="btn primary">{{language==='zh'?'取消':'Cancel'}}</span>
           <span class="b-line"></span>
           <span class="btn primary" @click="emitCallback">{{diycomfirm||comfirmtxt}}</span>
         </div>
@@ -65,7 +65,7 @@ import binding from './binding'
 import addcart from './addcart'
 import cooperation from './cooperation'
 import loading from './loadingicon'
-
+import { mapState } from 'vuex'
 let types = {
   warn: '提示',
   error: '错误',
@@ -109,6 +109,9 @@ export default {
     }
   },
   computed: {
+    ...mapState({
+      language: state => state.language.current
+    }),
     typeTxt () {
       return this.lang === 'en' ? typesEn[this.type] : types[this.type]
     },
@@ -120,10 +123,13 @@ export default {
   watch: {
     visible: function (newVal) {
       this.lang = localStorage.getItem('language')
-      this.comfirmtxt = this.lang === 'en' ? 'OK' : '确定'
+      this.comfirmtxt = this.lang === 'zh' ? '确定' : 'OK'
     }
   },
-  mounted () {},
+  mounted () {
+    this.lang = localStorage.getItem('language')
+    this.comfirmtxt = this.lang === 'zh' ? '确定' : 'OK'
+  },
   methods: {
     handleCooClose (data) {
       this.cooperationVisible = false

+ 2 - 2
mobile/src/lang/en/modules/agent.js

@@ -40,8 +40,8 @@ module.exports = {
         "positionPlaceholder": "Please Input the Title",
         "phoneLabel": "Phone",
         "phonePlaceholder": "Please Input the Phone",
-        "emailLabel": "Email",
-        "emailPlaceholder": "Please Input the Email",
+        "emailLabel": "E-mail",
+        "emailPlaceholder": "Please Input the E-mail",
         "submit": "Submit"
     }
 }

+ 4 - 3
mobile/src/lang/en/modules/login.js

@@ -18,11 +18,12 @@ module.exports = {
     "rePassword": "Enter password again",
     "cluse": "I have read 《4DKanKan User Agreement》",
     "findPassword": "Password Reset",
-    "emailPlaceholder": "E-mail",
+    "emailPlaceholder": "Please enter your email address",
     "hasAccount": "Already Have An Account, ",
     "zhijieLogin": "Log in Now",
     "resendTime": "{time}s resend",
     "passwordTip": "Password must contain English case and numbers no less than 8-16 characters",
-    "currentEmailTip": "Please enter a correct Email address",
-    "agreeXieyi": "Please read and agree to the user agreement"
+    "currentEmailTip": "Please enter a correct E-mail address",
+    "agreeXieyi": "Please read and agree to the user agreement",
+    account: 'Account'
 }

+ 1 - 0
mobile/src/lang/zh/modules/login.js

@@ -25,4 +25,5 @@ module.exports = {
   passwordTip: '密码必须包含英文大小写、数字、长度8-16个字符',
   currentEmailTip: '请填写正确的邮箱',
   agreeXieyi: '请阅读并同意四维看看用户协议',
+  account: '账号'
 }

+ 1 - 1
mobile/src/main.js

@@ -12,7 +12,7 @@ import { i18n } from './lang'
 Vue.use(VueAwesomeSwiper)
 // import axios from './util/http.js'
 // import router from './router'
-Vue.prototype.$cdn = 'https://4dscene.4dage.com/new4dkk/mobile/'
+Vue.prototype.$cdn = process.env.IS_INTERNATIONAL ? 'https://eurs3.4dkankan.com/website/mobile/' : 'https://4dscene.4dage.com/new4dkk/mobile/'
 // Vue.prototype.$cdn = 'https://4dscene.oss-cn-shenzhen.aliyuncs.com/new4dkk/mobile/'
 
 Vue.use(vuex)

+ 22 - 3
mobile/src/pages/account/forget/components/emailForget.vue

@@ -105,9 +105,28 @@ export default {
     },
 
     async getAuthCode () {
+      let check = value => {
+        for (let i = 0, len = value.length; i < len; i++) {
+          if (!value[i].val) {
+            return this.$toast.show('warn', (this.language === 'en' ? value[i].En : value[i].name) + this.langToast['7'])
+          }
+        }
+        return true
+      }
+      let checkStr = [
+        {
+          name: '邮箱地址',
+          En: 'E-mail',
+          val: this.phone
+        }
+      ]
+      if (!check(checkStr)) {
+        return
+      }
       let res = await this.$store.dispatch('getAuthCode', {
         phone: this.phone,
-        code: Number(this.codeActive[1].substr(1))
+        code: '',
+        qudao: 'email'
       })
       if (res) {
         this.interl && clearInterval(this.interl)
@@ -135,8 +154,8 @@ export default {
       }
       let checkStr = [
         {
-          name: '手机号码',
-          En: 'Phone number',
+          name: '邮箱',
+          En: 'E-Mail',
           val: this.phone
         },
         {

+ 1 - 1
mobile/src/pages/account/forget/index.vue

@@ -28,7 +28,7 @@ export default {
   },
   computed: {
     ...mapState({
-      isInternational: state => state.user.isInternational
+      isInternational: state => state.isInternational
     }),
     forgetComponent () {
       return this.isInternational ? 'emailForget' : 'phoneForget'

+ 7 - 6
mobile/src/pages/account/login/index.vue

@@ -4,13 +4,13 @@
     <div class="login-con">
       <div class="input-con" :class="{inputActive:inputActive==='text'}">
         <!-- <img :src="`${$cdn}images/icon/icon-phone@2x.png`" alt=""> -->
-        <input v-model="phone" oninput="value=value" @blur="inputActive=''" @focus="inputActive='text'" type="text" :placeholder="$t('login.phonePlaceholder')">
+        <input v-model="phone" oninput="value=value" @blur="inputActive=''" @focus="inputActive='text'" type="text" :placeholder="$t(isInternational ? 'login.emailPlaceholder' : 'login.phonePlaceholder')">
       </div>
       <div class="input-con"  :class="{inputActive:inputActive==='password'}">
         <!-- <img :src="`${$cdn}images/icon/icon-password@2x.png`" alt=""> -->
         <input v-model="password" @focus="inputActive='password'" @blur="inputActive=''" maxlength='16' type="password" :placeholder="$t('login.passwordPlaceholder')">
       </div>
-      <div class="forget">
+      <div class="forget" v-if="!isInternational">
         <!-- <router-link :to="{path:'/forget'}">{{langLogin.forget}}</router-link> -->
         <span></span>
         <router-link :to="{path:'/codeLogin'}">{{langLogin.codeLogin}}</router-link>
@@ -18,7 +18,7 @@
       <div class="btns" @click="login">{{langLogin.login}}</div>
       <div class="to-register">
         <div class="re-con">
-          <router-link :to="{path:'/forget'}">{{$t('login.forgetPassword')}}</router-link><router-link :to="{path:'/register'}">{{$t('login.registerAccount')}}</router-link>
+          <router-link :to="{path:'/forget'}">{{$t('login.forgetPassword')}}</router-link><router-link :to="{path: isInternational ? '/mailRegister' : '/register'}">{{$t('login.registerAccount')}}</router-link>
         </div>
       </div>
     </div>
@@ -36,7 +36,8 @@ export default {
       token: state => state.user.token,
       langToast: state => state.language.home.toast,
       language: state => state.language.current,
-      langLogin: state => state.language.home.home.loginAside
+      langLogin: state => state.language.home.home.loginAside,
+      isInternational: state => state.isInternational
     })
   },
   data () {
@@ -58,8 +59,8 @@ export default {
       }
       let checkStr = [
         {
-          name: '手机号码',
-          En: 'Phone number',
+          name: this.isInternational ? '邮箱地址' : '手机号码',
+          En: this.isInternational ? 'E-mail' : 'Phone number',
           val: this.phone
         },
         {

+ 25 - 20
mobile/src/pages/account/mailRegister/index.vue

@@ -1,13 +1,13 @@
 <template>
   <div class="register-layout">
-    <h1 class="common-title">{{ $t('login.registerTitle') }}</h1>
+    <h1 class="common-title">{{ $t('login.kankanAccountRegister') }}</h1>
     <div class="login-con">
       <div class="input-con" :class="{inputActive:inputActive==='phone'}">
-        <input class="guding" oninput="value=value" v-model="phone" @focus="inputActive='phone'" @blur="inputActive=''" type="text" :placeholder="$t('login.accountMailPlaceholder')">
+        <input class="guding" oninput="value=value" v-model="phone" @focus="inputActive='phone'" @blur="inputActive=''" type="text" :placeholder="$t('login.emailPlaceholder')">
       </div>
       <div class="code-con">
         <div class="input-con" :class="{inputActive:inputActive==='code'}">
-          <input v-model="authCode" @focus="inputActive='code'" @blur="inputActive=''" oninput="value=value.replace(/[^\d]/g,'')" maxlength='6' style="padding-left:6px;" type="text" :placeholder="$t('login.mailCodePlaceholder')">
+          <input v-model="authCode" @focus="inputActive='code'" @blur="inputActive=''" oninput="value=value.replace(/[^\d]/g,'')" maxlength='6' style="padding-left:6px;" type="text" :placeholder="langLogin.code.placeholder">
           <div v-if="!jishi" class="send-btn" @click="getAuthCode">{{langLogin.code.txt}}</div>
           <span class="send-btn" v-else>{{language==='en'?`Resend after ${interTime}s`:`${interTime}s后重新发送`}}</span>
         </div>
@@ -18,9 +18,9 @@
       <div class="input-con"  :class="{inputActive:inputActive==='confirm'}">
         <input v-model="confirmPass" @focus="inputActive='confirm'" @blur="inputActive=''" maxlength="16" type="password" :placeholder="langLogin.comfirmpass.placeholder">
       </div>
-      <div class="toMail">
+      <!-- <div class="toMail">
         <router-link to="/register">手机号注册</router-link>
-      </div>
+      </div> -->
       <div class="agree">
         <label class="check-con" @click="isAgree=!isAgree">
           <span class="check-box">
@@ -42,11 +42,12 @@
 <script>
 import selectCall from '../country.js'
 import {mapState} from 'vuex'
-import { encodeStr } from '@/util'
+import { encodeStr, reg } from '@/util'
 import { Base64 } from 'js-base64'
 export default {
   computed: {
     ...mapState({
+      token: state => state.user.token,
       language: state => state.language.current,
       langToast: state => state.language.home.toast,
       langLogin: state => state.language.home.home.loginAside
@@ -85,14 +86,16 @@ export default {
       this.codeActive = item
     },
     async getAuthCode () {
-      // if (!reg.phone.test(this.phone)) {
-      //   return
-      // }
+      if (!reg.email.test(this.phone)) {
+        this.$toast.show('warn', this.$t('login.currentEmailTip'))
+        return
+      }
 
       let res = await this.$store.dispatch('getAuthCode', {
         phone: this.phone,
         code: Number(this.codeActive[1].substr(1)),
-        type: 'resigter'
+        type: 'resigter',
+        qudao: 'email'
       })
       if (res) {
         this.interl && clearInterval(this.interl)
@@ -124,13 +127,8 @@ export default {
       }
       let checkStr = [
         {
-          name: '昵称',
-          En: 'User name',
-          val: this.nickname
-        },
-        {
-          name: '手机号码',
-          En: 'Phone number',
+          name: '邮箱地址',
+          En: 'E-mail',
           val: this.phone
         },
         {
@@ -164,7 +162,7 @@ export default {
         password: temp[0],
         phoneNum: this.phone,
         msgAuthCode: this.authCode,
-        nickName: this.nickname,
+        nickName: this.phone,
         country,
         confirmPwd: temp[1]
       }
@@ -177,8 +175,15 @@ export default {
       if (response.code !== 0) {
         return this.$toast.show('warn', this.langToast[response.code])
       }
-      this.$toast.show('success', this.langToast['23'], () => {
-        this.$router.push({path: '/login'})
+      this.$toast.showConfirm('success', this.langToast['23'], async () => {
+        let params1 = {
+          phoneNum: params.phoneNum,
+          password: encodeStr(Base64.encode(this.password))
+        }
+        await this.$store.dispatch('login', params1)
+        if (this.token) {
+          this.$router.push({name: 'information'})
+        }
       })
     }
   }

+ 1 - 0
mobile/src/pages/account/mailRegister/style.scss

@@ -150,6 +150,7 @@ input[type='password']{
 .send-btn {
   font-size: 14px;
   padding: 0 10px;
+  word-wrap: nowrap;
 }
 
 @media screen and (max-width: 340px) {

+ 11 - 5
mobile/src/pages/account/manage/change/index.vue

@@ -4,8 +4,8 @@
       <div class="order-input-con">
         <div class="order-input-item">
           <div class="order-sub">
-            <div class="top-title">{{langModify.phone.txt}}</div>
-            <div class="top-username">+{{info.country==='中国'?'86':info.country}} {{info.userName}}</div>
+            <div class="top-title">{{ $t('login.account') }}</div>
+            <div class="top-username"><span v-if="isPhone">+{{info.country==='中国'?'86':info.country}}</span> {{info.userName}}</div>
           </div>
         </div>
         <div class="order-input-item">
@@ -58,8 +58,12 @@ export default {
       language: state => state.language.current,
       langToast: state => state.language.home.toast,
       langModify: state => state.language.home.manage.modify,
-      info: state => state.user.info
-    })
+      info: state => state.user.info,
+      isInternational: state => state.isInternational
+    }),
+    isPhone () {
+      return this.info.userName.indexOf('@') === -1
+    }
   },
   data () {
     return {
@@ -76,7 +80,9 @@ export default {
       let {userName, country} = this.info
       let res = await this.$store.dispatch('getAuthCode', {
         phone: userName,
-        code: country === '中国' ? 86 : country
+        code: country === '中国' ? 86 : country,
+        email: userName,
+        qudao: this.isPhone ? '' : 'email'
       })
       if (res) {
         this.interl && clearInterval(this.interl)

+ 13 - 3
mobile/src/pages/home/components/plate5.vue

@@ -28,12 +28,13 @@
 </template>
 
 <script>
-import { News, EnNews } from '@/../../common/data/newsData'
+// import { News, EnNews } from '@/../../common/data/newsData'
 import { mapState } from 'vuex'
+import http from '@/util/http'
 export default {
   data () {
     return {
-      newsData: News.slice(0, 4),
+      newsData: [],
       swiperOption: {
         slidesPerView: 'auto',
         autoplay: false,
@@ -47,11 +48,20 @@ export default {
     })
   },
   mounted () {
-    this.newsData = this.language === 'zh' ? News.slice(0, 4) : EnNews.slice(0, 4)
+    this.loadNews()
   },
   methods: {
     toLink ({link}) {
       window.open(link)
+    },
+    loadNews () {
+      let urlMap = {
+        zh: '../www/newsList/news.json',
+        en: '../www/newsList/news-en.json'
+      }
+      http.get(`${urlMap[this.language]}?t=${new Date()}`).then(res => {
+        this.newsData = res.data.slice(0, 4)
+      })
     }
   }
 }

+ 1 - 1
mobile/src/pages/home/index.vue

@@ -106,7 +106,7 @@ export default {
       this.language === 'en' ? window.location.href = 'https://www.alibaba.com/product-detail/4DKanKan-Pro-3D-camera-3D-space_62183626283.html?spm=a2700.icbuShop.74.1.66b35b10I4miJd' : this.$router.push({name: 'purchase'})
     },
     showDetailVideo () {
-      const videoUrl = this.language === 'zh' ? `https://4d-tjw.oss-cn-shenzhen.aliyuncs.com/4dHouse/%E5%9B%9B%E7%BB%B4%E7%9C%8B%E7%9C%8BPro-%202020-7-22%E7%89%88%E6%9C%AC%288K%29.mp4` : `https://4dscene.4dage.com/new4dkk/v2/video/4DKanKanPro-en.mp4`
+      const videoUrl = this.language === 'zh' ? `${this.$cdn}v2/video/%E5%9B%9B%E7%BB%B4%E7%9C%8B%E7%9C%8BPro-%202020-7-22%E7%89%88%E6%9C%AC(8K).mp4` : `${this.$cdn}v2/video/4DKanKanPro-en.mp4`
       this.broadcast(videoUrl)
     }
   }

+ 11 - 2
mobile/src/pages/news/index.vue

@@ -29,8 +29,8 @@
 </template>
 
 <script>
-import { News, EnNews } from '@/../../common/data/newsData'
 import { mapState } from 'vuex'
+import http from '@/util/http'
 export default {
   data () {
     return {
@@ -78,7 +78,7 @@ export default {
     }
   },
   mounted () {
-    this.newsList = this.language === 'zh' ? News : EnNews
+    this.loadNews()
   },
   methods: {
     toNewsDetail (item) {
@@ -86,6 +86,15 @@ export default {
     },
     handleClickSlide (item) {
       window.open(item.link)
+    },
+    loadNews () {
+      let urlMap = {
+        zh: '../www/newsList/news.json',
+        en: '../www/newsList/news-en.json'
+      }
+      http.get(`${urlMap[this.language]}`).then(res => {
+        this.newsList = res.data
+      })
     }
   }
 }

+ 3 - 0
mobile/src/store/index.js

@@ -4,6 +4,9 @@ import ui from './ui'
 import language from './language'
 
 const store = new vuex.Store({
+  state: {
+    isInternational: process.env.IS_INTERNATIONAL
+  },
   modules: {
     user,
     language,

+ 1 - 1
mobile/src/store/language/cn/toast.js

@@ -73,7 +73,7 @@ export default{
   '3005': '验证码已过期',
   '3006': '验证码错误',
   '3007': '昵称已存在',
-  '3008': '该手机已被注册',
+  '3008': '该账号已被注册',
   '3009': '两次输入的密码不一致',
   '3010': '手机号码格式错误',
   '3011': '密码必须包含英文大小写、数字,长度8-16个字符',

+ 1 - 1
mobile/src/store/language/en/toast.js

@@ -72,7 +72,7 @@ export default{
   '3005': 'Verification code expired.',
   '3006': 'Incorrect verification code.',
   '3007': 'User name already exists.',
-  '3008': 'The mobile number is already registered.',
+  '3008': 'The account is already registered.',
   '3009': 'The password you typed don\'t match.',
   '3010': 'Incorrect format of mobile number.',
   '3011': 'Password must contain English case and numbers, 8-16 characters. ',

+ 9 - 3
mobile/src/store/user.js

@@ -39,7 +39,6 @@ try {
 
 export default {
   state: {
-    isInternational: process.env.IS_INTERNATIONAL,
     token: token,
     name: null,
     cart: cart,
@@ -366,8 +365,7 @@ export default {
     async getAuthCode (context, item) {
       let toastCode = localStorage.getItem('language') === 'en' ? toastEN : toastZH
 
-      let {phone, code, type = ''} = item
-      console.log(item)
+      let {phone, code, type = '', qudao} = item
       let areaNum = Number(code) || 86
       if (phone) {
         let resp = await http
@@ -390,6 +388,14 @@ export default {
           phoneNum: phone,
           areaNum
         }
+        if (qudao === 'email') {
+          params = {
+            email: phone,
+            country: 1
+          }
+          API.getEmailAuthCode(params)
+          return true
+        }
         http({
           method: 'post',
           data: params,

+ 41 - 0
pc/build/build.en.js

@@ -0,0 +1,41 @@
+'use strict'
+require('./check-versions')()
+
+process.env.NODE_ENV = 'production'
+
+const ora = require('ora')
+const rm = require('rimraf')
+const path = require('path')
+const chalk = require('chalk')
+const webpack = require('webpack')
+const config = require('../config')
+const webpackConfig = require('./webpack.prod.conf.en')
+process.env.IS_INTERNATIONAL = true
+const spinner = ora('building for production...')
+spinner.start()
+
+// rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
+  // if (err) throw err
+  webpack(webpackConfig, (err, stats) => {
+    spinner.stop()
+    if (err) throw err
+    process.stdout.write(stats.toString({
+      colors: true,
+      modules: false,
+      children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build.
+      chunks: false,
+      chunkModules: false
+    }) + '\n\n')
+
+    if (stats.hasErrors()) {
+      console.log(chalk.red('  Build failed with errors.\n'))
+      process.exit(1)
+    }
+
+    console.log(chalk.cyan('  Build complete.\n'))
+    console.log(chalk.yellow(
+      '  Tip: built files are meant to be served over an HTTP server.\n' +
+      '  Opening index.html over file:// won\'t work.\n'
+    ))
+  })
+// })

+ 3 - 1
pc/build/webpack.base.conf.js

@@ -20,6 +20,8 @@ const createLintingRule = () => ({
 })
 
 const isProd = process.env.NODE_ENV === 'production'
+const isInternational = process.env.IS_INTERNATIONAL
+
 module.exports = {
   context: path.resolve(__dirname, '../'),
   entry: {
@@ -58,7 +60,7 @@ module.exports = {
         options: {
           limit: 10000,
           name: utils.assetsPath('v2/images/[path][name].[ext]'),
-          publicPath: isProd ? 'https://4dscene.4dage.com/new4dkk/' : ''
+          publicPath: isProd ?  'https://4dscene.4dage.com/new4dkk/' : ''
         }
       },
       {

+ 104 - 0
pc/build/webpack.dev.conf.en.js

@@ -0,0 +1,104 @@
+'use strict'
+const utils = require('./utils')
+const webpack = require('webpack')
+const config = require('../config')
+const merge = require('webpack-merge')
+const path = require('path')
+const baseWebpackConfig = require('./webpack.base.conf')
+const CopyWebpackPlugin = require('copy-webpack-plugin')
+const HtmlWebpackPlugin = require('html-webpack-plugin')
+const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
+const portfinder = require('portfinder')
+const wepackDevServer = require('./wepack.dev.server')
+
+// const HOST = '192.168.0.172'
+const HOST = '0.0.0.0'
+const PORT = process.env.PORT && Number(process.env.PORT)
+
+const devWebpackConfig = merge(baseWebpackConfig, {
+  module: {
+    rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
+  },
+  // cheap-module-eval-source-map is faster for development
+  devtool: config.dev.devtool,
+
+  // these devServer options should be customized in /config/index.js
+  devServer: {
+    clientLogLevel: 'warning',
+    historyApiFallback: {
+      rewrites: [
+        { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },
+      ],
+    },
+    hot: true,
+    contentBase: false, // since we use CopyWebpackPlugin.
+    compress: true,
+    host: HOST || config.dev.host,
+    port: PORT || config.dev.port,
+    open: config.dev.autoOpenBrowser,
+    overlay: config.dev.errorOverlay
+      ? { warnings: false, errors: true }
+      : false,
+    publicPath: config.dev.assetsPublicPath,
+    proxy: config.dev.proxyTable,
+    quiet: true, // necessary for FriendlyErrorsPlugin
+    watchOptions: {
+      poll: config.dev.poll,
+    },
+    proxy: {
+      '/api': {
+        target: 'https://test.4dkankan.com',
+        changeOrigin: true,
+      }
+    },
+    setup: wepackDevServer
+  },
+  plugins: [
+    new webpack.DefinePlugin({
+      'process.env': require('../config/dev.env.en')
+    }),
+    new webpack.HotModuleReplacementPlugin(),
+    new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.
+    new webpack.NoEmitOnErrorsPlugin(),
+    // https://github.com/ampedandwired/html-webpack-plugin
+    new HtmlWebpackPlugin({
+      filename: 'index.html',
+      template: 'index.html',
+      inject: true
+    }),
+    // copy custom static assets
+    new CopyWebpackPlugin([
+      {
+        from: path.resolve(__dirname, '../static'),
+        to: config.dev.assetsSubDirectory,
+        ignore: ['.*']
+      }
+    ])
+  ]
+})
+
+module.exports = new Promise((resolve, reject) => {
+  portfinder.basePort = process.env.PORT || config.dev.port
+  portfinder.getPort((err, port) => {
+    if (err) {
+      reject(err)
+    } else {
+      // publish the new Port, necessary for e2e tests
+      process.env.PORT = port
+      // add port to devServer config
+      devWebpackConfig.devServer.port = port
+
+      // Add FriendlyErrorsPlugin
+      devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
+        compilationSuccessInfo: {
+          messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
+        },
+        onErrors: config.dev.notifyOnErrors
+        ? utils.createNotifierCallback()
+        : undefined
+      }))
+
+      resolve(devWebpackConfig)
+    }
+  })
+})

+ 10 - 1
pc/build/webpack.dev.conf.js

@@ -49,7 +49,16 @@ const devWebpackConfig = merge(baseWebpackConfig, {
       '/api': {
         target: 'https://test.4dkankan.com',
         changeOrigin: true,
-      }
+      },
+      '/www': {
+        target: 'https://test.4dkankan.com',
+        changeOrigin: true,
+      },
+      '/node-upload': {
+        target: 'http://vrhouse2.4dkankan.com',
+        changeOrigin: true,
+      },
+      
     },
     setup: wepackDevServer
   },

+ 166 - 0
pc/build/webpack.prod.conf.en.js

@@ -0,0 +1,166 @@
+'use strict'
+const path = require('path')
+const utils = require('./utils')
+const webpack = require('webpack')
+const config = require('../config')
+const merge = require('webpack-merge')
+const baseWebpackConfig = require('./webpack.base.conf')
+const CopyWebpackPlugin = require('copy-webpack-plugin')
+const HtmlWebpackPlugin = require('html-webpack-plugin')
+const ExtractTextPlugin = require('extract-text-webpack-plugin')
+const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
+const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
+
+const env = process.env.NODE_ENV === 'testing'
+  ? require('../config/test.env')
+  : require('../config/prod.env.en')
+
+const webpackConfig = merge(baseWebpackConfig, {
+  module: {
+    rules: [...utils.styleLoaders({
+      sourceMap: config.build.productionSourceMap,
+      extract: true,
+      usePostCSS: true
+    }, {
+      test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
+      loader: 'url-loader',
+      options: {
+        limit: 10000,
+        name: utils.assetsPath('v2/images/[path][name].[ext]'),
+        publicPath: './'
+      }
+    },)
+  ]
+  },
+  devtool: config.build.productionSourceMap ? config.build.devtool : false,
+  output: {
+    path: config.build.assetsRoot,
+    filename: utils.assetsPath('js/[name].[chunkhash].js'),
+    chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
+  },
+  plugins: [
+    // http://vuejs.github.io/vue-loader/en/workflow/production.html
+    new webpack.DefinePlugin({
+      'process.env': env
+    }),
+    new UglifyJsPlugin({
+      uglifyOptions: {
+        compress: {
+          warnings: false
+        }
+      },
+      sourceMap: config.build.productionSourceMap,
+      parallel: true
+    }),
+    // extract css into its own file
+    new ExtractTextPlugin({
+      filename: utils.assetsPath('css/[name].[contenthash].css'),
+      // Setting the following option to `false` will not extract CSS from codesplit chunks.
+      // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack.
+      // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`, 
+      // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110
+      allChunks: true,
+    }),
+    // Compress extracted CSS. We are using this plugin so that possible
+    // duplicated CSS from different components can be deduped.
+    new OptimizeCSSPlugin({
+      cssProcessorOptions: config.build.productionSourceMap
+        ? { safe: true, map: { inline: false } }
+        : { safe: true }
+    }),
+    // generate dist index.html with correct asset hash for caching.
+    // you can customize output by editing /index.html
+    // see https://github.com/ampedandwired/html-webpack-plugin
+    new HtmlWebpackPlugin({
+      filename: process.env.NODE_ENV === 'testing'
+        ? 'index.html'
+        : config.build.index,
+      template: 'index.html',
+      inject: true,
+      minify: {
+        removeComments: true,
+        collapseWhitespace: true,
+        removeAttributeQuotes: true
+        // more options:
+        // https://github.com/kangax/html-minifier#options-quick-reference
+      },
+      // necessary to consistently work with multiple chunks via CommonsChunkPlugin
+      chunksSortMode: 'dependency'
+    }),
+    // keep module.id stable when vendor modules does not change
+    new webpack.HashedModuleIdsPlugin(),
+    // enable scope hoisting
+    new webpack.optimize.ModuleConcatenationPlugin(),
+    // split vendor js into its own file
+    new webpack.optimize.CommonsChunkPlugin({
+      name: 'vendor',
+      minChunks (module) {
+        // any required modules inside node_modules are extracted to vendor
+        return (
+          module.resource &&
+          /\.js$/.test(module.resource) &&
+          module.resource.indexOf(
+            path.join(__dirname, '../node_modules')
+          ) === 0
+        )
+      }
+    }),
+    // extract webpack runtime and module manifest to its own file in order to
+    // prevent vendor hash from being updated whenever app bundle is updated
+    new webpack.optimize.CommonsChunkPlugin({
+      name: 'manifest',
+      minChunks: Infinity
+    }),
+    // This instance extracts shared chunks from code splitted chunks and bundles them
+    // in a separate chunk, similar to the vendor chunk
+    // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
+    new webpack.optimize.CommonsChunkPlugin({
+      name: 'app',
+      async: 'vendor-async',
+      children: true,
+      minChunks: 3
+    }),
+
+    // copy custom static assets
+    new CopyWebpackPlugin([
+      {
+        from: path.resolve(__dirname, '../static'),
+        to: config.build.assetsSubDirectory,
+        ignore: ['.*']
+      }
+    ]),
+
+    new CopyWebpackPlugin([
+      {
+        from: path.resolve(__dirname, '../main.html'),
+        to: path.resolve(__dirname, '../../dist'),
+        ignore: ['.*']
+      }
+    ])
+  ]
+})
+
+if (config.build.productionGzip) {
+  const CompressionWebpackPlugin = require('compression-webpack-plugin')
+
+  webpackConfig.plugins.push(
+    new CompressionWebpackPlugin({
+      asset: '[path].gz[query]',
+      algorithm: 'gzip',
+      test: new RegExp(
+        '\\.(' +
+        config.build.productionGzipExtensions.join('|') +
+        ')$'
+      ),
+      threshold: 10240,
+      minRatio: 0.8
+    })
+  )
+}
+
+if (config.build.bundleAnalyzerReport) {
+  const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
+  webpackConfig.plugins.push(new BundleAnalyzerPlugin())
+}
+
+module.exports = webpackConfig

+ 52 - 3
pc/build/wepack.dev.server.js

@@ -2,13 +2,38 @@ const fs = require('fs')
 const path = require('path')
 const bodyParser = require('body-parser');
 const langDir = path.join(__dirname, '../src/lang')
+const oss = require('ali-oss');
+const OSSCONFIG =  {
+  region: 'oss-cn-shenzhen',
+  //云账号AccessKey有所有API访问权限,建议遵循阿里云安全最佳实践,部署在服务端使用RAM子账号或STS,部署在客户端使用STS。
+  accessKeyId: 'LTAI4G6f1efh9idnE8DfA71X',
+  accessKeySecret: 'UQBHqXh4saRBXZeOYAIhJhnuzIcyhR',
+  bucket: '4dscene'
+}
+const store = oss(OSSCONFIG)
+var FtpDeploy = require("ftp-deploy");
+var ftpDeploy = new FtpDeploy();
+var config = {
+    user: "xuzhihao",
+    // Password optional, prompted if none given
+    password: "xuzhihao123",
+    host: "192.168.0.115",
+    port: 2222,
+    localRoot: './../common/data/',
+    remoteRoot: "/测试服务器-120.25.146.52/root/user/java/apache-tomcat-8.0.47/webapps/4dkankan_v2/WEB-INF/classes/web/www/newsList",
+    include: ["news.json", 'news-en.json'],
+    exclude: ["dist/**/*.map", "node_modules/**", "node_modules/**/.*", ".git/**"],
+    deleteRemote: false,
+    forcePasv: true,
+    sftp: true
+};
+
 module.exports = function(app, server) {
     app.use('/dev', bodyParser.json());
     app.use('/dev', bodyParser.urlencoded({ extended: false }));
 
     app.get('/dev/lang/:id', function(req, res) {
         let filePath = path.join(langDir, req.params.id + '/modules')
-        console.log(filePath)
         const files = fs.readdirSync(filePath)
         let data = {}
         files.forEach(item => {
@@ -20,9 +45,33 @@ module.exports = function(app, server) {
     app.post('/dev/lang/:id', async function(req, res) {
       await Object.keys(req.body).forEach(async item => {
         await fs.writeFile('./src/lang/' + req.params.id + `/modules/${item}.js`, 'module.exports = ' + JSON.stringify(req.body[item], null, 4), err => {
-          
+          res.json({ ok: true });
         })
       })
-      res.json({ ok: true });
+     
     });
+
+    app.post('/dev/news/update', async function(req, res) {
+      const data = req.body.data
+      const localUrl = {
+        zh: './../common/data/news.json',
+        en: './../common/data/news-en.json'
+      }
+      
+      await fs.writeFile(localUrl[req.body.lang], `${JSON.stringify(data, null, 4)}`, async err => {
+        ftpDeploy
+        .deploy(config)
+        .then(() => {
+          res.json({ ok: true });
+        })
+        .catch(err => console.log(err));
+        // try {
+        //   await store.put(oss_path, OUTPUT)
+        //   console.log('success')
+        // } catch (err) {
+        //   console.log(err)
+        // }
+        
+      })
+    })
 }

+ 8 - 0
pc/config/dev.env.en.js

@@ -0,0 +1,8 @@
+'use strict'
+const merge = require('webpack-merge')
+const prodEnv = require('./prod.env')
+
+module.exports = merge(prodEnv, {
+  NODE_ENV: '"development"',
+  IS_INTERNATIONAL: '"true"'
+})

+ 5 - 0
pc/config/prod.env.en.js

@@ -0,0 +1,5 @@
+'use strict'
+module.exports = {
+  NODE_ENV: '"production"',
+  IS_INTERNATIONAL: 'true'
+}

+ 3 - 0
pc/package.json

@@ -6,12 +6,14 @@
   "private": true,
   "scripts": {
     "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
+    "dev:en": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.en.js",
     "start": "npm run dev",
     "unit": "jest --config test/unit/jest.conf.js --coverage",
     "e2e": "node test/e2e/runner.js",
     "test": "npm run unit && npm run e2e",
     "lint": "eslint --ext .js,.vue src test/unit test/e2e/specs",
     "build": "node build/build.js",
+    "build:en": "node build/build.en.js",
     "preview": "http-server ../ -p 3030"
   },
   "dependencies": {
@@ -30,6 +32,7 @@
     "swiper": "5.3.6"
   },
   "devDependencies": {
+    "ali-oss": "^6.12.0",
     "autoprefixer": "^7.1.2",
     "babel-core": "^6.22.1",
     "babel-eslint": "^8.2.1",

二進制
pc/src/assets/images/refactor/mall/tag-icon.png


+ 1 - 1
pc/src/components/dev/Configuration.vue

@@ -31,7 +31,7 @@
 export default {
     data() {
         return {
-            menu: [{ text: "语言设置", name: "Lang" }, { text: "新闻设置", name: "News" }],
+            menu: [{ text: "语言设置", name: "Lang" }, { text: '新闻设置', name: 'News'}],
             page: "Lang"
         };
     },

+ 56 - 37
pc/src/components/dev/components/News.vue

@@ -9,22 +9,28 @@
     <layout>
         <template slot="header">
             <span>
-                <!-- 语言选择:
-                <select v-model="curr" @change="load()">
+                语言选择:
+                <select v-model="curr" @change="loadNews()">
                     <option
                         v-for="(item,key) in langs"
                         :key="key"
                         :value="item.name"
                     >{{item.text}}</option>
-                </select> -->
+                </select>
             </span>
-            <button >保存</button>
+            <button @click="onCreate">添加新闻</button>
+            <button @click="onSave">保存</button>
         </template>
         <template slot="content">
             <ul class="menus">
                 <li v-for="(item,key) in News" :key="key">
-                  <h1>第{{key + 1}}篇</h1>
-                    <img :src="item.img" />
+                  <h1>新闻</h1>
+                    <input type="file" @change="handleImgChange(key, $event)" />
+                    <img :src="item.img" width="304" height="200" />
+                    <div>
+                      <p>排序</p>
+                      <input v-model="item.sort" type="number" />
+                    </div>
                     <div>
                       <p>标题</p>
                       <input v-model="item.title" type="text" />
@@ -54,7 +60,7 @@
 import http from "@/util/http";
 import Layout from "./Layout";
 import { deepExtend } from '@/util/tools'
-import { News } from '@/../../common/data/newsData'
+// import { EnNews } from '@/../../common/data/newsData'
 export default {
     components: {
         Layout
@@ -63,7 +69,7 @@ export default {
         return {
             lang: null,
             curr: "zh",
-            News
+            News: []
         };
     },
     computed: {
@@ -75,24 +81,9 @@ export default {
         }
     },
     created() {
-        this.load();
+        this.loadNews()
     },
     methods: {
-        async load(curr) {
-            const result = await http.get("../dev/lang/" + (curr || this.curr));
-            if (result.data.ok) {
-                if (this.lang == null) {
-                    this.lang = result.data.data;
-                } else {
-                    this.lang = deepExtend(this.lang, result.data.data);
-                }
-            } else {
-                if (this.curr != "zh") {
-                    this.lang = null;
-                    this.load("zh");
-                }
-            }
-        },
         isChildren(key, value) {
             return key !== "__name" && typeof value === "string";
         },
@@ -108,20 +99,48 @@ export default {
                 }
             }
         },
+        loadNews () {
+            let urlMap = {
+                zh: 'https://4dscene.4dage.com/new4dkk/news/news.json',
+                en: 'https://4dscene.4dage.com/new4dkk/news/news-en.json'
+            }
+            http.get(urlMap[this.curr]).then(res => {
+                this.News = res.data || []
+            })
+        },
         onSave() {
-            // http.post(`../dev/lang/${this.curr}`, this.lang)
-            // .then(response => {
-            //     if (response.data.ok) {
-            //         this.$alert('保存成功', {
-            //             icon: 'success'
-            //         });
-            //     } else {
-            //         this.$alert('保存失败');
-            //     }
-            // })
-            // .catch(err => {
-            //     this.$alert('保存失败');
-            // });
+            this.News.sort((a, b) => a.sort - b.sort)
+            
+            http.post(`../dev/news/update`, { data: this.News.filter(item => item.title), lang: this.curr})
+            .then(response => {
+                if (response.data.ok) {
+                    this.$alert('保存成功', {
+                        icon: 'success'
+                    });
+                } else {
+                    this.$alert('保存失败');
+                }
+            })
+            .catch(err => {
+                this.$alert('保存失败');
+            });
+        },
+        handleImgChange (index, e) {
+            const file = e.target.files[0]
+            let formdata = new FormData()
+            formdata.append('file', file)
+            http.post(`../node-upload/uploadfile`, formdata)
+            .then(res => {
+                const url = res.data.data.url
+                this.News[index].img = url
+            })
+            .catch(err => {
+                console.error(err)
+                this.$alert('图片上传失败')
+            });
+        },
+        onCreate () {
+            this.News.unshift({})
         }
     }
 };

+ 1 - 1
pc/src/components/toast/extendtoast.vue

@@ -4,7 +4,7 @@
 
 <script>
 import Toast from './index.vue'
-let baseUrl = 'https://4dscene.4dage.com/new4dkk/'
+let baseUrl = process.env.IS_INTERNATIONAL ? '/' : 'https://4dscene.4dage.com/new4dkk/'
 
 let imgs = {
   success: baseUrl + 'images/icon/success.png',

+ 1 - 1
pc/src/lang/en/modules/agent.js

@@ -41,7 +41,7 @@ module.exports = {
         "phoneLabel": "Phone",
         "phonePlaceholder": "Please Input the Phone",
         "emailLabel": "Email",
-        "emailPlaceholder": "Please Input the Email",
+        "emailPlaceholder": "Please Input your account",
         "submit": "Submit"
     }
 }

+ 8 - 0
pc/src/lang/en/modules/conduct.js

@@ -11,6 +11,14 @@ module.exports = {
         "itemTitle6": "Autonomous Space Modeling in 10 Minutes. (About 100㎡)",
         "itemTitle7": "Multifunctional Editing Tool, Let Space Tell the Story"
     },
+    "sxz": {
+        "bannerTitle": "随心装 . 快速打造心中家的模样",
+        "itemTitle1": "1:1精准复刻真实房源",
+        "itemTitle2": "实物家具建模,让您所见即所得",
+        "itemTitle3": "快速打造3D家装效果,让人工智能真正服务于生活",
+        "itemTitle4": "高效全屋渲染,秒级响应拒绝等待",
+        "itemTitle5": "变装前后同屏看,一眼对比屋内变化"
+    },
     "coreTech": {
         "bannerTitle": "Technology",
         "bannerSubTitle": "Bringing Digital Technologies to the Everyday Life of Common People",

+ 1 - 1
pc/src/lang/en/modules/login.js

@@ -18,7 +18,7 @@ module.exports = {
     "rePassword": "Enter password again",
     "cluse": "I have read 《4DKanKan User Agreement》",
     "findPassword": "Password Reset",
-    "emailPlaceholder": "E-mail",
+    "emailPlaceholder": "Please enter your email address",
     "hasAccount": "Already had an account, ",
     "zhijieLogin": "Log in Now",
     "resendTime": "{time}s resend",

+ 2 - 2
pc/src/main.js

@@ -8,7 +8,7 @@ import { i18n } from './lang'
 import './register-components'
 // import axios from './util/http.js'
 // import router from './router'
-Vue.prototype.$cdn = 'https://4dscene.4dage.com/new4dkk/'
+Vue.prototype.$cdn = process.env.IS_INTERNATIONAL ? 'https://4dscene.4dage.com/new4dkk/' : 'https://4dscene.4dage.com/new4dkk/'
 
 Vue.use(vuex)
 Vue.use(Toast)
@@ -19,7 +19,7 @@ let router = require('./router').default
 
 Vue.prototype.$http = axios
 // Vue.prototype.$serverName = 'http://192.168.0.10:8080/'
-Vue.prototype.$serverName = 'https://www.4dkankan.com/'
+Vue.prototype.$serverName = ''
 
 // Vue.prototype.$serverName = 'https://test.4dkankan.com'
 

+ 13 - 3
pc/src/page/home2/index.vue

@@ -102,6 +102,7 @@ import number from '@/components/number'
 import sequence from '@/components/sequence'
 import {mapState} from 'vuex'
 import { News, EnNews } from '@/../../common/data/newsData'
+import http from "@/util/http"
 export default {
   data () {
     return {
@@ -214,8 +215,8 @@ export default {
     toMore (item) {
       this.$router.push(item.link)
     },
-    detailVideo () {
-      const videoUrl = this.language === 'zh' ? `https://4d-tjw.oss-cn-shenzhen.aliyuncs.com/4dHouse/%E5%9B%9B%E7%BB%B4%E7%9C%8B%E7%9C%8BPro-%202020-7-22%E7%89%88%E6%9C%AC%288K%29.mp4` : `${this.$cdn}v2/video/4DKanKanPro-en.mp4`
+    detailVideo() {
+      const videoUrl = this.language === 'zh' ? `${this.$cdn}v2/video/%E5%9B%9B%E7%BB%B4%E7%9C%8B%E7%9C%8BPro-%202020-7-22%E7%89%88%E6%9C%AC(8K).mp4` : `${this.$cdn}v2/video/4DKanKanPro-en.mp4`
       this.$bus.$emit('toggleVideo', { url: videoUrl })
     },
     toNewsLink (link) {
@@ -228,6 +229,15 @@ export default {
           id: item.value
         }
       })
+    },
+    loadNews () {
+      let urlMap = {
+        zh: '../www/newsList/news.json',
+        en: '../www/newsList/news-en.json'
+      }
+      http.get(`${urlMap[this.language]}`).then(res => {
+        this.plate05List = res.data.slice(0,4)
+      })
     }
   },
   computed: {
@@ -241,7 +251,7 @@ export default {
     }
   },
   mounted () {
-    this.plate05List = this.language === 'zh' ? News.slice(0, 4) : EnNews.slice(0, 4)
+    this.loadNews()
     let open = this.$route.query.open
     setTimeout(() => {
       if (open && !this.token) {

+ 1 - 1
pc/src/page/layout/header/index.vue

@@ -189,7 +189,7 @@ export default {
       info: state => state.user.info,
       deviceLogin: state => state.user.deviceLogin,
       languageList: state => state.language.languageList,
-      isInternational: state => state.user.isInternational,
+      isInternational: state => state.isInternational,
       homeLang: state => state.language.home
     }),
     languageObj () {

+ 5 - 1
pc/src/page/login/components/forget/emailForm.vue

@@ -105,6 +105,10 @@ export default {
       if (this.emailInterl) {
         return
       }
+      if (!this.form.phone) {
+        this.$toast.show('warn', (this.language === 'en' ? 'E-mail' : '邮箱') + this.langToast['7'])
+        return
+      }
       let res = await this.$store.dispatch('getAuthCode', {
         email: this.form.phone,
         qudao: 'email'
@@ -135,7 +139,7 @@ export default {
       let checkStr = [
         {
           name: this.type === 'email' ? '邮箱' : '手机',
-          En: 'Phone number',
+          En: this.type === 'email' ? 'E-mail' : 'Phone number',
           val: this.form.phone
         },
         {

+ 1 - 1
pc/src/page/login/components/forget/index.vue

@@ -22,7 +22,7 @@ export default {
   },
   computed: {
     ...mapState({
-      isInternational: state => state.user.isInternational
+      isInternational: state => state.isInternational
     }),
     title () {
       return this.$t('login.findPassword')

+ 1 - 1
pc/src/page/login/components/forget/selectType.vue

@@ -13,7 +13,7 @@ import { mapState } from 'vuex'
 export default {
   computed: {
     ...mapState({
-      isInternational: state => state.user.isInternational
+      isInternational: state => state.isInternational
     })
   },
   methods: {

+ 7 - 6
pc/src/page/login/components/login.vue

@@ -4,20 +4,21 @@
       <div class="login-tab" :class="{'is-active': !type || type === 'codeLogin'}" @click="toOtherLogin('')">{{$t('login.userLogin')}}</div><div class="login-tab" :class="{'is-active': type==='camera'}" @click="toOtherLogin('camera')">{{$t('login.cameraLogin')}}</div>
     </div>
     <div class="account-login" v-if="!type">
-      <input type="text" oninput="value=value.replace(/[^\d]/g,'')" maxlength="11" class="input" :placeholder="isInternational? $t('login.emailPlaceholder') : $t('login.phonePlaceholder')" v-model="form.phone">
+      <input type="text" v-if="!isInternational" oninput="value=value.replace(/[^\d]/g,'')" maxlength="11" class="input" :placeholder="isInternational? $t('login.emailPlaceholder') : $t('login.phonePlaceholder')" v-model="form.phone">
+      <input type="text" v-else class="input" :placeholder="isInternational? $t('login.emailPlaceholder') : $t('login.phonePlaceholder')" v-model="form.phone">
       <div class="password-w">
         <input maxLength="16" :type="showPassword ? 'test' : 'password'" class="input password" :placeholder="$t('login.passwordPlaceholder')" v-model="form.password" @keyup.enter="login">
         <h-icon :type="showPassword ? 'mimakejian' : 'mimabukejian'" class="password-visible" @click="showPassword=!showPassword" />
       </div>
       
       <div class="toCodeLogin tips-tap" >
-        <a @click="$router.push({name: 'login', query: {type: 'codeLogin'}})">{{$t('login.codeLogin')}}</a>
+        <!-- <a @click="$router.push({name: 'login', query: {type: 'codeLogin'}})">{{$t('login.codeLogin')}}</a> -->
       </div>
       <div class="checkbox"><input type="checkbox" v-model="rememberMe" id="rember" /><label for="rember">{{$t('login.rememberPassword')}}</label></div>
       <div class="login-btn"  @click="login" >{{$t('login.login')}}</div>
       <div class="others">
         <a class="other-actions" @click="$router.push('/login/forget')">{{ $t('login.forgetPassword') }}</a>
-        <a class="other-actions" @click="$router.push('/login/register')">{{ $t('login.registerAccount') }}</a>
+        <a class="other-actions" @click="$router.push(isInternational ? '/login/register?type=email' : '/login/register')">{{ $t('login.registerAccount') }}</a>
       </div>
     </div>
     <cameraLogin v-if="type === 'camera'" />
@@ -49,7 +50,7 @@ export default {
     ...mapState({
       language: state => state.language.current,
       langToast: state => state.language.home.toast,
-      isInternational: state => state.user.isInternational
+      isInternational: state => state.isInternational
     }),
     type () {
       return this.$route.query.type
@@ -83,8 +84,8 @@ export default {
         }
         let checkStr = [
           {
-            name: '手机',
-            En: 'Phone number',
+            name: this.isInternational ? '邮箱' : '手机',
+            En: this.isInternational ? 'E-mail' :'Phone number',
             val: this.form.phone
           },
           {

+ 10 - 6
pc/src/page/login/components/register/index.vue

@@ -33,9 +33,12 @@
       <!-- <input placeholder="请输入昵称" type="text" v-model="emailForm.nickname"> -->
       <div class="code-w">
         <input :placeholder="$t('login.emailPlaceholder')" class="phone" type="text" v-model="emailForm.phone">
-        <div class="send-code-btn" :class="{'is-disabled': interEmailTime}" @click="getEmailAuthCode">{{ interEmailTime ? `${interEmailTime}s后重试` : '获取验证码'}}</div>
       </div>
-      <input :placeholder="$t('login.codePlaceholder')"  type="text" v-model="emailForm.authCode">
+      <div class="code-w">
+        <input :placeholder="$t('login.codePlaceholder')"  type="text" v-model="emailForm.authCode">
+        <div class="send-code-btn" :class="{'is-disabled': interEmailTime}" @click="getEmailAuthCode">{{ interEmailTime ? $t('login.resendTime', { time: interEmailTime }) : $t('login.sendCodeBtnText')}}</div>
+      </div>
+      
       <div class="password-w" :class="{'show-tip': !emailForm.password && passwordTip}" @mouseleave="passwordTip=false" @click="focusInput('emialInput')" >
         <input maxLength="16" ref="emialInput" :placeholder="passwordTip ? '' : $t('login.setPassword')" autocomplete="new-password "  v-model="emailForm.password" type="password" @mouseover="passwordTip=true" >
         <p>{{$t('login.passwordTip')}}</p>
@@ -90,7 +93,7 @@ export default {
   computed : {
     ...mapState({
       langToast: state => state.language.home.toast,
-      isInternational: state => state.user.isInternational,
+      isInternational: state => state.isInternational,
       token: state => state.user.token,
       language: state => state.language.current
     })
@@ -124,6 +127,7 @@ export default {
     this.$bus.$on('isAgree', data => {
       this.showCluse = data
     })
+    this.type = this.isInternational ? 'email' : 'phone'
   },
   methods: {
     changeArea (item) {
@@ -166,7 +170,7 @@ export default {
         return
       }
       if (!this.emailForm.phone) {
-        this.$alert(this.$t('login.emailPlaceholder'))
+        this.$toast.show('warn', (this.language === 'en' ? 'E-mail' : '邮箱') + this.langToast['7'])
         return
       }
       let patt = /^[A-Za-z0-9\u4e00-\u9fa5]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/
@@ -216,7 +220,7 @@ export default {
         // },
         {
           name: this.type === 'email' ? '邮箱' : '手机',
-          En: 'Phone number',
+          En: this.type === 'email' ? 'E-mail' : 'Phone number',
           val: form.phone,
           required: true
         },
@@ -418,7 +422,7 @@ export default {
   }
   .password-w {
     position: relative;
-    margin-bottom: 20px;
+    margin: 0 0 20px;
     input {
       margin-bottom: 0;
     }

+ 1 - 1
pc/src/page/mall/kankanPro/style.scss

@@ -197,7 +197,7 @@
       display: flex;
       align-items: center;
       margin: 20px 0 36px;
-      background: url('https://4dscene.4dage.com/new4dkk/images/tag-icon.png') no-repeat right bottom;
+      background: url('~@/assets/images/refactor/mall/tag-icon.png') no-repeat right bottom;
       img{
         vertical-align: middle;
         margin-right: 20px;

+ 0 - 133
pc/src/page/manage/style.scss

@@ -1,139 +1,6 @@
 .manage-layout{
   $font-color:#2d2d2d;
   $theme-color:#1fe4dc;
-  .manage-bg{
-    background: url(https://4dscene.4dage.com/new4dkk/images/person-bg.png) no-repeat top center;
-    background-size: cover;
-    .manage-con{
-      padding: 30px 0;
-      .info{
-        color: #cfcfcf;
-        width: 730px;
-        display: block;
-        position: relative;
-        .card-img{
-          font-size: 0;
-          display: inline-block;
-          cursor: pointer;
-          background-position: center;
-          background-size: auto 100%;
-        }
-        .avatar{
-          width: 64px;
-          height: 64px;
-          flex-shrink: 0;
-          margin-right: 14px;
-          background-repeat: no-repeat;
-          background-size: cover;
-        }
-        .member{
-          display:flex;
-          flex-direction: column;
-          justify-content: space-between;
-          height: 64px;
-          padding: 2px 0;
-          p{
-            line-height: 1;
-            color: #cfcfcf;
-            font-size: 16px;
-            &:first-child{
-              font-size: 28px;
-              color: #fff;
-            }
-            .child-name{
-              display: inline-block;
-              position: relative;
-              cursor: pointer;
-              min-width: 120px;
-              ul{
-                background-color: #fff;
-                position: absolute;
-                width: calc(100% + 32px);
-                top: 22px;
-                left: -5px;
-                color: #969696;
-                max-height: 0;
-                overflow: hidden;
-                transition:all 0.3s ease;
-                li{
-                  line-height: 28px;
-                  padding: 0 5px;
-                  &:hover{
-                    background: $theme-color;
-                    color: #2d2d2d;
-                  }
-                }
-              }
-            }
-            .tab-active{
-              ul{
-                max-height: 120px;
-                overflow: auto;
-              }
-            }
-            .sanjiao{
-              display: inline-block;
-              position: relative;
-              &::before{
-                content: '';
-                border: 8px solid transparent;
-                border-top-color: #cfcfcf;
-                border-left-width: 5px;
-                border-right-width: 5px;
-                position: absolute;
-                right: -20px;
-                top: 4px;
-                z-index: 1;
-              }
-            }
-          }
-          
-        }
-        
-      }
-      .open-btn{
-        color: #010101;
-        line-height: 40px;
-        height: 40px;
-        margin-top: 20px;
-        width: 170px;
-        font-size: 16px;
-        text-align: center;
-        background-color: #1fe4dc;
-        cursor: pointer;
-      }
-      .deviceLogin{
-        display: block;
-      }
-      .capacity{
-        margin-top: 10px;
-        width: 680px;
-        .c-line{
-          width: 100%;
-          margin: 15px 0;
-          height: 8px;
-          background-color: #ccc;
-          .active{
-            background-color: $theme-color;
-            height: 100%;
-          }
-        }
-        .c-dec{
-          font-size: 16px;
-          color: #cfcfcf;
-        }
-        .c-detail{
-          color: #fff;
-          display: flex;
-          justify-content: space-between;
-          span{
-            font-size: 14px;
-            color: #cfcfcf;
-          }
-        }
-      }
-    }
-  }
   .manage-body{
     min-height: 676px;
     padding: 80px 0;

+ 10 - 4
pc/src/page/manage/temp/change.vue

@@ -2,7 +2,7 @@
   <div class="change-layout">
     <div class="change-con">
       <div class="input">
-       +{{info.country==='中国'?'86':info.country}} {{info.userName}}
+       <span v-if="isPhone">+{{info.country==='中国'?'86':info.country}}</span> {{info.userName}}
       </div>
       <div class="input yanzhengma">
         <input autocomplete="off" v-model="code" type="text" :placeholder="langModify.codep">
@@ -34,8 +34,12 @@ export default {
       langModify: state => state.language.home.manage.modify,
       langToast: state => state.language.home.toast,
       language: state => state.language.current,
-      info: state => state.user.info
-    })
+      info: state => state.user.info,
+      isInternational: state => state.isInternational
+    }),
+    isPhone () {
+      return this.info.userName.indexOf('@') === -1
+    }
   },
   data () {
     return {
@@ -52,7 +56,9 @@ export default {
       let {userName, country} = this.info
       let res = await this.$store.dispatch('getAuthCode', {
         phone: userName,
-        code: country === '中国' ? 86 : country
+        code: country === '中国' ? 86 : country,
+        email: userName,
+        qudao: this.isPhone ? '' : 'email'
       })
       if (res) {
         this.interl && clearInterval(this.interl)

+ 1 - 1
pc/src/page/manage/temp/scene.vue

@@ -817,7 +817,7 @@ $font-color: #2d2d2d;
   }
 
   .scene-nothing {
-    width: 75%;
+    // width: 75%;
     padding: 42px 0 210px 0;
     text-align: center;
     img {

+ 11 - 2
pc/src/page/news/index.vue

@@ -40,8 +40,8 @@
 import 'swiper/css/swiper.css'
 
 import { Swiper, SwiperSlide } from 'vue-awesome-swiper'
-import { News, EnNews } from '@/../../common/data/newsData'
 import { mapState } from 'vuex'
+import http from "@/util/http";
 export default {
   data () {
     return {
@@ -92,7 +92,7 @@ export default {
     SwiperSlide
   },
   mounted () {
-    this.newsData = this.language === 'zh' ? News : EnNews
+    this.loadNews()
   },
   methods: {
     slide(num) {
@@ -105,6 +105,15 @@ export default {
     },
     handleClickSlide (index) {
       window.open(this.banners[index].link)
+    },
+    loadNews () {
+      let urlMap = {
+        zh: '../www/newsList/news.json',
+        en: '../www/newsList/news-en.json'
+      }
+      http.get(`${urlMap[this.language]}`).then(res => {
+        this.newsData = res.data
+      })
     }
   }
 }

+ 0 - 266
pc/src/page/purchase/index.vue

@@ -1,266 +0,0 @@
-<template>
-  <div class="purchase-layout container">
-    <div class="plate01">
-      <div class="main-detail">
-        <browse
-          :idata='browdata'
-          :iactive='browactive'
-          :floder="'probrowse'"
-          class="product-img"
-         />
-        <div class="layout">
-          <img class="pro-logo" :src="language==='en'?`${$cdn}images/pro-logo-en.png`:`${$cdn}images/pro-logo.png`" alt="">
-          <p class="sub b-label" v-html="langPurchase.dec"></p>
-          <p class="price">
-             {{langPurchase.price}}
-            <span>{{langPurchase.yushou}}</span>
-          </p>
-          <template>
-            <p class="title first-title">{{langPurchase.color.key}}</p>
-            <div class="detail-box">
-              <i class="iconfont icon-yuandian">{{langPurchase.color.val}}</i>
-            </div>
-            <p class="title">{{langPurchase.service.key}}</p>
-            <div class="detail-box sceond-box">
-              <i class="iconfont icon-yuandian">{{langPurchase.service.type}}</i>
-              <!-- <span @click="scrollTo('rlgz')">{{langPurchase.why}}</span> -->
-              <ul>
-                <li v-for="(item, i) in langPurchase.service.val" :key="i">{{item}}</li>
-              </ul>
-            </div>
-            <p class="title ">{{langPurchase.gift.peijian}}</p>
-            <div class="detail-box zhijia" :style="{padding:language==='en'?'10px 2.5% 10px 3.5%':'10px 3.5%'}" :class="{'no-active':!selectParts}" @click="selectParts=!selectParts">
-              <img :src="`${$cdn}images/tag-icon.png`" class="t-click" alt>
-              <img :src="`${$cdn}images/zhijia.png`" alt>
-              <div>
-                <p>{{langPurchase.gift.val}}</p>
-                <p>{{langPurchase.gift.price}}</p>
-              </div>
-            </div>
-            <p class="title peijian">
-              <img :src="`${$cdn}images/perjian-warn.png`" alt="">
-              <span @click="$router.push({name:'purchasezhijia'})">{{langPurchase.gift.key}}</span>
-            </p>
-          </template>
-          <template v-if="language!=='en'">
-            <p class="title" style="margin-top:20px;">{{langPurchase.count.key}}</p>
-            <spinner class="spinner" @count="handleSpinner"/>
-          </template>
-          <div>
-            <div class="btns">
-              <div v-if="language!=='en'" class="button add-cart" @click="addcart">
-                <vcenter>
-                  <span>加入购物车</span>
-                </vcenter>
-              </div>
-              <div class="button"  @click="pay">
-                <vcenter>
-                  <span>{{langPurchase.buy}}</span>
-                </vcenter>
-              </div>
-            </div>
-            <div class="btn-dec" >{{langPurchase.tiaokuan}}</div>
-          </div>
-        </div>
-      </div>
-    </div>
-    <div class="plate02" >
-      <div class="b-title">{{langPurchase.guige.name}}</div>
-      <div class="params-img-con">
-        <img class="params-img" :src="`${$cdn}images/product_img_content_6_w.png`" alt="">
-      </div>
-      <div class="params-body">
-        <div class="params-con">
-          <div class="params-item" v-for="(item,index) in langPurchase.guige.arr" :key="index">
-            <div class="p-l">{{item.name}}</div>
-            <div class="p-r">
-              <div v-html="sub" v-for="(sub,i) in item.val" :key="i"></div>
-            </div>
-          </div>
-        </div>
-        <div class="sub">
-          <p>{{langPurchase.guige.dec}}</p>
-        </div>
-      </div>
-    </div>
-    <!-- <div class="plate03" ref="rlgz">
-      <div class="p03">
-        <div class="b-title">{{langPurchase.pricename}}</div>
-        <priceTable/>
-        <ul class="qa-con">
-          <li v-for="(item,i) in langPurchase.pricetxt" :key="i">
-            <p>{{item.q}}</p>
-            <p v-html="item.a"></p>
-          </li>
-          <p class="promise">{{langPurchase.notice}}</p>
-        </ul>
-        <p class="peijian" @click="$router.push({name:'pricedetail'})">
-          <img :src="`${$cdn}images/perjian-warn.png`" alt="">
-          <span>{{langPurchase.view}}</span>
-        </p>
-      </div>
-    </div> -->
-  </div>
-</template>
-
-<script>
-import { mapState } from 'vuex'
-import spinner from '@/components/spinner'
-import vcenter from '@/components/vcenter'
-import browse from '@/components/browse'
-import priceTable from '@/components/priceTable'
-import { getPosition } from '@/util'
-
-export default {
-  components: {
-    spinner,
-    vcenter,
-    browse,
-    priceTable
-  },
-  computed: {
-    ...mapState({
-      isLogin: state => state.user.name,
-      langPurchase: state => state.language.home.purchase,
-      language: state => state.language.current,
-      langToast: state => state.language.home.toast,
-      token: state => state.user.token,
-      cart: state => JSON.parse(state.user.cart)
-    })
-  },
-  data () {
-    let detail = {
-      left: [
-        '数据永久存储',
-        '高速上传计算队列',
-        '场景分享、热点编辑、隐私加密'
-      ],
-      right: ['多种个性化功能', '附送30G终身容量']
-    }
-    let guige = [
-      {
-        name: '容量和内存',
-        val: ['LPDDR4X双通道', '6G内存', 'UFS 2.1', '64GB 机身存储']
-      },
-      {
-        name: '机身尺寸和重量',
-        val: ['高度 = 153.3m', '宽度 = 74.5mm', '厚度 = 7.9mm', '重量 = 170g']
-      },
-      {
-        name: '处理器平台',
-        val: [
-          'Qualcomm® 骁龙™ 845 处理器',
-          '10nm 先进制程',
-          '单核主频可达 2.8GHz',
-          'Adreno™ 630 图形处理器,主频可达 700MHz',
-          '配备人工智能引擎(AI Engine)'
-        ]
-      },
-      {
-        name: '网络',
-        val: ['支持 VoLTE 高质量宽带', '支持三载波聚', '支持 LTE B41 4x4 MIMO']
-      },
-      {
-        name: '屏幕',
-        val: [
-          '6.17 英寸 In-Cell 全高清显示屏',
-          '2242 x 1080 分辨率,403 ppi',
-          '康宁®第三代大猩猩®玻璃'
-        ]
-      }
-    ]
-    let browdata = [
-      {
-        small: 'small-0',
-        big: 'big-0',
-        video: true
-      }, {
-        small: 'small-1',
-        big: 'big-1'
-      },
-      {
-        small: 'small-2',
-        big: 'big-2'
-      },
-      {
-        small: 'small-3',
-        big: 'big-3'
-      }]
-    let browactive = browdata[0]
-    return {
-      detail,
-      guige,
-      browactive,
-      browdata,
-      count: 1,
-      selectParts: true
-    }
-  },
-  methods: {
-    handleSpinner (data) {
-      this.count = data
-    },
-    async addcart () {
-      if (!this.token) {
-        return this.$bus.$emit('showAside')
-      }
-      let zhijia = {
-        goodsId: 7,
-        goodsCount: this.count,
-        skuSn: 'U15604134406280073'
-      }
-      let params = {
-        goodsId: 4,
-        goodsCount: this.count,
-        skuSn: 'U15609161635760015'
-      }
-      await this.$store.dispatch('addCart', params)
-      this.selectParts && await this.$store.dispatch('addCart', zhijia)
-      this.$toast.show('success', this.langToast['39'])
-    },
-    pay () {
-      if (this.language === 'en') {
-        return window.open('https://www.alibaba.com/product-detail/4DKanKan-Pro-3D-camera-3D-space_62183626283.html?spm=a2700.icbuShop.74.1.66b35b10I4miJd')
-      }
-      let params = {
-        type: 'detail',
-        sku: [{
-          goodsId: 4,
-          goodsCount: this.count,
-          price: 12800,
-          skuSn: 'U15609161635760015'
-        }],
-        fromList: false
-      }
-      let zhijia = {
-        goodsId: 7,
-        goodsCount: this.count,
-        price: 899,
-        skuSn: 'U15604134406280073'
-      }
-      this.selectParts && params.sku.push(zhijia)
-      this.$bus.$emit('showAside', params)
-    },
-    scrollTo (href) {
-      this.interval = null
-      let inter = 0
-      let tag = getPosition(this.$refs[href]).y - 90
-      let speed = Math.ceil(tag / 40)
-      this.interval = setInterval(() => {
-        window.scrollTo(0, inter)
-        inter += speed
-        if ((window.scrollY || window.pageYOffset) >= tag) {
-          clearInterval(this.interval)
-        }
-      })
-    }
-  },
-  beforeDestroy () {
-    clearInterval(this.interval)
-  }
-}
-</script>
-
-<style lang="scss" scoped>
-@import "./style.scss";
-</style>

+ 0 - 402
pc/src/page/purchase/style.scss

@@ -1,402 +0,0 @@
-.purchase-layout {
-  overflow: hidden;
-}
-
-.btns{
-  font-size: 0;
-}
-.btns .button{
-  cursor: pointer;
-  width: 250px;
-  height: 50px;
-  padding-top: 0;
-  padding-bottom: 0;
-  margin-top: 24px;
-  line-height: 50px;
-  color: #414141;
-  text-align: center;
-  font-size: 14px;
-  img{
-    vertical-align: middle;
-  }
-  span{
-    vertical-align: middle;
-    padding-left: 2px;
-  }
-}
-
-.plate01{
-  background: url(https://4dscene.4dage.com/new4dkk/images/purchase_bg.png) #f8f9fc top center no-repeat;
-  padding: 0 0 100px;
-  .main-detail{
-    padding-top: 80px;
-  }
-  .p1-banner{
-    background: #fff;
-    height: 65px;
-    text-align: right;
-    padding-right: 120px;
-    span{
-      display: inline-block;
-      margin-left: 50px;
-      padding-top: 40px;
-      cursor: pointer;
-      font-size: 16px;
-    }
-  }
-  .product-img{
-    display: inline-block;
-  }
-  .layout{
-    width: 540px;
-    margin-top: 20px;
-    margin-left: 8%;
-    display: inline-block;
-    vertical-align: top;
-    .pro-logo{
-      margin-bottom: 8px;
-      max-width: 100%;
-    }
-    .price{
-      margin-top: 20px;
-      font-size: 28px;
-      line-height: 1;
-      color: rgba(0,0,0,.7);
-      font-weight: 700;
-      border-bottom: 1px #e2e2e2 solid;
-      padding-bottom: 20px;
-      span{
-        font-size: 14px;
-        color: #1fe4dc;
-        font-weight: bold;
-      }
-    }
-    .title{
-      font-size: 14px;
-      color: rgba(0,0,0,.7);
-      line-height: 18px;
-      font-weight: 600;
-      margin:6px 0 0;
-    }
-   
-    .first-title{
-      margin-top: 20px;
-    }
-    .detail-box{
-      position: relative;
-      display: inline-block;
-      color: rgba(0,0,0,.45);
-      padding: 5px 0;
-      line-height: 1.5;
-      width: 250px;
-      box-sizing: border-box;
-      text-align: left;
-      &:first-of-type{
-        padding: 6px 0 11px;
-        height: auto;
-        box-sizing: border-box;
-        font-size: 16px;
-        color: #000;
-      }
-      .t-click{
-        position: absolute;
-        right: 0;
-        bottom: 0;
-        margin: 0;
-        width: 16px;
-        height: 16px;
-      }
-      .icon-yuandian::before{
-        margin-right: 6px;
-        font-size: 16px;
-      }
-      .line{
-        display: inline-block;
-        width: 1px;
-        height: 56px;
-        background: #e2e2e2;
-        margin: 5px 39px 0 40px;
-      }
-      ul{
-        display: inline-block;
-        vertical-align: top;
-        &:last-child{
-          margin: 2px 20px 10px;
-        }
-        li{
-          font-size: 14px;
-          line-height: 1.5;
-          position: relative;
-          &::before{
-            content: '';
-            width: 3px;
-            height: 3px;
-            border-radius: 50%;
-            display: inline-block;
-            position: absolute;
-            left: -15px;
-            top: 11px;
-            transform: translateY(-50%);
-            background-color: rgba(0,0,0,.45);
-          }
-        }
-      }
-    }
-    .sceond-box{
-      width: 600px;
-      padding-bottom: 2px;
-      .icon-yuandian{
-        padding: 12px 0 16px;
-        height: auto;
-        -webkit-box-sizing: border-box;
-        box-sizing: border-box;
-        font-size: 16px;
-        color: #000;
-      }
-      span{
-        font-size: 14px;
-        color: #1fe4dc;
-        font-weight: bold;
-        cursor: pointer;
-      }
-    }
-    .zhijia{
-      padding: 10px 3.5%;
-      cursor: pointer;
-      text-align: center;
-      border: 1px solid #70eee9;
-      display: flex;
-      align-items: center;
-      margin: 8px 0 6px;
-      img{
-        vertical-align: middle;
-        margin-right: 20px;
-        height: 60px;
-      }
-      p{
-        text-align: left;
-        vertical-align: middle;
-        font-size: 14px;
-      }
-    }
-    .peijian{
-      vertical-align: middle;
-      color: #486ace;
-      font-weight: normal;
-      img{
-        vertical-align: middle;
-        width: 16px;
-        margin: 0;
-        height: 16px;
-      }
-      span{
-        display: inline-block;
-        cursor: pointer;
-        vertical-align: middle;
-      }
-    }
-    .no-active{
-      border: 1px solid #e3e3e3;
-      background-color: #fff;
-      .t-click{
-        display: none;
-      }
-    }
-    .spinner{
-      margin-top: 10px;
-    }
-    .btns {
-      img{
-        width: 24px;
-      }
-    }
-    .btns .add-cart{
-      background: #e7e7e7;
-      margin-right: 40px;
-    }
-    .btn-dec{
-      margin-top: 12px;
-      font-size: 14px;
-      color: rgba(0,0,0,.7);
-      font-weight: bolder;
-      line-height: 16px;
-    }
-  }
-}
-
-.plate02{
-  width: 100%;
-  margin: 0 auto;
-  padding: 84px 0 0;
-  text-align: center;
-
-  .b-title{
-    font-weight: 700;
-    font-size: 28px;
-    color: rgba(0, 0, 0, 0.7);
-    line-height: 44px;
-    max-width: 300px;
-    margin: 0 auto;
-  }
-  .params-img-con{
-    background: #fff;
-    .params-img{
-      max-width: 1150px;
-      margin: 84px auto;
-    }
-  }
-
-  .params-body{
-    width: 100%;
-    background: #f8f9fc;
-    padding: 84px 0;
-    .params-con{
-      width: 621px;
-      margin: 28px auto 12px;
-      .params-item{
-        width: 100%;
-        border-top: 1px solid #e7e7e7;
-        text-align: left;
-        padding: 28px 0;
-        &:last-of-type{
-         border-bottom: 1px solid #e7e7e7;
-        }
-        &>div{
-          display: inline-block;
-          text-align: left;
-        }
-        .p-l{
-          width: 100px;
-          height: 100%;
-          text-align: left;
-          font-size: 14px;
-          color: rgba(0, 0, 0, 0.7);
-          line-height: 1.5;
-          font-weight: 600;
-          margin: 0;
-          vertical-align: top;
-        }
-        .p-r{
-          width: calc(100% - 120px);
-          height: 100%;
-          text-align: left;
-          div{
-            line-height: 1.5;
-            font-size: 14px;
-            color: rgba(0, 0, 0, 0.45);
-            margin-right: 50px;
-          }
-        }
-      }
-    }
-    .sub{
-      width: 621px;
-      text-indent: 28px;
-      text-align: left;
-      margin: 12px auto 0;
-      font-size: 14px;
-      color: rgba(0, 0, 0, 0.45);
-      line-height: 16px;
-      p:first-of-type{
-        text-indent: 0px;
-      }
-    }
-  }
-  
-}
-
-.plate03{
-  width: 100%;
-  background-color: #fff;
-  .p03{
-    width: 621px;
-    margin: 0 auto;
-    padding: 84px 0;
-    text-align: center;
-    .b-title{
-      font-weight: 700;
-      font-size: 28px;
-      color: rgba(0, 0, 0, 0.7);
-      line-height: 44px;
-      margin: 0 auto 30px;
-    }
-    .price-txt{
-      text-align: justify;
-      font-size: 14px;
-      padding-left: 20px;
-      margin-top: 25px;
-      li{
-        font-size: 14px;
-        color: #666;
-        line-height: 31px;
-        list-style: disc;
-      }
-    }
-    .qa-con{
-      max-width: 1182px;
-      margin: 20px auto 0;
-      li{
-        margin-bottom: 18px;
-        text-align: justify;
-        font-family:'Microsoft Yahei','微软雅黑',Tahoma,Arial,Helvetica,STHeiti;
-        p{
-          font-size: 14px;
-          color: #666;
-          line-height: 31px;
-          &:first-of-type{
-            font-size: 18px;
-            color: #333;
-            font-weight: bold;
-            margin-bottom: 11px;
-          }
-        }
-      }
-      .promise{
-        text-align: left;
-        font-size: 14px;
-        color: #666;
-        line-height: 31px;
-      }
-    }
-    .peijian{
-      vertical-align: middle;
-      color: #486ace;
-      font-weight: normal;
-      text-align: left;
-      font-size: 14px;
-      margin-top: 5px;
-      margin-left: 2px;
-      cursor: pointer;
-      img{
-        vertical-align: middle;
-        width: 16px;
-        height: 16px;
-      }
-      span{
-        display: inline-block;
-        vertical-align: middle;
-      }
-    }
-  }
-}
-
-@media screen and (min-width: 2000px) {
-  .plate01 {
-    .main-detail{
-      max-width: 1920px;
-      margin: 0 auto!important;
-    }
-  }
-}
-
-@media screen and (max-width: 1450px) {
-  .plate01 {
-    .product-img{
-      margin-left: 50px!important;
-
-    }
-    .layout{
-      margin-left:5%;
-    }
-  }
-}

+ 0 - 5
pc/src/router/index.js

@@ -251,11 +251,6 @@ let router = new Router({
       component: resolve => require(['@/page/agent'], resolve)
     },
     {
-      path: '/purchase',
-      name: 'purchase',
-      component: resolve => require(['@/page/purchase'], resolve)
-    },
-    {
       path: '/purchasezhijia',
       name: 'purchasezhijia',
       component: resolve => require(['@/page/purchasezhijia'], resolve)

+ 3 - 0
pc/src/store/index.js

@@ -4,6 +4,9 @@ import ui from './ui'
 import language from './language'
 
 const store = new vuex.Store({
+  state: {
+    isInternational: process.env.IS_INTERNATIONAL,
+  },
   modules: {
     user,
     language,

+ 1 - 1
pc/src/store/language/en/toast.js

@@ -65,7 +65,7 @@ export default{
   '3005': 'Verification code expired.',
   '3006': 'Incorrect verification code.',
   '3007': 'User name already exists.',
-  '3008': 'The mobile number is already registered.',
+  '3008': 'The account is already registered.',
   '3009': 'The password you typed don\'t match.',
   '3010': 'Incorrect user name length. (2-11 characters)',
   '3011': 'Password must contain English case and numbers, 8-16 characters. ',

+ 2 - 3
pc/src/store/user.js

@@ -33,7 +33,6 @@ try {
 
 export default {
   state: {
-    isInternational: process.env.IS_INTERNATIONAL,
     token: token,
     fdkankantoken,
     name: null,
@@ -423,7 +422,6 @@ export default {
     },
     async getAuthCode (context, item) {
       let toastCode = localStorage.getItem('language') === 'en' ? toastEN : toastZH
-
       let {phone, code, type = '', qudao, email} = item
       let areaNum = Number(code) || 86
 
@@ -452,7 +450,8 @@ export default {
         }
         if (qudao === 'email') {
           params = {
-            email
+            email,
+            country: 1
           }
           Api.getEmailAuthCode(params)
           return true

+ 1 - 1
pc/src/util/http.js

@@ -19,7 +19,7 @@ axios.defaults.retryDelay = 1000
 
 // 拦截请求,做登陆,或head处理
 axios.interceptors.request.use(function (config) {
-  if (config.method === 'post') {
+  if (config.method === 'post' && Object.prototype.toString.call(config.data) !== '[object FormData]') {
     config.data = {
       ...config.data
     }