Bläddra i källkod

Merge branch 'master' of http://47.101.159.18:3000/Muchinfo/THJ_Mobile

gitmuchinfo 3 år sedan
förälder
incheckning
241a57263d
71 ändrade filer med 1784 tillägg och 360 borttagningar
  1. 7 1
      fxgl.txt
  2. 38 0
      public/proto/thj.proto
  3. 2 2
      src/business/auth/index.ts
  4. 198 127
      src/business/bank/index.ts
  5. 1 1
      src/business/common/index.ts
  6. 83 3
      src/business/credit/index.ts
  7. 2 2
      src/business/goods/index.ts
  8. 33 1
      src/constants/account.ts
  9. 29 0
      src/constants/bank.ts
  10. 7 0
      src/constants/credit.ts
  11. 7 1
      src/constants/funcode.ts
  12. 2 0
      src/constants/market.ts
  13. 8 0
      src/constants/regex.ts
  14. 27 14
      src/filters/index.ts
  15. 20 3
      src/hooks/navigation/index.ts
  16. BIN
      src/packages/mobile/assets/images/lottery/lottery-bg.png
  17. BIN
      src/packages/mobile/assets/images/lottery/lottery-money.png
  18. BIN
      src/packages/mobile/assets/images/lottery/lottery-red-envelope.png
  19. BIN
      src/packages/mobile/assets/images/lottery/lottery-title.png
  20. 9 0
      src/packages/mobile/assets/themes/global/global.less
  21. 11 0
      src/packages/mobile/components/base/select/index.less
  22. 102 0
      src/packages/mobile/components/base/select/index.vue
  23. 0 0
      src/packages/mobile/components/layouts/navback/index.vue
  24. 5 0
      src/packages/mobile/router/index.ts
  25. 73 24
      src/packages/mobile/views/account/certification/index.vue
  26. 31 27
      src/packages/mobile/views/bank/sign/components/edit/index.vue
  27. 17 0
      src/packages/mobile/views/bank/sign/index.less
  28. 40 4
      src/packages/mobile/views/bank/sign/index.vue
  29. 73 7
      src/packages/mobile/views/bank/wallet/components/deposit/index.vue
  30. 13 0
      src/packages/mobile/views/bank/wallet/components/withdraw/index.less
  31. 48 13
      src/packages/mobile/views/bank/wallet/components/withdraw/index.vue
  32. 1 1
      src/packages/mobile/views/bank/wallet/index.less
  33. 5 1
      src/packages/mobile/views/bank/wallet/index.vue
  34. 59 0
      src/packages/mobile/views/credit/lottery/components/reward/index.less
  35. 66 0
      src/packages/mobile/views/credit/lottery/components/reward/index.vue
  36. 164 0
      src/packages/mobile/views/credit/lottery/index.less
  37. 80 0
      src/packages/mobile/views/credit/lottery/index.vue
  38. 1 1
      src/packages/mobile/views/credit/signin/index.less
  39. 6 5
      src/packages/mobile/views/credit/signin/index.vue
  40. 92 6
      src/packages/mobile/views/goods/details/index.vue
  41. 27 1
      src/packages/mobile/views/home/components/main/index.less
  42. 7 7
      src/packages/mobile/views/home/components/main/index.vue
  43. 3 12
      src/packages/mobile/views/home/components/mine/index.less
  44. 70 16
      src/packages/mobile/views/home/components/mine/index.vue
  45. 1 1
      src/packages/mobile/views/home/components/purchase/index.vue
  46. 1 1
      src/packages/mobile/views/home/index.vue
  47. 2 2
      src/packages/mobile/views/news/details/index.vue
  48. 1 1
      src/packages/mobile/views/rules/ccwl/index.vue
  49. 18 5
      src/packages/mobile/views/user/login/index.vue
  50. 48 0
      src/packages/mobile/views/user/register/components/reward/index.less
  51. 42 0
      src/packages/mobile/views/user/register/components/reward/index.vue
  52. 0 39
      src/packages/mobile/views/user/register/index.less
  53. 4 12
      src/packages/mobile/views/user/register/index.vue
  54. 9 2
      src/services/api/bank/index.ts
  55. 7 0
      src/services/api/common/index.ts
  56. 17 0
      src/services/api/credit/index.ts
  57. 1 1
      src/services/api/goods/index.ts
  58. 1 0
      src/services/bus/interface.ts
  59. 7 0
      src/services/socket/index.ts
  60. 1 1
      src/services/socket/trade/index.ts
  61. 20 4
      src/stores/modules/account.ts
  62. 1 1
      src/stores/modules/enum.ts
  63. 17 1
      src/stores/modules/user.ts
  64. 14 0
      src/types/model/common.d.ts
  65. 20 0
      src/types/model/credit.d.ts
  66. 7 7
      src/types/model/goods.d.ts
  67. 20 0
      src/types/proto/bank.d.ts
  68. 24 0
      src/types/proto/credit.d.ts
  69. 2 1
      src/types/proto/trade.d.ts
  70. 1 1
      src/utils/vant/index.ts
  71. 31 0
      src/utils/websocket/crypto.ts

+ 7 - 1
fxgl.txt

@@ -1,3 +1,9 @@
 Store Password : muchinfo
 key Alias : fxgl
-key Password : muchinfo
+key Password : muchinfo
+
+cn.muchinfo.thj_debug_v1.0.0.apk
+cn.muchinfo.thj_release_v1.0.0.apk
+
+Go服务:http://101.133.236.116:8280/cfg?key=mtp_20
+升级检测:http://47.116.5.81:8012/PhoneService/GetUpdateInfo

+ 38 - 0
public/proto/thj.proto

@@ -537,4 +537,42 @@ message SpotPresaleBreachOfContractConfirmRsp {
 		optional uint32 UserID = 4; // 用户ID,必填
 		optional uint64 WRTradeDetailID = 5; // 采购成交单ID
 		optional string ClientSerialNo = 6; // 客户端流水号
+}
+
+// 铁合金抽奖接口请求
+message THJRedPacketLotteryReq {
+	optional MessageHead Header = 1;
+		optional uint32 UserID = 2; // 用户ID,必填
+		optional uint32 ClientType = 3; // 终端类型
+		optional string ClientSerialNo = 4; // 客户端流水号
+}
+
+// 铁合金抽奖接口响应
+message THJRedPacketLotteryRsp {
+	optional MessageHead Header = 1; // 消息头
+	optional int32 RetCode = 2; // 返回码
+	optional string RetDesc = 3; // 描述信息
+		optional uint32 UserID = 4; // 用户ID,必填
+		optional double RedPacketAmount = 5; // 中奖红包金额
+		optional uint32 GearLevel = 6; // 档位
+		optional string ClientSerialNo = 7; // 客户端流水号
+}
+
+// 账户资金信息请求 0 4 6
+message AccountFundInfoReq {
+	optional MessageHead Header = 1; // MessageHead
+	optional uint64 OrderId = 2; // uint64 查询委托单号
+	optional uint64 AccountId = 3; // uint64 查询资金账号
+	optional uint32 QueryBitMask = 4; // uint32 查询位掩码
+}
+// 账户资金信息响应 0 4 7
+message AccountFundInfoRsp {
+	optional MessageHead Header = 1; // MessageHead 消息头
+	optional int32 RetCode = 2; // int32 返回码
+	optional string RetDesc = 3; // string 描述信息
+	optional uint64 OrderId = 4; // uint64 查询委托单号
+	optional uint64 AccountId = 5; // uint64 查询资金账号
+	optional double AvailableMargin = 6; // double 可用保证金,查询位掩码: 1
+	optional double AvailableOutMoney = 7; // double 可出资金,查询位掩码: 2
+	optional int32 IsCleanAccount = 8; // int32 是否是干净账户,查询位掩码: 4
 }

+ 2 - 2
src/business/auth/index.ts

