macmini 1 هفته پیش
والد
کامیت
86187594ab
4فایلهای تغییر یافته به همراه629 افزوده شده و 1 حذف شده
  1. 385 0
      src/components/FilterTime.vue
  2. 1 1
      src/pages-A/withdraw/index.vue
  3. 243 0
      src/pages-A/withdrawalDetails/index.vue
  4. BIN
      src/static/images/income/tx-icon.png

+ 385 - 0
src/components/FilterTime.vue

@@ -0,0 +1,385 @@
+<script setup lang="ts">
+import dayjs from 'dayjs'
+import { reactive, ref, watch } from 'vue'
+
+// 定义组件属性
+const props = defineProps<{
+    // 初始时间区间,格式为[开始时间, 结束时间]
+    value?: [string, string]
+    // 占位符文本
+    placeholder?: string
+    // 是否禁用
+    disabled?: boolean
+}>()
+
+// 定义事件
+const emit = defineEmits<{
+    // 选择时间后触发的事件
+    (e: 'update:value', value: [string, string]): void
+    // 确定按钮点击事件
+    (e: 'confirm', value: [string, string]): void
+    // 重置按钮点击事件
+    (e: 'reset'): void
+}>()
+
+// 内部状态管理
+const state = reactive({
+    // 时间选择器弹窗是否显示
+    popupVisible: false,
+    // 日期选择器弹窗是否显示
+    datePopupVisible: false,
+    // 当前正在选择的时间类型(start或end)
+    currentDateType: 'start' as 'start' | 'end',
+    // 开始时间
+    startTime: '',
+    // 结束时间
+    endTime: '',
+    // 格式化后的开始时间显示
+    startText: '开始时间',
+    // 格式化后的结束时间显示
+    endText: '结束时间',
+    // 选择器显示文本
+    displayText: props.placeholder || '选择时间区间',
+})
+
+// 日期选择器配置
+const datePickerConfig = reactive({
+    // 日期格式
+    format: 'yyyy-MM-dd',
+    // 当前选择的日期
+    value: dayjs().format('YYYY-MM-DD'),
+    // 最小日期(可选)
+    minDate: 0,
+    // 最大日期(可选)
+    maxDate: 0,
+})
+
+// 监听传入的value变化,更新内部状态
+watch(() => props.value, (newValue) => {
+    if (newValue && Array.isArray(newValue) && newValue.length === 2) {
+        state.startTime = newValue[0]
+        state.endTime = newValue[1]
+        updateDisplayText()
+    }
+}, { immediate: true })
+
+// 打开时间选择器弹窗
+function openPopup() {
+    if (props.disabled) {
+        return
+    }
+    state.popupVisible = true
+}
+
+// 关闭时间选择器弹窗
+function closePopup() {
+    state.popupVisible = false
+}
+
+// 打开日期选择器
+function openDatePicker(type: 'start' | 'end') {
+    state.currentDateType = type
+    // 设置当前选择的日期
+    if (type === 'start') {
+        datePickerConfig.value = state.startTime || dayjs().format('YYYY-MM-DD')
+        // 结束时间之后的日期不可选
+        datePickerConfig.maxDate = state.endTime ? new Date(state.endTime).getTime() : 0
+    }
+    else {
+        datePickerConfig.value = state.endTime || dayjs().format('YYYY-MM-DD')
+        // 开始时间之前的日期不可选
+        datePickerConfig.minDate = state.startTime ? new Date(state.startTime).getTime() : 0
+    }
+    state.datePopupVisible = true
+}
+
+// 关闭日期选择器
+function closeDatePicker() {
+    state.datePopupVisible = false
+}
+
+// 选择日期确认
+function confirmDate() {
+    const selectedDate = datePickerConfig.value
+
+    if (state.currentDateType === 'start') {
+        state.startTime = selectedDate
+        state.startText = selectedDate
+        // 如果结束时间早于开始时间,自动调整结束时间
+        if (state.endTime && dayjs(state.endTime).isBefore(dayjs(selectedDate))) {
+            state.endTime = ''
+            state.endText = '结束时间'
+        }
+    }
+    else {
+        state.endTime = selectedDate
+        state.endText = selectedDate
+        // 如果开始时间晚于结束时间,自动调整开始时间
+        if (state.startTime && dayjs(state.startTime).isAfter(dayjs(selectedDate))) {
+            state.startTime = ''
+            state.startText = '开始时间'
+        }
+    }
+
+    closeDatePicker()
+    updateDisplayText()
+}
+
+// 更新显示文本
+function updateDisplayText() {
+    if (state.startTime && state.endTime) {
+        state.displayText = `${state.startTime} 至 ${state.endTime}`
+    }
+    else {
+        state.displayText = props.placeholder || '选择时间区间'
+    }
+}
+
+// 确认选择
+function confirmSelection() {
+    if (!state.startTime || !state.endTime) {
+        uni.showToast({
+            title: '请选择完整的时间区间',
+            icon: 'none',
+        })
+        return
+    }
+
+    const result: [string, string] = [state.startTime, state.endTime]
+    emit('update:value', result)
+    emit('confirm', result)
+    closePopup()
+}
+
+// 重置选择
+function resetSelection() {
+    state.startTime = ''
+    state.endTime = ''
+    state.startText = '开始时间'
+    state.endText = '结束时间'
+    state.displayText = props.placeholder || '选择时间区间'
+    emit('update:value', ['', ''])
+    emit('reset')
+}
+
+// 暴露方法给父组件
+defineExpose({
+    openPopup
+})
+</script>
+
+<template>
+    <view class="filter-time-component">
+        <!-- 时间选择器触发按钮 -->
+        <!-- <view
+            class="filter-time-trigger"
+            :class="{ disabled }"
+            @click="openPopup"
+        >
+            <text>{{ state.displayText }}</text>
+            <text class="iconfont icon-down" />
+        </view> -->
+
+        <!-- 时间选择器弹窗(顶部弹出) -->
+        <up-popup
+            v-model:show="state.popupVisible"
+            mode="top"
+            :closeable="true"
+            @close="closePopup"
+        >
+            <view class="time-picker-container">
+                <!-- 标题栏 -->
+                <view class="time-picker-header">
+                    <text class="title">时间筛选</text>
+                    <text class="reset-btn" @click="resetSelection">重置</text>
+                </view>
+
+                <!-- 时间选择区域 -->
+                <view class="time-picker-body">
+                    <view class="time-item">
+                        <text class="label">开始时间</text>
+                        <view class="time-selector" @click="openDatePicker('start')">
+                            <text class="time-text">{{ state.startText }}</text>
+                            <text class="iconfont icon-down" />
+                        </view>
+                    </view>
+
+                    <view class="time-item">
+                        <text class="label">结束时间</text>
+                        <view class="time-selector" @click="openDatePicker('end')">
+                            <text class="time-text">{{ state.endText }}</text>
+                            <text class="iconfont icon-down" />
+                        </view>
+                    </view>
+                </view>
+
+                <!-- 操作按钮 -->
+                <view class="time-picker-footer">
+                    <view class="btn cancel-btn" @click="closePopup">
+                        取消
+                    </view>
+                    <view class="btn confirm-btn" @click="confirmSelection">
+                        确定
+                    </view>
+                </view>
+            </view>
+        </up-popup>
+
+        <!-- 日期选择器 -->
+        <up-datetime-picker
+            v-model="datePickerConfig.value"
+            mode="date"
+            :show="state.datePopupVisible"
+            @confirm="confirmDate"
+            @cancel="closeDatePicker"
+        />
+    </view>
+</template>
+
+<style scoped>
+.filter-time-component {
+  position: relative;
+  display: inline-block;
+}
+
+.filter-time-trigger {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  height: 80rpx;
+  padding: 0 30rpx;
+  background-color: #f5f5f5;
+  border-radius: 10rpx;
+  font-size: 28rpx;
+  color: #333333;
+  cursor: pointer;
+}
+
+.filter-time-trigger.disabled {
+  opacity: 0.5;
+  pointer-events: none;
+}
+
+.iconfont {
+  margin-left: 10rpx;
+  font-size: 24rpx;
+  color: #999999;
+}
+
+/* 时间选择器弹窗样式 */
+.time-picker-container {
+  background-color: #ffffff;
+  border-radius: 0 0 20rpx 20rpx;
+  padding-bottom: 30rpx;
+}
+
+.time-picker-header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding: 30rpx;
+  border-bottom: 1rpx solid #eeeeee;
+}
+
+.time-picker-header .title {
+  font-size: 32rpx;
+  font-weight: 500;
+  color: #333333;
+}
+
+.time-picker-header .reset-btn {
+  font-size: 28rpx;
+  color: #64a6ff;
+  cursor: pointer;
+}
+
+.time-picker-body {
+  padding: 30rpx;
+}
+
+.time-item {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  margin-bottom: 30rpx;
+}
+
+.time-item:last-child {
+  margin-bottom: 0;
+}
+
+.time-item .label {
+  font-size: 30rpx;
+  color: #333333;
+}
+
+.time-selector {
+  display: flex;
+  align-items: center;
+  justify-content: flex-end;
+  padding: 20rpx 30rpx;
+  background-color: #f5f5f5;
+  border-radius: 10rpx;
+  cursor: pointer;
+}
+
+.time-selector .time-text {
+  font-size: 28rpx;
+  color: #666666;
+  margin-right: 10rpx;
+}
+
+.time-picker-footer {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding: 0 30rpx;
+}
+
+.btn {
+  flex: 1;
+  height: 80rpx;
+  line-height: 80rpx;
+  text-align: center;
+  border-radius: 10rpx;
+  font-size: 30rpx;
+  font-weight: 500;
+  cursor: pointer;
+}
+
+.btn.cancel-btn {
+  background-color: #f5f5f5;
+  color: #666666;
+  margin-right: 20rpx;
+}
+
+.btn.confirm-btn {
+  background-color: #64a6ff;
+  color: #ffffff;
+  margin-left: 20rpx;
+}
+
+/* 日期选择器弹窗样式 */
+.date-picker-container {
+  background-color: #ffffff;
+  border-radius: 20rpx 20rpx 0 0;
+}
+
+.date-picker-header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding: 30rpx;
+  border-bottom: 1rpx solid #eeeeee;
+}
+
+.date-picker-header .title {
+  font-size: 32rpx;
+  font-weight: 500;
+  color: #333333;
+}
+
+.date-picker-body {
+  padding: 20rpx 0;
+}
+</style>

