Pārlūkot izejas kodu

银行卡绑定功能;
新增上传组件;
公用样式及方法调整

(cherry picked from commit 1a0bc4faa99663e8bc50bdb528c313cfabadaf0c)

lishanshan 3 mēneši atpakaļ
vecāks
revīzija
6e946b07a6

+ 1 - 0
env/.env.development

@@ -14,6 +14,7 @@ VITE_SERVER_BASEURL = 'https://life.baoxianzhanggui.com/locallive-pro-java'
 
 # 静态资源请求地址
 VITE_CDN_DOMAIN = 'https://life.baoxianzhanggui.com/pro-img/securities'
+VITE_CDN_LOCAL_DOMAIN = 'https://life.baoxianzhanggui.com/pro-img/cust/static'
 
 # 微信小程序码跳转版本
 VITE_WEIXIN_MINIAPP_VERSION = 'trial'

+ 1 - 0
env/.env.production

@@ -16,6 +16,7 @@ VITE_SERVER_BASEURL = 'https://life.baoxianzhanggui.com/locallive-pro-java'
 
 # 静态资源请求地址
 VITE_CDN_DOMAIN = 'https://life.baoxianzhanggui.com/pro-img/securities'
+VITE_CDN_LOCAL_DOMAIN = 'https://life.baoxianzhanggui.com/pro-img/cust/static'
 
 # 微信小程序码跳转版本
 # VITE_WEIXIN_MINIAPP_VERSION = 'release'

+ 71 - 0
src/api/pay.ts

@@ -0,0 +1,71 @@
+import type {
+    bankcardListResponse,
+    netWithdrawResponse,
+    bankListResponse,
+    bankSignRecordResponse,
+    bankAccountDetailResponse,
+    netBankcardUpdateParams,
+    netBankcardUpdateResponse,
+} from '@/api/types/pay'
+import { http } from '@/http/alova'
+
+/**
+ *
+ * 提现相关API
+ */
+
+// 获取银行卡列表
+export function getBankcardList() {
+    return http.Get<bankcardListResponse>('/app/bank-card/list')
+}
+
+// 发起提现
+export function netWithdraw(params: netWithdrawResponse) {
+    return http.Post('/couponCenter/APP/couponIssuerAccount/withdraw', params)
+}
+
+/**
+ *
+ * 银行卡相关API
+ */
+
+// 银行卡-重新提交
+export function netBankcardUpdate(params: netBankcardUpdateParams) {
+    return http.Post<netBankcardUpdateResponse>('/identity/authentication/application', params, {
+        meta: {
+            returnResponse: true
+        }
+    })
+}
+
+// 银行卡-获取银行卡列表
+export function getBankList() {
+    return http.Get<bankListResponse>('/identity/authentication/bankList', {
+        meta: {
+            returnResponse: true
+        }
+    })
+}
+
+// 查询普通用户是否有会员进件记录
+export function getBankSignRecord() {
+  return http.Get<bankSignRecordResponse>('/localBankAccountInformationRecord/localBankAccountInformationRecord/getByUserId', {
+    meta: {
+      returnResponse: true
+    }
+  })
+}
+
+// 获取银行账户信息详情
+export function getBankCardInfo(accountInformationId: string) {
+  return http.Get<bankAccountDetailResponse>(`/identity/authentication/queryByAccountInformationRecordId?accountInformationId=${accountInformationId}`, {
+    meta: {
+      returnResponse: true
+    }
+  })
+}
+
+// 更新绑卡状态
+export function updateBankCardState(data: { meberId: string }) {
+  return http.Post('/identity/authentication/updateBankCardState', data)
+}

+ 80 - 0
src/api/types/pay.ts

@@ -0,0 +1,80 @@
+export interface bankcardListResponse {
+    id: string
+    bankName: string
+    bankCardNumber: string
+    bankImage: string
+    logo?: string
+}
+
+export interface netWithdrawResponse {
+    amount: string
+    bankId: string
+    callBackUrl: string
+}
+
+// 银行卡信息
+export interface bankCardInfo {
+  id: string
+  bankId: string
+  bankName: string
+  bankCardNumber: string
+  name: string
+  contractedPhone: string
+  reservedPhone: string
+}
+// 身份证信息
+export interface idCardInfo {
+  name: string
+  identityNo: string
+  idEffectiveDate: string
+  idExpiryDate: string
+  addr: string
+  idPhotoFront: string
+  idPhotoBack: string
+}
+// 银行账户详情
+export interface bankAccountDetailResponse {
+  id: string
+  state: string // 00-初始,01-签约成功, 02-拒绝, 03-待审核, 04-待签约
+  reason: string
+  bankCard: bankCardInfo
+  idCardInfo: idCardInfo
+}
+// 银行签约记录
+export interface bankSignRecordResponse {
+  id: string
+  state: string
+}
+// 银行列表项
+export interface bankListItem {
+  id: string
+  bankName: string
+}
+export type bankListResponse = bankListItem[]
+// 银行卡更新参数
+export interface netBankcardUpdateParams {
+  bankCard: {
+    bankId: string
+    bankName: string
+    bankCardNumber: string
+    name: string
+    contractedPhone: string
+    reservedPhone: string
+    callbackUrl?: string
+  }
+  idCardInfo: {
+    name: string
+    identityNo: string
+    idEffectiveDate: string
+    idExpiryDate: string
+    addr: string
+    idPhotoFront: string
+    idPhotoBack: string
+  }
+}
+// 银行卡更新响应
+export interface netBankcardUpdateResponse {
+  state: string
+  cerUrl?: string
+  meberId?: string
+}

+ 20 - 8
src/components/IncomeItem.vue

@@ -2,31 +2,37 @@
     <view class="invite-item" @tap="handleItemClick">
         <view class="income-header-menu-item">
             <view class="income-header-menu-icon">
-                <image v-if="type === 0" :src="getImageUrl('@img/income/djs.png')" mode="aspectFit" />
-                <image v-else :src="getImageUrl('@img/income/hb.png')" mode="aspectFit" />
+                <image :src="getImageUrl(`@img/income/${iconImages[type]}.png`)" mode="aspectFit" />
                 <view class="income-header-menu-text">
-                    <view v-if="[5, 6].includes(data.commissionType)" class="income-header-menu-text-nickname">
+                    <view v-if="[5, 6, 7].includes(data.commissionType)" class="income-header-menu-text-nickname">
                         <template v-if="data.commissionType === 5">
                             拉新奖励
                         </template>
-                        <template v-else>
+                        <template v-if="data.commissionType === 6">
                             分享奖励
                         </template>
+                        <template v-else>
+                            提现到银行卡
+                        </template>
                     </view>
                     <view v-else class="income-header-menu-text-nickname">
                         {{ data.receiveUserName }}
                     </view>
-                    <view v-if="![5, 6].includes(data.commissionType)">
+                    <view v-if="![5, 6, 7].includes(data.commissionType)">
                         {{ data.couponName }}
                         <template v-if="data.status === 2">
                             <text class="text-red ml-10rpx">(退款)</text>
                         </template>
                     </view>
+                    <view>
+                    <!-- <view v-if="[7].includes(data.commissionType)"> -->
+                        {{ data.bankName }}招商银行
+                    </view>
                 </view>
             </view>
             <view class="income-header-menu-left">
                 <view class="income-header-menu-left-amount">
-                    <view class="center" v-if="![5, 6].includes(data.commissionType)">
+                    <view class="center" v-if="![5, 6, 7].includes(data.commissionType)">
                         <template v-if="data.status === 2">
                             <up-icon name="minus" size="12" color="#333333" />
                         </template>
@@ -35,10 +41,10 @@
                         </template> -->
                     </view>
                     <up-text mode="price" :style="{ flex: '0' }" :text="data.changeAmount" align="right"
-                        color="#333333" />
+                        color="#222222" font-size="30rpx" />
                 </view>
                 <view class="income-header-menu-left-time">
-                    {{ type === 1 ? data.settleTime : '预估' }}
+                    {{ type === 2 ? data.createTime : type === 1 ? data.settleTime : '预估' }}
                 </view>
             </view>
         </view>
@@ -63,6 +69,12 @@ const props = defineProps({
     },
 })
 
