zhb 2 месяцев назад
Родитель
Сommit
4cf10682ce

+ 161 - 0
components/cwg-confirm-popup.vue

@@ -0,0 +1,161 @@
+<template>
+    <uni-popup ref="popup" type="center" :is-mask-click="false" @change="onChange">
+        <view class="confirm-popup">
+            <view class="confirm-title">{{ title }}</view>
+            <view class="confirm-content">{{ content }}</view>
+            <view class="confirm-buttons">
+                <button class="confirm-btn cancel" @click="cancel">{{ cancelText }}</button>
+                <button class="confirm-btn confirm" @click="confirm">{{ confirmText }}</button>
+            </view>
+        </view>
+    </uni-popup>
+</template>
+
+<script setup>
+import { ref, onMounted, onUnmounted } from 'vue'
+const popup = ref(null)
+const title = ref('提示')
+const content = ref('')
+const confirmText = ref('确定')
+const cancelText = ref('取消')
+let currentEventId = null
+
+const handleShowConfirm = (options) => {
+    title.value = options.title || '提示'
+    content.value = options.content || ''
+    confirmText.value = options.confirmText || '确定'
+    cancelText.value = options.cancelText || '取消'
+    currentEventId = options.eventId
+    popup.value?.open()
+}
+
+const closeAndResult = (result) => {
+    popup.value?.close()
+    if (currentEventId) {
+        uni.$emit(`confirmResult_${currentEventId}`, result)
+        currentEventId = null
+    }
+}
+
+const confirm = () => closeAndResult(true)
+const cancel = () => closeAndResult(false)
+const onChange = (e) => {
+    if (e.type === 'close') closeAndResult(false)
+}
+
+onMounted(() => {
+    uni.$on('showConfirm', handleShowConfirm)
+})
+onUnmounted(() => {
+    uni.$off('showConfirm', handleShowConfirm)
+})
+</script>
+
+<style scoped lang="scss">
+@import "@/uni.scss";
+
+.confirm-popup {
+    width: px2rpx(500);
+    background-color: #fff;
+    border-radius: px2rpx(16);
+    padding: px2rpx(20) px2rpx(16);
+    text-align: center;
+    box-shadow: 0 px2rpx(10) px2rpx(20) rgba(0, 0, 0, 0.1);
+    animation: popup-in 0.3s ease-out;
+}
+
+.confirm-title {
+    font-size: px2rpx(24);
+    font-weight: 600;
+    color: #333;
+    margin-bottom: px2rpx(30);
+}
+
+.confirm-content {
+    font-size: px2rpx(20);
+    color: #666;
+    margin-bottom: px2rpx(30);
+    line-height: 1.5;
+    word-break: break-word;
+}
+
+.confirm-buttons {
+    display: flex;
+    justify-content: space-between;
+    gap: px2rpx(24);
+}
+
+.confirm-btn {
+    flex: 1;
+    height: px2rpx(40);
+    line-height: px2rpx(40);
+    border-radius: px2rpx(20);
+    font-size: px2rpx(20);
+    border: none;
+    background-color: #f5f5f5;
+    color: #666;
+    font-weight: 500;
+    transition: all 0.2s ease;
+    -webkit-tap-highlight-color: transparent;
+}
+
+.confirm-btn:active {
+    transform: scale(0.98);
+    opacity: 0.9;
+}
+
+.confirm-btn.confirm {
+    background-color: #2979ff;
+    color: #fff;
+}
+
+.confirm-btn.confirm:active {
+    background-color: #1a66e5;
+}
+
+.confirm-btn.cancel {
+    background-color: #f5f5f5;
+    color: #666;
+}
+
+.confirm-btn.cancel:active {
+    background-color: #e6e6e6;
+}
+
+@keyframes popup-in {
+    from {
+        opacity: 0;
+        transform: scale(0.9);
+    }
+
+    to {
+        opacity: 1;
+        transform: scale(1);
+    }
+}
+
+/* 响应式调整 */
+@media screen and (max-width: 750px) {
+    .confirm-popup {
+        width: 85vw;
+        max-width: px2rpx(600);
+        padding: px2rpx(32) px2rpx(24);
+    }
+
+    .confirm-title {
+        font-size: px2rpx(24);
+        margin-bottom: px2rpx(16);
+    }
+
+    .confirm-content {
+        font-size: px2rpx(20);
+        margin-bottom: px2rpx(32);
+    }
+
+    .confirm-btn {
+        height: px2rpx(48);
+        line-height: px2rpx(48);
+        font-size: px2rpx(24);
+    }
+}
+</style>

