li.shaoyi 2 年之前
父节点
当前提交
fa751e7336

+ 1 - 0
.gitignore

@@ -1,5 +1,6 @@
 .DS_Store
 node_modules
+electron
 /dist
 
 

+ 19 - 0
main.js

@@ -0,0 +1,19 @@
+const { app, BrowserWindow, Menu } = require('electron')
+
+const createWindow = () => {
+    Menu.setApplicationMenu(null)
+
+    const win = new BrowserWindow({
+        center: true,
+        minWidth: 1280,
+        minHeight: 800,
+        icon: 'dist/favicon.ico'
+    })
+
+    win.maximize()
+    win.loadFile('dist/index.html')
+}
+
+app.whenReady().then(() => {
+    createWindow()
+})

+ 30 - 3
package.json

@@ -1,7 +1,8 @@
 {
   "name": "muchinfo",
-  "version": "0.1.0",
+  "version": "1.0.0",
   "private": true,
+  "main": "main.js",
   "scripts": {
     "dev:pc": "vue-cli-service serve --mode pc",
     "dev:mobile": "vue-cli-service serve --mode mobile",
@@ -24,7 +25,9 @@
     "build:qdhs@pc": "vue-cli-service build --mode qdhs@pc",
     "build:tc": "vue-cli-service build --mode tc",
     "build:tc@pc": "vue-cli-service build --mode tc@pc",
-    "lint": "vue-cli-service lint"
+    "lint": "vue-cli-service lint",
+    "dev": "electron .",
+    "build": "electron-builder"
   },
   "dependencies": {
     "@tinymce/tinymce-vue": "^5.0.0",
@@ -67,6 +70,7 @@
     "@vue/eslint-config-typescript": "^9.1.0",
     "compression-webpack-plugin": "^10.0.0",
     "copy-webpack-plugin": "^11.0.0",
+    "electron": "^22.0.0",
     "eslint": "^7.32.0",
     "eslint-plugin-vue": "^8.0.3",
     "less": "^4.0.0",
@@ -77,5 +81,28 @@
     "typescript": "~4.5.5",
     "vconsole": "^3.14.6",
     "worker-loader": "^3.0.8"
+  },
+  "build": {
+    "productName": "多元世纪交易中心",
+    "directories": {
+      "output": "electron"
+    },
+    "files":[
+      "dist",
+      "main.js",
+      "!node_modules"
+    ],
+    "nsis": {
+      "oneClick": false,
+      "allowToChangeInstallationDirectory": true
+    },
+    "win": {
+      "icon": "dist/app/icons/1024x1024.png",
+      "target": [
+        {
+          "target": "nsis"
+        }
+      ]
+    }
   }
-}
+}

+ 1 - 0
src/constants/funcode.ts