+const iconImages = [
+    'djs',  // type 0: 待结算
+    'hb',   // type 1: 已结算
+    'card', // type 2: 已提现
+]
+
 const emit = defineEmits(['itemClick'])
 
 const handleItemClick = () => {

+ 197 - 0
src/components/upload/index.vue

@@ -0,0 +1,197 @@
+<template>
+  <u-upload
+    :name="name" 
+    :width="width"
+    :height="height"
+    :accept="accept"
+    :file-list="localFileList"
+    :multiple="multiple" 
+    :max-count="maxCount"
+    :disabled="disabled"
+    v-if="$slots.content"
+    @after-read="afterRead" 
+    @delete="onDelete">
+    <slot name="content" :data="localFileList"></slot>
+  </u-upload>
+  <u-upload
+    :name="name" 
+    :width="width"
+    :height="height"
+    :accept="accept"
+    :file-list="localFileList"
+    :multiple="multiple" 
+    :max-count="maxCount"
+    :disabled="disabled"
+    v-else
+    @after-read="afterRead" 
+    @delete="onDelete">
+  </u-upload>
+</template>
+
+<script setup lang="ts">
+import { ref, watch, useAttrs } from 'vue'
+
+// 使用环境变量
+const baseUrl = import.meta.env.VITE_SERVER_BASEURL
+
+// 定义props
+const props = defineProps({
+  api: {
+    type: String,
+    default: '/sys/common/upload'
+  },
+  name: {
+    type: String,
+    default: ''
+  },
+  accept: {
+    type: String,
+    default: 'image'
+  },
+  width: {
+    type: [String, Number],
+    default: 150
+  },
+  height: {
+    type: [String, Number],
+    default: 150
+  },
+  maxCount: {
+    type: Number,
+    default: 5
+  },
+  multiple: {
+    type: Boolean,
+    default: true
+  },
+  disabled: {
+    type: Boolean,
+    default: false
+  },
+  // 初始图片列表
+  initList: {
+    type: Array as () => any[],
+    default: () => []
+  }
+})
+
+const emit = defineEmits(['fileList', 'complete'])
+
+// 本地文件列表
+const localFileList = ref<any[]>([])
+
+// 监听初始列表变化
+watch(() => props.initList, (newVal) => {
+  if (newVal && newVal.length > 0) {
+    localFileList.value = newVal.map(item => ({
+      ...item,
+      status: 'success',
+      message: ''
+    }))
+  } else {
+    localFileList.value = []
+  }
+}, { immediate: true, deep: true })
+
+// 上传后的处理函数
+const attrs = useAttrs()
+async function afterRead(event: { file: any }) {
+  // 当设置 multiple 为 true 时, file 为数组格式,否则为对象格式
+  const lists = [].concat(event.file)
+  let fileListLen = localFileList.value.length
+  
+  lists.forEach((item: any) => {
+    localFileList.value.push({
+      ...item,
+      status: "uploading" as const,
+      message: "上传中",
+      localUrl: item.url, // 保存本地文件路径
+    })
+  })
+  
+  // 无预览时,使用toast提示
+  if (!attrs.previewImage) {
+    uni.showLoading({ title: '上传中...' })
+  }
+  
+  for (let i = 0; i < lists.length; i++) {
+    try {
+      const result = await uploadFilePromise(lists[i].url)
+      const item = localFileList.value[fileListLen]
+      localFileList.value.splice(
+        fileListLen,
+        1,
+        Object.assign(item, {
+          status: "success" as const,
+          message: "",
+          url: `${baseUrl}/${result}`,
+          localUrl: item.localUrl
+        })
+      )
+      fileListLen++
+      
+      // 无预览时,使用toast
+      if (!attrs.previewImage) {
+        uni.hideLoading();
+        uni.showToast({
+          icon: 'none',
+          title: result.message || '上传完成',
+          duration: 2000
+        });
+        emit('complete')
+      }
+    } catch (error) {
+      console.error('上传失败:', error)
+    }
+  }
+  emit('fileList', localFileList.value)
+}
+
+// 上传文件
+function uploadFilePromise(url: string): Promise<string> {
+  return new Promise((resolve, reject) => {
+    uni.uploadFile({
+      url: `${baseUrl}${props.api}`,
+      name: "file",
+      formData: {
+        'biz': 'temp',
+      },
+      filePath: url,
+      success: (res) => {
+        try {
+          const data = JSON.parse(res.data)
+          if (data.success) {
+            resolve(data.message)
+          } else {
+            reject(new Error(data.message || '上传失败'))
+          }
+        } catch (e) {
+          reject(new Error('解析响应失败'))
+        }
+      },
+      fail: (err) => {
+        reject(err)
+      }
+    })
+  })
+}
+
+// 删除文件
+function onDelete(event: { index: number }) {
+  uni.showModal({
+    title: '提示',
+    content: '确定要删除?',
+    cancelText: '取消',
+    confirmText: '确定',
+    success: (res) => {
+      if (res.confirm) {
+        localFileList.value.splice(event.index, 1)
+        emit('fileList', localFileList.value)
+      }
+    }
+  })
+}
+</script>
+
+<style lang="scss" scoped>
+</style>

+ 785 - 0
src/pages-B/payment/bankCard-edit.vue

@@ -0,0 +1,785 @@
+<script setup lang="ts">
+import { ref, computed } from 'vue'
+import '@/utils/dateFormat'
+import { netBankcardUpdate, getBankList, getBankSignRecord, getBankCardInfo } from '@/api/pay'
+import { getImageUrl } from '@/utils/imageUtil'
+import Upload from '@/components/upload/index.vue';
+import BankCardSelector from './bankCard-select.vue';
+
+// 域名
+const domain = ref('')
+// 审核状态:00-初始,01-签约成功, 02-拒绝, 03-待审核, 04-待签约
+const auditStatus = ref('')
+// 审核失败的错误信息
+const auditMessage = ref('')
+// 是否处于可提交状态
+const isReSubmit = ref(false)
+
+// 表单数据
+const form = ref({
+  name: '',
+  contractedPhone: '',
+  phone: '',
+  bankId: '',
+  bankName: '',
+  bankCardNumber: '',
+  openingBank: '',
+  startDate: '',
+  endDate: '',
+  idCard: '',
+  idAddress: '',
+  frontCard: '',
+  backCard: '',
+})
+
+// 表单校验规则
+const rules = ref({
+  bankName: [
+    {
+      required: true,
+      message: '请选择存管银行',
+      trigger: ['blur', 'change'],
+    },
+  ],
+  bankCardNumber: [
+    {
+      required: true,
+      message: '请输入银行卡号',
+      trigger: ['blur', 'change'],
+    },
+    {
+      min: 13,
+      max: 19,
+      message: '银行卡号位数不正确',
+      trigger: ['blur', 'change'],
+    },
+  ],
+  name: [
+    {
+      required: true,
+      message: '请输入姓名',
+      trigger: ['blur', 'change'],
+    },
+    { max: 20, message: '不能超过20个字符', trigger: ['blur', 'change'] },
+  ],
+  contractedPhone: [
+    {
+      required: true,
+      message: '请输入手机号',
+      trigger: ['blur', 'change'],
+    },
+    {
+      pattern: /^1[3-9]\d{9}$/,
+      message: '手机号码不正确',
+      trigger: ['blur', 'change'],
+    },
+  ],
+  phone: [
+    {
+      required: true,
+      message: '请输入手机号',
+      trigger: ['blur', 'change'],
+    },
+    {
+      pattern: /^1[3-9]\d{9}$/,
+      message: '手机号码不正确',
+      trigger: ['blur', 'change'],
+    },
+  ],
+  idCard: [
+    {
+      required: true,
+      message: '请输入证件号码',
+      trigger: ['blur', 'change'],
+    },
+    {
+      pattern: /^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/,
+      message: '证件号码不正确',
+      trigger: ['blur', 'change'],
+    },
+  ],
+  idAddress: [
+    {
+      required: true,
+      message: '请输入证件地址',
+      trigger: ['blur', 'change'],
+    },
+  ],
+  endDate: [
+    {
+      required: true,
+      message: '请选择证件截止有效期',
+      trigger: ['blur', 'change'],
+    },
+  ],
+  startDate: [
+    {
+      required: true,
+      message: '请选择证件起始有效期',
+      trigger: ['blur', 'change'],
+    },
+  ],
+  backCard: [
+    {
+      required: true,
+      message: '请上传身份证国徽面',
+      trigger: ['change'],
+    },
+  ],
+  frontCard: [
+    {
+      required: true,
+      message: '请上传身份证人像面',
+      trigger: ['change'],
+    },
+  ],
+})
+
+// 银行数据
+const bankData = ref([])
+const isAgreed = ref([])
+const showDate = ref(false)
+type DateFieldName = 'startDate' | 'endDate'
+const datetimeKey = ref<DateFieldName | undefined>(undefined) // 当前操作的日期字段名
+const datetimeValue = ref(new Date().getTime()) // 日期选择器的值(时间戳)
+const maxDate = new Date('2200-12-31').getTime()
+
+// 上传组件的文件列表
+type UploadFileItem = {
+  url: string
+  localUrl?: string
+  status?: 'success' | 'uploading' | 'error'
+  message?: string
+}
+
+const idCardBackList = ref<UploadFileItem[]>([])
+const idCardFrontList = ref<UploadFileItem[]>([])
+
+const fileListMap: Record<string, Ref<UploadFileItem[]>> = {
+  idCardFrontList,
+  idCardBackList
+}
+
+// 计算属性
+const idCardFrontUrl = computed(() => {
+  return idCardFrontList.value.length > 0 ? idCardFrontList.value[0].url : ''
+})
+
+const idCardBackUrl = computed(() => {
+  return idCardBackList.value.length > 0 ? idCardBackList.value[0].url : ''
+})
+
+const isFormDisabled = computed(() => {
+  return ['00', '02', '03', '04'].includes(auditStatus.value) && !isReSubmit.value
+})
+
+const statusIcon = computed(() => {
+  switch (auditStatus.value) {
+    case '00':
+    case '03':
+    case '04':
+      return 'clock'
+    case '02':
+      return 'close-circle'
+    default:
+      return 'clock'
+  }
+})
+
+// 审核状态
+const statusText = computed(() => {
+  switch (auditStatus.value) {
+    case '00':
+      return '银行卡信息已提交'
+    case '02':
+      return '银行卡审核被拒绝'
+    case '03':
+      return '银行卡待审核'
+    case '04':
+      return '银行卡待签约'
+    default:
+      return ''
+  }
+})
+
+// 页面加载
+onLoad((options?: Record<string, string>) => {
+  fetchBankList()
+  const { type, cardId } = options || {}
+  if (type === 'update' && cardId) {
+    fetchBankAccountDetail(cardId)
+  } else {
+    getSignStatus()
+  }
+  
+  // #ifdef H5
+  domain.value = extractDomain(window.location.href)
+  // #endif
+  // #ifndef H5
+  domain.value = extractDomain(import.meta.env.VITE_SERVER_BASEURL)
+  // #endif
+})
+
+onShow(() => {
+    //   form.value.phone = uni.getStorageSync('user')?.userInfo?.phone;
+})
+
+const formRef = ref()
+onReady(() => {
+    formRef.value.setRules(rules.value);
+})
+
+// 获取银行列表
+function fetchBankList() {
+    getBankList().then((res: any) => {
+        if (res.data.code === 200 && res.data.result) {
+          bankData.value = res.data.result;
+        }
+    });
+}
+
+// 获取签约状态
+function getSignStatus() {
+    getBankSignRecord().then((res: any) => {
+        if (res.data.code === 200 && res.data.result) {
+            const { state } = res.data.result;
+            switch (state) {
+                case '01':
+                    // 签约成功跳转银行卡列表
+                    uni.navigateTo({
+                      url: '/packageB/payment/bankCard-list',
+                    });
+                    break;
+                case '00':
+                case '02':
+                case '03':
+                case '04':
+                    // 查详情接口
+                    fetchBankAccountDetail(res.data.result.id);
+            }
+        }
+    }).catch((err: any) => {
+        console.error('获取审核状态失败:', err);
+    });
+}
+
+// 获取银行账户信息详情
+function fetchBankAccountDetail(id: string) {
+    return getBankCardInfo(id).then((res: any) => {
+        if (res.data.code === 200 && res.data.result) {
+            auditStatus.value = res.data.result.state;
+            auditMessage.value = res.data.result.reason;
+            const bankCard = res.data.result.bankCard || {};
+            const idCardInfo = res.data.result.idCardInfo || {};
+            const accountInfo = {
+                name: bankCard.name || idCardInfo.name || '',
+                bankId: bankCard.bankId || '',
+                bankName: bankCard.bankName || '',
+                idCard: idCardInfo.identityNo || '',
+                startDate: formatIdCardDate(idCardInfo.idEffectiveDate),
+                endDate: formatIdCardDate(idCardInfo.idExpiryDate),
+                idAddress: idCardInfo.addr || '',
+                bankCardNumber: bankCard.bankCardNumber || '',
+                contractedPhone: bankCard.contractedPhone || '',
+                phone: bankCard.reservedPhone || '',
+            };
+            form.value = { ...form.value, ...accountInfo };
+            form.value.frontCard = idCardInfo.idPhotoFront || '';
+            form.value.backCard = idCardInfo.idPhotoBack || '';
+            idCardFrontList.value = idCardInfo.idPhotoFront ? [{ url: idCardInfo.idPhotoFront }] : [];
+            idCardBackList.value = idCardInfo.idPhotoBack ? [{ url: idCardInfo.idPhotoBack }] : [];
+        }
+    });
+}
+
+// 银行选择
+const bcSelector = ref()
+function onShowBank() {
+  bcSelector.value?.open()
+}
+
+const selectedId = ref('') // 选中的银行ID
+type BankCardItem = {
+    id: string | number
+    logo: string
+    bankName: string
+    last4?: string
+}
+
+function onSelectBank(e: BankCardItem) {
+    form.value.bankId = String(e.id);
+    form.value.bankName = e.bankName;
+    validateField('bankName')
+}
+    
+// 日期选择
+function onShowDate(fieldName: DateFieldName) {
+    datetimeKey.value = fieldName
+    // 如果form中已有值,使用该值;否则使用当前日期
+    const currentValue = form.value[fieldName]
+    datetimeValue.value = currentValue 
+      ? new Date(currentValue).getTime() 
+      : new Date().getTime()
+    showDate.value = true
+}
+
+function onSelectDate(e: { value: number }) {
+    form.value[datetimeKey.value!] = new Date(e.value).Format('YYYY-MM-DD')
+    showDate.value = false
+    // 手动触发表单校验
+    validateField(datetimeKey.value!)
+}
+
+// 校验指定字段
+function validateField(field: string) {
+    if (formRef.value) {
+        formRef.value.validateField(field)
+    }
+}
+
+// 上传回调
+function onUpload(e: any[], t: string) {
+    const listRef = fileListMap[t]
+    if (listRef) {
+      listRef.value = e  // ✅ 正确赋值
+      console.log(`${t} 已更新:`, e)
+    }
+    if (e.length > 0) {
+      // 获取身份证识别信息
+      getIdCardInfo(t);
+    }
+}
+
+// 获取身份证识别信息
+function getIdCardInfo(type: string) {
+      const fileList = type === 'idCardFrontList' ? idCardFrontList.value : idCardBackList.value;
+      const fileItem = fileList[0];
+      // 使用保留的本地文件路径
+      let localFilePath = fileItem.localUrl;
+      uni.uploadFile({
+        url: import.meta.env.VITE_SERVER_BASEURL + '/order/identify/idCard',
+        filePath: localFilePath,
+        name: type === 'idCardFrontList' ? 'image1' : 'image2',
+        header: {
+          'X-Access-Token': uni.getStorageSync('token'),
+          // #ifdef MP-WEIXIN
+          AppType: '6',
+          deviceId: uni.getStorageSync('openId'),
+          // #endif
+          // #ifdef H5
+          AppType: 1,
+          deviceId: 'h5',
+          // #endif
+        },
+        success: uploadFileRes => {
+          formRef.value.setRules({});
+          let res = JSON.parse(uploadFileRes.data);
+          if (res.code == 200) {
+            let idCardInfo = res.result.customerInfo;
+            if (type === 'idCardFrontList') {
+              form.value.name = idCardInfo.name || '';
+              form.value.idCard = idCardInfo.identifyNumber || '';
+              form.value.idAddress = idCardInfo.addr || '';
+            } else {
+              // 处理身份证起始日期格式转换
+              if (idCardInfo.identifyValidDate) {
+                form.value.startDate = idCardInfo.identifyValidDate.replace(/^(\d{4})(\d{2})(\d{2})$/, '$1-$2-$3');
+              }
+              if (idCardInfo.identifyValidEndDate) {
+                form.value.endDate = idCardInfo.identifyValidEndDate.replace(/^(\d{4})(\d{2})(\d{2})$/, '$1-$2-$3');
+              }
+            }
+            nextTick(() => {
+              // 恢复校验规则
+              formRef.value.setRules(rules);
+              // 如果需要,清除校验状态
+              formRef.value.clearValidate();
+            });
+          } else {
+            uni.$u.toast(res.message);
+          }
+        },
+      });
+}
+    
+// 身份证日期格式转换
+function formatIdCardDate(dateStr?: string): string {
+  if (!dateStr) return ''
+  return dateStr.replace(/^(\d{4})(\d{2})(\d{2})$/, '$1-$2-$3')
+}
+
+// 会员注册确认-跳转三方链接
+function confirmRegister(url: string, id?: string) {
+  const validate = openExternalLink(url)
+  if (validate) {
+    // #ifdef H5
+    window.location.href = url
+    // #endif
+    // #ifndef H5
+    uni.navigateTo({
+      url: `/pages-B/payment/web-view?webUrl=${encodeURIComponent(url)}&memberId=${id || ''}`,
+    })
+    // #endif
+  } else {
+    uni.showToast({
+      title: '打开失败,请检查链接',
+      icon: 'none',
+    })
+  }
+}
+
+// 校验URL格式
+function openExternalLink(url: string): boolean {
+  if (!url || !/^https?:\/\/.+/.test(url)) {
+    return false
+  }
+  return true
+}
+
+// 提交
+async function onSubmit() {
+    // if(isAgreed.value === 0) {
+    // 	uni.showToast({ title: '请先同意用户协议', icon: 'none' });
+    // 	return;
+    // }
+    // 将计算属性的值赋给form对象,以便校验
+    form.value.frontCard = idCardFrontUrl.value
+    form.value.backCard = idCardBackUrl.value
+
+    formRef.value.validate().then((res: any) => {
+        // console.log('表单校验成功', form.value);
+        let str = uni.$u.queryParams({
+            signStatus: '01'
+        });
+        let callbackUrl = `${domain.value}/pages-B/payment/certification-status${str}`
+        let params = {
+            bankCard: {
+                bankId: form.value.bankId,
+                bankName: form.value.bankName,
+                bankCardNumber: form.value.bankCardNumber,
+                name: form.value.name,
+                contractedPhone: form.value.contractedPhone,
+                reservedPhone: form.value.phone,
+                callbackUrl: callbackUrl,
+            },   
+            idCardInfo: {
+                name: form.value.name,
+                identityNo: form.value.idCard,
+                idEffectiveDate: form.value.startDate,
+                idExpiryDate: form.value.endDate,
+                addr: form.value.idAddress,
+                idPhotoFront: form.value.frontCard,
+                idPhotoBack: form.value.backCard,
+            },
+        };
+        // console.log(params.bankCard.callbackUrl,'------------------');
+        
+        uni.showLoading({
+          title: '处理中...',
+          mask: true,
+        });
+        netBankcardUpdate(params)
+          .then((res: any) => {
+            uni.hideLoading();
+            if (res.data.code === 200 && res.data.result) {
+              if (res.data.result.state == '01' && res.data.result.cerUrl) {
+                // 会员注册确认
+                confirmRegister(res.data.result.cerUrl, res.data.result.meberId);
+              } else {
+                uni.navigateTo({
+                  url: '/pages-B/payment/certification-status?signStatus=' + res.data.result.state,
+                });
+              }
+            } else {
+              uni.$u.toast(res.data.message);
+            }
+          })
+          .catch((err: any) => {
+            uni.hideLoading();
+            uni.$u.toast('网络异常,请稍后重试');
+          });
+    })
+    .catch((errors: any) => {
+        let f = errors.some((item: any) => ['idCardBackUrl', 'idCardFrontUrl'].indexOf(item.field) !== -1);
+        if (f) uni.$u.toast(errors[0].message);
+    });
+}
+
+// 重新提交
+function onReSubmit() {
+  isReSubmit.value = true
+}
+
+// 取主域名
+function extractDomain(url: string): string {
+  const regex = /^(https?:\/\/[^\/]+)/
+  const match = url.match(regex)
+  return match ? match[1] : ''
+}
+</script>
+
+<template>
+  <view class='new-content'>
+    <u-navbar title="绑定银行卡" :autoBack="true" :placeholder="true" :title-style="{ fontWeight: 'bold' }"></u-navbar>
+    <!-- 审核状态 -->
+    <view class="audit-status" v-if="auditStatus && auditStatus != '01'">
+      <view class="status-content">
+        <view class="status-info">
+          <u-icon :name="statusIcon" size="42rpx" class="status-icon"></u-icon>
+          <text class="status-text">{{ statusText }}</text>
+        </view>
+        <view class="status-btn">
+          <u-button
+            v-if="auditStatus == '02' && !isReSubmit"
+            type="primary"
+            @click="onReSubmit"
+            :customStyle="{
+              height: '59rpx',
+              fontSize: '29rpx',
+              color: '#FE171B',
+              backgroundColor: 'rgba(254, 23, 27, 0.05)',
+              border: '1rpx solid #FE171B',
+              padding: '3rpx 31rpx 8rpx',
+            }"
+            >重新提交</u-button
+          >
+        </view>
+      </view>
+      <!-- 审核失败错误信息 -->
+      <view class="error-message" v-if="auditStatus != '01' && auditMessage">
+        <text class="message-text">{{ auditMessage }}</text>
+      </view>
+    </view>
+
+    <view class="page-container">
+      <u--form
+        ref="formRef"
+        :model="form"
+        :rules="rules"
+        labelPosition="left"
+        labelWidth="180rpx"
+        :labelStyle="{
+          fontWeight: '500',
+          fontSize: '28rpx',
+          color: '#333333',
+        }"
+      >
+        <view class="form-section">
+          <view class="idcard-upload">
+            <view>
+              <view
+                class="upload-wrapper"
+                :style="{
+                  backgroundImage: `url(${getImageUrl('@local/mine/img-bank-bg.png')})`,
+                }"
+              >
+                <Upload name="idCardFront" width="300rpx" height="180rpx" :maxCount="1" @fileList="onUpload($event, 'idCardFrontList')" :disabled="isFormDisabled" :initList="idCardFrontList">
+                  <template v-slot:content>
+                    <view class="upload-slot">
+                      <image class="slot-image" :src="getImageUrl('@local/mine/img-bank0.png')" mode="aspectFit"> </image>
+                    </view>
+                  </template>
+                </Upload>
+              </view>
+              <view style="font-size: 24rpx; color: #666; text-align: center">身份证人像面</view>
+            </view>
+
+            <view>
+              <view
+                class="upload-wrapper"
+                :style="{
+                  backgroundImage: `url(${getImageUrl('@local/mine/img-bank-bg.png')})`,
+                }"
+              >
+                <Upload name="idCardBack" width="300rpx" height="180rpx" :maxCount="1" @fileList="onUpload($event, 'idCardBackList')" :disabled="isFormDisabled" :initList="idCardBackList">
+                  <template v-slot:content>
+                    <view class="upload-slot">
+                      <image class="slot-image" :src="getImageUrl('@local/mine/img-bank1.png')" mode="aspectFit"> </image>
+                    </view>
+                  </template>
+                </Upload>
+              </view>
+              <view style="font-size: 24rpx; color: #666; text-align: center">身份证国徽面</view>
+            </view>
+          </view>
+
+          <!-- 隐藏的表单项,用于校验图片 -->
+          <u-form-item prop="frontCard" style="display: none"></u-form-item>
+          <u-form-item prop="backCard" style="display: none"></u-form-item>
+        </view>
+
+        <view class="form-section">
+          <u-form-item label="姓名" prop="name" :border-bottom="true">
+            <u--input v-model="form.name" border="none" placeholder="请输入姓名" :disabled="isFormDisabled"></u--input>
+          </u-form-item>
+          <u-form-item label="证件号码" prop="idCard" :border-bottom="true">
+            <u--input v-model="form.idCard" border="none" placeholder="请输入证件号码" :disabled="isFormDisabled"></u--input>
+          </u-form-item>
+          <u-form-item label="证件有效期始" prop="startDate" :border-bottom="true" @click="onShowDate('startDate')" :disabled="isFormDisabled">
+            <u--input v-model="form.startDate" border="none" placeholder="请选择" :disabled="isFormDisabled"></u--input>
+            <template #right>
+                <u-icon name="arrow-right"></u-icon>
+            </template>
+          </u-form-item>
+          <u-form-item label="证件有效期止" prop="endDate" :border-bottom="true" @click="onShowDate('endDate')" :disabled="isFormDisabled">
+            <u--input v-model="form.endDate" border="none" placeholder="请选择" :disabled="isFormDisabled"></u--input>
+            <template #right>
+                <u-icon name="arrow-right"></u-icon>
+            </template>
+            <!-- <u-icon slot="right" name="arrow-right"></u-icon> -->
+          </u-form-item>
+          <u-form-item label="证件地址" prop="idAddress" :border-bottom="true">
+            <u--input v-model="form.idAddress" border="none" placeholder="请输入证件地址" :disabled="isFormDisabled"></u--input>
+          </u-form-item>
+        </view>
+
+        <view class="form-section">
+          <u-form-item label="银行" prop="bankName" :border-bottom="true" @click="onShowBank" :disabled="isFormDisabled">
+            <u--input v-model="form.bankName" border="none" placeholder="请选择" :disabled="isFormDisabled"></u--input>
+            <template #right>
+                <u-icon name="arrow-right"></u-icon>
+            </template>
+          </u-form-item>
+          <u-form-item label="银行卡号" prop="bankCardNumber" :border-bottom="true">
+            <u--input v-model="form.bankCardNumber" type="number" border="none" placeholder="请输入银行卡号" :disabled="isFormDisabled"></u--input>
+          </u-form-item>
+          <u-form-item label="开卡人姓名" prop="name" :border-bottom="true">
+            <u--input v-model="form.name" border="none" placeholder="请输入姓名" :disabled="isFormDisabled"></u--input>
+          </u-form-item>
+          <u-form-item label="签约手机号" prop="contractedPhone" :border-bottom="true">
+            <u--input v-model="form.contractedPhone" border="none" type="number" placeholder="请输入手机号" :disabled="isFormDisabled"></u--input>
+          </u-form-item>
+          <u-form-item label="预留手机号" prop="phone" :border-bottom="true">
+            <u--input v-model="form.phone" border="none" type="number" placeholder="请输入手机号" :disabled="isFormDisabled"></u--input>
+          </u-form-item>
+        </view>
+      </u--form>
+
+      <view class="agreement-section" v-if="0">
+        <u-checkbox-group v-model="isAgreed">
+          <u-checkbox name="agree" shape="circle" label="我已阅读并同意《用户服务协议》" labelSize="24rpx" labelColor="#606266"></u-checkbox>
+        </u-checkbox-group>
+      </view>
+
+      <view class="footer-bar">
+        <u-button type="primary" @click="onSubmit" v-if="(auditStatus == '02' && isReSubmit) || !auditStatus || auditStatus == '01'">保 存</u-button>
+      </view>
+    </view>
+
+    <!-- 选择器组件 -->
+    <BankCardSelector ref="bcSelector" :showChangeCard="false" :dataList="bankData" v-model="selectedId" @select="onSelectBank" />
+    <!-- 日期选择 -->
+    <u-datetime-picker
+      ref="datetimeRef"
+      mode="date"
+      :show="showDate"
+      :min-date="new Date('1950-01-01').getTime()"
+      :max-date="maxDate"
+      :close-on-click-overlay="true"
+      v-model="datetimeValue"
+      @close="showDate = false"
+      @cancel="showDate = false"
+      @confirm="onSelectDate"
+    ></u-datetime-picker>
+  </view>
+</template>
+
+<style lang="scss" scoped>
+.page-container {
+  min-height: 100vh;
+  padding: 24rpx 24rpx;
+  background-color: #f8f8fa;
+
+  .form-section {
+    margin: 0 0 24rpx;
+    padding: 20rpx 24rpx;
+    box-shadow: 0 2rpx 8rpx 0 rgba(214, 225, 237, 0.1);
+    border-radius: 16rpx;
+    border: 1rpx solid #f0f3f5;
+    background-color: #fff;
+  }
+
+  .idcard-upload {
+    width: 100%;
+    display: flex;
+    gap: calc(100% - 600rpx - 16rpx);
+
+    .upload-wrapper {
+      padding: 4rpx;
+      background-repeat: no-repeat;
+      background-size: 100% 100%;
+
+      ::v-deep .u-upload__wrap__preview {
+        margin: 0;
+      }
+    }
+
+    .upload-slot {
+      width: 300rpx;
+      height: 180rpx;
+      border-radius: 8rpx;
+      display: flex;
+      align-items: center;
+      flex-direction: column;
+      justify-content: center;
+
+      .slot-image {
+        width: 100%;
+        height: 100%;
+      }
+    }
+  }
+
+  .agreement-section {
+    padding: 20rpx 30rpx;
+  }
+}
+
+.footer-bar {
+  ::v-deep .u-button {
+    width: 100%;
+    height: 76rpx;
+  }
+}
+
+/* 审核状态样式 */
+.audit-status {
+  padding: 20rpx 33rpx;
+  background-color: #fff;
+  box-shadow: 0 2rpx 8rpx 0 rgba(214, 225, 237, 0.1);
+
+  .status-content {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+
+    .status-info {
+      display: flex;
+      font-size: 37.5rpx;
+      color: #333333;
+
+      .status-icon {
+        margin-right: 12rpx;
+      }
+
+      .status-text {
+        font-size: 38rpx;
+        color: #333333;
+        font-weight: 500;
+      }
+    }
+
+    .status-btn {
+      width: 200rpx;
+    }
+  }
+
+  .error-message {
+    font-size: 27rpx;
+    font-weight: 400;
+    color: #666666;
+    line-height: 1.5;
+    margin-top: 16rpx;
+  }
+}
+</style>