+ 1 - 0
components/cwg-page-wrapper.vue

@@ -8,6 +8,7 @@
     </cwg-match-media>
     <LanguageDropdown style="width: 0;display: none;" />
     <cwg-progress />
+    <cwg-confirm-popup />
     <view class="page-content" :style="{ backgroundColor: bgColor }">
       <cwg-match-media :max-width="991" v-if="!isLoginPage">
         <view class="left-sidebar" :class="{ 'sidebar-visible': sidebarVisible }">

+ 24 - 0
hooks/useConfirm.ts

@@ -0,0 +1,24 @@
+export function useConfirm() {
+  const confirm = (options) => {
+    return new Promise((resolve, reject) => {
+      const eventId = Date.now() + '_' + Math.random()
+      const resultEvent = `confirmResult_${eventId}`
+      
+      const handler = (result) => {
+        uni.$off(resultEvent, handler)
+        if (result) resolve(true)
+        else reject(false)
+      }
+      
+      uni.$on(resultEvent, handler)
+      uni.$emit('showConfirm', { ...options, eventId })
+      
+      // 超时处理
+      setTimeout(() => {
+        uni.$off(resultEvent, handler)
+        reject(new Error('确认弹窗超时'))
+      }, 60000)
+    })
+  }
+  return confirm
+}

+ 132 - 8
pages/customer/payment-history.vue

@@ -20,7 +20,95 @@
                     {{typeMap.find(item => item.value === row.type)?.text}}
                 </template>
                 <template #status="{ row }">
-                    <OrderStatusMachineCell :row="row" @cancel="handleOrderCancel" @action="handleOrderAction" />
+                    <view class="status-box">
+                        <view v-if="row.type == 1">
+                            <text v-if="
+                                row.status == 1 &&
+                                row.callbackStatus == 0 &&
+                                (row.expireTime == null || time < row.expireTime)
+                            " class="status-tag status-pending" v-t="'State.ToBeProcessed'"></text>
+                            <text v-if="
+                                row.status == 2 &&
+                                row.executionStatus == 2 &&
+                                row.callbackStatus == 1
+                            " class="status-tag status-completed" v-t="'State.Completed'"></text>
+                            <text v-if="
+                                row.status == 2 &&
+                                (row.executionStatus == 1 || row.executionStatus == 0) &&
+                                row.callbackStatus != 2 &&
+                                (row.expireTime == null || time < row.expireTime)
+                            " class="status-tag status-processing" v-t="'State.InTheProcessing'"></text>
+                            <text v-if="
+                                row.callbackStatus == 2 ||
+                                row.status == 3 ||
+                                row.executionStatus == 3
+                            " class="status-tag status-failed" v-t="'State.Refused'"></text>
+                            <text v-if="
+                                !(
+                                    row.status == 2 &&
+                                    row.executionStatus == 2 &&
+                                    row.callbackStatus == 1
+                                ) &&
+                                !(
+                                    row.callbackStatus == 2 ||
+                                    row.status == 3 ||
+                                    row.executionStatus == 3
+                                ) &&
+                                row.expireTime != null &&
+                                time > row.expireTime
+                            " class="status-tag status-expired" v-t="'State.expireTime'"></text>
+                        </view>
+
+                        <view v-if="row.type == 2">
+                            <text v-if="
+                                row.status == 1 &&
+                                row.callbackStatus == 0 &&
+                                (row.expireTime == null || time < row.expireTime)
+                            " class="status-tag status-pending" v-t="'State.ToBeProcessed'"></text>
+                            <text v-if="
+                                row.status == 2 &&
+                                row.executionStatus == 2 &&
+                                row.submitStatus == 2
+                            " class="status-tag status-completed" v-t="'State.Completed'"></text>
+                            <text v-if="
+                                (row.status == 2 &&
+                                    (row.executionStatus == 1 || row.executionStatus == 0) &&
+                                    row.callbackStatus != 2) ||
+                                (row.status == 2 &&
+                                    row.executionStatus == 2 &&
+                                    row.callbackStatus == 0 &&
+                                    row.type == 2 &&
+                                    row.submitStatus != 2)
+                            " class="status-tag status-processing" v-t="'State.InTheProcessing'"></text>
+                            <text v-if="
+                                row.callbackStatus == 2 ||
+                                row.status == 3 ||
+                                row.executionStatus == 3
+                            " class="status-tag status-failed" v-t="'State.Refused'"></text>
+                        </view>
+
+                        <text v-if="row.status == 5" class="status-tag status-cancelled" v-t="'State.Cancelled'"></text>
+
+                        <!-- 取消按钮(保留原有功能,不添加状态类名) -->
+                        <view v-if="
+                            row.status == 1 &&
+                            row.callbackStatus == 0 &&
+                            (row.expireTime == null || time < row.expireTime) &&
+                            row.type == 2
+                        ">
+                            <text class="status-tag btn crm-cursor status-cancelled" v-t="'Btn.Cancel'"
+                                @click.stop="cancleConfirm(row.id, 1)"></text>
+                        </view>
+                        <view v-if="
+                            row.status == 2 &&
+                            row.executionStatus == 2 &&
+                            row.backstageStatus == 1 &&
+                            row.type == 2
+                        ">
+                            <text class="status-tag btn crm-cursor status-cancelled" v-t="'Btn.Cancel'"
+                                @click.stop="cancleConfirm(row.id, 2)"></text>
+                        </view>
+                    </view>
                 </template>
                 <template #btn="{ row }">
                     <text :class="['operation-btn', row.status !== 4 ? 'disabled' : '']" @click="openAddFile(row)">
