li.shaoyi 7 ay önce
ebeveyn
işleme
8abda7016c

+ 13 - 0
oem/tce-th/config/appconfig.json

@@ -0,0 +1,13 @@
+{
+  "appId": "com.management.tce_th",
+  "appName": "TCE",
+  "version": "1.0.0",
+  "versionCode": "10000",
+  "apiUrl": "http://192.168.30.188:8080",
+  "tokenStore": "session",
+  "locales": [
+    "zh-CN",
+    "en-US",
+    "vi"
+  ]
+}

+ 14 - 0
oem/tce-vi/config/appconfig.json

@@ -0,0 +1,14 @@
+{
+  "appId": "com.management.tce_vi",
+  "appName": "Tce Vietnam",
+  "version": "1.0.0",
+  "versionCode": "10000",
+  "apiUrl": "http://192.168.30.188:8080",
+  "tokenStore": "session",
+  "locales": [
+    "zh-CN",
+    "en-US",
+    "th"
+  ],
+  "tradeChannel": "ws"
+}

+ 2 - 1
public/config/appconfig.json

@@ -4,5 +4,6 @@
   "version": "1.0.0",
   "versionCode": "10000",
   "apiUrl": "http://192.168.30.188:8080",
-  "tokenStore": "session"
+  "tokenStore": "session",
+  "loginVerifyCodeEnabled": false
 }

+ 16 - 2
script/oem.env.json

@@ -1,15 +1,29 @@
 [
     {
         "VUE_APP_ENV": "mobile",
-        "VUE_APP_NAME": "多元世纪",
+        "VUE_APP_NAME": "MTP",
         "VUE_APP_ROOT": "src/packages/mobile/",
         "VUE_APP_TRADE_CHANNEL": "ws"
     },
     {
         "VUE_APP_ENV": "pc",
-        "VUE_APP_NAME": "多元世纪交易中心",
+        "VUE_APP_NAME": "MTP",
         "VUE_APP_ROOT": "src/packages/pc/",
         "VUE_APP_TRADE_CHANNEL": "ws",
         "VUE_APP_API_BETA": "http://103.40.249.126:18280/cfg?key=mtp_20"
+    },
+    {
+        "VUE_APP_ENV": "tce-th@pc",
+        "VUE_APP_NAME": "TCE",
+        "VUE_APP_ROOT": "src/packages/pc/",
+        "VUE_APP_OEM": "oem/tce-th/",
+        "VUE_APP_TRADE_CHANNEL": "ws"
+    },
+    {
+        "VUE_APP_ENV": "tce-vi@pc",
+        "VUE_APP_NAME": "Tce Vietnam",
+        "VUE_APP_ROOT": "src/packages/pc/",
+        "VUE_APP_OEM": "oem/tce-vi/",
+        "VUE_APP_TRADE_CHANNEL": "ws"
     }
 ]

+ 11 - 3
src/packages/pc/components/layouts/header/index.vue

@@ -24,7 +24,7 @@
                 <span class="g-icon--language"></span>
                 <template #dropdown>
                     <el-dropdown-menu>
-                        <template v-for="item in getLanguageList()" :key="item.value">
+                        <template v-for="item in languages" :key="item.value">
                             <el-dropdown-item @click="switchLanguage(item.value)">{{ item.label }}</el-dropdown-item>
                         </template>
                     </el-dropdown-menu>
@@ -41,7 +41,7 @@
                 <template #dropdown>
                     <el-dropdown-menu>
                         <el-dropdown-item :icon="Key" @click="openComponent('Password')">{{ t('operation.modifypwd')
-                            }}</el-dropdown-item>
+                        }}</el-dropdown-item>
                         <el-dropdown-item :icon="SwitchButton" @click="eventBus.$emit('Logout')">{{
                             t('operation.loginout') }}</el-dropdown-item>
                     </el-dropdown-menu>
@@ -53,12 +53,13 @@
 </template>
 
 <script lang="ts" setup>
-import { ref, onMounted, defineAsyncComponent } from 'vue'
+import { ref, onMounted, computed, defineAsyncComponent } from 'vue'
 import { ArrowRight, Key, SwitchButton, ArrowDown } from '@element-plus/icons-vue'
 import { useComponent } from '@/hooks/component'
 import { getLanguageList, Language } from '@/constants/language'
 import { useGlobalStore, useUserStore, i18n } from '@/stores'
 import { localData } from '@/stores/storage'