+ 201 - 0
src/pages-B/payment/bankCard-list.vue

@@ -0,0 +1,201 @@
+<script setup lang="ts">
+import { ref, computed } from 'vue'
+import { getBankcardList } from '@/api/pay'
+import { getImageUrl } from '@/utils/imageUtil'
+
+// 响应式状态
+const cardList = ref<any[]>([])
+const showActions = ref(false)
+const actions = ref([{ name: '解绑' }])
+const selectedCard = ref<any>(null)
+
+// 静态资源URL
+const staticUrl = import.meta.env.VITE_SERVER_BASEURL
+
+// 计算属性
+const btnText = computed(() => {
+	return cardList.value.length === 0 ? '添加' : '更换'
+})
+
+// 页面加载
+onLoad(() => {
+	// 可以在这里做一些初始化操作
+})
+
+// 页面显示
+onShow(() => {
+	getData()
+})
+
+// 获取数据
+function getData() {
+	getBankcardList().then((res: any) => {
+		if (res.code === 200) {
+			cardList.value = res.result
+		}
+	})
+}
+
+// 获取卡片背景样式
+function getCardBackground(card: any) {
+	const cardColor = card.colorValue || '136, 136, 136'
+	return `linear-gradient(90deg, rgba(${cardColor}, 0.4), rgba(${cardColor}, 0.8)), url(${getImageUrl('@local/vehicle/card-mask.png')}) center/cover no-repeat`
+}
+
+// 添加/更换银行卡
+function addBankCard() {
+	if (btnText.value === '更换') {
+		const cardId = cardList.value[0]?.id
+		if (cardId) {
+			uni.navigateTo({
+				url: `/pages-B/payment/bankCard-edit?type=update&cardId=${cardId}`
+			})
+		}
+	} else {
+		uni.navigateTo({
+			url: '/pages-B/payment/bankCard-edit'
+		})
+	}
+}
+
+// 显示操作菜单
+function showCardActions(card: any) {
+	selectedCard.value = card
+	showActions.value = true
+}
+
+// 选择操作
+function onActionSelect(action: { name: string }) {
+	if (action.name === '解绑') {
+		uni.showModal({
+			title: '确认解绑',
+			content: `您确定要解绑尾号为 ${selectedCard.value?.bankCardNumber?.substring(12)} 的银行卡吗?`,
+			success: (res) => {
+				if (res.confirm && selectedCard.value) {
+					// 调用解绑接口(需要添加)
+					// netBankcardDel(selectedCard.value.id).then((res) => {
+					//   uni.$u.toast(res.message)
+					//   if (res.code === 200) {
+					//     getData()
+					//   }
+					// })
+					uni.$u.toast('解绑功能开发中')
+				}
+			}
+		})
+	}
+	showActions.value = false
+}
+</script>
+
+<template>
+	<view class="page-container new-content">
+		<u-navbar title="银行卡" :autoBack="true" :placeholder="true" :border-bottom="false"></u-navbar>
+
+		<view class="card-list">
+			<view class="card-item" v-for="card in cardList" :key="card.id" :style="{ background: getCardBackground(card) }">
+				<view class="card-header">
+					<image class="bank-logo" :src="card.bankImage" mode="aspectFit"></image>
+					<view class="bank-info">
+						<view class="bank-name">{{ card.bankName }}</view>
+						<view class="card-type">{{ card.cardType || '储蓄卡' }}</view>
+					</view>
+                    <!-- <image class="more" :src="getImageUrl('@local/groupMeal/icon_more.png')" @click="showCardActions(card)"></image> -->
+				</view>
+				<view class="card-number">
+					<text>●●●●</text>
+					<text>●●●●</text>
+					<text>●●●●</text>
+					<text class="n">{{ card.bankCardNumber.substring(12) }}</text>
+				</view>
+			</view>
+			<u-empty 
+				text="没有可用的银行卡" 
+				textColor="#333" 
+				v-if="cardList.length === 0"
+				:icon="`${staticUrl}/static/common/empty15.png`"
+			>
+			</u-empty>
+		</view>
+
+		<view class="footer-bar">
+			<u-button type="primary" @click="addBankCard">{{ btnText }}银行卡</u-button>
+		</view>
+
+		<!-- 操作菜单 -->
+		<u-action-sheet
+			description="解除绑定后从银行卡列表中移除,需要重新添加使用"
+			:round="20"
+			:actions="actions"
+			:show="showActions"
+			:safeAreaInsetBottom="true"
+			@select="onActionSelect"
+			@close="showActions = false"
+		></u-action-sheet>
+	</view>
+</template>
+
+<style lang="scss" scoped>
+.page-container {
+	background-color: #f8f8fa;
+	min-height: 100vh;
+}
+.card-list {
+	padding: 24rpx;
+}
+.card-item {
+	color: #333333;
+	padding: 28rpx;
+	margin-bottom: 20rpx;
+	border-radius: 12rpx;
+	box-shadow: 0 8rpx 20rpx rgba(0, 0, 0, 0.1);
+	.card-header {
+		margin-bottom: 40rpx;
+		position: relative;
+		display: flex;
+		align-items: center;
+		.bank-logo {
+			width: 80rpx;
+			height: 80rpx;
+			border-radius: 12rpx;
+			background-color: #fff;
+		}
+		.bank-info {
+			flex: 1;
+			margin-left: 20rpx;
+			.bank-name {
+				font-size: 32rpx;
+				font-weight: 500;
+			}
+			.card-type {
+				font-size: 24rpx;
+				opacity: 0.8;
+			}
+		}
+		.more {
+			width: 56rpx;
+			height: 40rpx;
+			position: absolute;
+			top: 0;
+			right: 0;
+		}
+	}
+	.card-number {
+		font-size: 14rpx;
+		letter-spacing: 2rpx;
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		.n {
+			font-size: 36rpx;
+			font-family: fantasy;
+		}
+	}
+}
+.footer-bar {
+	::v-deep .u-button {
+		width: 100%;
+		height: 76rpx;
+	}
+}
+</style>

