Handy_Cao 4 月之前
父節點
當前提交
eb0703a777

+ 2 - 2
public/locales/en-US.json

@@ -109,7 +109,7 @@
         "setting": "Settings",
         "about": "About Us",
         "modifypwd": "Change password",
-        "usercancel": "Cancel service"
+        "usercancel": "Account deletion"
     },
     "operation": {
         "add": "Add",
@@ -1522,7 +1522,7 @@
             "tips8": "Password reset successfully, please log in again."
         },
         "cancel": {
-            "title": "Cancel service",
+            "title": "Account deletion",
             "confirmcancellation": "Confirm cancellation",
             "submitmessage": "After the account is canceled, it cannot be used in the system again. If there is a balance in the account, manual review is required for cancellation. Are you sure you want to cancel the account?",
             "tips_1": "To ensure the security of your account, the following conditions must be met when submitting a cancellation request:",

+ 18 - 0
src/packages/mobile/components/modules/images/Index.vue

@@ -0,0 +1,18 @@
+<template>
+    <app-view class="g-detail" v-if="record" style="background-color: #fff;margin-top: 5px;">
+        <p v-if="record.pictureurl">
+            <img :src="getFileUrl(record.pictureurl)" alt="" />
+        </p>
+    </app-view>
+</template>
+
+<script lang="ts" setup>
+import { PropType } from 'vue'
+import { getFileUrl } from '@/filters'
+
+defineProps({
+    record: {
+        type: Object as PropType<Model.GoodsRsp>
+    }
+})
+</script>

+ 48 - 0
src/packages/mobile/router/section.ts

@@ -41,6 +41,16 @@ const homeRoutes: RouteRecordRaw[] = [
         path: 'market',
         name: 'home-market',
         component: () => import('@mobile/views/market/list/Index.vue'),
+    },
+    {
+        path: 'mall',
+        name: 'home-mall',
+        component: () => import('@mobile/views/mall/list/Index.vue'),
+    },
+    {
+        path: 'score',
+        name: 'home-score',
+        component: () => import('@mobile/views/score/list/Index.vue'),
     }
 ]
 
