li.shaoyi 1 年之前
父節點
當前提交
53bb02a92b

+ 0 - 2
app/electron.build.js

@@ -12,8 +12,6 @@ const fileContents = fs.readFileSync(convertPath('dist/config/appconfig.json'),
 const appConfig = JSON.parse(fileContents)
 const isBeta = process.argv.includes('demo') // 是否测试版
 
-console.log(isBeta)
-
 // 打包之前清空目录
 exec('rd/s/q ' + outputDir)
 

+ 1 - 1
app/package.json

@@ -1,6 +1,6 @@
 {
   "name": "trading-beta",
-  "version": "1.0.14",
+  "version": "1.0.15",
   "main": "main.js",
   "dependencies": {
     "electron-updater": "^6.1.4",

+ 5 - 3
file/android/fxgl.txt

@@ -134,9 +134,11 @@ http://101.133.236.116:8280/cfg?key=mtp_20
 
 泰商所
 演示盘
-com.muchinfo.tss_demo_v1.0.0.apk
+com.muchinfo.tss_test_v1.0.0.apk
 http://218.17.158.45:25067/cfg?key=test_tss
-
 模拟盘
 com.muchinfo.tss_demo_v1.0.0.apk
-http://13.215.98.185:8280/cfg?key=tjs_demo
+http://13.215.98.185:8280/cfg?key=tjs_demo
+实盘
+com.muchinfo.tss_release_v1.0.0.apk
+http://47.128.190.111:8280/cfg?key=tss_sp

+ 2 - 2
oem/tss/config/appconfig.json

@@ -1,8 +1,8 @@
 {
   "appId": "com.muchinfo.tss",
   "appName": "TCE",
-  "version": "1.0.14",
-  "versionCode": "100014",
+  "version": "1.0.15",
+  "versionCode": "100015",
   "apiUrl": "http://192.168.31.204:8080/cfg?key=test_204",
   "tradeChannel": "ws",
   "showLoginAlert": true,

+ 0 - 2
src/packages/mobile/views/pricing/list/v2/index.less

@@ -1,7 +1,6 @@
 .pricing-v2 {
     display: flex;
     flex-wrap: wrap;
-    color: #e5e5e5;
     line-height: normal;
     padding: 5px;
 
@@ -20,7 +19,6 @@
                 align-items: center;
                 font-size: 14px;
                 font-weight: bold;
-                color: #222;
                 border-bottom: 1px solid #f1f1f1;
                 padding-bottom: 10px;
             }

+ 22 - 39
src/packages/mobile/views/pricing/list/v2/index.vue

@@ -1,7 +1,7 @@
 <template>
     <app-view>
         <template #header>
-            <app-navbar :title="titleName" :show-back-button="showBackButton" />
+            <app-navbar :title="groupInfo ? groupInfo.goodsgroupname : '价格'" :show-back-button="showBackButton" />
         </template>
         <div class="pricing-v2">
             <div class="pricing-v2-item" v-for="(item, index) in dataList" :key="index">
@@ -12,11 +12,15 @@
                     <section @click="rowClick(item)">
                         <dl>
                             <dt>{{ t('quote.ask') }}</dt>
-                            <dd :class="item.bidColor">{{ item.bid }}</dd>
+                            <dd :class="item.bidColor">
+                                {{ handleNumberValue(formatDecimal(item.bid, item.decimalplace)) }}
+                            </dd>
                         </dl>
                         <dl>
                             <dt>{{ t('quote.bid') }}</dt>
-                            <dd :class="item.askColor">{{ item.ask }}</dd>
+                            <dd :class="item.askColor">
+                                {{ handleNumberValue(formatDecimal(item.ask, item.decimalplace)) }}
+                            </dd>
                         </dl>
                     </section>
                 </article>
@@ -26,56 +30,35 @@
 </template>
 
 <script lang="ts" setup>
-import { computed, onUnmounted, onActivated, PropType } from 'vue'
-import { parsePercent, handleNumberValue, formatDecimal } from '@/filters'
-import { useNavigation } from '@mobile/router/navigation'
-import { useFuturesStore, i18n } from '@/stores'
+import { computed, onUnmounted, onActivated } from 'vue'
+import { handleNumberValue, formatDecimal } from '@/filters'
 import { ETradeMode } from '@/constants/client'
+import { useNavigation } from '@mobile/router/navigation'
+import { useUserStore, useFuturesStore, i18n } from '@/stores'
 import quoteSocket from '@/services/websocket/quote'
 import { BuyOrSell, BuildType } from '@/constants/order'
 
-const props = defineProps({
+defineProps({
     showBackButton: {
         type: Boolean,
         default: false
-    },
-    marketSection: {
-        type: Object as PropType<Model.Marketsectionconfignew>
     }
 })
 
-const { global: { t } } = i18n
-const { router, getQueryString } = useNavigation()
+const { router, getQueryStringToNumber } = useNavigation()
+const t = i18n.global.t
+const groupId = getQueryStringToNumber('groupId')
 const futuresStore = useFuturesStore()
+const userStore = useUserStore()
 
-const title = getQueryString('title')
-const titleName = computed(() => title ? decodeURIComponent(title) : props.marketSection?.displayname ?? '订单点价')
+const groupInfo = computed(() => {
+    const list = userStore.getUserDataInfo('goodsgroups')
+    return list.find((e) => e.goodsgroupid === groupId)
+})
 
 const dataList = computed(() => {
-    const list = futuresStore.getGoodsListByTradeMode(ETradeMode.TRADEMODE_MARKETMAKE)
-    return list.map((item) => {
-        const quote = futuresStore.getGoodsQuote(item.goodscode)
-        const { lastColor, openedColor, lowestColor, highestColor, last = 0, presettle = 0, rise = 0, change, amplitude, highest = 0, lowest = 0, opened = 0, ask = 0, bid = 0, bidColor, askColor, decimalplace } = quote.value ?? {}
-        return {
-            ...item,
-            lastColor,
-            openedColor,
-            lowestColor,
-            highestColor,
-            last: handleNumberValue(formatDecimal(last, decimalplace)),
-            rise: handleNumberValue(formatDecimal(rise, decimalplace)),
-            change: parsePercent(change),
-            opened: handleNumberValue(formatDecimal(opened, decimalplace)),
-            presettle: handleNumberValue(formatDecimal(presettle, decimalplace)),
-            lowest: handleNumberValue(formatDecimal(lowest, decimalplace)),
-            highest: handleNumberValue(formatDecimal(highest, decimalplace)),
-            amplitude: parsePercent(amplitude),
-            ask: handleNumberValue(formatDecimal(ask, decimalplace)),
-            bid: handleNumberValue(formatDecimal(bid, decimalplace)),
-            bidColor,
-            askColor
-        }
-    })
+    const list = futuresStore.quotationList
+    return list.filter((e) => groupId ? e.goodsgroupid === groupId : e.trademode === ETradeMode.TRADEMODE_MARKETMAKE)
 })
 
 const goodsCodes = dataList.value.map((e) => e.goodscode.toUpperCase())

+ 21 - 5
src/packages/tss/router/index.ts

@@ -32,15 +32,12 @@ const routes: Array<RouteRecordRaw> = [
       {
         path: '',
         name: 'home',
-        component: () => import('@mobile/views/home/Index.vue'),
-        props: {
-          iosUpdateUrl: 'https://itunes.apple.com/lookup?id=6483808619'
-        },
+        component: () => import('../views/home/index.vue'),
         children: [
           {
             path: '',
             name: 'home-index',
-            component: () => import('@mobile/views/home/main/Index.vue'),
+            component: () => import('../views/home/main/index.vue'),
             meta: {
               ignoreAuth: true,
             },
@@ -50,12 +47,31 @@ const routes: Array<RouteRecordRaw> = [
             name: 'home-mine',
             component: () => import('../views/mine/Index.vue'),
           },
+          {
+            path: 'pricing',
+            name: 'home-pricing-v2',
+            component: () => import('@mobile/views/pricing/list/v2/index.vue'),
+          },
           ...homeRoutes
         ]
       }
     ]
   },
   {
+    path: '/product',
+    component: Page,
+    children: [
+      {
+        path: '',
+        name: 'product',
+        component: () => import('../views/product/index.vue'),
+        props: {
+          showBackButton: true
+        }
+      }
+    ]
+  },
+  {
     path: '/user',
     component: Page,
     children: [

+ 14 - 0
src/packages/tss/views/home/index.less

@@ -0,0 +1,14 @@
+.home {
+    .app-tabbar {
+        background: var(--tabbar-background) !important;
+        margin-top: auto;
+
+        .app-iconfont {
+            color: var(--tabbar-icon);
+
+            &.is-active {
+                color: var(--tabbar-icon-active);
+            }
+        }
+    }
+}

+ 137 - 0
src/packages/tss/views/home/index.vue

@@ -0,0 +1,137 @@
+<template>
+  <div class="home g-flex">
+    <router-view v-slot="{ Component }">
+      <RouterTransition :css="cssTransition">
+        <!-- 缓存所有组件 -->
+        <keep-alive>
+          <component class="g-flex__body" :is="Component" v-bind="{ marketSection }" />
+        </keep-alive>
+      </RouterTransition>
+    </router-view>
+    <app-tabbar :data-list="tabList" :data-index="currentTab" @click="onTabClick" />
+    <app-updater :ios-update-url="iosUpdateUrl" />
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { shallowRef, nextTick, watch, onMounted, onActivated, computed } from 'vue'
+import { Tabbar } from '@mobile/components/base/tabbar/types'
+import { useNavigation } from '@mobile/router/navigation'
+import { useMarketSection } from '@/business/market'
+import { useLoginStore, useFuturesStore } from '@/stores'
+import { i18n } from '@/stores'
+import { displayname } from "@/filters"
+import AppTabbar from '@mobile/components/base/tabbar/index.vue'
+import AppUpdater from '@mobile/components/base/updater/index.vue'
+import RouterTransition from '@mobile/components/base/router-transition/index.vue'
+
+const { global: { t } } = i18n
+
+defineProps({
+  iosUpdateUrl: String
+})
+
+const { route, routerTo } = useNavigation()
+const loginStore = useLoginStore()
+const futuresStore = useFuturesStore()
+const cssTransition = shallowRef(true) // 是否使用css动画
+const currentTab = shallowRef(0)
+const marketSection = shallowRef() // 当前选中的板块
+
+const tabIndex = computed(() => {
+  const value = window.sessionStorage.getItem('currentTab')
+  if (value) {
+    const parsedValue = JSON.parse(value)
+    if (parsedValue.name === route.name) {
+      return parsedValue.index
+    }
+  }
+  return tabList.value.findIndex((e) => e.name === route.name)
+})
+
+// 获取市场板块
+const { allMarket } = useMarketSection(() => {
+  const tab = tabList.value[tabIndex.value]
+  marketSection.value = tab?.params
+  currentTab.value = tabIndex.value
+})
+
+// 导航标签列表
+const tabList = computed(() => {
+  const result: Tabbar[] = [
+    {
+      name: 'home-index',
+      label: t('tabbar.home'),
+      icon: 'g-icon-home--line',
+      activeIcon: 'g-icon-home--fill',
+    },
+    {
+      name: 'home-mine',
+      label: t('tabbar.mine'),
+      icon: 'g-icon-mine--line',
+      activeIcon: 'g-icon-mine--fill',
+    }
+  ]
+  if (loginStore.token) {
+    const market = allMarket.value.find((e) => e.marketids === '10101')
+    if (market) {
+      result.splice(1, 0, {
+        name: 'home-pricing-v2',
+        label: displayname(market),
+        icon: 'g-icon-pricing--line',
+        activeIcon: 'g-icon-pricing--fill',
+      })
+    }
+  } else {
+    result.splice(1, 0, {
+      name: '#',
+      label: t('tabbar.trade'),
+      icon: 'g-icon-quote--line',
+      activeIcon: 'g-icon-quote--fill',
+    })
+  }
+  return result
+})
+
+const onTabClick = (index: number) => {
+  const { name, params } = tabList.value[index]
+  marketSection.value = params
+  if (index === 0 || loginStore.token) {
+    // 缓存当前选中的标签位置,防止 F5 刷新页面后无法定位到当前标签
+    window.sessionStorage.setItem('currentTab', JSON.stringify({
+      index,
+      name
+    }))
+    currentTab.value = index
+    routerTo(name, true)
+  } else {
+    window.sessionStorage.removeItem('currentTab')
+    routerTo('user-login')
+  }
+}
+
+watch(() => route.name, () => {
+  if (tabIndex.value > -1) {
+    cssTransition.value = false
+    currentTab.value = tabIndex.value
+  }
+  nextTick(() => {
+    cssTransition.value = true
+  })
+})
+
+onMounted(() => {
+  currentTab.value = tabIndex.value
+})
+
+onActivated(() => {
+  // 页面显示时刷新盘面(待优化)
+  if (loginStore.token && futuresStore.goodsList.length) {
+    futuresStore.getQuoteDay()
+  }
+})
+</script>
+
+<style lang="less">
+@import './index.less';
+</style>

+ 134 - 0
src/packages/tss/views/home/main/index.less

@@ -0,0 +1,134 @@
+@import '@mobile/assets/themes/base/mixin.less';
+
+.home-main {
+    &__header {
+        background: linear-gradient(var(--navbar-background), var(--navbar-background) 60%, transparent 60%);
+        padding: 10px;
+    }
+
+    &__iconbar {
+        ul {
+            display: flex;
+            flex-wrap: wrap;
+            padding-top: 12px;
+
+            &:last-child {
+                padding-bottom: 12px;
+            }
+
+            li {
+                display: flex;
+                flex-direction: column;
+                align-items: center;
+                width: calc(~'100% / 4');
+                text-align: center;
+
+                .g-icon {
+                    width: 36px;
+                    height: 36px;
+                    font-size: 22px;
+                    color: #fff;
+                    background-color: #000;
+                    border-radius: 50%;
+                    margin-bottom: 4px;
+
+                    &-listing--line {
+                        background-color: #4e9ddb;
+                    }
+
+                    &-transfer--line {
+                        background-color: #42739b;
+                    }
+
+                    &-presale--line {
+                        background-color: #8c8b94;
+                    }
+
+                    &-pickup--line {
+                        background-color: #72c990;
+                    }
+
+                    &-spot--line {
+                        background-color: #9d6969;
+                    }
+
+                    &-pricing--line {
+                        background-color: #8272c9;
+                    }
+
+                    &-swap--line {
+                        background-color: #ebc413;
+                    }
+
+                    &-pricing--line {
+                        background-color: #df4343;
+                    }
+                }
+            }
+        }
+    }
+
+    &__titlebar {
+        .van-cell__title {
+            font-size: 16px;
+            font-weight: bold;
+        }
+
+        .van-cell__value {
+            flex: none;
+            color: #666;
+        }
+    }
+
+    &__market {
+        .scrollbar {
+            width: 100%;
+            padding: 10px;
+        }
+
+        .van-swipe {
+            height: 32px;
+            background-color: #f6f6f6;
+
+            ul {
+                display: flex;
+                justify-content: space-around;
+                align-items: center;
+                height: inherit;
+                font-size: 13px;
+            }
+        }
+    }
+
+    &__news {
+        .article {
+            &-item {
+                .van-cell__title {
+                    span {
+                        .mixin-text-overflow()
+                    }
+                }
+
+                .van-cell__value {
+                    flex: initial;
+                    font-size: 12px;
+                    margin-left: 24px;
+                }
+            }
+        }
+    }
+
+    &__group {
+        ul {
+            display: flex;
+            flex-wrap: wrap;
+
+            li {
+                width: 50%;
+                font-size: 13px;
+                text-align: center;
+                padding: 10px;
+            }
+        }
+    }
+}

+ 97 - 0
src/packages/tss/views/home/main/index.vue

@@ -0,0 +1,97 @@
+<template>
+  <app-view class="home-main">
+    <template #header>
+      <app-navbar :title="globalStore.getSystemInfo('appName')" :show-back-button="false" />
+    </template>
+    <Banner :data-list="topBanners" />
+    <PullRefresh class="home-main__container" v-model="refreshing" @refresh="onRefresh">
+      <app-block>
+        <Cell :value="$t('common.more')" :to="{ name: 'notice-list' }" icon="volume" is-link>
+          <template #title>
+            <Badge :offset="[10, 8]" :dot="noticeStore.unreadList.length > 0">{{ $t('routes.notice') }}</Badge>
+          </template>
+        </Cell>
+      </app-block>
+      <app-block class="home-main__iconbar">
+        <ul>
+          <li @click="$router.push({ name: 'product' })">
+            <Iconfont label-direction="bottom" icon="g-icon-swap--line">产品</Iconfont>
+          </li>
+          <li @click="$router.push({ name: 'pricing-list' })">
+            <Iconfont label-direction="bottom" icon="g-icon-spot--line">价格</Iconfont>
+          </li>
+          <li @click="$router.push({ name: 'pricing-trade', query: {} })">
+            <Iconfont label-direction="bottom" icon="g-icon-pickup--line">预定</Iconfont>
+          </li>
+          <li @click="$router.push({ name: 'pricing-trade', query: {} })">
+            <Iconfont label-direction="bottom" icon="g-icon-pricing--line">交收</Iconfont>
+          </li>
+        </ul>
+      </app-block>
+      <app-block class="home-main__news">
+        <CellGroup class="article">
+          <Cell class="home-main__titlebar" :title="$t('routes.news')" :value="$t('common.more')" icon="fire"
+            :to="{ name: 'news-list' }" is-link />
+          <template v-if="!loginStore.token">
+            <template v-for="(item, index) in newsList" :key="index">
+              <Cell class="article-item" :title="item.title" :value="formatDate(item.publishdate, 'MM/DD')"
+                :to="{ name: 'news-detail', query: { id: item.id } }" />
+            </template>
+          </template>
+        </CellGroup>
+      </app-block>
+      <app-block class="home-main__group" v-if="loginStore.token">
+        <ProductList />
+      </app-block>
+    </PullRefresh>
+  </app-view>
+</template>
+
+<script lang="ts" setup>
+import { shallowRef } from 'vue'
+import { Cell, CellGroup, PullRefresh, Badge } from 'vant'
+import { formatDate } from "@/filters"
+import { queryImageConfigs } from "@/services/api/common"
+import { queryNewTitles } from "@/services/api/news"
+import { useLoginStore, useNoticeStore, useGlobalStore } from '@/stores'
+import Banner from '@mobile/components/base/banner/index.vue'
+import Iconfont from '@/components/base/iconfont/index.vue'
+import ProductList from '../../product/list/index.vue'
+
+const noticeStore = useNoticeStore()
+const loginStore = useLoginStore()
+const globalStore = useGlobalStore()
+const refreshing = shallowRef(false) // 是否处于加载中状态
+const topBanners = shallowRef<string[]>([]); // 轮播图列表
+const newsList = shallowRef<Model.NewTitlesRsp[]>([]) // 资讯列表
+
+// 下拉刷新
+const onRefresh = () => {
+  if (!topBanners.value.length) {
+    queryImageConfigs({
+      data: {
+        imageType: 1,
+      }
+    }).then((res) => {
+      topBanners.value = res.data.map((e) => e.imagepath)
+    })
+  }
+  // 市场资讯
+  queryNewTitles({
+    data: {
+      page: 1,
+      pagesize: 10,
+    }
+  }).then((res) => {
+    newsList.value = res.data
+  }).finally(() => {
+    refreshing.value = false
+  })
+}
+
+onRefresh()
+</script>
+
+<style lang="less">
+@import "./index.less";
+</style>

+ 17 - 0
src/packages/tss/views/product/index.less

@@ -0,0 +1,17 @@
+.product {
+    &-list {
+        padding: 10px;
+
+        ul {
+            display: flex;
+            flex-wrap: wrap;
+
+            li {
+                width: 50%;
+                font-size: 13px;
+                text-align: center;
+                padding: 10px;
+            }
+        }
+    }
+}

+ 23 - 0
src/packages/tss/views/product/index.vue

@@ -0,0 +1,23 @@
+<template>
+    <app-view class="product">
+        <template #header>
+            <app-navbar title="产品" :show-back-button="showBackButton" />
+        </template>
+        <ProductList class="product-list" />
+    </app-view>
+</template>
+
+<script lang="ts" setup>
+import ProductList from './list/index.vue'
+
+defineProps({
+    showBackButton: {
+        type: Boolean,
+        default: false
+    }
+})
+</script>
+
+<style lang="less">
+@import "./index.less";
+</style>

+ 33 - 0
src/packages/tss/views/product/list/index.vue

@@ -0,0 +1,33 @@
+<template>
+    <div>
+        <ul>
+            <template v-for="item in goodsGroups" :key="item.goodsgroupid">
+                <li v-if="item.thumurls"
+                    @click="$router.push({ name: 'pricing-list', query: { groupId: item.goodsgroupid } })">
+                    <Image :src="item.thumurls" radius="4" />
+                    <span>{{ item.goodsgroupname }}</span>
+                </li>
+            </template>
+        </ul>
+    </div>
+</template>
+
+<script lang="ts" setup>
+import { computed } from 'vue'
+import { Image } from 'vant'
+import { getFileUrl } from "@/filters"
+import { useUserStore } from '@/stores'
+
+const userStore = useUserStore()
+
+const goodsGroups = computed(() => {
+    const list = userStore.getUserDataInfo('goodsgroups')
+    return list.filter((e) => e.marketid.toString().startsWith('10')).map((e) => {
+        const [firstImg] = e.thumurls.split(',')
+        return {
+            ...e,
+            thumurls: firstImg ? getFileUrl(firstImg) : ''
+        }
+    })
+})
+</script>

+ 1 - 0
src/types/model/account.d.ts

@@ -113,6 +113,7 @@ declare global {
             quoterid: number;//报价商ID[场外期权]
             quotesourcegroupid: number;//所属行情源分组ID[参考行情市场用\通道交易]
             syncgoodsqty: number;//同步合约数[通道交易-投资管理用] - 0表示不限
+            thumurls: string; // 缩略图片(1:1)(逗号分隔)
         }
 
         interface LoginAccount {