+ 160 - 0
src/pages-B/payment/bankCard-select.vue

@@ -0,0 +1,160 @@
+<script setup lang="ts">
+import { ref, watch } from 'vue'
+
+// 定义props
+const props = withDefaults(defineProps<{
+	// 银行卡列表
+	dataList: Array<{
+		id: string | number
+		logo: string
+		bankName: string
+		last4?: string
+	}>
+	// v-model 绑定值
+	modelValue: string | number
+	title?: string
+	subtitle?: string
+	showChangeCard?: boolean
+}>(), {
+	title: '选择银行卡',     // ✅ 默认值
+	subtitle: '',            // ✅ 默认值
+	showChangeCard: true
+})
+
+const emit = defineEmits<{
+	(e: 'update:modelValue', value: string | number): void
+	(e: 'select', card: typeof props.dataList[0]): void
+}>()
+
+// 内部状态
+const show = ref(false)
+const selectedCardId = ref<string | number>(props.modelValue)
+
+// 静态资源URL(根据实际情况修改)
+const staticUrl = import.meta.env.VITE_SERVER_BASEURL
+
+// 监听 v-model 变化
+watch(() => props.modelValue, (newVal) => {
+	selectedCardId.value = newVal
+})
+
+// 方法
+function open() {
+	show.value = true
+}
+
+function close() {
+	show.value = false
+}
+
+function selectCard(card: typeof props.dataList[0]) {
+	selectedCardId.value = card.id
+	emit('update:modelValue', card.id)  // 使用 update:modelValue
+	emit('select', card)
+	close()
+}
+
+function addCard() {
+	const cardId = props.dataList[0]?.id
+	if (cardId) {
+		uni.navigateTo({
+			url: `/pages-B/payment/bankCard-edit?type=update&cardId=${cardId}` 
+		})
+	}
+	close()
+}
+
+// 暴露方法给父组件
+defineExpose({
+	open,
+	close
+})
+</script>
+
+<template>
+	<u-popup mode="bottom" bgColor="#F7F8FC" 
+		:show="show" :round="20"
+        :safeAreaInsetBottom="true"
+		@open="open"
+        @close="close"
+    >
+		<view class="popup-container">
+			<view class="popup-header">
+				<view class="title">{{ title }}</view>
+				<view class="subtitle">{{ subtitle }}</view>
+			</view>
+
+			<view class="card-list-box">
+				<scroll-view scroll-y class="card-list-scroll">
+					<!-- 银行卡列表 -->
+					<view 
+						class="card-item" 
+						v-for="card in dataList" 
+						:key="card.id"
+						@click="selectCard(card)"
+					>
+						<image class="bank-icon" :src="card.logo" mode="aspectFit"></image>
+						<text class="card-name">{{ card.bankName }} <text v-if="card.last4">({{ card.last4 }})</text></text>
+						<u-icon v-if="selectedCardId === card.id" name="checkbox-mark" color="#2979ff" size="40rpx"></u-icon>
+					</view>
+
+					<!-- 绑定银行卡 -->
+					<view v-if="showChangeCard" class="card-item add-card" @click="addCard">
+						<image class="bank-icon" :src="`${staticUrl}/static/mine/icon-bankadd.png`" mode="aspectFit"></image>
+						<text class="card-name">更换银行卡</text>
+						<u-icon name="arrow-right" color="#C7C6CA" size="32rpx"></u-icon>
+					</view>
+				</scroll-view>
+			</view>
+		</view>
+	</u-popup>
+</template>
+
+<style lang="scss" scoped>
+.u-popup {
+	flex: initial;
+}
+.popup-container {
+    .popup-header {
+        padding: 20rpx 30rpx;
+        text-align: center;
+        border-bottom: 1rpx solid #f0f0f0;
+        .title {
+            font-size: 30rpx;
+            font-weight: 500;
+            color: #232832;
+        }
+        .subtitle {
+            font-weight: 400;
+            font-size: 24rpx;
+            color: rgba(51,51,51,0.8);
+        }
+    }
+	.card-list-box {
+		margin: 0 32rpx 64rpx;
+		border-radius: 20rpx;
+		overflow: hidden;
+	}
+    .card-list-scroll {
+        max-height: 60vh;
+		background: #fff;
+    }
+    .card-item {
+        display: flex;
+        align-items: center;
+        padding: 30rpx;
+        border-bottom: 1rpx solid #f0f0f0;
+
+        .bank-icon {
+            width: 40rpx;
+            height: 40rpx;
+            margin-right: 20rpx;
+        }
+        .card-name {
+            flex: 1;
+            font-size: 30rpx;
+            color: #303133;
+        }
+    }
+}
+</style>

