li.shaoyi 2 rokov pred
rodič
commit
bf84f9a361

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

@@ -1,7 +1,7 @@
 {
   "appName": "贵茶数字化",
-  "version": "1.0.22",
-  "versionCode": "100022",
+  "version": "1.0.23",
+  "versionCode": "100023",
   "apiUrl": "http://192.168.31.204:8080/cfg?key=test_204",
   "tradeChannel": "http",
   "modules": [

+ 1 - 0
src/packages/mobile/components/layouts/login/index.vue

@@ -100,6 +100,7 @@ const formSubmit = () => {
         }).catch((err) => {
           showSliderVerify.value = false
           sliderVerifyStatus.value = false
+          formData.password = ''
           showFailToast(err)
 
           setTimeout(() => {

+ 146 - 35
src/packages/mobile/components/modules/chart/index.vue

@@ -1,63 +1,166 @@
 <template>
     <div ref="chartRef" style="width:375px;height: 300px;background-color: #fff;"></div>
     <ul>
-        <li @click="onChange(0)">日线</li>
-        <li @click="onChange(4)">1分钟线</li>
+        <li @click="changePeriod(4)">1分钟</li>
+        <li @click="changePeriod(5)">5分钟</li>
     </ul>
 </template>
 
 <script lang="ts" setup>
-import { shallowRef } from 'vue'
+import { shallowRef, onMounted } from 'vue'
 import { Chart } from 'hqchart'
+import { ChartCycleType } from '@/constants/chart'
 import { queryHistoryDatas } from '@/services/api/quote'
 import { useFuturesStore } from '@/stores'
 import moment from 'moment'
+import quoteSocket from '@/services/websocket/quote'
+
+const props = defineProps({
+    goodsCode: {
+        type: String,
+        default: 'XAUUSD'
+    }
+})
+
+const { getGoodsQuote, quoteWatch } = useFuturesStore()
+const quote = getGoodsQuote(props.goodsCode)
+const subscribe = quoteSocket.createSubscribe()
 
 const chartRef = shallowRef()
+const chartCycleType = shallowRef(ChartCycleType.Minutes) // 周期类型
+const chartInstance = shallowRef() // 图表实例
+
+subscribe.start(props.goodsCode)
+
+let updateChart: (opt: { symbol: string; data: (string | number)[][]; }) => void = () => ({})
+
+// 切换周期
+const changePeriod = (period: number) => {
+    switch (period) {
+        case 1:
+            chartCycleType.value = ChartCycleType.Week
+            break
+        case 4:
+            chartCycleType.value = ChartCycleType.Minutes
+            break
+        case 5:
+            chartCycleType.value = ChartCycleType.Minutes5
+            break
+    }
+    //chartInstance.value.ChangeSymbol('600000.sh') // 切换股票
+    chartInstance.value.ChangePeriod(period) // 切换周期
+}
+
+
 const historyData = shallowRef<Model.HistoryDatasRsp[]>([])
-const futuresStore = useFuturesStore()
-const quote = futuresStore.getGoodsQuote('AGTD')
 
-const chartInstance = shallowRef() // 图表实例
 
-const onChange = (period: number) => {
-    chartInstance.value.ChangePeriod(period)
+const reqeustHistoryData = () => {
+    // 获取历史行情
+    queryHistoryDatas({
+        data: {
+            cycleType: chartCycleType.value,
+            goodsCode: props.goodsCode,
+            count: 1440,
+        }
+    }).then((res) => {
+        const data = res.data.sort((a, b) => moment(a.ts).valueOf() - moment(b.ts).valueOf())
+        historyData.value = data
+
+        updateChart({
+            symbol: '600000.sh', // 股票代码(必填)
+            data: data.map((e) => ([
+                moment(e.ts).format('YYYYMMDD'),
+                quote.value?.preclose ?? 0,
+                e.o,
+                e.h,
+                e.l,
+                e.c,
+                e.tv,
+                e.tt,
+                moment(e.ts).format('HHmm'),
+            ]))
+        })
+    })
 }
 
-// 获取历史行情
-queryHistoryDatas({
-    data: {
-        cycleType: 1,
-        goodsCode: 'AGTD',
-        count: 1440,
+quoteWatch(props.goodsCode, (q) => {
+    const { last, lasttime } = q
+    if (last && lasttime) {
+
+        const lastIndex = historyData.value.length - 1 // 历史数据最后索引位置
+
+        const oldTime = lastIndex === -1 ? moment(lasttime) : moment(historyData.value[lastIndex].ts) // 历史行情最后时间
+        const diffTime = moment(lasttime).valueOf() - oldTime.valueOf() // 计算时间差
+
+
+
+
+        // 判断时间差是否大于周期时间
+        if (lastIndex === -1 || diffTime > 60 * 1000) {
+            historyData.value.push({
+                o: last,
+                h: last,
+                l: last,
+                c: last,
+                tv: 0,
+                tt: 0,
+                hv: 0,
+                s: 0,
+                ts: lasttime,
+                f: false
+            })
+        } else {
+            // 更新列表中最后一条记录的数据
+            const record = historyData.value[lastIndex]
+            if (record.l > last) {
+                record.l = last // 更新最低价
+            }
+            if (record.h < last) {
+                record.h = last // 更新最高价
+            }
+            record.c = last // 更新收盘价
+        }
+
+
+
+
+
+        // updateChart({
+        //     symbol: '600000.sh',
+        //     data: historyData.value.map((e) => ([
+        //         moment(e.ts).format('YYYYMMDD'),
+        //         quote.value?.preclose ?? 0,
+        //         e.o,
+        //         e.h,
+        //         e.l,
+        //         e.c,
+        //         e.tv,
+        //         e.tt,
+        //         moment(e.ts).format('HHmm'),
+        //     ]))
+        // })
     }
-}).then((res) => {
-    historyData.value = res.data.sort((a, b) => moment(a.ts).valueOf() - moment(b.ts).valueOf())
+})
 
+onMounted(() => {
+    // https://blog.csdn.net/jones2000/article/details/90272733
     const option = {
-        Symbol: '600000.sh', // 必填
+        Symbol: '600000.sh', // 股票代码(必填)
         Type: '历史K线图',
         IsAutoUpdate: false,
-        NetworkFilter: (data: { Name: string; PreventDefault: boolean; }, callback: (opt: { symbol: string; data: (string | number)[][]; }) => void) => {
-            console.log('历史K线图', data)
+        IsApiPeriod: true, // 每次切换周期请求接口数据
+        NetworkFilter: (data: { Name: string; Explain: string; PreventDefault: boolean; }, callback: (opt: { symbol: string; data: (string | number)[][]; }) => void) => {
             data.PreventDefault = true
+            // https://blog.csdn.net/jones2000/article/details/100557649
             switch (data.Name) {
-                // https://blog.csdn.net/jones2000/article/details/100557649
                 case 'KLineChartContainer::ReqeustHistoryMinuteData':
-                    callback({
-                        symbol: '600000.sh', // 必填
-                        data: historyData.value.map((e) => ([
-                            moment(e.ts).format('YYYYMMDD'),
-                            quote.value?.preclose ?? 0,
-                            e.o,
-                            e.h,
-                            e.l,
-                            e.c,
-                            e.tv,
-                            e.tt,
-                            moment(e.ts).format('HHmm'),
-                        ]))
-                    })
+                    updateChart = callback
+                    reqeustHistoryData()
+                    break
+                case 'KLineChartContainer::RequestHistoryData':
+                    updateChart = callback
+                    reqeustHistoryData()
                     break
             }
         },
@@ -78,9 +181,17 @@ queryHistoryDatas({
             // 0=日线 1=周线 2=月线 3=年线 9=季线  [40001-50000) 自定义日线
             // 4=1分钟 5=5分钟 6=15分钟 7=30分钟 8=60分钟 11=120分钟 12=240分钟 [20001-30000) 自定义分钟
             Period: 4,
-            PageSize: 70,
+            PageSize: 50,
             IsShowTooltip: true
         },
+        Frame: [
+            {
+                IsShowRightText: false // 是否显示Y轴右侧刻度
+            },
+            {
+                IsShowRightText: false // 是否显示Y轴右侧刻度
+            }
+        ]
     }
 
     const resource = Chart.JSChart.GetResource()

+ 9 - 9
src/packages/mobile/views/order/position/components/goods/detail/components/transfer/Index.vue

@@ -23,23 +23,22 @@
                 </CellGroup>
                 <CellGroup title="转让信息" inset>
                     <Cell title="当前价" :value="handleNumberValue(quote?.last)" />
-                    <Cell title="转让数量" :value="formData.OrderQty" />
                     <Field name="OrderPrice" :rules="formRules.OrderPrice" label="转让价格">
                         <template #input>
                             <Stepper v-model="formData.OrderPrice" theme="round" button-size="22" :min="0"
                                 :decimal-length="selectedRow.decimalplace" :step="priceStep" :auto-fixed="false" />
                         </template>
                     </Field>
-                    <!-- <Field name="OrderQty" :rules="formRules.OrderQty" label="转让数量">
+                    <Field name="OrderQty" :rules="formRules.OrderQty" label="转让数量">
                         <template #input>
-                            <Stepper v-model="formData.OrderQty" theme="round" button-size="22" :min="0"
-                                :max="maxQty" :auto-fixed="false" integer />
+                            <Stepper v-model="formData.OrderQty" theme="round" button-size="22" :min="0" :max="maxQty"
+                                :auto-fixed="false" integer />
                         </template>
-                    </Field> -->
+                    </Field>
                 </CellGroup>
             </Form>
             <template #footer>
-                <Button block square type="danger" :disabled="!formData.OrderQty" @click="formRef?.submit">转让</Button>
+                <Button block square type="danger" @click="formRef?.submit">转让</Button>
             </template>
         </app-view>
     </app-modal>
@@ -53,7 +52,7 @@ import { getBuyOrSellName, BuyOrSell } from '@/constants/order'
 import { formatDecimal, handleNumberValue, handleRequestBigNumber, handlePriceColor } from '@/filters'
 import { useOrder } from '@/business/trade'
 import { dialog, fullloading } from '@/utils/vant'
-import { useFuturesStore,usePositionStore } from '@/stores'
+import { useFuturesStore, usePositionStore } from '@/stores'
 import { EBuildType, EDelistingType, EListingSelectType, EOrderOperateType, EPriceMode, EValidType } from '@/constants/client'
 
 const props = defineProps({
@@ -78,8 +77,9 @@ const priceStep = computed(() => {
 
 // 可用数量
 const maxQty = computed(() => {
-    const item = positionStore.positionList.find((e) => e.goodsid === props.selectedRow.goodsid && e.buyorsell === props.selectedRow.buyorsell)
-    return Math.min(item?.enableqty ?? 0, props.selectedRow.holderqty)
+    const record = positionStore.positionList.find((e) => e.goodsid === props.selectedRow.goodsid && e.buyorsell === props.selectedRow.buyorsell)
+    const qty = props.selectedRow.holderqty - props.selectedRow.freezeqty
+    return Math.min(record?.enableqty ?? 0, qty)
 })
 
 // 损益

+ 6 - 4
src/packages/pc/components/layouts/header/index.vue

@@ -80,11 +80,13 @@ const userAvatar = computed(() => {
 
 onMounted(() => {
     const reportAgree = localData.getValue('reportAgree')
-    const agreeIndex = reportAgree.findIndex((e) => e.loginId === loginStore.loginId)
+    const userReportAgree = reportAgree.find((e) => e.loginId === loginStore.loginId)
     /// 如果未同意 或者跨天
-    if (!reportAgree[agreeIndex] || diffDays(reportAgree[agreeIndex].agreedTime) > 0) {
-        reportAgree.splice(agreeIndex, 1)
-        localData.setValue('reportAgree', reportAgree)
+    if (!userReportAgree || diffDays(userReportAgree.agreedTime) > 0) {
+        if (userReportAgree) {
+            userReportAgree.isAgree = false
+            localData.setValue('reportAgree', reportAgree)
+        }
         openComponent('report')
     }
     // 监听全屏变化

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

@@ -117,7 +117,7 @@ const formSubmit = () => {
         showSliderVerify.value = false
         sliderVerifyStatus.value = false
         formData.password = ''
-        ElMessage.error(err as string)
+        ElMessage.error('登录失败:' + err as string)
 
         setTimeout(() => {
           showSliderVerify.value = true

+ 14 - 10
src/packages/pc/views/footer/goods/detail/components/transfer/index.vue

@@ -17,26 +17,29 @@
             <el-form-item label="持仓价">
                 <span>{{ formatDecimal(selectedRow.holderprice, selectedRow.decimalplace) }}</span>
             </el-form-item>
-            <el-form-item label="可用数量">
-                <span>{{ maxQty }}</span>
+            <el-form-item label="冻结数量">
+                <span>{{ selectedRow.freezeqty }}</span>
             </el-form-item>
             <el-form-item label="参考损益">
                 <span :class="handlePriceColor(closepl)">{{ formatDecimal(closepl) }}</span>
             </el-form-item>
-            <el-form-item prop="OrderQty" label="转让数量">
-                <div class="g-qty-group">
-                    <el-input-number placeholder="请输入数量" v-model="formData.OrderQty" :precision="0" :max="maxQty" :min="0"
-                        disabled />
-                </div>
+            <el-form-item label="可用数量">
+                <span>{{ maxQty }}</span>
             </el-form-item>
             <el-form-item prop="OrderPrice" label="转让价格">
                 <el-input-number placeholder="请输入价格" v-model="formData.OrderPrice" :step="priceStep"
                     :precision="selectedRow.decimalplace" />
             </el-form-item>
+            <el-form-item prop="OrderQty" label="转让数量">
+                <div class="g-qty-group">
+                    <el-input-number placeholder="请输入数量" v-model="formData.OrderQty" :precision="0" :max="maxQty"
+                        :min="0" />
+                </div>
+            </el-form-item>
         </el-form>
         <template #footer>
             <el-button type="info" @click="onCancel(false)">取消</el-button>
-            <el-button type="primary" :disabled="!formData.OrderQty" @click="onCloseSumit">提交</el-button>
+            <el-button type="primary" @click="onCloseSumit">提交</el-button>
         </template>
     </app-drawer>
 </template>
@@ -64,8 +67,9 @@ const quote = futuresStore.getGoodsQuote(props.selectedRow.goodscode)
 
 // 可用数量
 const maxQty = computed(() => {
-    const item = positionStore.positionList.find((e) => e.goodsid === props.selectedRow.goodsid && e.buyorsell === props.selectedRow.buyorsell)
-    return Math.min(item?.enableqty ?? 0, props.selectedRow.holderqty)
+    const record = positionStore.positionList.find((e) => e.goodsid === props.selectedRow.goodsid && e.buyorsell === props.selectedRow.buyorsell)
+    const qty = props.selectedRow.holderqty - props.selectedRow.freezeqty
+    return Math.min(record?.enableqty ?? 0, qty)
 })
 
 // 价格步长

+ 4 - 3
src/packages/pc/views/footer/goods/detail/index.vue

@@ -22,13 +22,14 @@
 </template>
 
 <script lang="ts" setup>
-import { shallowRef, defineAsyncComponent } from 'vue'
+import { shallowRef, defineAsyncComponent, onUnmounted } from 'vue'
 import { getBuyOrSellName } from '@/constants/order'
 import { useRequest } from '@/hooks/request'
 import { useComponent } from '@/hooks/component'
 import { useLocalPagination } from '@/hooks/pagination'
 import { queryTradeHolderDetail } from '@/services/api/order'
 import AppTable from '@pc/components/base/table/index.vue'
+import eventBus from '@/services/bus'
 
 const componentMap = new Map<string, unknown>([
     ['transfer', defineAsyncComponent(() => import('./components/transfer/index.vue'))],
@@ -62,12 +63,12 @@ const tableColumns = shallowRef<Model.TableColumn[]>([
 ])
 
 // 接收头寸变化通知通知
-//const posChangedNtf = eventBus.$on('PosChangedNtf', () => run())
+const posChangedNtf = eventBus.$on('PosChangedNtf', () => run())
 
 const showComponent = (componentName: string, row: Model.TradeHolderDetailRsp) => {
     selectedRow.value = row
     openComponent(componentName)
 }
 
-//onUnmounted(() => posChangedNtf.cancel())
+onUnmounted(() => posChangedNtf.cancel())
 </script>

+ 3 - 5
src/packages/qxst/views/account/certification/Index.vue

@@ -77,9 +77,9 @@ const isReadonly = computed(() => userESignRecords.value.some((e) => e.templatet
 /// 查询记录
 const { loading: buttonLoading, dataList: userESignRecords, run: getUserESignRecord } = useRequest(queryUserESignRecord, {
     onSuccess: (res) => {
-        const [firstStep] = res.data
-        if (firstStep?.recordstatus === 3) {
-            const { name, idCard, idCardPhoto, idCardPhotoBackURL, mobile, idCardType } = JSON.parse(firstStep.authinfo)
+        const record = res.data.find((e) => e.templatetype === 1 && e.recordstatus === 3)
+        if (record) {
+            const { name, idCard, idCardPhoto, idCardPhotoBackURL, mobile, idCardType } = JSON.parse(record.authinfo || '{}')
             formData.name = name
             formData.idCard = idCard
             formData.idCardPhoto = idCardPhoto
@@ -90,8 +90,6 @@ const { loading: buttonLoading, dataList: userESignRecords, run: getUserESignRec
     },
     onError: (err) => {
         showFailToast(err)
-        /// 报错返回
-        router.back()
     }
 })
 

+ 13 - 3
src/packages/sbyj/views/user/login/Index.vue

@@ -12,7 +12,7 @@
           placeholder="请输入密码" :rules="[{ required: true, message: '请输入密码' }]" autocomplete="off" />
         <Field>
           <template #input>
-            <SliderVerify @statu="slide" style="max-width: 100%;margin: auto;" />
+            <SliderVerify @statu="slide" style="max-width: 100%;margin: auto;" v-if="showSliderVerify" />
           </template>
         </Field>
       </CellGroup>
@@ -34,7 +34,7 @@
         <span @click="routerTo('rules-yszc')">《隐私政策》</span>
       </div>
       <div class="login-footer__version" v-if="plus.getSystemInfo('os') !== 'Web'">
-        <span>v{{ appVersion }}</span>
+        <span>v1.0.{{ appVersion }}</span>
       </div>
     </div>
   </app-statusbar>
@@ -54,9 +54,12 @@ const { routerBack, routerTo } = useNavigation()
 const { formData, userLogin } = useLogin()
 const formRef = shallowRef<FormInstance>()
 const checked = shallowRef(false) // 是否同意协议管理
-const appVersion = plus.getSystemInfo('version') // 应用版本号
+const showSliderVerify = shallowRef(true) // 验证滑块组件重载
 const sliderVerifyStatus = shallowRef(false) // 滑块验证状态
 
+const meta = document.getElementsByTagName('meta')
+const appVersion = meta.namedItem('revised')?.content ?? '0'
+
 // 导航跳转
 const navigationTo = (name: string) => {
   fullloading((hideLoading) => {
@@ -87,7 +90,14 @@ const formSubmit = () => {
           routerBack()
           showSuccessToast('登录成功')
         }).catch((err) => {
+          showSliderVerify.value = false
+          sliderVerifyStatus.value = false
+          formData.password = ''
           showFailToast(err)
+
+          setTimeout(() => {
+            showSliderVerify.value = true
+          }, 0)
         })
       }, '登录中...')
     } else {

+ 6 - 3
src/stores/modules/futures.ts

@@ -15,9 +15,12 @@ import moment from 'moment'
 export const useFuturesStore = defineStore(() => {
     // 行情监听集合
     const quoteWatchMap = new Map<string, { keys: string[]; callback: (value: Partial<Model.QuoteDayRsp>) => void; }>()
-    // 市场ID
+    // 当前市场ID
     const marketIds = shallowRef<number[]>([])
-    // 请求响应成功的回调集合
+
+    // 请求商品列表成功后的回调集合
+    // 由于异步请求原因,页面发起行情订阅时商品数据可能还不存在
+    // 将回调事件收集到合集中,待请求成功后再执行
     const rsponseTask = new Set<() => void>()
 
     const state = reactive({
@@ -46,7 +49,7 @@ export const useFuturesStore = defineStore(() => {
         }
     }
 
-    // 设置市场ID
+    // 设置当前市场ID
     const setMarketId = (...values: number[]) => {
         marketIds.value = values
         const list = marketGoodsList.value