+import service from '@/services'
 import eventBus from '@/services/bus'
 
 const componentMap = new Map<string, unknown>([
@@ -83,6 +84,13 @@ const exitFullSreen = () => {
     document.exitFullscreen();
 }
 
+// 语言列表
+const languages = computed(() => {
+    const languageList = getLanguageList()
+    const values = service.getConfig('locales')
+    return values.length ? languageList.filter((e) => values.includes(e.value)) : languageList
+})
+
 // 切换语言
 const switchLanguage = (lange: Language) => {
     if (i18n.global.locale !== lange) {

+ 9 - 1
src/packages/pc/views/auth/components/layout/index.vue

@@ -10,7 +10,7 @@
         </el-button>
         <template #dropdown>
           <el-dropdown-menu>
-            <template v-for="item in getLanguageList()" :key="item.value">
+            <template v-for="item in languages" :key="item.value">
               <el-dropdown-item @click="switchLanguage(item.value)">{{ item.label }}</el-dropdown-item>
             </template>
           </el-dropdown-menu>
@@ -38,6 +38,7 @@ import { ArrowDown } from '@element-plus/icons-vue'
 import { getLanguageList, getLanguageLabelByValue, Language } from '@/constants/language'
 import { i18n } from '@/stores'
 import { localData } from '@/stores/storage'
+import service from '@/services'
 
 defineProps({
   title: {
@@ -51,6 +52,13 @@ const t = i18n.global.t
 const meta = document.getElementsByTagName('meta')
 const version = meta.namedItem('revised')?.content ?? '0'
 
+// 语言列表
+const languages = computed(() => {
+  const languageList = getLanguageList()
+  const values = service.getConfig('locales')
+  return values.length ? languageList.filter((e) => values.includes(e.value)) : languageList
+})
+
 // 当前语言
 const languageLabel = computed(() => getLanguageLabelByValue(i18n.global.locale))
 

+ 5 - 2
src/packages/pc/views/auth/login/index.vue

@@ -7,7 +7,7 @@
       <el-form-item prop="password">
         <el-input type="password" :placeholder="t('auth.login.password')" v-model="formData.password" size="large" />
       </el-form-item>
-      <el-form-item prop="verifyCode">
+      <el-form-item prop="verifyCode" v-if="service.getConfig('loginVerifyCodeEnabled')">
         <div class="el-form-item--col">
           <el-input :placeholder="t('auth.login.verifyCode')" v-model="formData.verifyCode" size="large" />
           <el-image :src="verifyImage" style="width: 180px; height: 38px; cursor: pointer;" @click="run()"
@@ -55,6 +55,9 @@ const formRules: FormRules = {
   ],
   password: [
     { required: true, max: 32, message: t('common.pleaseenter'), trigger: 'blur' }
+  ],
+  verifyCode: [
+    { required: true, message: '请输入验证码' }
   ]
 }
 
@@ -72,7 +75,7 @@ const formSubmit = () => {
       try {
         loading.value = true
         await userLogin()
-        
+
         // 路由跳转前需移除 NotFound 路由,否则可能因为动态路由还未加载,路由不存在而被匹配到 NotFound 页面
         dynamicRouter.unregisterRoutes()
 

+ 3 - 3
src/packages/pc/views/investor/manage/user/components/details/view.vue

@@ -20,9 +20,9 @@
             </template>
             <!-- 是否认证 -->
             <template #hasAuth="{ value }">
-                <p>{{ flagEnum.getEnumTypeName(value) }}</p>
+                <p>{{ hasauthEnum.getEnumTypeName(value) }}</p>
                 <p class="g-red" v-if="newData && newData.userAccountDetailVo.hasAuth !== value">
-                    {{ flagEnum.getEnumTypeName(newData.userAccountDetailVo.hasAuth) }}
+                    {{ hasauthEnum.getEnumTypeName(newData.userAccountDetailVo.hasAuth) }}
                 </p>
             </template>
             <!-- 开户方式 -->
@@ -247,7 +247,7 @@ const newData = ref<Investor.InvestorListDetailRsp['newResult']>()
 const oldData = ref<Investor.InvestorListDetailRsp['oldResult']>()
 
 // 是否认证
-const flagEnum = useEnum('flag')
+const hasauthEnum = useEnum('hasauth')
 // 开户方式
 const openmodeEnum = useEnum('openmode')
 // 状态

+ 14 - 6
src/packages/pc/views/report/profitshare/components/details/index.vue

@@ -115,12 +115,20 @@ const showComponent = (code: string, row: Model.ShareAmountQueryRsp) => {
 
 const formatDate = computed(() => {
     const { tradedate, quarter } = props.queryParams
-    const formats: { [key: number]: { parse: string; output: string } } = {
-        4: { parse: 'YYYY', output: 'YYYY年' },
-        6: { parse: 'YYYYMM', output: 'YYYY年MM月' },
-        8: { parse: 'YYYYMMDD', output: 'YYYY年MM月DD日' }
+
+    const formats = new Map([
+        [4, { parse: 'YYYY', output: 'YYYY年' }],
+        [6, { parse: 'YYYYMM', output: 'YYYY年MM月' }],
+        [8, { parse: 'YYYYMMDD', output: 'YYYY年MM月DD日' }],
+    ])
+
+    const formatConfig = formats.get(tradedate.length)
+
+    if (formatConfig) {
+        const { parse, output } = formatConfig
+        return moment(tradedate, parse).format(output) + (quarter ? getQuarterName(quarter) : '')
     }
-    const { parse, output } = formats[tradedate.length]
-    return moment(tradedate, parse).format(output) + (quarter ? getQuarterName(quarter) : '')
+
+    return tradedate
 })
 </script>

+ 9 - 10
src/services/index.ts

@@ -1,3 +1,4 @@
+import { reactive } from 'vue'
 import axios from 'axios'
 import plus from '@/utils/h5plus'
 
@@ -10,11 +11,13 @@ interface AppConfig {
     quoteUrl: string; // WS地址
     tradeUrl: string; // WS地址
     tokenStore: 'session' | 'local'; // token 存储方式
+    locales: string[]; // 多语言配置
+    loginVerifyCodeEnabled: boolean; // 是否启用登录验证码
 }
 
 export default new (class {
     /** 服务配置信息 */
-    private appConfig: AppConfig = {
+    private appConfig = reactive<AppConfig>({
         appId: '',
         appName: '',
         version: '1.0.0',
@@ -23,7 +26,9 @@ export default new (class {
         quoteUrl: '',
         tradeUrl: '',
         tokenStore: 'session',
-    }
+        locales: [],
+        loginVerifyCodeEnabled: true
+    })
 
     /** 服务初始化完成状态 */
     isReady = false
@@ -40,16 +45,10 @@ export default new (class {
         const filePath = './config/appconfig.json'
         if (plus.hasPlus()) {
             const res = await plus.getLocalFileContent(filePath)
-            this.appConfig = {
-                ...this.appConfig,
-                ...JSON.parse(res)
-            }
+            Object.assign(this.appConfig, JSON.parse(res))
         } else {
             const res = await axios(filePath)
-            this.appConfig = {
-                ...this.appConfig,
-                ...res.data
-            }
+            Object.assign(this.appConfig, res.data)
         }
         this.isReady = true
     }

+ 53 - 0
vue.config.js

@@ -1,3 +1,4 @@
+const fs = require('fs')
 const { merge } = require('webpack-merge')
 const path = require('path')
 const moment = require('moment')
@@ -5,6 +6,7 @@ const { defineConfig } = require('@vue/cli-service')
 const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin')
 const CompressionPlugin = require('compression-webpack-plugin')
 const tsImportPluginFactory = require('ts-import-plugin')
+const CopyWebpackPlugin = require('copy-webpack-plugin')
 
 const convertPath = (dir) => path.resolve(__dirname, dir)
 const outputDir = convertPath('app/dist') // 打包输出目录
@@ -84,6 +86,57 @@ module.exports = defineConfig({
       }))
   },
   configureWebpack: (config) => {
+    const oem = process.env.VUE_APP_OEM
+
     config.devtool = 'source-map'
+
+    // const manifestPath = oem ? convertPath(oem + 'manifest.json') : convertPath('public/manifest.json')
+    // const manifestContents = fs.readFileSync(manifestPath, 'utf-8')
+    // const manifest = JSON.parse(manifestContents)
+    // console.log(manifest)
+
+    if (process.env.NODE_ENV === 'production' && !process.argv.includes('test')) {
+      const configPath = oem ? convertPath(oem + 'config/appconfig.json') : convertPath('public/config/appconfig.json')
+      const cfgContents = fs.readFileSync(configPath, 'utf-8')
+      const cfg = JSON.parse(cfgContents)
+
+      // 自动修改版本号
+      if (process.argv.includes('ver')) {
+        const arr = cfg.version.split('.').map(Number)
+        arr[arr.length - 1] += 1 // 版本号自动+1
+
+        // 版本号100进1
+        for (let i = arr.length - 1; i >= 0; i--) {
+          if (i > 0 && arr[i] > 99) {
+            arr[i] = 0
+            if (arr[i - 1] > -1) {
+              arr[i - 1] += 1
+            }
+          }
+        }
+
+        cfg.version = arr.join('.')
+        cfg.versionCode = (Number(cfg.versionCode) + 1).toString()
+      }
+
+      cfg.appName = process.env.VUE_APP_NAME
+      cfg.tradeChannel = process.env.VUE_APP_TRADE_CHANNEL
+
+      fs.writeFileSync(configPath, JSON.stringify(cfg, null, 2))
+      console.log(cfg)
+    }
+
+    if (oem) {
+      // 打包时复制指定目录文件到目标目录
+      config.plugins.push(new CopyWebpackPlugin({
+        patterns: [
+          {
+            from: convertPath(oem), // 指定目录
+            to: outputDir, // 目标目录
+            force: true, // 强制覆盖文件
+          }
+        ]
+      }))
+    }
   }
 })