@@ -10,7 +10,7 @@ export function useAuth() {
     const user = reactive<Proto.LoginReq>({
         LoginID: localStorage.getItem('thj_app_loginid') ?? '',
         LoginPWD: '',
-        GUID: v4(),
+        GUID: '',
         LoginType: 0,
         ClientType: 4,
         Version: '2.0.0.0',
@@ -25,7 +25,7 @@ export function useAuth() {
             return Promise.reject(err)
         }
 
-        await userLogin(user).then(() => {
+        await userLogin({ ...user, GUID: v4() }).then(() => {
             localStorage.setItem('thj_app_loginid', user.LoginID)
         }).catch((err) => catchError(err))
 

+ 198 - 127
src/business/bank/index.ts

@@ -1,9 +1,9 @@
-import { reactive, shallowRef } from 'vue'
+import { reactive, shallowRef, computed } from 'vue'
 import { useDataTable } from '@/hooks/datatable'
 import { useLoginStore, useUserStore } from '@/stores'
-import { queryHisAmountLog, t2bBankWithdraw, queryBankAccountSign, t2bBankDeposit, queryAccountInOutApply, queryCusBankSignBank, t2bBankSign, t2bBankCancelSign } from '@/services/api/bank'
-import { formatDate } from '@/filters'
-import { Model } from 'echarts'
+import { queryHisAmountLog, t2bBankWithdraw, queryBankAccountSign, t2bBankDeposit, queryAccountInOutApply, queryCusBankSignBank, t2bBankSign, t2bBankCancelSign, accountFundInfoReq } from '@/services/api/bank'
+import { decryptAES } from '@/utils/websocket/crypto'
+import moment from "moment";
 
 // 历史资金流水列表
 export function useBankStatementList() {
@@ -50,7 +50,7 @@ export function getAccountInOutApplyList() {
     const getAccouuntInOutApplyList = () => {
         loading.value = true
         return queryAccountInOutApply({
-            data:{
+            data: {
                 userid: getUserId()
             },
             success: (res) => {
@@ -64,12 +64,12 @@ export function getAccountInOutApplyList() {
     }
 
     return {
-        loading, 
+        loading,
         dataList,
-        total, 
-        pageIndex, 
-        pageSize, 
-        pageCount, 
+        total,
+        pageIndex,
+        pageSize,
+        pageCount,
         getAccouuntInOutApplyList
     }
 }
@@ -93,26 +93,29 @@ export function getCusBankSignBankList() {
     }
 
     return {
-        loading, 
+        loading,
         dataList,
-        total, 
-        pageIndex, 
-        pageSize, 
-        pageCount, 
+        total,
+        pageIndex,
+        pageSize,
+        pageCount,
         getCusBankSignBankList
     }
 }
 
 // 查询当前已经签约信息列表
-export function getBankAccountList() {
-    const { dataList, total, pageIndex, pageSize, pageCount } = useDataTable<Model.BankAccountSignRsp>()
+export function useBankAccountSign() {
+    const { getUserId } = useLoginStore()
+    const { dataList } = useDataTable<Model.BankAccountSignRsp>()
     const loading = shallowRef(false)
 
     const getBankAccountList = () => {
         loading.value = true
         return queryBankAccountSign({
+            data: {
+                userid: getUserId()
+            },
             success: (res) => {
-                total.value = res.total
                 dataList.value = res.data
             },
             complete: () => {
@@ -121,57 +124,70 @@ export function getBankAccountList() {
         })
     }
 
+    const bankInfo = computed(() => {
+        return dataList.value[0]
+    })
+
     return {
-        loading, 
+        loading,
         dataList,
-        total, 
-        pageIndex, 
-        pageSize, 
-        pageCount, 
+        bankInfo,
         getBankAccountList
     }
 }
 
 // 提现请求
-export function doWithdraw() {
+export function useDoWithdraw() {
 
     /// 获取UserId
-    const {getUserId} = useLoginStore()
+    const { getUserId } = useLoginStore()
     const loading = shallowRef(false)
 
     /// 获取当前是否已签约
     const bankAccountSign = shallowRef<Model.BankAccountSignRsp[]>([])
 
+    const sign = computed<Partial<Model.BankAccountSignRsp>>(() => {
+        if (bankAccountSign.value.length) {
+            return bankAccountSign.value[0]
+        }
+        return {}
+    })
+
     /// 查询签约信息
     const request = queryBankAccountSign({
         data: {
             userid: getUserId()
         },
-        success:(res)=>{
-            bankAccountSign.value=res.data,
-            ({
-                bankaccountname2:formData.BankAccoutName,
-                branchbankname: formData.BranchBankName,
-                accountcode: formData.AccountCode,
-                cusbankid: formData.CusBankID,
-                bankid: formData.OpenCardBankId
-            }=res.data[0])
+        success: (res) => {
+            if (res.data.length) {
+                bankAccountSign.value = res.data
+                const data = res.data[0]
+                formData.Currency = data.currency
+                formData.CusBankID = data.cusbankid
+                formData.BankAccoutName = data.bankaccountname
+                formData.OpenCardBankId = data.bankid
+                formData.AccountCode = data.accountcode
+                formData.BranchBankName = data.branchbankname
+                formData.BankAccoutName = data.bankaccountname2
+                formData.BankAccoutNum = data.bankaccountno2
+            }
         }
     })
 
     const formData = reactive<Partial<Proto.t2bBankWithdrawReq>>({
         AccountType: 0,
-        ExtOperatorID: new Date().getTime(),
-        AppDateTime: formatDate(Date(), 'yyyy-MM-dd HH:mm:ss'),
-        extend_info: JSON.stringify({"sex": 1})
+        AppDateTime: moment(new Date()).format('YYYY-MM-DD HH:mm:ss'),
+        extend_info: JSON.stringify({ "sex": 1 })
     })
 
     const onSubmit = async () => {
         await request
         loading.value = true
+
         return t2bBankWithdraw({
             data: {
                 ...formData,
+                ExtOperatorID: new Date().getTime()
             },
             complete: () => {
                 loading.value = false
@@ -180,40 +196,40 @@ export function doWithdraw() {
     }
 
     return {
-        loading, 
-        onSubmit, 
-        formData, 
-        bankAccountSign
+        loading,
+        onSubmit,
+        formData,
+        sign
     }
 }
 
 // 充值请求
-export function doDeposit(url: string) {
+export function useDoDeposit() {
     /// 获取UserId
-    const {getUserId} = useLoginStore()
+    const { getUserId } = useLoginStore()
     const loading = shallowRef(false)
     /// 获取当前是否已签约
-    const bankAccountSign = shallowRef<Model.BankAccountSignRsp[]>([])
-
-    const formData = reactive<Partial<Proto.t2bBankDepositReq>>({
-        ExtOperatorID: new Date().getTime(),
-        extend_info: JSON.stringify({'certificate_photo_url': url, "sex": 1})
-    })
+    const sign = shallowRef<Model.BankAccountSignRsp[]>([])
+    /// 常量信息
+    const extendInfo = { certificate_photo_url: '', sex: 1 }
+    /// formData
+    const formData = reactive<Partial<Proto.t2bBankDepositReq>>({})
 
     const request = queryBankAccountSign({
         data: {
             userid: getUserId()
         },
-        success:(res)=>{
-            bankAccountSign.value=res.data,
-            ({
-                amount: formData.Amount,
-                cusbankid: formData.CusBankID,
-                currency: formData.Currency,
-                bankaccountname2: formData.BankAccoutName,
-                bankaccountno2: formData.BankAccoutNum,
-                accountcode: formData.AccountCode
-            })
+        success: (res) => {
+            if (res.data.length) {
+                sign.value = res.data
+                const data = res.data[0]
+                formData.Currency = data.currency
+                formData.CusBankID = data.cusbankid
+                formData.BankAccoutName = data.bankaccountname2
+                formData.OpenCardBankId = data.bankid
+                formData.AccountCode = data.accountcode
+                formData.BankAccoutNum = data.bankaccountno2
+            }
         }
     })
 
@@ -222,7 +238,9 @@ export function doDeposit(url: string) {
         loading.value = true
         return t2bBankDeposit({
             data: {
-                ...formData
+                ...formData,
+                ExtOperatorID: new Date().getTime(),
+                extend_info: JSON.stringify(extendInfo)
             },
             complete: () => {
                 loading.value = false
@@ -231,59 +249,80 @@ export function doDeposit(url: string) {
     }
 
     return {
-        loading, 
-        onSubmit, 
+        loading,
+        onSubmit,
+        extendInfo,
         formData,
-        bankAccountSign
+        sign
     }
 }
 
 /// 银行签约请求
-export function doBankSign() {
+export function useDoBankSign() {
 
     const loading = shallowRef(false)
-    const {getUserInfo} = useUserStore()
-    const {getFirstAccountId} = useLoginStore()
+    const { getUserInfo } = useUserStore()
+    const { getFirstAccountId } = useLoginStore()
+
+    /// 托管银行信息
+    const cusSignBank = shallowRef<Model.CusBankSignBankRsp>()
+    /// 查询签约银行信息
+    const request = queryCusBankSignBank({
+        success: (res) => {
+            if (res.data.length) {
+                const data = res.data[0]
+                cusSignBank.value = data
+                formData.Currency = data.currency
+                formData.CusBankID = data.cusbankid
+                formData.TradeDate = data.tradedate
+            }
+        }
+    })
+
+    /// 银行列表
+    const banklist = computed(() => {
+        return (cusSignBank.value?.Banklst ?? []).filter(e => e.status == 0)
+    })
+
+    const { getBankAccountList, bankInfo } = useBankAccountSign()
+    /// 判断是否有签约信息 有就做修改
+    getBankAccountList().then(()=>{
+        ({
+            currency:formData.Currency,
+             cusbankid:formData.CusBankID,
+             accountcode:formData.AccountCode,
+             accountname:formData.BankAccountName,
+             branchbankname: formData.OpenBankName,
+             bankaccountno: formData.BankAccountNo,
+             bankid:formData.OpenBankAccId,
+            }=bankInfo.value)
+    })
 
     /// 数据
-    const formData = reactive <Partial<Proto.t2bBankSignReq>>({
+    const formData = reactive<Partial<Proto.t2bBankSignReq>>({
         AccountType: 1,
         IsForce: 0,
         AgentCertType: 0,
-        OperateType: 1,
+        OperateType: bankInfo.value ? 1 : 2,
         BankCardType: 0,
         BankAccountType: 1,
-        extend_info: JSON.stringify({"sex": 1}),
-        ExtOperatorID: new Date().getTime(),
+        extend_info: JSON.stringify({ "sex": 1 }),
         AccountCode: getFirstAccountId().toString(),
-        CertID: getUserInfo()?.cardnum,
-        CertType: getUserInfo()?.cardtypeid.toString()
-    })
-
-    const cusSignBank = shallowRef<Model.CusBankSignBankRsp[]>([])
-
-    /// 查询签约银行信息
-    const request = queryCusBankSignBank({
-        success: (res) => {
-            cusSignBank.value = res.data,
-            ({
-                accountName: formData.AccountName,
-                branchBankName: formData.OpenBankName,
-                bankNo: formData.BankAccountNo,
-                currency: formData.Currency,
-                tradedate: formData.TradeDate,
-                bankid: formData.OpenBankAccId,
-                cusbankid: formData.CusBankID
-            })
-        }
+        CertID: decryptAES(getUserInfo()?.cardnum ?? ''),
+        CertType: getUserInfo()?.cardtypeid.toString(),
+        BankAccountName: getUserInfo()?.customername,
     })
 
     const onSubmit = async () => {
         await request
         loading.value = true
+
         return t2bBankSign({
             data: {
-                ...formData
+                ...formData,
+                ExtOperatorID: new Date().getTime(),
+                AccountName: formData.BankAccountName,
+                ExBankName: banklist.value.find(obj => obj.bankid === formData.OpenBankAccId)?.bankname
             },
             complete: () => {
                 loading.value = false
@@ -294,53 +333,59 @@ export function doBankSign() {
     return {
         loading,
         formData,
-        cusSignBank,
-        onSubmit
+        banklist,
+        onSubmit,
+        bankInfo
     }
 }
 
 /// 银行解约请求
-export function doCancelBankSign() {
+export function useDoCancelBankSign() {
+    const { getBankAccountList, bankInfo } = useBankAccountSign()
+    
     /// 获取UserId
-    const {getUserId} = useLoginStore()
     const loading = shallowRef(false)
+
+    /// 表单信息
+    const formData = reactive<Partial<Proto.t2bBankCancelSignReq>>({
+        IsForce: 0,
+    })
     /// 获取当前是否已签约
-    const bankAccountSign = shallowRef<Model.BankAccountSignRsp[]>([])
-    const formData = reactive<Partial<Proto.t2bBankCancelSignReq>>({})
-    
-    /// 请求签约信息
-    const request = queryBankAccountSign({
-        data: {
-            userid: getUserId()
-        },
-        success:(res)=>{
-            bankAccountSign.value=res.data,
-            ({
-                cusbankid: formData.CusBankID,
-                currency: formData.Currency,
-                tradedate: formData.TradeDate,
-                accountcode: formData.AccountCode,
-                bankaccountno: formData.BankChildAcc,
-                bankaccountname: formData.BankChildAccName
-            })
-        }
+    const sign = shallowRef<Model.BankAccountSignRsp[]>([])
+
+    getBankAccountList().then(()=>{
+        ({
+            currency:formData.Currency,
+             cusbankid:formData.CusBankID,
+             accountcode:formData.AccountCode,
+            }=bankInfo.value)
+        
     })
 
-    const formSubmit = async () => {
-        await request
-        const { cusbankid, currency, accountcode, bankaccountno, bankaccountname } = bankAccountSign.value[0]
+    // /// 请求签约信息
+    // const request = queryBankAccountSign({
+    //     data: {
+    //         userid: getUserId()
+    //     },
+    //     success: (res) => {
+    //         if (res.data.length) {
+    //             sign.value = res.data
+    //             const data = res.data[0]  
+    //             formData.Currency = data.currency,
+    //             formData.CusBankID = data. cusbankid,
+    //             formData.AccountCode = data. accountcode        
+    //         }
+    //     }
+    // })
+
+    const cancelSubmit = async () => {
+        // await request
         loading.value = true
         /// 发起请求
         return t2bBankCancelSign({
             data: {
-                IsForce: 0,
-                ExtOperatorID: new Date().getTime(),
                 ...formData,
-                CusBankID: cusbankid,
-                Currency: currency,
-                AccountCode: accountcode,
-                BankChildAcc: bankaccountno,
-                BankChildAccName: bankaccountname
+                ExtOperatorID: new Date().getTime(),
             },
             complete: () => {
                 loading.value = false
@@ -349,9 +394,35 @@ export function doCancelBankSign() {
     }
 
     return {
-        loading, 
-        formSubmit, 
+        loading,
+        cancelSubmit,
         formData,
-        bankAccountSign
+        sign,
+        bankInfo
+    }
+}
+
+/// 账户资金信息请求
+export function useAccountFundInfo() {
+    /// 获取UserId
+    const { getFirstAccountId } = useLoginStore()
+
+    /// 数据
+    const fund = shallowRef<Partial<Proto.AccountFundInfoRsp>>({})
+
+    /// 账户资金信息
+    accountFundInfoReq({
+        data: {
+            QueryBitMask: 2,
+            OrderId: new Date().getTime(),
+            AccountId: getFirstAccountId(),
+        },
+        success: (res) => {
+            fund.value = res
+        },
+    })
+
+    return {
+        fund
     }
 }

+ 1 - 1
src/business/common/index.ts

@@ -25,7 +25,7 @@ export async function initBaseData() {
     await errorInfoStore.getErrorInfoList()
 
     if (loginStore.getToken()) {
-        await checkToken()
+        await checkToken().then(() => checkTokenLoop())
         await Promise.all([
             userStore.getUserData(),
             menuStore.getUserMenuList(),

+ 83 - 3
src/business/credit/index.ts

@@ -1,10 +1,14 @@
 import { shallowRef } from 'vue'
 import { useDataTable } from '@/hooks/datatable'
+import { v4 } from 'uuid'
+import { ClientType } from '@/constants/client'
+import { ScoreConfigType } from '@/constants/credit'
 import { useLoginStore } from '@/stores'
-import { queryUserScoreLog } from '@/services/api/credit'
+import { queryUserAccount } from '@/services/api/account'
+import { queryUserScoreLog, queryTHJScoreConfig, thjRedPacketLottery } from '@/services/api/credit'
 
 // 积分流水列表
-export function useCreditStatementList() {
+export function useCreditStatementList(stype?: ScoreConfigType) {
     const { getUserId } = useLoginStore()
     const { dataList, total, pageIndex, pageSize, pageCount } = useDataTable<Model.UserScoreLogRsp>()
     const loading = shallowRef(false)
@@ -13,9 +17,10 @@ export function useCreditStatementList() {
         loading.value = true
         return queryUserScoreLog({
             data: {
+                stype,
+                userid: getUserId(),
                 page: pageIndex.value,
                 pagesize: pageSize.value,
-                userid: getUserId(),
             },
             success: (res) => {
                 total.value = res.total
@@ -36,4 +41,79 @@ export function useCreditStatementList() {
         pageCount,
         getCreditStatementList,
     }
+}
+
+// 积分抽奖
+export function useCreditLottery() {
+    const { getUserId } = useLoginStore()
+    const loading = shallowRef(false)
+    const creditList = shallowRef<Model.UserScoreLogRsp[]>([])
+    const creditConfig = shallowRef<Partial<Model.THJScoreConfigRsp>>({})
+    const userAccount = shallowRef<Partial<Model.UserAccount>>({})
+
+    const redEnvelope = shallowRef<Partial<Proto.THJRedPacketLotteryRsp>>({
+        GearLevel: 0,
+        RedPacketAmount: 0,
+    })
+
+    const formRefresh = () => {
+        queryUserAccount({
+            data: {
+                userID: getUserId()
+            },
+            success: (res) => {
+                userAccount.value = res.data
+            }
+        })
+        queryUserScoreLog({
+            data: {
+                stype: ScoreConfigType.Lottery,
+                userid: getUserId(),
+                page: 1,
+                pagesize: 10,
+            },
+            success: (res) => {
+                creditList.value = res.data
+            },
+        })
+    }
+
+    const formSubmit = () => {
+        loading.value = true
+        return thjRedPacketLottery({
+            data: {
+                UserID: getUserId(),
+                ClientType: ClientType.Web, // 终端类型
+                ClientSerialNo: v4() // 客户端流水号
+            },
+            success: (res) => {
+                redEnvelope.value = res
+                formRefresh()
+            },
+            complete: () => {
+                loading.value = false
+            }
+        })
+    }
+
+    queryTHJScoreConfig({
+        data: {
+            stype: ScoreConfigType.Lottery
+        },
+        success: (res) => {
+            if (res.data.length) {
+                creditConfig.value = res.data[0]
+            }
+        },
+    })
+
+    return {
+        loading,
+        creditConfig,
+        userAccount,
+        creditList,
+        redEnvelope,
+        formSubmit,
+        formRefresh,
+    }
 }

+ 2 - 2
src/business/goods/index.ts

@@ -37,7 +37,7 @@ export function useWrstandardList() {
 
 // 采购详细
 export function useWrstandardDetails(wrstandardid: number) {
-    const details = shallowRef<Model.THJWrstandardDetailRsp>()
+    const details = shallowRef<Partial<Model.THJWrstandardDetailRsp>>({})
 
     const getWrstandardDetails = () => {
         return queryTHJWrstandardDetail({
@@ -45,7 +45,7 @@ export function useWrstandardDetails(wrstandardid: number) {
                 wrstandardid,
             },
             success: (res) => {
-                console.log(res)
+                details.value = res.data
             }
         })
     }

+ 33 - 1
src/constants/account.ts

@@ -3,6 +3,38 @@ import { useEnumStore } from '@/stores'
 const { getEnumTypeList, getEnumTypeName, getEnumTypeValue } = useEnumStore()
 
 /**
+ * 实名认证状态
+ */
+export enum AuthStatus {
+    Uncertified = 0, // 未认证
+    Certified = 1, // 已认证
+    Submitted = 2, // 已提交
+    Rejected = 3, // 已拒绝
+}
+
+/**
+ * 实名认证状态列表
+ * @returns 
+ */
+export function getAuthStatusList() {
+    return [
+        { label: '未实名', value: AuthStatus.Uncertified },
+        { label: '已实名', value: AuthStatus.Certified },
+        { label: '实名审核中', value: AuthStatus.Submitted },
+        { label: '实名未通过', value: AuthStatus.Rejected },
+    ]
+}
+
+/**
+ * 获取实名认证状态名称
+ * @returns 
+ */
+export function getAuthStatusName(value: number) {
+    const enums = getAuthStatusList()
+    return getEnumTypeName(enums, value)
+}
+
+/**
  * 获取对应的证件类型列表
  * @returns 
  */
@@ -23,7 +55,7 @@ export function getCertificateTypeCodeName(value: number) {
  * 获取对应的证件类型名称的值
  * @returns 
  */
- export function getCertificateTypeCodeValue(label: string) {
+export function getCertificateTypeCodeValue(label: string) {
     const enums = getCertificateTypeList()
     return getEnumTypeValue(enums, label)
 }

+ 29 - 0
src/constants/bank.ts

@@ -3,6 +3,35 @@ import { useEnumStore } from '@/stores'
 const { getEnumTypeList, getEnumTypeName } = useEnumStore()
 
 /**
+ * 签约状态
+ */
+export enum SignStatus {
+    Unsigned = 1,  // 未签约
+    Audit = 2,  // 签约待审核
+    Signing = 3,  // 签约中
+    Signed = 4,  // 已签约
+    Rescinded = 7,  // 已解约
+    Refuse = 10, // 审核拒绝
+}
+
+/**
+ * 获取签约状态列表
+ * @returns 
+ */
+export function getSignStatusList() {
+    return getEnumTypeList('signstatus')
+}
+
+/**
+ * 获取签约状态名称
+ * @returns 
+ */
+export function getSignStatusName(value: number) {
+    const enums = getSignStatusList()
+    return getEnumTypeName(enums, value)
+}
+
+/**
  * 获取资金流水操作类型列表
  * @returns 
  */

+ 7 - 0
src/constants/credit.ts

@@ -3,6 +3,13 @@ import { useEnumStore } from '@/stores'
 const { getEnumTypeList, getEnumTypeName } = useEnumStore()
 
 /**
+ * 积分配置类型
+ */
+export enum ScoreConfigType {
+    Lottery = 7, // 抽奖得红包
+}
+
+/**
  * 获取积分操作类型列表
  * @returns 
  */

+ 7 - 1
src/constants/funcode.ts

@@ -25,7 +25,9 @@ export enum FunCode {
     t2bBankWithdrawReq = 589837, // 出金请求
     t2bBankWithdrawRsp = 589838, // 出金应答
     t2bBankDepositReq = 589843, // 入金请求
-    t2bBankDepositRsp = 589844, //入金应答
+    t2bBankDepositRsp = 589844,   //入金应答
+    AccountFundInfoReq = 262150,   /// 账户资金信息请求(0, 4, 6)
+    AccountFundInfoRsp = 262151,  /// 账户资金信息响应(0, 4, 7)
 
     UserReceiveInfoReq = 1900545, // 新增修改收货地址请求
     UserReceiveInfoRsp = 1900546, // 新增修改收货地址请求响应
@@ -36,6 +38,7 @@ export enum FunCode {
 
     // 交易通知
     MoneyChangedNotify = 131076, // 资金变化通知
+    UserChangeNtf = 131106,    /// 用户变更通知(0, 2, 34)
 
     // 行情内容
     QuoteBeat = 0x12, // 心跳
@@ -44,6 +47,9 @@ export enum FunCode {
     QueryQuoteReq = 0x22, // 盘面查询请求
     QueryQuoteRsp = 0x23, // 盘面查询应答
 
+    THJRedPacketLotteryReq = 1441863, // 铁合金抽奖接口请求
+    THJRedPacketLotteryRsp = 1441864, // 铁合金抽奖接口响应
+
     SpotPresaleDestingOrderReq = 1441849, // 铁合金现货预售摘牌接口请求
     SpotPresaleDestingOrderRsp = 1441850, // 铁合金现货预售摘牌接口应答
     SpotPresaleDeliveryConfirmReq = 1441861, // 铁合金现货预售交收确认接口请求

+ 2 - 0
src/constants/market.ts

@@ -6,4 +6,6 @@ export enum Market {
     GZ = 67201,
     /** 铁合金 */
     THJ = 64201,
+    /** 铁合金挂牌 */
+    THJ_Listing = 65201,
 }

+ 8 - 0
src/constants/regex.ts

@@ -21,5 +21,13 @@ export const validateRules = {
     enname: {
         validate: (val: string) => /^[a-zA-Z0-9_]{1,}$/.test(val),
         message: '只能输入英文字母、数字、下划线',
+    },
+    cardno: {
+        validate: (val: string) => /^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/.test(val),
+        message: '身份证号码不合规',
+    },
+    bankcardno: {
+        validate: (val: string) => /^([1-9]{1})(\d{15}|\d{16}|\d{18})$/.test(val),
+        message: '银行卡号码不合规',
     }
 }

+ 27 - 14
src/filters/index.ts

@@ -3,19 +3,43 @@ import service from '@/services'
 import moment from 'moment'
 
 /**
+ * 获取url对象方法
+ * @param url 
+ * @param base 
+ * @returns 
+ */
+export function getUrl(url: string, base?: string) {
+    const { uploadUrl } = service.config
+    return new URL(url, base ?? uploadUrl)
+}
+
+/**
  * 获取图片地址
  * @param fileUrl 
  */
 export function getImageUrl(fileUrl: string) {
     if (fileUrl) {
-        const { openApiUrl } = service.config
-        return openApiUrl + fileUrl.replace('./', '/')
+        const url = getUrl(fileUrl)
+        return url.href
     }
-    // 返回默认图片
     return ''
 }
 
 /**
+ * 格式化html字符串
+ * @param text 
+ * @returns 
+ */
+export function formatHtmlString(text: string) {
+    const html = text.replace(/[\n\r]/g, '<br />')
+    return html.replace(/<img [^>]*src=['"]([^'"]+)[^>]*>/gi, (match, capture) => {
+        // 替换img标签src地址为绝对路径
+        const url = getUrl(capture)
+        return `<img src="${url.href}"  alt=""/>`
+    })
+}
+
+/**
  * 处理价格颜色的显示
  */
 export function handlePriceColor(curValue: number, preValue: number) {
@@ -110,17 +134,6 @@ export function formatDate(date: string, format = 'YYYY-MM-DD HH:mm:ss') {
     return moment(date).format(format)
 }
 
-export function formatText(text: string) {
-    const { uploadUrl } = service.config
-    const html = text.replace(/[\n\r]/g, '<br />')
-
-    //return html.replace(/(<img[^>]*src=['"])(?:(?!(https|http)))([^>]*>)/g, `$1${uploadUrl}$2/$3`)
-
-    return html.replace(/<img [^>]*src=['"]([^'"]+)[^>]*>/gi, (match, capture) => {
-        return `<img src="${uploadUrl.replace('upload', '') + capture.replace('./', '')}"  alt=""/>`
-    })
-}
-
 /**
  * Des密钥
  * 手机发送验证码时加密方法

+ 20 - 3
src/hooks/navigation/index.ts

@@ -1,4 +1,4 @@
-import { useRoute, useRouter, RouteParamsRaw, onBeforeRouteLeave } from 'vue-router'
+import { useRoute, useRouter, onBeforeRouteLeave } from 'vue-router'
 import animateRouter from '@mobile/router/animateRouter'
 
 export function useNavigation() {
@@ -21,6 +21,21 @@ export function useNavigation() {
         }
     }
 
+    // 获取查询字符串
+    const getQueryString = (name: string) => {
+        const qs = route.query[name]
+        if (qs) {
+            return qs.toString()
+        }
+        return ''
+    }
+
+    const getQueryStringToNumber = (name: string) => {
+        const reg = /^[0-9]+.?[0-9]*/
+        const value = getQueryString(name)
+        return reg.test(value) ? Number(value) : 0
+    }
+
     // 返回指定页面
     const backTo = <T extends object>(to: string, params?: T) => {
         const { state } = animateRouter
@@ -37,8 +52,8 @@ export function useNavigation() {
     }
 
     // 路由跳转
-    const routerTo = (to: string, params?: RouteParamsRaw) => {
-        router.push({ name: to, params })
+    const routerTo = (to: string) => {
+        router.push({ name: to })
     }
 
     // 导航守卫
@@ -57,6 +72,8 @@ export function useNavigation() {
         router,
         setGlobalUrlParams,
         getGlobalUrlParams,
+        getQueryString,
+        getQueryStringToNumber,
         backTo,
         routerTo,
         beforeRouteLeave,

BIN
src/packages/mobile/assets/images/lottery/lottery-bg.png


BIN
src/packages/mobile/assets/images/lottery/lottery-money.png


BIN
src/packages/mobile/assets/images/lottery/lottery-red-envelope.png


BIN
src/packages/mobile/assets/images/lottery/lottery-title.png


+ 9 - 0
src/packages/mobile/assets/themes/global/global.less

@@ -71,4 +71,13 @@
             margin-right: .24rem;
         }
     }
+}
+
+.van {
+    &-dialog {
+        &__message {
+            font-size  : .28rem;
+            line-height: .44rem;
+        }
+    }
 }

+ 11 - 0
src/packages/mobile/components/base/select/index.less

@@ -0,0 +1,11 @@
+.app-select {
+    width: 100%;
+
+    /deep/ .van-field {
+        &__label {
+            &:empty {
+                display: none;
+            }
+        }
+    }
+}

+ 102 - 0
src/packages/mobile/components/base/select/index.vue

@@ -0,0 +1,102 @@
+<template>
+    <div class="app-select">
+        <Field ref="fieldRef" :name="name" :rules="rules" @click="showPicker = true" :is-link="isLink">
+            <template #label>
+                <slot name="label">
+                    {{ label }}
+                </slot>
+            </template>
+            <template #input>
+                <slot name="input" :item="props.options[selectedIndex]">
+                    <input :value="columnLabel" :placeholder="placeholder" readonly />
+                </slot>
+            </template>
+        </Field>
+        <Popup v-model:show="showPicker" position="bottom" teleport="body">
+            <Picker :columns="columns" @cancel="onCancel" @confirm="onConfirm" />
+        </Popup>
+    </div>
+</template>
+
+<script lang="ts" setup>
+import { shallowRef, computed, PropType, watch } from 'vue'
+import { Field, FieldRule, Popup, Picker, PickerOption, FieldInstance } from 'vant'
+
+const props = defineProps({
+    modelValue: {
+        type: [Number, String]
+    },
+    options: {
+        type: Array,
+        default: () => ([])
+    },
+    optionProps: {
+        type: Object as PropType<{ label?: string; value?: string; }>,
+        default: () => ({
+            label: 'label',
+            value: 'value'
+        })
+    },
+    rules: {
+        type: Array as PropType<FieldRule[]>
+    },
+    name: String,
+    label: String,
+    placeholder: {
+        type: String,
+        default: '请选择'
+    },
+    isLink: {
+        type: Boolean,
+        default: true
+    }
+})
+
+const emit = defineEmits(['update:show', 'update:modelValue', 'confirm', 'cancel'])
+const fieldRef = shallowRef<FieldInstance>()
+const showPicker = shallowRef(false) // 是否弹出选择器
+const selectedIndex = shallowRef(-1)
+
+const columns = computed(() => {
+    if (props.options) {
+        return props.options.map((e) => e[props.optionProps.label])
+    }
+    return []
+})
+
+// 当前显示的标签
+const columnLabel = computed(() => {
+    const item = props.options[selectedIndex.value]
+    if (item) {
+        return item[props.optionProps.label] ?? ''
+    }
+    return ''
+})
+
+const onCancel = (currentValue: PickerOption, currentIndex: number) => {
+    showPicker.value = false
+    emit('cancel', props.modelValue, currentIndex)
+}
+
+const onConfirm = (currentValue: PickerOption, currentIndex: number) => {
+    showPicker.value = false
+    if (selectedIndex.value !== currentIndex) {
+        // 更新当前选中的值
+        const item = props.options[currentIndex]
+        const value = item[props.optionProps.value]
+        selectedIndex.value = currentIndex
+        fieldRef.value?.validate()
+
+        emit('update:modelValue', value)
+        emit('confirm', value, currentIndex)
+    }
+}
+
+watch(() => [props.modelValue, props.options], ([value, items]) => {
+    selectedIndex.value = items.findIndex((e) => e[props.optionProps.value]?.toString() === value?.toString())
+})
+</script>
+
+<style lang="less" scoped>
+@import './index.less';
+</style>

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


+ 5 - 0
src/packages/mobile/router/index.ts

@@ -227,6 +227,11 @@ const routes: Array<RouteRecordRaw> = [
         name: 'credit-statement',
         component: () => import('../views/credit/statement/index.vue'),
       },
+      {
+        path: 'lottery',
+        name: 'credit-lottery',
+        component: () => import('../views/credit/lottery/index.vue'),
+      },
     ]
   },
   {

+ 73 - 24
src/packages/mobile/views/account/certification/index.vue

@@ -5,24 +5,24 @@
         </template>
         <Form ref="formRef" class="g-form__container" @submit="onSubmit">
             <CellGroup inset>
-                <Field v-model="formData.username" name="username" label="姓名" placeholder="必填" :rules="formRules.username"/>
-                <Field v-model="formData.cardtype" name="cardtype" label="证件类型" placeholder="请选择证件类型" is-link readonly @click="showPicker = true"/>
-                <Field v-model="formData.cardnum" name="cardnum" label="证件号码" placeholder="必填" :rules="formRules.cardnum"/>
-                <Field v-model="formData.cardbackphotourl" name="cardbackphotourl" label="证件正面照片" :rules="formRules.cardbackphotourl">
+                <Field v-model="formData.username" name="username" label="姓名" placeholder="请输入用户姓名"
+                    :rules="formRules.username" />
+                <app-select v-model="formData.cardtype" name="cardtype" label="证件类型" placeholder="请选择证件类型"
+                    :rules="formRules.cardtype" :options="enums" />
+                <Field v-model="formData.cardnum" name="cardnum" label="证件号码" placeholder="请输入证件号码"
+                    :rules="formRules.cardnum" />
+                <Field name="cardbackphotourl" label="证件正面照片" :rules="formRules.cardbackphotourl">
                     <template #input>
-                        <Uploader/>
+                        <Uploader v-model="frontList" max-count="1" :after-read="b_afterRead" />
                     </template>
                 </Field>
-                <Field v-model="formData.cardfrontphotourl" name="cardfrontphotourl" label="证件反面照片" :rules="formRules.cardfrontphotourl">
+                <Field name="cardfrontphotourl" label="证件反面照片" :rules="formRules.cardfrontphotourl">
                     <template #input>
-                        <Uploader />
+                        <Uploader v-model="backList" max-count="1" :after-read="f_afterRead" />
                     </template>
                 </Field>
             </CellGroup>
         </Form>
-        <Popup v-model:show="showPicker" round position="bottom">
-            <Picker  title="请选择证件类型" :columns="enums" @cancel="showPicker = false" @confirm="onConfirm" />
-        </Popup>
         <template #footer>
             <div class="g-form__footer">
                 <Button type="primary" @click="formRef?.submit" round block>提交实名认证</Button>
@@ -32,29 +32,82 @@
 </template>
 
 <script lang="ts" setup>
-import { reactive, shallowRef } from 'vue'
-import { CellGroup, Button, Field, Form, FormInstance, Uploader, Toast, FieldRule, Picker, Popup } from 'vant'
+import { shallowRef, computed, ref } from 'vue'
+import { CellGroup, Button, Field, Form, FormInstance, Uploader, Toast, FieldRule } from 'vant'
 import { addAuthReq } from '@/business/user'
-import { Model } from 'echarts/core';
 import { fullloading, dialog } from '@/utils/vant';
-import { ref } from 'vue';
 import { getCertificateTypeList } from "@/constants/account";
+import AppSelect from '@mobile/components/base/select/index.vue'
+import { useNavigation } from '@/hooks/navigation'
+import axios from 'axios';
+import { getServiceUrl } from '@/services/http';
 
 const formRef = shallowRef<FormInstance>()
 const { formData, formSubmit } = addAuthReq()
-const showPicker = ref(false);
-const result = ref('');
+const { router } = useNavigation()
 
+/// 证件正面地址
+const backList = ref([]);
+/// 证件背面地址
+const frontList = ref([]);
 /// 获取对应的证件枚举类型
-const enums = getCertificateTypeList().map(obj=>{ return obj.label })
+const enums = computed(() => { return getCertificateTypeList().map(obj => { return { label: obj.label, value: obj.value } }) })
 
 
+const b_afterRead = (file: any) => {
+    file.status = 'uploading';
+    file.message = '上传中...';
+
+    var data = new FormData()
+    data.append('file', file.file)
+    let config = { headers: { "Content-type": "multipart/form-data" } }
+
+    /// 图片上传
+    axios.post(getServiceUrl('uploadUrl'), data, config).then(res => {
+        if (res.status == 200) {
+            file.status = 'success';
+            file.message = '上传成功';
+            formData.cardbackphotourl = res.data[0].filePath
+        } else {
+            file.status = 'failed';
+            file.message = '上传失败';
+        }
+    })
+};
+
+const f_afterRead = (file: any) => {
+    file.status = 'uploading';
+    file.message = '上传中...';
+
+    var data = new FormData()
+    data.append('file', file.file)
+    let config = { headers: { "Content-type": "multipart/form-data" } }
+
+    /// 图片上传
+    axios.post(getServiceUrl('uploadUrl'), data, config).then(res => {
+        if (res.status == 200) {
+            file.status = 'success';
+            file.message = '上传成功';
+            formData.cardfrontphotourl = res.data[0].filePath
+        } else {
+            file.status = 'failed';
+            file.message = '上传失败';
+        }
+    })
+};
+
 // 表单验证规则
 const formRules: { [key in keyof Model.AddAuthReq]?: FieldRule[] } = {
     username: [{
         required: true,
         message: '请输入用户姓名',
     }],
+    cardtype: [{
+        message: '请选择证件类型',
+        validator: () => {
+            return !!formData.cardtype
+        }
+    }],
     cardnum: [{
         required: true,
         message: '请输入证件号码',
@@ -69,17 +122,13 @@ const formRules: { [key in keyof Model.AddAuthReq]?: FieldRule[] } = {
     }],
 }
 
-const onConfirm = (value: any) => {
-    result.value = value;
-    showPicker.value = false;
-    formData.cardtype = value
-}
-
 const onSubmit = () => {
     fullloading((hideLoading) => {
         formSubmit().then(() => {
             hideLoading()
-            dialog('实名认证提交请求成功')
+            dialog('实名认证提交请求成功').then(() => {
+                router.back()
+            })
         }).catch((err) => {
             Toast.fail(err)
         })

+ 31 - 27
src/packages/mobile/views/bank/sign/components/edit/index.vue

@@ -1,24 +1,25 @@
 <template>
-    <app-view>
+    <app-view class="g-form">
         <template #header>
-            <app-navbar title="添加签约用户" />
+            <app-navbar>{{bankInfo ? '修改签约用户' : '添加签约用户'}}</app-navbar>
         </template>
         <Form ref="formRef" class="g-form__container" @submit="formSubmit">
             <CellGroup inset>
-                <Field name="BankName" is-link label="开户银行" placeholder="请选择银行信息" @click="showPicker = true" />
-                <Field name="BankNo" label="银行卡号" v-model="formData.BankAccountNo" placeholder="请输入银行卡账号" :rules="formRules.BankAccountNo" />
-                <Field name="AccountName" label="姓名" v-model="formData.BankAccountName" placeholder="请输入银行卡账户名" :rules="formRules.BankAccountName" />
-                <Field name="BranchBankName" label="支行名称" v-model="formData.OpenBankName" placeholder="请输入银行卡支行名称" :rules="formRules.OpenBankName" />
-                <Field name="SignStatus" label="签约状态" readonly model-value="签约中" />
+                <app-select v-model="formData.OpenBankAccId" name="BankName" label="开户银行" placeholder="请选择开户银行"
+                    :rules="formRules.OpenBankAccId" :options="banklist"
+                    :optionProps="{ label: 'bankname', value: 'bankid' }" />
+                <Field name="BankNo" label="银行卡号" v-model="formData.BankAccountNo" placeholder="请输入银行卡账号"
+                    :rules="formRules.BankAccountNo" />
+                <Field name="AccountName" label="姓名" readonly v-model="formData.BankAccountName" placeholder="请输入银行卡账户名"
+                    :rules="formRules.BankAccountName" />
+                <Field name="BranchBankName" label="支行名称" v-model="formData.OpenBankName" placeholder="请输入银行卡支行名称"
+                    :rules="formRules.OpenBankName" />
+                <Field v-if="false" name="SignStatus" label="签约状态" readonly model-value="签约中" />
             </CellGroup>
         </Form>
-        <Popup v-model:show="showPicker" round position="bottom">
-            <Picker  title="可签约银行" :columns="columns" @cancel="showPicker = false" @confirm="onConfirm" />
-        </Popup>
         <template #footer>
             <div class="g-form__footer">
-                <Button type="success" round block>解约</Button>
-                <Button type="primary" round block @click="formRef?.submit()">提交</Button>
+                <Button type="primary" round block @click="formRef?.submit()">{{ bankInfo ? '修改' :  '提交' }}</Button>
             </div>
         </template>
     </app-view>
@@ -27,31 +28,34 @@
 <script lang="ts" setup>
 
 import { shallowRef } from 'vue'
-import { CellGroup, Button, Field, Form, FormInstance, FieldRule, Toast, Picker, Popup } from 'vant'
+import { CellGroup, Button, Field, Form, FormInstance, FieldRule, Toast } from 'vant'
 import { fullloading, dialog } from '@/utils/vant'
-import { doBankSign } from '@/business/bank'
-import { ref } from 'vue';
+import { useDoBankSign } from '@/business/bank'
 import { useNavigation } from '@/hooks/navigation'
+import AppSelect from '@mobile/components/base/select/index.vue'
+import { validateRules } from '@/constants/regex'
 
-const {router}=useNavigation()
-const { formData, onSubmit, cusSignBank } = doBankSign()
-const result = ref('');
-const showPicker = ref(false);
+const { router } = useNavigation()
+const { formData, onSubmit, banklist, bankInfo } = useDoBankSign()
 const formRef = shallowRef<FormInstance>()
 
-/// 获取银行信息
-const columns = ['杭州', '宁波', '温州', '绍兴', '湖州', '嘉兴', '金华'];
-
-const onConfirm = (value: any) => {
-    result.value = value;
-    showPicker.value = false;
-}
-
 // 表单验证规则
 const formRules: { [key in keyof Proto.t2bBankSignReq]?: FieldRule[] } = {
+    OpenBankAccId: [{
+        message: '请选择银行信息',
+        validator: () => {
+            return !!formData.OpenBankAccId
+        }
+    }],
     BankAccountNo: [{
         required: true,
         message: '请输入银行卡账号',
+        // validator: (val) => {
+        //     if (validateRules.bankcardno.validate(val)) {
+        //         return true
+        //     }
+        //     return validateRules.bankcardno.message
+        // }
     }],
     BankAccountName: [{
         required: true,

+ 17 - 0
src/packages/mobile/views/bank/sign/index.less

@@ -0,0 +1,17 @@
+.bank-sign {
+    &__container {
+        .van-cell {
+            &__title {
+                flex: initial;
+            }
+        }
+    }
+
+    &__empty {
+        text-align: center;
+
+        .van-button {
+            width: 50%;
+        }
+    }
+}

+ 40 - 4
src/packages/mobile/views/bank/sign/index.vue

@@ -3,13 +3,49 @@
         <template #header>
             <app-navbar title="签约账户管理" />
         </template>
-        <Button @click="routerTo('add-banksign')">添加签约账户</Button>
+        <div class="bank-sign__container" v-if="bankInfo">
+            <CellGroup>
+                <Cell title="开户银行" :value="bankInfo.bankname" />
+                <Cell title="银行卡号" :value="bankInfo.bankaccountno" />
+                <Cell title="姓名" :value="bankInfo.bankaccountname" />
+                <Cell title="支行名称" :value="bankInfo.branchbankname" />
+                <Cell title="状态" :value="getSignStatusName(bankInfo.signstatus)" />
+            </CellGroup>
+            <Button type="primary" round @click="formSubmit">解约</Button>
+            <Button type="primary" round @click="routerTo('add-banksign')">修改</Button>
+        </div>
+        <div class="bank-sign__empty" v-else>
+            <Empty description="您还未添加签约账户" />
+            <Button type="primary" @click="$router.push({ name: 'add-banksign' })" round>添加签约账户</Button>
+        </div>
     </app-view>
 </template>
 
 <script lang="ts" setup>
-import { Button } from 'vant'
+import { CellGroup, Cell, Button, Empty, Toast } from 'vant'
+import { fullloading, dialog } from '@/utils/vant'
+import { getSignStatusName } from '@/constants/bank'
+import { useDoCancelBankSign } from '@/business/bank'
 import { useNavigation } from '@/hooks/navigation'
 
-const { routerTo } = useNavigation()
-</script>
+const { cancelSubmit, bankInfo} = useDoCancelBankSign()
+const { router, routerTo } = useNavigation()
+
+const formSubmit = () => {
+    fullloading((hideLoading) => {
+        cancelSubmit().then(() => {
+            hideLoading()
+            dialog('签约提交成功,请耐心等待审核。').then(() => {
+                router.back()
+            })
+        }).catch((err) => {
+            Toast.fail(err)
+        })
+    })
+}
+
+</script>
+
+<style lang="less">
+@import './index.less';
+</style>

+ 73 - 7
src/packages/mobile/views/bank/wallet/components/deposit/index.vue

@@ -1,25 +1,91 @@
 <template>
-    <app-view>
-        <Form ref="formRef">
+    <app-view class="g-form">
+        <Form ref="formRef" class="g-form__container" @submit="formSubmit">
             <CellGroup inset>
-                <Field label="充值金额" placeholder="请填写充值金额" />
+                <Field v-model="formData.Amount" label="充值金额" placeholder="请填写充值金额" :rules="formRules.Amount" />
                 <Field label="凭证">
                     <template #input>
-                        <span>请上传银行充值流水截图</span>
-                        <Uploader />
+                        <Uploader v-model="fileList" name="fileList" :max-size="5 * 1024 * 1024" @oversize="onOversize"
+                            max-count="1" :after-read="afterRead" :rules="formRules.fileList" />
                     </template>
                 </Field>
             </CellGroup>
         </Form>
         <template #footer>
-            <Button round block type="primary">确定</Button>
+            <div class="g-form__footer">
+                <Button round block type="primary" @click="formRef?.submit()">确定</Button>
+            </div>
         </template>
     </app-view>
 </template>
 
 <script lang="ts" setup>
 import { ref } from 'vue'
-import { Form, Field, CellGroup, Button, Uploader, FormInstance } from 'vant'
+import { Form, Field, CellGroup, Button, Uploader, FieldRule, FormInstance, Toast } from 'vant'
+import { useDoDeposit } from '@/business/bank'
+import { fullloading, dialog } from '@/utils/vant';
+import { useNavigation } from '@/hooks/navigation'
+import axios from 'axios';
+import { getServiceUrl } from '@/services/http';
 
 const formRef = ref<FormInstance>()
+const { formData, onSubmit, extendInfo } = useDoDeposit()
+const { router } = useNavigation()
+
+/// 证件正面地址
+const fileList = ref([]);
+
+const afterRead = (file: any) => {
+    file.status = 'uploading';
+    file.message = '上传中...';
+
+    var data = new FormData()
+    data.append('file', file.file)
+    const config = { headers: { "Content-type": "multipart/form-data" } }
+
+    /// 图片上传
+    axios.post(getServiceUrl('uploadUrl'), data, config).then(res => {
+        /// 上传成功
+        if (res.status == 200) {
+            file.status = 'success';
+            file.message = '上传成功';
+            /// 赋值
+            extendInfo.certificate_photo_url = res.data[0].filePath
+        } else {
+            file.status = 'failed';
+            file.message = '上传失败';
+        }
+    })
+};
+
+
+const onOversize = () => {
+    Toast('图片大小不能超过 5Mb')
+};
+
+// 表单验证规则
+const formRules: { [key in keyof Proto.t2bBankDepositReq | 'fileList']?: FieldRule[] } = {
+    Amount: [{
+        required: true,
+        message: '请填写充值金额',
+    }],
+    fileList: [{
+        required: true,
+        message: '请上传转账凭证',
+    }]
+}
+
+const formSubmit = () => {
+    fullloading((hideLoading) => {
+        onSubmit().then(() => {
+            hideLoading()
+            dialog('充值申请提交成功,请等待审核。').then(() => {
+                router.back()
+            })
+        }).catch((err) => {
+            Toast.fail(err)
+        })
+    })
+}
+
 </script>

+ 13 - 0
src/packages/mobile/views/bank/wallet/components/withdraw/index.less

@@ -0,0 +1,13 @@
+.bank-wallet-withdraw {
+    .form-field {
+        .van-field__control {
+            display       : flex;
+            flex-direction: column;
+            align-items   : flex-start;
+        }
+
+        &__tips {
+            font-size: .24rem;
+        }
+    }
+}

+ 48 - 13
src/packages/mobile/views/bank/wallet/components/withdraw/index.vue

@@ -1,29 +1,54 @@
 <template>
-    <app-view>
-        <Form class="g-form__container" @submit="formSubmit">
+    <app-view class="g-form bank-wallet-withdraw">
+        <Form ref="formRef" class="g-form__container" @submit="formSubmit">
             <CellGroup inset>
-                <Field label="提现金额" v-model="formData.Amount" placeholder="请填写提现金额" :rules="formRules.Amount"/>
-                <Field label="开户银行" v-model="formData.BankAccoutName" readonly placeholder="请填写开户银行" />
-                <Field label="银行卡号" v-model="formData.BankAccoutNum" readonly placeholder="请填写银行卡号" />
-                <Field label="姓名" v-model="formData.BankChildAccountName" readonly placeholder="请填写姓名" />
-                <Field label="支行名称"  v-model="formData.BranchBankName" readonly placeholder="请填写支行名称" />
+                <Field class="form-field" label="提现金额" v-model="formData.Amount" :rules="formRules.Amount">
+                    <template #input>
+                        <input v-model="formData.Amount" placeholder="请填写提现金额" />
+                        <span class="form-field__tips">可出金额:{{ fund.AvailableOutMoney }}</span>
+                    </template>
+                </Field>
+                <Field label="开户银行">
+                    <template #input>
+                        {{ sign.bankname }}
+                    </template>
+                </Field>
+                <Field label="银行卡号">
+                    <template #input>
+                        {{ sign.bankaccountno }}
+                    </template>
+                </Field>
+                <Field label="姓名">
+                    <template #input>
+                        {{ sign.bankaccountname }}
+                    </template>
+                </Field>
+                <Field label="支行名称">
+                    <template #input>
+                        {{ sign.branchbankname }}
+                    </template>
+                </Field>
             </CellGroup>
         </Form>
         <template #footer>
-            <Button round block type="primary" @click="formRef?.submit()">确定</Button>
+            <div class="g-form__footer">
+                <Button round block type="primary" @click="formRef?.submit()">确定</Button>
+            </div>
         </template>
     </app-view>
 </template>
 
 <script lang="ts" setup>
-import { doWithdraw } from '@/business/bank';
+import { useDoWithdraw, useAccountFundInfo } from '@/business/bank';
 import { Form, Field, CellGroup, FormInstance, Button, FieldRule, Toast } from 'vant'
 import { shallowRef } from 'vue'
 import { fullloading, dialog } from '@/utils/vant'
 import { useNavigation } from '@/hooks/navigation'
 
-const { formData, onSubmit } = doWithdraw()
-const {router}=useNavigation()
+const { formData, onSubmit, sign } = useDoWithdraw()
+/// 资金账户信息
+const { fund } = useAccountFundInfo()
+const { router } = useNavigation()
 const formRef = shallowRef<FormInstance>()
 
 // 表单验证规则
@@ -31,6 +56,12 @@ const formRules: { [key in keyof Proto.t2bBankWithdrawReq]?: FieldRule[] } = {
     Amount: [{
         required: true,
         message: '请输入出金金额',
+        validator: (val) => {
+            if (val <= (fund.value.AvailableOutMoney ?? 0.0)) {
+                return true
+            }
+            return '超过可出金额'
+        }
     }]
 }
 
@@ -38,7 +69,7 @@ const formSubmit = () => {
     fullloading((hideLoading) => {
         onSubmit().then(() => {
             hideLoading()
-            dialog('出金成功。').then(() => {
+            dialog('出金提交申请成功,请耐心等待审核。').then(() => {
                 router.back()
             })
         }).catch((err) => {
@@ -46,4 +77,8 @@ const formSubmit = () => {
         })
     })
 }
-</script>
+</script>
+
+<style lang="less">
+@import './index.less';
+</style>

+ 1 - 1
src/packages/mobile/views/bank/wallet/index.less

@@ -1,4 +1,4 @@
-.account-bank {
+.bank-wallet {
     .van-tabs {
         display       : flex;
         flex-direction: column;

+ 5 - 1
src/packages/mobile/views/bank/wallet/index.vue

@@ -3,7 +3,7 @@
         <template #header>
             <app-navbar title="充值提现" />
         </template>
-        <Tabs>
+        <Tabs v-model:active="active">
             <Tab title="充值">
                 <app-deposit />
             </Tab>
@@ -15,11 +15,15 @@
 </template>
 
 <script lang="ts" setup>
+import { shallowRef } from 'vue'
 import { Tab, Tabs } from 'vant'
+import { useNavigation } from '@/hooks/navigation'
 import AppDeposit from './components/deposit/index.vue'
 import AppWithdraw from './components/withdraw/index.vue'
 
+const { getQueryStringToNumber } = useNavigation()
 
+const active = shallowRef(getQueryStringToNumber('tab'))
 </script>
 
 <style lang="less">

+ 59 - 0
src/packages/mobile/views/credit/lottery/components/reward/index.less

@@ -0,0 +1,59 @@
+.lottery-reward {
+    display        : flex;
+    justify-content: center;
+    align-items    : center;
+
+    &__wrapper {
+        display        : flex;
+        flex-direction : column;
+        justify-content: space-between;
+        width          : 7.04rem;
+        height         : 9.56rem;
+        background     : url('@mobile/assets/images/lottery/lottery-red-envelope.png') no-repeat;
+        background-size: contain;
+    }
+
+    &__content {
+        font-size  : .72rem;
+        font-weight: bold;
+        text-align : center;
+        color      : #f34868;
+        padding-top: 3.2rem;
+
+        &::after {
+            content  : '元';
+            font-size: large;
+        }
+    }
+
+    &__footer {
+        text-align    : center;
+        padding-bottom: 1.5rem;
+
+        button {
+            width        : 3.64rem;
+            height       : 1.04rem;
+            font-size    : .36rem;
+            font-weight  : bold;
+            color        : #8F4301;
+            background   : linear-gradient(180deg, #FDEFDA 0%, #FFBE7A 100%);
+            box-shadow   : inset 0 -.04rem 1rem 0 #FFDBAC;
+            border-radius: .52rem;
+        }
+
+        .tips {
+            display       : flex;
+            flex-direction: column;
+            color         : #FAF7DE;
+            font-size     : .4rem;
+            font-weight   : bold;
+            margin-bottom : .32rem;
+
+            span {
+                &:first-child {
+                    margin-bottom: .12rem;
+                }
+            }
+        }
+    }
+}

+ 66 - 0
src/packages/mobile/views/credit/lottery/components/reward/index.vue

@@ -0,0 +1,66 @@
+<template>
+    <Overlay class="lottery-reward" :show="showReward">
+        <div class="lottery-reward__wrapper">
+            <div class="lottery-reward__content">
+                <span>{{ value.toFixed(2) }}</span>
+            </div>
+            <div class="lottery-reward__footer">
+                <div class="tips">
+                    <template v-if="value">
+                        <span>恭喜您获得</span>
+                        <span>{{ levelText }}</span>
+                    </template>
+                    <template v-else>
+                        <span>{{ levelText }}</span>
+                    </template>
+                </div>
+                <button type="button" @click="showReward = false">开心收下</button>
+            </div>
+        </div>
+    </Overlay>
+</template>
+
+<script lang="ts" setup>
+import { computed } from 'vue'
+import { Overlay } from 'vant'
+
+const props = defineProps({
+    show: {
+        type: Boolean,
+        default: false
+    },
+    value: {
+        type: Number,
+        default: 0
+    },
+    level: {
+        type: Number,
+        default: 0
+    },
+})
+
+const emit = defineEmits(['update:show'])
+
+const showReward = computed({
+    get: () => props.show,
+    set: (val) => emit('update:show', val)
+})
+
+const levelText = computed(() => {
+    switch (props.level) {
+        case 1: {
+            return '一等奖'
+        }
+        case 2: {
+            return '二等奖'
+        }
+        default: {
+            return '谢谢参与'
+        }
+    }
+})
+</script>
+
+<style lang="less">
+@import './index.less';
+</style>

+ 164 - 0
src/packages/mobile/views/credit/lottery/index.less

@@ -0,0 +1,164 @@
+.credit-lottery {
+    background     : url('@mobile/assets/images/lottery/lottery-bg.png') repeat-y center top;
+    background-size: 100% auto;
+
+    .app-navbar {
+        &__backbutton {
+            >.van-icon {
+                color: #fff;
+            }
+        }
+
+        &__wrapper {
+            color           : #fff;
+            background-color: transparent;
+        }
+    }
+
+    &__title {
+        text-align : center;
+        padding-top: .48rem;
+
+        img {
+            width: 4.54rem;
+        }
+    }
+
+    &__container {
+        position      : relative;
+        padding       : .32rem;
+        padding-bottom: 1.5rem;
+
+        &::after {
+            content        : '';
+            position       : absolute;
+            left           : 0;
+            bottom         : 0;
+            width          : 1.36rem;
+            height         : 2.08rem;
+            background     : url('@mobile/assets/images/lottery/lottery-money.png') no-repeat left top;
+            background-size: contain;
+        }
+    }
+
+    &__container &__box {
+        background-color: #FEB15B;
+        border-radius   : .32rem;
+        padding         : .2rem;
+
+        &:not(:first-child) {
+            margin-top: .32rem;
+        }
+    }
+
+    &__draw {
+        .block {
+            background   : linear-gradient(180deg, #FFE8A2 0%, #FFD478 100%);
+            box-shadow   : 0 .04rem .12rem 0 #FCA442;
+            border-radius: .32rem;
+            padding      : .36rem;
+
+            &-prize {
+                display    : flex;
+                align-items: center;
+                gap        : .2rem;
+
+                li {
+                    position        : relative;
+                    z-index         : 1;
+                    flex            : 1;
+                    display         : flex;
+                    flex-direction  : column;
+                    justify-content : center;
+                    align-items     : center;
+                    height          : 1.8rem;
+                    color           : #FF4E1F;
+                    background-color: #F5D484;
+                    border-radius   : .32rem;
+                    padding         : .2rem;
+                    padding-bottom  : .28rem;
+
+                    &::before {
+                        position        : absolute;
+                        z-index         : -1;
+                        bottom          : .06rem;
+                        content         : '';
+                        width           : 100%;
+                        height          : 100%;
+                        background-color: #fff;
+                        border-radius   : .32rem;
+                    }
+
+                    span {
+                        white-space: nowrap;
+
+                        &:first-child {
+                            font-size  : .36rem;
+                            font-weight: bold;
+                        }
+                    }
+                }
+            }
+
+            &-button {
+                margin: .36rem 0 .16rem 0;
+
+                button {
+                    position        : relative;
+                    z-index         : 1;
+                    width           : 100%;
+                    height          : .9rem;
+                    line-height     : 1;
+                    font-size       : .36rem;
+                    color           : #fff;
+                    background-color: #DC3414;
+                    border-radius   : .45rem;
+                    padding-bottom  : .08rem;
+
+                    &::before {
+                        position     : absolute;
+                        z-index      : -1;
+                        bottom       : .08rem;
+                        content      : '';
+                        width        : 100%;
+                        height       : 100%;
+                        background   : linear-gradient(180deg, #FF8308 0%, #FF5331 100%);
+                        border-radius: .45rem;
+                    }
+                }
+            }
+
+            &-info {
+                display        : flex;
+                justify-content: space-between;
+                align-items    : center;
+                color          : #E84B2D;
+            }
+        }
+    }
+
+    &__record {
+        .block {
+            background-color: #fff;
+            box-shadow      : 0 .04rem .08rem 0 #FCA849;
+            border-radius   : .32rem;
+            padding         : .36rem;
+
+            &-title {
+                font-size : .36rem;
+                text-align: center;
+                color     : #AC1C00;
+            }
+
+            &-item {
+                display        : flex;
+                justify-content: space-between;
+                align-items    : center;
+                color          : #AC1C00;
+                font-size      : .32rem;
+                border-bottom  : 1px solid #eee;
+                padding        : .24rem 0;
+            }
+        }
+    }
+}

+ 80 - 0
src/packages/mobile/views/credit/lottery/index.vue

@@ -0,0 +1,80 @@
+<template>
+    <app-view class="credit-lottery">
+        <template #header>
+            <app-navbar title="积分红包" />
+        </template>
+        <div class="credit-lottery__title">
+            <img src="@mobile/assets/images/lottery/lottery-title.png" />
+        </div>
+        <div class="credit-lottery__container">
+            <div class="credit-lottery__box credit-lottery__draw">
+                <div class="block">
+                    <ul class="block-prize">
+                        <li>
+                            <span>一等奖</span>
+                            <span>现金红包</span>
+                        </li>
+                        <li>
+                            <span>二等奖</span>
+                            <span>现金红包</span>
+                        </li>
+                        <li>
+                            <span>谢谢参与</span>
+                        </li>
+                    </ul>
+                    <div class="block-button">
+                        <button type="button" @click="onSubmit">立即抽奖</button>
+                    </div>
+                    <div class="block-info">
+                        <span>当前积分:{{ userAccount.curscore ?? 0 }}</span>
+                        <span>*抽一次消耗{{ creditConfig.parma1 ?? 0 }}积分</span>
+                    </div>
+                </div>
+            </div>
+            <div class="credit-lottery__box credit-lottery__record">
+                <dl class="block">
+                    <dt class="block-title">- 我的红包 -</dt>
+                    <dd class="block-item" v-for="(item, index) in creditList" :key="index">
+                        <span>{{ item.createtime }}</span>
+                        <span>{{ item.redpacketvalue }}</span>
+                    </dd>
+                </dl>
+            </div>
+        </div>
+        <Reward v-model:show="showReward" :value="redEnvelope.RedPacketAmount" :level="redEnvelope.GearLevel" />
+    </app-view>
+</template>
+
+<script lang="ts" setup>
+import { shallowRef } from 'vue'
+import { Toast } from 'vant'
+import { fullloading } from '@/utils/vant'
+import { useCreditLottery } from '@/business/credit'
+import Reward from './components/reward/index.vue'
+
+const { creditConfig, userAccount, creditList, redEnvelope, formSubmit, formRefresh } = useCreditLottery()
+const showReward = shallowRef(false) // 显示抽奖红包
+
+const onSubmit = () => {
+    const { curscore = 0 } = userAccount.value
+    const { parma1 = 0 } = creditConfig.value
+    if (curscore < parma1) {
+        Toast.fail('可用积分不足')
+    } else {
+        fullloading((hideLoading) => {
+            formSubmit().then(() => {
+                hideLoading()
+                showReward.value = true
+            }).catch(() => {
+                Toast.fail('抽奖失败')
+            })
+        })
+    }
+}
+
+formRefresh()
+</script>
+
+<style lang="less">
+@import './index.less';
+</style>

+ 1 - 1
src/packages/mobile/views/credit/signin/index.less

@@ -1,4 +1,4 @@
-.mine-signin {
+.credit-signin {
     @backgroundImage: linear-gradient(#FF9145, #FF512F 3.9rem, transparent 3.9rem);
 
     .app-navbar {

+ 6 - 5
src/packages/mobile/views/credit/signin/index.vue

@@ -1,10 +1,10 @@
 <template>
-    <app-view class="mine-signin">
+    <app-view class="credit-signin">
         <template #header>
             <app-navbar title="我的任务" @ready="onReady" />
         </template>
-        <div ref="headerRef" class="mine-signin__header">
-            <div class="mine-signin__header-wrapper">
+        <div ref="headerRef" class="credit-signin__header">
+            <div class="credit-signin__header-wrapper">
                 <div class="integral">
                     <div class="integral-title">
                         <app-iconfont icon="icon-jifenyue">积分余额</app-iconfont>
@@ -19,7 +19,8 @@
                             <app-iconfont icon="icon-qiandao" label-direction="bottom">签到</app-iconfont>
                         </li>
                         <li>
-                            <app-iconfont icon="icon-jifenchoujiang" label-direction="bottom">积分红包</app-iconfont>
+                            <app-iconfont icon="icon-jifenchoujiang" label-direction="bottom"
+                                @click="routerTo('credit-lottery')">积分红包</app-iconfont>
                         </li>
                         <li>
                             <app-iconfont icon="icon-jifenliushui" label-direction="bottom"
@@ -29,7 +30,7 @@
                 </div>
             </div>
         </div>
-        <div class="mine-signin__task">
+        <div class="credit-signin__task">
             <dl class="list">
                 <dt class="list-title">积分任务</dt>
                 <dd class="list-item">

+ 92 - 6
src/packages/mobile/views/goods/details/index.vue

@@ -3,22 +3,108 @@
         <template #header>
             <app-navbar title="采购详情" />
         </template>
+        <Form ref="formRef" @submit="onSubmit">
+            <CellGroup>
+                <app-select v-model="formData.THJDeliveryMode" name="THJDeliveryMode" label="交割方式"
+                    :rules="formRules.THJDeliveryMode" :options="details.deliverymodes"
+                    :optionProps="{ label: 'enumdicname', value: 'enumitemname' }" />
+                <Field label="交割月份">
+                    <template #input>
+                        <app-select placeholder="开始月份" :options="deliveryMonths" :is-link="false"
+                            @confirm="onMonthChange" />
+                        <app-select v-model="presaleApplyId" placeholder="结束日期" :options="deliveryDays"
+                            :optionProps="{ label: 'enddate', value: 'presaleapplyid' }" :is-link="false"
+                            @confirm="onDayChange" />
+                    </template>
+                </Field>
+                <app-select v-model="formData.DepositID" name="DepositID" label="支付方式" :rules="formRules.DepositID"
+                    :options="presaleApplyDeposits" />
+                <Field v-model="formData.Qty" name="Qty" type="digit" label="采购数量" placeholder="必填"
+                    :rules="formRules.Qty" />
+            </CellGroup>
+        </Form>
+        <Button type="primary" @click="formRef?.submit" round block>采购下单</Button>
     </app-view>
 </template>
 
 <script lang="ts" setup>
+import { shallowRef, computed } from 'vue'
+import { CellGroup, Cell, Button, Field, Form, FormInstance, Checkbox, Toast, FieldRule, Popup, Picker, PickerOption } from 'vant'
+import { fullloading, dialog } from '@/utils/vant'
 import { useNavigation } from '@/hooks/navigation'
 import { useWrstandardDetails } from '@/business/goods'
 import { usePurchaseOrderDesting } from '@/business/trade'
+import AppSelect from '@mobile/components/base/select/index.vue'
+import Long from 'long'
 
-const props = defineProps({
-    wrstandardid: {
-        type: Number,
-        required: true
-    }
+const { getQueryStringToNumber } = useNavigation()
+const wrstandardid = getQueryStringToNumber('wrstandardid')
+const formRef = shallowRef<FormInstance>()
+
+const { details, getWrstandardDetails } = useWrstandardDetails(wrstandardid)
+const { formData, formSubmit } = usePurchaseOrderDesting()
+
+// 表单验证规则
+const formRules: { [key in keyof Proto.SpotPresaleDestingOrderReq]?: FieldRule[] } = {
+    THJDeliveryMode: [{
+        message: '请选择交割方式',
+        validator: () => {
+            return !!formData.THJDeliveryMode
+        }
+    }],
+}
+
+// 交割月份列表
+const deliveryMonths = computed(() => {
+    const months = details.value.deliverymonth ?? []
+    const monthMap = new Map<string, { label: string, value: string; }>()
+    months.forEach(({ endmonth }) => {
+        if (!monthMap.has(endmonth)) {
+            monthMap.set(endmonth, {
+                label: endmonth,
+                value: endmonth,
+            })
+        }
+    })
+    return [...monthMap.values()]
+})
+
+// 交割日期列表
+const deliveryDays = shallowRef<Model.THJWrstandardDetailRsp['deliverymonth']>([])
+// 当前选中的预售申请ID
+const presaleApplyId = shallowRef('')
+
+// 预售申请列表
+const presaleApplyDeposits = computed(() => {
+    const deposits = details.value.presaleapplydeposits ?? []
+    return deposits.map(({ depositrate, discountamount }) => ({
+        label: `${depositrate},${discountamount}`,
+        value: discountamount
+    }))
 })
 
-const { details, getWrstandardDetails } = useWrstandardDetails(props.wrstandardid)
+// 切换交割月份
+const onMonthChange = (value: string) => {
+    const months = details.value.deliverymonth ?? []
+    deliveryDays.value = months.filter((e) => e.endmonth === value)
+    presaleApplyId.value = ''
+}
+
+// 切换交割日期
+const onDayChange = (value: string) => {
+    presaleApplyId.value = value
+}
+
+const onSubmit = () => {
+    fullloading(() => {
+        formData.PresaleApplyID = Long.fromString(presaleApplyId.value)
+        formSubmit().then(() => {
+            Toast.success('下单成功')
+        }).catch((err) => {
+            Toast.fail(err)
+        })
+    })
+}
 
 getWrstandardDetails()
 </script>

+ 27 - 1
src/packages/mobile/views/home/components/main/index.less

@@ -12,9 +12,11 @@
     }
 
     &__banner {
-        background-color: #999;
+        background-color: #fff;
 
         .van-swipe {
+            background-color: #999;
+
             &-item {
                 font-size: 0;
 
@@ -25,6 +27,30 @@
                 }
             }
         }
+
+        &--header {
+            .van-swipe {
+                min-height: 3rem;
+
+                &-item {
+                    height: 3rem;
+                }
+            }
+        }
+
+        &--body {
+            padding    : .24rem .32rem;
+            padding-top: 0;
+
+            .van-swipe {
+                min-height   : 1.5rem;
+                border-radius: .16rem;
+
+                &-item {
+                    height: 1.5rem;
+                }
+            }
+        }
     }
 
     &__iconbar {

+ 7 - 7
src/packages/mobile/views/home/components/main/index.vue

@@ -3,9 +3,9 @@
     <template #header>
       <app-navbar title="铁合金掌上行" :show-back-button="false" />
     </template>
-    <div class="home-main__banner">
-      <Swipe :autoplay="3000" indicator-color="white" lazy-render style="min-height:3rem">
-        <SwipeItem v-for="(item, index) in topBanners" :key="index" style="height:3rem">
+    <div class="home-main__banner home-main__banner--header">
+      <Swipe :autoplay="3000" indicator-color="white" lazy-render>
+        <SwipeItem v-for="(item, index) in topBanners" :key="index">
           <img :src="getImageUrl(item.imagepath)" />
         </SwipeItem>
       </Swipe>
@@ -35,9 +35,9 @@
           </li>
         </ul>
       </div>
-      <div class="home-main__banner">
-        <Swipe :autoplay="3000" indicator-color="white" lazy-render style="min-height:1.5rem">
-          <SwipeItem v-for="(item, index) in bodyBanners" :key="index" style="height:1.5rem">
+      <div class="home-main__banner home-main__banner--body">
+        <Swipe :autoplay="3000" indicator-color="white" lazy-render>
+          <SwipeItem v-for="(item, index) in bodyBanners" :key="index">
             <img :src="getImageUrl(item.imagepath)" />
           </SwipeItem>
         </Swipe>
@@ -66,7 +66,7 @@
           <template v-for="(item, index) in newsList" :key="index">
             <Cell class="article-item" title-class="article-item__title" value-class="article-item__time"
               :title="item.title" :value="formatDate(item.creaedate, 'MM/DD')"
-              @click="routerTo('news-details', { details: JSON.stringify(item) })" />
+              :to="{ name: 'news-details', params: { details: JSON.stringify(item) } }" />
           </template>
         </CellGroup>
       </div>

+ 3 - 12
src/packages/mobile/views/home/components/mine/index.less

@@ -45,24 +45,15 @@
                     display       : flex;
                     flex-direction: column;
 
-                    span {
+                    >span {
                         line-height: .4rem;
-
-                        &:first-child {
-                            font-size  : .3rem;
-                            font-weight: bold;
-                        }
-
-                        &:last-child {
-                            font-size: .24rem;
-                            color    : #A1B1C5;
-                        }
+                        font-size  : .3rem;
+                        font-weight: bold;
                     }
                 }
             }
 
             &-account {
-                flex           : 1;
                 display        : flex;
                 flex-direction : column;
                 justify-content: center;

+ 70 - 16
src/packages/mobile/views/home/components/mine/index.vue

@@ -11,7 +11,9 @@
               <img class="g-image--avatar" src="" />
             </div>
             <div class="profile-user__info">
-              <span>{{ getUserId() }}</span>
+              <div>
+                <Tag :type="authStatus ? 'success' : 'primary'">{{ getAuthStatusName(authStatus) }}</Tag>
+              </div>
               <span>{{ getLoginId() }}</span>
             </div>
           </div>
@@ -27,11 +29,11 @@
           </div>
           <div class="bank-item">
             <span>冻结</span>
-            <span>{{ accountInfo.freezemargin }}</span>
+            <span>{{ freezeMargin }}</span>
           </div>
           <div class="bank-item">
             <span>可用</span>
-            <span>{{ accountInfo.usedmargin }}</span>
+            <span>{{ avaiableMoney }}</span>
           </div>
           <div class="bank-item">
             <span>
@@ -41,8 +43,8 @@
           </div>
         </div>
         <div class="button">
-          <Button size="small" @click="routerTo('bank-wallet')" round>提现</Button>
-          <Button type="primary" size="small" color="#00577C" @click="routerTo('bank-wallet')" round>充值</Button>
+          <Button size="small" @click="doInOutMoney('1')" round>提现</Button>
+          <Button type="primary" size="small" color="#00577C" @click="doInOutMoney('0')" round>充值</Button>
         </div>
       </div>
     </div>
@@ -51,7 +53,7 @@
         <li @click="routerTo('mine-generalize')" style="align-items:flex-start">
           <app-iconfont icon="icon-wodetuiguang" label-direction="bottom">我的推广</app-iconfont>
         </li>
-        <li>
+        <!-- <li>
           <app-iconfont icon="icon-wodedingdan" label-direction="bottom">我的订单</app-iconfont>
         </li>
         <li>
@@ -62,17 +64,18 @@
         </li>
         <li @click="routerTo('mine-address')">
           <app-iconfont icon="icon-shouhuodizhi" label-direction="bottom">收货地址</app-iconfont>
-        </li>
+        </li> -->
       </ul>
     </div>
     <div class="g-navmenu">
       <CellGroup>
-        <Cell is-link  :to="{ name: 'account-certification' }">
+        <Cell is-link :to="{ name: 'account-certification' }"
+          v-if="[AuthStatus.Uncertified, AuthStatus.Rejected].includes(authStatus)">
           <template #title>
             <app-iconfont icon="icon-shimingrenzheng">实名认证</app-iconfont>
           </template>
         </Cell>
-        <Cell is-link :to="{ name: 'bank-sign' }">
+        <Cell is-link :to="{ name: 'bank-sign' }" v-if="authStatus === AuthStatus.Certified">
           <template #title>
             <app-iconfont icon="icon-qianyuezhanghu">签约账户</app-iconfont>
           </template>
@@ -116,18 +119,23 @@
 </template>
 
 <script lang="ts" setup>
-import { shallowRef } from 'vue'
-import { Cell, CellGroup, Button } from 'vant'
-import { dialog } from '@/utils/vant'
+import { shallowRef, onActivated } from 'vue'
+import { Cell, CellGroup, Button, Tag, Toast } from 'vant'
+import { fullloading, dialog } from '@/utils/vant'
 import { useNavigation } from '@/hooks/navigation'
-import { useLoginStore, useAccountStore } from '@/stores'
+import { useLoginStore, useAccountStore, } from '@/stores'
 import { useAuth } from '@/business/auth'
 import AppIconfont from '@mobile/components/base/iconfont/index.vue'
+import { useBankAccountSign } from '@/business/bank'
+import { queryUserAccount } from '@/services/api/account'
+import { AuthStatus, getAuthStatusName } from '@/constants/account';
 
-const { routerTo } = useNavigation()
-const { getLoginId, getUserId, getFirstAccountId } = useLoginStore()
+const { router, routerTo } = useNavigation()
+const { getUserId, getLoginId, getFirstAccountId } = useLoginStore()
+const { getBankAccountList, bankInfo } = useBankAccountSign()
 const { logout } = useAuth()
-const { accountInfo } = useAccountStore()
+const { accountInfo, freezeMargin, avaiableMoney } = useAccountStore()
+const authStatus = shallowRef(AuthStatus.Uncertified) // 实名认证状态
 const headerRef = shallowRef<HTMLDivElement>()
 
 const onReady = (el: HTMLDivElement) => {
@@ -135,6 +143,38 @@ const onReady = (el: HTMLDivElement) => {
   headerRef.value?.style.setProperty('background-position', `0 -${el.clientHeight}px`)
 }
 
+/// 进行出入金操作判断
+const doInOutMoney = (tab: string) => {
+  if (authStatus.value === AuthStatus.Certified) {
+    fullloading((hideLoading) => {
+      getBankAccountList().then(() => {
+        hideLoading()
+        const { signstatus } = bankInfo.value ?? {}
+        /// 只有已签约的情况下才可以进行出入金
+        if (signstatus && signstatus === 4) {
+          router.push({ name: 'bank-wallet', query: { tab } })
+        } else {
+          dialog('请先添加签约账户信息。', {
+            showCancelButton: true,
+            confirmButtonText: '去签约'
+          }).then(() => {
+            router.push({ name: 'bank-sign' })
+          })
+        }
+      }).catch(() => {
+        Toast.fail('加载失败')
+      })
+    }, '正在加载...')
+  } else {
+    dialog('请先实名认证,再进行出入金操作业务。', {
+      showCancelButton: true,
+      confirmButtonText: '去实名'
+    }).then(() => {
+      router.push({ name: 'account-certification' })
+    })
+  }
+}
+
 const userLogout = () => {
   dialog('是否退出当前账号?', {
     showCancelButton: true
@@ -142,6 +182,20 @@ const userLogout = () => {
     logout()
   })
 }
+
+onActivated(() => {
+  if (authStatus.value !== AuthStatus.Certified) {
+    // 获取用户账号信息
+    queryUserAccount({
+      data: {
+        userID: getUserId()
+      },
+      success: (res) => {
+        authStatus.value = res.data.hasauth
+      }
+    })
+  }
+})
 </script>
 
 <style lang="less">

+ 1 - 1
src/packages/mobile/views/home/components/purchase/index.vue

@@ -10,7 +10,7 @@
     <app-pull-refresh class="home-purchase__container" v-model:error="error" v-model:pageIndex="pageIndex"
       :page-count="pageCount" :updateList="dataList" @refresh="onRefresh">
       <template #default="{ item }">
-        <Cell @click="$router.push({ name: 'goods-details' })">
+        <Cell @click="$router.push({ name: 'goods-details', query: { wrstandardid: item.wrstandardid } })">
           <template #title>
             <img :src="getImageUrl(item.thumurls)" />
             <span>{{ item.wrstandardname }}</span>

+ 1 - 1
src/packages/mobile/views/home/index.vue

@@ -59,7 +59,7 @@ const tabList: Tabbar[] = [
 ]
 
 const onChange = (index: number) => {
-  if (index !== 2) {
+  if (![1, 2].includes(index)) {
     componentId.value = tabList[index].name
   }
 }

+ 2 - 2
src/packages/mobile/views/news/details/index.vue

@@ -9,7 +9,7 @@
                 <span>{{ details.author }}</span>
                 <span>{{ details.creaedate }}</span>
             </h4>
-            <p v-html="formatText(details.context)"></p>
+            <p v-html="formatHtmlString(details.context)"></p>
         </section>
     </app-view>
 </template>
@@ -17,7 +17,7 @@
 <script lang="ts" setup>
 import { shallowRef } from 'vue'
 import { useNavigation } from '@/hooks/navigation'
-import { formatText } from '@/filters'
+import { formatHtmlString } from '@/filters'
 
 const { route } = useNavigation()
 const params = route.params.details

+ 1 - 1
src/packages/mobile/views/rules/ccwl/index.vue

@@ -19,6 +19,6 @@
 </template>
 
 <script lang="ts" setup>
-import { Tab, Tabs, Button, Empty } from 'vant'
+import { Tab, Tabs, Empty } from 'vant'
 import AppHtml from '@mobile/components/base/html-panel/index.vue'
 </script>

+ 18 - 5
src/packages/mobile/views/user/login/index.vue

@@ -9,8 +9,8 @@
           :rules="[{ required: true, message: '请输入密码' }]" autocomplete="off" />
       </CellGroup>
       <div class="button-link">
-        <router-link :to="{ name: 'register' }">用户注册</router-link>
-        <router-link :to="{ name: 'forget' }">忘记密码</router-link>
+        <span @click="navigationTo('register')">用户注册</span>
+        <span @click="navigationTo('forget')">忘记密码</span>
       </div>
       <div class="button-submit">
         <Button type="primary" native-type="submit" round block>登录</Button>
@@ -19,9 +19,9 @@
     <div class="login-footer">
       <Checkbox v-model="checked" />
       <span>我已阅读并同意</span>
-      <span @click="routerTo('rules-zcxy')" style="color:#E92020">《用户注册协议》</span>
+      <span @click="navigationTo('rules-zcxy')" style="color:#E92020">《用户注册协议》</span>
       <span>和</span>
-      <span @click="routerTo('rules-yszc')" style="color:#E92020">《隐私政策》</span>
+      <span @click="navigationTo('rules-yszc')" style="color:#E92020">《隐私政策》</span>
     </div>
   </app-statusbar>
 </template>
@@ -33,6 +33,7 @@ import { Button, Field, CellGroup, Form, Checkbox, Toast } from 'vant'
 import { fullloading } from '@/utils/vant'
 import { useAuth } from '@/business/auth'
 import { useNavigation } from '@/hooks/navigation'
+import service from '@/services'
 
 const { routerTo } = useNavigation()
 const { user, login } = useAuth()
@@ -40,6 +41,18 @@ const route = useRoute()
 const router = useRouter()
 const checked = shallowRef(false) // 是否同意协议管理
 
+// 导航跳转
+const navigationTo = (name: string) => {
+  fullloading((hideLoading) => {
+    service.onReady().then(() => {
+      hideLoading()
+      routerTo(name)
+    }).catch(() => {
+      Toast.fail('初始化失败')
+    })
+  })
+}
+
 const formSubmit = () => {
   if (checked.value) {
     fullloading((hideLoading) => {
@@ -54,7 +67,7 @@ const formSubmit = () => {
       }).catch((err) => {
         Toast.fail(err)
       })
-    })
+    }, '登录中...')
   } else {
     Toast('请先同意使用条款')
   }

+ 48 - 0
src/packages/mobile/views/user/register/components/reward/index.less

@@ -0,0 +1,48 @@
+.register-reward {
+    display        : flex;
+    justify-content: center;
+    align-items    : center;
+
+    &__wrapper {
+        position : relative;
+        font-size: 200%;
+        color    : #ffff00;
+
+        .content {
+            position  : absolute;
+            top       : 40%;
+            width     : 100%;
+            text-align: center;
+            transition: transform 500ms;
+
+            &.is-hide {
+                transform: scale(0)
+            }
+
+            &.is-show {
+                transform: scale(1)
+            }
+
+            h1 {
+                font-size  : .8em;
+                font-weight: bold;
+            }
+
+            p {
+                font-size  : 1.3em;
+                font-weight: bold;
+            }
+        }
+
+        .buttons {
+            position  : absolute;
+            bottom    : 12%;
+            width     : 100%;
+            text-align: center;
+
+            .van-button {
+                width: 50%;
+            }
+        }
+    }
+}

+ 42 - 0
src/packages/mobile/views/user/register/components/reward/index.vue

@@ -0,0 +1,42 @@
+<template>
+    <Overlay class="register-reward" :show="show">
+        <div class="register-reward__wrapper">
+            <img src="@mobile/assets/images/hongbao.png" />
+            <div class="content" v-if="!showRedEnvelope">
+                <h1>注册成功!</h1>
+            </div>
+            <div :class="['content', showRedEnvelope ? 'is-show' : 'is-hide']">
+                <h1>恭喜获得红包</h1>
+                <p>{{ value }}</p>
+            </div>
+            <div class="buttons">
+                <Button @click="router.back()" round v-if="showRedEnvelope">好的</Button>
+                <Button @click="showRedEnvelope = true" round v-else>打开红包</Button>
+            </div>
+        </div>
+    </Overlay>
+</template>
+
+<script lang="ts" setup>
+import { shallowRef } from 'vue'
+import { Button, Overlay } from 'vant'
+import { useNavigation } from '@/hooks/navigation'
+
+defineProps({
+    show: {
+        type: Boolean,
+        default: false
+    },
+    value: {
+        type: Number,
+        default: 0
+    }
+})
+
+const { router } = useNavigation()
+const showRedEnvelope = shallowRef(false)
+</script>
+
+<style lang="less" scoped>
+@import './index.less';
+</style>

+ 0 - 39
src/packages/mobile/views/user/register/index.less

@@ -9,43 +9,4 @@
             }
         }
     }
-
-    &__reward {
-        display        : flex;
-        justify-content: center;
-        align-items    : center;
-
-        .wrapper {
-            display        : flex;
-            flex-direction : column;
-            align-items    : center;
-            justify-content: center;
-            width          : 100%;
-            height         : 100%;
-            color          : #ffff00;
-            background     : url('@mobile/assets/images/hongbao.png') no-repeat center center;
-            background-size: 100%;
-            padding-top    : 2.5rem;
-
-            .title {
-                font-size  : .4rem;
-                font-weight: bold;
-            }
-
-            .details {
-                font-size  : .72rem;
-                font-weight: bold;
-            }
-
-            .buttons {
-                width     : 100%;
-                text-align: center;
-                margin-top: 1.8rem;
-
-                .van-button {
-                    width: 50%;
-                }
-            }
-        }
-    }
 }

+ 4 - 12
src/packages/mobile/views/user/register/index.vue

@@ -43,30 +43,22 @@
       <div class="g-form__footer">
         <Button type="primary" @click="formRef?.submit" round block>免费注册</Button>
       </div>
-      <Overlay class="register__reward" :show="showReward">
-        <div class="wrapper">
-          <div class="title">注册成功!</div>
-          <div class="title">恭喜获得红包</div>
-          <div class="details">{{ redEnvelope }}</div>
-          <div class="buttons">
-            <Button @click="router.back()" round>好的</Button>
-          </div>
-        </div>
-      </Overlay>
+      <Reward :show="showReward" :value="redEnvelope" />
     </template>
   </app-view>
 </template>
 
 <script lang="ts" setup>
 import { reactive, ref, computed } from 'vue'
-import { CellGroup, Cell, Button, Field, Form, FormInstance, Checkbox, Toast, FieldRule, Overlay } from 'vant'
+import { CellGroup, Cell, Button, Field, Form, FormInstance, Checkbox, Toast, FieldRule } from 'vant'
 import { useCountDown } from '@vant/use'
 import { fullloading, dialog } from '@/utils/vant'
 import { validateRules } from '@/constants/regex'
 import { useNavigation } from '@/hooks/navigation'
 import { userRegister, sendRegisterVerifyCode, queryMyRegisterMoney } from '@/services/api/common'
-// import AppQrcodeScan from '@mobile/components/base/qrcode-scan/index.vue'
 import cryptojs from 'crypto-js'
+import Reward from './components/reward/index.vue'
+// import AppQrcodeScan from '@mobile/components/base/qrcode-scan/index.vue'
 
 const { router, routerTo } = useNavigation()
 const formRef = ref<FormInstance>()

+ 9 - 2
src/services/api/bank/index.ts

@@ -4,16 +4,23 @@ import { tradeServerRequest } from '@/services/socket/trade'
 import { TradeParams } from '@/services/socket/trade/interface'
 
 /**
+ * 账户资金信息请求
+ */
+export function accountFundInfoReq(params: TradeParams<Partial<Proto.AccountFundInfoReq>, Proto.AccountFundInfoRsp>) {
+    return tradeServerRequest('AccountFundInfoReq', 'AccountFundInfoRsp', params);
+}
+
+/**
  * 银行签约
  */
-export function t2bBankSign(params: TradeParams<Partial<Proto.t2bBankSignReq>, Proto.t2bBankSignRsp>) {
+ export function t2bBankSign(params: TradeParams<Partial<Proto.t2bBankSignReq>, Proto.t2bBankSignRsp>) {
     return tradeServerRequest('t2bBankSignReq', 't2bBankSignRsp', params);
 }
 
 /**
  * 银行解约
  */
-export function t2bBankCancelSign(params: TradeParams<Proto.t2bBankCancelSignReq, Proto.t2bBankCancelSignRsp>) {
+export function t2bBankCancelSign(params: TradeParams<Partial<Proto.t2bBankCancelSignReq>, Proto.t2bBankCancelSignRsp>) {
     return tradeServerRequest('t2bBankCancelSignReq', 't2bBankCancelSignRsp', params);
 }
 

+ 7 - 0
src/services/api/common/index.ts

@@ -44,6 +44,13 @@ export function sendResetVerifyCode(params: CommonParams<{ req: Model.SendResetV
 }
 
 /**
+ * 文件上传
+ */
+ export function uploadFile(params: CommonParams<{ req: Model.uploadFileReq, rsp: Model.uploadFileRsp }>) {
+    return commonRequest(getServiceUrl('uploadUrl'), 'post', params);
+}
+
+/**
  * 查询轮播图配置信息
  */
 export function queryImageConfigs(params: HttpParams<{ req: Model.ImageConfigsReq, rsp: Model.ImageConfigsRsp[] }>) {

+ 17 - 0
src/services/api/credit/index.ts

@@ -1,9 +1,26 @@
+import { Market } from '@/constants/market';
 import { httpRequest } from '@/services/http'
 import { HttpParams } from '@/services/http/interface'
+import { tradeServerRequest } from '@/services/socket/trade'
+import { TradeParams } from '@/services/socket/trade/interface'
 
 /**
  * 查询我的积分流水
  */
 export function queryUserScoreLog(params: HttpParams<{ req: Model.UserScoreLogReq, rsp: Model.UserScoreLogRsp[] }>) {
     return httpRequest('/Ferroalloy/QueryUserScoreLog', 'get', params);
+}
+
+/**
+ * 查询积分配置
+ */
+export function queryTHJScoreConfig(params: HttpParams<{ req: Model.THJScoreConfigReq, rsp: Model.THJScoreConfigRsp[] }>) {
+    return httpRequest('/Ferroalloy/QueryTHJScoreConfig', 'get', params);
+}
+
+/**
+ * 铁合金抽奖
+ */
+export function thjRedPacketLottery(params: TradeParams<Proto.THJRedPacketLotteryReq, Proto.THJRedPacketLotteryRsp>) {
+    return tradeServerRequest('THJRedPacketLotteryReq', 'THJRedPacketLotteryRsp', params, Market.THJ_Listing);
 }

+ 1 - 1
src/services/api/goods/index.ts

@@ -18,6 +18,6 @@ export function queryTHJWrstandard(params: HttpParams<{ req: Model.THJWrstandard
 /**
  * 查询采购详情
  */
-export function queryTHJWrstandardDetail(params: HttpParams<{ req: Model.THJWrstandardDetailReq, rsp: Model.THJWrstandardDetailRsp[] }>) {
+export function queryTHJWrstandardDetail(params: HttpParams<{ req: Model.THJWrstandardDetailReq, rsp: Model.THJWrstandardDetailRsp }>) {
     return httpRequest('/Ferroalloy/QueryTHJWrstandardDetail', 'get', params);
 }

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

@@ -6,6 +6,7 @@ export enum EventKey {
     QuoteServerReconnectNotify, // 行情服务重连成功通知
     LogoutNotify, // 用户登出通知
     MoneyChangedNotify, // 资金变动通知
+    UserChangeNtf,       // 账户变更通知
 }
 
 /**

+ 7 - 0
src/services/socket/index.ts

@@ -47,6 +47,13 @@ export default new (class {
                     }, delay, funCode.toString())
                     break;
                 }
+                case FunCode.UserChangeNtf: {  // 账户信息变更通知
+                    timerInterceptor.debounce(() => {
+                        // 账户信息变更通知
+                        eventBus.$emit('UserChangeNtf');
+                    }, delay, funCode.toString())
+                    break;
+                } 
                 case FunCode.LogoutRsp: {
                     // 通知上层 用户登出
                     protobuf.responseDecode<Proto.LogoutRsp>(FunCode[funCode], content).then(({ RetCode, RetDesc }) => {

+ 1 - 1
src/services/socket/trade/index.ts

@@ -97,7 +97,7 @@ export async function tradeServerRequest<Req, Rsp>(reqKey: keyof typeof FunCode,
             }
             default: {
                 // 银行 业务 以 Status 作为判断依据
-                if (Status === 0) {
+                if (Status === 0 || Status == 6007) {
                     success && success(res);
                     return Promise.resolve();
                 }

+ 20 - 4
src/stores/modules/account.ts

@@ -28,11 +28,27 @@ const store = new (class extends VueStore<StoreState> {
         })
     }
 
+    /** 当前资金账户信息 */
+    private accountInfo = computed(() => {
+        return this.state.accountList.find((e) => e.accountid === this.state.accountId)
+    })
+
+    /** 冻结资金 */
+    private freezeMargin = computed(() => {
+        const { freezecharge = 0, freezemargin = 0, otherfreezemargin = 0, outamountfreeze = 0 } = this.accountInfo.value ?? {}
+        return freezecharge + freezemargin + otherfreezemargin + outamountfreeze
+    })
+
+    /** 可用资金 */
+    private avaiableMoney = computed(() => {
+        const { currentbalance = 0 } = this.accountInfo.value ?? {}
+        return currentbalance - this.freezeMargin.value
+    })
+
     getters = {
-        /** 当前资金账户信息 */
-        accountInfo: computed(() => {
-            return this.state.accountList.find((e) => e.accountid === this.state.accountId)
-        })
+        accountInfo: this.accountInfo,
+        freezeMargin: this.freezeMargin,
+        avaiableMoney: this.avaiableMoney
     }
 
     actions = {

+ 1 - 1
src/stores/modules/enum.ts

@@ -17,7 +17,7 @@ interface StoreState {
     allEnums: ShallowRef<Model.EnumRsp[]>;
 }
 
-const enumKeys = ['clientType', 'scoreConfigType', 'accountBusinessCode', 'certificatetype'] as const
+const enumKeys = ['clientType', 'scoreConfigType', 'accountBusinessCode', 'certificatetype', 'signstatus'] as const
 
 /**
  * 枚举存储类

+ 17 - 1
src/stores/modules/user.ts

@@ -1,7 +1,8 @@
-import { toRefs, shallowReadonly } from 'vue'
+import { toRefs, shallowReadonly, computed } from 'vue'
 import { queryLoginData } from '@/services/api/account'
 import { useLoginStore } from './login'
 import { VueStore } from '../base'
+import eventBus from '@/services/bus';
 
 interface StoreState {
     loading: boolean;
@@ -24,6 +25,20 @@ const store = new (class extends VueStore<StoreState>{
             },
         }
         super(state)
+
+        eventBus.$on('UserChangeNtf',()=>{
+            this.actions.getUserData()
+        })
+    }
+
+    /** 是否已实名认证 */
+    private hasAuth = computed(() => {
+        const { userAccount } = this.state.userData
+        return userAccount?.hasauth === 1
+    })
+
+    getters = {
+        hasAuth: this.hasAuth
     }
 
     actions = {
@@ -63,6 +78,7 @@ const store = new (class extends VueStore<StoreState>{
 export function useUserStore() {
     return shallowReadonly({
         ...toRefs(store.state),
+        ...store.getters,
         ...store.actions,
         ...store.methods,
     })

+ 14 - 0
src/types/model/common.d.ts

@@ -128,5 +128,19 @@ declare global {
         interface MyRegisterMoneyRsp {
             amount: number; // 红包
         }
+
+        /** 文件上传请求 */
+        interface uploadFileReq {
+            /// 文件信息
+            data: FormData
+        }
+
+        /** 文件上传回应 */
+        interface uploadFileRsp {
+            /// 上传文件路径
+            filePath?: string,
+            /// 上传文件名
+            fileName?: string
+        }
     }
 }

+ 20 - 0
src/types/model/credit.d.ts

@@ -2,6 +2,7 @@ declare namespace Model {
     /** 查询我的积分流水 请求 */
     interface UserScoreLogReq {
         userid: number; // 用户ID
+        stype?: number; // [格式:1,2,3] 配置类型 - 1:注册红包 2:签到积分 3:推广积分 4:下级用户下单积分 5:自己采购下单积分 6:自己供求下单积分 7:抽奖配置
         page?: number; // 页码
         pagesize?: number; // 每页条数
     }
@@ -9,7 +10,26 @@ declare namespace Model {
     /** 查询我的积分流水 响应 */
     interface UserScoreLogRsp {
         createtime: string; // 记账时间
+        redpacketvalue: number; // 红包值【抽奖】
         score: number; // 变动积分
         scoreconfigtype: number; // 配置类型 - 1:注册红包 2:签到积分 3:推广积分 4:下级用户下单积分 5:自己采购下单积分 6:自己供求下单积分 7:抽奖配置
+        stype: string; // [格式:1,2,3] 配置类型 - 1:注册红包 2:签到积分 3:推广积分 4:下级用户下单积分 5:自己采购下单积分 6:自己供求下单积分 7:抽奖配置
+    }
+
+    /** 查询积分配置 请求 */
+    interface THJScoreConfigReq {
+        stype?: number; // [格式:1,2,3] 配置类型 - 1:注册红包 2:签到积分 3:推广积分 4:下级用户下单积分 5:自己采购下单积分 6:自己供求下单积分 7:抽奖配置
+    }
+
+    /** 查询积分配置 响应 */
+    interface THJScoreConfigRsp {
+        parma1: number; // 参数1
+        parma2: number; // 参数2
+        parma3: number; // 参数3
+        parma4: number; // 参数4
+        parma5: number; // 参数5
+        parma6: number; // 参数6
+        parma7: number; // 参数6
+        scoreconfigtype: number; // 积分配置类型 - 1:注册送红包 2:签到送积分 3:推广送积分 4:下级用户下单送积分 5:自己采购下单送积分 6:自己供求下单送积分 7:抽奖得红包
     }
 }

+ 7 - 7
src/types/model/goods.d.ts

@@ -97,30 +97,30 @@ declare namespace Model {
 
     /** 查询采购详情 响应 */
     interface THJWrstandardDetailRsp {
-        deliveryModes: {
+        deliverymodes: {
             enumdicname: string; // 枚举项名称
             enumitemname: number; // 枚举项值
         }[];
-        deliveryMonth: {
+        deliverymonth: {
             enddate: string; // 预售结束日期(yyyy-mm-dd)
             endmonth: string; // 预售结束月份(yyyy-mm)
             orderqty: number; // 委托数量
-            presaleapplyid: number; // 预售申请ID(184+Unix秒时间戳(10位)+xxxxxx)
+            presaleapplyid: string; // 预售申请ID(184+Unix秒时间戳(10位)+xxxxxx)
             remainqty: number; // 可用数量
             tradeqty: number; // 成交数量
         }[];
-        goodsInfo: {
+        goodsinfo: {
             pictureurls: string; // 详情图片(逗号分隔)
             spotgoodsprice: number; // 现货价格
             wrstandardcode: string; // 现货商品代码
             wrstandardid: number; // 现货商品ID(自增 SEQ_GOODS 确保不重复)
             wrstandardname: string; // 现货商品名称
-        }[];
-        presaleApplyDeposits: {
+        };
+        presaleapplydeposits: {
             depositrate: number; // 定金比例
             discountamount: number; // 优惠金额(每吨)
         }[];
-        spotGoodsPriceLogs: {
+        spotGoodspricelogs: {
             spotgoodsprice: number; // 现货价格
             tradedate: string; // 交易日(yyyyMMdd)
         }[];

+ 20 - 0
src/types/proto/bank.d.ts

@@ -175,5 +175,25 @@ declare global {
             CerterCheckDate: string; // 中心对账日期
             NetAddr: string; // 网络地址(当这里有网址时,应自动跳转网页)
         }
+
+        // 账户资金信息请求 0 4 6
+        interface AccountFundInfoReq {
+            Header?: IMessageHead;
+            OrderId: number;       // uint64 查询委托单号
+            AccountId: number;     // uint64 查询资金账号
+            QueryBitMask: number; // uint32 查询位掩码
+        }
+
+        // 账户资金信息响应 0 4 7
+        interface AccountFundInfoRsp {
+            Header?: IMessageHead;
+            RetCode: number; // int32 返回码
+            RetDesc: string; // string 描述信息
+            OrderId: number; // uint64 查询委托单号
+            AccountId: number; // uint64 查询资金账号
+            AvailableMargin: number; // double 可用保证金,查询位掩码: 1
+            AvailableOutMoney: number; // double 可出资金,查询位掩码: 2
+            IsCleanAccount: number; // int32 是否是干净账户,查询位掩码: 4
+        }
     }
 }

+ 24 - 0
src/types/proto/credit.d.ts

@@ -0,0 +1,24 @@
+import { IMessageHead } from '@/services/socket/trade/protobuf/proto'
+
+declare global {
+    namespace Proto {
+        /** 铁合金抽奖接口请求 */
+        interface THJRedPacketLotteryReq {
+            Header?: IMessageHead;
+            UserID: number; // 用户ID,必填
+            ClientType: number; // 终端类型
+            ClientSerialNo: string; // 客户端流水号
+        }
+
+        /** 铁合金抽奖接口响应 */
+        interface THJRedPacketLotteryRsp {
+            Header?: IMessageHead;
+            RetCode: number; // 返回码
+            RetDesc: string; // 描述信息
+            UserID: number; // 用户ID,必填
+            RedPacketAmount: number; // 中奖红包金额
+            GearLevel: number; // 档位
+            ClientSerialNo: string; // 客户端流水号
+        }
+    }
+}

+ 2 - 1
src/types/proto/trade.d.ts

@@ -1,4 +1,5 @@
 import { IMessageHead } from '@/services/socket/trade/protobuf/proto'
+import Long from 'long'
 
 declare global {
     namespace Proto {
@@ -7,7 +8,7 @@ declare global {
             Header?: IMessageHead;
             UserID: number; // 用户ID,必填
             AccountID: number; // 资金账号,必填
-            PresaleApplyID: number; // 预售申请ID,必填
+            PresaleApplyID: Long; // 预售申请ID,必填
             Qty: number; // 预售数量,必填
             DepositID: number; // 定金方式,THJ_PresaleApplyDeposit表ID,必填
             THJDeliveryMode: number; // 交割方式,必填1:平台仓储2:自提

+ 1 - 1
src/utils/vant/index.ts

@@ -39,7 +39,7 @@ export function showLoading(message = '正在加载...', callback?: (hideLoading
  * @param callback 
  * @param message 
  */
-export function fullloading(callback: (hideLoading: () => void) => void, message = '正在加载...') {
+export function fullloading(callback: (hideLoading: () => void) => void, message = '提交中...') {
     return showLoading(message, callback)
 }
 

+ 31 - 0
src/utils/websocket/crypto.ts

@@ -6,6 +6,8 @@ const aeskey: CryptoJS.lib.WordArray = CryptoJS.enc.Utf8.parse('F7A72DE7D6264530
 const macKeyLeft: CryptoJS.lib.WordArray = CryptoJS.enc.Hex.parse('B0FB83E39A5EBFAA'); // 这里要用CryptoJS.enc.Hex.parse,CryptoJS.enc.Utf8.parse返回是16位的
 /** MAC检验码右8字节 */
 const macKeyRight: CryptoJS.lib.WordArray = CryptoJS.enc.Hex.parse('BE471362A58393FF');
+/** Phone AES密钥 */
+const phoneaeskey = '0d299ce2d4105282f7471074cb0f9f9d';
 /** MAC检验向量 */
 const iv = new Uint8Array([0xd9, 0x51, 0xdb, 0xe0, 0x37, 0xc8, 0x23, 0x25]);
 
@@ -150,3 +152,32 @@ export const decrypt50 = (encryptData: Uint8Array, size: number): Uint8Array | n
 
     return a.subarray(0, size);
 };
+
+/**
+ * AES数据解密方法
+ * @param encryptData 密文
+ * @param size 明文长度
+ */
+export const decryptAES = (value: string): string | undefined => {
+    const ciphertext = hexStringToUint8Array(value);
+    const key = hexStringToUint8Array(phoneaeskey);
+
+    const cipherParams = CryptoJS.lib.CipherParams.create({
+        ciphertext: uint8ArrayToWordArray(ciphertext),
+    });
+
+    const decrytped = CryptoJS.AES.decrypt(cipherParams, uint8ArrayToWordArray(key), aesOption);
+    const h = wordArrayToUint8Array(decrytped).subarray(0, decrytped.sigBytes);
+    const result = new TextDecoder().decode(h);
+    return result;
+}
+
+/**
+ * 十六进制字符串转Uint8Array
+ * @param hex 
+ * @returns 
+ */
+function hexStringToUint8Array(hex: string) {
+    const buffer = hex.match(/.{1,2}/g)?.map((byte) => parseInt(byte, 16));
+    return new Uint8Array(buffer ?? []);
+}