@@ -38,7 +126,8 @@ import { computed, ref } from 'vue';
 import { useI18n } from 'vue-i18n';
 const { t, locale } = useI18n();
 import { financialApi } from '@/service/financial';
-import OrderStatusMachineCell from './components/OrderStatusMachineCell.vue'
+import { useConfirm } from '@/hooks/useConfirm'
+
 const search = ref({})
 const typeMap = computed(() => ([
     { value: null, text: t('Custom.PaymentHistory.All') },
@@ -54,11 +143,6 @@ const orderStatusMap = computed(() => ([
     { value: 5, text: t('State.expireTime') },
     { value: 6, text: t('State.Cancelled') },
 ]));
-
-const handleOrderCancel = (row) => {
-    console.log('取消订单:', row)
-}
-
 const isZh = computed(() => ['cn', 'zh', 'zhHant'].includes(locale.value));
 // 表格列配置
 const columns = ref([
@@ -144,6 +228,46 @@ const mobilePrimaryFields = ref([
 ])
 const listApi = ref(null)
 listApi.value = financialApi.BalanceList
+const confirm = useConfirm()
+
+const cancleConfirm = async (id, type) => {
+    try {
+        await confirm({
+            title: t('Msg.SystemPrompt'),
+            content: t('Msg.Cancle'),
+            confirmText: t('Btn.item6'),
+            cancelText: t('Btn.item7')
+        })
+        cancle(id, type)
+    } catch (error) {
+    }
+}
+const tableRef = ref(null)
+const cancle = async (id, type) => {
+    try {
+        let api
+        if (type == 1) {
+            api = financialApi.withdrawCancel
+        } else {
+            api = financialApi.withdrawCancelBackstage
+        }
+        let res = await api({ id: id });
+        if (res.code == 200) {
+            uni.showToast({
+                title: t("Msg.Success"),
+                icon: 'success'
+            });
+            tableRef.value.refreshTable()
+        } else {
+            uni.showToast({
+                title: res.msg,
+                icon: 'error'
+            });
+        }
+    } catch (error) {
+        console.log('取消删除')
+    }
+}
 </script>
 
 <style scoped lang="scss">
@@ -192,7 +316,7 @@ listApi.value = financialApi.BalanceList
 }
 
 .operation-btn {
-    :deep(span) {
+    :deep(text) {
         display: flex;
         align-items: center;
         justify-content: center;

+ 3 - 35
pages/customer/transfer.vue

@@ -93,14 +93,10 @@
                                         </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>
+                                    <button class="s-btn reselect" type="primary" @click="toTransfer">{{ locale === 'es'
+                                        ?
+                                        'Enviar solicitud' : t('Btn.Submit') }}</button>
                                 </view>
                             </uni-forms>
                         </view>
@@ -674,34 +670,6 @@ watch(transferType, (newVal) => {
         }
     }
 
-    .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);

+ 3 - 36
pages/ib/agent-transfer.vue

@@ -141,14 +141,10 @@
                                         </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>
+                                    <button class="s-btn reselect" type="primary" @click="toTransfer">{{ locale === 'es'
+                                        ?
+                                        'Enviar solicitud' : t('Btn.Submit') }}</button>
                                 </view>
                             </uni-forms>
                         </view>
@@ -803,35 +799,6 @@ onLoad((options) => {
         }
     }
 
-    .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;

+ 5 - 38
pages/ib/transfer.vue

@@ -90,12 +90,9 @@
                                 </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>
+                                    <button class="s-btn reselect" type="primary" @click="toTransfer">{{ locale === 'es'
+                                        ?
+                                        'Enviar solicitud' : t('Btn.Submit') }}</button>
                                 </view>
                             </uni-forms>
                         </view>
@@ -580,9 +577,8 @@ watch(() => form.agree6, (newVal) => {
     }
 })
 onMounted(() => {
-    if (!getInfoStatus5.value) {
-        // InfoStatus5.value = true
-        dialogDontActive.value = true
+    if (getInfoStatus5.value) {
+        InfoStatus5.value = true
     }
 
     ransferInfo();
@@ -749,35 +745,6 @@ onLoad((options) => {
             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;
-        }
-    }
 }
 
 // 动画

+ 103 - 0
static/scss/global/global.scss

@@ -909,6 +909,42 @@ uni-content {
     font-weight: bold;
 }
 
+.s-btn {
+    &.reselect {
+        background-color: var(--color-zinc-100);
+        color: var(--color-navy-700);
+        border: none;
+        font-size: px2rpx(14);
+        padding: px2rpx(8) px2rpx(20);
+        border-radius: px2rpx(8);
+
+        &:active {
+            background-color: var(--color-zinc-200);
+        }
+    }
+
+    &[type="primary"] {
+        width: 100%;
+        height: px2rpx(48);
+        background: var(--color-navy-900);
+        color: #fff;
+        border-radius: px2rpx(12);
+        font-size: px2rpx(16);
+        font-weight: 600;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        border: none;
+        margin-top: px2rpx(30);
+        transition: all 0.2s;
+
+        &:active {
+            transform: scale(0.98);
+            background: var(--color-navy-800);
+        }
+    }
+}
+
 .search-bar {
     display: flex;
     align-items: center;
@@ -1047,6 +1083,14 @@ uni-content {
     }
 }
 
+
+
+.status-box {
+    display: flex;
+    align-items: center;
+    gap: px2rpx(4);
+}
+
 @media screen and (max-width: 991px) {
     .info-card {
         box-sizing: border-box;
@@ -1054,6 +1098,12 @@ uni-content {
         border: 0 !important;
         border-radius: 0 !important;
     }
+
+    .status-box {
+        display: flex;
+        flex-direction: column;
+        gap: px2rpx(4);
+    }
 }
 
 // 状态标签基础样式
@@ -1074,6 +1124,59 @@ uni-content {
     }
 }
 
+// 状态标签基础样式
+.status-tag {
+    display: inline-block;
+    padding: px2rpx(4) px2rpx(8);
+    border-radius: px2rpx(2);
+    font-size: px2rpx(15);
+    text-align: center;
+    min-width: px2rpx(80);
+    box-sizing: border-box;
+}
+
+// 待处理
+.status-pending {
+    background-color: #fef3c7;
+    color: #d97706;
+}
+
+// 处理中
+.status-processing {
+    background-color: #a9d3ee;
+    color: #0284c7;
+}
+
+// 成功
+.status-success {
+    background-color: #a3f7c0;
+    color: #16a34a;
+}
+
+// 失败
+.status-failed {
+    background-color: #f39c9c;
+    color: #dc2626;
+}
+
+// 取消 / 已取消
+.status-cancelled {
+    background-color: #a4bef2;
+    color: #4b5563;
+}
+
+// 已过期
+.status-expired,
+.status-canceled {
+    background-color: #f5efb1;
+    color: #a16207;
+}
+
+// 已完成
+.status-completed {
+    background-color: #d1fae5;
+    color: #059669;
+}
 
 // 待处理
 .status-warning {