+ 163 - 0
src/pages-B/payment/certification-status.vue

@@ -0,0 +1,163 @@
+<script setup lang="ts">
+import { ref } from 'vue'
+import uw from '@/utils/uni.webview.1.5.6.js'
+import { getImageUrl } from '@/utils/imageUtil'
+
+// 响应式状态
+const query = ref<Record<string, string>>({})
+const signStatus = ref('')
+const statusIcon = ref('')
+const statusText = ref('')
+
+// 页面加载
+onLoad((options?: Record<string, string>) => {
+	if (options) {
+		query.value = options
+		// 获取审核状态
+		const { signStatus: status } = options
+		signStatus.value = status || ''
+		
+		switch (signStatus.value) {
+			case '00':
+				statusText.value = '已提交,等待审核中'
+				statusIcon.value = 'pending'
+				break
+			case '01':
+				statusText.value = '签约成功'
+				statusIcon.value = 'success'
+				break
+			case '02':
+				statusText.value = '拒绝'
+				statusIcon.value = 'reject'
+				break
+			case '03':
+				statusText.value = '待审核'
+				statusIcon.value = 'pending'
+				break
+			case '04':
+				statusText.value = '待签约'
+				statusIcon.value = 'sign'
+				break
+			default:
+				statusText.value = '处理中,请耐心等待'
+				statusIcon.value = 'pending'
+				break
+		}
+	}
+})
+
+// 返回方法
+function goBack() {
+	uw.webView.postMessage({
+		data: {
+			action: 'message'
+		}
+	})
+	
+	const pages = getCurrentPages()
+	if (pages.length > 1) {
+		uni.navigateBack()
+	} else {
+		if (typeof window !== 'undefined' && window.history && window.history.length > 1) {
+			history.back()
+		} else {
+			uni.redirectTo({
+				url: '/pages-B/payment/bankCard-list'
+			})
+		}
+	}
+
+}
+</script>
+
+<template>
+	<view class="page-container new-content">
+		<u-navbar title="认证结果" :bgColor="'#ffffff'" :placeholder="true" autoBack />
+
+		<view class="content-wrapper">
+			<view class="result-icon">
+				<image class="status-icon" :src="getImageUrl(`@local/bankCard/${statusIcon}.png`)" />
+			</view>
+
+			<text class="result-title">{{ statusText }}</text>
+
+			<view class="action-buttons">
+				<u-button class="btn" @click="goBack">点击返回</u-button>
+				<text class="back-link" v-if="0" @click="goBack">返回</text>
+			</view>
+		</view>
+	</view>
+</template>
+
+<style lang="scss" scoped>
+.page-container {
+	min-height: 100vh;
+	background-color: #ffffff;
+	border-top: 1rpx solid #141414;
+
+	.status-icon {
+		width: 100rpx;
+		height: 100rpx;
+		margin-top: 84rpx;
+		margin-bottom: 30rpx;
+	}
+
+	.content-wrapper {
+		flex: 1;
+		display: flex;
+		align-items: center;
+		flex-direction: column;
+		padding: 80rpx 60rpx;
+
+		.result-icon {
+			margin-bottom: 28rpx;
+		}
+
+		.result-title {
+			font-weight: 500;
+			font-size: 48rpx;
+			color: #333333;
+			margin-bottom: 40rpx;
+		}
+
+		.result-desc {
+			font-weight: 400;
+			font-size: 28rpx;
+			color: #666666;
+			text-align: center;
+			line-height: 1.6;
+			margin-bottom: 20rpx;
+			max-width: 500rpx;
+		}
+	}
+
+	.action-buttons {
+		position: absolute;
+		bottom: 200rpx;
+		left: 60rpx;
+		right: 60rpx;
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+
+		.btn {
+			width: 100%;
+			height: 76rpx;
+			font-weight: 500;
+			font-size: 28rpx;
+			color: #FFFFFF;
+			background: var(--theme-color);
+			border-radius: 8rpx;
+			margin-bottom: 10rpx;
+		}
+
+		.back-link {
+			font-weight: 400;
+			font-size: 28rpx;
+			color: #606266;
+			padding: 20rpx;
+			margin-top: 28rpx;
+		}
+	}
+}
+</style>