@@ -70,6 +80,44 @@ const pageRoutes: RouteRecordRaw[] = [
         ]
     },
     {
+        path: '/mall',
+        component: Page,
+        children: [
+            {
+                path: 'list',
+                name: 'mall-list',
+                component: () => import('@mobile/views/mall/list/Index.vue'),
+                props: {
+                    showBackButton: true
+                }
+            },
+            {
+                path: 'trade',
+                name: 'mall-trade',
+                component: () => import('@mobile/views/mall/trade/Index.vue'),
+            }
+        ]
+    },
+    {
+        path: '/score',
+        component: Page,
+        children: [
+            {
+                path: 'list',
+                name: 'score-list',
+                component: () => import('@mobile/views/score/list/Index.vue'),
+                props: {
+                    showBackButton: true
+                }
+            },
+            {
+                path: 'trade',
+                name: 'score-trade',
+                component: () => import('@mobile/views/score/trade/Index.vue'),
+            }
+        ]
+    },
+    {
         path: '/spot',
         component: Page,
         children: [

+ 10 - 0
src/packages/mobile/views/home/Index.vue

@@ -102,6 +102,16 @@ const addTab = (item: Model.Marketsectionconfignew) => {
       tab.icon = 'g-icon-presale--line'
       tab.activeIcon = 'g-icon-presale--fill'
       break
+    case 53:
+      tab.name = 'home-mall'
+      tab.icon = 'g-icon-quote--line'
+      tab.activeIcon = 'g-icon-quote--fill'
+      break
+    case 54:
+      tab.name = 'home-score'
+      tab.icon = 'g-icon-quote--line'
+      tab.activeIcon = 'g-icon-quote--fill'
+      break
     case 99:
       tab.name = 'home-market'
       tab.icon = 'g-icon-quote--line'

+ 8 - 0
src/packages/mobile/views/home/main/Index.vue

@@ -111,6 +111,14 @@ const iconbar = computed(() => allMarket.value.reduce<{
         item.name = 'ballot-list'
         item.icon = 'g-icon-presale--line'
         break
+      case 53:
+          item.name = 'mall-list'
+          item.icon = 'g-icon-quote--line'
+          break
+      case 54:
+        item.name = 'score-list'
+        item.icon = 'g-icon-quote--line'
+        break
       case 99:
         item.name = 'market-list'
         item.icon = 'g-icon-quote--line'

+ 79 - 0
src/packages/mobile/views/mall/list/Index.vue

@@ -0,0 +1,79 @@
+<template>
+    <app-view>
+        <template #header>
+            <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">
+                <article>
+                    <h3>
+                        <span>{{ item.goodscode }}</span>
+                    </h3>
+                    <section @click="rowClick(item)">
+                        <dl>
+                            <dt>销售价</dt>
+                            <dd :class="item.askColor">
+                                {{ handleNumberValue(formatDecimal(item.ask, item.decimalplace)) }}
+                            </dd>
+                        </dl>
+                    </section>
+                </article>
+            </div>
+        </div>
+    </app-view>
+</template>
+
+<script lang="ts" setup>
+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'
+
+defineProps({
+    showBackButton: {
+        type: Boolean,
+        default: false
+    }
+})
+
+const { router, getQueryStringToNumber } = useNavigation()
+const t = i18n.global.t
+const groupId = getQueryStringToNumber('groupId')
+const futuresStore = useFuturesStore()
+const userStore = useUserStore()
+
+const groupInfo = computed(() => {
+    const list = userStore.getUserDataInfo('goodsgroups')
+    return list.find((e) => e.goodsgroupid === groupId)
+})
+
+const dataList = computed(() => {
+    const list = futuresStore.quotationList
+    return list.filter((e) => groupId ? e.goodsgroupid === groupId : e.trademode === ETradeMode.TRADEMODE_MALL)
+})
+
+const goodsCodes = dataList.value.map((e) => e.goodscode.toUpperCase())
+const subscribe = quoteSocket.createSubscribe()
+
+const rowClick = (row: Model.GoodsQuote) => {
+    router.push({
+        name: 'mall-trade',
+        query: {
+            goodsid: row.goodsid,
+            goodscode: row.goodscode,
+            buyOrSell: BuyOrSell.Buy,
+            buildType: BuildType.Open
+        }
+    })
+}
+
+onActivated(() => subscribe.start(...goodsCodes))
+onUnmounted(() => subscribe.stop())
+</script>
+
+<style lang="less">
+@import './index.less';
+</style>

+ 48 - 0
src/packages/mobile/views/mall/list/index.less

@@ -0,0 +1,48 @@
+.pricing-v2 {
+    display: flex;
+    flex-wrap: wrap;
+    line-height: normal;
+    padding: 5px;
+
+    &-item {
+        width: 50%;
+        padding: 5px;
+
+        article {
+            background-color: #fff;
+            border-radius: 5px;
+            padding: 15px;
+
+            h3 {
+                display: flex;
+                justify-content: space-between;
+                align-items: center;
+                font-size: 14px;
+                font-weight: bold;
+                border-bottom: 1px solid #f1f1f1;
+                padding-bottom: 10px;
+            }
+
+            section {
+                padding-top: 10px;
+
+                dl {
+                    display: flex;
+                    justify-content: space-between;
+                    align-items: center;
+
+                    dt {
+                        font-size: 12px;
+                        color: #71848f;
+                    }
+
+                    dd {
+                        font-size: 16px;
+                        font-weight: bold;
+                        line-height: 1.5;
+                    }
+                }
+            }
+        }
+    }
+}

+ 173 - 0
src/packages/mobile/views/mall/trade/Index.vue

@@ -0,0 +1,173 @@
+<!--  全款商城 - 交易下单 -->
+<template>
+    <app-view class="pricing-trade">
+       <template #header>
+            <app-navbar :title="quote ? `${quote.goodscode}` : $t('quote.pricing.title')" />
+        </template>
+        <Banner :data-list="topBanners" />
+        <div class="pricing-trade__header" v-if="goods">
+            <h1 class="pricing-trade__header-title">{{ goods.goodsname }}</h1>
+             <div class="pricing-trade__header-price">
+                <dl v-if="quote.askColor">
+                    <dt>积分</dt>
+                    <dd>
+                        {{ currencyFormat(quote?.ask, quote?.currencyid, {
+                            fractionDigits: quote?.decimalplace, noneValue: true
+                        }) }}
+                    </dd>
+                </dl> 
+            </div> 
+        </div>
+        <!-- 下单选择区域 -->
+        <Form ref="formRef" class="pricing-trade__form" @submit="onSubmit">
+            <Field name="OrderQty" :label="$t('tss.orderqty')" label-align="top">
+                <template #input>
+                    <div class="g-qty-group">
+                        <RadioGroup v-model="qtyStep" direction="horizontal" @change="onQtyRadioChange">
+                            <Radio v-for="(value, index) in qtyStepList" :key="index" :name="value">{{ value }}
+                            </Radio>
+                        </RadioGroup>
+                        <div class="g-qty-group__stepper">
+                            <Stepper v-model="orderQty" :min="1" :step="qtyStep" integer />
+                        </div>
+                    </div>
+                </template>
+            </Field>
+        </Form>
+       <Tabs v-model:active="active">
+            <template v-for="(item, index) in components.filter(e => e.show === true)" :key="index">
+                <Tab :title="item.title" :name="item.name">
+                    <component :is="item.component" v-bind="{ quote }" />
+                </Tab>
+            </template>
+        </Tabs>
+         <template #footer v-if="goods">
+            <div class="g-form__footer inset">
+                <Button type="danger" block :disabled="orderQty === 0" @click="onSubmit">
+                    {{ $t('operation.buynow') }}
+                </Button>
+            </div>
+        </template>
+    </app-view>
+</template>
+
+<script lang="ts" setup>
+import { shallowRef, onMounted, onUnmounted, computed, defineAsyncComponent } from 'vue'
+import { Form, Field, Button, FormInstance, Radio, RadioGroup, Tab, Tabs } from 'vant'
+import { useFuturesStore, useUserStore, i18n } from '@/stores'
+import { useNavigation } from '@mobile/router/navigation'
+import { currencyFormat } from '@/filters'
+import { useOrder } from '@/business/trade'
+import { fullloading, dialog } from '@/utils/vant'
+import { BuildType, BuyOrSell, PriceMode } from '@/constants/order'
+import eventBus from '@/services/bus'
+import quoteSocket from '@/services/websocket/quote'
+import Stepper from '@mobile/components/base/stepper/index.vue'
+import Banner from '@mobile/components/base/banner/index.vue'
+
+const { getQueryString, getQueryStringToNumber, routerBack } = useNavigation()
+
+const futuresStore = useFuturesStore()
+
+const goodsCode = getQueryString('goodscode') ?? ''
+const goodsid = getQueryStringToNumber('goodsid')
+const quote = futuresStore.getGoodsQuote(goodsCode)
+const goods = futuresStore.getGoods(goodsid)
+
+const { global: { t } } = i18n
+const subscribe = quoteSocket.createSubscribe()
+
+const formRef = shallowRef<FormInstance>()
+const { formData, formSubmit } = useOrder()
+
+const { getSystemParamValue } = useUserStore()
+const system_1012 = getSystemParamValue('1012') ?? '1'
+
+const active = shallowRef('images')
+
+// 数量步长列表
+const qtyStepList = computed(() => {
+    const result = [1, 5, 10]
+    const system_1009 = getSystemParamValue('1009') ?? '1'
+    return result.map((value) => +system_1009 * value)
+})
+const qtyStep = shallowRef(qtyStepList.value[0]) // 数量步长
+const orderQty = shallowRef(qtyStepList.value[0]) // 默认数量
+
+// 计算市价
+const marketPrice = computed(() => {
+    const { ask = 0, bid = 0 } = quote.value ?? {}
+    return formData.BuyOrSell === BuyOrSell.Buy ? ask : bid
+})
+
+// Banner图
+const topBanners = computed(() => {
+    const { bannerurls } = futuresStore.getGoods(goodsCode) ?? {}
+    return bannerurls?.split(',') ?? []
+})
+
+const pictureurl = computed(() => {
+    return goods?.pictureurl ?? ''
+})
+
+// 数量切换
+const onQtyRadioChange = (value: number) => {
+    orderQty.value = value
+}
+
+const components = [
+    {
+        name: 'images',
+        title: t('operation.details'),
+        component: defineAsyncComponent(() => import('@mobile/components/modules/images/Index.vue')),
+        show: pictureurl.value != ''
+    },
+    {
+        name: 'chart',
+        title: t('quote.title'),
+        component: defineAsyncComponent(() => import('@mobile/views/pricing/trade/components/detail/Index.vue')),
+        show: system_1012 != '0'
+    }
+]
+
+// 委托下单
+const onSubmit = () => {
+    const { goodsid, marketid } = quote.value ?? {}
+    formData.BuyOrSell = BuyOrSell.Buy
+    formData.BuildType = BuildType.Open
+    formData.PriceMode = PriceMode.Market
+    formData.MarketMaxSub = 100.0
+    formData.GoodsID = goodsid
+    formData.MarketID = marketid
+    formData.OrderQty = orderQty.value
+    // 市价
+    if (formData.PriceMode === PriceMode.Market) { formData.OrderPrice = marketPrice.value }
+
+    fullloading((hideLoading) => {
+        formSubmit().then(() => {
+            hideLoading()
+            dialog({ message: t('common.submitsuccess'), confirmButtonText: t('operation.confirm') }).then(() => {
+                // 成交通知
+                eventBus.$emit('OrderDealedNtf')
+                // 返回上层试图
+                routerBack()
+            })
+        }).catch((err) => {
+            hideLoading(err, 'fail')
+        })
+    })
+}
+
+onMounted(() => {
+    // 订阅商品行情
+    subscribe.start(quote.value?.goodscode ?? '')
+})
+
+onUnmounted(() => {
+    subscribe.stop()
+})
+</script>
+
+<style lang="less">
+@import './index.less';
+</style>

+ 67 - 0
src/packages/mobile/views/mall/trade/index.less

@@ -0,0 +1,67 @@
+.pricing-trade {
+    &__header {
+        background-color: #fff;
+        padding: 10px;
+        margin-top: 5px;
+
+        &-title {
+            font-size: 16px;
+            font-weight: bold;
+        }
+
+        &-price {
+            display: flex;
+            justify-content: space-between;
+            color: #C19239;
+            margin-top: 10px;
+
+            dl {
+                display: flex;
+                align-items: baseline;
+
+                dt {
+                    font-size: 12px;
+                    margin-right: 5px;
+                    color: #333;
+                }
+
+                dd {
+                    font-size: 22px;
+                    font-weight: 400;
+                }
+            }
+        }
+    }
+
+    &__form {
+        margin-top: 5px;
+
+        .g-qty-group {
+            align-items: initial;
+
+            &__stepper {
+                width: 50%;
+                margin-top: 10px;
+            }
+
+            .van-radio {
+                --van-radio-checked-icon-color: #31AE58;
+
+                width: auto;
+                padding: 5px 10px 5px 0;
+
+                &[aria-checked="true"] .van-radio__label {
+                    background-color: #F0FFE3;
+                }
+
+                &__label {
+                    padding: 4px 10px;
+                }
+
+                &-group {
+                    margin-top: 0;
+                }
+            }
+        }
+    }
+}

+ 79 - 0
src/packages/mobile/views/score/list/Index.vue

@@ -0,0 +1,79 @@
+<template>
+    <app-view>
+        <template #header>
+            <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">
+                <article>
+                    <h3>
+                        <span>{{ item.goodscode }}</span>
+                    </h3>
+                    <section @click="rowClick(item)">
+                        <dl>
+                            <dt>积分</dt>
+                            <dd :class="item.askColor">
+                                {{ handleNumberValue(formatDecimal(item.ask, item.decimalplace)) }}
+                            </dd>
+                        </dl>
+                    </section>
+                </article>
+            </div>
+        </div>
+    </app-view>
+</template>
+
+<script lang="ts" setup>
+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'
+
+defineProps({
+    showBackButton: {
+        type: Boolean,
+        default: false
+    }
+})
+
+const { router, getQueryStringToNumber } = useNavigation()
+const t = i18n.global.t
+const groupId = getQueryStringToNumber('groupId')
+const futuresStore = useFuturesStore()
+const userStore = useUserStore()
+
+const groupInfo = computed(() => {
+    const list = userStore.getUserDataInfo('goodsgroups')
+    return list.find((e) => e.goodsgroupid === groupId)
+})
+
+const dataList = computed(() => {
+    const list = futuresStore.quotationList
+    return list.filter((e) => e.trademode === ETradeMode.TRADEMODE_SCORE)
+})
+
+const goodsCodes = dataList.value.map((e) => e.goodscode.toUpperCase())
+const subscribe = quoteSocket.createSubscribe()
+
+const rowClick = (row: Model.GoodsQuote) => {
+    router.push({
+        name: 'score-trade',
+        query: {
+            goodsid: row.goodsid,
+            goodscode: row.goodscode,
+            buyOrSell: BuyOrSell.Buy,
+            buildType: BuildType.Open
+        }
+    })
+}
+
+onActivated(() => subscribe.start(...goodsCodes))
+onUnmounted(() => subscribe.stop())
+</script>
+
+<style lang="less">
+@import './index.less';
+</style>

+ 48 - 0
src/packages/mobile/views/score/list/index.less

@@ -0,0 +1,48 @@
+.pricing-v2 {
+    display: flex;
+    flex-wrap: wrap;
+    line-height: normal;
+    padding: 5px;
+
+    &-item {
+        width: 50%;
+        padding: 5px;
+
+        article {
+            background-color: #fff;
+            border-radius: 5px;
+            padding: 15px;
+
+            h3 {
+                display: flex;
+                justify-content: space-between;
+                align-items: center;
+                font-size: 14px;
+                font-weight: bold;
+                border-bottom: 1px solid #f1f1f1;
+                padding-bottom: 10px;
+            }
+
+            section {
+                padding-top: 10px;
+
+                dl {
+                    display: flex;
+                    justify-content: space-between;
+                    align-items: center;
+
+                    dt {
+                        font-size: 12px;
+                        color: #71848f;
+                    }
+
+                    dd {
+                        font-size: 16px;
+                        font-weight: bold;
+                        line-height: 1.5;
+                    }
+                }
+            }
+        }
+    }
+}

+ 173 - 0
src/packages/mobile/views/score/trade/Index.vue

@@ -0,0 +1,173 @@
+<!-- 积分商城 - 交易下单 -->
+<template>
+    <app-view class="pricing-trade">
+       <template #header>
+            <app-navbar :title="quote ? `${quote.goodscode}` : $t('quote.pricing.title')" />
+        </template>
+        <Banner :data-list="topBanners" />
+        <div class="pricing-trade__header" v-if="goods">
+            <h1 class="pricing-trade__header-title">{{ goods.goodsname }}</h1>
+             <div class="pricing-trade__header-price">
+                <dl v-if="quote.askColor">
+                    <dt>积分</dt>
+                    <dd>
+                        {{ currencyFormat(quote?.ask, quote?.currencyid, {
+                            fractionDigits: quote?.decimalplace, noneValue: true
+                        }) }}
+                    </dd>
+                </dl> 
+            </div> 
+        </div>
+        <!-- 下单选择区域 -->
+        <Form ref="formRef" class="pricing-trade__form" @submit="onSubmit">
+            <Field name="OrderQty" :label="$t('tss.orderqty')" label-align="top">
+                <template #input>
+                    <div class="g-qty-group">
+                        <RadioGroup v-model="qtyStep" direction="horizontal" @change="onQtyRadioChange">
+                            <Radio v-for="(value, index) in qtyStepList" :key="index" :name="value">{{ value }}
+                            </Radio>
+                        </RadioGroup>
+                        <div class="g-qty-group__stepper">
+                            <Stepper v-model="orderQty" :min="1" :step="qtyStep" integer />
+                        </div>
+                    </div>
+                </template>
+            </Field>
+        </Form>
+       <Tabs v-model:active="active">
+            <template v-for="(item, index) in components.filter(e => e.show === true)" :key="index">
+                <Tab :title="item.title" :name="item.name">
+                    <component :is="item.component" v-bind="{ quote }" />
+                </Tab>
+            </template>
+        </Tabs>
+         <template #footer v-if="goods">
+            <div class="g-form__footer inset">
+                <Button type="danger" block :disabled="orderQty === 0" @click="onSubmit">
+                    {{ $t('operation.buynow') }}
+                </Button>
+            </div>
+        </template>
+    </app-view>
+</template>
+
+<script lang="ts" setup>
+import { shallowRef, onMounted, onUnmounted, computed, defineAsyncComponent } from 'vue'
+import { Form, Field, Button, FormInstance, Radio, RadioGroup, Tab, Tabs } from 'vant'
+import { useFuturesStore, useUserStore, i18n } from '@/stores'
+import { useNavigation } from '@mobile/router/navigation'
+import { currencyFormat } from '@/filters'
+import { useOrder } from '@/business/trade'
+import { fullloading, dialog } from '@/utils/vant'
+import { BuildType, BuyOrSell, PriceMode } from '@/constants/order'
+import eventBus from '@/services/bus'
+import quoteSocket from '@/services/websocket/quote'
+import Stepper from '@mobile/components/base/stepper/index.vue'
+import Banner from '@mobile/components/base/banner/index.vue'
+
+const { getQueryString, getQueryStringToNumber, routerBack } = useNavigation()
+
+const futuresStore = useFuturesStore()
+
+const goodsCode = getQueryString('goodscode') ?? ''
+const goodsid = getQueryStringToNumber('goodsid')
+const quote = futuresStore.getGoodsQuote(goodsCode)
+const goods = futuresStore.getGoods(goodsid)
+
+const { global: { t } } = i18n
+const subscribe = quoteSocket.createSubscribe()
+
+const formRef = shallowRef<FormInstance>()
+const { formData, formSubmit } = useOrder()
+
+const { getSystemParamValue } = useUserStore()
+const system_1012 = getSystemParamValue('1012') ?? '1'
+
+const active = shallowRef('images')
+
+// 数量步长列表
+const qtyStepList = computed(() => {
+    const result = [1, 5, 10]
+    const system_1009 = getSystemParamValue('1009') ?? '1'
+    return result.map((value) => +system_1009 * value)
+})
+const qtyStep = shallowRef(qtyStepList.value[0]) // 数量步长
+const orderQty = shallowRef(qtyStepList.value[0]) // 默认数量
+
+// 计算市价
+const marketPrice = computed(() => {
+    const { ask = 0, bid = 0 } = quote.value ?? {}
+    return formData.BuyOrSell === BuyOrSell.Buy ? ask : bid
+})
+
+// Banner图
+const topBanners = computed(() => {
+    const { bannerurls } = futuresStore.getGoods(goodsCode) ?? {}
+    return bannerurls?.split(',') ?? []
+})
+
+const pictureurl = computed(() => {
+    return goods?.pictureurl ?? ''
+})
+
+// 数量切换
+const onQtyRadioChange = (value: number) => {
+    orderQty.value = value
+}
+
+const components = [
+    {
+        name: 'images',
+        title: t('operation.details'),
+        component: defineAsyncComponent(() => import('@mobile/components/modules/images/Index.vue')),
+        show: pictureurl.value != ''
+    },
+    {
+        name: 'chart',
+        title: t('quote.title'),
+        component: defineAsyncComponent(() => import('@mobile/views/pricing/trade/components/detail/Index.vue')),
+        show: system_1012 != '0'
+    }
+]
+
+// 委托下单
+const onSubmit = () => {
+    const { goodsid, marketid } = quote.value ?? {}
+    formData.BuyOrSell = BuyOrSell.Buy
+    formData.BuildType = BuildType.Open
+    formData.PriceMode = PriceMode.Market
+    formData.MarketMaxSub = 100.0
+    formData.GoodsID = goodsid
+    formData.MarketID = marketid
+    formData.OrderQty = orderQty.value
+    // 市价
+    if (formData.PriceMode === PriceMode.Market) { formData.OrderPrice = marketPrice.value }
+
+    fullloading((hideLoading) => {
+        formSubmit().then(() => {
+            hideLoading()
+            dialog({ message: t('common.submitsuccess'), confirmButtonText: t('operation.confirm') }).then(() => {
+                // 成交通知
+                eventBus.$emit('OrderDealedNtf')
+                // 返回上层试图
+                routerBack()
+            })
+        }).catch((err) => {
+            hideLoading(err, 'fail')
+        })
+    })
+}
+
+onMounted(() => {
+    // 订阅商品行情
+    subscribe.start(quote.value?.goodscode ?? '')
+})
+
+onUnmounted(() => {
+    subscribe.stop()
+})
+</script>
+
+<style lang="less">
+@import './index.less';
+</style>

+ 67 - 0
src/packages/mobile/views/score/trade/index.less

@@ -0,0 +1,67 @@
+.pricing-trade {
+    &__header {
+        background-color: #fff;
+        padding: 10px;
+        margin-top: 5px;
+
+        &-title {
+            font-size: 16px;
+            font-weight: bold;
+        }
+
+        &-price {
+            display: flex;
+            justify-content: space-between;
+            color: #C19239;
+            margin-top: 10px;
+
+            dl {
+                display: flex;
+                align-items: baseline;
+
+                dt {
+                    font-size: 12px;
+                    margin-right: 5px;
+                    color: #333;
+                }
+
+                dd {
+                    font-size: 22px;
+                    font-weight: 400;
+                }
+            }
+        }
+    }
+
+    &__form {
+        margin-top: 5px;
+
+        .g-qty-group {
+            align-items: initial;
+
+            &__stepper {
+                width: 50%;
+                margin-top: 10px;
+            }
+
+            .van-radio {
+                --van-radio-checked-icon-color: #31AE58;
+
+                width: auto;
+                padding: 5px 10px 5px 0;
+
+                &[aria-checked="true"] .van-radio__label {
+                    background-color: #F0FFE3;
+                }
+
+                &__label {
+                    padding: 4px 10px;
+                }
+
+                &-group {
+                    margin-top: 0;
+                }
+            }
+        }
+    }
+}

+ 1 - 1
src/packages/pc/views/market/trade/swap/detail/index.vue

@@ -25,7 +25,7 @@
                         <el-button type="primary" v-if="userStore.userType != 5"
                             @click="openComponent('listing')">{{ t('operation.listing') }}</el-button>
                     </div>
-                </div>
+                </div>h
             </template>
             <Chart class="g-view-detail__container" v-bind="{ goodsCode, makretId: swapQuote?.marketid }" v-if="active" />
             <Order class="g-view-detail__container" v-bind="{ goodsId, quoteGoods: selectedRow }" v-else />