@@ -54,6 +54,7 @@ export enum FunCode {
     OrderDealedNtf = 131081, // 委托单成交通知
     MarketStatusChangeNtf = 131089, // 市场状态变更通知
     PosChangedNtf = 131075, // 头寸变化通知
+    RiskCutNtf = 131119, // 斩仓通知
 
     // 行情内容
     QuoteBeat = 0x12, // 心跳

+ 6 - 1
src/packages/pc/App.vue

@@ -22,9 +22,14 @@ const router = useRouter()
 eventBus.$on('LogoutNotify', (msg) => {
   userLogout(() => {
     if (msg) {
-      ElMessageBox.alert(msg as string)
+      ElMessageBox.alert(msg as string, '下线通知')
     }
     router.replace({ name: 'login' })
   })
 })
+
+// 接收斩仓通知
+eventBus.$on('RiskCutNtf', (msg) => {
+  ElMessageBox.alert(msg as string, '斩仓通知')
+})
 </script>

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

@@ -14,7 +14,7 @@
       </el-form-item>
       <el-form-item>
         <div class="text-link">
-          <span @click="isRegister = true" v-if="shwoRegister">立即注册</span>
+          <span @click="click" v-if="shwoRegister">立即注册</span>
           <span @click="islogin = false">忘记密码?</span>
         </div>
       </el-form-item>
@@ -63,7 +63,7 @@ const shwoRegister = shallowRef(false)
 const islogin = shallowRef(true)
 const isRegister = shallowRef(false)
 const qrContent = shallowRef('')
-const rloading = shallowRef(true)
+const rloading = shallowRef(false)
 
 const formRules: FormRules = {
   userName: [
@@ -74,6 +74,16 @@ const formRules: FormRules = {
   ]
 }
 
+const click = () => {
+  isRegister.value = true
+  rloading.value = true
+  service.onReady().then((res) => {
+    qrContent.value = res.mobileOpenUrl
+  }).finally(() => {
+    rloading.value = false
+  })
+}
+
 const formSubmit = () => {
   formRef.value?.validate(async (valid) => {
     if (valid) {
@@ -96,11 +106,8 @@ const formSubmit = () => {
   })
 }
 
-service.onReady().then(() => {
-  shwoRegister.value = service.getSystemInfo('shwoRegister')
-  qrContent.value = service.getConfig('mobileOpenUrl')
-}).finally(() => {
-  rloading.value = false
+service.systemInfoAsync.then((res) => {
+  shwoRegister.value = res.shwoRegister
 })
 </script>
 

+ 4 - 4
src/packages/pc/views/footer/goods/position/components/transfer/index.vue

@@ -131,16 +131,16 @@ const onRadioChange = (value: number) => {
 }
 
 onMounted(() => {
-    const { bid, ask, presettle = 0 } = quote.value ?? {}
+    const { bid, ask, last, presettle = 0 } = quote.value ?? {}
     switch (props.selectedRow.buyorsell) {
         case BuyOrSell.Buy:
-            formData.OrderPrice = ask || presettle
+            formData.OrderPrice = ask || last || presettle
             break
         case BuyOrSell.Sell:
-            formData.OrderPrice = bid || presettle
+            formData.OrderPrice = bid || last || presettle
             break
         default:
-            formData.OrderPrice = presettle
+            formData.OrderPrice = last || presettle
     }
     formData.OrderQty = props.selectedRow.enableqty
 })

+ 8 - 4
src/packages/sbyj/views/delivery/components/processing/list/index.vue

@@ -10,8 +10,10 @@
                             {{ item.buyorsell === BuyOrSell.Buy ? '买料: ' : '卖料: ' }}
                             {{ item.goodscode + '/' + item.goodsname }}
                         </h4>
-                        <span v-if="item.buyorsell === BuyOrSell.Buy && item.deliverystatus === 2" style="color: #972929;">确认截止时间: {{ formatDate(item.toconfirmtime) }}</span>
-                        <span v-else-if="item.buyorsell === BuyOrSell.Buy && !item.payedamount" style="color: #972929;">付款截止时间: {{ formatDate(item.topaytime) }}</span>
+                        <span v-if="item.buyorsell === BuyOrSell.Buy && item.deliverystatus === 2"
+                            style="color: #972929;">确认截止时间: {{ formatDate(item.toconfirmtime) }}</span>
+                        <span v-else-if="item.buyorsell === BuyOrSell.Buy && !item.payedamount"
+                            style="color: #972929;">付款截止时间: {{ formatDate(item.topaytime) }}</span>
                     </div>
                 </div>
                 <div class="g-order-list__content">
@@ -120,8 +122,10 @@ const deliveryCommit = (item: Model.MyTradeGoodsDeliveryOfflineRsp) => {
         formData.OperateType = 1 // 操作类型,必填1:客户确认2:客户付款
         fullloading((hideLoading) => {
             deliveryClientOperator({ data: formData }).then(() => {
-                hideLoading('提交成功', 'success')
-                pullRefreshRef.value?.refresh()
+                setTimeout(() => {
+                    hideLoading('提交成功', 'success')
+                    pullRefreshRef.value?.refresh()
+                }, 300)
             }).catch((err) => {
                 hideLoading(err, 'fail')
             })

+ 110 - 0
src/packages/sbyj/views/order/history/index.vue

@@ -0,0 +1,110 @@
+<template>
+    <app-modal direction="right" height="100%" v-model:show="showModal">
+        <app-view>
+            <template #header>
+                <app-navbar title="全部订单" @back="closed" />
+            </template>
+            <app-pull-refresh ref="pullRefreshRef" v-model:loading="loading" v-model:error="error"
+                v-model:pageIndex="pageIndex" :page-count="pageCount" @refresh="run">
+                <div class="g-order-list">
+                    <div class="g-order-list__box" v-for="(item, index) in dataList" :key="index">
+                        <div class="g-order-list__titlebar">
+                            <div class="left">
+                                <h4 :class="item.buyorsell === BuyOrSell.Buy ? 'g-price-up' : 'g-price-down'">
+                                    {{ item.buyorsell === BuyOrSell.Buy ? '买料:' : '卖料:' }}
+                                    {{ item.goodscode + '/' + item.goodsname }}
+                                </h4>
+                            </div>
+                        </div>
+                        <div class="g-order-list__content">
+                            <ul>
+                                <li>
+                                    <span>订单重量</span>
+                                    <span>
+                                        {{ item.holderqty * item.agreeunit }}
+                                        {{ getGoodsUnitName(item.goodunitid) }}
+                                    </span>
+                                </li>
+                                <li>
+                                    <span>可用重量</span>
+                                    <span>
+                                        {{ enableqty(item) + getGoodsUnitName(item.goodunitid) }}
+                                    </span>
+                                </li>
+                                <li>
+                                    <span>订单金额</span>
+                                    <span>{{ item.tradeamount }}</span>
+                                </li>
+                                <li>
+                                    <span>订单价格</span>
+                                    <span>{{ formatDecimal(item.openprice) }}</span>
+                                </li>
+                                <li>
+                                    <span>已付定金</span>
+                                    <span>{{ item.payeddeposit }}</span>
+                                </li>
+                                <li>
+                                    <span>已补定金</span>
+                                    <span>{{ handleNumberValue(item.restockdeposit) }}</span>
+                                </li>
+                                <li>
+                                    <span>订单天数</span>
+                                    <span>{{ item.holddays }}天</span>
+                                </li>
+                                <li>
+                                    <span>滞纳金</span>
+                                    <span>{{ handleNumberValue(item.callatefee) }}</span>
+                                </li>
+                            </ul>
+                        </div>
+                    </div>
+                </div>
+            </app-pull-refresh>
+        </app-view>
+    </app-modal>
+</template>
+
+<script lang="ts" setup>
+import { shallowRef } from 'vue'
+import { formatDecimal, handleNumberValue } from '@/filters'
+import { BuyOrSell } from '@/constants/order'
+import { getGoodsUnitName } from '@/constants/unit'
+import { useRequest } from '@/hooks/request'
+import { queryTradeHolderDetailEx } from '@/services/api/order'
+import AppModal from '@/components/base/modal/index.vue'
+import AppPullRefresh from '@mobile/components/base/pull-refresh/index.vue'
+
+const showModal = shallowRef(true)
+const error = shallowRef(false)
+const dataList = shallowRef<Model.TradeHolderDetailExRsp[]>([])
+
+// 可用重量
+const enableqty = (item: Model.TradeHolderDetailExRsp) => {
+    return (item.holderqty - item.freezeqty) * item.agreeunit
+}
+
+const { loading, pageIndex, pageCount, run } = useRequest(queryTradeHolderDetailEx, {
+    params: {
+        pagesize: 20,
+    },
+    onSuccess: (res) => {
+        if (pageIndex.value === 1) {
+            dataList.value = []
+        }
+        dataList.value.push(...res.data)
+    },
+    onError: () => {
+        error.value = true
+    }
+})
+
+// 关闭弹窗
+const closed = () => {
+    showModal.value = false
+}
+
+// 暴露组件属性给父组件调用
+defineExpose({
+    closed,
+})
+</script>

+ 8 - 1
src/packages/sbyj/views/order/list/index.vue

@@ -1,7 +1,13 @@
 <template>
     <app-view>
         <template #header>
-            <app-navbar title="我的订单" />
+            <app-navbar title="我的订单">
+                <template #right>
+                    <div class="button-more" @click="openComponent('history')">
+                        <span>更多</span>
+                    </div>
+                </template>
+            </app-navbar>
         </template>
         <app-pull-refresh ref="pullRefreshRef" v-model:loading="loading" v-model:error="error" @refresh="getSBYJMyOrders">
             <div class="g-order-list">
@@ -89,6 +95,7 @@ import { useSBYJOrder } from '@/business/order'
 import AppPullRefresh from '@mobile/components/base/pull-refresh/index.vue'
 
 const componentMap = new Map<string, unknown>([
+    ['history', defineAsyncComponent(() => import('../history/index.vue'))], // 更多
     ['detail', defineAsyncComponent(() => import('../detail/index.vue'))], // 详情
     ['supplement', defineAsyncComponent(() => import('./components/supplement-deposit/index.vue'))], // 补充
     ['delivery', defineAsyncComponent(() => import('./components/market-order-delivery/index.vue'))], // 交收

+ 15 - 0
src/services/api/order/index.ts

@@ -211,6 +211,21 @@ export function querySBYJMyOrders(config: RequestConfig<Model.SBYJMyOrderReq> =
 }
 
 /**
+ * 查询我的订单
+ * @param config 
+ * @returns 
+ */
+export function queryTradeHolderDetailEx(config: RequestConfig<Model.TradeHolderDetailExReq> = {}) {
+    return http.commonRequest<Model.TradeHolderDetailExRsp[]>({
+        url: '/sbyj/QueryTradeHolderDetailEx',
+        params: {
+            accountid: getAccountId(),
+            ...config.data
+        },
+    })
+}
+
+/**
  * 查询我的交收(水贝亿爵)
  */
 export function queryMyTradeGoodsDeliveryOfflines(config: RequestConfig<Model.MyTradeGoodsDeliveryOfflineReq> = {}) {

+ 1 - 0
src/services/bus/types.ts

@@ -14,6 +14,7 @@ export enum EventCode {
     OrderDealedNtf, // 成交通知
     OrderRsp, // 委托回应通知
     PosChangedNtf, // 头寸变化通知
+    RiskCutNtf, // 斩仓通知
 }
 
 /**

+ 27 - 16
src/services/index.ts

@@ -2,6 +2,12 @@ import axios from 'axios'
 import plus from '@/utils/h5plus'
 
 export default new (class {
+    constructor() {
+        this.systemInfoAsync = this.getAppConfig()
+    }
+
+    systemInfoAsync
+
     systemInfo = {
         version: '1.0.0',
         versionCode: '100000',
@@ -81,28 +87,33 @@ export default new (class {
         })
     }
 
+    private async getAppConfig(): Promise<typeof this.systemInfo> {
+        const filePath = './config/appconfig.json'
+        if (plus.hasPlus()) {
+            const res = await plus.getLocalFileContent(filePath)
+            return JSON.parse(res)
+        } else {
+            const res = await axios(filePath)
+            return res.data
+        }
+    }
+
     /**
      * 初始化服务配置
      * https://uniapp.dcloud.net.cn/tutorial/app-ios-uiwebview.html
      */
-    private init(): Promise<typeof this.config> {
+    private async init(): Promise<typeof this.config> {
         this.isPending = true
-        return new Promise((resolve, reject) => {
-            const filePath = './config/appconfig.json'
-            const getAppConfig = async () => {
-                if (plus.hasPlus()) {
-                    const res = await plus.getLocalFileContent(filePath)
-                    return JSON.parse(res)
-                } else {
-                    const res = await axios(filePath)
-                    return res.data
-                }
+        await this.systemInfoAsync.then((res) => {
+            this.systemInfo = {
+                ...this.systemInfo,
+                ...res
             }
-            getAppConfig().then((res) => {
-                this.systemInfo = {
-                    ...this.systemInfo,
-                    ...res
-                }
+        }).catch(() => {
+            this.systemInfoAsync = this.getAppConfig()
+        })
+        return new Promise((resolve, reject) => {
+            this.systemInfoAsync.then(() => {
                 // 获取服务接口地址
                 axios(this.systemInfo.apiUrl).then((res) => {
                     this.config = res.data.data

+ 16 - 11
src/services/websocket/trade.ts

@@ -41,15 +41,13 @@ export default new (class {
                     break
                 }
                 case FunCode.MarketStatusChangeNtf: {
-                    if (content) {
-                        const res = new TextDecoder().decode(content)
-                        const { Status } = JSON.parse(res)
-                        // 运行状态 - 0:初始化 1:待开市 2:开市 3:休市 4:手工休市 5:闭市 6:确认行权开始 7:确认行权结束 10:日终处理开始 11:日终处理成功 12:日终处理失败 13基础服务结算开始 14基础服务结算成功 23.资金结算开始 24.资金结算成功 25.资金结算失败 26.系统结算成功 27.系统结算失败 28.盘中处理开始 29.盘中处理成功 30.盘中处理失败 31.资金结算开始(内) 32.资金结算成功(内) 33.资金结算失败(内) 40.签到开始 41.签到成功 42.签到部份成功 43.签到失败 44.签退开始 45.签退成功 46.签退部份成功 47.签退失败 48.对账开始 49.对账成功 50.对账失败 51.清算开始 52.清算成功 53.清算失败 54.清算部分成功 55. 系统结算开始 62.今日免清算
-                        if ([1, 26].includes(Status)) {
-                            //console.log('接收到市场状态变更通知', Status)
-                            // 用户登出通知
-                            eventBus.$emit('LogoutNotify', '系统维护,请稍后重新登录')
-                        }
+                    const res = new TextDecoder().decode(content)
+                    const { Status } = JSON.parse(res) as Proto.MarketStatusChangeNtf
+                    // 运行状态 - 0:初始化 1:待开市 2:开市 3:休市 4:手工休市 5:闭市 6:确认行权开始 7:确认行权结束 10:日终处理开始 11:日终处理成功 12:日终处理失败 13基础服务结算开始 14基础服务结算成功 23.资金结算开始 24.资金结算成功 25.资金结算失败 26.系统结算成功 27.系统结算失败 28.盘中处理开始 29.盘中处理成功 30.盘中处理失败 31.资金结算开始(内) 32.资金结算成功(内) 33.资金结算失败(内) 40.签到开始 41.签到成功 42.签到部份成功 43.签到失败 44.签退开始 45.签退成功 46.签退部份成功 47.签退失败 48.对账开始 49.对账成功 50.对账失败 51.清算开始 52.清算成功 53.清算失败 54.清算部分成功 55. 系统结算开始 62.今日免清算
+                    if ([1, 26].includes(Status)) {
+                        //console.log('接收到市场状态变更通知', Status)
+                        // 用户登出通知
+                        eventBus.$emit('LogoutNotify', '系统维护,请稍后重新登录')
                     }
                     break
                 }
@@ -57,7 +55,7 @@ export default new (class {
                     //console.log('接收到挂牌委托变更广播通知', funCode)
                     timerInterceptor.debounce(() => {
                         // 挂牌委托变更广播通知
-                        eventBus.$emit('ListingOrderChangeNtf');
+                        eventBus.$emit('ListingOrderChangeNtf')
                     }, delay, funCode.toString())
                     break;
                 }
@@ -65,10 +63,17 @@ export default new (class {
                     //console.log('接收到头寸变化通知', funCode)
                     timerInterceptor.debounce(() => {
                         // 头寸变化通知
-                        eventBus.$emit('PosChangedNtf');
+                        eventBus.$emit('PosChangedNtf')
                     }, delay, funCode.toString())
                     break;
                 }
+                case FunCode.RiskCutNtf: {
+                    console.log('接收到斩仓通知', funCode)
+                    const res = new TextDecoder().decode(content)
+                    const riskCutNtf = JSON.parse(res) as Proto.RiskCutNtf
+                    eventBus.$emit('RiskCutNtf', `尊敬的客户,您的风险率过高,请及时采取妥善措施化解风险,包括且不限于订货转让和入金等措施。如风险率达${riskCutNtf.CutRate}及以上,根据风险管理办法规定,系统将自动将您的订货强行转让,直至风险率低于${riskCutNtf.SafeRate}。强行转让造成的损失将由您自行承担,请您知悉。`)
+                    break;
+                }
                 default: {
                     if (funCode) {
                         console.warn('接收到未定义的通知', funCode)

+ 53 - 0
src/types/model/order.d.ts

@@ -1730,4 +1730,57 @@ declare namespace Model {
         /// 确认时间
         confirmtime: string;
     }
+
+    /** 查询我的订单 请求 */
+    interface TradeHolderDetailExReq {
+        accountid?: number; // 资金账户
+        tradedate?: number; // 交易日(yyyyMMdd)
+        goodsid?: number; // 商品ID
+        page?: number; // 页码
+        pagesize?: number; // 每页条数
+    }
+
+    /** 查询我的订单 响应 */
+    interface TradeHolderDetailExRsp {
+        accountid: number; // 账号ID
+        agreeunit: number; // 期货合约乘数
+        buyorsell: number; // 方向 - 0:买 1:卖
+        callatefee: number; // 已计滞纳金
+        closepl: number; // 平仓盈亏
+        cutdepositrate: number; // 斩仓定金率
+        cutprice: number; // 斩仓价格
+        decimalplace: number; // 报价小数位
+        deliveryqty: number; // 交收手数(已交收)
+        depositrate: number; // 订单定金率
+        floatpl: number; // 浮动盈亏
+        freezeqty: number; // 冻结数量
+        goodscode: string; // 期货合约代码(内部)
+        goodsid: number; // 商品ID
+        goodsname: string; // 期货合约名称
+        goodunitid: number; // 报价单位ID
+        handlestatus: number; // 处理状态
+        holddays: number; // 持仓天数
+        holderamount: number; // 持仓金额
+        holderprice: number; // 持仓价格
+        holderqty: number; // 持仓数量
+        latefeealgorithm: number; // 滞纳金收取方式 1:比率 2:固定
+        latefeedays: number; // 滞纳金起计天数
+        latefeevalue: number; // 滞纳金收取值
+        marketid: number; // 市场ID
+        matchaccountid: number; // 对手账号ID
+        matchuserid: number; // 对手用户ID
+        openprice: number; // 建仓价格
+        openqty: number; // 建仓数量
+        payeddeposit: number; // 已付定金
+        payedlatefee: number; // 已付滞纳金
+        promptdepositrate: number; // 提示定金率
+        promptprice: number; // 提示价格
+        refundabledeposit: number; // 可退定金
+        restockdeposit: number; // 补充定金
+        tradeamount: number; // 成交金额
+        tradedate: string; // 交易日(yyyyMMdd)
+        tradeid: string; // 成交单号(101+Unix秒时间戳(10位)+2位(MarketServiceID)+xxxx)
+        tradetime: string; // 交易时间
+        userid: number; // 用户ID
+    }
 }

+ 37 - 0
src/types/proto/notify.d.ts

@@ -0,0 +1,37 @@
+import { IMessageHead } from './proto'
+
+declare global {
+    namespace Proto {
+        /** 公共通知消息头 */
+        interface NotifyHead {
+            NtfMode: number; // 消息通知模式(单播、组播、广播)
+            ExcludeAcctIDs: number; // 排除账号集合(首先过滤此集合帐号)
+            ExchIDs: number; // 目标交易所集合(若匹配所属交易所,推送)
+            MemberIDs: number; // 目标会员集合(若匹配所属会员,推送)
+            AccountIDs: number; // 目标账号集合(以上均不匹配时,最后检查此集合)
+        }
+
+        // 市场状态变更通知
+        interface MarketStatusChangeNtf {
+            Header: IMessageHead;
+            NtfHeader: NotifyHead; // NotifyHead 公共消息头
+            MarketID: number; // uint32 市场ID
+            Status: number; // uint32 状态
+            StepType: number; // uint32 阶段
+            TradeDay: string; // string 交易日
+            NotifyTime: string; // string 通知发送时间
+        }
+
+        /** 斩仓通知 */
+        interface RiskCutNtf {
+            Header: IMessageHead;
+            NtfHeader: NotifyHead; // NotifyHead 公共消息头
+            AccountId: number; // uint64 账号id
+            RiskLevel: number; // int32 当前风险级别:1:提示,
+            Idate: string; // datetime 时间
+            CurRate: number; // double 实时风险率
+            CutRate: number; // double 斩仓触发风险率
+            SafeRate: number; // double 斩仓恢复风险率
+        }
+    }
+}