+ 108 - 0
src/pages-B/payment/web-view.vue

@@ -0,0 +1,108 @@
+<script setup lang="ts">
+import { ref, computed, onMounted } from 'vue'
+import { updateBankCardState } from '@/api/pay'
+
+// 响应式状态
+const wv = ref<any>(null)
+const webUrl = ref('')
+const memberId = ref('')
+const isBack = ref(false)
+
+// 计算属性
+const navHeight = computed(() => {
+	const { statusBarHeight } = uni.getSystemInfoSync()
+	return (statusBarHeight || 0) + 44
+})
+
+const fullscreen = computed(() => {
+	const { uniPlatform } = uni.getSystemInfoSync()
+	return uniPlatform !== 'app'
+})
+
+// 页面加载
+onLoad((options?: Record<string, string>) => {
+	if (options) {
+		const { webUrl: url, memberId: id } = options
+		memberId.value = id || ''
+		
+		if (url) {
+			webUrl.value = decodeURIComponent(url)
+		}
+	}
+})
+
+// 页面就绪
+onMounted(() => {	
+	// 拦截物理返回键
+	// uni.onBackPress((options) => {
+	// 	if (options.from === 'backbutton' && isBack.value) {
+	// 		backToList()
+	// 		return true
+	// 	}
+	// 	return false
+	// })
+})
+
+// ✅ 使用页面生命周期钩子 onBackPress
+function onBackPress(options: { from: string }) {
+	if (options.from === 'backbutton' && isBack.value) {
+		backToList()
+		return true
+	}
+	return false
+}
+
+
+// 返回方法
+function goBack() {
+	if (isBack.value) {
+		backToList()
+		return
+	}
+	uni.navigateBack()
+}
+
+// 返回列表
+function backToList() {
+	if (memberId.value) {
+		updateBankCardState({
+			meberId: memberId.value,
+		}).then((res: any) => {
+			if (res.data.code === 200) {
+				// 成功处理
+			}
+		})
+	}
+	
+	uni.navigateBack({
+		delta: 2,
+	})
+}
+
+// 接收 H5 发送的消息
+function onWebViewMessage(e: any) {
+	const lastData = e.detail.data[e.detail.data.length - 1]
+	isBack.value = lastData.action === 'message'
+	backToList()
+}
+</script>
+
+<template>
+	<view class="webview-container new-content">
+		<u-navbar
+			title="认证流程"
+			:fixed="true"
+			@leftClick="goBack"
+		></u-navbar>
+		<web-view
+			:src="webUrl"
+			:fullscreen="fullscreen"
+			:webview-styles="{ top: `${navHeight}px`, bottom: 0 }"
+			@message="onWebViewMessage"
+		></web-view>
+	</view>
+</template>
+
+<style lang="scss" scoped>
+
+</style>

+ 351 - 0
src/pages-B/payment/withdrawal.vue

