zhb 2 miesięcy temu
rodzic
commit
2443579138

+ 14 - 0
pages.json

@@ -100,6 +100,20 @@
         "navigationStyle": "custom"
       }
     },
+    {
+      "path": "pages/activities/monthly-list",
+      "style": {
+        "navigationBarTitleText": "",
+        "navigationStyle": "custom"
+      }
+    },
+    {
+      "path": "pages/activities/surplus-list",
+      "style": {
+        "navigationBarTitleText": "",
+        "navigationStyle": "custom"
+      }
+    },
     {
       "path": "pages/activities/detail",
       "style": {

+ 338 - 0
pages/activities/components/GiftApplicationPopup.vue

@@ -0,0 +1,338 @@
+<template>
+  <cwg-popup v-model:visible="visible" type="center" :mask-click="false" :showFooters="true" :showClose="false"
+    :title="title">
+    <view class="popup-content">
+      <uni-forms ref="formRef" :model="giftForm" :rules="rules" label-position="top" validate-trigger="submit">
+        <view class="form-row">
+          <view class="form-col-full">
+            <uni-forms-item :label="t('UtaskList.item18')" name="giveCode">
+              <cwg-combox v-model:value="giftForm.giveCode" :clearable="false" :options="giftOptions"
+                :placeholder="t('UtaskList.item19')" @change="handleGiftChange" />
+            </uni-forms-item>
+          </view>
+        </view>
+        <view class="form-row">
+          <view class="form-col-full">
+            <uni-forms-item :label="t('UtaskList.item20')" name="giveAddress">
+              <uni-easyinput v-model="giftForm.giveAddress" :placeholder="t('UtaskList.item21')" />
+            </uni-forms-item>
+          </view>
+        </view>
+        <view class="form-row">
+          <view class="form-col-full">
+            <uni-forms-item :label="t('UtaskList.item24')" name="giveAcceptName">
+              <uni-easyinput v-model="giftForm.giveAcceptName" :placeholder="t('UtaskList.item25')" />
+            </uni-forms-item>
+          </view>
+        </view>
+        <view class="form-row">
+          <view class="form-col-full">
+            <uni-forms-item :label="t('UtaskList.item22')" name="givePhone">
+              <uni-easyinput v-model="giftForm.givePhone" :placeholder="t('UtaskList.item23')" />
+            </uni-forms-item>
+          </view>
+        </view>
+      </uni-forms>
+    </view>
+    <template #footer>
+      <button @click="cancel">{{ t('Btn.Cancel') }}</button>
+      <button type="primary" @click="submit" :loading="submitting">{{ t('Btn.Confirm') }}</button>
+    </template>
+  </cwg-popup>
+</template>
+
+<script setup>
+import { ref, computed, reactive, watch } from 'vue';
+import { useI18n } from 'vue-i18n';
+
+const props = defineProps({
+  visible: { type: Boolean, default: false },
+  title: { type: String, default: '' },
+  giftList: { type: Array, default: () => [] },
+  giftForm: { type: Object, default: () => ({}) }
+});
+const emit = defineEmits(['update:visible', 'confirm', 'cancel']);
+const { t, locale } = useI18n();
+
+const visible = computed({
+  get: () => props.visible,
+  set: (value) => emit('update:visible', value)
+});
+
+// 表单数据
+const giftForm = reactive({
+  giveCode: props.giftForm.giveCode || '',
+  giveAddress: props.giftForm.giveAddress || '',
+  giveAcceptName: props.giftForm.giveAcceptName || '',
+  givePhone: props.giftForm.givePhone || ''
+});
+
+// 表单引用
+const formRef = ref(null);
+// 提交状态
+const submitting = ref(false);
+
+// 礼物选项
+const giftOptions = computed(() => {
+  return props.giftList.map(gift => ({
+    text: `${gift.giveCode} - ${gift.giveName}`,
+    value: gift.giveCode
+  }));
+});
+
+// 表单验证规则
+const rules = {
+  giveCode: {
+    rules: [
+      {
+        required: true,
+        errorMessage: t("UtaskList.item19")
+      }
+    ]
+  },
+  giveAddress: {
+    rules: [
+      {
+        required: true,
+        errorMessage: t("UtaskList.item21")
+      }
+    ]
+  },
+  giveAcceptName: {
+    rules: [
+      {
+        required: true,
+        errorMessage: t("UtaskList.item25")
+      }
+    ]
+  },
+  givePhone: {
+    rules: [
+      {
+        required: true,
+        errorMessage: t("UtaskList.item23")
+      }
+    ]
+  }
+};
+
+const cancel = () => {
+  visible.value = false;
+  emit('cancel');
+};
+
+const handleGiftChange = (value) => {
+  giftForm.giveCode = value;
+};
+
+const submit = async () => {
+  if (submitting.value) return;
+  submitting.value = true;
+
+  try {
+    // 表单验证
+    await formRef.value?.validate();
+
+    visible.value = false;
+    emit('confirm', giftForm);
+  } catch (error) {
+    console.log('Form validation failed:', error);
+  } finally {
+    submitting.value = false;
+  }
+};
+watch(() => props.visible, (newVal) => {
+  if (newVal) {
+    giftForm.giveCode = ''
+    giftForm.giveAddress = ''
+    giftForm.giveAcceptName = ''
+    giftForm.givePhone = ''
+  }
+})
+</script>
+
+<style lang="scss" scoped>
+@import "@/uni.scss";
+
+.popup-content {
+  padding: px2rpx(20);
+
+  .form-row {
+    margin-bottom: px2rpx(20);
+
+    &:last-child {
+      margin-bottom: 0;
+    }
+  }
+
+  .form-col-full {
+    width: 100%;
+  }
+
+  :deep(.uni-forms) {
+    width: 100%;
+
+    :deep(.uni-forms-item) {
+      margin-bottom: px2rpx(16);
+
+      :deep(.uni-forms-item__label) {
+        font-size: px2rpx(14);
+        color: #606266;
+        margin-bottom: px2rpx(8);
+      }
+
+      :deep(.uni-forms-item__error) {
+        font-size: px2rpx(12);
+        color: #f56c6c;
+        margin-top: px2rpx(4);
+      }
+
+      :deep(.uni-easyinput) {
+        width: 100%;
+
+        :deep(input) {
+          width: 100%;
+          height: px2rpx(40);
+          padding: 0 px2rpx(12);
+          border: 1px solid #dcdfe6;
+          border-radius: px2rpx(4);
+          font-size: px2rpx(14);
+          transition: border-color 0.3s;
+
+          &:focus {
+            outline: none;
+            border-color: #409eff;
+            box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
+          }
+        }
+      }
+
+      :deep(.cwg-combox) {
+        width: 100%;
+
+        :deep(.uni-select) {
+          width: 100%;
+
+          :deep(.uni-select-input) {
+            width: 100%;
+            height: px2rpx(40);
+            padding: 0 px2rpx(12);
+            border: 1px solid #dcdfe6;
+            border-radius: px2rpx(4);
+            font-size: px2rpx(14);
+            transition: border-color 0.3s;
+
+            &:focus {
+              outline: none;
+              border-color: #409eff;
+              box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
+            }
+          }
+        }
+      }
+    }
+  }
+
+  .tips {
+    background-color: #f5f7fa;
+    border-radius: px2rpx(8);
+    padding: px2rpx(16);
+    margin-bottom: px2rpx(20);
+
+    .title {
+      font-size: px2rpx(14);
+      font-weight: 600;
+      color: #303133;
+      margin-bottom: px2rpx(8);
+    }
+
+    view {
+      font-size: px2rpx(13);
+      color: #606266;
+      line-height: 1.5;
+      margin-bottom: px2rpx(8);
+
+      &:last-child {
+        margin-bottom: 0;
+      }
+    }
+  }
+
+  button {
+    width: 100%;
+    height: px2rpx(44);
+    border-radius: px2rpx(4);
+    font-size: px2rpx(16);
+    font-weight: 500;
+    transition: all 0.3s;
+
+    &.reselect {
+      background-color: #409eff;
+      color: #ffffff;
+      border: none;
+
+      &:hover {
+        background-color: #66b1ff;
+      }
+
+      &:active {
+        background-color: #3a8ee6;
+      }
+
+      &[disabled] {
+        background-color: #c6e2ff;
+        cursor: not-allowed;
+      }
+    }
+  }
+}
+
+@media (max-width: 768px) {
+  .popup-content {
+    padding: px2rpx(16);
+
+    .form-row {
+      margin-bottom: px2rpx(16);
+    }
+
+    :deep(.uni-forms) {
+      :deep(.uni-forms-item) {
+        margin-bottom: px2rpx(12);
+
+        :deep(.uni-easyinput) {
+          :deep(input) {
+            height: px2rpx(36);
+            font-size: px2rpx(13);
+          }
+        }
+
+        :deep(.cwg-combox) {
+          :deep(.uni-select) {
+            :deep(.uni-select-input) {
+              height: px2rpx(36);
+              font-size: px2rpx(13);
+            }
+          }
+        }
+      }
+    }
+
+    .tips {
+      padding: px2rpx(12);
+
+      .title {
+        font-size: px2rpx(13);
+      }
+
+      view {
+        font-size: px2rpx(12);
+      }
+    }
+
+    button {
+      height: px2rpx(40);
+      font-size: px2rpx(15);
+    }
+  }
+}
+</style>

+ 3 - 3
pages/activities/composables/useActivityActions.ts

@@ -126,7 +126,7 @@ export function useActivityActions(
     }
 
     const toActivity24Trading = () => {
-        uni.navigateTo({ url: '/pages/customer/new' })
+        uni.navigateTo({ url: '/pages/analytics/new' })
     }
 
     const toHistoryLuckyDraw = () => {
@@ -138,11 +138,11 @@ export function useActivityActions(
     }
 
     const goSurplusTaskList = () => {
-        uni.navigateTo({ url: '/pages/customer/surplus-list' })
+        uni.navigateTo({ url: '/pages/activities/surplus-list' })
     }
 
     const goMonthlyTaskList = () => {
-        uni.navigateTo({ url: '/pages/customer/monthly-list' })
+        uni.navigateTo({ url: '/pages/activities/monthly-list' })
     }
 
     const toDocumentary = () => {

+ 709 - 0
pages/activities/monthly-list.vue

@@ -0,0 +1,709 @@
+<template>
+    <cwg-page-wrapper class="create-page" :isHeaderFixed="true">
+        <cwg-header :title="t('wallet.item52')" />
+
+        <view id="custom_history" class="">
+            <view class="main-content">
+                <!-- 无任务列表提示 -->
+                <cwg-empty-state v-if="!tableData || tableData.length === 0" />
+                <!-- 数据卡片展示 -->
+                <view class="outer-card" v-for="(item, index) in tableData" :key="index"
+                    v-show="tableData && tableData.length > 0">
+                    <view class="data-cards">
+                        <!-- 第一行:总数据 -->
+                        <view class="total-data-row">
+                            <view class="data-card total-card">
+                                <view class="card-content">
+                                    <view class="card-title">{{ t("UtaskList.item13") }}</view>
+                                    <view class="card-value" style="color: #ff4d4f">
+                                        {{ item.depositAmount || 0 }}
+                                    </view>
+                                </view>
+                            </view>
+                            <view class="data-card total-card">
+                                <view class="card-content">
+                                    <view class="card-title">{{ t("UtaskList.item3") }}</view>
+                                    <view class="card-value" style="color: #ff4d4f">
+                                        {{ item.completeVolume || 0 }}
+                                    </view>
+                                </view>
+                            </view>
+                            <view class="data-card total-card">
+                                <view class="card-content">
+                                    <view class="card-title">{{ t("Label.State") }}</view>
+                                    <view class="card-value" :style="getStatusStyle(item.status)">
+                                        {{ getStatusText(item.status) }}
+                                    </view>
+                                </view>
+                            </view>
+
+                            <view class="data-card total-card" v-if="item.status === 2">
+                                <view class="card-content">
+                                    <view class="card-title">
+                                        {{ t("MonthlyActivities.item4") }}
+                                    </view>
+                                    <view class="card-value" :style="getGiveStatusStyle(item.giveStatus)">
+                                        {{ getGiveStatusText(item.giveStatus) }}
+                                    </view>
+                                </view>
+                            </view>
+                        </view>
+
+                        <!-- 第二行:分数据 -->
+                        <view class="sub-data-row">
+                            <view class="data-card">
+                                <view class="card-content">
+                                    <view class="card-value">{{ item.endDate }}</view>
+                                    <view class="card-desc">{{ t("UtaskList.item10") }}</view>
+                                </view>
+                            </view>
+                            <view class="data-card" v-if="item.status === 2">
+                                <view class="card-content">
+                                    <view class="card-value">{{ item.applyTime }}</view>
+                                    <view class="card-desc">{{ t("MonthlyActivities.item6") }}</view>
+                                </view>
+                            </view>
+                            <view class="data-card" v-if="item.status === 2">
+                                <view class="card-content">
+                                    <view class="card-value">{{ item.grantTime }}</view>
+                                    <view class="card-desc">{{ t("MonthlyActivities.item7") }}</view>
+                                </view>
+                            </view>
+                            <view class="data-card" v-if="item.logisticsOrder">
+                                <view class="card-content">
+                                    <view class="card-value">{{ item.logisticsOrder }}</view>
+                                    <view class="card-desc">{{ t("MonthlyActivities.item9") }}</view>
+                                </view>
+                            </view>
+                            <view class="data-card btn-card" v-show="shouldShowCard(item, item)">
+                                <!-- 取消按钮 - status为1时显示 -->
+                                <button v-if="item.status === 1" type="danger" size="small" @click="cancelTask(item.id)"
+                                    :loading="loadingStates[item.id] === 'cancel'">
+                                    {{ t("Btn.Cancel") }}
+                                </button>
+                                <!-- 礼物申请按钮 - status为2且giveStatus为1时显示 -->
+                                <button v-if="item.status === 2 && item.giveStatus === 1 && lang1" type="primary"
+                                    size="small" @click="applyGift(item.id)"
+                                    :loading="loadingStates[item.id] === 'applyGift'">
+                                    {{ t("Btn.Application") }}
+                                </button>
+                            </view>
+                        </view>
+
+                        <!-- 提示信息 -->
+                        <view class="tip-info" style="padding: 10px 0; color: #909399; font-size: 14px">
+                            {{ t("MonthlyActivities.item10") }}
+                        </view>
+                    </view>
+                </view>
+            </view>
+
+            <!-- 礼物申请对话框 -->
+            <GiftApplicationPopup v-model:visible="dialogGiftApplication" :title="t('Btn.Application')"
+                :giftList="giftList" :giftForm="giftForm" @confirm="submitGiftApply" />
+        </view>
+    </cwg-page-wrapper>
+</template>
+
+<script setup lang="ts">
+import { ref, computed, onMounted, watch } from 'vue'
+import { onLoad, onPullDownRefresh, onReachBottom } from '@dcloudio/uni-app'
+import { useI18n } from 'vue-i18n'
+import { activityApi } from "@/service/activity"
+import Config from "@/config/index"
+import GiftApplicationPopup from "./components/GiftApplicationPopup.vue"
+import { useConfirm } from '@/hooks/useConfirm'
+const confirm = useConfirm()
+const { t, locale } = useI18n()
+let { Code } = Config
+
+// ---------- 存储辅助函数(模拟原 Session) ----------
+const Session = {
+    Get(key: string, parse = false) {
+        const value = uni.getStorageSync(key)
+        if (parse && value) {
+            try {
+                return JSON.parse(value)
+            } catch {
+                return value
+            }
+        }
+        return value
+    }
+}
+
+// ---------- 响应式数据 ----------
+const flag = ref(false)
+const reasons = ref({})
+const pictLoading = ref(false)
+const tableData = ref<any[]>([])
+const time = ref("")
+const loadingStates = ref<Record<string, string | null>>({}) // 用于跟踪每个任务的加载状态
+
+// 卡片数据
+const totalTasks = ref(0)
+const totalRewards = ref(0)
+const completionRate = ref("0")
+const inProgressTasks = ref(0)
+const completedTasks = ref(0)
+const expiredTasks = ref(0)
+const rejectedTasks = ref(0)
+const todayRewards = ref(0)
+const weekRewards = ref(0)
+const monthRewards = ref(0)
+const activityLevel = ref("0")
+
+// 礼物申请对话框相关
+const dialogGiftApplication = ref(false)
+const giftList = ref<any[]>([])
+const giftForm = ref({
+    id: null as number | null,
+    giveCode: "",
+    giveName: "",
+    giveAddress: "",
+    givePhone: "",
+    giveAcceptName: "",
+})
+const giftSubmitting = ref(false)
+
+// 原 watch 中依赖的 search(保留)
+const search = ref({ type: "" })
+
+// ---------- 计算属性 ----------
+const expireTime = computed(() => {
+    return Session.Get("user", true)
+})
+
+const lang = computed(() => {
+    return (Session.Get("lang") == "en" && document.body.clientWidth < 1330)
+})
+// 注意:document 在 uni-app 中不可用,此处保留原逻辑但需注意运行环境
+// 若需兼容,可改用 uni.getSystemInfoSync().windowWidth
+
+const lang1 = computed(() => {
+    const user = Session.Get("user", true)
+    return user?.customInfo?.country == "CN"
+})
+
+
+
+// ---------- 方法 ----------
+const getStatus = (status: number) => {
+    if (status == 1) {
+        return t("State.Ongoing")
+    } else if (status == 2) {
+        return t("State.Completed")
+    } else if (status == 3) {
+        return t("State.Cancelled")
+    } else if (status == 4) {
+        return t("State.expireTime")
+    }
+}
+
+const getStatusText = (status: number) => {
+    if (status == 1) {
+        return t("State.InTask")
+    } else if (status == 2) {
+        return t("UtaskList.item6")
+    } else if (status == 3) {
+        return t("State.Cancelled")
+    } else if (status == 4) {
+        return t("State.Ended")
+    }
+    return ""
+}
+
+const getStatusStyle = (status: number) => {
+    if (status == 1) {
+        return "color: #ffd591;"
+    } else if (status == 2) {
+        return "color: #52c41a;"
+    } else if (status == 3) {
+        return "color: #999999;"
+    } else if (status == 4) {
+        return "color: #999999;"
+    }
+    return "color: #333;"
+}
+
+const getGiveStatusText = (giveStatus: number) => {
+    if (giveStatus == 1) {
+        return t("State.NotApply")
+    } else if (giveStatus == 2) {
+        return t("State.Applied")
+    } else if (giveStatus == 3) {
+        return t("State.Granted")
+    }
+    return ""
+}
+
+const getGiveStatusStyle = (giveStatus: number) => {
+    if (giveStatus == 1) {
+        return "color: #999999;"
+    } else if (giveStatus == 2) {
+        return "color: #1890ff;"
+    } else if (giveStatus == 3) {
+        return "color: #52c41a;"
+    }
+    return "color: #333;"
+}
+
+const canPerformAction = (task: any) => {
+    return task.status === 1 || (task.status === 2 && task.withdrawStatus === 1)
+}
+
+const shouldShowCard = (el: any) => {
+    const hasCancelButton = el.status === 1
+    const hasGiftApplyButton = el.status === 2 && el.giveStatus === 1 && lang1.value
+    return hasCancelButton || hasGiftApplyButton
+}
+
+// 恢复信用(原 completeTask)
+const completeTask = async (id: number) => {
+    return new Promise(async (resolve) => {
+        const resConfirm = await uni.showModal({
+            title: t("Msg.SystemPrompt"),
+            content: t("surplusList.item9"),
+            confirmText: t("Btn.Confirm"),
+            cancelText: t("Btn.Cancel"),
+        })
+        if (!resConfirm.confirm) return resolve(null)
+
+        loadingStates.value[id] = "complete"
+        try {
+            const res = await activityApi.ActivitySurplusRecoverCredit({ id })
+            if (res.code == Code.StatusOK) {
+                uni.showToast({ title: t("Msg.Success"), icon: "success" })
+                searchFunc()
+            } else {
+                uni.showToast({ title: res.msg, icon: "none" })
+            }
+        } catch (error) {
+            uni.showToast({ title: t("Msg.Fail"), icon: "none" })
+        } finally {
+            loadingStates.value[id] = null
+            resolve(null)
+        }
+    })
+}
+
+// 提现
+const withdrawTask = async (id: number) => {
+    return new Promise(async (resolve) => {
+        const resConfirm = await uni.showModal({
+            title: t("Msg.SystemPrompt"),
+            content: t("UtaskList.item15"),
+            confirmText: t("Btn.Confirm"),
+            cancelText: t("Btn.Cancel"),
+        })
+        if (!resConfirm.confirm) return resolve(null)
+
+        loadingStates.value[id] = "withdraw"
+        try {
+            const res = await activityApi.UcoinWithdraw({ id })
+            if (res.code == Code.StatusOK) {
+                uni.showToast({ title: t("Msg.Success"), icon: "success" })
+                searchFunc()
+            } else {
+                uni.showToast({ title: res.msg, icon: "none" })
+            }
+        } catch (error) {
+            uni.showToast({ title: t("Msg.Fail"), icon: "none" })
+        } finally {
+            loadingStates.value[id] = null
+            resolve(null)
+        }
+    })
+}
+
+// 取消任务
+const cancelTask = async (id: number) => {
+    try {
+        await confirm({
+            title: t("Msg.SystemPrompt"),
+            content: t("UtaskList.item8"),
+            confirmText: t("Btn.Confirm"),
+            cancelText: t("Btn.Cancel"),
+        })
+        const res = await activityApi.ActivityMonthlyCancel({ id })
+        if (res.code == Code.StatusOK) {
+            uni.showToast({ title: t("UtaskList.item9"), icon: "success" })
+            searchFunc()
+        } else {
+            uni.showToast({ title: res.msg, icon: "none" })
+        }
+    } catch (error) {
+        if (error?.msg) uni.showToast({ title: error.msg, icon: "none" })
+    }
+}
+
+// 礼物申请 - 打开对话框并获取礼品列表
+const applyGift = async (id: number) => {
+    // 重置表单
+    giftForm.value = {
+        id: id,
+        giveCode: "",
+        giveName: "",
+        giveAddress: "",
+        givePhone: "",
+        giveAcceptName: "",
+    }
+    giftList.value = []
+
+    // 获取礼品列表
+    try {
+        const res = await activityApi.ActivityMonthlyGiveList({ id })
+        if (res.code == Code.StatusOK) {
+            giftList.value = res.data || []
+            dialogGiftApplication.value = true
+        } else {
+            uni.showToast({ title: res.msg, icon: "none" })
+        }
+    } catch (error) {
+        uni.showToast({ title: t("Msg.Fail"), icon: "none" })
+    }
+}
+
+// 处理礼品选择变化
+const handleGiftChange = (giveCode: string) => {
+    const selectedGift = giftList.value.find(gift => gift.giveCode === giveCode)
+    if (selectedGift) {
+        giftForm.value.giveName = selectedGift.giveName
+    }
+}
+
+// 提交礼物申请
+const submitGiftApply = async (form) => {
+    // 表单验证(假设模板中有 ref="giftForm" 的 uni-forms 组件)
+    // 这里需要适配实际验证方式,简化起见调用一个验证函数,若验证失败则返回
+    // 由于 uni-app 中没有直接提供 validate,需根据实际组件实现,此处保留原逻辑结构
+    // 实际使用时请配合 uni-forms 或自定义验证
+    const valid = true // 占位,实际需调用 this.$refs.giftForm.validate()
+    if (!valid) return
+
+    const resConfirm = await uni.showModal({
+        title: t("Msg.SystemPrompt"),
+        content: t("MonthlyActivities.item10"),
+        confirmText: t("Btn.Confirm"),
+        cancelText: t("Btn.Cancel"),
+    })
+    if (!resConfirm.confirm) return
+
+    giftSubmitting.value = true
+    try {
+        const res = await activityApi.ActivityMonthlyGiveApply({
+            id: giftForm.value.id,
+            giveCode: form.giveCode,
+            giveName: form.giveName,
+            giveAddress: form.giveAddress.trim(),
+            givePhone: form.givePhone.trim(),
+        })
+        if (res.code == Code.StatusOK) {
+            uni.showToast({ title: t("Msg.Success"), icon: "success" })
+            dialogGiftApplication.value = false
+            searchFunc()
+        } else {
+            uni.showToast({ title: res.msg, icon: "none" })
+        }
+    } catch (error) {
+        uni.showToast({ title: t("Msg.Fail"), icon: "none" })
+    } finally {
+        giftSubmitting.value = false
+    }
+}
+
+// 查看任务进度
+const viewTaskProgress = async (id: number) => {
+    loadingStates.value[id] = "progress"
+    try {
+        const res = await activityApi.UcoinProgress()
+        if (res.code == Code.StatusOK) {
+            uni.showToast({ title: res.msg, icon: "none" })
+        } else {
+            uni.showToast({ title: res.msg, icon: "none" })
+        }
+    } catch (error) {
+        uni.showToast({ title: t("Msg.Fail"), icon: "none" })
+    } finally {
+        loadingStates.value[id] = null
+    }
+}
+
+// 计算卡片数据(保留原方法体)
+const calculateCardData = () => {
+    // 可根据需求实现
+}
+
+// 返回活动
+const backActivity = () => {
+    uni.navigateBack()
+}
+
+// 获取列表
+const searchFunc = async () => {
+    if (flag.value) return
+    flag.value = true
+
+    pictLoading.value = true
+    let res = await activityApi.ActivityMonthlyTaskList()
+    if (res.code == Code.StatusOK) {
+        tableData.value = res.data
+        calculateCardData()
+        uni.showToast({ title: t("Msg.SearchSuccess"), icon: "success" })
+        pictLoading.value = false
+        flag.value = false
+    } else {
+        uni.showToast({ title: res.msg, icon: "none" })
+        pictLoading.value = false
+        flag.value = false
+    }
+}
+
+// ---------- 生命周期与监听 ----------
+onMounted(() => {
+    searchFunc()
+})
+
+// 原 watch(保留)
+watch(
+    () => search.value.type,
+    () => {
+        searchFunc()
+    }
+)
+
+// 若需要 uni-app 页面生命周期可额外添加 onShow 等
+// onShow(() => {})
+</script>
+
+<style lang="scss" scoped>
+@import "@/uni.scss";
+
+#custom_history {
+    width: 100%;
+    height: 100%;
+
+    .no-data-container {
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        height: px2rpx(300);
+
+        .no-data-content {
+            text-align: center;
+            color: #999;
+
+            i {
+                font-size: px2rpx(48);
+                margin-bottom: px2rpx(16);
+                display: block;
+            }
+
+            p {
+                font-size: px2rpx(16);
+                margin: 0;
+            }
+        }
+    }
+
+    .main-content {
+        width: 100%;
+        height: calc(100% - 50px);
+        // @include bg_white();
+        padding: px2rpx(20);
+        box-sizing: border-box;
+        overflow: hidden;
+        overflow-y: auto;
+    }
+
+    .state.btn {
+        background: #eb3f57;
+        color: white;
+        padding: px2rpx(3) px2rpx(15);
+        border-radius: px2rpx(4);
+    }
+
+    .action-buttons {
+        display: flex;
+        flex-direction: column;
+        gap: px2rpx(8);
+        align-items: center;
+
+        .el-button {
+            min-width: px2rpx(80);
+            font-size: px2rpx(12);
+        }
+
+        .status-text {
+            font-size: px2rpx(14);
+            color: #666;
+            font-weight: 500;
+        }
+    }
+
+    // 外层卡片样式
+    .outer-card {
+        background: #ffffff;
+        border-radius: px2rpx(16);
+        padding: px2rpx(24);
+        box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
+        border: 1px solid #f0f0f0;
+        margin-bottom: px2rpx(30);
+    }
+
+    // 数据卡片样式
+    .data-cards {
+        display: flex;
+        flex-direction: column;
+        gap: px2rpx(20);
+
+        .total-data-row {
+            display: grid;
+            grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
+            gap: px2rpx(15);
+        }
+
+        .sub-data-row {
+            display: grid;
+            grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
+            gap: px2rpx(15);
+        }
+
+        .data-card {
+            background: #ffffff;
+            border-radius: px2rpx(12);
+            padding: px2rpx(24);
+            //   box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
+            border: 1px solid #f0f0f0;
+            transition: all 0.3s ease;
+            text-align: center;
+
+            &:hover {
+                transform: translateY(-2px);
+                box-shadow: 0 4px 20px rgba(0, 0, 0, 0.12);
+            }
+
+            // 根据状态设置背景色
+            &.status-1 {
+                background: #fff7e6; // 任务中 - 黄色背景
+                border-color: #ffd591;
+            }
+
+            &.status-2 {
+                background: #f6ffed; // 已完成 - 绿色背景
+                border-color: #b7eb8f;
+            }
+
+            &.status-3,
+            &.status-4 {
+                background: #f5f5f5; // 已取消/已结束 - 灰色背景
+                border-color: #d9d9d9;
+            }
+
+            &.total-card {
+                background: #ffffff;
+                color: #333;
+
+                // border: 1px solid gray;
+                .card-value {
+                    color: #333;
+                }
+
+                .card-desc {
+                    color: #999;
+                }
+
+                // 状态卡片的特殊样式
+                &.status-1 {
+                    background: #fff7e6;
+                    border-color: #ffd591;
+                }
+
+                &.status-2 {
+                    background: #f6ffed;
+                    border-color: #b7eb8f;
+                }
+
+                &.status-3,
+                &.status-4 {
+                    background: #f5f5f5;
+                    border-color: #d9d9d9;
+                }
+            }
+
+            &.sub-card {
+                background: #ffffff;
+                border-left: px2rpx(4) solid #667eea;
+
+                &:nth-child(2) {
+                    border-left-color: #52c41a;
+                }
+
+                &:nth-child(3) {
+                    border-left-color: #faad14;
+                }
+
+                &:nth-child(4) {
+                    border-left-color: #ff4d4f;
+                }
+            }
+
+            .card-content {
+                .card-title {
+                    font-size: px2rpx(16);
+                    color: #666;
+                    margin-bottom: px2rpx(8);
+                    font-weight: 500;
+                }
+
+                .card-value {
+                    font-size: px2rpx(18);
+                    font-weight: 700;
+                    color: #333;
+                    margin-bottom: px2rpx(4);
+                    line-height: 1;
+                }
+
+                .card-desc {
+                    font-size: px2rpx(12);
+                    color: #999;
+                    line-height: 1.4;
+                }
+            }
+        }
+
+        .btn-card {
+            display: flex;
+            justify-content: center;
+            align-items: center;
+            flex-direction: column;
+            gap: px2rpx(10);
+
+            button {
+                width: 100%;
+            }
+        }
+    }
+
+    // 响应式设计
+    @media (max-width: 768px) {
+        .data-cards {
+
+            .total-data-row,
+            .sub-data-row {
+                grid-template-columns: 1fr;
+            }
+
+            .data-card {
+                padding: px2rpx(16);
+
+                .card-content {
+                    .card-value {
+                        font-size: px2rpx(24);
+                    }
+                }
+            }
+        }
+    }
+}
+</style>

+ 551 - 0
pages/activities/surplus-list.vue

@@ -0,0 +1,551 @@
+<template>
+    <cwg-page-wrapper class="create-page" :isHeaderFixed="true">
+        <cwg-header :title="t('wallet.item52')" />
+        <view id="custom_history" class="">
+
+            <view class="main-content">
+                <!-- 无任务列表提示 -->
+                <cwg-empty-state v-if="!tableData || tableData.length === 0" />
+
+                <!-- 数据卡片展示 -->
+                <view class="outer-card" v-for="(item, index) in tableData" :key="index"
+                    v-show="tableData && tableData.length > 0">
+                    <view class="data-cards">
+                        <!-- 第一行:总数据 -->
+                        <view class="total-data-row">
+                            <view class="data-card total-card">
+                                <view class="card-content">
+                                    <view class="card-title">{{ t("UtaskList.item13") }}</view>
+                                    <view class="card-value" style="color: #ff4d4f">
+                                        {{ item.depositAmount || 0 }}
+                                    </view>
+                                </view>
+                            </view>
+                            <view class="data-card total-card">
+                                <view class="card-content">
+                                    <view class="card-title">{{ t("UtaskList.item3") }}</view>
+                                    <view class="card-value" style="color: #ff4d4f">
+                                        {{ item.completeVolume || 0 }}
+                                    </view>
+                                </view>
+                            </view>
+                            <view class="data-card total-card">
+                                <view class="card-content">
+                                    <view class="card-title">{{ t("surplusList.item8") }}</view>
+                                    <view class="card-value" style="color: #ff4d4f">
+                                        {{ item.amount }}
+                                    </view>
+                                </view>
+                            </view>
+                            <view class="data-card total-card">
+                                <view class="card-content">
+                                    <view class="card-title">{{ t("Label.State") }}</view>
+                                    <view class="card-value" :style="item.status == 1 ? 'color: #ffd591;' : 'color: #52c41a;'
+                                        ">
+                                        {{ item.status == 1 ? t("State.Ongoing") : t("State.Completed") }}
+                                    </view>
+                                </view>
+                            </view>
+                        </view>
+
+                        <!-- 第二行:分数据 -->
+                        <view class="sub-data-row">
+                            <view class="data-card">
+                                <view class="card-content">
+                                    <view class="card-value">{{ item.revokeCredit == 1 ? t("surplusList.Item5") :
+                                        t("surplusList.item6") }}</view>
+                                    <view class="card-desc">{{ t("surplusList.item4") }}</view>
+                                </view>
+                            </view>
+                            <view class="data-card">
+                                <view class="card-content">
+                                    <view class="card-value">{{ item.needVolume || 0 }}</view>
+                                    <view class="card-desc">{{ t("UtaskList.Item5") }}</view>
+                                </view>
+                            </view>
+                            <view class="data-card">
+                                <view class="card-content">
+                                    <view class="card-value">{{ item.amount }}</view>
+                                    <view class="card-desc">{{ t("UtaskList.item14") }}</view>
+                                </view>
+                            </view>
+                            <view class="data-card">
+                                <view class="card-content">
+                                    <view class="card-value">{{ item.endTime }}</view>
+                                    <view class="card-desc">{{ t("wallet.item55") }}</view>
+                                </view>
+                            </view>
+                            <view class="data-card">
+                                <view class="card-content">
+                                    <view class="card-value">{{ item.endDate }}</view>
+                                    <view class="card-desc">{{ t("UtaskList.item10") }}</view>
+                                </view>
+                            </view>
+                            <view class="data-card" v-show="shouldShowCard(item, item)">
+                                <button v-if="item.status === 1 && item.revokeCredit === 2" type="primary" size="small"
+                                    @click="completeTask(item.id)" :loading="loadingStates[item.id] === 'complete'">
+                                    {{ t("surplusList.item7") }}
+                                </button>
+                                <button v-if="item.status === 1" type="danger" size="small" @click="cancelTask(item.id)"
+                                    :loading="loadingStates[item.id] === 'cancel'">
+                                    {{ t("Btn.Cancel") }}
+                                </button>
+                            </view>
+                        </view>
+                    </view>
+                </view>
+            </view>
+        </view>
+    </cwg-page-wrapper>
+</template>
+
+<script setup lang="ts">
+import { ref, computed, onMounted, watch } from 'vue'
+import { onLoad, onPullDownRefresh, onReachBottom } from '@dcloudio/uni-app'
+import { useI18n } from 'vue-i18n'
+import { activityApi } from "@/service/activity"
+import Config from "@/config/index"
+import { useConfirm } from '@/hooks/useConfirm'
+const confirm = useConfirm()
+const { t, locale } = useI18n()
+let { Code } = Config
+
+// ---------- 存储辅助函数(模拟原 Session) ----------
+const Session = {
+    Get(key: string, parse = false) {
+        const value = uni.getStorageSync(key)
+        if (parse && value) {
+            try {
+                return JSON.parse(value)
+            } catch {
+                return value
+            }
+        }
+        return value
+    }
+}
+
+// ---------- 响应式数据 ----------
+const flag = ref(false)
+const reasons = ref({})
+const pictLoading = ref(false)
+const tableData = ref<any[]>([])
+const time = ref("")
+const loadingStates = ref<Record<string, string | null>>({})
+
+// 卡片数据
+const totalTasks = ref(0)
+const totalRewards = ref(0)
+const completionRate = ref("0")
+const inProgressTasks = ref(0)
+const completedTasks = ref(0)
+const expiredTasks = ref(0)
+const rejectedTasks = ref(0)
+const todayRewards = ref(0)
+const weekRewards = ref(0)
+const monthRewards = ref(0)
+const activityLevel = ref("0")
+
+// 原 watch 中依赖的 search(保留)
+const search = ref({ type: "" })
+
+// ---------- 计算属性 ----------
+const expireTime = computed(() => {
+    return JSON.parse(Session.Get("user", true))
+})
+
+// 注意:document.body.clientWidth 在 uni-app 中不可用,改为获取屏幕宽度
+const getScreenWidth = () => {
+    const systemInfo = uni.getSystemInfoSync()
+    return systemInfo.windowWidth
+}
+
+const lang = computed(() => {
+    return (Session.Get("lang") == "en" && getScreenWidth() < 1330)
+})
+
+// ---------- 方法 ----------
+const getStatus = (status: number) => {
+    if (status == 1) {
+        return t("State.Ongoing")
+    } else if (status == 2) {
+        return t("State.Completed")
+    } else if (status == 3) {
+        return t("State.Cancelled")
+    } else if (status == 4) {
+        return t("State.expireTime")
+    }
+}
+
+const canPerformAction = (task: any) => {
+    return task.status === 1 || (task.status === 2 && task.withdrawStatus === 1)
+}
+
+const shouldShowCard = (el: any) => {
+    const hasRecoverCreditButton = el.status === 1 && el.revokeCredit === 2
+    const hasCancelButton = el.status === 1
+    return hasRecoverCreditButton || hasCancelButton
+}
+
+// 恢复信用
+const completeTask = async (id: number) => {
+    try {
+        await confirm({
+            title: t("Msg.SystemPrompt"),
+            content: t("surplusList.item9"),
+            confirmText: t("Btn.Confirm"),
+            cancelText: t("Btn.Cancel"),
+        })
+        const res = await activityApi.ActivitySurplusRecoverCredit({ id })
+        if (res.code == Code.StatusOK) {
+            uni.showToast({ title: t("UtaskList.item9"), icon: "success" })
+            searchFunc()
+        } else {
+            uni.showToast({ title: res.msg, icon: "none" })
+        }
+    } catch (error) {
+        if (error?.msg) uni.showToast({ title: error.msg, icon: "none" })
+    }
+}
+
+// 提现
+const withdrawTask = async (id: number) => {
+    return new Promise(async (resolve) => {
+        const resConfirm = await uni.showModal({
+            title: t("Msg.SystemPrompt"),
+            content: t("UtaskList.item15"),
+            confirmText: t("Btn.Confirm"),
+            cancelText: t("Btn.Cancel"),
+        })
+        if (!resConfirm.confirm) return resolve(null)
+
+        loadingStates.value[id] = "withdraw"
+        try {
+            const res = await activityApi.UcoinWithdraw({ id })
+            if (res.code == Code.StatusOK) {
+                uni.showToast({ title: t("Msg.Success"), icon: "success" })
+                searchFunc()
+            } else {
+                uni.showToast({ title: res.msg, icon: "none" })
+            }
+        } catch (error) {
+            uni.showToast({ title: t("Msg.Fail"), icon: "none" })
+        } finally {
+            loadingStates.value[id] = null
+            resolve(null)
+        }
+    })
+}
+// 取消任务
+const cancelTask = async (id: number) => {
+    try {
+        await confirm({
+            title: t("Msg.SystemPrompt"),
+            content: t("UtaskList.item8"),
+            confirmText: t("Btn.Confirm"),
+            cancelText: t("Btn.Cancel"),
+        })
+        const res = await activityApi.ActivityMonthlyCancel({ id })
+        if (res.code == Code.StatusOK) {
+            uni.showToast({ title: t("UtaskList.item9"), icon: "success" })
+            searchFunc()
+        } else {
+            uni.showToast({ title: res.msg, icon: "none" })
+        }
+    } catch (error) {
+        if (error?.msg) uni.showToast({ title: error.msg, icon: "none" })
+    }
+}
+
+// 查看任务进度
+const viewTaskProgress = async (id: number) => {
+    loadingStates.value[id] = "progress"
+    try {
+        const res = await activityApi.UcoinProgress()
+        if (res.code == Code.StatusOK) {
+            uni.showToast({ title: res.msg, icon: "none" })
+        } else {
+            uni.showToast({ title: res.msg, icon: "none" })
+        }
+    } catch (error) {
+        uni.showToast({ title: t("Msg.Fail"), icon: "none" })
+    } finally {
+        loadingStates.value[id] = null
+    }
+}
+
+// 计算卡片数据
+const calculateCardData = () => {
+    // 可根据需求实现
+}
+
+// 返回活动
+const backActivity = () => {
+    uni.navigateTo({ url: "/customer/activities" })
+}
+
+// 获取列表
+const searchFunc = async () => {
+    if (flag.value) return
+    flag.value = true
+
+    pictLoading.value = true
+    let res = await activityApi.ActivitySurplusTaskList()
+    if (res.code == Code.StatusOK) {
+        tableData.value = res.data
+        calculateCardData()
+        uni.showToast({ title: t("Msg.SearchSuccess"), icon: "success" })
+        pictLoading.value = false
+        flag.value = false
+    } else {
+        uni.showToast({ title: res.msg, icon: "none" })
+        pictLoading.value = false
+        flag.value = false
+    }
+}
+
+// ---------- 生命周期与监听 ----------
+onMounted(() => {
+    searchFunc()
+})
+
+// 原 watch(保留)
+watch(
+    () => search.value.type,
+    () => {
+        searchFunc()
+    }
+)
+</script>
+
+<style lang="scss" scoped>
+@import "@/uni.scss";
+
+#custom_history {
+    width: 100%;
+    height: 100%;
+
+    .no-data-container {
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        height: px2rpx(300);
+
+        .no-data-content {
+            text-align: center;
+            color: #999;
+
+            i {
+                font-size: px2rpx(48);
+                margin-bottom: px2rpx(16);
+                display: block;
+            }
+
+            p {
+                font-size: px2rpx(16);
+                margin: 0;
+            }
+        }
+    }
+
+    .main-content {
+        width: 100%;
+        height: calc(100% - 50px);
+        // @include bg_white();
+        padding: px2rpx(20);
+        box-sizing: border-box;
+        overflow: hidden;
+        overflow-y: auto;
+    }
+
+    .state.btn {
+        background: #eb3f57;
+        color: white;
+        padding: px2rpx(3) px2rpx(15);
+        border-radius: px2rpx(4);
+    }
+
+    .action-buttons {
+        display: flex;
+        flex-direction: column;
+        gap: px2rpx(8);
+        align-items: center;
+
+        .el-button {
+            min-width: px2rpx(80);
+            font-size: px2rpx(12);
+        }
+
+        .status-text {
+            font-size: px2rpx(14);
+            color: #666;
+            font-weight: 500;
+        }
+    }
+
+    // 外层卡片样式
+    .outer-card {
+        background: #ffffff;
+        border-radius: px2rpx(16);
+        padding: px2rpx(24);
+        box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
+        border: 1px solid #f0f0f0;
+        margin-bottom: px2rpx(30);
+    }
+
+    // 数据卡片样式
+    .data-cards {
+        display: flex;
+        flex-direction: column;
+        gap: px2rpx(20);
+
+        .total-data-row {
+            display: grid;
+            grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
+            gap: px2rpx(15);
+        }
+
+        .sub-data-row {
+            display: grid;
+            grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
+            gap: px2rpx(15);
+        }
+
+        .data-card {
+            background: #ffffff;
+            border-radius: px2rpx(12);
+            padding: px2rpx(24);
+            //   box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
+            border: 1px solid #f0f0f0;
+            transition: all 0.3s ease;
+            text-align: center;
+
+            &:hover {
+                transform: translateY(-2px);
+                box-shadow: 0 4px 20px rgba(0, 0, 0, 0.12);
+            }
+
+            // 根据状态设置背景色
+            &.status-1 {
+                background: #fff7e6; // 任务中 - 黄色背景
+                border-color: #ffd591;
+            }
+
+            &.status-2 {
+                background: #f6ffed; // 已完成 - 绿色背景
+                border-color: #b7eb8f;
+            }
+
+            &.status-3,
+            &.status-4 {
+                background: #f5f5f5; // 已取消/已结束 - 灰色背景
+                border-color: #d9d9d9;
+            }
+
+            &.total-card {
+                background: #ffffff;
+                color: #333;
+
+                // border: 1px solid gray;
+                .card-value {
+                    color: #333;
+                }
+
+                .card-desc {
+                    color: #999;
+                }
+
+                // 状态卡片的特殊样式
+                &.status-1 {
+                    background: #fff7e6;
+                    border-color: #ffd591;
+                }
+
+                &.status-2 {
+                    background: #f6ffed;
+                    border-color: #b7eb8f;
+                }
+
+                &.status-3,
+                &.status-4 {
+                    background: #f5f5f5;
+                    border-color: #d9d9d9;
+                }
+            }
+
+            &.sub-card {
+                background: #ffffff;
+                border-left: px2rpx(4) solid #667eea;
+
+                &:nth-child(2) {
+                    border-left-color: #52c41a;
+                }
+
+                &:nth-child(3) {
+                    border-left-color: #faad14;
+                }
+
+                &:nth-child(4) {
+                    border-left-color: #ff4d4f;
+                }
+            }
+
+            .card-content {
+                .card-title {
+                    font-size: px2rpx(16);
+                    color: #666;
+                    margin-bottom: px2rpx(8);
+                    font-weight: 500;
+                }
+
+                .card-value {
+                    font-size: px2rpx(18);
+                    font-weight: 700;
+                    color: #333;
+                    margin-bottom: px2rpx(4);
+                    line-height: 1;
+                }
+
+                .card-desc {
+                    font-size: px2rpx(12);
+                    color: #999;
+                    line-height: 1.4;
+                }
+            }
+        }
+
+        .btn-card {
+            display: flex;
+            justify-content: center;
+            align-items: center;
+            flex-direction: column;
+            gap: px2rpx(10);
+
+            button {
+                width: 100%;
+            }
+        }
+    }
+
+    // 响应式设计
+    @media (max-width: 768px) {
+        .data-cards {
+
+            .total-data-row,
+            .sub-data-row {
+                grid-template-columns: 1fr;
+            }
+
+            .data-card {
+                padding: px2rpx(16);
+
+                .card-content {
+                    .card-value {
+                        font-size: px2rpx(24);
+                    }
+                }
+            }
+        }
+    }
+}
+</style>

+ 903 - 11
pages/common/download.vue

@@ -1,23 +1,915 @@
 <template>
     <cwg-page-wrapper class="create-page" :isHeaderFixed="true">
-        <cwg-header :title="t('Home.page_ib.item3')" />
-        <view class="account-section">
+        <cwg-header :title="t('Downloadpage.item1')" />
+        <view id="custom_Downloadpage">
+            <view class="main-content">
+                <cwg-asset-tabs v-model="activeName" :tabs="tabsConfig" />
+                <view v-if="activeName == 'MT4'">
+                    <view class="download-section">
+                        <view class="section-header">
+                            <view class="section-title" v-t="'Downloadpage.item2'" />
+                            <view class="section-subtitle" v-t="'Downloadpage.item3'" />
+                        </view>
+                        <view class="download-cards">
+                            <a class="download-card" :href="myLink + '/mt/cwgmarketssvgltd4setup.exe'"
+                                download="cwgmarketssvgltd4setup.exe">
+                                <image class="card-icon" src="/static/images/windows-os-1-48.png" alt=""
+                                    mode="widthFix" />
+                                <view class="card-info">
+                                    <view class="card-title" v-t="'Downloadpage.item4'" />
+                                    <view class="card-desc" v-t="'Downloadpage.item5'" />
+                                </view>
+                                <view class="download-badge">Windows</view>
+                            </a>
+                            <a class="download-card" :href="myLink + '/mt/mt4.zip'" download="mt4.zip">
+                                <image class="card-icon" src="/static/images/windows-os-1-48.png" alt=""
+                                    mode="widthFix" />
+                                <view class="card-info">
+                                    <view class="card-title" v-t="'Downloadpage.item4-1'" />
+                                    <view class="card-desc" v-t="'Downloadpage.item5'" />
+                                </view>
+                                <view class="download-badge">ZIP</view>
+                            </a>
+                            <a class="download-card" :href="myLink + '/mt/MetaTrader4.pkg'" download="MetaTrader4.pkg">
+                                <image class="card-icon" src="/static/images/apple-os-1-48.png" alt=""
+                                    mode="widthFix" />
+                                <view class="card-info">
+                                    <view class="card-title" v-t="'Downloadpage.item6'" />
+                                    <view class="card-desc" v-t="'Downloadpage.item5'" />
+                                </view>
+                                <view class="download-badge download-badge-apple">macOS</view>
+                            </a>
+                        </view>
+                    </view>
+                    <view class="download-section web-section">
+                        <view class="section-header">
+                            <view class="section-title" v-t="'Downloadpage.item38'" />
+                        </view>
+                        <view class="download-cards">
+                            <a class="download-card download-card-web" target="_blank"
+                                :href="'https://www.' + link + '.com/' + getLang(lang) + '/mt4/web'">
+                                <view class="card-info">
+                                    <view class="card-title" v-t="'Downloadpage.item40'" />
+                                </view>
+                                <text class="icon-arrow">→</text>
+                            </a>
+                        </view>
+                    </view>
+                    <view class="download-section mobile-section">
+                        <view class="section-header">
+                            <view class="section-title" v-t="'Downloadpage.item7'" />
+                        </view>
+                        <view class="mobile-cards">
+                            <view class="mobile-card">
+                                <view class="mobile-card-header">
+                                    <image class="card-icon" src="/static/images/android-os-3-72.png" alt=""
+                                        mode="widthFix" />
+                                    <view class="card-info">
+                                        <view class="card-title" v-t="'Downloadpage.item8'" />
+                                        <view class="card-desc" v-t="'Downloadpage.item9'" />
+                                    </view>
+                                </view>
+                                <view class="qr-codes">
+                                    <view class="qr-item">
+                                        <view class="qr-label">Google Play</view>
+                                        <QrCode :text="mt41" :logoImage="logoImage"></QrCode>
+                                    </view>
+                                    <view class="qr-item">
+                                        <view class="qr-label">MetaTrader .Apk</view>
+                                        <QrCode :text="mt42" :logoImage="logoImage"></QrCode>
+                                    </view>
+                                </view>
+                            </view>
+                            <view class="mobile-card">
+                                <view class="mobile-card-header">
+                                    <image class="card-icon" src="/static/images/apple-os-3-72.png" alt=""
+                                        mode="widthFix" />
+                                    <view class="card-info">
+                                        <view class="card-title" v-t="'Downloadpage.item8'" />
+                                        <view class="card-desc" v-t="'Downloadpage.item10'" />
+                                    </view>
+                                </view>
+                                <view class="qr-codes qr-codes-single">
+                                    <view class="qr-item">
+                                        <view class="qr-label">App Store</view>
+                                        <QrCode :text="mt43" :logoImage="logoImage"></QrCode>
+                                    </view>
+                                </view>
+                            </view>
+                        </view>
+                    </view>
+
+                </view>
+                <view v-if="activeName == 'MT5'">
+                    <view class="download-section">
+                        <view class="section-header">
+                            <view class="section-title" v-t="'Downloadpage.item11'" />
+                            <view class="section-subtitle" v-t="'Downloadpage.item12'" />
+                        </view>
+                        <view class="download-cards">
+                            <a class="download-card" :href="myLink + '/mt/cwgmarketssvg5setup.exe'"
+                                download="cwgmarketssvg5setup.exe">
+                                <image class="card-icon" src="/static/images/windows-os-1-48.png" alt=""
+                                    mode="widthFix" />
+                                <view class="card-info">
+                                    <view class="card-title" v-t="'Downloadpage.item4'" />
+                                    <view class="card-desc" v-t="'Downloadpage.item5'" />
+                                </view>
+                                <view class="download-badge">Windows</view>
+                            </a>
+                            <a class="download-card" :href="myLink + '/mt/mt5.zip'" download="mt5.zip">
+                                <image class="card-icon" src="/static/images/windows-os-1-48.png" alt=""
+                                    mode="widthFix" />
+                                <view class="card-info">
+                                    <view class="card-title" v-t="'Downloadpage.item4-1'" />
+                                    <view class="card-desc" v-t="'Downloadpage.item5'" />
+                                </view>
+                                <view class="download-badge">ZIP</view>
+                            </a>
+                            <a class="download-card" :href="myLink + '/mt/MetaTrader5.pkg'" download="MetaTrader5.dmg">
+                                <image class="card-icon" src="/static/images/apple-os-1-48.png" alt=""
+                                    mode="widthFix" />
+                                <view class="card-info">
+                                    <view class="card-title" v-t="'Downloadpage.item6'" />
+                                    <view class="card-desc" v-t="'Downloadpage.item5'" />
+                                </view>
+                                <view class="download-badge download-badge-apple">macOS</view>
+                            </a>
+                        </view>
+                    </view>
+                    <view class="download-section web-section">
+                        <view class="section-header">
+                            <view class="section-title" v-t="'Downloadpage.item39'" />
+                        </view>
+                        <view class="download-cards">
+                            <a class="download-card download-card-web" target="_blank"
+                                :href="'https://www.' + link + '.com/' + getLang(lang) + '/mt5/web'">
+                                <view class="card-info">
+                                    <view class="card-title" v-t="'Downloadpage.item40'" />
+                                </view>
+                                <text class="icon-arrow">→</text>
+                            </a>
+                        </view>
+                    </view>
+                    <view class="download-section mobile-section">
+                        <view class="section-header">
+                            <view class="section-title" v-t="'Downloadpage.item13'" />
+                        </view>
+                        <view class="mobile-cards">
+                            <view class="mobile-card">
+                                <view class="mobile-card-header">
+                                    <image class="card-icon" src="/static/images/android-os-3-72.png" alt=""
+                                        mode="widthFix" />
+                                    <view class="card-info">
+                                        <view class="card-title" v-t="'Downloadpage.item8'" />
+                                        <view class="card-desc" v-t="'Downloadpage.item14'" />
+                                    </view>
+                                </view>
+                                <view class="qr-codes">
+                                    <view class="qr-item">
+                                        <view class="qr-label">Google Play</view>
+                                        <QrCode :text="mt51" :logoImage="logoImage"></QrCode>
+                                    </view>
+                                    <view class="qr-item">
+                                        <view class="qr-label">MetaTrader .Apk</view>
+                                        <QrCode :text="mt52" :logoImage="logoImage"></QrCode>
+                                    </view>
+                                </view>
+                            </view>
+                            <view class="mobile-card">
+                                <view class="mobile-card-header">
+                                    <image class="card-icon" src="/static/images/apple-os-3-72.png" alt=""
+                                        mode="widthFix" />
+                                    <view class="card-info">
+                                        <view class="card-title" v-t="'Downloadpage.item8'" />
+                                        <view class="card-desc" v-t="'Downloadpage.item15'" />
+                                    </view>
+                                </view>
+                                <view class="qr-codes qr-codes-single">
+                                    <view class="qr-item">
+                                        <view class="qr-label">App Store</view>
+                                        <QrCode :text="mt53" :logoImage="logoImage"></QrCode>
+                                    </view>
+                                </view>
+                            </view>
+                        </view>
+                    </view>
+                </view>
+                <view v-if="activeName == 'Instruments'">
+                    <view class="instruments-banner">
+                        <view class="banner-content">
+                            <view class="banner-title" v-t="'Downloadpage.item18'" />
+                            <view class="banner-subtitle" v-t="'Downloadpage.item19'" />
+                        </view>
+                        <view class="banner-buttons">
+                            <a class="banner-btn" style="padding: 10px 25px;min-width: 220px;"
+                                href="https://mt.tradingcentral.cn/download"
+                                v-if="['cn', 'zhHant'].indexOf(Session.Get('lang')) != -1">
+                                <view v-t="'Downloadpage.item20'" />
+                                <view class="btn-tag">中国大陆用户</view>
+                            </a>
+                            <a class="banner-btn" style="padding: 10px 25px;min-width: 220px;"
+                                href="https://mt.tradingcentral.com/download"
+                                v-if="['cn', 'zhHant'].indexOf(Session.Get('lang')) != -1">
+                                <view v-t="'Downloadpage.item20'" />
+                                <view class="btn-tag">港澳台地区及海外用户</view>
+                            </a>
+                            <a class="banner-btn" href="https://mt.tradingcentral.com/download"
+                                v-if="['cn', 'zhHant'].indexOf(Session.Get('lang')) == -1">
+                                <span v-t="'Downloadpage.item20'"></span>
+                            </a>
+                        </view>
+                    </view>
+                    <view class="features-grid">
+                        <view class="feature-item">
+                            <view class="feature-icon">
+                                <i class="iconfenxi1 iconfont"></i>
+                            </view>
+                            <view class="feature-content">
+                                <view class="feature-title" v-t="'Downloadpage.item21'" />
+                                <view class="feature-desc" v-t="'Downloadpage.item22'" />
+                            </view>
+                        </view>
+                        <view class="feature-item">
+                            <view class="feature-icon">
+                                <i class="iconlazhutu iconfont"></i>
+                            </view>
+                            <view class="feature-content">
+                                <view class="feature-title" v-t="'Downloadpage.item23'" />
+                                <view class="feature-desc" v-t="'Downloadpage.item24'" />
+                            </view>
+                        </view>
+                        <view class="feature-item">
+                            <view class="feature-icon">
+                                <i class="iconzhibiao iconfont"></i>
+                            </view>
+                            <view class="feature-content">
+                                <view class="feature-title" v-t="'Downloadpage.item25'" />
+                                <view class="feature-desc" v-t="'Downloadpage.item26'" />
+                            </view>
+                        </view>
+                    </view>
+                    <view class="info-section">
+                        <view class="info-block">
+                            <view class="info-title"><span v-t="'Downloadpage.item27'"></span></view>
+                            <view class="info-content">
+                                <view class="info-item">
+                                    <view v-t="'Downloadpage.item28'" class="info-label" />
+                                    <view v-t="'Downloadpage.item29'" class="info-text" />
+                                </view>
+                                <view class="info-item info-item-border">
+                                    <view v-t="'Downloadpage.item30'" class="info-label" />
+                                    <view v-t="'Downloadpage.item31'" class="info-text" />
+                                </view>
+                            </view>
+                        </view>
+                    </view>
+                    <view class="info-section">
+                        <view class="info-block">
+                            <view class="info-title"><span v-t="'Downloadpage.item32'"></span></view>
+                            <view class="info-subtitle" v-t="'Downloadpage.item33'" />
+                            <view class="info-content">
+                                <view class="info-item">
+                                    <view v-t="'Downloadpage.item34'" class="info-label" />
+                                    <view v-t="'Downloadpage.item35'" class="info-text" />
+                                    <view class="info-image">
+                                        <image src="/static/images/tc-bg1.jpg" alt="" mode="widthFix" />
+                                    </view>
+                                </view>
+                                <view class="info-item">
+                                    <view v-t="'Downloadpage.item36'" class="info-label" />
+                                    <view v-t="'Downloadpage.item37'" class="info-text" />
+                                </view>
+                            </view>
+                        </view>
+                    </view>
+                </view>
+            </view>
         </view>
     </cwg-page-wrapper>
 </template>
 
 <script setup lang="ts">
-import { ref, reactive, computed, onMounted, onUnmounted } from 'vue'
-import { onLoad } from '@dcloudio/uni-app'
-import { useI18n } from 'vue-i18n' // uni-app 中已集成,但需配置
-import { customApi } from '@/service/custom'
-import { financialApi } from '@/service/financial'
-import Config from '@/config/index'
-import AddBankDialog from '@/components/AddBankDialog.vue';
-import PaymentMethodsList from './components/PaymentMethodsList.vue'
+import { ref, computed, onMounted } from 'vue'
+import QrCode from "@/components/QRCode.vue"
+// import logoImage from "@/assets/images/MTBG.jpg"
+import { useI18n } from 'vue-i18n'
+
 const { t, locale } = useI18n()
-const isZh = computed(() => ['cn', 'zhHant'].includes(locale.value))
+
+// ---------- 存储辅助函数(模拟原 Session) ----------
+const Session = {
+    Get(key: string, parse = false) {
+        const value = uni.getStorageSync(key)
+        if (parse && value) {
+            try {
+                return JSON.parse(value)
+            } catch {
+                return value
+            }
+        }
+        return value
+    }
+}
+
+// ---------- 响应式数据 ----------
+const activeName = ref('MT4')
+const link = ref('')
+const myLink = ref('')
+const logoImageRef = ref('')  // 保持变量名 logoImage
+const mt41 = ref('https://www.metatrader4.com/en/download#download-block-android?server=CWGMarketsSVGLtd-Demo,CWGMarketsSVGLtd-Live')
+const mt42 = ref('')  // 稍后动态计算
+const mt43 = ref('https://apps.apple.com/us/app/metatrader-4/id496212596?server=CWGMarketsSVGLtd-Demo,CWGMarketsSVGLtd-Live')
+const mt51 = ref('https://download.metatrader.com/cdn/mobile/mt5/android?server=CWGMarketsSVG-Demo,CWGMarketsSVG-Live')
+const mt52 = ref('')
+const mt53 = ref('https://download.metatrader.com/cdn/mobile/mt5/ios?server=CWGMarketsSVG-Demo,CWGMarketsSVG-Live')
+
+// ---------- 辅助函数:获取当前域名主体(兼容 H5) ----------
+const getDomainMain = () => {
+    // 在 uni-app H5 环境下可使用 window.location
+    // 若在其他环境(如 App 内嵌 webview)可能没有 window,这里做兼容
+    if (typeof window !== 'undefined' && window.location && window.location.host) {
+        const parts = window.location.host.split('.')
+        return parts.length > 1 ? parts[1] : ''
+    }
+    // 降级方案:从配置或空字符串
+    return ''
+}
+
+const getOrigin = () => {
+    if (typeof window !== 'undefined' && window.location && window.location.origin) {
+        return window.location.origin
+    }
+    return ''
+}
+const tabsConfig = computed(() => [
+    { text: 'MT4', value: 'MT4' },
+    { text: 'MT5', value: 'MT5' },
+])
+// ---------- 计算属性 ----------
+const country = computed(() => {
+    const user = Session.Get("user", true)
+    return user?.customInfo?.country
+})
+
+const lang = computed(() => {
+    return Session.Get('lang')
+})
+
+// ---------- 方法 ----------
+const getLang = (lang: string) => {
+    let val = 'en'
+    if (lang == 'cn') {
+        val = 'cn'
+    } else if (lang == 'zhHant') {
+        val = 'zh'
+    } else {
+        val = 'en'
+    }
+    return val
+}
+
+// ---------- 生命周期 ----------
+onMounted(() => {
+    // 计算动态链接
+    const domainMain = getDomainMain()
+    const origin = getOrigin()
+
+    link.value = domainMain
+    myLink.value = origin
+
+    // 原 mt42 动态拼接
+    mt42.value = `https://secure.${domainMain}.com/metatrader/metatrader4.apk`
+    // 原 mt52 动态拼接
+    mt52.value = `${origin}/mt/metatrader5.apk`
+})
+
+// 组件导出(可选,defineOptions 保留组件名)
+defineOptions({
+    name: "custom_Downloadpage"
+})
 </script>
 <style lang="scss" scoped>
 @import "@/uni.scss";
+
+#custom_Downloadpage {
+    width: 100%;
+    height: 100%;
+
+    .main-content {
+        width: 100%;
+        height: calc(100% - px2rpx(50));
+        padding: px2rpx(10);
+        box-sizing: border-box;
+        overflow-y: auto;
+    }
+
+    .download-section {
+        background: #fff;
+        border-radius: px2rpx(12);
+        padding: px2rpx(24) px2rpx(20);
+        margin-bottom: px2rpx(16);
+        box-shadow: 0 px2rpx(2) px2rpx(12) rgba(0, 0, 0, 0.06);
+
+        .section-header {
+            text-align: center;
+            margin-bottom: px2rpx(24);
+        }
+
+        .section-title {
+            font-size: px2rpx(20);
+            font-weight: 600;
+            color: #333;
+            margin-bottom: px2rpx(8);
+        }
+
+        .section-subtitle {
+            font-size: px2rpx(14);
+            color: #666;
+            line-height: 1.5;
+        }
+    }
+
+    .download-cards {
+        display: flex;
+        justify-content: center;
+        flex-wrap: wrap;
+        gap: px2rpx(16);
+    }
+
+    .download-card {
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        padding: px2rpx(24) px2rpx(20);
+        background: linear-gradient(135deg, #4990EF 0%, #3A7BE0 100%);
+        color: #fff;
+        border-radius: px2rpx(12);
+        min-width: px2rpx(160);
+        max-width: px2rpx(200);
+        text-decoration: none;
+        transition: all 0.3s ease;
+        position: relative;
+        overflow: hidden;
+
+        &::before {
+            content: '';
+            position: absolute;
+            top: 0;
+            left: 0;
+            right: 0;
+            bottom: 0;
+            background: linear-gradient(135deg, rgba(255, 255, 255, 0.1) 0%, transparent 100%);
+        }
+
+        &:hover {
+            transform: translateY(px2rpx(-4));
+            box-shadow: 0 px2rpx(8) px2rpx(24) rgba(73, 144, 239, 0.35);
+        }
+
+        &:active {
+            transform: translateY(px2rpx(-2));
+        }
+
+        .card-icon {
+            width: px2rpx(48);
+            height: px2rpx(48);
+            margin-bottom: px2rpx(16);
+            position: relative;
+            z-index: 1;
+        }
+
+        .card-info {
+            text-align: center;
+            position: relative;
+            z-index: 1;
+
+            .card-title {
+                font-size: px2rpx(15);
+                font-weight: 500;
+                margin-bottom: px2rpx(4);
+            }
+
+            .card-desc {
+                font-size: px2rpx(12);
+                opacity: 0.85;
+            }
+        }
+
+        .download-badge {
+            margin-top: px2rpx(12);
+            padding: px2rpx(4) px2rpx(12);
+            background: rgba(255, 255, 255, 0.2);
+            border-radius: px2rpx(20);
+            font-size: px2rpx(11);
+            font-weight: 500;
+            position: relative;
+            z-index: 1;
+        }
+
+        .download-badge-apple {
+            background: rgba(0, 0, 0, 0.25);
+        }
+    }
+
+    .download-card-web {
+        flex-direction: row;
+        justify-content: space-between;
+        padding: px2rpx(20) px2rpx(24);
+        min-width: px2rpx(280);
+        max-width: 100%;
+        background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+
+        .card-info {
+            text-align: left;
+        }
+
+        .icon-arrow {
+            font-size: px2rpx(20);
+            opacity: 0.9;
+        }
+
+        &:hover {
+            .icon-arrow {
+                transform: translateX(px2rpx(4));
+                transition: transform 0.3s ease;
+            }
+        }
+    }
+
+    .mobile-section {
+        .mobile-cards {
+            display: flex;
+            flex-direction: column;
+            gap: px2rpx(20);
+        }
+
+        .mobile-card {
+            background: #f8f9fa;
+            border-radius: px2rpx(12);
+            padding: px2rpx(20);
+            border: px2rpx(1) solid #eee;
+        }
+
+        .mobile-card-header {
+            display: flex;
+            align-items: center;
+            margin-bottom: px2rpx(20);
+
+            .card-icon {
+                width: px2rpx(40);
+                height: px2rpx(40);
+                margin-right: px2rpx(16);
+            }
+
+            .card-info {
+                text-align: left;
+
+                .card-title {
+                    font-size: px2rpx(16);
+                    font-weight: 600;
+                    color: #333;
+                    margin-bottom: px2rpx(4);
+                }
+
+                .card-desc {
+                    font-size: px2rpx(13);
+                    color: #666;
+                }
+            }
+        }
+
+        .qr-codes {
+            display: flex;
+            justify-content: space-around;
+            flex-wrap: wrap;
+            gap: px2rpx(16);
+
+            &.qr-codes-single {
+                justify-content: center;
+            }
+        }
+
+        .qr-item {
+            text-align: center;
+
+            .qr-label {
+                font-weight: 600;
+                font-size: px2rpx(13);
+                color: #333;
+                margin-bottom: px2rpx(10);
+                line-height: 2;
+            }
+        }
+    }
+
+    .instruments-banner {
+        width: 100%;
+        padding: px2rpx(40) px2rpx(20);
+        background-image: url('/static/images/tc_bg.png');
+        background-size: cover;
+        background-repeat: no-repeat;
+        background-position: center;
+        color: #fff;
+        text-align: center;
+        border-radius: px2rpx(12);
+        margin-bottom: px2rpx(16);
+        box-sizing: border-box;
+
+        .banner-content {
+            margin-bottom: px2rpx(24);
+        }
+
+        .banner-title {
+            font-size: px2rpx(22);
+            font-weight: 600;
+            margin-bottom: px2rpx(8);
+        }
+
+        .banner-subtitle {
+            font-size: px2rpx(14);
+            opacity: 0.9;
+        }
+
+        .banner-buttons {
+            display: flex;
+            justify-content: center;
+            flex-wrap: wrap;
+            gap: px2rpx(12);
+        }
+
+        .banner-btn {
+            display: inline-flex;
+            flex-direction: column;
+            align-items: center;
+            padding: px2rpx(14) px2rpx(25);
+            background-color: #EB3F57;
+            color: #fff;
+            font-size: px2rpx(14);
+            text-decoration: none;
+            border-radius: px2rpx(8);
+            transition: all 0.3s ease;
+            min-width: px2rpx(200);
+
+            &:hover {
+                background-color: #d6364d;
+                transform: translateY(px2rpx(-2));
+                box-shadow: 0 px2rpx(4) px2rpx(12) rgba(235, 63, 87, 0.4);
+            }
+
+            .btn-tag {
+                font-size: px2rpx(12);
+                opacity: 0.85;
+                margin-top: px2rpx(4);
+            }
+        }
+    }
+
+    .features-grid {
+        background: #fff;
+        border-radius: px2rpx(12);
+        padding: px2rpx(24) px2rpx(20);
+        margin-bottom: px2rpx(16);
+        box-shadow: 0 px2rpx(2) px2rpx(12) rgba(0, 0, 0, 0.06);
+
+        .feature-item {
+            display: flex;
+            align-items: center;
+            padding: px2rpx(20) 0;
+            border-bottom: px2rpx(1) solid #f0f0f0;
+
+            &:last-child {
+                border-bottom: none;
+            }
+        }
+
+        .feature-icon {
+            width: px2rpx(64);
+            height: px2rpx(64);
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            background: linear-gradient(135deg, #fef0f0 0%, #ffe8e8 100%);
+            border-radius: px2rpx(12);
+            margin-right: px2rpx(16);
+            flex-shrink: 0;
+
+            i {
+                font-size: px2rpx(32);
+                color: #EB3F57;
+            }
+        }
+
+        .feature-content {
+            flex: 1;
+        }
+
+        .feature-title {
+            font-size: px2rpx(16);
+            font-weight: 600;
+            color: #333;
+            margin-bottom: px2rpx(6);
+        }
+
+        .feature-desc {
+            font-size: px2rpx(14);
+            color: #666;
+            line-height: 1.6;
+        }
+    }
+
+    .info-section {
+        background: #fff;
+        border-radius: px2rpx(12);
+        padding: px2rpx(24) px2rpx(20);
+        margin-bottom: px2rpx(16);
+        box-shadow: 0 px2rpx(2) px2rpx(12) rgba(0, 0, 0, 0.06);
+
+        .info-block {
+            .info-title {
+                font-size: px2rpx(20);
+                font-weight: 600;
+                text-align: center;
+                margin-bottom: px2rpx(20);
+
+                span {
+                    border-bottom: px2rpx(3) solid #EB3F57;
+                    padding: 0 px2rpx(8) px2rpx(8);
+                }
+            }
+
+            .info-subtitle {
+                font-size: px2rpx(14);
+                color: #666;
+                text-align: center;
+                margin-bottom: px2rpx(24);
+            }
+        }
+
+        .info-content {
+            .info-item {
+                padding: px2rpx(20) 0;
+                border-bottom: px2rpx(1) dashed #e0e0e0;
+
+                &:last-child {
+                    border-bottom: none;
+                }
+
+                &.info-item-border {
+                    border-top: px2rpx(1) dashed #e0e0e0;
+                }
+            }
+
+            .info-label {
+                font-size: px2rpx(15);
+                font-weight: 600;
+                color: #333;
+                margin-bottom: px2rpx(8);
+            }
+
+            .info-text {
+                font-size: px2rpx(14);
+                color: #666;
+                line-height: 1.6;
+            }
+
+            .info-image {
+                display: flex;
+                justify-content: center;
+                padding-top: px2rpx(24);
+
+                image {
+                    max-width: 100%;
+                    border-radius: px2rpx(8);
+                }
+            }
+        }
+    }
+
+    @media (max-width: 768px) {
+        .download-section {
+            padding: px2rpx(16) px2rpx(12);
+            border-radius: px2rpx(8);
+
+            .section-title {
+                font-size: px2rpx(18);
+            }
+        }
+
+        .download-cards {
+            gap: px2rpx(12);
+        }
+
+        .download-card {
+            min-width: px2rpx(140);
+            padding: px2rpx(16) px2rpx(12);
+
+            .card-icon {
+                width: px2rpx(40);
+                height: px2rpx(40);
+            }
+
+            .card-info {
+                .card-title {
+                    font-size: px2rpx(13);
+                }
+
+                .card-desc {
+                    font-size: px2rpx(11);
+                }
+            }
+        }
+
+        .download-card-web {
+            width: 100%;
+        }
+
+        .mobile-section {
+            .mobile-card {
+                padding: px2rpx(16);
+            }
+
+            .qr-codes {
+                gap: px2rpx(12);
+            }
+        }
+
+        .instruments-banner {
+            padding: px2rpx(24) px2rpx(16);
+            border-radius: px2rpx(8);
+
+            .banner-title {
+                font-size: px2rpx(18);
+            }
+
+            .banner-btn {
+                min-width: px2rpx(160);
+                padding: px2rpx(12) px2rpx(20);
+            }
+        }
+
+        .features-grid {
+            padding: px2rpx(16) px2rpx(12);
+            border-radius: px2rpx(8);
+
+            .feature-item {
+                flex-direction: column;
+                text-align: center;
+                padding: px2rpx(16) 0;
+            }
+
+            .feature-icon {
+                margin-right: 0;
+                margin-bottom: px2rpx(12);
+            }
+        }
+
+        .info-section {
+            padding: px2rpx(16) px2rpx(12);
+            border-radius: px2rpx(8);
+
+            .info-block {
+                .info-title {
+                    font-size: px2rpx(18);
+                }
+            }
+        }
+    }
+
+    :deep(.el-tabs) {
+        .el-tabs__header {
+            margin-bottom: px2rpx(20);
+        }
+
+        .el-tabs__nav-wrap::after {
+            height: px2rpx(1);
+        }
+
+        .el-tabs__item {
+            font-size: px2rpx(15);
+            color: #999;
+            padding: 0 px2rpx(20);
+            height: px2rpx(44);
+            line-height: px2rpx(44);
+
+            &.is-active {
+                color: #4990EF;
+                font-weight: 600;
+            }
+        }
+
+        .el-tabs__active-bar {
+            height: px2rpx(3);
+            border-radius: px2rpx(3);
+        }
+    }
+
+    :deep(uni-image),
+    :deep(img) {
+        width: px2rpx(30);
+        height: px2rpx(30);
+        object-fit: cover;
+        border-radius: px2rpx(6);
+    }
+}
 </style>