+ 1 - 1
src/pages-A/withdraw/index.vue

@@ -70,7 +70,7 @@ function goPage(page: string) {
         <view class="income-header-menu-btn">
             申请提现
         </view>
-        <view class="income-header-menu-btn-cancle">
+        <view class="income-header-menu-btn-cancle" @click.stop="goPage('withdrawalDetails')">
             提现记录
         </view>
     </view>

+ 243 - 0
src/pages-A/withdrawalDetails/index.vue

@@ -0,0 +1,243 @@
+<script lang="ts" setup>
+import { storeToRefs } from 'pinia'
+import { ref } from 'vue'
+import FilterTime from '@/components/FilterTime.vue'
+import { LOGIN_PAGE } from '@/router/config'
+import { useUserStore } from '@/store'
+import { useTokenStore } from '@/store/token'
+import { changtime, menuButtonInfo, safeAreaInsets, systemInfo } from '@/utils'
+
+definePage({
+    style: {
+        navigationBarTitleText: '提现明细',
+    },
+})
+
+const userStore = useUserStore()
+const tokenStore = useTokenStore()
+// 使用storeToRefs解构userInfo
+const { userInfo } = storeToRefs(userStore)
+
+function menuClick(page: string) {
+    uni.navigateTo({
+        url: `/pages-A/${page}/index`,
+    })
+}
+
+const show = ref(false)
+const filterValue = ref<string[]>([])
+function confirm() {
+    // 函数实现
+    show.value = false
+}
+function cancel() {
+    show.value = false
+}
+function close() {
+    show.value = false
+}
+function showTimeFilter() {
+    show.value = true
+}
+
+// 处理时间选择变化
+function handleTimeChange(value: string[]) {
+    console.log('时间选择变化', value)
+    filterValue.value = value
+}
+
+// 格式化时间显示
+function formatTimeDisplay() {
+    if (!filterValue.value || filterValue.value[0] === '' || filterValue.value[1] === '') {
+        return '全部时间'
+    }
+    return `${filterValue.value[0]} 至 ${filterValue.value[1]}`
+}
+const wjsList = ref([
+    {
+        id: 1,
+        nickName: '昵称1',
+        name: '优惠券1',
+        price: 1000,
+        date: '2023-08-01 10:00:00',
+    },
+    {
+        id: 2,
+        nickName: '昵称2',
+        name: '优惠券2',
+        price: 1300,
+        date: '2023-10-01 10:00:00',
+    },
+])
+const yjsList = ref([
+    {
+        id: 1,
+        nickName: '昵称3',
+        name: '优惠券3',
+        price: 2300,
+        date: '2023-10-01 10:00:00',
+    },
+    {
+        id: 2,
+        nickName: '昵称4',
+        name: '优惠券4',
+        price: 300,
+        date: '2023-10-01 10:00:00',
+    },
+])
+const list = ref([])
+onLoad(() => {
+    list.value = [...wjsList.value]
+})
+
+function goPage(page: string) {
+    uni.navigateTo({
+        url: `/pages-A/${page}/index`,
+    })
+}
+
+const filterTimeRef = ref()
+function openDatePicker() {
+    // 调用子组件的方法
+    filterTimeRef.value.openPopup()
+}
+</script>
+
+<template>
+    <view class="profile-container">
+        <!-- 时间筛选 -->
+        <view class="income-menu-time-filter">
+            <view class="income-menu-time-filter-left">
+                <view class="income-menu-time-filter-text" @click="showTimeFilter">
+                    <text>{{ formatTimeDisplay() }}</text>
+                    <up-icon name="arrow-down" color="#666666" size="14" />
+                </view>
+                <view>合计:¥4396.00</view>
+            </view>
+            <view class="income-menu-time-filter-right" @click="openDatePicker">
+                日期筛选
+            </view>
+        </view>
+        <view class="income-header-menu">
+            <!-- 优惠券列表 -->
+            <view class="income-header-menu-list">
+                <view v-for="item in list" :key="item.id" class="income-header-menu-item">
+                    <view class="income-header-menu-icon">
+                        <image src="@/static/images/income/tx-icon.png" mode="aspectFit" />
+                        <view class="income-header-menu-text">
+                            <view class="income-header-menu-text-nickname">
+                                提现金额
+                            </view>
+                            <view>2025/10/29  12:44:26</view>
+                        </view>
+                    </view>
+                    <view class="income-header-menu-left">
+                        <view class="income-header-menu-left-amount">
+                            ¥{{ item.price }}
+                        </view>
+                    </view>
+                </view>
+            </view>
+        </view>
+        <up-datetime-picker
+            v-model="filterValue"
+            mode="year-month"
+            :show="show"
+            close-on-click-overlay
+            @confirm="confirm"
+            @cancel="cancel"
+        />
+        <FilterTime ref="filterTimeRef" v-model="filterValue" @update:value="handleTimeChange" />
+    </view>
+</template>
+
+<style lang="scss" scoped>
+.profile-container {
+  font-family: Alibaba PuHuiTi;
+  min-height: 100vh;
+  background-color: #f5f5f5;
+  line-height: 1;
+  padding: 20rpx;
+  .income-menu-time-filter {
+    height: 83rpx;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    font-weight: 400;
+    font-size: 24rpx;
+    color: #333333;
+    .income-menu-time-filter-left {
+      display: flex;
+      align-items: center;
+      gap: 39rpx;
+      .income-menu-time-filter-text {
+        font-weight: 400;
+        font-size: 26rpx;
+        color: #000000;
+        display: flex;
+        align-items: center;
+        gap: 12rpx;
+      }
+    }
+    .income-menu-time-filter-right {
+      font-weight: 400;
+      font-size: 26rpx;
+      color: #1777ff;
+      display: flex;
+      align-items: center;
+    }
+  }
+  .income-header-menu {
+    .income-header-menu-list {
+      background: #ffffff;
+      border-radius: 10rpx;
+      .income-header-menu-item {
+        height: 88rpx;
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        border-bottom: 1px solid #eeeeee;
+        padding: 32rpx 0 29rpx;
+        margin: 0 26rpx;
+        &:last-child {
+          border-bottom: none;
+        }
+        .income-header-menu-icon {
+          display: flex;
+          justify-content: center;
+          align-items: center;
+          gap: 19rpx;
+          image {
+            width: 60rpx;
+            height: 60rpx;
+          }
+          .income-header-menu-text {
+            font-weight: 400;
+            font-size: 24rpx;
+            color: #888888;
+            .income-header-menu-text-nickname {
+              font-size: 28rpx;
+              color: #222222;
+              margin-bottom: 18rpx;
+            }
+          }
+        }
+        .income-header-menu-left {
+          height: 100%;
+          line-height: 100%;
+          font-weight: 500;
+          font-size: 24rpx;
+          color: #888888;
+          display: flex;
+          align-items: center;
+          .income-header-menu-left-amount {
+            font-size: 30rpx;
+            color: #222222;
+            margin-bottom: 18rpx;
+          }
+        }
+      }
+    }
+  }
+}
+</style>

BIN
src/static/images/income/tx-icon.png