@@ -0,0 +1,351 @@
+<script setup lang="ts">
+import { ref, onMounted } from 'vue'
+import { useRequest } from 'alova/client'
+import { getBankcardList, netWithdraw, type BankCard, type WithdrawParams } from '@/api/pay'
+import { getImageUrl } from '@/utils/imageUtil'
+import BankCardSelector from './bankCard-select.vue';
+
+// 当前选择的银行卡
+const bankCard = ref<BankCard>({ id: '', bankName: '', bankCardNumber: '', bankImage: '' })
+// 当前可提现余额
+const availableBalance = ref('0')
+// 提现金额输入框的值
+const withdrawalAmount = ref('')
+// 银行卡数据
+const bankCards = ref<BankCard[]>([])
+// 银行卡数量
+const bankCardNumber = ref(0)
+// 默认选中的卡ID
+const selectedCardId = ref('')
+// 输入框焦点
+const focus = ref(false)
+// 域名
+const domain = ref('')
+
+// 获取银行卡列表
+const { send: fetchBankCards } = useRequest(getBankcardList, {
+    immediate: false,
+    onSuccess: (data: BankCard[]) => {
+        bankCards.value = data
+        if (data.length > 0) {
+            bankCard.value = data[0]
+        }
+    },
+    onError: (error: any) => {
+        console.error('获取银行卡列表失败:', error)
+    }
+})
+
+// 页面加载时
+onLoad((query?: Record<string, string>) => {
+    if (query?.m?.length) availableBalance.value = query.m
+    if (query?.bankNumber?.length) bankCardNumber.value = parseInt(query.bankNumber)
+    fetchBankCards()
+    
+    // #ifdef H5
+    domain.value = extractDomain(window.location.href)
+    // #endif
+    // #ifndef H5
+    domain.value = extractDomain(import.meta.env.VITE_SERVER_BASEURL)
+    // #endif
+})
+
+onReady(() => {
+    focus.value = true
+})
+
+// 选择/修改银行卡
+const bcSelector = ref()
+function selectBankCard() {
+    if (bankCardNumber.value === 0) {
+        uni.navigateTo({ url: '/pages-B/payment/bankCard-edit' })
+    } else {
+        bcSelector.value?.open()
+    }
+}
+
+// 选择事件回调
+function handleCardSelect(card: BankCard) {
+    console.log('您选择了:', card)
+    bankCard.value = card
+    uni.showToast({
+        title: `已选择: ${card.bankName}`,
+        icon: 'none'
+    })
+}
+
+// 全部提现操作
+function allWithdraw() {
+    withdrawalAmount.value = availableBalance.value
+    console.log('已设置全部提现:', withdrawalAmount.value)
+}
+
+// 申请提现
+async function applyWithdrawal() {
+    const amount = parseFloat(withdrawalAmount.value)
+    const balance = parseFloat(availableBalance.value)
+    
+    if (amount > balance) {
+        uni.showToast({
+            title: '提现金额不能超过可提现余额',
+            icon: 'none'
+        })
+        return
+    }
+    
+    if (!bankCard.value.id) {
+        uni.showToast({
+            title: '请先添加银行卡',
+            icon: 'none'
+        })
+        return
+    }
+    
+    const params: WithdrawParams = {
+        amount: withdrawalAmount.value,
+        bankId: bankCard.value.id,
+        callBackUrl: `${domain.value}pages-B/payment/withdrawal?m=${availableBalance.value}`
+    }
+    console.log('提交提现申请:', params)
+
+    // 提现
+    uni.showLoading({ title: '提交中' })
+    try {
+        const res = await netWithdraw(params)
+        console.log('提现结果:', res)
+        uni.hideLoading()
+        uni.showToast({ title: '提现申请成功', icon: 'success' })
+        // 成功后可跳转到提现记录页
+    } catch (error: any) {
+        uni.hideLoading()
+        if (error?.message) {
+            uni.showToast({
+                title: error.message,
+                icon: 'none'
+            })
+        }
+    }
+}
+
+// 跳转三方链接
+function toThirdPartyLink(url: string) {
+    // #ifdef H5
+    window.location.href = url
+    // #endif
+    // #ifndef H5
+    uni.navigateTo({
+        url: `/pages-B/payment/web-view?webUrl=${encodeURIComponent(url)}`,
+    })
+    // #endif
+}
+
+// 取主域名
+function extractDomain(url: string): string {
+    const regex = /^(https?:\/\/[^\/]+)/
+    const match = url.match(regex)
+    return match ? match[1] : ''
+}
+</script>
+
+<template>
+	<view class="page-container new-content">
+		<u-navbar title="提现" :auto-back="true" bgColor="#ffffff" :placeholder="true" :title-style="{ fontWeight: 'bold' }"></u-navbar>
+
+		<view class="form-card">
+			<view class="section-item" @click="selectBankCard">
+				<view class="label">提现至</view>
+				<view class="content">
+					<view class="bank-info">
+						<image 
+							class="bank-icon" 
+							:src="getImageUrl('@local/mine/icon-bankadd.png')"
+							mode="heightFix"
+						/>
+						<text class="bank-name">{{ bankCard.bankName || bankCardNumber === 0 ? '添加银行卡' : '更换银行卡' }} <text v-if="bankCard.bankCardNumber">({{ bankCard.bankCardNumber.slice(-4) }})</text></text>
+					</view>
+                    <u-icon name="arrow-right" color="#C7C6CA" size="32rpx"></u-icon>
+				</view>
+			</view>
+
+			<view class="section-input">
+				<view class="label">提现金额</view>
+				<view class="input-row">
+					<text class="currency-symbol">¥</text>
+					<u--input 
+						type="digit" 
+						border="none"
+						fontSize="52rpx"
+						placeholder="请输入提现金额" 
+						:focus="focus"
+						:customStyle="{ 
+							padding: '0 10rpx' 
+						}"
+						v-model="withdrawalAmount" 
+					></u--input>
+				</view>
+				
+				<view class="balance-info">
+					<text>当前可提现余额{{ availableBalance }}元,</text>
+					<text class="all-withdraw-btn" @click="allWithdraw">全部提现</text>
+				</view>
+				<template v-if="withdrawalAmount && parseFloat(withdrawalAmount) > 0">
+					<view class="tips-info">
+						<text class="l">到账金额</text>
+						<text class="r">{{ (parseFloat(withdrawalAmount) - 1).toFixed(2) }}</text>
+					</view>
+					<view class="tips-info">
+						<text class="l">手续费</text>
+						<text class="r">1元</text>
+					</view>
+				</template>
+			</view>
+		</view>
+
+		<view class="footer-bar">
+            <u-button 
+				type="primary" 
+				:disabled="!withdrawalAmount || parseFloat(withdrawalAmount) <= 1" 
+				@click="applyWithdrawal"
+			>申请提现</u-button>
+		</view>
+        <!-- 选择器组件 -->
+		<BankCardSelector 
+			ref="bcSelector" 
+			subtitle="请留意各银行到账时间"
+			:data-list="bankCards"
+			v-model="selectedCardId"
+			@select="handleCardSelect"
+		/>
+	</view>
+</template>
+
+<style lang="scss" scoped>
+.page-container {
+	min-height: 100vh;
+	background-color: #f8f8fa;
+	display: flex;
+	flex-direction: column;
+
+	.form-card {
+		flex: 1;
+		display: flex;
+		flex-direction: column;
+	}
+}
+
+// 提现至:银行卡选择区域
+.section-item {
+	padding: 20rpx 32rpx;
+	// margin-bottom: 20rpx;
+	
+	.label {
+		font-weight: 500;
+		font-size: 32rpx;
+		color: #333333;
+	}
+	
+	.content {
+		padding-top: 20rpx;
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+	}
+
+	.bank-info {
+		display: flex;
+		align-items: center;
+		margin-right: 15rpx;
+		
+		.bank-icon {
+			width: 48rpx;
+			height: 48rpx;
+			margin-right: 10rpx;
+			display: block;
+		}
+		
+		.bank-name {
+			font-size: 30rpx;
+			line-height: 30rpx;
+		}
+	}
+}
+
+// 提现金额输入区域
+.section-input {
+	flex: 1;
+	padding: 24rpx 32rpx;
+	background: #FFFFFF;
+	box-shadow: 0 4rpx 10rpx 0 #DAE3F4;
+	border-radius: 12rpx 12rpx 0 0;
+	
+	.label {
+		font-weight: 400;
+		font-size: 30rpx;
+		color: #333333;
+		margin-bottom: 20rpx;
+	}
+	
+	.input-row {
+		display: flex;
+		align-items: center;
+		padding: 20rpx 0;
+		border-bottom: 1rpx solid #E1E1E1;
+
+		.currency-symbol {
+			font-size: 52rpx; 
+			font-weight: bold;
+			color: #333;
+			padding-right: 10rpx;
+		}
+
+		// .withdrawal-input {
+		// 	font-size: 52rpx;
+		// 	padding: 0 10rpx;
+		// 	flex: 1;
+		// }
+	}
+
+	.balance-info {
+		font-weight: 400;
+		font-size: 28rpx;
+		color: rgba(51,51,51,0.6);
+		margin: 20rpx 0;
+		
+		.all-withdraw-btn {
+			color: #0879FF;
+			margin-left: 10rpx;
+			
+			&:active {
+				opacity: 0.8;
+			}
+		}
+	}
+
+	.tips-info {
+		margin-bottom: 20rpx;
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+
+		.l {
+			font-weight: 400;
+			font-size: 28rpx;
+			color: #666666;
+		}
+
+		.r {
+			font-weight: 500;
+			font-size: 28rpx;
+			color: #262F48;
+		}
+	}
+}
+
+// 底部按钮
+.footer-bar {
+	::v-deep .u-button {
+		width: 100%;
+		height: 76rpx;
+	}
+}
+</style>

+ 75 - 22
src/pages/income/income.vue

@@ -4,6 +4,7 @@ import { usePagination, useRequest } from 'alova/client'
 import { storeToRefs } from 'pinia'
 import { computed, ref, watch } from 'vue'
 import { getAccountCount } from '@/api/home'
+import { getBankSignRecord } from '@/api/pay'
 import { getCouponIssuerAccountByPageMap } from '@/api/income'
 import IncomeItem from '@/components/IncomeItem.vue'
 import MescrollBody from '@/custom-components/custom-mescroll/mescroll-body.vue'
@@ -273,12 +274,48 @@ function goPage(page: string) {
     })
 }
 
