zhb 2 miesięcy temu
rodzic
commit
9b09d09d41

+ 1 - 1
components/cwg-popup.vue

@@ -2,7 +2,7 @@
     <uni-popup ref="popupRef" type="center" @change="handlePopupChange">
         <view class="cwg-dialog">
             <!-- 弹窗头部 -->
-            <view class="dialog-header">
+            <view class="dialog-header" v-if="title">
                 <text class="dialog-title">{{ title || t('Tips.DeleteAccount') }}</text>
                 <uni-icons type="closeempty" size="20" color="#999" @click="closeDialog" />
             </view>

+ 950 - 0
pages/customer/transfer.vue

@@ -0,0 +1,950 @@
+<template>
+    <cwg-page-wrapper class="create-page" :isHeaderFixed="true">
+        <cwg-header :title="t('Home.page_customer.item5')" />
+        <view id="custom_Transfer" class="transfer-page">
+            <view class="main-content">
+                <!-- 步骤1:选择转出账户 -->
+                <view class="box box-step1">
+                    <view class="b-card">
+                        <view class="card-top">
+                            <view class="card-row">
+                                <view class="tit">
+                                    <text class="iconfont icon-caret-right"></text>
+                                    <span>{{ t('Custom.Transfer.Title1') }}</span>
+                                </view>
+                            </view>
+                            <view class="card-row">
+                                <cwg-combox v-model:value="loginValue" :clearable="false" :options="withdrawDisplayList"
+                                    :placeholder="t('placeholder.choose')" />
+                            </view>
+                        </view>
+                    </view>
+                </view>
+
+                <!-- 步骤2:转账表单 -->
+                <view class="box box-step2" v-if="step2">
+                    <view class="b-card">
+                        <view class="card-top">
+                            <view class="card-row card-tit">
+                                <view class="title-wrapper">
+                                    <view class="tit">
+                                        <text class="iconfont icon-caret-right"></text>
+                                        <span>{{ t('Custom.Transfer.Title2') }}</span>
+                                    </view>
+                                </view>
+                            </view>
+
+                            <view class="card-row">
+                                <cwg-combox v-model:value="transferType" :clearable="false"
+                                    :options="transferTypeOptions" :placeholder="t('placeholder.choose')" />
+                            </view>
+
+                            <uni-forms ref="formRef" :model="form" :rules="rules" label-position="top"
+                                validate-trigger="submit">
+                                <view class="form-row">
+                                    <view class="form-col">
+                                        <!-- 转出账户 -->
+                                        <uni-forms-item :label="t('Custom.Transfer.TransferAccounts')"
+                                            name="withdrawLogin">
+                                            <cwg-combox :disabled="true" v-model:value="form.withdrawLogin"
+                                                :clearable="false" :options="withdrawDisplayList"
+                                                :placeholder="t('placeholder.choose')" />
+                                        </uni-forms-item>
+                                    </view>
+                                    <view class="form-col">
+                                        <!-- 转入账户 -->
+                                        <uni-forms-item :label="t('Custom.Transfer.IntoAccount')" name="depositLogin"
+                                            :error-message="depositErrorMessage">
+                                            <cwg-combox v-model:value="form.depositLogin" :clearable="false"
+                                                :options="depositDisplayList" :placeholder="t('placeholder.choose')" />
+                                        </uni-forms-item>
+                                    </view>
+                                </view>
+
+                                <view class="form-row">
+                                    <view class="form-col">
+                                        <!-- 货币类型 -->
+                                        <uni-forms-item :label="t('Custom.Transfer.CurrencyType')" name="currency">
+
+                                            <cwg-combox v-model:value="form.currency" :clearable="false"
+                                                :options="currencyOptions" :placeholder="t('placeholder.choose')" />
+                                        </uni-forms-item>
+                                    </view>
+                                    <view class="form-col">
+                                        <!-- 转账金额 -->
+                                        <uni-forms-item :label="t('Custom.Transfer.Amount')" name="amount"
+                                            :error-message="amountErrorMessage">
+                                            <uni-easyinput v-model="form.amount" :placeholder="t('placeholder.input')"
+                                                @blur="validateAmount" />
+                                        </uni-forms-item>
+                                    </view>
+                                </view>
+
+                                <view class="form-row">
+                                    <view class="form-col-full">
+                                        <uni-forms-item>
+                                            <view class="tips">
+                                                <view class="title">{{ t('Custom.Transfer.Tips') }}</view>
+                                                <view>{{ t('Custom.Transfer.Tips1') }}</view>
+                                                <view>{{ t('Custom.Transfer.Tips2') }}</view>
+                                                <view>{{ t('Custom.Transfer.Tips3') }}</view>
+                                                <view>{{ t('Custom.Transfer.Tips4') }}</view>
+                                            </view>
+                                        </uni-forms-item>
+                                    </view>
+                                </view>
+
+                                <view class="form-row">
+                                    <view class="form-col-full">
+                                        <button class="s-btn" type="primary" @click="toTransfer" :disabled="submitting">
+                                            <span v-if="locale === 'es'">Enviar solicitud</span>
+                                            <span v-else>{{ t('Btn.Submit') }}</span>
+                                        </button>
+                                    </view>
+                                </view>
+                            </uni-forms>
+                        </view>
+                    </view>
+                </view>
+            </view>
+
+            <!-- 成功/失败弹窗 -->
+            <cwg-popup v-model:visible="dialogCheck" ref="resultPopup" type="center" :mask-click="false"
+                :showFooter="false">
+                <view class="popup-content" v-if="dialogVisible">
+                    <view class="icon"><cwg-icon name="verified" :size="80" color="#009933" /></view>
+                    <view class="des1">{{ t('ApplicationDialog.Des1') }}</view>
+                    <view class="dialog-footer">
+                        <button type="primary" @click="closeDia">{{ t('Btn.Confirm') }}</button>
+                        <button @click="closeDia">{{ t('Btn.Cancel') }}</button>
+                    </view>
+                </view>
+                <view class="popup-content" v-else>
+                    <view class="icon"><cwg-icon name="gy" :size="80" color="#eb3f57" /></view>
+                    <view class="des1">{{ responseMessage }}</view>
+                    <view class="dialog-footer">
+                        <button type="primary" @click="closeDia">{{ t('Btn.Confirm') }}</button>
+                        <button @click="closeDia">{{ t('Btn.Cancel') }}</button>
+                    </view>
+                </view>
+            </cwg-popup>
+
+            <!-- 等待弹窗 -->
+            <cwg-popup v-model:visible="dialogCheckWait" type="center" :mask-click="false" :showFooter="false">
+                <view class="popup-content wait-popup">
+                    <view class="icon"><cwg-icon name="icon_history" :size="80" color="#eb3f57" /></view>
+
+                    <view class="des1">{{ t('ApplicationDialog.Des38') }}</view>
+                </view>
+            </cwg-popup>
+        </view>
+    </cwg-page-wrapper>
+</template>
+
+<script setup>
+import { ref, reactive, computed, onMounted, watch } from 'vue'
+import { useI18n } from 'vue-i18n'
+import { onLoad, onReady } from '@dcloudio/uni-app'
+import { getCurrentInstance } from 'vue'
+import { isAfterJuly28 } from '@/utils/dateUtils'
+import { customApi } from '@/service/custom'
+import { financialApi } from '@/service/financial'
+import { ibApi } from '@/service/ib'
+// import ServiceA from "@/service/activity"; // 原注释保留
+import useUserStore from '@/stores/use-user-store'
+const userStore = useUserStore()
+const customInfo = computed(() => {
+    return userStore?.userInfo?.customInfo || {}
+})
+import Config from '@/config/index'
+
+const { Code } = Config
+const { t } = useI18n()
+
+// 获取全局实例(用于访问 Session、$pigeon 等)
+const { proxy } = getCurrentInstance()
+const Session = proxy?.Session
+const $pigeon = proxy?.$pigeon
+
+// 响应式数据
+const loginValue = ref('')
+const flag = ref(false)
+const responseMessage = ref('') // 弹窗响应信息
+const loginOptions = ref([])
+const toOptions = ref([])
+// 跨系统内转下拉数据
+const accountList = ref([])
+const systemTransferType = ref(0)
+const step2 = ref(false)
+const transferType = ref('internal')
+const amountLimits = reactive({
+    minAmount: '',
+    maxAmount: '',
+})
+const form = reactive({
+    currency: 'USD',
+    depositLogin: null,
+    withdrawLogin: null,
+    amount: ''
+})
+const dialogCheck = ref(false)
+const dialogVisible = ref(false)
+const dialogCheckWait = ref(false)
+const pageQuery = ref({})
+const rules = {
+    withdrawLogin: {
+        rules: [
+            {
+                required: true,
+                errorMessage: t('vaildate.select.empty')
+            },
+        ],
+    },
+    depositLogin: {
+        rules: [
+            {
+                required: true,
+                errorMessage: t('vaildate.select.empty')
+            },
+        ],
+    },
+    amount: {
+        rules: [
+            {
+                required: true,
+                errorMessage: t('vaildate.amount.format'),
+            },
+            {
+                validateFunction: (rule, value, data, callback) => {
+                    console.log(rule, value, data, callback, 2121212);
+                    if (!value) {
+                        callback(t('vaildate.amount.format'))
+                    } else if (
+                        amountLimits.minAmount &&
+                        amountLimits.maxAmount &&
+                        (parseFloat(amountLimits.minAmount) > parseFloat(value) ||
+                            parseFloat(amountLimits.maxAmount) < parseFloat(value))
+                    ) {
+                        callback(t('vaildate.amount.amount') +
+                            amountLimits.minAmount +
+                            '-' +
+                            amountLimits.maxAmount)
+                    } else if (!/^[0-9]+([.]{1}[0-9]{1,2})?$/.test(value)) {
+                        callback(t('vaildate.amount.format'))
+                    }
+                    return true
+                }
+            },
+        ],
+    }
+}
+function validateAmount() {
+    const amount = form.amount
+    if (!amount) {
+        amountErrorMessage.value = t('vaildate.required')
+        return false
+    }
+    const numValue = parseFloat(amount)
+    if (isNaN(numValue)) {
+        amountErrorMessage.value = t('vaildate.amount.format')
+        return false
+    }
+    if (amountLimits.minAmount && numValue < parseFloat(amountLimits.minAmount)) {
+        amountErrorMessage.value = t('vaildate.amount.amount') + amountLimits.minAmount + '-' + amountLimits.maxAmount
+        return false
+    }
+    if (amountLimits.maxAmount && numValue > parseFloat(amountLimits.maxAmount)) {
+        amountErrorMessage.value = t('vaildate.amount.amount') + amountLimits.minAmount + '-' + amountLimits.maxAmount
+        return false
+    }
+    if (!/^[0-9]+([.]{1}[0-9]{1,2})?$/.test(amount)) {
+        amountErrorMessage.value = t('vaildate.amount.format')
+        return false
+    }
+    amountErrorMessage.value = ''
+    return true
+}
+
+// 模板引用(对应原 this.$refs.form)
+const formRef = ref(null)
+
+// 计算属性
+const getInfoAgentTransfer = computed(() => {
+    return customInfo.value?.agentTransfer
+})
+
+
+// 单位类型
+function groupCurrency(type) {
+    const map = { GBP: ': £', USD: ': $', EUR: ': €', USC: ': ¢' }
+    return map[type] || ': $'
+}
+// 单位类型
+function groupCurrency1(type) {
+    const map = { GBP: '£', USD: '$', EUR: '€', USC: '¢' }
+    return map[type] || '$'
+}
+
+
+// 账户类型
+function groupTypeName(type) {
+    const typeMap = {
+        '1': t('AccountType.ClassicAccount'),
+        '2': t('AccountType.SeniorAccount'),
+        '3': isAfterJuly28() ? '--' : t('AccountType.AgencyAccount'),
+        '5': t('AccountType.SpeedAccount'),
+        '6': t('AccountType.SpeedAccount'),
+        '7': t('AccountType.StandardAccount'),
+        '8': t('AccountType.CentAccount')
+    }
+    return typeMap[type] || ''
+}
+
+const closeDia = () => {
+    formRef.value?.clearValidate()
+    step2.value = false
+    loginValue.value = ''
+    form.depositLogin = null
+    form.withdrawLogin = null
+    form.amount = ''
+    transferType.value = 'internal'
+    dialogCheck.value = false
+    dialogVisible.value = false
+}
+const submitting = ref(false);
+const toTransfer = async () => {
+    // 防止重复提交
+    if (submitting.value) return;
+    submitting.value = true;
+
+    try {
+        // 表单验证(验证失败会抛出异常)
+        await formRef.value?.validate()
+        // 检查活动参与情况(仅内部转账检查转出账户)[保留注释]
+        if (transferType.value === 'internal') {
+            // 活动检查代码已注释,保留原样
+        }
+        dialogCheckWait.value = true;
+
+        let res;
+        if (transferType.value === 'internal') {
+            res = await financialApi.TransferApply({ ...form });
+        } else if (transferType.value === 'agent') {
+            res = await ibApi.agentTransCtaferApply({ ...form });
+        } else if (transferType.value === 'system') {
+            const selectedAccount = accountList.value.find(item => item.login === form.depositLogin);
+            res = await financialApi.transferSystemApply({
+                currency: form.currency,
+                depositLogin: form.depositLogin,
+                withdrawLogin: form.withdrawLogin,
+                amount: form.amount,
+                depositPlatform: selectedAccount?.platform || 'MT4',
+                depositCurrency: selectedAccount?.currency || form.currency,
+                depositType: selectedAccount?.type || 2,
+            });
+        }
+
+        dialogCheckWait.value = false;
+
+        if (res.code == Code.StatusOK) {
+            dialogCheck.value = true;
+            dialogVisible.value = true;
+        } else {
+            responseMessage.value = res.msg;
+            dialogCheck.value = true;
+            dialogVisible.value = false;
+        }
+    } catch (error) {
+        console.log(error, 12121);
+
+        if (error instanceof Array) {
+            uni.showToast({ title: error[0].errorMessage, icon: 'none' });
+            return
+        } else {
+            console.log(232312);
+
+            responseMessage.value = error.msg;
+            dialogCheck.value = true;
+            dialogVisible.value = false;
+        }
+    } finally {
+        submitting.value = false;
+        if (dialogCheckWait.value) dialogCheckWait.value = false;
+    }
+};
+
+const cancle = () => {
+    step2.value = false
+    loginValue.value = ''
+    transferType.value = 'internal'
+}
+// 转出账户列表 
+const withdrawDisplayList = computed(() => {
+    return loginOptions.value.map(item => ({
+        text: `${item.login} - ${groupTypeName(item.type)} - ${t('Custom.Deposit.AvailableBalance')}${groupCurrency(item.currency)}${item.balance}`,
+        value: item.login,
+        disable: isWithdrawDisabled(item)
+    }))
+})
+const transferTypeOptions = computed(() => [
+    { value: 'internal', text: t('Home.page_customer.item5') },
+    ...(getInfoAgentTransfer.value ? [{ value: 'agent', text: t('Home.page_ib.item9') }] : []),
+    ...(systemTransferType.value === 1 ? [{ value: 'system', text: t('Custom.Transfer.SystemTransfer') }] : []),
+]);
+const currencyOptions = ref([{ value: 'USD', text: 'USD' }])
+// 转入账户列表 
+const depositDisplayList = computed(() => {
+    return toOptions.value.map(item => ({
+        text: `${item.login} - ${groupTypeName(item.type)} - ${t('Custom.Deposit.AvailableBalance')}${groupCurrency(item.currency)}${item.balance}`,
+        value: item.login,
+        disable: isDepositDisabled(item)
+    }))
+})
+// 转出账户是否禁用
+const isWithdrawDisabled = (item) => {
+    const closeFunc = item.closeFunctions || []
+    const transType = transferType.value // transferType 是 ref
+
+    return (
+        closeFunc.indexOf('5') !== -1 ||   // 包含'5'时禁用
+        closeFunc.indexOf('6') !== -1 ||   // 包含'6'时禁用
+        (transType === 'agent' && closeFunc.indexOf('3') !== -1) ||
+        (transType === 'internal' && closeFunc.indexOf('7') !== -1) ||
+        (transType === 'system' && closeFunc.indexOf('7') !== -1)
+    )
+}
+// 转入账户是否禁用
+function isDepositDisabled(item) {
+    const closeFunc = item.closeFunctions || []
+    if (transferType.value === 'agent' && closeFunc.indexOf('3') !== -1) return true
+    if (transferType.value === 'internal' && closeFunc.indexOf('7') !== -1) return true
+    if (transferType.value === 'system' && closeFunc.indexOf('7') !== -1) return true
+    return false
+}
+const getDateList = async () => {
+    let res = await customApi.CustomDropdown({ platform: '' })
+    if (res.code == Code.StatusOK) {
+        loginOptions.value = res.data
+    } else {
+        $pigeon?.MessageError(res.msg)
+    }
+}
+
+// 获取代理列表
+const getAgentList = async () => {
+    let res = await financialApi.getAgentList({})
+    if (res.code == Code.StatusOK) {
+        toOptions.value = res.data
+    } else {
+        $pigeon?.MessageError(res.msg)
+    }
+}
+
+//获取内转数额区间
+const getAmount = async () => {
+    let res = await financialApi.transferInfo({})
+    if (res.code == Code.StatusOK) {
+        Object.assign(amountLimits, res.data)
+    } else {
+        $pigeon?.MessageError(res.msg)
+    }
+}
+
+// 获取跨系统转账下拉列表
+const getTransferSystemDropdown = async () => {
+    let res = await financialApi.transferSystemDropdown({})
+    if (res.code == Code.StatusOK && res.data) {
+        systemTransferType.value = res.data.transferType ?? 0
+        console.log(systemTransferType.value, 'systemTransferType')
+        accountList.value = res.data.accountList || []
+    }
+}
+
+// 生命周期
+onLoad((options) => {
+    pageQuery.value = options
+    if (options.login) {
+        handleRouteParams(options)
+    }
+})
+// 更新转出账户选项
+function updateToOptions() {
+    toOptions.value = []
+    form.depositLogin = null
+    if (transferType.value === 'internal') {
+        loginOptions.value.forEach(item => {
+            if (item.login != form.withdrawLogin) {
+                toOptions.value.push(item)
+            }
+        })
+    } else if (transferType.value === 'agent') {
+        getAgentList()
+    } else if (transferType.value === 'system') {
+        toOptions.value = accountList.value.filter(item => item.login != form.withdrawLogin)
+    }
+}
+// 处理路由参数
+function handleRouteParams(options) {
+    form.withdrawLogin = Number(options.login)
+    step2.value = true
+    loginValue.value = options.login
+    updateToOptions()
+}
+onMounted(() => {
+    getDateList()
+    getAmount()
+    getTransferSystemDropdown()
+})
+
+// 侦听器
+watch(loginValue, (newVal) => {
+    if (newVal) {
+        step2.value = true
+        form.withdrawLogin = Number(loginValue.value)
+        toOptions.value = []
+        if (transferType.value === 'internal') {
+            // 内部转账逻辑
+            loginOptions.value.forEach((item) => {
+                if (item.login != newVal) {
+                    toOptions.value.push(item)
+                }
+            })
+        } else if (transferType.value === 'agent') {
+            // 代理内转逻辑
+            getAgentList()
+        } else if (transferType.value === 'system') {
+            // 跨系统内转,排除当前选择的转出账户
+            toOptions.value = accountList.value.filter(
+                (item) => item.login != form.withdrawLogin
+            )
+        }
+    }
+})
+
+watch(transferType, (newVal) => {
+    if (form.depositLogin) {
+        form.depositLogin = null
+    }
+    if (loginValue.value && step2.value) {
+        toOptions.value = []
+        if (newVal === 'internal') {
+            // 内部转账逻辑
+            loginOptions.value.forEach((item) => {
+                if (item.login != loginValue.value) {
+                    toOptions.value.push(item)
+                }
+            })
+        } else if (newVal === 'agent') {
+            // 代理内转逻辑
+            getAgentList()
+        } else if (newVal === 'system') {
+            // 跨系统内转,排除当前选择的转出账户
+            toOptions.value = accountList.value.filter(
+                (item) => item.login != form.withdrawLogin
+            )
+        }
+    }
+})
+</script>
+<style lang="scss" scoped>
+@import "@/uni.scss";
+
+.transfer-page {
+    width: 100%;
+    padding-bottom: px2rpx(20);
+
+    .main-content {
+        text-align: left;
+        padding: px2rpx(10);
+
+        .box {
+            padding-top: px2rpx(5);
+            color: #303133;
+
+            .b-card {
+                background-color: #fff;
+                margin-bottom: px2rpx(10);
+                border-radius: px2rpx(6);
+                box-shadow: 0 px2rpx(1) px2rpx(6) 0 rgba(0, 0, 0, 0.05);
+
+                &:hover {
+                    box-shadow: 0 px2rpx(2) px2rpx(8) 0 rgba(0, 0, 0, 0.1);
+                    transform: translateY(px2rpx(-1));
+                }
+
+                .card-top {
+                    padding: px2rpx(15) px2rpx(20);
+
+                    .card-row {
+                        margin-bottom: px2rpx(12);
+
+                        &:last-child {
+                            margin-bottom: 0;
+                        }
+                    }
+
+                    .tit {
+                        font-size: px2rpx(16);
+                        margin-bottom: px2rpx(12);
+                        font-weight: 600;
+
+                        .iconfont {
+                            font-size: px2rpx(18);
+                            margin-right: px2rpx(8);
+                            color: #409eff;
+                        }
+                    }
+
+                    .title-wrapper {
+                        display: flex;
+                        justify-content: space-between;
+                        align-items: center;
+                    }
+                }
+            }
+        }
+
+        .box-step2 {
+            .form-row {
+                display: flex;
+                flex-wrap: wrap;
+                margin: 0 px2rpx(-5);
+                margin-bottom: px2rpx(12);
+
+                &:last-child {
+                    margin-bottom: 0;
+                }
+
+                .form-col {
+                    flex: 1;
+                    padding: 0 px2rpx(5);
+                    min-width: px2rpx(140);
+
+                    @media (max-width: 750rpx) {
+                        width: 100%;
+                    }
+                }
+
+                .form-col-full {
+                    width: 100%;
+                    padding: 0 px2rpx(5);
+                }
+            }
+
+            .tips {
+                line-height: 1.8;
+                font-size: px2rpx(12);
+                color: #909399;
+                background-color: #f9f9f9;
+                padding: px2rpx(12);
+                border-radius: px2rpx(4);
+                border-left: px2rpx(2) solid #409eff;
+
+                .title {
+                    font-weight: 600;
+                    margin-bottom: px2rpx(6);
+                    color: #606266;
+                }
+            }
+        }
+    }
+
+    .picker-select {
+        background-color: #f5f7fa;
+        border: 1px solid #dcdfe6;
+        border-radius: px2rpx(4);
+        padding: px2rpx(12) px2rpx(14);
+        font-size: px2rpx(14);
+        color: #606266;
+        line-height: 1.4;
+        width: 100%;
+        box-sizing: border-box;
+
+        &:hover {
+            border-color: #409eff;
+        }
+
+        &.picker-disabled {
+            background-color: #f5f7fa;
+            color: #c0c4cc;
+            cursor: not-allowed;
+        }
+    }
+
+    .disabled-input {
+        background-color: #f5f7fa;
+        border: 1px solid #dcdfe6;
+        border-radius: px2rpx(4);
+        padding: px2rpx(12) px2rpx(14);
+        font-size: px2rpx(14);
+        color: #606266;
+        width: 100%;
+        box-sizing: border-box;
+    }
+
+    .m-input {
+        background-color: #f5f7fa;
+        border: 1px solid #dcdfe6;
+        border-radius: px2rpx(4);
+        padding: px2rpx(12) px2rpx(14);
+        font-size: px2rpx(14);
+        width: 100%;
+        box-sizing: border-box;
+
+        &:focus {
+            border-color: #409eff;
+            outline: none;
+            box-shadow: 0 0 0 px2rpx(1) rgba(64, 158, 255, 0.2);
+        }
+    }
+
+    .s-btn {
+        width: 100%;
+        padding: px2rpx(12) px2rpx(20);
+        border-radius: px2rpx(4);
+        margin: px2rpx(10) 0;
+        background-color: #409eff;
+        color: #fff;
+        border: none;
+        font-size: px2rpx(15);
+        font-weight: 500;
+        cursor: pointer;
+
+        &:hover {
+            background-color: #66b1ff;
+            transform: translateY(px2rpx(-1));
+            box-shadow: 0 px2rpx(2) px2rpx(6) 0 rgba(64, 158, 255, 0.3);
+        }
+
+        &:active {
+            background-color: #3a8ee6;
+            transform: translateY(0);
+        }
+
+        &[disabled] {
+            opacity: 0.6;
+            cursor: not-allowed;
+        }
+    }
+
+    .popup-content {
+        padding: px2rpx(30) px2rpx(20);
+        text-align: center;
+        min-width: px2rpx(250);
+        max-width: 80%;
+        margin: 0 auto;
+
+        @media (max-width: 750rpx) {
+            min-width: 80%;
+            max-width: 90%;
+            margin: 0 px2rpx(10);
+        }
+
+        position: relative;
+
+        .icon {
+            .iconfont {
+                font-size: px2rpx(60);
+                display: block;
+                margin: 0 auto;
+            }
+
+            .icon-chenggong {
+                color: #67c23a;
+            }
+
+            .icon-jingshi {
+                color: #f56c6c;
+            }
+
+            .icon-dengdai {
+                color: #e6a23c;
+            }
+        }
+
+        .des1 {
+            font-weight: 600;
+            font-size: px2rpx(16);
+            margin: px2rpx(20) 0 px2rpx(15);
+            color: #303133;
+            line-height: 1.4;
+            padding: 0 px2rpx(10);
+        }
+
+        .dialog-footer {
+            display: flex;
+            justify-content: center;
+            gap: px2rpx(10);
+            margin-top: px2rpx(10);
+
+            @media (max-width: 750rpx) {
+                flex-direction: column;
+                align-items: center;
+                gap: px2rpx(6);
+            }
+
+            button {
+                min-width: px2rpx(90);
+                padding: 0 px2rpx(12);
+                border-radius: px2rpx(4);
+                font-size: px2rpx(14);
+                transition: all 0.3s ease;
+                cursor: pointer;
+
+                &[type="primary"] {
+                    background-color: #409eff;
+                    color: #fff;
+                    border: none;
+
+                    &:hover {
+                        background-color: #66b1ff;
+                        transform: translateY(px2rpx(-1));
+                        box-shadow: 0 px2rpx(2) px2rpx(6) 0 rgba(64, 158, 255, 0.3);
+                    }
+
+                    &:active {
+                        background-color: #3a8ee6;
+                        transform: translateY(0);
+                    }
+                }
+
+                &:not([type="primary"]) {
+                    background-color: #fff;
+                    border: 1px solid #dcdfe6;
+                    color: #606266;
+
+                    &:hover {
+                        border-color: #409eff;
+                        color: #409eff;
+                        transform: translateY(px2rpx(-1));
+                    }
+
+                    &:active {
+                        transform: translateY(0);
+                    }
+                }
+            }
+        }
+    }
+
+    .wait-popup {
+        .des1 {
+            margin-top: px2rpx(10);
+        }
+
+        .icon {
+            .iconfont {
+                animation: spin 1s linear infinite;
+            }
+        }
+    }
+}
+
+// 动画
+@keyframes popupFadeIn {
+    from {
+        opacity: 0;
+        transform: scale(0.9);
+    }
+
+    to {
+        opacity: 1;
+        transform: scale(1);
+    }
+}
+
+@keyframes fadeIn {
+    from {
+        opacity: 0;
+        transform: translateY(px2rpx(10));
+    }
+
+    to {
+        opacity: 1;
+        transform: translateY(0);
+    }
+}
+
+@keyframes pulse {
+    0% {
+        transform: scale(1);
+    }
+
+    50% {
+        transform: scale(1.05);
+    }
+
+    100% {
+        transform: scale(1);
+    }
+}
+
+@keyframes spin {
+    from {
+        transform: rotate(0deg);
+    }
+
+    to {
+        transform: rotate(360deg);
+    }
+}
+
+// 表单错误信息样式
+.uni-forms-item__error {
+    font-size: px2rpx(12);
+    color: #f56c6c;
+    margin-top: px2rpx(4);
+}
+
+// 适配不同屏幕尺寸(媒体查询中的 750rpx 保持不变)
+@media (max-width: 750rpx) {
+    .transfer-page {
+        .main-content {
+            padding: px2rpx(8);
+
+            .box {
+                .b-card {
+                    .card-top {
+                        padding: px2rpx(12) px2rpx(16);
+
+                        .tit {
+                            font-size: px2rpx(14);
+
+                            .iconfont {
+                                font-size: px2rpx(16);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        .s-btn {
+            font-size: px2rpx(14);
+            padding: px2rpx(10) px2rpx(16);
+        }
+
+        .popup-content {
+            padding: px2rpx(20) px2rpx(16);
+
+            .icon {
+                .iconfont {
+                    font-size: px2rpx(50);
+                }
+            }
+
+            .des1 {
+                font-size: px2rpx(14);
+                margin: px2rpx(15) 0 px2rpx(10);
+            }
+
+            .dialog-footer {
+                button {
+                    min-width: px2rpx(80);
+                    font-size: px2rpx(13);
+                }
+            }
+        }
+    }
+}
+</style>

+ 4 - 0
service/financial.ts

@@ -29,6 +29,10 @@ export const financialApi = {
   TransferList: (params = {}) => post('/transfer/list', params, 'Host04'),
   /** 内转用到的数据(最大 最小内转量) */
   transferInfo: (params = {}) => post('/transfer/info', params, 'Host04'),
+  /** 跨系统内转-获取VU账户下拉数据 */
+  transferSystemDropdown: (params = {}) => post('/transfer/system/dropdown', params, 'Host04'),
+  /** 跨系统内转-申请 */
+  transferSystemApply: (params = {}) => post('/transfer/system/apply', params, 'Host04'),
   /** 获取代理账户列表 */
   getAgentList: (params = {}) => post('/account/agent/list', params, 'Host80'),
   /** 银行电汇入金 */

+ 4 - 4
uni_modules/uni-forms/components/uni-forms/validate.js

@@ -64,10 +64,10 @@ const types = {
 		}
 		return typeof value === 'number';
 	},
-	"boolean": function(value) {
+	"boolean": function (value) {
 		return typeof value === 'boolean';
 	},
-	"float": function(value) {
+	"float": function (value) {
 		return types.number(value) && !types.integer(value);
 	},
 	array(value) {
@@ -452,7 +452,7 @@ function Message() {
 		default: '验证错误',
 		defaultInvalid: '提交的字段{field}在数据库中并不存在',
 		validateFunction: '验证无效',
-		required: '{label}必填',
+		required: '{label}必填§§§§',
 		'enum': '{label}超出范围',
 		timestamp: '{label}格式无效',
 		whitespace: '{label}不能为空',
@@ -483,4 +483,4 @@ function Message() {
 
 SchemaValidator.message = new Message();
 
-export default SchemaValidator
+export default SchemaValidator

+ 27 - 0
utils/agencyAccountUtils.js

@@ -0,0 +1,27 @@
+import { isAfterJuly28 } from './dateUtils';
+
+/**
+ * 检查是否应该显示AgencyAccount
+ * @returns {boolean} 如果应该显示返回true,否则返回false
+ */
+export function shouldShowAgencyAccount() {
+  return !isAfterJuly28();
+}
+
+/**
+ * 获取AgencyAccount的显示文本
+ * @param {Function} t - i18n翻译函数
+ * @returns {string} 显示文本
+ */
+export function getAgencyAccountText(t) {
+  return shouldShowAgencyAccount() ? t('AccountType.AgencyAccount') : '--';
+}
+
+/**
+ * 检查账户类型是否为AgencyAccount且应该隐藏
+ * @param {number} accountType - 账户类型
+ * @returns {boolean} 如果是AgencyAccount且应该隐藏返回true
+ */
+export function isAgencyAccountAndShouldHide(accountType) {
+  return accountType === 3 && isAfterJuly28();
+} 

+ 107 - 0
utils/jsonUtils.js

@@ -0,0 +1,107 @@
+/**
+ * 安全的 JSON 解析工具函数
+ * 处理各种可能导致 JSON 解析错误的情况
+ */
+
+/**
+ * 安全解析 JSON 字符串
+ * @param {string|object} jsonString - 要解析的 JSON 字符串或对象
+ * @param {any} defaultValue - 解析失败时的默认值
+ * @returns {any} 解析后的对象或默认值
+ */
+export function safeJsonParse(jsonString, defaultValue = null) {
+  try {
+    // 如果输入为空或null,返回默认值
+    if (!jsonString || jsonString === 'null' || jsonString === 'undefined') {
+      return defaultValue;
+    }
+    
+    if (typeof jsonString === 'string') {
+      // 尝试解析JSON字符串
+      const parsed = JSON.parse(jsonString);
+      return parsed;
+    } else if (jsonString && typeof jsonString === 'object') {
+      // 如果已经是对象,直接返回
+      return jsonString;
+    } else {
+      return defaultValue;
+    }
+  } catch (error) {
+    console.error('JSON parse error:', error, 'Input:', jsonString);
+    return defaultValue;
+  }
+}
+
+/**
+ * 安全解析API响应数据中的URL
+ * @param {any} responseData - API响应的data字段
+ * @param {string} urlKey - URL字段的键名,默认为'url'
+ * @param {string} defaultValue - 解析失败时的默认值
+ * @returns {string} 解析出的URL或默认值
+ */
+export function safeParseUrl(responseData, urlKey = 'url', defaultValue = '') {
+  try {
+    let parsedData;
+    
+    if (typeof responseData === 'string') {
+      parsedData = JSON.parse(responseData);
+    } else if (responseData && typeof responseData === 'object') {
+      parsedData = responseData;
+    } else {
+      console.error('Invalid response data format:', responseData);
+      return defaultValue;
+    }
+    
+    return parsedData[urlKey] || defaultValue;
+  } catch (error) {
+    console.error('URL parse error:', error, 'Data:', responseData);
+    return defaultValue;
+  }
+}
+
+/**
+ * 安全获取用户数据
+ * @param {object} session - Session 对象
+ * @returns {object} 用户数据对象
+ */
+export function safeGetUserData(session) {
+  const userData = session.Get("user", true);
+  return safeJsonParse(userData, {});
+}
+
+/**
+ * 安全获取 Session 中的布尔值
+ * @param {object} session - Session 对象
+ * @param {string} key - Session 键名
+ * @param {boolean} defaultValue - 默认值
+ * @returns {boolean} 布尔值
+ */
+export function safeGetBoolean(session, key, defaultValue = false) {
+  try {
+    const value = session.Get(key);
+    if (value === null || value === undefined) {
+      return defaultValue;
+    }
+    return JSON.parse(value);
+  } catch (error) {
+    console.error(`Error parsing boolean for key ${key}:`, error);
+    return defaultValue;
+  }
+}
+
+/**
+ * 安全获取 Session 中的对象
+ * @param {object} session - Session 对象
+ * @param {string} key - Session 键名
+ * @param {object} defaultValue - 默认值
+ * @returns {object} 对象
+ */
+export function safeGetObject(session, key, defaultValue = {}) {
+  try {
+    const value = session.Get(key, true);
+    return safeJsonParse(value, defaultValue);
+  } catch (error) {
+    console.error(`Error parsing object for key ${key}:`, error);
+    return defaultValue;
+  }
+} 

+ 2 - 0
utils/request.js

@@ -100,6 +100,8 @@ const responseInterceptor = (response, options = {}) => {
     }
     if (data.code === 200) {
       return data;
+    } else if (data.code === 400) {
+      return Promise.reject(data);
     } else {
       uni.showToast({
         title: data.msg || "请求失败",

+ 18 - 0
utils/statusColor.js

@@ -0,0 +1,18 @@
+export const statusColor = (status)=> {
+  let color = 'crm_state_blue';
+  switch (status) {
+    case 'wait_process':
+      color = 'crm_state_yellow';
+      break
+    case 'processing':
+      color = 'crm_state_orange';
+      break
+    case 'success':
+      color = 'crm_state_blue';
+      break
+    case 'fail':
+      color = 'crm_state_gray';
+      break
+  }
+return `state ${color}`;
+}