+function goToWithdrawal() {
+    // 获取可提现余额
+    const withdrawableAmount = 100
+    // const withdrawableAmount = accountCountData.value?.withdrawableAmount || 0
+    uni.navigateTo({
+        url: `/pages-B/payment/withdrawal?m=${withdrawableAmount}`,
+    })
+}
+
 function handleItemClick(item) {
     uni.navigateTo({
         url: `/pages-A/incomeShareInfo/index?batchId=${item.batchId}&commissionType=${item.commissionType}&type=${activeIndex.value}`,
     })
     // incomeShareInfoRef.value.show(item)
 }
+
+function onAClick() {
+    // 判断是否有会员进件记录
+    getBankSignRecord().then((res: any) => {
+        if (res.data.code === 200) {
+            if (!res.data.result) {
+                // 没有签约记录,跳转银行卡列表
+                uni.navigateTo({ url: '/pages-B/payment/bankCard-list' });
+            } else {
+                const { state } = res.data.result;
+                if (state === '01') {
+                    // 签约成功,直接跳转银行卡列表
+                    uni.navigateTo({ url: '/pages-B/payment/bankCard-list' });
+                } else {
+                    // 其他状态,跳转银行卡编辑页
+                    uni.navigateTo({ url: '/pages-B/payment/bankCard-edit' });
+                }
+            }
+        } else {
+            uni.$u.toast(res.data.message);
+        }
+        })
+        .catch((err: any) => {
+            console.log('接口调用失败:', err);
+            uni.$u.toast('网络异常,请稍后重试');
+        });
+}
 </script>
 
 <template>
@@ -301,9 +338,13 @@ function handleItemClick(item) {
                             {{ accountCountData?.balance || 0 }}
                         </view>
                         <view class="income-header-balance-num-btns">
-                            <view v-if="isUnlock" class="income-header-balance-num-btn js"
+                            <!-- <view v-if="isUnlock" class="income-header-balance-num-btn js"
                                 @click="goPage('unlockRewards')">
                                 解锁
+                            </view> -->
+                            <view class="income-header-balance-num-btn tx"
+                                @click="goToWithdrawal">
+                                申请提现
                             </view>
                         </view>
                     </view>
@@ -311,10 +352,10 @@ function handleItemClick(item) {
                 <view class="income-header-tips">
                     <view class="income-header-tips-item">
                         <view class="income-header-tips-item-num">
-                            {{ accountCountData?.withdrawableAmount || 0 }}
+                            {{ accountCountData?.totalCommission || 0 }}
                         </view>
                         <view class="income-header-tips-item-des">
-                            可提现金额
+                            累计收益
                         </view>
                     </view>
                     <view class="income-header-tips-item">
@@ -322,15 +363,23 @@ function handleItemClick(item) {
                             {{ accountCountData?.unsettledAmount || 0 }}
                         </view>
                         <view class="income-header-tips-item-des">
-                            未结算金额
+                            待结算
                         </view>
-                    </view>
+                    </view>                    
                     <view class="income-header-tips-item">
                         <view class="income-header-tips-item-num">
-                            {{ accountCountData?.totalCommission || 0 }}
+                            {{ accountCountData?.withdrawableAmount || 0 }}
                         </view>
                         <view class="income-header-tips-item-des">
-                            累计收益
+                            可提现金额
+                        </view>
+                    </view>
+                    <view class="income-header-tips-item" @click="onAClick">
+                        <view class="income-header-tips-item-num">
+                            {{ accountCountData?.unsettledAmount || 0 }}
+                        </view>
+                        <view class="income-header-tips-item-des">
+                            银行卡
                         </view>
                     </view>
                 </view>
@@ -355,6 +404,10 @@ function handleItemClick(item) {
                     @click="tabChange(1)">
                     已结算
                 </view>
+                <view class="income-header-menu-filter-item" :class="[activeIndex === 2 ? 'active' : '']"
+                    @click="tabChange(2)">
+                    已提现
+                </view>
             </view>
             <!-- 时间筛选 -->
             <view class="income-menu-time-filter" :style="{ top: `calc(${menuButtonInfo.bottom + 4}px + 90rpx)` }">
@@ -422,7 +475,7 @@ function handleItemClick(item) {
                     gap: 15rpx;
 
                     .income-header-balance-num-btn {
-                        padding: 20rpx 40rpx;
+                        padding: 20rpx 40rpx 15rpx;
                         border-radius: 33rpx;
                         font-weight: 400;
                         font-size: 26rpx;
@@ -433,8 +486,8 @@ function handleItemClick(item) {
                         }
 
                         &.tx {
-                            background: #bfbfbf;
-                            color: #747474;
+                            background: radial-gradient( 0% 0% at 0% 0%, #F8E1B4 0%, #F9EFC7 100%), #DA4C47;
+                            color: #ED6B66;
                         }
                     }
                 }
@@ -459,18 +512,18 @@ function handleItemClick(item) {
                 font-size: 34rpx;
                 color: #ffffff;
                 line-height: 1;
-                position: relative;
-
-                &:not(:last-child):after {
-                    content: '';
-                    position: absolute;
-                    top: 50%;
-                    right: 0;
-                    transform: translateY(-50%);
-                    width: 2px;
-                    height: 40rpx;
-                    background: #ffffff;
-                }
+                // position: relative;
+
+                // &:not(:last-child):after {
+                //     content: '';
+                //     position: absolute;
+                //     top: 50%;
+                //     right: 0;
+                //     transform: translateY(-50%);
+                //     width: 2px;
+                //     height: 40rpx;
+                //     background: #ffffff;
+                // }
 
                 .income-header-tips-item-num {
                     font-weight: bold;

+ 17 - 0
src/style/index.scss

@@ -32,6 +32,23 @@ page {
   // --wot-button-primary-bg-color: green;
 }
 
+// 底部操作栏
+.footer-bar {
+	position: fixed;
+	left: 0;
+	right: 0;
+	bottom: 0;
+	z-index: 1;
+	background-color: #fff;
+	padding: 20rpx 24rpx;
+	box-shadow: 0 -4rpx 12rpx rgba(0, 0, 0, 0.05);
+	padding-bottom: calc(constant(safe-area-inset-bottom) + 20rpx);
+	padding-bottom: calc(env(safe-area-inset-bottom) + 20rpx);
+	display: flex;
+	align-items: center;
+	justify-content: space-between;
+}
+
 /*
 border-t-1
 由于uniapp中无法使用*选择器,使用魔法代替*,加上此规则可以简化border与divide的使用,并提升布局的兼容性

+ 39 - 0
src/utils/dateFormat.ts

@@ -0,0 +1,39 @@
+/**
+ * 日期格式化原型扩展
+ * 使用方式: new Date().Format('YYYY-MM-DD HH:mm:ss')
+ */
+Date.prototype.Format = function (fmt: string): string {
+  const o: Record<string, number> = {
+    "M+": this.getMonth() + 1, // 月份
+    "D+": this.getDate(), // 日
+    "h+": this.getHours(), // 小时
+    "m+": this.getMinutes(), // 分
+    "s+": this.getSeconds(), // 秒
+    "q+": Math.floor((this.getMonth() + 3) / 3), // 季度
+    "S": this.getMilliseconds() // 毫秒
+  };
+  if (/(Y+)/.test(fmt)) {
+    fmt = fmt.replace(
+      RegExp.$1,
+      (this.getFullYear() + "").substr(4 - RegExp.$1.length)
+    );
+  }
+  for (const k in o) {
+    if (new RegExp(`(${k})`).test(fmt)) {
+      fmt = fmt.replace(
+        RegExp.$1,
+        RegExp.$1.length === 1
+          ? `${o[k]}`
+          : (`00${o[k]}`).substr((o[k] + "").length)
+      );
+    }
+  }
+  return fmt;
+};
+
+// 为 TypeScript 添加类型声明
+declare global {
+  interface Date {
+    Format(fmt: string): string;
+  }
+}

+ 4 - 3
src/utils/imageUtil.ts

@@ -1,6 +1,7 @@
 export function getImageUrl(localPath: string): string {
-    // 移除本地路径前缀(如@img/或@/static/)
-    const relativePath = localPath.replace(/^@img\/|^@\/static\//, '')
+    // 移除本地路径前缀(如@img/或@local/或@/static/)
+    const relativePath = localPath.replace(/^@img\/|^@local\/|^@\/static\//, '')
     // 拼接CDN域名
-    return `${import.meta.env.VITE_CDN_DOMAIN}/${relativePath}`
+    const cdnDOMAIN = localPath.startsWith('@local/') ? import.meta.env.VITE_CDN_LOCAL_DOMAIN : import.meta.env.VITE_CDN_DOMAIN
+    return `${cdnDOMAIN}/${relativePath}`
 }

+ 11 - 3
src/utils/systemInfo.ts

@@ -10,8 +10,15 @@ menuButtonInfo = uni.getMenuButtonBoundingClientRect()
 // #endif
 
 // #ifndef MP-WEIXIN
-// 其他平台提供默认值
-menuButtonInfo = null
+// 其他平台提供默认值(避免在H5等平台为null导致报错)
+menuButtonInfo = {
+    height: 44,
+    bottom: 44,
+    top: 0,
+    left: 0,
+    right: 0,
+    width: 0,
+}
 // #endif
 
 // #ifdef MP-WEIXIN
@@ -30,7 +37,8 @@ safeAreaInsets = systemInfo.safeArea
 // #ifndef MP-WEIXIN
 // 其他平台继续使用uni API
 systemInfo = uni.getSystemInfoSync()
-safeAreaInsets = systemInfo.safeAreaInsets
+// safeAreaInsets = systemInfo.safeAreaInsets
+safeAreaInsets = systemInfo.safeAreaInsets || { top: 0, bottom: 0, left: 0, right: 0 }
 // #endif
 
 // console.log('systemInfo', systemInfo)

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 0
src/utils/uni.webview.1.5.6.js


Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels