caiyuqin пре 3 месеци
родитељ
комит
9f05f72991

+ 24 - 23
package.json

@@ -99,22 +99,22 @@
   "dependencies": {
     "@alova/adapter-uniapp": "^2.0.14",
     "@alova/shared": "^1.3.1",
-    "@dcloudio/uni-app": "3.0.0-4070620250821001",
-    "@dcloudio/uni-app-harmony": "3.0.0-4070620250821001",
-    "@dcloudio/uni-app-plus": "3.0.0-4070620250821001",
-    "@dcloudio/uni-components": "3.0.0-4070620250821001",
-    "@dcloudio/uni-h5": "3.0.0-4070620250821001",
-    "@dcloudio/uni-mp-alipay": "3.0.0-4070620250821001",
-    "@dcloudio/uni-mp-baidu": "3.0.0-4070620250821001",
-    "@dcloudio/uni-mp-harmony": "3.0.0-4070620250821001",
-    "@dcloudio/uni-mp-jd": "3.0.0-4070620250821001",
-    "@dcloudio/uni-mp-kuaishou": "3.0.0-4070620250821001",
-    "@dcloudio/uni-mp-lark": "3.0.0-4070620250821001",
-    "@dcloudio/uni-mp-qq": "3.0.0-4070620250821001",
-    "@dcloudio/uni-mp-toutiao": "3.0.0-4070620250821001",
-    "@dcloudio/uni-mp-weixin": "3.0.0-4070620250821001",
-    "@dcloudio/uni-mp-xhs": "3.0.0-4070620250821001",
-    "@dcloudio/uni-quickapp-webview": "3.0.0-4070620250821001",
+    "@dcloudio/uni-app": "3.0.0-5000720260410001",
+    "@dcloudio/uni-app-harmony": "3.0.0-5000720260410001",
+    "@dcloudio/uni-app-plus": "3.0.0-5000720260410001",
+    "@dcloudio/uni-components": "3.0.0-5000720260410001",
+    "@dcloudio/uni-h5": "3.0.0-5000720260410001",
+    "@dcloudio/uni-mp-alipay": "3.0.0-5000720260410001",
+    "@dcloudio/uni-mp-baidu": "3.0.0-5000720260410001",
+    "@dcloudio/uni-mp-harmony": "3.0.0-5000720260410001",
+    "@dcloudio/uni-mp-jd": "3.0.0-5000720260410001",
+    "@dcloudio/uni-mp-kuaishou": "3.0.0-5000720260410001",
+    "@dcloudio/uni-mp-lark": "3.0.0-5000720260410001",
+    "@dcloudio/uni-mp-qq": "3.0.0-5000720260410001",
+    "@dcloudio/uni-mp-toutiao": "3.0.0-5000720260410001",
+    "@dcloudio/uni-mp-weixin": "3.0.0-5000720260410001",
+    "@dcloudio/uni-mp-xhs": "3.0.0-5000720260410001",
+    "@dcloudio/uni-quickapp-webview": "3.0.0-5000720260410001",
     "abortcontroller-polyfill": "^1.7.8",
     "alova": "^3.3.3",
     "clipboard": "^2.0.11",
@@ -122,11 +122,12 @@
     "image-tools": "^1.4.0",
     "js-cookie": "^3.0.5",
     "lz-string": "^1.5.0",
+    "mescroll-uni": "^1.3.7",
     "pinia": "2.0.36",
     "pinia-plugin-persistedstate": "3.2.1",
     "pinyin-pro": "^3.27.0",
     "uview-plus": "^3.6.17",
-    "vue": "^3.4.21",
+    "vue": "^3.5.35",
     "vue-i18n": "9.1.9",
     "vue-router": "4.5.1",
     "z-paging": "2.8.7"
@@ -134,11 +135,11 @@
   "devDependencies": {
     "@commitlint/cli": "^19.8.1",
     "@commitlint/config-conventional": "^19.8.1",
-    "@dcloudio/types": "^3.4.8",
-    "@dcloudio/uni-automator": "3.0.0-4070620250821001",
-    "@dcloudio/uni-cli-shared": "3.0.0-4070620250821001",
-    "@dcloudio/uni-stacktracey": "3.0.0-4070620250821001",
-    "@dcloudio/vite-plugin-uni": "3.0.0-4070620250821001",
+    "@dcloudio/types": "^3.4.31",
+    "@dcloudio/uni-automator": "3.0.0-5000720260410001",
+    "@dcloudio/uni-cli-shared": "3.0.0-5000720260410001",
+    "@dcloudio/uni-stacktracey": "3.0.0-5000720260410001",
+    "@dcloudio/vite-plugin-uni": "3.0.0-5000720260410001",
     "@esbuild/darwin-arm64": "0.20.2",
     "@esbuild/darwin-x64": "0.20.2",
     "@iconify-json/carbon": "^1.2.4",
@@ -160,7 +161,7 @@
     "@uni-ku/root": "1.4.1",
     "@unocss/eslint-plugin": "^66.2.3",
     "@unocss/preset-legacy-compat": "66.0.0",
-    "@vue/runtime-core": "^3.4.21",
+    "@vue/runtime-core": "^3.5.35",
     "@vue/tsconfig": "^0.1.3",
     "autoprefixer": "^10.4.20",
     "cross-env": "^10.0.0",

+ 28 - 0
project.config.json

@@ -0,0 +1,28 @@
+{
+  "appid": "wx23776edbfe90d642",
+  "compileType": "miniprogram",
+  "libVersion": "3.16.1",
+  "packOptions": {
+    "ignore": [],
+    "include": []
+  },
+  "setting": {
+    "coverView": true,
+    "es6": true,
+    "postcss": true,
+    "minified": true,
+    "enhance": true,
+    "showShadowRootInWxmlPanel": true,
+    "packNpmRelationList": [],
+    "babelSetting": {
+      "ignore": [],
+      "disablePlugins": [],
+      "outputPath": ""
+    }
+  },
+  "condition": {},
+  "editorSetting": {
+    "tabIndent": "insertSpaces",
+    "tabSize": 2
+  }
+}

+ 7 - 0
project.private.config.json

@@ -0,0 +1,7 @@
+{
+  "description": "项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html",
+  "projectname": "couponCenter_mpapp",
+  "setting": {
+    "compileHotReLoad": true
+  }
+}

+ 666 - 0
src/components/dateRangePicker/index.vue

@@ -0,0 +1,666 @@
+<template>
+  <view>
+    <u-popup :show="show" mode="bottom" round="16" @close="handleClose">
+      <view class="date-range-picker">
+        <view class="picker-header">
+          <text class="title">选择时间</text>
+          <view class="close-btn" @click="handleCancel">
+            <u-icon name="close" color="#999" size="28"></u-icon>
+          </view>
+        </view>
+
+        <view class="selected-range">
+          <view class="range-box" :class="{ active: activePicker === 'start', empty: !hasStartDate }"
+            @click="switchPicker('start')">
+            <text class="range-value">{{ startDateStr }}</text>
+          </view>
+          <text class="range-separator">至</text>
+          <view class="range-box" :class="{ active: activePicker === 'end', empty: !hasEndDate }"
+            @click="switchPicker('end')">
+            <text class="range-value">{{ endDateStr }}</text>
+          </view>
+        </view>
+
+        <view class="picker-body" @click="selectCurrentPickerDate">
+          <picker-view class="picker-view" :value="yearValue" @change="onYearChange"
+            :indicator-style="pickerIndicatorStyle">
+            <picker-view-column>
+              <view v-for="(year, idx) in yearList" :key="idx" class="picker-item"
+                :class="{ active: yearValue[0] === idx }">
+                {{ year }}年
+              </view>
+            </picker-view-column>
+          </picker-view>
+
+          <picker-view class="picker-view" :value="monthValue" @change="onMonthChange"
+            :indicator-style="pickerIndicatorStyle">
+            <picker-view-column>
+              <view v-for="(month, idx) in monthList" :key="idx" class="picker-item"
+                :class="{ active: monthValue[0] === idx }">
+                {{ month }}月
+              </view>
+            </picker-view-column>
+          </picker-view>
+
+          <picker-view class="picker-view" :value="dayValue" @change="onDayChange"
+            :indicator-style="pickerIndicatorStyle">
+            <picker-view-column>
+              <view v-for="(day, idx) in dayList" :key="idx" class="picker-item"
+                :class="{ active: dayValue[0] === idx }">
+                {{ day }}日
+              </view>
+            </picker-view-column>
+          </picker-view>
+        </view>
+
+        <view class="picker-footer">
+          <button class="reset-btn" @click="handleReset">重置</button>
+          <button class="confirm-btn" @click="handleConfirm">确定</button>
+        </view>
+      </view>
+    </u-popup>
+  </view>
+</template>
+
+<script setup lang="ts">
+import { ref, computed, watch, nextTick } from "vue";
+
+type PickerType = "start" | "end";
+type DateParts = {
+  year: number;
+  month: number;
+  day: number;
+};
+
+const props = defineProps<{
+  show: boolean;
+  startDate?: string;
+  endDate?: string;
+}>();
+
+const emit = defineEmits<{
+  (e: "update:show", value: boolean): void;
+  (e: "confirm", value: { startDate: string; endDate: string }): void;
+  (e: "cancel"): void;
+}>();
+
+const activePicker = ref<PickerType>("start");
+
+const now = new Date();
+const defaultYear = now.getFullYear();
+const defaultMonth = now.getMonth() + 1;
+const defaultDay = now.getDate();
+
+const startYear = ref(defaultYear);
+const startMonth = ref(defaultMonth);
+const startDay = ref(defaultDay);
+const endYear = ref(defaultYear);
+const endMonth = ref(defaultMonth);
+const endDay = ref(defaultDay);
+
+const hasStartDate = ref(false);
+const hasEndDate = ref(false);
+
+const yearValue = ref<number[]>([0]);
+const monthValue = ref<number[]>([0]);
+const dayValue = ref<number[]>([0]);
+
+const currentDisplayYear = ref(defaultYear);
+const currentDisplayMonth = ref(defaultMonth);
+const currentDisplayDay = ref(defaultDay);
+
+const pickerIndicatorStyle =
+  "height: 40px; border-top: 1rpx solid #ddd; border-bottom: 1rpx solid #ddd;";
+
+const getStartDate = (): DateParts => ({
+  year: startYear.value,
+  month: startMonth.value,
+  day: startDay.value,
+});
+
+const getEndDate = (): DateParts => ({
+  year: endYear.value,
+  month: endMonth.value,
+  day: endDay.value,
+});
+
+const getDateTime = (date: DateParts): number => {
+  return new Date(date.year, date.month - 1, date.day).getTime();
+};
+
+type SelectableRange = {
+  min: DateParts | null;
+  max: DateParts | null;
+};
+
+const getSelectableRange = (): SelectableRange => {
+  if (activePicker.value === "start" && hasEndDate.value) {
+    return { min: null, max: getEndDate() };
+  }
+
+  if (activePicker.value === "end" && hasStartDate.value) {
+    return { min: getStartDate(), max: null };
+  }
+
+  return { min: null, max: null };
+};
+
+const normalizeSelectableDate = (date: DateParts): DateParts => {
+  const { min, max } = getSelectableRange();
+
+  if (min && getDateTime(date) < getDateTime(min)) return { ...min };
+  if (max && getDateTime(date) > getDateTime(max)) return { ...max };
+
+  return date;
+};
+
+const yearList = computed(() => {
+  const { min, max } = getSelectableRange();
+  const minYear = min
+    ? min.year
+    : Math.min(
+        defaultYear,
+        startYear.value,
+        endYear.value,
+        currentDisplayYear.value
+      ) - 10;
+  const maxYear = max
+    ? max.year
+    : Math.max(
+        defaultYear,
+        startYear.value,
+        endYear.value,
+        currentDisplayYear.value,
+        minYear
+      ) + 20;
+  const years: number[] = [];
+
+  for (let i = minYear; i <= maxYear; i++) {
+    years.push(i);
+  }
+
+  return years;
+});
+
+const monthList = computed(() => {
+  const { min, max } = getSelectableRange();
+  const minMonth = min && currentDisplayYear.value === min.year ? min.month : 1;
+  const maxMonth =
+    max && currentDisplayYear.value === max.year ? max.month : 12;
+  const months: number[] = [];
+
+  for (let i = minMonth; i <= maxMonth; i++) {
+    months.push(i);
+  }
+
+  return months;
+});
+
+const getDaysInMonth = (year: number, month: number): number => {
+  return new Date(year, month, 0).getDate();
+};
+
+const dayList = computed(() => {
+  const { min, max } = getSelectableRange();
+  const minDay =
+    min &&
+    currentDisplayYear.value === min.year &&
+    currentDisplayMonth.value === min.month
+      ? min.day
+      : 1;
+  const maxDay =
+    max &&
+    currentDisplayYear.value === max.year &&
+    currentDisplayMonth.value === max.month
+      ? max.day
+      : getDaysInMonth(currentDisplayYear.value, currentDisplayMonth.value);
+  const days: number[] = [];
+
+  for (let i = minDay; i <= maxDay; i++) {
+    days.push(i);
+  }
+
+  return days;
+});
+
+const formatDateText = (date: DateParts): string =>
+  `${date.year}年${date.month}月${date.day}日`;
+const formatDateValue = (date: DateParts): string =>
+  `${date.year}-${String(date.month).padStart(2, "0")}-${String(
+    date.day
+  ).padStart(2, "0")}`;
+
+const startDateStr = computed(() =>
+  hasStartDate.value
+    ? formatDateText({
+        year: startYear.value,
+        month: startMonth.value,
+        day: startDay.value,
+      })
+    : "开始时间"
+);
+const endDateStr = computed(() =>
+  hasEndDate.value
+    ? formatDateText({
+        year: endYear.value,
+        month: endMonth.value,
+        day: endDay.value,
+      })
+    : "结束时间"
+);
+
+const getYearIndex = (year: number): number => {
+  const idx = yearList.value.findIndex((y) => y === year);
+  return idx >= 0 ? idx : 0;
+};
+
+const getMonthIndex = (month: number): number => {
+  const idx = monthList.value.findIndex((item) => item === month);
+  return idx >= 0 ? idx : 0;
+};
+const getDayIndex = (day: number): number => {
+  const idx = dayList.value.findIndex((item) => item === day);
+  return idx >= 0 ? idx : 0;
+};
+
+const getDisplayDate = (): DateParts => ({
+  year: currentDisplayYear.value,
+  month: currentDisplayMonth.value,
+  day: currentDisplayDay.value,
+});
+
+const getActiveDate = (): DateParts => {
+  if (activePicker.value === "start") {
+    return hasStartDate.value ? getStartDate() : getDisplayDate();
+  }
+
+  if (hasEndDate.value) return getEndDate();
+  if (hasStartDate.value) return getStartDate();
+  return getDisplayDate();
+};
+
+const setPickerValue = (date: DateParts) => {
+  const normalizedDate = normalizeSelectableDate(date);
+  const safeDay = Math.min(
+    normalizedDate.day,
+    getDaysInMonth(normalizedDate.year, normalizedDate.month)
+  );
+
+  currentDisplayYear.value = normalizedDate.year;
+  currentDisplayMonth.value = normalizedDate.month;
+  currentDisplayDay.value = safeDay;
+
+  yearValue.value = [0];
+  monthValue.value = [0];
+  dayValue.value = [0];
+
+  nextTick(() => {
+    yearValue.value = [getYearIndex(normalizedDate.year)];
+    monthValue.value = [getMonthIndex(normalizedDate.month)];
+
+    nextTick(() => {
+      dayValue.value = [getDayIndex(safeDay)];
+    });
+  });
+};
+
+const forceUpdatePicker = () => {
+  setPickerValue(getActiveDate());
+};
+
+const getPickerDate = (): DateParts => {
+  const yearIdx = yearValue.value[0] ?? 0;
+  const monthIdx = monthValue.value[0] ?? 0;
+  const dayIdx = dayValue.value[0] ?? 0;
+
+  const year = yearList.value[yearIdx] || yearList.value[0] || defaultYear;
+  const month = monthList.value[monthIdx] || monthList.value[0] || defaultMonth;
+  const maxDay = getDaysInMonth(year, month);
+  const day = Math.min(
+    dayList.value[dayIdx] || dayList.value[0] || defaultDay,
+    maxDay
+  );
+
+  return normalizeSelectableDate({ year, month, day });
+};
+
+const syncToActual = () => {
+  const date = getPickerDate();
+
+  currentDisplayYear.value = date.year;
+  currentDisplayMonth.value = date.month;
+  currentDisplayDay.value = date.day;
+  dayValue.value = [getDayIndex(date.day)];
+
+  if (activePicker.value === "start") {
+    hasStartDate.value = true;
+    startYear.value = date.year;
+    startMonth.value = date.month;
+    startDay.value = date.day;
+    return;
+  }
+
+  hasEndDate.value = true;
+  endYear.value = date.year;
+  endMonth.value = date.month;
+  endDay.value = date.day;
+};
+
+const onYearChange = (e: any) => {
+  const val = e.detail.value;
+  if (val[0] !== yearValue.value[0]) {
+    const year = yearList.value[val[0]] || yearList.value[0] || defaultYear;
+    const date = normalizeSelectableDate({
+      year,
+      month: currentDisplayMonth.value,
+      day: currentDisplayDay.value,
+    });
+
+    setPickerValue(date);
+    nextTick(() => {
+      syncToActual();
+    });
+  }
+};
+
+const onMonthChange = (e: any) => {
+  const val = e.detail.value;
+  if (val[0] !== monthValue.value[0]) {
+    const month = monthList.value[val[0]] || monthList.value[0] || defaultMonth;
+    const date = normalizeSelectableDate({
+      year: currentDisplayYear.value,
+      month,
+      day: currentDisplayDay.value,
+    });
+
+    setPickerValue(date);
+    nextTick(() => {
+      syncToActual();
+    });
+  }
+};
+
+const onDayChange = (e: any) => {
+  const val = e.detail.value;
+  if (val[0] !== dayValue.value[0]) {
+    dayValue.value = [val[0]];
+    syncToActual();
+  }
+};
+
+const showToast = (title: string) => {
+  uni.showToast({
+    title,
+    icon: "none",
+  });
+};
+
+const validateDateRange = (): boolean => {
+  if (!hasStartDate.value || !hasEndDate.value) return true;
+
+  if (getDateTime(getStartDate()) > getDateTime(getEndDate())) {
+    showToast("结束时间不能小于开始时间");
+    return false;
+  }
+
+  return true;
+};
+const selectCurrentPickerDate = () => {
+  syncToActual();
+};
+
+const switchPicker = (picker: PickerType) => {
+  if (activePicker.value === picker) return;
+  activePicker.value = picker;
+  forceUpdatePicker();
+};
+
+const handleReset = () => {
+  hasStartDate.value = false;
+  hasEndDate.value = false;
+
+  startYear.value = defaultYear;
+  startMonth.value = defaultMonth;
+  startDay.value = defaultDay;
+  endYear.value = defaultYear;
+  endMonth.value = defaultMonth;
+  endDay.value = defaultDay;
+
+  currentDisplayYear.value = defaultYear;
+  currentDisplayMonth.value = defaultMonth;
+  currentDisplayDay.value = defaultDay;
+  activePicker.value = "start";
+  forceUpdatePicker();
+};
+
+const handleCancel = () => {
+  emit("cancel");
+  emit("update:show", false);
+};
+
+const handleConfirm = () => {
+  if (!validateDateRange()) return;
+  if (!hasStartDate.value || !hasEndDate.value) {
+    return showToast("请选择时间");
+  }
+
+  emit("confirm", {
+    startDate: hasStartDate.value ? formatDateValue(getStartDate()) : "",
+    endDate: hasEndDate.value ? formatDateValue(getEndDate()) : "",
+  });
+  emit("update:show", false);
+};
+
+const handleClose = () => {
+  emit("update:show", false);
+};
+
+const parseDate = (value: string | undefined): DateParts | null => {
+  if (!value) return null;
+  const parts = value.split("-").map((item) => Number(item));
+  if (parts.length !== 3 || parts.some((item) => Number.isNaN(item)))
+    return null;
+  return {
+    year: parts[0],
+    month: parts[1],
+    day: parts[2],
+  };
+};
+
+const initDates = () => {
+  const start = parseDate(props.startDate);
+  hasStartDate.value = Boolean(start);
+  if (start) {
+    startYear.value = start.year;
+    startMonth.value = start.month;
+    startDay.value = start.day;
+  } else {
+    startYear.value = defaultYear;
+    startMonth.value = defaultMonth;
+    startDay.value = defaultDay;
+  }
+
+  const end = parseDate(props.endDate);
+  hasEndDate.value = Boolean(end);
+  if (end) {
+    endYear.value = end.year;
+    endMonth.value = end.month;
+    endDay.value = end.day;
+  } else {
+    endYear.value = defaultYear;
+    endMonth.value = defaultMonth;
+    endDay.value = defaultDay;
+  }
+
+  const displayDate = start ||
+    end || { year: defaultYear, month: defaultMonth, day: defaultDay };
+  activePicker.value = start ? "start" : end ? "end" : "start";
+  setPickerValue(displayDate);
+};
+
+watch(
+  () => props.show,
+  (newVal) => {
+    if (newVal) {
+      setTimeout(() => {
+        initDates();
+      }, 50);
+    }
+  }
+);
+
+initDates();
+</script>
+
+<style lang="scss" scoped>
+.date-range-picker {
+  position: relative;
+  overflow: hidden;
+  background-color: #fff;
+  border-radius: 16rpx 16rpx 0 0;
+  padding-bottom: calc(30rpx + env(safe-area-inset-bottom));
+}
+
+.picker-header {
+  position: relative;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  height: 96rpx;
+  border-bottom: 1rpx solid rgba(142, 157, 181, 0.22);
+
+  .title {
+    font-size: 34rpx;
+    line-height: 48rpx;
+    font-weight: 600;
+    color: #2e3b51;
+  }
+
+  .close-btn {
+    position: absolute;
+    right: 22rpx;
+    top: 50%;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    width: 64rpx;
+    height: 64rpx;
+    transform: translateY(-50%);
+  }
+}
+
+.selected-range {
+  display: grid;
+  grid-template-columns: minmax(0, 1fr) 42rpx minmax(0, 1fr);
+  align-items: center;
+  column-gap: 18rpx;
+  padding: 32rpx 28rpx 10rpx;
+
+  .range-box {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    height: 36px;
+    padding: 0 16rpx;
+    border: 1rpx solid #ddd;
+    border-radius: 4rpx;
+    background-color: #fff;
+    box-sizing: border-box;
+
+    &.active {
+      border-color: #e45283;
+    }
+
+    .range-value {
+      display: block;
+      max-width: 100%;
+      overflow: hidden;
+      text-align: center;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+      font-size: 30rpx;
+      line-height: 36px;
+      font-weight: 400;
+      color: #3f4f67;
+    }
+  }
+
+  .range-separator {
+    text-align: center;
+    font-size: 28rpx;
+    line-height: 36px;
+    color: #34445b;
+  }
+}
+
+.picker-body {
+  display: grid;
+  grid-template-columns: repeat(3, minmax(0, 1fr));
+  height: 200px;
+  padding: 0 20rpx;
+  background-color: #fff;
+  box-sizing: border-box;
+
+  .picker-view {
+    width: 100%;
+    min-width: 0;
+    height: 100%;
+
+    .picker-item {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      width: 100%;
+      height: 40px;
+      line-height: 40px;
+      padding: 0;
+      text-align: center;
+      font-size: 28rpx;
+      color: #999999;
+      box-sizing: border-box;
+
+      &.active {
+        color: #333333;
+      }
+    }
+  }
+}
+
+.picker-footer {
+  display: grid;
+  grid-template-columns: repeat(2, minmax(0, 1fr));
+  gap: 18rpx;
+  padding: 24rpx 30rpx 0;
+  background-color: #fff;
+
+  .reset-btn,
+  .confirm-btn {
+    width: 100%;
+    height: 49px;
+    line-height: 49px;
+    margin: 0;
+    padding: 0;
+    border-radius: 999rpx;
+    text-align: center;
+    font-size: 32rpx;
+    font-weight: 400;
+    box-sizing: border-box;
+
+    &::after {
+      border: none;
+    }
+  }
+
+  .reset-btn {
+    border: 1rpx solid #ff536b;
+    background-color: transparent;
+    color: #ff536b;
+  }
+
+  .confirm-btn {
+    border: 1rpx solid #ff536b;
+    background-color: #ff536b;
+    color: #fff;
+  }
+}
+</style>

+ 191 - 0
src/components/listScrollView/index.vue

@@ -0,0 +1,191 @@
+<template>
+  <view class="lsv-container">
+    <MescrollUni ref="mescrollRef" :fixed="false" :up="upOption" :down="downOption" @up="upCallback"
+      @down="downCallback" @init="mescrollInit" @omove="onMove" @scroll="onScroll">
+      <view class="lsv-list">
+        <slot name="list" :data="dataList" :is-scrolling="isScrolling"></slot>
+      </view>
+    </MescrollUni>
+  </view>
+</template>
+
+<script setup lang="ts">
+import { ref, computed, onUnmounted } from "vue";
+import MescrollUni from "mescroll-uni/mescroll-uni.vue";
+
+interface Props {
+  // 数据接口
+  api?: Function;
+  // 初始参数
+  init?: Record<string, any>;
+  // 空状态配置
+  empty?: Record<string, any>;
+  // mescroll 额外配置
+  option?: Record<string, any>;
+  // 自定义list数据的字段
+  keyName?: string;
+  // page相关
+  noPage?: boolean;
+  // 每页大小
+  pageSize?: number | string;
+}
+
+const props = withDefaults(defineProps<Props>(), {
+  api: () => {},
+  init: () => ({}),
+  empty: () => ({}),
+  option: () => ({}),
+  keyName: "records",
+  noPage: false,
+  pageSize: 10,
+});
+
+const emit = defineEmits<{
+  (e: "move", value: any): void;
+  (e: "scroll", value: any): void;
+  (e: "afterFetch", data: any): void;
+  (e: "finishFetch"): void;
+}>();
+
+const mescrollRef = ref<any>(null);
+const mescroll = ref<any>(null);
+const dataList = ref<any[]>([]);
+const isScrolling = ref(false);
+const params = ref<Record<string, any>>({});
+let scrollTimer: ReturnType<typeof setTimeout> | null = null;
+
+// 上拉加载的配置
+const upOption = computed(() => {
+  return {
+    onScroll: true,
+    textNoMore: "-- 暂无更多数据 --",
+    page: {
+      size: Number(props.pageSize), // 每页数据的数量,默认10
+    },
+    empty: {
+      use: true,
+      tip: "暂时什么都没有~",
+      icon: "/static/images/mescroll-empty1.png",
+      ...props.empty,
+    },
+    ...props.option,
+    ...(props.option as any)?.upOption,
+  };
+});
+
+// 下拉刷新的配置
+const downOption = computed(() => {
+  return {
+    ...props.option,
+    ...(props.option as any)?.downOption,
+  };
+});
+
+// mescroll 初始化
+const mescrollInit = (e: any) => {
+  mescroll.value = e;
+};
+
+// 滚动中
+const onMove = (e: any) => {
+  emit("move", e);
+};
+
+// 滚动事件(带防抖)
+const onScroll = (e: any) => {
+  isScrolling.value = true;
+  emit("scroll", e);
+
+  if (scrollTimer) clearTimeout(scrollTimer);
+  scrollTimer = setTimeout(() => {
+    isScrolling.value = false;
+  }, 200);
+};
+
+// 下拉刷新
+const downCallback = () => {
+  mescroll.value?.resetUpScroll();
+};
+
+// 上拉加载
+const upCallback = (e: any) => {
+  const requestParams = {
+    pageNo: e.num,
+    pageSize: e.size,
+    ...props.init,
+    ...params.value,
+  };
+
+  // 置空
+  if (e.num == 1) dataList.value = [];
+
+  // 请求
+  props
+    .api(requestParams)
+    .then((res: any) => {
+      if (res) {
+        let response = res;
+        // 无分页处理,用于那些需要下拉和空列表的地方
+        if (props.noPage) {
+          response = {
+            ...res,
+            result: {
+              records: res.result,
+              total: res.result.length,
+            },
+          };
+        }
+        const result = response || {};
+        const records = result[props.keyName]
+          ? result[props.keyName].records || result[props.keyName]
+          : [];
+
+        emit("afterFetch", result);
+
+        // 数据
+        dataList.value = dataList.value.concat(records);
+        mescroll.value?.endBySize(
+          records.length,
+          result.total || result[props.keyName]?.total
+        );
+
+        if (dataList.value.length >= result.total) {
+          emit("finishFetch");
+        }
+      } else {
+        // 触发empty显示
+        mescroll.value?.endSuccess(0);
+        // 取消加载提示
+        mescroll.value?.endErr();
+      }
+    })
+    .catch(() => {
+      mescroll.value?.endErr();
+    });
+};
+
+// 重新加载列表
+const reloadList = (p: Record<string, any>) => {
+  params.value = p;
+  mescroll.value?.resetUpScroll();
+};
+
+// 清理资源
+onUnmounted(() => {
+  if (scrollTimer) clearTimeout(scrollTimer);
+});
+
+defineExpose({
+  reloadList,
+});
+</script>
+
+<style lang="scss" scoped>
+.lsv-container {
+  height: 100%;
+  position: relative;
+  .lsv-list {
+    min-height: 100%;
+  }
+}
+</style>

+ 128 - 0
src/components/popup/index.vue

@@ -0,0 +1,128 @@
+<template>
+  <view class="tip-modal-content">
+    <u-popup :show="isShow" :title="title" :showConfirmButton="false" mode="bottom" border-radius="20">
+      <view class="head-title">
+        {{ title }}
+        <image class='head-close' :src="getImageUrl('@img/income/close.png')" @click="close"></image>
+      </view>
+      <view class="slot-content">
+        <slot></slot>
+      </view>
+
+      <view class="u-flex btn-box" v-if='isFooter'>
+        <view class="btn cancel" @click="close">取消</view>
+        <view class="btn sure" @click="sure">确定</view>
+      </view>
+    </u-popup>
+  </view>
+</template>
+
+<script setup>
+import { getImageUrl } from "@/utils/imageUtil";
+import { ref } from 'vue'
+
+// Props 定义
+const props = defineProps({
+  isShow: {
+    type: Boolean,
+    default: false
+  },
+  title: {
+    type: String,
+    default: ''
+  },
+  isFooter: {
+    type: Boolean,
+    default: true
+  }
+})
+
+const emit = defineEmits(['close', 'sure'])
+
+const close = () => {
+  emit('close')
+}
+
+const sure = () => {
+  emit('sure')
+}
+
+
+// 暴露给父组件的方法(如果需要)
+defineExpose({
+})
+</script>
+
+<style lang="scss" scoped>
+.tip-modal-content {
+  ::v-deep .u-modal__button-group--confirm-button {
+    padding: 0;
+  }
+
+  .modal-footer {
+    border-top: 1rpx solid #eeeeee;
+    padding: 16rpx;
+  }
+
+  ::v-deep .default .u-popup__content {
+    background: linear-gradient(
+      to bottom,
+      #f8e0e3 0%,
+      #ffffff 30%,
+      #ffffff 100%
+    );
+  }
+
+  .head-title {
+    text-align: center;
+    border-bottom: 1rpx solid #eee;
+    height: 86rpx;
+    line-height: 86rpx;
+    padding: 0 30rpx;
+    font-weight: 500;
+    font-size: 36rpx;
+    position: relative;
+    color: #333;
+  }
+
+  .head-close {
+    position: absolute;
+    right: 30rpx;
+    top: 30rpx;
+    height: 32rpx;
+    width: 32rpx;
+    z-index: 1;
+  }
+  .btn-box {
+    background-color: #fff;
+    padding: 16rpx 24rpx 20rpx 24rpx;
+    border-top: 1rpx solid #eee;
+
+    .btn {
+      font-size: 28rpx;
+      border-radius: 54rpx;
+      height: 68rpx;
+      line-height: 68rpx;
+      text-align: center;
+    }
+
+    .cancel {
+      border: 1rpx solid #ccc;
+      color: #999999;
+      width: 205rpx;
+      margin-right: 24rpx;
+    }
+
+    .sure {
+      color: #fff;
+      background: linear-gradient(90deg, #77b6ff 0%, #449aff 100%);
+      flex: 1;
+    }
+  }
+
+  .slot-content {
+    margin: 32rpx;
+    min-height: 20vh;
+  }
+}
+</style>

+ 257 - 0
src/components/popupLayer/index.vue

@@ -0,0 +1,257 @@
+<template>
+  <scroll-view v-show="ifShow" scroll-y @tap="ableClose" @touchmove.stop.prevent class="popup-layer"
+    :class="{ 'popup-layer--closing': isClosing }" :style="{ height: height }">
+    <view ref="popRef" class="popup-box" :class="[`popup-box--${direction}`, { 'popup-box--closing': isClosing }]"
+      @tap.stop="stopEvent" :style="locationStyle">
+      <slot></slot>
+    </view>
+  </scroll-view>
+</template>
+
+<script setup lang="ts">
+import { ref, computed, nextTick, onBeforeUnmount } from "vue";
+
+type Direction = "top" | "bottom" | "left" | "right";
+
+interface Props {
+  /** 弹出方向 */
+  direction?: Direction;
+  /** 是否点击遮罩自动关闭 */
+  autoClose?: boolean;
+  /** 是否适配自定义导航栏 */
+  isTransNav?: boolean;
+  /** 导航栏高度(px) */
+  navHeight?: number;
+}
+
+const props = withDefaults(defineProps<Props>(), {
+  direction: "top",
+  autoClose: true,
+  isTransNav: false,
+  navHeight: 0,
+});
+
+const emit = defineEmits<{
+  (e: "close"): void;
+}>();
+
+const ifShow = ref(false);
+const isClosing = ref(false); // 是否正在关闭(用于动画)
+const translateValue = ref(-100); // 位移距离百分比
+const ifToggle = ref(false); // 动画是否完成
+const height = ref("0px"); // 弹窗高度
+const popRef = ref<HTMLElement | null>(null);
+
+// 定时器
+let openTimer: ReturnType<typeof setTimeout> | null = null;
+let toggleTimer: ReturnType<typeof setTimeout> | null = null;
+let closeTimer: ReturnType<typeof setTimeout> | null = null;
+
+// 位移样式
+const translateStyle = computed(() => {
+  const translate = translateValue.value;
+
+  if (props.isTransNav) {
+    const map: Record<Direction, string> = {
+      top: `transform: translateY(${-translate}%);`,
+      bottom: `transform: translateY(calc(${translate}% + ${props.navHeight}px));`,
+      left: `transform: translateX(${-translate}%);`,
+      right: `transform: translateX(${translate}%);`,
+    };
+    return map[props.direction];
+  }
+
+  const map: Record<Direction, string> = {
+    top: `transform: translateY(${-translate}%);`,
+    bottom: `transform: translateY(${translate}%);`,
+    left: `transform: translateX(${-translate}%);`,
+    right: `transform: translateX(${translate}%);`,
+  };
+  return map[props.direction];
+});
+
+// 位置样式
+const locationStyle = computed(() => {
+  const positionMap: Record<Direction, string> = {
+    top: "bottom: 0px; width: 100%;",
+    bottom: "top: 0px; width: 100%;",
+    left: "right: 0px; height: 100%;",
+    right: "left: 0px; height: 100%;",
+  };
+  return positionMap[props.direction] + translateStyle.value;
+});
+
+// 获取弹窗高度(用于遮罩层)
+const getPopupHeight = () => {
+  nextTick(() => {
+    const query = uni.createSelectorQuery();
+    query
+      .select(".popup-layer")
+      .boundingClientRect((rect: any) => {
+        if (rect && rect.top !== undefined) {
+          height.value = `calc(100% - ${rect.top}px)`;
+        }
+      })
+      .exec();
+  });
+};
+
+// 清除所有定时器
+const clearAllTimers = () => {
+  if (openTimer) {
+    clearTimeout(openTimer);
+    openTimer = null;
+  }
+  if (toggleTimer) {
+    clearTimeout(toggleTimer);
+    toggleTimer = null;
+  }
+  if (closeTimer) {
+    clearTimeout(closeTimer);
+    closeTimer = null;
+  }
+};
+
+// 重置状态(为下次打开做准备)
+const resetState = () => {
+  translateValue.value = -100;
+  ifToggle.value = false;
+  isClosing.value = false;
+};
+
+// 显示弹窗
+const show = () => {
+  if (ifShow.value) return;
+
+  clearAllTimers();
+
+  // 重置状态
+  resetState();
+
+  // 显示弹窗
+  ifShow.value = true;
+
+  // 延迟开始动画,确保 DOM 已渲染
+  openTimer = setTimeout(() => {
+    translateValue.value = 0;
+    openTimer = null;
+  }, 50);
+
+  // 标记动画完成
+  toggleTimer = setTimeout(() => {
+    ifToggle.value = true;
+    toggleTimer = null;
+  }, 350);
+
+  // 获取弹窗高度
+  getPopupHeight();
+};
+
+// 关闭弹窗
+const close = () => {
+  // 防止重复关闭或未完成动画时关闭
+  if (
+    closeTimer !== null ||
+    !ifToggle.value ||
+    isClosing.value ||
+    !ifShow.value
+  ) {
+    return;
+  }
+
+  // 标记正在关闭
+  isClosing.value = true;
+
+  // 开始位移动画
+  translateValue.value = -100 - props.navHeight;
+
+  // 等待动画完成后隐藏
+  closeTimer = setTimeout(() => {
+    ifShow.value = false;
+    isClosing.value = false;
+    ifToggle.value = false;
+    height.value = "0px";
+    closeTimer = null;
+  }, 300);
+
+  // 触发关闭事件
+  emit("close");
+};
+
+// 点击遮罩关闭
+const ableClose = () => {
+  if (props.autoClose && ifToggle.value && !isClosing.value && ifShow.value) {
+    close();
+  }
+};
+
+// 阻止事件冒泡
+const stopEvent = () => {
+  // 空函数,用于阻止事件冒泡到遮罩层
+};
+
+onBeforeUnmount(() => {
+  clearAllTimers();
+});
+
+defineExpose({
+  show,
+  close,
+  ifShow,
+});
+</script>
+
+<style lang="scss" scoped>
+.popup-layer {
+  position: fixed;
+  left: 0;
+  z-index: 999;
+  width: 100%;
+  background: rgba(0, 0, 0, 0.4);
+  overflow: hidden;
+  transition: background 0.3s ease;
+
+  // 关闭时遮罩淡出
+  &.popup-layer--closing {
+    background: rgba(0, 0, 0, 0);
+    pointer-events: none;
+  }
+}
+
+.popup-box {
+  position: absolute;
+  z-index: 1000;
+  background: #ffffff;
+  transition: transform 0.3s ease-in-out;
+
+  // 方向样式
+  &--top {
+    bottom: 0;
+    width: 100%;
+    border-radius: 20rpx 20rpx 0 0;
+  }
+
+  &--bottom {
+    top: 0;
+    width: 100%;
+    border-radius: 0 0 20rpx 20rpx;
+  }
+
+  &--left {
+    right: 0;
+    height: 100%;
+    border-radius: 20rpx 0 0 20rpx;
+  }
+
+  &--right {
+    left: 0;
+    height: 100%;
+    border-radius: 0 20rpx 20rpx 0;
+  }
+
+  // 关闭时保持动画
+  &.popup-box--closing {
+    transition: transform 0.3s ease-in-out;
+  }
+}
+</style>

+ 279 - 0
src/pages-C/income/incomeCancel.vue

@@ -0,0 +1,279 @@
+<script setup lang="ts">
+import { ref } from "vue";
+
+definePage({
+  style: {
+    navigationBarTitleText: "核销收益",
+    navigationStyle: "custom",
+  },
+});
+
+const cumulativeData = ref([
+  {
+    id: 1,
+    type: "邀请新人",
+    amount: "37.92",
+    userName: "林雨欣",
+    inviteDate: "2026-05-18",
+  },
+  {
+    id: 2,
+    type: "邀请新人",
+    amount: "45.00",
+    userName: "张三",
+    inviteDate: "2026-05-19",
+  },
+  {
+    id: 3,
+    type: "邀请新人",
+    amount: "28.50",
+    userName: "李四",
+    inviteDate: "2026-05-20",
+  },
+]);
+
+// 定义tab列表
+const tabList = ref([
+  { label: "今天", value: "0" },
+  { label: "近7天", value: "1" },
+  { label: "近30天", value: "2" },
+]);
+
+// 当前选中的tab
+const currentTab = ref("0");
+
+// 点击tab事件
+const handleTabClick = (value) => {
+  currentTab.value = value;
+  console.log("选中tab:", value);
+};
+</script>
+
+<template>
+  <view class="income-cumulative-container">
+    <u-navbar title="核销收益" :autoBack="true" :placeholder="true" :border-bottom="false"
+      :title-style="{ fontWeight: 'bold', color: '#000' }" leftIconColor="#000"></u-navbar>
+
+    <view class="filter-bar">
+      <view class="filter-tabs">
+        <view v-for="(item, index) in tabList" :key="index" class="filter-tab"
+          :class="{ active: currentTab === item.value }" @click="handleTabClick(item.value)">
+          <text class="tab-text">{{ item.label }}</text>
+        </view>
+      </view>
+      <view class="total-amount">
+        <text class="total-label">合计</text>
+        <text class="total-value">340</text>
+        <text class="total-unit">元</text>
+      </view>
+    </view>
+
+    <view class="cumulative-content">
+      <view class="cumulative-list">
+        <view class="cumulative-item" v-for="item in cumulativeData" :key="item.id">
+          <view class="item-header">
+            <text class="item-type">{{ item.type }}</text>
+            <view class="item-amount">
+              <text class="amount-plus">收益</text>
+              <text class="amount-value">{{ item.amount }}</text>
+              <text class="total-unit ">元</text>
+            </view>
+          </view>
+          <view class="divider"></view>
+          <view class="item-info">
+            <view class="info-row">
+              <text class="info-label">核销用户</text>
+              <text class="info-value">{{ item.userName }}</text>
+            </view>
+            <view class="info-row">
+              <text class="info-label">核销时间</text>
+              <text class="info-value">{{ item.inviteDate }}</text>
+            </view>
+            <view class="info-row">
+              <text class="info-label">商品名称</text>
+              <text class="info-value">{{ item.inviteDate }}</text>
+            </view>
+            <view class="info-row">
+              <text class="info-label">核销商家</text>
+              <text class="info-value">{{ item.inviteDate }}</text>
+            </view>
+            <view class="info-row">
+              <text class="info-label">实付金额</text>
+              <text class="info-value red">{{ item.inviteDate }}</text>
+            </view>
+            <view class="info-row">
+              <text class="info-label">券核销金额</text>
+              <text class="info-value red">{{ item.inviteDate }}</text>
+            </view>
+            <view class="info-row">
+              <text class="info-label">结算时间</text>
+              <text class="info-value ">{{ item.inviteDate }}</text>
+            </view>
+          </view>
+        </view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<style lang="scss" scoped>
+.income-cumulative-container {
+  min-height: 100vh;
+  background: #f8f8fa;
+
+  .filter-bar {
+    width: 100%;
+    height: 92rpx;
+    background: #ffffff;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding: 0 32rpx;
+    box-sizing: border-box;
+
+    .filter-tabs {
+      display: flex;
+      gap: 24rpx;
+
+      .filter-tab {
+        padding: 4rpx 30rpx;
+        border-radius: 32rpx;
+        background: #f7f8fa;
+        display: flex;
+        height: 44rpx;
+        line-height: 44rpx;
+
+        &.active {
+          background: #ff5365;
+
+          .tab-text {
+            color: #ffffff;
+          }
+        }
+
+        .tab-text {
+          font-weight: 400;
+          font-size: 26rpx;
+          color: #333333;
+        }
+      }
+    }
+
+    .total-amount {
+      display: flex;
+      align-items: baseline;
+      gap: 8rpx;
+
+      .total-label {
+        font-weight: 400;
+        font-size: 26rpx;
+        color: #2c2c2c;
+      }
+
+      .total-value {
+        font-weight: 600;
+        font-size: 42rpx;
+        color: #ff5365;
+      }
+
+      .total-unit {
+        font-weight: 400;
+        font-size: 26rpx;
+        color: #ff5365;
+      }
+    }
+  }
+
+  .cumulative-content {
+    width: 100%;
+    padding: 32rpx;
+    box-sizing: border-box;
+  }
+
+  .cumulative-list {
+    display: flex;
+    flex-direction: column;
+    gap: 32rpx;
+  }
+
+  .cumulative-item {
+    background: #ffffff;
+    border-radius: 32rpx;
+    padding: 46rpx 32rpx;
+
+    .item-header {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      margin-bottom: 32rpx;
+
+      .item-type {
+        font-weight: 600;
+        font-size: 32rpx;
+        color: #2c2c2c;
+        line-height: 1;
+      }
+
+      .item-amount {
+        display: flex;
+        align-items: baseline;
+        gap: 8rpx;
+
+        .amount-plus {
+          font-size: 26rpx;
+          color: #2c2c2c;
+          line-height: 1;
+        }
+
+        .amount-value {
+          font-weight: 600;
+          font-size: 42rpx;
+          color: #ff5365;
+          line-height: 1;
+        }
+      }
+    }
+
+    .divider {
+      width: 622rpx;
+      height: 2rpx;
+      background: #eaeaea;
+      margin-bottom: 32rpx;
+    }
+
+    .item-info {
+      display: flex;
+      flex-direction: column;
+      gap: 32rpx;
+
+      .info-row {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+
+        .info-label {
+          font-weight: 400;
+          font-size: 30rpx;
+          color: #86909c;
+          line-height: 1;
+        }
+
+        .info-value {
+          font-weight: 400;
+          font-size: 30rpx;
+          color: #333333;
+          line-height: 1;
+        }
+        .red {
+          color: #ff5365;
+        }
+      }
+    }
+    .total-unit {
+      font-weight: 400;
+      font-size: 26rpx;
+      color: #ff5365;
+    }
+  }
+}
+</style>

+ 296 - 0
src/pages-C/income/incomeCumulative.vue

@@ -0,0 +1,296 @@
+<script setup lang="ts">
+import { ref } from "vue";
+import LsvList from "@/components/listScrollView/index.vue";
+import { getNewCouponList } from "@/api/home";
+
+definePage({
+  style: {
+    navigationBarTitleText: "累计收益",
+    navigationStyle: "custom",
+  },
+});
+
+const cumulativeData = ref([
+  {
+    id: 1,
+    type: "邀请新人",
+    amount: "37.92",
+    userName: "林雨欣",
+    inviteDate: "2026-05-18",
+  },
+  {
+    id: 2,
+    type: "邀请新人",
+    amount: "45.00",
+    userName: "张三",
+    inviteDate: "2026-05-19",
+  },
+  {
+    id: 3,
+    type: "邀请新人",
+    amount: "28.50",
+    userName: "李四",
+    inviteDate: "2026-05-20",
+  },
+]);
+
+// 定义tab列表
+const tabList = ref([
+  { label: "今天", value: "0" },
+  { label: "近7天", value: "1" },
+  { label: "近30天", value: "2" },
+]);
+
+// 当前选中的tab
+const currentTab = ref("0");
+
+// 点击tab事件
+const handleTabClick = (value) => {
+  currentTab.value = value;
+  console.log("选中tab:", value);
+};
+
+const listRef = ref();
+
+// 初始参数
+// const initParams = ref({
+//   name: "",
+//   relatedId: "1889837335015636994",
+//   type: "3",
+//   country: "",
+// });
+
+const getCouponListApi = (params) => {
+  return getNewCouponList({
+    pageNo: params.pageNo,
+    pageSize: params.pageSize,
+    type: 3,
+    relatedId: "1889837335015636994",
+  });
+};
+
+// 刷新列表
+const refreshList = () => {
+  listRef.value?.reloadList();
+};
+</script>
+
+<template>
+  <view class="income-cumulative-container">
+    <view>
+      <u-navbar title="累计收益" :autoBack="true" :placeholder="true" :border-bottom="false"
+        :title-style="{ fontWeight: 'bold', color: '#000' }" leftIconColor="#000"></u-navbar>
+
+      <view class="filter-bar">
+        <view class="filter-tabs">
+          <view v-for="(item, index) in tabList" :key="index" class="filter-tab"
+            :class="{ active: currentTab === item.value }" @click="handleTabClick(item.value)">
+            <text class="tab-text">{{ item.label }}</text>
+          </view>
+        </view>
+        <view class="total-amount">
+          <text class="total-label">合计</text>
+          <text class="total-value">340</text>
+          <text class="total-unit">元</text>
+        </view>
+      </view>
+    </view>
+
+    <view class="cumulative-content">
+      <LsvList ref="listRef" :api="getCouponListApi" :page-size="4" key-name="records">
+        <template #list="{ data }">
+          <view class="cumulative-list">
+            <view class="cumulative-item" v-for="item in data" :key="item.id">
+              <view class="item-header">
+                <text class="item-type">{{ item.type }}</text>
+                <view class="item-amount">
+                  <text class="amount-plus">收益</text>
+                  <text class="amount-value">{{ item.amount }}</text>
+                  <text class="total-unit">元</text>
+                </view>
+              </view>
+              <view class="divider"></view>
+              <view class="item-info">
+                <view class="info-row">
+                  <text class="info-label">领取人</text>
+                  <text class="info-value">{{ item.userName }}</text>
+                </view>
+                <view class="info-row">
+                  <text class="info-label">领取时间</text>
+                  <text class="info-value">{{ item.inviteDate }}</text>
+                </view>
+                <view class="info-row">
+                  <text class="info-label">结算状态</text>
+                  <text class="info-value">{{ item.inviteDate }}</text>
+                </view>
+              </view>
+            </view>
+          </view>
+        </template>
+      </LsvList>
+
+    </view>
+  </view>
+</template>
+
+<style lang="scss" scoped>
+.income-cumulative-container {
+  min-height: 100vh;
+  background: #f8f8fa;
+  display: flex;
+  flex-direction: column;
+
+  .filter-bar {
+    width: 100%;
+    height: 92rpx;
+    background: #ffffff;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding: 0 32rpx;
+    box-sizing: border-box;
+
+    .filter-tabs {
+      display: flex;
+      gap: 24rpx;
+
+      .filter-tab {
+        padding: 4rpx 30rpx;
+        border-radius: 32rpx;
+        background: #f7f8fa;
+        display: flex;
+        height: 44rpx;
+        line-height: 44rpx;
+        &.active {
+          background: #ff5365;
+
+          .tab-text {
+            color: #ffffff;
+          }
+        }
+
+        .tab-text {
+          font-weight: 400;
+          font-size: 26rpx;
+          color: #333333;
+        }
+      }
+    }
+
+    .total-amount {
+      display: flex;
+      align-items: baseline;
+      gap: 8rpx;
+
+      .total-label {
+        font-weight: 400;
+        font-size: 26rpx;
+        color: #2c2c2c;
+      }
+
+      .total-value {
+        font-weight: 600;
+        font-size: 42rpx;
+        color: #ff5365;
+      }
+
+      .total-unit {
+        font-weight: 400;
+        font-size: 26rpx;
+        color: #ff5365;
+      }
+    }
+  }
+
+  .cumulative-content {
+    width: 100%;
+    padding: 32rpx;
+    box-sizing: border-box;
+    flex: 1;
+  }
+
+  .cumulative-list {
+    display: flex;
+    flex-direction: column;
+    gap: 32rpx;
+    height: 100%;
+  }
+
+  .cumulative-item {
+    background: #ffffff;
+    border-radius: 32rpx;
+    padding: 46rpx 32rpx;
+
+    .item-header {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      margin-bottom: 32rpx;
+
+      .item-type {
+        font-weight: 600;
+        font-size: 32rpx;
+        color: #2c2c2c;
+        line-height: 1;
+      }
+
+      .item-amount {
+        display: flex;
+        align-items: baseline;
+        gap: 8rpx;
+
+        .amount-plus {
+          font-size: 26rpx;
+          color: #2c2c2c;
+          line-height: 1;
+        }
+
+        .amount-value {
+          font-weight: 600;
+          font-size: 42rpx;
+          color: #ff5365;
+          line-height: 1;
+        }
+      }
+      .total-unit {
+        font-weight: 400;
+        font-size: 26rpx;
+        color: #ff5365;
+      }
+    }
+
+    .divider {
+      width: 622rpx;
+      height: 2rpx;
+      background: #eaeaea;
+      margin-bottom: 32rpx;
+    }
+
+    .item-info {
+      display: flex;
+      flex-direction: column;
+      gap: 32rpx;
+
+      .info-row {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+
+        .info-label {
+          font-weight: 400;
+          font-size: 30rpx;
+          color: #86909c;
+          line-height: 1;
+        }
+
+        .info-value {
+          font-weight: 400;
+          font-size: 30rpx;
+          color: #333333;
+          line-height: 1;
+        }
+      }
+    }
+  }
+}
+</style>

+ 250 - 0
src/pages-C/income/incomeInvitation.vue

@@ -0,0 +1,250 @@
+<script setup lang="ts">
+import { ref } from "vue";
+
+definePage({
+  style: {
+    navigationBarTitleText: "邀新收益",
+    navigationStyle: "custom",
+  },
+});
+
+const cumulativeData = ref([
+  {
+    id: 1,
+    type: "邀请新人",
+    amount: "37.92",
+    userName: "林雨欣",
+    inviteDate: "2026-05-18",
+  },
+  {
+    id: 2,
+    type: "邀请新人",
+    amount: "45.00",
+    userName: "张三",
+    inviteDate: "2026-05-19",
+  },
+  {
+    id: 3,
+    type: "邀请新人",
+    amount: "28.50",
+    userName: "李四",
+    inviteDate: "2026-05-20",
+  },
+]);
+
+// 定义tab列表
+const tabList = ref([
+  { label: "今天", value: "0" },
+  { label: "近7天", value: "1" },
+  { label: "近30天", value: "2" },
+]);
+
+// 当前选中的tab
+const currentTab = ref("0");
+
+// 点击tab事件
+const handleTabClick = (value) => {
+  currentTab.value = value;
+  console.log("选中tab:", value);
+};
+</script>
+
+<template>
+  <view class="income-cumulative-container">
+    <u-navbar title="邀新收益" :autoBack="true" :placeholder="true" :border-bottom="false"
+      :title-style="{ fontWeight: 'bold', color: '#000' }" leftIconColor="#000"></u-navbar>
+
+    <view class="filter-bar">
+      <view class="filter-tabs">
+        <view v-for="(item, index) in tabList" :key="index" class="filter-tab"
+          :class="{ active: currentTab === item.value }" @click="handleTabClick(item.value)">
+          <text class="tab-text">{{ item.label }}</text>
+        </view>
+      </view>
+      <view class="total-amount">
+        <text class="total-label">合计</text>
+        <text class="total-value">340</text>
+        <text class="total-unit">元</text>
+      </view>
+    </view>
+
+    <view class="cumulative-content">
+      <view class="cumulative-list">
+        <view class="cumulative-item" v-for="item in cumulativeData" :key="item.id">
+          <view class="item-header">
+            <text class="item-type">{{ item.type }}</text>
+            <view class="item-amount">
+              <text class="amount-plus">+</text>
+              <text class="amount-value">{{ item.amount }}</text>
+            </view>
+          </view>
+          <view class="divider"></view>
+          <view class="item-info">
+            <view class="info-row">
+              <text class="info-label">用户名称</text>
+              <text class="info-value">{{ item.userName }}</text>
+            </view>
+            <view class="info-row">
+              <text class="info-label">邀请日期</text>
+              <text class="info-value">{{ item.inviteDate }}</text>
+            </view>
+          </view>
+        </view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<style lang="scss" scoped>
+.income-cumulative-container {
+  min-height: 100vh;
+  background: #f8f8fa;
+
+  .filter-bar {
+    width: 100%;
+    height: 92rpx;
+    background: #ffffff;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding: 0 32rpx;
+    box-sizing: border-box;
+
+    .filter-tabs {
+      display: flex;
+      gap: 24rpx;
+
+      .filter-tab {
+        padding: 4rpx 30rpx;
+        border-radius: 32rpx;
+        background: #f7f8fa;
+        display: flex;
+        height: 44rpx;
+        line-height: 44rpx;
+        &.active {
+          background: #ff5365;
+
+          .tab-text {
+            color: #ffffff;
+          }
+        }
+
+        .tab-text {
+          font-weight: 400;
+          font-size: 26rpx;
+          color: #333333;
+        }
+      }
+    }
+
+    .total-amount {
+      display: flex;
+      align-items: baseline;
+      gap: 8rpx;
+
+      .total-label {
+        font-weight: 400;
+        font-size: 26rpx;
+        color: #2c2c2c;
+      }
+
+      .total-value {
+        font-weight: 600;
+        font-size: 42rpx;
+        color: #ff5365;
+      }
+
+      .total-unit {
+        font-weight: 400;
+        font-size: 26rpx;
+        color: #ff5365;
+      }
+    }
+  }
+
+  .cumulative-content {
+    width: 100%;
+    padding: 32rpx;
+    box-sizing: border-box;
+  }
+
+  .cumulative-list {
+    display: flex;
+    flex-direction: column;
+    gap: 32rpx;
+  }
+
+  .cumulative-item {
+    background: #ffffff;
+    border-radius: 32rpx;
+    padding: 46rpx 32rpx;
+
+    .item-header {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      margin-bottom: 32rpx;
+
+      .item-type {
+        font-weight: 600;
+        font-size: 32rpx;
+        color: #2c2c2c;
+        line-height: 1;
+      }
+
+      .item-amount {
+        display: flex;
+        align-items: baseline;
+        gap: 8rpx;
+
+        .amount-plus {
+          font-weight: 600;
+          font-size: 40rpx;
+          color: #ff5365;
+          line-height: 1;
+        }
+
+        .amount-value {
+          font-weight: 600;
+          font-size: 42rpx;
+          color: #ff5365;
+          line-height: 1;
+        }
+      }
+    }
+
+    .divider {
+      width: 622rpx;
+      height: 2rpx;
+      background: #eaeaea;
+      margin-bottom: 32rpx;
+    }
+
+    .item-info {
+      display: flex;
+      flex-direction: column;
+      gap: 32rpx;
+
+      .info-row {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+
+        .info-label {
+          font-weight: 400;
+          font-size: 30rpx;
+          color: #86909c;
+          line-height: 1;
+        }
+
+        .info-value {
+          font-weight: 400;
+          font-size: 30rpx;
+          color: #333333;
+          line-height: 1;
+        }
+      }
+    }
+  }
+}
+</style>

+ 244 - 0
src/pages-C/income/incomeMoney.vue

@@ -0,0 +1,244 @@
+<script setup lang="ts">
+import { ref } from "vue";
+import { getImageUrl } from "@/utils/imageUtil";
+definePage({
+  style: {
+    navigationBarTitleText: "累计提现",
+    navigationStyle: "custom",
+  },
+});
+
+const moneyData = ref([
+  {
+    id: 1,
+    type: "提现到银行卡",
+    amount: "37.92",
+    bankName: "招商银行",
+    date: "2026-05-18 19:00:00",
+  },
+  {
+    id: 2,
+    type: "提现到银行卡",
+    amount: "37.92",
+    bankName: "招商银行",
+    date: "2026-05-18 19:00:00",
+  },
+  {
+    id: 3,
+    type: "提现到银行卡",
+    amount: "37.92",
+    bankName: "招商银行",
+    date: "2026-05-18 19:00:00",
+  },
+  {
+    id: 4,
+    type: "提现到银行卡",
+    amount: "37.92",
+    bankName: "招商银行",
+    date: "2026-05-18 19:00:00",
+  },
+  {
+    id: 5,
+    type: "提现到银行卡",
+    amount: "37.92",
+    bankName: "招商银行",
+    date: "2026-05-18 19:00:00",
+  },
+  {
+    id: 6,
+    type: "提现到银行卡",
+    amount: "37.92",
+    bankName: "招商银行",
+    date: "2026-05-18 19:00:00",
+  },
+  {
+    id: 7,
+    type: "提现到银行卡",
+    amount: "37.92",
+    bankName: "招商银行",
+    date: "2026-05-18 19:00:00",
+  },
+]);
+
+const showPicker = ref(false);
+const defaultStart = "";
+const defaultEnd = "";
+
+const handleConfirm = (value: { startDate: string; endDate: string }) => {
+  console.log("选中的日期范围:", value);
+  // 处理业务逻辑
+};
+
+const handleCancel = () => {
+  console.log("取消选择");
+};
+</script>
+
+<template>
+  <view class="income-money-container">
+    <u-navbar title="累计提现" :autoBack="true" :placeholder="true" :border-bottom="false"
+      :title-style="{ fontWeight: 'bold', color: '#000' }" leftIconColor="#000"></u-navbar>
+
+    <view class='income-select'>
+      <view class='income-time' @click='showPicker=true'>
+        <view class='income-time-name'>2026 05.10-2026 05.12 </view>
+        <u--image class='income-down' :src="getImageUrl('@img/income/down.png')" mode="aspectFit" width="18rpx"
+          height="18rpx">
+        </u--image>
+      </view>
+      <view class="total-amount">
+        <text class="total-label">合计</text>
+        <text class="total-value">340</text>
+        <text class="total-unit">元</text>
+      </view>
+    </view>
+
+    <view class="money-content">
+      <view class="money-card">
+        <view class="money-list">
+          <view class="money-item" v-for="item in moneyData" :key="item.id">
+            <view class="item-header">
+              <text class="item-type">{{ item.type }}</text>
+              <view class="item-amount">
+                <text class="amount-symbol">¥</text>
+                <text class="amount-value">{{ item.amount }}</text>
+              </view>
+            </view>
+            <view class="item-info">
+              <text class="info-label">{{ item.bankName }}</text>
+              <text class="info-value">{{ item.date }}</text>
+            </view>
+            <view class="divider" v-if="item.id !== moneyData.length"></view>
+          </view>
+        </view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<style lang="scss" scoped>
+.income-money-container {
+  min-height: 100vh;
+  background: #f8f8fa;
+
+  .money-content {
+    padding: 32rpx;
+  }
+  .income-select {
+    padding: 32rpx;
+    box-sizing: border-box;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    background: #fff;
+    .income-time-name {
+      font-size: 28rpx;
+      color: #333;
+    }
+    .income-time {
+      display: flex;
+    }
+    .income-down {
+      margin-top: -4rpx;
+    }
+    .total-amount {
+      display: flex;
+      align-items: baseline;
+      gap: 8rpx;
+
+      .total-label {
+        font-weight: 400;
+        font-size: 26rpx;
+        color: #2c2c2c;
+      }
+
+      .total-value {
+        font-weight: 600;
+        font-size: 42rpx;
+        color: #ff5365;
+      }
+
+      .total-unit {
+        font-weight: 400;
+        font-size: 26rpx;
+        color: #ff5365;
+      }
+    }
+  }
+  .money-card {
+    background: #ffffff;
+    border-radius: 32rpx;
+    padding: 0 32rpx;
+    box-sizing: border-box;
+
+    .money-list {
+      display: flex;
+      flex-direction: column;
+    }
+
+    .money-item {
+      .item-header {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        margin-bottom: 33rpx;
+
+        .item-type {
+          font-weight: 600;
+          font-size: 32rpx;
+          color: #2c2c2c;
+          line-height: 1;
+        }
+
+        .item-amount {
+          display: flex;
+          align-items: baseline;
+          gap: 4rpx;
+
+          .amount-symbol {
+            font-weight: 400;
+            font-size: 26rpx;
+            color: #ff5365;
+            line-height: 1;
+          }
+
+          .amount-value {
+            font-weight: 600;
+            font-size: 42rpx;
+            color: #ff5365;
+            line-height: 1;
+          }
+        }
+      }
+
+      .item-info {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        margin-bottom: 32rpx;
+
+        .info-label {
+          font-weight: 400;
+          font-size: 30rpx;
+          color: #86909c;
+          line-height: 1;
+        }
+
+        .info-value {
+          font-weight: 400;
+          font-size: 30rpx;
+          color: #333333;
+          line-height: 1;
+        }
+      }
+
+      .divider {
+        width: 622rpx;
+        height: 1rpx;
+        background: #eaeaea;
+        margin-bottom: 32rpx;
+      }
+    }
+  }
+}
+</style>

+ 259 - 0
src/pages-C/income/incomeSend.vue

@@ -0,0 +1,259 @@
+<script setup lang="ts">
+import { ref } from "vue";
+
+definePage({
+  style: {
+    navigationBarTitleText: "发券收益",
+    navigationStyle: "custom",
+  },
+});
+
+const cumulativeData = ref([
+  {
+    id: 1,
+    type: "邀请新人",
+    amount: "37.92",
+    userName: "林雨欣",
+    inviteDate: "2026-05-18",
+  },
+  {
+    id: 2,
+    type: "邀请新人",
+    amount: "45.00",
+    userName: "张三",
+    inviteDate: "2026-05-19",
+  },
+  {
+    id: 3,
+    type: "邀请新人",
+    amount: "28.50",
+    userName: "李四",
+    inviteDate: "2026-05-20",
+  },
+]);
+
+// 定义tab列表
+const tabList = ref([
+  { label: "今天", value: "0" },
+  { label: "近7天", value: "1" },
+  { label: "近30天", value: "2" },
+]);
+
+// 当前选中的tab
+const currentTab = ref("0");
+
+// 点击tab事件
+const handleTabClick = (value) => {
+  currentTab.value = value;
+  console.log("选中tab:", value);
+};
+</script>
+
+<template>
+  <view class="income-cumulative-container">
+    <u-navbar title="发券收益" :autoBack="true" :placeholder="true" :border-bottom="false"
+      :title-style="{ fontWeight: 'bold', color: '#000' }" leftIconColor="#000"></u-navbar>
+
+    <view class="filter-bar">
+      <view class="filter-tabs">
+        <view v-for="(item, index) in tabList" :key="index" class="filter-tab"
+          :class="{ active: currentTab === item.value }" @click="handleTabClick(item.value)">
+          <text class="tab-text">{{ item.label }}</text>
+        </view>
+      </view>
+      <view class="total-amount">
+        <text class="total-label">合计</text>
+        <text class="total-value">340</text>
+        <text class="total-unit">元</text>
+      </view>
+    </view>
+
+    <view class="cumulative-content">
+      <view class="cumulative-list">
+        <view class="cumulative-item" v-for="item in cumulativeData" :key="item.id">
+          <view class="item-header">
+            <text class="item-type">{{ item.type }}</text>
+            <view class="item-amount">
+              <text class="amount-plus">收益</text>
+              <text class="amount-value">{{ item.amount }}</text>
+              <text class="total-unit">元</text>
+            </view>
+          </view>
+          <view class="divider"></view>
+          <view class="item-info">
+            <view class="info-row">
+              <text class="info-label">领取人</text>
+              <text class="info-value">{{ item.userName }}</text>
+            </view>
+            <view class="info-row">
+              <text class="info-label">领取时间</text>
+              <text class="info-value">{{ item.inviteDate }}</text>
+            </view>
+            <view class="info-row">
+              <text class="info-label">结算状态</text>
+              <text class="info-value">{{ item.inviteDate }}</text>
+            </view>
+          </view>
+        </view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<style lang="scss" scoped>
+.income-cumulative-container {
+  min-height: 100vh;
+  background: #f8f8fa;
+
+  .filter-bar {
+    width: 100%;
+    height: 92rpx;
+    background: #ffffff;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding: 0 32rpx;
+    box-sizing: border-box;
+
+    .filter-tabs {
+      display: flex;
+      gap: 24rpx;
+
+      .filter-tab {
+        padding: 4rpx 30rpx;
+        border-radius: 32rpx;
+        background: #f7f8fa;
+        display: flex;
+        height: 44rpx;
+        line-height: 44rpx;
+        &.active {
+          background: #ff5365;
+
+          .tab-text {
+            color: #ffffff;
+          }
+        }
+
+        .tab-text {
+          font-weight: 400;
+          font-size: 26rpx;
+          color: #333333;
+        }
+      }
+    }
+
+    .total-amount {
+      display: flex;
+      align-items: baseline;
+      gap: 8rpx;
+
+      .total-label {
+        font-weight: 400;
+        font-size: 26rpx;
+        color: #2c2c2c;
+      }
+
+      .total-value {
+        font-weight: 600;
+        font-size: 42rpx;
+        color: #ff5365;
+      }
+
+      .total-unit {
+        font-weight: 400;
+        font-size: 26rpx;
+        color: #ff5365;
+      }
+    }
+  }
+
+  .cumulative-content {
+    width: 100%;
+    padding: 32rpx;
+    box-sizing: border-box;
+  }
+
+  .cumulative-list {
+    display: flex;
+    flex-direction: column;
+    gap: 32rpx;
+  }
+
+  .cumulative-item {
+    background: #ffffff;
+    border-radius: 32rpx;
+    padding: 46rpx 32rpx;
+
+    .item-header {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      margin-bottom: 32rpx;
+
+      .item-type {
+        font-weight: 600;
+        font-size: 32rpx;
+        color: #2c2c2c;
+        line-height: 1;
+      }
+
+      .item-amount {
+        display: flex;
+        align-items: baseline;
+        gap: 8rpx;
+
+        .amount-plus {
+          font-size: 26rpx;
+          color: #2c2c2c;
+          line-height: 1;
+        }
+
+        .amount-value {
+          font-weight: 600;
+          font-size: 42rpx;
+          color: #ff5365;
+          line-height: 1;
+        }
+      }
+      .total-unit {
+        font-weight: 400;
+        font-size: 26rpx;
+        color: #ff5365;
+      }
+    }
+
+    .divider {
+      width: 622rpx;
+      height: 2rpx;
+      background: #eaeaea;
+      margin-bottom: 32rpx;
+    }
+
+    .item-info {
+      display: flex;
+      flex-direction: column;
+      gap: 32rpx;
+
+      .info-row {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+
+        .info-label {
+          font-weight: 400;
+          font-size: 30rpx;
+          color: #86909c;
+          line-height: 1;
+        }
+
+        .info-value {
+          font-weight: 400;
+          font-size: 30rpx;
+          color: #333333;
+          line-height: 1;
+        }
+      }
+    }
+  }
+}
+</style>

+ 200 - 0
src/pages-C/income/invitation.vue

@@ -0,0 +1,200 @@
+<script setup lang="ts">
+import { ref } from "vue";
+import { getImageUrl } from "@/utils/imageUtil";
+definePage({
+  style: {
+    navigationBarTitleText: "邀新奖励",
+    navigationStyle: "custom",
+  },
+});
+
+const totalIncome = ref("38.00");
+
+const invitationList = ref([
+  {
+    id: 1,
+    userName: "吴磊",
+    invitationTime: "2026-05-18 19:00:00",
+    commission: "38.00",
+  },
+  {
+    id: 2,
+    userName: "吴磊",
+    invitationTime: "2026-05-18 19:00:00",
+    commission: "38.00",
+  },
+  {
+    id: 3,
+    userName: "吴磊",
+    invitationTime: "2026-05-18 19:00:00",
+    commission: "38.00",
+  },
+  {
+    id: 4,
+    userName: "吴磊",
+    invitationTime: "2026-05-18 19:00:00",
+    commission: "38.00",
+  },
+]);
+</script>
+
+<template>
+  <view class="invitation-container">
+    <u-navbar title="邀新奖励" :autoBack="true" :placeholder="true" :border-bottom="false"
+      :title-style="{ fontWeight: 'bold', color: '#000' }" leftIconColor="#000"></u-navbar>
+
+    <view class="invitation-content">
+      <view class="invitation-card">
+        <view class="card-header">
+          <view class="title-section">
+            <view class="title-line"></view>
+            <text class="title-text">邀新奖励</text>
+          </view>
+          <view class="total-income">
+            <text class="total-label">总收益</text>
+            <view>
+              <text class="total-symbol">¥</text>
+              <text class="total-amount">{{ totalIncome }}</text>
+            </view>
+          </view>
+        </view>
+
+        <view class="header-divider"></view>
+
+        <view class="records-list">
+          <view v-for="(item, index) in invitationList" :key="item.id" class="record-item">
+            <view class="info-row">
+              <text class="info-label">用户名称</text>
+              <text class="info-value">{{ item.userName }}</text>
+            </view>
+            <view class="info-row">
+              <text class="info-label">邀请时间</text>
+              <text class="info-value">{{ item.invitationTime }}</text>
+            </view>
+            <view class="info-row">
+              <text class="info-label">获得佣金</text>
+              <text class="info-value highlight">¥{{ item.commission }}</text>
+            </view>
+
+            <view v-if="index < invitationList.length - 1" class="record-divider"></view>
+          </view>
+        </view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<style lang="scss" scoped>
+.invitation-container {
+  min-height: 100vh;
+  background: #f8f8fa;
+
+  .invitation-content {
+    padding: 32rpx;
+  }
+
+  .invitation-card {
+    background: #ffffff;
+    border-radius: 32rpx;
+    padding: 32rpx;
+
+    .card-header {
+      display: flex;
+      justify-content: space-between;
+      align-items: flex-start;
+
+      .title-section {
+        display: flex;
+        align-items: center;
+        gap: 18rpx;
+
+        .title-line {
+          width: 6rpx;
+          height: 32rpx;
+          background: #ff5365;
+        }
+
+        .title-text {
+          font-weight: 600;
+          font-size: 32rpx;
+          color: #333333;
+          line-height: 32rpx;
+        }
+      }
+
+      .total-income {
+        display: flex;
+        align-items: baseline;
+        gap: 8rpx;
+
+        .total-label {
+          font-weight: 400;
+          font-size: 26rpx;
+          color: #2c2c2c;
+          line-height: 26rpx;
+        }
+
+        .total-symbol {
+          font-weight: 600;
+          font-size: 26rpx;
+          color: #ff5365;
+          line-height: 26rpx;
+        }
+
+        .total-amount {
+          font-weight: 600;
+          font-size: 40rpx;
+          color: #ff5365;
+          line-height: 40rpx;
+        }
+      }
+    }
+
+    .header-divider {
+      width: 100%;
+      height: 1rpx;
+      background: #eaeaea;
+      margin-top: 32rpx;
+      margin-bottom: 32rpx;
+    }
+
+    .records-list {
+      .record-item {
+        padding: 0;
+
+        .info-row {
+          display: flex;
+          justify-content: space-between;
+          margin-bottom: 30rpx;
+
+          .info-label {
+            font-weight: 400;
+            font-size: 30rpx;
+            color: #666666;
+            line-height: 30rpx;
+          }
+
+          .info-value {
+            font-weight: 400;
+            font-size: 30rpx;
+            color: #333333;
+            line-height: 30rpx;
+
+            &.highlight {
+              color: #ff5365;
+            }
+          }
+        }
+
+        .record-divider {
+          width: 100%;
+          height: 1rpx;
+          background: #eaeaea;
+          margin-top: 32rpx;
+          margin-bottom: 32rpx;
+        }
+      }
+    }
+  }
+}
+</style>

+ 255 - 0
src/pages-C/income/shareInfo.vue

@@ -0,0 +1,255 @@
+<script setup lang="ts">
+import { ref } from "vue";
+import { safeAreaInsets, menuButtonInfo } from "@/utils";
+import CustomNavigationBar from "@/components/CustomNavigationBar.vue";
+
+definePage({
+  style: {
+    navigationBarTitleText: "分享奖励",
+    navigationStyle: "custom",
+  },
+});
+
+const navigationBarHeight = ref(0);
+
+function getNavigationBarHeight() {
+  const menuButtonHeight = menuButtonInfo?.height || 32;
+  const statusBarHeight = safeAreaInsets?.top || 0;
+  navigationBarHeight.value = statusBarHeight + menuButtonHeight + 12;
+}
+
+onMounted(() => {
+  getNavigationBarHeight();
+});
+
+const totalIncome = ref("38.00");
+
+const shareList = ref([
+  {
+    id: 1,
+    couponName: "某某某某某优惠券",
+    income: "2.00",
+    receiveTime: "2026-05-18 19:00:00",
+    receiver: "林雨欣",
+  },
+  {
+    id: 2,
+    couponName: "某某某某某优惠券",
+    income: "17.00",
+    receiveTime: "2026-05-18 19:00:00",
+    receiver: "吴雨桐",
+  },
+  {
+    id: 3,
+    couponName: "某某某某某优惠券",
+    income: "18.00",
+    receiveTime: "2026-05-18 19:00:00",
+    receiver: "周昭宁",
+  },
+  {
+    id: 4,
+    couponName: "某某某某某优惠券",
+    income: "8.00",
+    receiveTime: "2026-05-18 19:00:00",
+    receiver: "周宇轩",
+  },
+]);
+
+function handleViewDetail(item) {
+  uni.showToast({
+    title: `查看 ${item.couponName} 详情`,
+    icon: "none",
+  });
+}
+</script>
+
+<template>
+  <view class="share-info-container">
+    <u-navbar title="分享奖励" :autoBack="true" :placeholder="true" :border-bottom="false"
+      :title-style="{ fontWeight: 'bold',color:'#000' }" leftIconColor='#000'></u-navbar>
+
+    <view class="share-info-content">
+      <view class="share-card">
+        <view class="card-header">
+          <view class="title-section">
+            <view class="title-line"></view>
+            <text class="title-text">分享奖励</text>
+          </view>
+          <view class="total-income">
+            <text class="total-label">总收益</text>
+            <view>
+              <text class="total-symbol">¥</text>
+              <text class="total-amount">{{ totalIncome }}</text>
+            </view>
+          </view>
+        </view>
+
+        <view class="records-list">
+          <view v-for="(item, index) in shareList" :key="item.id" class="record-item">
+            <view class="record-top">
+              <text class="coupon-name">{{ item.couponName }}</text>
+              <view class="income-amount">
+                <text class="income-label">收益</text>
+                <text class="income-value">¥{{ item.income }}</text>
+              </view>
+            </view>
+
+            <view class="record-bottom">
+              <view class="info-row">
+                <text class="info-label">领取人</text>
+                <text class="info-value">{{ item.receiver }}</text>
+              </view>
+              <view class="info-row">
+                <text class="info-label">领取时间</text>
+                <text class="info-value">{{ item.receiveTime }}</text>
+              </view>
+            </view>
+
+            <view v-if="index < shareList.length - 1" class="record-divider"></view>
+          </view>
+        </view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<style lang="scss" scoped>
+.share-info-container {
+  min-height: 100vh;
+  background: #f8f8fa;
+
+  .share-info-content {
+    padding: 32rpx;
+  }
+
+  .share-card {
+    background: #ffffff;
+    border-radius: 32rpx;
+    padding: 32rpx;
+
+    .card-header {
+      display: flex;
+      justify-content: space-between;
+      align-items: flex-start;
+      padding-bottom: 32rpx;
+      border-bottom: 1rpx solid #eaeaea;
+
+      .title-section {
+        display: flex;
+        align-items: center;
+        gap: 18rpx;
+
+        .title-line {
+          width: 6rpx;
+          height: 32rpx;
+          background: #ff5365;
+        }
+
+        .title-text {
+          font-weight: 600;
+          font-size: 32rpx;
+          color: #333333;
+          line-height: 32rpx;
+        }
+      }
+
+      .total-income {
+        display: flex;
+        align-items: baseline;
+        gap: 8rpx;
+
+        .total-label {
+          font-weight: 400;
+          font-size: 26rpx;
+          color: #2c2c2c;
+          line-height: 26rpx;
+          margin-right: 4rpx;
+        }
+
+        .total-symbol {
+          font-weight: 600;
+          font-size: 26rpx;
+          color: #ff5365;
+          line-height: 26rpx;
+        }
+
+        .total-amount {
+          font-weight: 600;
+          font-size: 40rpx;
+          color: #ff5365;
+          line-height: 40rpx;
+        }
+      }
+    }
+
+    .records-list {
+      .record-item {
+        padding: 32rpx 0;
+
+        .record-top {
+          display: flex;
+          justify-content: space-between;
+          align-items: center;
+          margin-bottom: 40rpx;
+
+          .coupon-name {
+            font-weight: 500;
+            font-size: 28rpx;
+            color: #2c2c2c;
+            line-height: 28rpx;
+          }
+
+          .income-amount {
+            display: flex;
+            align-items: center;
+            gap: 12rpx;
+
+            .income-label {
+              font-weight: 400;
+              font-size: 30rpx;
+              color: #333333;
+              line-height: 30rpx;
+            }
+
+            .income-value {
+              font-weight: 500;
+              font-size: 30rpx;
+              color: #ff5365;
+              line-height: 30rpx;
+            }
+          }
+        }
+
+        .record-bottom {
+          .info-row {
+            display: flex;
+            justify-content: space-between;
+            margin-bottom: 30rpx;
+
+            .info-label {
+              font-weight: 400;
+              font-size: 30rpx;
+              color: #666666;
+              line-height: 30rpx;
+            }
+
+            .info-value {
+              font-weight: 400;
+              font-size: 30rpx;
+              color: #333333;
+              line-height: 30rpx;
+            }
+          }
+        }
+
+        .record-divider {
+          width: 100%;
+          height: 1rpx;
+          background: #eaeaea;
+          margin-top: 32rpx;
+        }
+      }
+    }
+  }
+}
+</style>

+ 212 - 0
src/pages-C/income/writeOff.vue

@@ -0,0 +1,212 @@
+<script setup lang="ts">
+import { ref } from "vue";
+
+definePage({
+  style: {
+    navigationBarTitleText: "核销奖励",
+    navigationStyle: "custom",
+  },
+});
+
+const totalIncome = ref("38.00");
+
+const writeOffList = ref([
+  {
+    id: 1,
+    couponName: "精品洗车优惠券",
+    writeOffTime: "2026-05-15 15:00:00",
+    user: "吴磊",
+    productName: "精品洗车",
+    storeName: "京东养车",
+    payAmount: "48.00",
+    commission: "48.00",
+  },
+  {
+    id: 2,
+    couponName: "精品洗车优惠券",
+    writeOffTime: "2026-05-15 15:00:00",
+    user: "吴磊",
+    productName: "精品洗车",
+    storeName: "京东养车",
+    payAmount: "48.00",
+    commission: "48.00",
+  },
+]);
+</script>
+
+<template>
+  <view class="write-off-container">
+    <u-navbar title="核销奖励" :autoBack="true" :placeholder="true" :border-bottom="false"
+      :title-style="{ fontWeight: 'bold', color: '#000' }" leftIconColor="#000"></u-navbar>
+
+    <view class="write-off-content">
+      <view class="write-off-card">
+        <view class="card-header">
+          <view class="title-section">
+            <view class="title-line"></view>
+            <text class="title-text">核销奖励</text>
+          </view>
+          <view class="total-income">
+            <text class="total-label">总收益</text>
+            <view>
+              <text class="total-symbol">¥</text>
+              <text class="total-amount">{{ totalIncome }}</text>
+            </view>
+          </view>
+        </view>
+
+        <view class="header-divider"></view>
+
+        <view class="records-list">
+          <view v-for="(item, index) in writeOffList" :key="item.id" class="record-item">
+            <view class="info-row">
+              <text class="info-label">优惠券名称</text>
+              <text class="info-value">{{ item.couponName }}</text>
+            </view>
+            <view class="info-row">
+              <text class="info-label">核销时间</text>
+              <text class="info-value">{{ item.writeOffTime }}</text>
+            </view>
+            <view class="info-row">
+              <text class="info-label">核销用户</text>
+              <text class="info-value">{{ item.user }}</text>
+            </view>
+            <view class="info-row">
+              <text class="info-label">商品名称</text>
+              <text class="info-value">{{ item.productName }}</text>
+            </view>
+            <view class="info-row">
+              <text class="info-label">核销商家</text>
+              <text class="info-value">{{ item.storeName }}</text>
+            </view>
+            <view class="info-row">
+              <text class="info-label">实付金额</text>
+              <text class="info-value highlight">¥{{ item.payAmount }}</text>
+            </view>
+            <view class="info-row">
+              <text class="info-label">获得佣金</text>
+              <text class="info-value highlight">¥{{ item.commission }}</text>
+            </view>
+
+            <view v-if="index < writeOffList.length - 1" class="record-divider"></view>
+          </view>
+        </view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<style lang="scss" scoped>
+.write-off-container {
+  min-height: 100vh;
+  background: #f8f8fa;
+
+  .write-off-content {
+    padding: 32rpx;
+  }
+
+  .write-off-card {
+    background: #ffffff;
+    border-radius: 32rpx;
+    padding: 32rpx;
+
+    .card-header {
+      display: flex;
+      justify-content: space-between;
+      align-items: flex-start;
+
+      .title-section {
+        display: flex;
+        align-items: center;
+        gap: 18rpx;
+
+        .title-line {
+          width: 6rpx;
+          height: 32rpx;
+          background: #ff5365;
+        }
+
+        .title-text {
+          font-weight: 600;
+          font-size: 32rpx;
+          color: #333333;
+          line-height: 32rpx;
+        }
+      }
+
+      .total-income {
+        display: flex;
+        align-items: baseline;
+        gap: 8rpx;
+
+        .total-label {
+          font-weight: 400;
+          font-size: 26rpx;
+          color: #2c2c2c;
+          line-height: 26rpx;
+        }
+
+        .total-symbol {
+          font-weight: 600;
+          font-size: 26rpx;
+          color: #ff5365;
+          line-height: 26rpx;
+        }
+
+        .total-amount {
+          font-weight: 600;
+          font-size: 40rpx;
+          color: #ff5365;
+          line-height: 40rpx;
+        }
+      }
+    }
+
+    .header-divider {
+      width: 100%;
+      height: 1rpx;
+      background: #eaeaea;
+      margin-top: 32rpx;
+      margin-bottom: 32rpx;
+    }
+
+    .records-list {
+      .record-item {
+        padding: 0;
+
+        .info-row {
+          display: flex;
+          justify-content: space-between;
+          margin-bottom: 30rpx;
+
+          .info-label {
+            font-weight: 400;
+            font-size: 30rpx;
+            color: #666666;
+            line-height: 30rpx;
+          }
+
+          .info-value {
+            font-weight: 400;
+            font-size: 30rpx;
+            color: #333333;
+            line-height: 30rpx;
+
+            &.highlight {
+              color: #ff5365;
+            }
+          }
+        }
+
+        .record-divider {
+          width: 100%;
+          height: 1rpx;
+          background: #eaeaea;
+          margin-top: 32rpx;
+          margin-bottom: 32rpx;
+        }
+      }
+    }
+  }
+}
+</style>

+ 318 - 0
src/pages-C/my/myCumulativeGroup.vue

@@ -0,0 +1,318 @@
+<script setup lang="ts">
+import { ref } from "vue";
+
+definePage({
+  style: {
+    navigationBarTitleText: "累计团员",
+    navigationStyle: "custom",
+  },
+});
+
+const cumulativeData = ref([
+  {
+    id: 1,
+    type: "领取人",
+    amount: "37.92",
+    userName: "林雨欣",
+    inviteDate: "2026-05-18",
+  },
+  {
+    id: 2,
+    type: "领取人",
+    amount: "45.00",
+    userName: "张三",
+    inviteDate: "2026-05-19",
+  },
+  {
+    id: 3,
+    type: "领取人",
+    amount: "28.50",
+    userName: "李四",
+    inviteDate: "2026-05-20",
+  },
+]);
+
+// 定义tab列表
+const tabList = ref([
+  { label: "全部", value: "0" },
+  { label: "直邀", value: "1" },
+  { label: "间邀", value: "2" },
+]);
+
+// 当前选中的tab
+const currentTab = ref("0");
+
+// 点击tab事件
+const handleTabClick = (value) => {
+  currentTab.value = value;
+  console.log("选中tab:", value);
+};
+
+const keyword = ref("");
+async function search() {}
+async function clear() {}
+</script>
+
+<template>
+  <view class="income-cumulative-container">
+    <u-navbar title="累计团员" :autoBack="true" :placeholder="true" :border-bottom="false"
+      :title-style="{ fontWeight: 'bold', color: '#000' }" leftIconColor="#000"></u-navbar>
+
+    <view class="invite-content-search">
+      <u-search :showAction="true" actionText="搜索" bgColor="#F7F8FA" v-model="keyword" color='#333' placeholder='搜索用户名称'
+        :actionStyle='{ color: "#FF5365", fontSize: "32rpx" }'></u-search>
+    </view>
+
+    <view class="stats-card">
+      <view class="stat-item">
+        <text class="stat-label">团员总数</text>
+        <text class="stat-value">24</text>
+      </view>
+      <view class="stat-divider"></view>
+      <view class="stat-item">
+        <text class="stat-label">直邀人数</text>
+        <text class="stat-value">24</text>
+      </view>
+      <view class="stat-divider"></view>
+      <view class="stat-item">
+        <text class="stat-label">间邀人数</text>
+        <text class="stat-value">24</text>
+      </view>
+    </view>
+
+    <view class="filter-bar">
+      <view class="filter-tabs">
+        <view v-for="(item, index) in tabList" :key="index" class="filter-tab"
+          :class="{ active: currentTab === item.value }" @click="handleTabClick(item.value)">
+          <text class="tab-text">{{ item.label }}</text>
+        </view>
+      </view>
+    </view>
+
+    <view class="cumulative-content">
+      <view class="cumulative-list">
+        <view class="cumulative-item" v-for="item in cumulativeData" :key="item.id">
+          <view class="item-header">
+            <text class="item-type">{{ item.type }}</text>
+            <view class='item-type-name blue'>直接邀请</view>
+            <view class='item-type-name purple'>间接邀请</view>
+            <view class='item-type-name green'>二级团员</view>
+          </view>
+          <view class="divider"></view>
+          <view class="item-info">
+            <view class="info-row">
+              <text class="info-label">邀请日期</text>
+              <text class="info-value">{{ item.inviteDate }}</text>
+            </view>
+          </view>
+        </view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<style lang="scss" scoped>
+.income-cumulative-container {
+  min-height: 100vh;
+  background: #f8f8fa;
+
+  ::v-deep.u-search__content {
+    border-radius: 40rpx;
+    background-color: #f7f8fa;
+  }
+
+  .invite-content-search {
+    background: #fff;
+    padding: 0 32rpx 16rpx 32rpx;
+  }
+
+  .stats-card {
+    width: 100%;
+    height: 150rpx;
+    background: #ffffff;
+    display: flex;
+    justify-content: space-around;
+    align-items: center;
+    padding: 0 40rpx;
+    box-sizing: border-box;
+
+    border-top: 1rpx solid #eaeaea;
+    border-bottom: 1rpx solid #eaeaea;
+    .stat-item {
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      gap: 8rpx;
+
+      .stat-label {
+        font-weight: 400;
+        font-size: 24rpx;
+        color: #86909c;
+        line-height: 1;
+      }
+
+      .stat-value {
+        font-family: Roboto;
+        font-weight: 600;
+        font-size: 36rpx;
+        color: #333333;
+        line-height: 1.1;
+      }
+    }
+
+    .stat-divider {
+      width: 2rpx;
+      height: 30rpx;
+      background: #d9d9d9;
+    }
+  }
+
+  .filter-bar {
+    width: 100%;
+    height: 92rpx;
+    background: #ffffff;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding: 0 32rpx;
+    box-sizing: border-box;
+
+    .filter-tabs {
+      display: flex;
+      gap: 24rpx;
+
+      .filter-tab {
+        padding: 4rpx 30rpx;
+        border-radius: 32rpx;
+        background: #f7f8fa;
+        display: flex;
+        height: 44rpx;
+        line-height: 44rpx;
+
+        &.active {
+          background: #ff5365;
+
+          .tab-text {
+            color: #ffffff;
+          }
+        }
+
+        .tab-text {
+          font-weight: 400;
+          font-size: 26rpx;
+          color: #333333;
+        }
+      }
+    }
+  }
+
+  .cumulative-content {
+    width: 100%;
+    padding: 32rpx;
+    box-sizing: border-box;
+  }
+
+  .cumulative-list {
+    display: flex;
+    flex-direction: column;
+    gap: 32rpx;
+  }
+
+  .cumulative-item {
+    background: #ffffff;
+    border-radius: 32rpx;
+    padding: 46rpx 32rpx;
+
+    .item-header {
+      display: flex;
+      align-items: center;
+      margin-bottom: 32rpx;
+
+      .item-type {
+        font-weight: 600;
+        font-size: 32rpx;
+        color: #2c2c2c;
+        line-height: 1;
+      }
+
+      .item-type-name {
+        padding: 6rpx 16rpx;
+
+        font-size: 24rpx;
+        border-radius: 4rpx;
+        margin-left: 14rpx;
+      }
+      .blue {
+        background: #e8f3ff;
+        color: #165dff;
+      }
+      .purple {
+        color: #722ed1;
+        background: #e8f3ff;
+      }
+      .green {
+        color: #00b42a;
+        background: #e8ffea;
+      }
+
+      .item-amount {
+        display: flex;
+        align-items: baseline;
+        gap: 8rpx;
+
+        .amount-plus {
+          font-size: 26rpx;
+          color: #2c2c2c;
+          line-height: 1;
+        }
+
+        .amount-value {
+          font-weight: 600;
+          font-size: 42rpx;
+          color: #ff5365;
+          line-height: 1;
+        }
+      }
+
+      .total-unit {
+        font-weight: 400;
+        font-size: 26rpx;
+        color: #ff5365;
+      }
+    }
+
+    .divider {
+      width: 622rpx;
+      height: 2rpx;
+      background: #eaeaea;
+      margin-bottom: 32rpx;
+    }
+
+    .item-info {
+      display: flex;
+      flex-direction: column;
+      gap: 32rpx;
+
+      .info-row {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+
+        .info-label {
+          font-weight: 400;
+          font-size: 30rpx;
+          color: #86909c;
+          line-height: 1;
+        }
+
+        .info-value {
+          font-weight: 400;
+          font-size: 30rpx;
+          color: #333333;
+          line-height: 1;
+        }
+      }
+    }
+  }
+}
+</style>

+ 258 - 0
src/pages-C/my/myCumulativeInvitation.vue

@@ -0,0 +1,258 @@
+<script setup lang="ts">
+import { ref } from "vue";
+
+definePage({
+  style: {
+    navigationBarTitleText: "累计邀请",
+    navigationStyle: "custom",
+  },
+});
+
+const cumulativeData = ref([
+  {
+    id: 1,
+    type: "领取人",
+    amount: "37.92",
+    userName: "林雨欣",
+    inviteDate: "2026-05-18",
+  },
+  {
+    id: 2,
+    type: "领取人",
+    amount: "45.00",
+    userName: "张三",
+    inviteDate: "2026-05-19",
+  },
+  {
+    id: 3,
+    type: "领取人",
+    amount: "28.50",
+    userName: "李四",
+    inviteDate: "2026-05-20",
+  },
+]);
+
+// 定义tab列表
+const tabList = ref([
+  { label: "今天", value: "0" },
+  { label: "近7天", value: "1" },
+  { label: "近30天", value: "2" },
+]);
+
+// 当前选中的tab
+const currentTab = ref("0");
+
+// 点击tab事件
+const handleTabClick = (value) => {
+  currentTab.value = value;
+  console.log("选中tab:", value);
+};
+</script>
+
+<template>
+  <view class="income-cumulative-container">
+    <u-navbar title="累计邀请" :autoBack="true" :placeholder="true" :border-bottom="false"
+      :title-style="{ fontWeight: 'bold', color: '#000' }" leftIconColor="#000"></u-navbar>
+
+    <view class="filter-bar">
+      <view class="filter-tabs">
+        <view v-for="(item, index) in tabList" :key="index" class="filter-tab"
+          :class="{ active: currentTab === item.value }" @click="handleTabClick(item.value)">
+          <text class="tab-text">{{ item.label }}</text>
+        </view>
+      </view>
+      <view class="total-amount">
+        <text class="total-label">合计</text>
+        <text class="total-value">340</text>
+        <text class="total-unit">元</text>
+      </view>
+    </view>
+
+    <view class="cumulative-content">
+      <view class="cumulative-list">
+        <view class="cumulative-item" v-for="item in cumulativeData" :key="item.id">
+          <view class="item-header">
+            <text class="item-type">{{ item.type }}</text>
+            <view class='item-type-name'>直接邀请</view>
+          </view>
+          <view class="divider"></view>
+          <view class="item-info">
+            <view class="info-row">
+              <text class="info-label">用户名称</text>
+              <text class="info-value">{{ item.userName }}</text>
+            </view>
+            <view class="info-row">
+              <text class="info-label">邀请日期</text>
+              <text class="info-value">{{ item.inviteDate }}</text>
+            </view>
+          </view>
+        </view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<style lang="scss" scoped>
+.income-cumulative-container {
+  min-height: 100vh;
+  background: #f8f8fa;
+
+  .filter-bar {
+    width: 100%;
+    height: 92rpx;
+    background: #ffffff;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding: 0 32rpx;
+    box-sizing: border-box;
+
+    .filter-tabs {
+      display: flex;
+      gap: 24rpx;
+
+      .filter-tab {
+        padding: 4rpx 30rpx;
+        border-radius: 32rpx;
+        background: #f7f8fa;
+        display: flex;
+        height: 44rpx;
+        line-height: 44rpx;
+        &.active {
+          background: #ff5365;
+
+          .tab-text {
+            color: #ffffff;
+          }
+        }
+
+        .tab-text {
+          font-weight: 400;
+          font-size: 26rpx;
+          color: #333333;
+        }
+      }
+    }
+
+    .total-amount {
+      display: flex;
+      align-items: baseline;
+      gap: 8rpx;
+
+      .total-label {
+        font-weight: 400;
+        font-size: 26rpx;
+        color: #2c2c2c;
+      }
+
+      .total-value {
+        font-weight: 600;
+        font-size: 42rpx;
+        color: #ff5365;
+      }
+
+      .total-unit {
+        font-weight: 400;
+        font-size: 26rpx;
+        color: #ff5365;
+      }
+    }
+  }
+
+  .cumulative-content {
+    width: 100%;
+    padding: 32rpx;
+    box-sizing: border-box;
+  }
+
+  .cumulative-list {
+    display: flex;
+    flex-direction: column;
+    gap: 32rpx;
+  }
+
+  .cumulative-item {
+    background: #ffffff;
+    border-radius: 32rpx;
+    padding: 46rpx 32rpx;
+
+    .item-header {
+      display: flex;
+      align-items: center;
+      margin-bottom: 32rpx;
+
+      .item-type {
+        font-weight: 600;
+        font-size: 32rpx;
+        color: #2c2c2c;
+        line-height: 1;
+      }
+      .item-type-name {
+        padding: 6rpx 16rpx;
+        background: #e8f3ff;
+        color: #165dff;
+        font-size: 24rpx;
+        border-radius: 4rpx;
+        margin-left: 14rpx;
+      }
+
+      .item-amount {
+        display: flex;
+        align-items: baseline;
+        gap: 8rpx;
+
+        .amount-plus {
+          font-size: 26rpx;
+          color: #2c2c2c;
+          line-height: 1;
+        }
+
+        .amount-value {
+          font-weight: 600;
+          font-size: 42rpx;
+          color: #ff5365;
+          line-height: 1;
+        }
+      }
+      .total-unit {
+        font-weight: 400;
+        font-size: 26rpx;
+        color: #ff5365;
+      }
+    }
+
+    .divider {
+      width: 622rpx;
+      height: 2rpx;
+      background: #eaeaea;
+      margin-bottom: 32rpx;
+    }
+
+    .item-info {
+      display: flex;
+      flex-direction: column;
+      gap: 32rpx;
+
+      .info-row {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+
+        .info-label {
+          font-weight: 400;
+          font-size: 30rpx;
+          color: #86909c;
+          line-height: 1;
+        }
+
+        .info-value {
+          font-weight: 400;
+          font-size: 30rpx;
+          color: #333333;
+          line-height: 1;
+        }
+      }
+    }
+  }
+}
+</style>

+ 258 - 0
src/pages-C/my/myTodayInvitation.vue

@@ -0,0 +1,258 @@
+<script setup lang="ts">
+import { ref } from "vue";
+
+definePage({
+  style: {
+    navigationBarTitleText: "今日邀请",
+    navigationStyle: "custom",
+  },
+});
+
+const cumulativeData = ref([
+  {
+    id: 1,
+    type: "领取人",
+    amount: "37.92",
+    userName: "林雨欣",
+    inviteDate: "2026-05-18",
+  },
+  {
+    id: 2,
+    type: "领取人",
+    amount: "45.00",
+    userName: "张三",
+    inviteDate: "2026-05-19",
+  },
+  {
+    id: 3,
+    type: "领取人",
+    amount: "28.50",
+    userName: "李四",
+    inviteDate: "2026-05-20",
+  },
+]);
+
+// 定义tab列表
+const tabList = ref([
+  { label: "今天", value: "0" },
+  { label: "近7天", value: "1" },
+  { label: "近30天", value: "2" },
+]);
+
+// 当前选中的tab
+const currentTab = ref("0");
+
+// 点击tab事件
+const handleTabClick = (value) => {
+  currentTab.value = value;
+  console.log("选中tab:", value);
+};
+</script>
+
+<template>
+  <view class="income-cumulative-container">
+    <u-navbar title="今日邀请" :autoBack="true" :placeholder="true" :border-bottom="false"
+      :title-style="{ fontWeight: 'bold', color: '#000' }" leftIconColor="#000"></u-navbar>
+
+    <view class="filter-bar">
+      <view class="filter-tabs">
+        <view v-for="(item, index) in tabList" :key="index" class="filter-tab"
+          :class="{ active: currentTab === item.value }" @click="handleTabClick(item.value)">
+          <text class="tab-text">{{ item.label }}</text>
+        </view>
+      </view>
+      <view class="total-amount">
+        <text class="total-label">合计</text>
+        <text class="total-value">340</text>
+        <text class="total-unit">元</text>
+      </view>
+    </view>
+
+    <view class="cumulative-content">
+      <view class="cumulative-list">
+        <view class="cumulative-item" v-for="item in cumulativeData" :key="item.id">
+          <view class="item-header">
+            <text class="item-type">{{ item.type }}</text>
+            <view class='item-type-name'>直接邀请</view>
+          </view>
+          <view class="divider"></view>
+          <view class="item-info">
+            <view class="info-row">
+              <text class="info-label">用户名称</text>
+              <text class="info-value">{{ item.userName }}</text>
+            </view>
+            <view class="info-row">
+              <text class="info-label">邀请日期</text>
+              <text class="info-value">{{ item.inviteDate }}</text>
+            </view>
+          </view>
+        </view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<style lang="scss" scoped>
+.income-cumulative-container {
+  min-height: 100vh;
+  background: #f8f8fa;
+
+  .filter-bar {
+    width: 100%;
+    height: 92rpx;
+    background: #ffffff;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding: 0 32rpx;
+    box-sizing: border-box;
+
+    .filter-tabs {
+      display: flex;
+      gap: 24rpx;
+
+      .filter-tab {
+        padding: 4rpx 30rpx;
+        border-radius: 32rpx;
+        background: #f7f8fa;
+        display: flex;
+        height: 44rpx;
+        line-height: 44rpx;
+        &.active {
+          background: #ff5365;
+
+          .tab-text {
+            color: #ffffff;
+          }
+        }
+
+        .tab-text {
+          font-weight: 400;
+          font-size: 26rpx;
+          color: #333333;
+        }
+      }
+    }
+
+    .total-amount {
+      display: flex;
+      align-items: baseline;
+      gap: 8rpx;
+
+      .total-label {
+        font-weight: 400;
+        font-size: 26rpx;
+        color: #2c2c2c;
+      }
+
+      .total-value {
+        font-weight: 600;
+        font-size: 42rpx;
+        color: #ff5365;
+      }
+
+      .total-unit {
+        font-weight: 400;
+        font-size: 26rpx;
+        color: #ff5365;
+      }
+    }
+  }
+
+  .cumulative-content {
+    width: 100%;
+    padding: 32rpx;
+    box-sizing: border-box;
+  }
+
+  .cumulative-list {
+    display: flex;
+    flex-direction: column;
+    gap: 32rpx;
+  }
+
+  .cumulative-item {
+    background: #ffffff;
+    border-radius: 32rpx;
+    padding: 46rpx 32rpx;
+
+    .item-header {
+      display: flex;
+      align-items: center;
+      margin-bottom: 32rpx;
+
+      .item-type {
+        font-weight: 600;
+        font-size: 32rpx;
+        color: #2c2c2c;
+        line-height: 1;
+      }
+      .item-type-name {
+        padding: 6rpx 16rpx;
+        background: #e8f3ff;
+        color: #165dff;
+        font-size: 24rpx;
+        border-radius: 4rpx;
+        margin-left: 14rpx;
+      }
+
+      .item-amount {
+        display: flex;
+        align-items: baseline;
+        gap: 8rpx;
+
+        .amount-plus {
+          font-size: 26rpx;
+          color: #2c2c2c;
+          line-height: 1;
+        }
+
+        .amount-value {
+          font-weight: 600;
+          font-size: 42rpx;
+          color: #ff5365;
+          line-height: 1;
+        }
+      }
+      .total-unit {
+        font-weight: 400;
+        font-size: 26rpx;
+        color: #ff5365;
+      }
+    }
+
+    .divider {
+      width: 622rpx;
+      height: 2rpx;
+      background: #eaeaea;
+      margin-bottom: 32rpx;
+    }
+
+    .item-info {
+      display: flex;
+      flex-direction: column;
+      gap: 32rpx;
+
+      .info-row {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+
+        .info-label {
+          font-weight: 400;
+          font-size: 30rpx;
+          color: #86909c;
+          line-height: 1;
+        }
+
+        .info-value {
+          font-weight: 400;
+          font-size: 30rpx;
+          color: #333333;
+          line-height: 1;
+        }
+      }
+    }
+  }
+}
+</style>

+ 201 - 0
src/pages-C/my/sendCoupon.vue

@@ -0,0 +1,201 @@
+<script setup lang="ts">
+import { ref } from "vue";
+import { getImageUrl } from "@/utils/imageUtil";
+
+import PopupLayer from "@/components/popupLayer/index.vue";
+
+definePage({
+  style: {
+    navigationBarTitleText: "累计发券",
+    navigationStyle: "custom",
+  },
+});
+
+// 定义tab列表
+const tabList = ref([
+  { label: "今天", value: "0" },
+  { label: "近7天", value: "1" },
+  { label: "近30天", value: "2" },
+]);
+
+// 当前选中的tab
+const currentTab = ref("0");
+
+// 点击tab事件
+const handleTabClick = (value) => {
+  currentTab.value = value;
+  console.log("选中tab:", value);
+};
+
+const keyword = ref("");
+async function search() {}
+async function clear() {}
+
+const isOpen = ref(false);
+const popupRef = ref();
+function clickSelect() {
+  isOpen.value = !isOpen.value;
+  isOpen.value ? popupRef.value?.show() : popupRef.value?.close();
+}
+
+const rewardTabs = ref([
+  { label: "全部", type: "0" },
+  { label: "折扣券", type: "1" },
+  { label: "兑换券", type: "2" },
+  { label: "满减券", type: "3" },
+]);
+
+const classIndex = ref(-1);
+const classItem = ref({
+  label: "",
+  type: "",
+});
+function classClick(index, item) {
+  classIndex.value = index;
+  classItem.value = item;
+  clickSelect();
+}
+</script>
+
+<template>
+  <view class="income-cumulative-container">
+    <u-navbar title="累计发券" :autoBack="true" :placeholder="true" :border-bottom="false"
+      :title-style="{ fontWeight: 'bold', color: '#000' }" leftIconColor="#000"></u-navbar>
+
+    <view class="invite-content-search">
+      <u-search :showAction="true" actionText="搜索" bgColor="#F7F8FA" v-model="keyword" color='#333' placeholder='搜索券名称'
+        :actionStyle='{ color: "#FF5365", fontSize: "32rpx" }'></u-search>
+    </view>
+
+    <view class="filter-box">
+      <view class='income-select' @click='clickSelect()'>
+        <view class='income-time'>
+          <view class='income-time-name'>{{classItem.label?classItem.label:'全部类型'}} </view>
+
+          <u--image class='income-down' :src="getImageUrl('@img/income/down.png')" mode="aspectFit" width="18rpx"
+            height="18rpx">
+          </u--image>
+        </view>
+      </view>
+      <view class="stat-divider"></view>
+      <view class="filter-bar">
+        <view class="filter-tabs">
+          <view v-for="(item, index) in tabList" :key="index" class="filter-tab"
+            :class="{ active: currentTab === item.value }" @click="handleTabClick(item.value)">
+            <text class="tab-text">{{ item.label }}</text>
+          </view>
+        </view>
+      </view>
+    </view>
+
+    <PopupLayer direction="bottom" ref='popupRef'>
+      <view class="classification-cont">
+        <view class='classification-list' v-for='(item,index) in rewardTabs' :key='index'
+          @click='classClick(index,item)'>
+          <view class='classification-label'>{{ item.label }}</view>
+          <u--image class='selected-icon' v-show='classIndex==index' :src="getImageUrl('@img/income/selected.png')"
+            mode="aspectFit" width="32rpx" height="32rpx">
+          </u--image>
+        </view>
+      </view>
+
+    </PopupLayer>
+
+  </view>
+
+</template>
+
+<style lang="scss" scoped>
+.income-cumulative-container {
+  min-height: 100vh;
+  background: #f8f8fa;
+
+  ::v-deep.u-search__content {
+    border-radius: 40rpx;
+    background-color: #f7f8fa;
+  }
+
+  .invite-content-search {
+    background: #fff;
+    padding: 0 32rpx 16rpx 32rpx;
+  }
+  .filter-box {
+    display: flex;
+    align-items: center;
+    background: #fff;
+    height: 92rpx;
+    padding: 0 32rpx;
+    box-sizing: border-box;
+    .stat-divider {
+      width: 2rpx;
+      height: 40rpx;
+      background: #d9d9d9;
+      margin: 0 32rpx;
+    }
+    .filter-bar {
+      background: #ffffff;
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      box-sizing: border-box;
+
+      .filter-tabs {
+        display: flex;
+        gap: 24rpx;
+
+        .filter-tab {
+          padding: 4rpx 30rpx;
+          border-radius: 32rpx;
+          background: #f7f8fa;
+          display: flex;
+          height: 44rpx;
+          line-height: 44rpx;
+
+          &.active {
+            background: #ff5365;
+
+            .tab-text {
+              color: #ffffff;
+            }
+          }
+
+          .tab-text {
+            font-weight: 400;
+            font-size: 26rpx;
+            color: #333333;
+          }
+        }
+      }
+    }
+    .income-select {
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      .income-time-name {
+        font-size: 26rpx;
+        color: #333;
+        margin-right: 8rpx;
+      }
+      .income-time {
+        display: flex;
+      }
+      .income-down {
+        margin-top: -4rpx;
+      }
+    }
+  }
+  .classification-cont {
+    padding: 0 32rpx;
+    box-sizing: border-box;
+    .classification-list {
+      display: flex;
+      justify-content: space-between;
+    }
+    .classification-label {
+      color: #2c2c2c;
+      font-size: 26rpx;
+      padding-bottom: 32rpx;
+    }
+  }
+}
+</style>

+ 250 - 0
src/pages-C/my/sendRecipientsNumber.vue

@@ -0,0 +1,250 @@
+<script setup lang="ts">
+import { ref } from "vue";
+
+definePage({
+  style: {
+    navigationBarTitleText: "累计领取人数",
+    navigationStyle: "custom",
+  },
+});
+
+const cumulativeData = ref([
+  {
+    id: 1,
+    type: "领取人",
+    amount: "37.92",
+    userName: "林雨欣",
+    inviteDate: "2026-05-18",
+  },
+  {
+    id: 2,
+    type: "领取人",
+    amount: "45.00",
+    userName: "张三",
+    inviteDate: "2026-05-19",
+  },
+  {
+    id: 3,
+    type: "领取人",
+    amount: "28.50",
+    userName: "李四",
+    inviteDate: "2026-05-20",
+  },
+]);
+
+// 定义tab列表
+const tabList = ref([
+  { label: "今天", value: "0" },
+  { label: "近7天", value: "1" },
+  { label: "近30天", value: "2" },
+]);
+
+// 当前选中的tab
+const currentTab = ref("0");
+
+// 点击tab事件
+const handleTabClick = (value) => {
+  currentTab.value = value;
+  console.log("选中tab:", value);
+};
+</script>
+
+<template>
+  <view class="income-cumulative-container">
+    <u-navbar title="累计领取人数" :autoBack="true" :placeholder="true" :border-bottom="false"
+      :title-style="{ fontWeight: 'bold', color: '#000' }" leftIconColor="#000"></u-navbar>
+
+    <view class="filter-bar">
+      <view class="filter-tabs">
+        <view v-for="(item, index) in tabList" :key="index" class="filter-tab"
+          :class="{ active: currentTab === item.value }" @click="handleTabClick(item.value)">
+          <text class="tab-text">{{ item.label }}</text>
+        </view>
+      </view>
+      <view class="total-amount">
+        <text class="total-label">合计</text>
+        <text class="total-value">340</text>
+        <text class="total-unit">元</text>
+      </view>
+    </view>
+
+    <view class="cumulative-content">
+      <view class="cumulative-list">
+        <view class="cumulative-item" v-for="item in cumulativeData" :key="item.id">
+          <view class="item-header">
+            <text class="item-type">{{ item.type }}</text>
+          </view>
+          <view class="divider"></view>
+          <view class="item-info">
+            <view class="info-row">
+              <text class="info-label">用户名称</text>
+              <text class="info-value">{{ item.userName }}</text>
+            </view>
+            <view class="info-row">
+              <text class="info-label">邀请日期</text>
+              <text class="info-value">{{ item.inviteDate }}</text>
+            </view>
+          </view>
+        </view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<style lang="scss" scoped>
+.income-cumulative-container {
+  min-height: 100vh;
+  background: #f8f8fa;
+
+  .filter-bar {
+    width: 100%;
+    height: 92rpx;
+    background: #ffffff;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding: 0 32rpx;
+    box-sizing: border-box;
+
+    .filter-tabs {
+      display: flex;
+      gap: 24rpx;
+
+      .filter-tab {
+        padding: 4rpx 30rpx;
+        border-radius: 32rpx;
+        background: #f7f8fa;
+        display: flex;
+        height: 44rpx;
+        line-height: 44rpx;
+        &.active {
+          background: #ff5365;
+
+          .tab-text {
+            color: #ffffff;
+          }
+        }
+
+        .tab-text {
+          font-weight: 400;
+          font-size: 26rpx;
+          color: #333333;
+        }
+      }
+    }
+
+    .total-amount {
+      display: flex;
+      align-items: baseline;
+      gap: 8rpx;
+
+      .total-label {
+        font-weight: 400;
+        font-size: 26rpx;
+        color: #2c2c2c;
+      }
+
+      .total-value {
+        font-weight: 600;
+        font-size: 42rpx;
+        color: #ff5365;
+      }
+
+      .total-unit {
+        font-weight: 400;
+        font-size: 26rpx;
+        color: #ff5365;
+      }
+    }
+  }
+
+  .cumulative-content {
+    width: 100%;
+    padding: 32rpx;
+    box-sizing: border-box;
+  }
+
+  .cumulative-list {
+    display: flex;
+    flex-direction: column;
+    gap: 32rpx;
+  }
+
+  .cumulative-item {
+    background: #ffffff;
+    border-radius: 32rpx;
+    padding: 46rpx 32rpx;
+
+    .item-header {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      margin-bottom: 32rpx;
+
+      .item-type {
+        font-weight: 600;
+        font-size: 32rpx;
+        color: #2c2c2c;
+        line-height: 1;
+      }
+
+      .item-amount {
+        display: flex;
+        align-items: baseline;
+        gap: 8rpx;
+
+        .amount-plus {
+          font-size: 26rpx;
+          color: #2c2c2c;
+          line-height: 1;
+        }
+
+        .amount-value {
+          font-weight: 600;
+          font-size: 42rpx;
+          color: #ff5365;
+          line-height: 1;
+        }
+      }
+      .total-unit {
+        font-weight: 400;
+        font-size: 26rpx;
+        color: #ff5365;
+      }
+    }
+
+    .divider {
+      width: 622rpx;
+      height: 2rpx;
+      background: #eaeaea;
+      margin-bottom: 32rpx;
+    }
+
+    .item-info {
+      display: flex;
+      flex-direction: column;
+      gap: 32rpx;
+
+      .info-row {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+
+        .info-label {
+          font-weight: 400;
+          font-size: 30rpx;
+          color: #86909c;
+          line-height: 1;
+        }
+
+        .info-value {
+          font-weight: 400;
+          font-size: 30rpx;
+          color: #333333;
+          line-height: 1;
+        }
+      }
+    }
+  }
+}
+</style>

+ 481 - 0
src/pages-C/share/shareInfo.vue

@@ -0,0 +1,481 @@
+<script setup lang="ts">
+import { ref } from "vue";
+import { getImageUrl } from "@/utils/imageUtil";
+import Upopup from "@/components/popup/index.vue";
+
+definePage({
+  style: {
+    navigationBarTitleText: "分享信息",
+    navigationStyle: "custom",
+  },
+});
+
+const couponData = ref({
+  discount: "7",
+  unit: "折",
+  name: "顺溜早餐折扣券",
+  validity: "长期有效",
+});
+
+const couponDetails = ref({
+  applicableProducts: "部分商品",
+  merchantCount: "99+",
+  usageRules: "满20元打8折,最高优惠10元",
+  usagePeriod: "长期有效",
+  dailyLimit: "20张",
+  applicableArea: "山西省-太原市-小店区、山西省-太原市-迎泽区",
+});
+
+const rules = [
+  "本优惠券为平台正规电子券,支持线上领取、线下门店扫码核销使用。",
+  "所有优惠券均有使用时效,请在券面标注有效期内使用,逾期自动失效,不予补发、不退换。",
+  "每位用户按照平台规则限制领取数量,同一账号、同一设备、同一手机号视为同一用户,禁止恶意批量刷券。",
+  "优惠券不兑现、不找零、不可拆分使用,单笔订单仅可核销一张优惠券,特殊活动除外。",
+  "优惠券不可与门店其他特价活动、线下团购、会员特价叠加共用,具体以门店现场规则为准。",
+  "若发生订单退款、取消订单,已使用优惠券不予返还;未使用优惠券在有效期内自动退回券包。",
+  "严禁倒卖、转售、恶意套现优惠券,一经发现平台有权冻结券码并取消使用资格。",
+  "活动最终使用解释权归对应门店及发券平台所有。",
+];
+
+const storeShow = ref(false);
+const shopShow = ref(false);
+</script>
+
+<template>
+  <view class="share-info-container">
+    <u-navbar title="分享信息" :autoBack="true" :placeholder="true" :border-bottom="false"
+      :title-style="{ fontWeight: 'bold', color: '#000' }" leftIconColor="#000"></u-navbar>
+
+    <view class="share-info-content">
+      <view class="coupon-card">
+        <!-- 折扣券 -->
+        <!-- <view class="card-background" :style="{
+                    backgroundImage: `url(${getImageUrl('@img/income/share-bg.png')})`,
+                    backgroundRepeat: 'no-repeat',
+                    backgroundSize: 'contain',
+                    backgroundPosition: 'center'
+                }">
+
+          <view class="card-content">
+            <view class="discount-section">
+              <view class="discount-value">
+                <text class="discount-number">{{ couponData.discount }}</text>
+                <text class="discount-unit">{{ couponData.unit }}</text>
+              </view>
+            </view>
+
+            <view class="info-section">
+              <text class="coupon-name">{{ couponData.name }}</text>
+              <text class="validity-text">{{ couponData.validity }}</text>
+            </view>
+          </view>
+        </view> -->
+        <!-- 满减券 -->
+        <!-- <view class="card-background" :style="{
+                    backgroundImage: `url(${getImageUrl('@img/income/share-bg.png')})`,
+                    backgroundRepeat: 'no-repeat',
+                    backgroundSize: 'contain',
+                    backgroundPosition: 'center'
+                }">
+
+          <view class="card-content">
+            <view class="discount-section">
+              <view class="discount-value">
+                <text class="discount-number">{{ couponData.discount }}</text>
+                <text class="discount-unit">元</text>
+              </view>
+            </view>
+
+            <view class="info-section">
+              <text class="coupon-name">{{ couponData.name }}</text>
+              <view>
+                <view class='coupon-red'>满200减8元</view>
+                <view class="validity-text">{{ couponData.validity }}</view>
+              </view>
+            </view>
+          </view>
+        </view> -->
+        <!-- 兑换券 -->
+        <view class="card-background">
+
+          <view class="card-content change-content">
+            <view class="info-section">
+              <text class="coupon-name">{{ couponData.name }}</text>
+              <view>
+                <view class='coupon-red'>免费兑换</view>
+                <view class="validity-text">{{ couponData.validity }}</view>
+              </view>
+            </view>
+          </view>
+        </view>
+      </view>
+
+      <view class="details-card">
+        <view class="detail-item has-arrow" @click='storeShow = true'>
+          <text class="detail-label">适用商品</text>
+          <view class="detail-right">
+            <text class="detail-value">{{ couponDetails.applicableProducts }}</text>
+            <u--image class='income-right' :src="getImageUrl('@img/income/arrow-right.png')" mode="aspectFit"
+              width="32rpx" height="32rpx"></u--image>
+          </view>
+        </view>
+
+        <view class="detail-item has-arrow" @click='shopShow = true'>
+          <text class="detail-label">适用商家</text>
+          <view class="detail-right">
+            <view class="merchant-avatars">
+              <view class="avatar" v-for="i in 6" :key="i">
+                <u--image :src="getImageUrl('@img/income/bg.png')" mode="" width="40rpx" height="40rpx"
+                  shape="circle"></u--image>
+              </view>
+
+              <text class="avatar-text">{{ couponDetails.merchantCount }}</text>
+            </view>
+            <u--image class='income-right' :src="getImageUrl('@img/income/arrow-right.png')" mode="aspectFit"
+              width="32rpx" height="32rpx"></u--image>
+          </view>
+        </view>
+
+        <view class="detail-item">
+          <text class="detail-label">使用规则</text>
+          <text class="detail-value">{{ couponDetails.usageRules }}</text>
+        </view>
+
+        <view class="detail-item">
+          <text class="detail-label">使用期限</text>
+          <text class="detail-value">{{ couponDetails.usagePeriod }}</text>
+        </view>
+
+        <view class="detail-item">
+          <text class="detail-label">每日限制领取</text>
+          <text class="detail-value">{{ couponDetails.dailyLimit }}</text>
+        </view>
+
+        <view class="detail-item last">
+          <text class="detail-label">适用地区</text>
+          <text class="detail-value">{{ couponDetails.applicableArea }}</text>
+        </view>
+      </view>
+
+      <!-- 温馨提示 -->
+      <view class='card-text'>
+        <view class='card-text-title'>
+          温馨提示:
+        </view>
+        <view class="rules-list">
+          <view class="rule-item" v-for="(rule, index) in rules" :key="index">
+            <text class="rule-index">{{ index + 1 }}.</text>
+            <text class="rule-text">{{ rule }}</text>
+          </view>
+        </view>
+      </view>
+    </view>
+
+    <view class="share-info">
+      <view class="share-btn">去分享</view>
+    </view>
+  </view>
+
+  <!-- 适用商品 -->
+  <Upopup :isShow="storeShow" title='适用商品' @close='storeShow = false' :isFooter='false'>
+    <view class="store-cont">
+      大同刀削面 | 水蒸蛋 | 水煮肉片 | 葱爆羊肉 | 打卤面 |葱爆羊肉 | 打卤面 |葱爆羊肉 | 打卤面
+    </view>
+  </Upopup>
+
+  <!-- 适用商家 -->
+  <Upopup :isShow="shopShow" title='适用商家' @close='shopShow = false' :isFooter='false'>
+    <view class="shop-cont">
+      <view class="shop-item">
+        <text class="shop-name">顺溜刀削面(国际能源中心店)</text>
+      </view>
+      <view class="shop-info" v-for='(item,index) in 8' :key='index'>
+        <view class="info-row">
+          <view class='info-dis-c'>
+            <u--image class='location-image' :src="getImageUrl('@img/income/location.png')" mode="aspectFit"
+              width="24rpx" height="24rpx" />
+            <text class="shop-distance">距您367m</text>
+            <view class="divider"></view>
+          </view>
+          <text class="info-text">万柏林区迎泽西大街100号国际能源中心B座1层大堂</text>
+        </view>
+        <view class="info-time">
+          <u--image :src="getImageUrl('@img/income/time.png')" mode="aspectFit" width="24rpx" height="24rpx" />
+          <text class="info-text">07:00 - 22:00</text>
+        </view>
+      </view>
+    </view>
+  </Upopup>
+</template>
+
+<style lang="scss" scoped>
+.share-info-container {
+  min-height: 100vh;
+  background: #f8f8fa;
+  padding-bottom: 140rpx;
+
+  .share-info-content {
+    margin: 0 32rpx;
+    display: flex;
+    flex-direction: column;
+    gap: 32rpx;
+  }
+
+  .coupon-card {
+    width: 100%;
+    height: 192rpx;
+    border-radius: 16rpx;
+    overflow: hidden;
+    margin-top: 32rpx;
+
+    .card-background {
+      width: 100%;
+      height: 100%;
+      position: relative;
+
+      .card-content {
+        display: flex;
+        align-items: center;
+        height: 100%;
+        padding: 0 58rpx;
+        box-sizing: border-box;
+
+        .discount-section {
+          .discount-value {
+            display: flex;
+            align-items: baseline;
+            gap: 2rpx;
+
+            .discount-number {
+              font-weight: 700;
+              font-size: 68rpx;
+              color: #ff5365;
+              line-height: 1;
+            }
+
+            .discount-unit {
+              font-weight: 500;
+              font-size: 32rpx;
+              color: #ff5365;
+              line-height: 1;
+              margin-left: 4rpx;
+            }
+          }
+        }
+
+        .info-section {
+          margin-left: 80rpx;
+          display: flex;
+          flex-direction: column;
+          gap: 24rpx;
+
+          .coupon-name {
+            font-weight: 600;
+            font-size: 28rpx;
+            color: #000000;
+            line-height: 1.4;
+          }
+          .coupon-red {
+            color: #ff5365;
+            font-size: 24rpx;
+            margin-bottom: 8rpx;
+          }
+          .validity-text {
+            font-weight: 400;
+            font-size: 24rpx;
+            color: #808080;
+            line-height: 1.4;
+          }
+        }
+      }
+      .change-content {
+        border-radius: 16rpx;
+        border: 12rpx solid #ff5365;
+        padding: 0 24rpx;
+        .info-section {
+          margin-left: 0;
+        }
+      }
+    }
+  }
+
+  .details-card {
+    background: #ffffff;
+    border-radius: 16rpx;
+    padding: 24rpx 22rpx 0rpx 22rpx;
+    border: 1rpx solid #f1eeeb;
+    box-sizing: border-box;
+    width: 100%;
+
+    .detail-item {
+      display: flex;
+      align-items: center;
+      padding: 17rpx 0 17rpx;
+      border-bottom: 1rpx solid #eeeeee;
+
+      &.last {
+        border-bottom: none;
+        align-items: flex-start;
+      }
+
+      &.has-arrow {
+        .detail-right {
+          display: flex;
+          align-items: center;
+          justify-content: space-between;
+          flex: 1;
+        }
+      }
+
+      .detail-label {
+        font-weight: 400;
+        font-size: 28rpx;
+        color: #333333;
+        width: 212rpx;
+      }
+
+      .detail-value {
+        font-weight: 400;
+        font-size: 26rpx;
+        color: #333333;
+        flex: 1;
+      }
+
+      .merchant-avatars {
+        display: flex;
+        align-items: center;
+        gap: 4rpx;
+
+        .avatar {
+          width: 40rpx;
+          height: 40rpx;
+          border-radius: 50%;
+        }
+
+        .avatar-text {
+          font-weight: 400;
+          font-size: 26rpx;
+          color: #333333;
+          margin-left: 8rpx;
+        }
+      }
+    }
+  }
+
+  .card-text {
+    .card-text-title {
+      font-size: 32rpx;
+      color: #333;
+      font-weight: 500;
+      margin-bottom: 24rpx;
+    }
+
+    .rules-list {
+      display: flex;
+      flex-direction: column;
+      gap: 6rpx;
+
+      .rule-item {
+        display: flex;
+        align-items: flex-start;
+        gap: 12rpx;
+        line-height: 40rpx;
+        color: #666;
+        font-size: 26rpx;
+
+        .rule-text {
+          flex: 1;
+
+          line-height: 40rpx;
+          text-align: justify;
+        }
+      }
+    }
+  }
+
+  .share-info {
+    position: fixed;
+    bottom: 0;
+    width: 100%;
+    height: 116rpx;
+    padding: 20rpx 32rpx;
+    box-sizing: border-box;
+    background: #fff;
+
+    .share-btn {
+      background: linear-gradient(90deg, #ff8f83 0%, #ff3f51 100%);
+      border-radius: 8rpx;
+      height: 80rpx;
+      line-height: 80rpx;
+      text-align: center;
+      color: #fff;
+      font-size: 32rpx;
+      font-weight: 500;
+    }
+  }
+}
+
+.store-cont {
+  font-size: 28rpx;
+  line-height: 50rpx;
+  height: 50rpx;
+}
+
+.shop-cont {
+  background: #ffffff;
+  max-height: 60vh;
+  overflow: auto;
+
+  .shop-item {
+    margin-bottom: 12rpx;
+
+    .shop-name {
+      font-weight: 500;
+      font-size: 28rpx;
+      color: #000000;
+    }
+  }
+
+  .shop-info {
+    margin-bottom: 40rpx;
+    .info-row {
+      padding-bottom: 6rpx;
+
+      .info-dis-c {
+        display: inline-block;
+      }
+      .location-image {
+        display: inline-block;
+      }
+
+      .info-text {
+        font-weight: 400;
+        font-size: 26rpx;
+        color: #86909c;
+        line-height: 1.4;
+      }
+      .shop-distance {
+        font-weight: 400;
+        font-size: 26rpx;
+        color: #000000;
+        margin: 0 12rpx 0 10rpx;
+      }
+    }
+
+    .divider {
+      width: 2rpx;
+      height: 24rpx;
+      background: #d9d9d9;
+      margin: 0 16rpx;
+      display: inline-block;
+    }
+    .info-time {
+      display: flex;
+      align-items: flex-start;
+      gap: 10rpx;
+      color: #86909c;
+      font-size: 26rpx;
+    }
+  }
+}
+</style>

+ 509 - 717
src/pages/income/income.vue

@@ -1,775 +1,567 @@
 <script lang="ts" setup>
-import { onReachBottom } from '@dcloudio/uni-app'
-import { usePagination, useRequest } from 'alova/client'
-import { storeToRefs } from 'pinia'
-import { computed, ref, watch } from 'vue'
-import dayjs from 'dayjs'
-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'
-import FilterTime from '@/components/FilterTime.vue'
-import { useShare } from '@/hooks/useShare'
-import { useUserStore } from '@/store'
-import { useTokenStore } from '@/store/token'
-import { changtime, menuButtonInfo, rpxToPx, safeAreaInsets, systemInfo } from '@/utils'
-import { getImageUrl } from '@/utils/imageUtil'
-import IncomeShareInfo from '@/components/IncomeShareInfo.vue'
+import { ref, computed } from "vue";
+import { storeToRefs } from "pinia";
+import { useUserStore } from "@/store/user";
+import { useTokenStore } from "@/store/token";
+import { safeAreaInsets, menuButtonInfo } from "@/utils";
+import CustomNavigationBar from "@/components/CustomNavigationBar.vue";
+import { getImageUrl } from "@/utils/imageUtil";
+import Classification from "./classification.vue";
+import Upopup from "@/components/popup/index.vue";
+import DateRangePicker from "@/components/dateRangePicker/index.vue";
 
 definePage({
-    style: {
-        navigationBarTitleText: '收益',
-        navigationStyle: 'custom',
-        onReachBottomDistance: 157,
-    },
-})
-
-const userStore = useUserStore()
-const tokenStore = useTokenStore()
-// 使用storeToRefs解构userInfo
-const { userInfo } = storeToRefs(userStore)
-const { hasLogin } = storeToRefs(tokenStore)
-
-// 发券人账户表-通过userId查询账户信息
-const { send: getAccountCountRequest, data: accountCountData } = useRequest(getAccountCount, {
-    immediate: false,
-})
-
-// 新增:刷新状态
-const refreshing = ref(false)
-const activeIndex = ref(0)
-const showLoading = ref(false)
-const pickerDate = ref(Date.now())
-const pickerShow = ref(false)
-const totalAmount = ref(0)
-const mescrollHeight = ref(0)
-const incomeShareInfoRef = ref(null)
-const filterTimeRef = ref()
-// 默认日期范围:本月第一天到今天
-const getDefaultDateRange = (): [string, string] => {
-    const now = dayjs()
-    const startOfMonth = now.startOf('month').format('YYYY/MM/DD')
-    const today = now.format('YYYY/MM/DD')
-    return [startOfMonth, today]
+  style: {
+    navigationStyle: "custom",
+    navigationBarTitleText: "收益",
+  },
+});
+
+const userStore = useUserStore();
+const tokenStore = useTokenStore();
+const { hasLogin } = storeToRefs(tokenStore);
+
+const navigationBarHeight = ref(0);
+
+function getNavigationBarHeight() {
+  const menuButtonHeight = menuButtonInfo?.height || 32;
+  const statusBarHeight = safeAreaInsets?.top || 0;
+  navigationBarHeight.value = statusBarHeight + menuButtonHeight + 12;
 }
-const dateRangeValue = ref<[string, string]>(getDefaultDateRange())
-let mescroll = null
-
-// 新增:滚动距离和透明度
-const scrollTop = ref(0)
-const headerOverlayOpacity = computed(() => {
-    // 当滚动距离超过150px时,完全显示纯色背景
-    const headerHeightForRPX = 526
-    const headerHeightForPx = rpxToPx(headerHeightForRPX)
-    const maxScroll = menuButtonInfo.height + safeAreaInsets.top + 64 + (headerHeightForPx - (menuButtonInfo.height + safeAreaInsets.top + 64)) / 2
-    return Math.min(scrollTop.value / maxScroll, 1)
-})
-
-// 新增:监听页面滚动
-onPageScroll((e) => {
-    scrollTop.value = e.scrollTop
-})
-
-const { send: getIncomeData, data: incomeData, pageCount, loading, page, pageSize, isLastPage, refresh, reload, onSuccess, onError } = usePagination((page, pageSize) => {
-    const params: Record<string, any> = {
-        pageNo: page,
-        pageSize,
-        type: 1,
-        status: activeIndex.value,
-    }
-    // 根据tab类型选择日期参数
-    if (activeIndex.value === 2) {
-        // 已提现:使用日期范围
-        if (dateRangeValue.value[0] && dateRangeValue.value[1]) {
-            params.startDate = dateRangeValue.value[0]
-            params.endDate = dateRangeValue.value[1]
-        }
-    } else {
-        // 待结算/已结算:使用年月
-        params.year = changtime(pickerDate.value, 'YYYY')
-        params.month = changtime(pickerDate.value, 'MM')
-    }
-    return getCouponIssuerAccountByPageMap(params)
-}, {
-    initialPage: 1,
-    initialPageSize: 10,
-    append: true,
-    preloadNextPage: false,
-    preloadPreviousPage: false,
-    total: response => response?.total || 0,
-    data: response => response?.detailList || [],
-    immediate: false,
-})
-
-watch(() => incomeData.value, (newVal) => {
-    if (newVal.length === 0 && !loading.value) {
-        mescroll.showEmpty()
-    }
-    else {
-        mescroll.removeEmpty()
-    }
-})
-
-watch(() => headerOverlayOpacity.value, (newVal) => {
-    if (newVal > 0) {
-        mescroll.lockDownScroll(true)
-    }
-    else {
-        mescroll.lockDownScroll(false)
-    }
-})
 
-watch(() => loading.value, (newVal) => {
-    if (newVal && !showLoading.value) {
-        mescroll.showUpScroll()
-    }
-})
-
-// ============ mescroll 配置 ============
-const downOption = reactive({
-    use: true,
-    auto: false,
-    bgColor: '#ed6b66',
-    textColor: '#fff',
-    textInOffset: '下拉刷新',
-    textOutOffset: '释放刷新',
-    textLoading: '刷新中...',
-    offset: 80,
-})
-
-const upOption = reactive({
-    use: true,
-    auto: false,
-    noMoreSize: 7,
-    isBounce: true,
-    page: {
-        num: page.value,
-        size: pageSize.value,
+onMounted(() => {
+  getNavigationBarHeight();
+});
+
+const incomeList = ref([
+  {
+    id: 1,
+    type: "share",
+    title: "分享奖励",
+    amount: "38.00",
+    createTime: "2026-05-20 19:00:00",
+    couponName: "某某某某某优惠券",
+    status: "pending",
+  },
+  {
+    id: 2,
+    type: "share",
+    title: "分享奖励",
+    amount: "58.00",
+    createTime: "2026-05-20 19:00:00",
+    couponName: "某某某优惠券",
+    status: "settled",
+  },
+  {
+    id: 3,
+    type: "share",
+    title: "分享奖励",
+    amount: "58.00",
+    createTime: "2026-05-20 19:00:00",
+    couponName: "优惠券3",
+    status: "pending",
+  },
+]);
+
+function handleViewDetail(item) {
+  uni.navigateTo({
+    url: "/pages-C/income/invitation",
+  });
+}
+const form = ref({});
+const visibleNoticeList = computed(() => {
+  const data = form.value || {};
+  return [
+    {
+      title: "累计收益(元)",
+      content: data.totalIncome || 0,
+      page: "/pages-C/income/incomeCumulative",
     },
-    empty: {
-        use: true,
-        tip: '暂无数据',
-        btnText: '刷新试试',
-        icon: '/static/images/mescroll-empty.png',
-        fixed: false,
+    {
+      title: "累计提现(元)",
+      content: data.totalWithdraw || 0,
+      percentage: data.totalWithdrawPercent || "76.76%",
+      page: "/pages-C/income/incomeMoney",
     },
-    textNoMore: '已经到底了',
-    iconLoadingColor: '#ed6b66',
-    toTop: {
-        src: '/static/images/mescroll-totop.png',
-        offset: 1000,
-        bottom: 120,
+    {
+      title: "银行卡(张)",
+      content: data.bankCards || 0,
+      page: "/pages-C/my/cumulativeGroup",
     },
-    isBoth: false,
-    // 开启虚拟列表优化(大数据量时)
-    virtual: {
-        use: false,
-        rowHeight: 300,
-    }
-})
-
-function mescrollInit(ref) {
-    mescroll = ref
-}
-
-async function downCallback() {
-    // await refresh()
-    await getAccountCountRequest()
-    await refresh()
-    await userStore.fetchUserInfo()
-}
-
-async function onRefresh() {
-    refreshing.value = true
-    await getAccountCountRequest()
-    await refresh()
-    await userStore.fetchUserInfo()
-    refreshing.value = false
-}
-
-async function upCallback() {
-    // console.log('上拉加载触发')
-    if (isLastPage.value) {
-        console.log(mescroll)
-        mescroll.endSuccess(0, false)
-        return
-    }
-
-    page.value++
-}
-
-onReachBottom(() => {
-    // console.log('上拉加载触发')
-    if (isLastPage.value) {
-        // console.log(mescroll)
-        // mescroll.endSuccess(0, false)
-        return
-    }
-    mescroll.showUpScroll()
-    page.value++
-})
-
-onSuccess((response) => {
-    let isNextPage = isLastPage.value
-    // 此处处理alova isLastPage默认值为true,首次请求出现的无法加载下一页问题
-    if (pageCount.value > page.value && isLastPage.value) {
-        isNextPage = false
-    }
-    totalAmount.value = response.data?.totalAmount || 0
-    // mescroll.setPageNum(page.value - 1)
-    mescroll.endSuccess(incomeData.value.length, !isNextPage)
-})
-
-onError(() => {
-    console.log('请求失败')
-    mescroll.endErr()
-    console.log(mescroll)
-    mescroll.hideUpScroll()
-    mescroll.showEmpty()
-})
-
-async function tabChange(index) {
-    // 切换tab时,重置上拉加载
-    // mescroll.resetUpScroll(true)
-    // mescroll.showDownScroll()
-    activeIndex.value = index
-    mescroll.hideUpScroll()
-    mescroll.scrollTo(0, 0)
-    showLoading.value = true
-    await reload()
-    showLoading.value = false
-    console.log(activeIndex.value, '切换tab');
-    
-}
+    {
+      title: "邀新收益(元)",
+      content: data.inviteIncome || 0,
+      percentage: data.inviteIncomePercent || "45.38%",
+      page: "/pages-C/income/incomeInvitation",
+    },
+    {
+      title: "发券收益(元)",
+      content: data.shareIncome || 0,
+      page: "/pages-C/income/incomeSend",
+    },
+    {
+      title: "核销收益(元)",
+      content: data.writeOffIncome || 0,
+      page: "/pages-C/income/incomeCancel",
+    },
+  ];
+});
 
-async function pickerConfirm() {
-    pickerShow.value = false
-    mescroll.scrollTo(0, 0)
-    mescroll.hideUpScroll()
-    showLoading.value = true
-    await reload()
-    showLoading.value = false
+function listInfo(item) {
+  if (item.page) {
+    uni.navigateTo({
+      url: item.page,
+    });
+  }
 }
 
-async function handleDateRangeChange(value: [string, string]) {
-    dateRangeValue.value = value
-    mescroll.scrollTo(0, 0)
-    mescroll.hideUpScroll()
-    showLoading.value = true
-    await reload()
-    showLoading.value = false
+const classShow = ref(false);
+const rewardTabs = ref([
+  { label: "全部", type: "0" },
+  { label: "分享奖励", type: "1" },
+  { label: "核销奖励", type: "2" },
+  { label: "邀新奖励", type: "3" },
+]);
+const classIndex = ref(-1);
+const classItem = ref({
+  label: "",
+  type: "",
+});
+function classClick(index, item) {
+  classIndex.value = index;
+  classShow.value = false;
+  classItem.value = item;
 }
 
-function formatDateRangeDisplay() {
-    if (!dateRangeValue.value[0] || !dateRangeValue.value[1]) {
-        return '选择日期范围'
-    }
-    return `${dateRangeValue.value[0]} - ${dateRangeValue.value[1]}`
+//滚动
+const scrollTop = ref(0);
+onPageScroll((e) => {
+  scrollTop.value = e.scrollTop;
+});
+
+const filterValue = ref<string[] | string>([]);
+// 处理时间选择变化
+function handleTimeChange(value: string | string[]) {
+  console.log("时间选择变化", value);
+  filterValue.value = value;
 }
 
-onShow(async () => {
-    // 登录后查询收益数据
-    if (hasLogin) {
-        // await getAccountCountRequest()
-        // await reload()
-        // 数据会在useScroll的onMounted中自动加载,这里不需要额外调用
-    }
-})
-
-onLoad(async () => {
-    // 计算mescroll最小高度
-    const systemInfo = uni.getSystemInfoSync()
-    const vh100 = systemInfo.screenHeight - systemInfo.safeAreaInsets.bottom
-    const tabbarHeight = (systemInfo.screenWidth / 750) * 100
-    mescrollHeight.value = vh100 - tabbarHeight
-
-    if (hasLogin) {
-        await getAccountCountRequest()
-        await getIncomeData()
-        // 数据会在useScroll的onMounted中自动加载,这里不需要额外调用
-    }
-})
-
-// 创建分享hook实例
-const { getShareConfig, getTimelineShareConfig } = useShare()
+// 手动打开选择器
+const pickerRef = ref();
+const openPicker = () => {
+  pickerRef.value?.openPopup();
+};
 
-// #ifdef MP-WEIXIN
-// 分享给好友生命周期函数
-onShareAppMessage(async (options) => {
-    return await getShareConfig()
-})
+const showPicker = ref(false);
+const defaultStart = "";
+const defaultEnd = "";
 
-// 分享到朋友圈生命周期函数
-onShareTimeline(async () => {
-    return await getTimelineShareConfig()
-})
-// #endif
+const handleConfirm = (value: { startDate: string; endDate: string }) => {
+  console.log("选中的日期范围:", value);
+  // 处理业务逻辑
+};
 
-const isUnlock = computed(() => accountCountData && accountCountData?.status === 0)
-
-function goPage(page: string) {
-    uni.navigateTo({
-        url: `/pages-A/${page}/index`,
-    })
-}
-
-function goToWithdrawal() {
-    // 获取可提现余额
-    const money = accountCountData.value?.withdrawableAmount || 0
-    const bankNumber = accountCountData.value?.bankNumber || 0
-    uni.navigateTo({
-        url: `/pages-B/payment/withdrawal?m=${money}&bankNumber=${bankNumber}`,
-    })
-}
-
-function handleItemClick(item: any) {
-    if (activeIndex.value == 2) {
-        // 跳转提现详情页
-        uni.navigateTo({
-            url: `/pages-B/payment/detail-withdraw?id=${item.payoutNo}`,
-        })
-    } else {
-        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('网络异常,请稍后重试');
-        });
-}
+const handleCancel = () => {
+  console.log("取消选择");
+};
 </script>
 
 <template>
-    <view class="profile-container">
-        <view class="income-header-overlay"
-            :style="{ opacity: headerOverlayOpacity, height: `calc(${menuButtonInfo.bottom + 4}px + 50rpx)` }" />
-        <mescroll-body id="mescrollContainer" :height="`${mescrollHeight}px`" :sticky="true" :down="downOption"
-            :up="upOption" :fixed="false" :is-show-empty="true" @init="mescrollInit" @down="downCallback"
-            @up="upCallback" @emptyclick="reload">
-            <!-- 顶部区域 -->
-            <view class="income-header"
-                :style="{ background: `url(${getImageUrl('@img/income/income-bg.png')})`, backgroundSize: '100% 110%', backgroundPosition: 'left bottom', backgroundRepeat: 'no-repeat' }">
-                <view class="income-header-avatar-info"
-                    :style="{ paddingTop: `calc(${safeAreaInsets.top}px + ${menuButtonInfo.height}px)` }">
-                    <view class="income-header-balance">
-                        当前账户余额(元)
-                    </view>
-                    <view class="income-header-balance-num">
-                        <view class="income-header-balance-num-amount">
-                            {{ accountCountData?.balance || 0 }}
-                        </view>
-                        <view class="income-header-balance-num-btns">
-                            <!-- <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>
-                </view>
-                <view class="income-header-tips">
-                    <view class="income-header-tips-item">
-                        <view class="income-header-tips-item-num">
-                            {{ accountCountData?.totalCommission || 0 }}
-                        </view>
-                        <view class="income-header-tips-item-des">
-                            累计收益
-                        </view>
-                    </view>
-                    <view class="income-header-tips-item">
-                        <view class="income-header-tips-item-num">
-                            {{ accountCountData?.unsettledAmount || 0 }}
-                        </view>
-                        <view class="income-header-tips-item-des">
-                            待结算
-                        </view>
-                    </view>                    
-                    <view class="income-header-tips-item">
-                        <view class="income-header-tips-item-num">
-                            {{ 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?.bankNumber || 0 }}
-                        </view>
-                        <view class="income-header-tips-item-des">
-                            银行卡
-                        </view>
-                    </view>
-                </view>
-            </view>
-            <!-- 公告 -->
-            <view v-if="isUnlock" class="income-header-notice">
-                <view class="income-header-notice-icon">
-                    <image :src="getImageUrl('@img/income/notice.png')" mode="aspectFit" />
-                </view>
-                <view class="income-header-notice-content">
-                    在考核周期内未达标已锁定收益,<text>前往查看~</text>
-                </view>
-            </view>
-            <!-- 结算筛选 -->
-            <view class="income-header-menu-filter"
-                :style="{ marginTop: isUnlock ? '24rpx' : '-50rpx', top: `${menuButtonInfo.bottom + 4}px` }">
-                <view class="income-header-menu-filter-item" :class="[activeIndex === 0 ? 'active' : '']"
-                    @click="tabChange(0)">
-                    待结算
-                </view>
-                <view class="income-header-menu-filter-item" :class="[activeIndex === 1 ? 'active' : '']"
-                    @click="tabChange(1)">
-                    已结算
-                </view>
-                <view class="income-header-menu-filter-item" :class="[activeIndex === 2 ? 'active' : '']"
-                    @click="tabChange(2)">
-                    已提现
-                </view>
+  <view class="page-container">
+    <!-- 顶部蓝色渐变背景 -->
+    <view class="page-header-bg" :style="{
+                    backgroundImage: `url(${getImageUrl('@img/income/bg.png')})`,
+                    backgroundSize: 'cover',
+                    backgroundRepeat: 'no-repeat',
+                    backgroundPosition: 'center'
+                }">
+    </view>
+
+    <u-navbar title="收益" :autoBack="true" :placeholder="true" :border-bottom="false"
+      :title-style="{ fontWeight: 'bold', color: scrollTop > 1 ? '#000' : '#fff' }"
+      :bgColor="scrollTop>1?'#fff':'transparent'" :leftIconColor="scrollTop>1?'#000':'#fff'"></u-navbar>
+
+    <!-- 页面主内容 -->
+    <view class="main-content">
+      <view class="income-card" :style="{
+                    backgroundImage: `url(${getImageUrl('@img/income/bg2.png')})`,
+                    backgroundSize: 'cover',
+                    backgroundRepeat: 'no-repeat',
+                    backgroundPosition: 'center'
+                }">
+        <view class="income-card__header">
+          <view class="income-card__balance">
+            <view class="income-card__balance-label">当前账户余额(元)</view>
+            <view class="income-card__balance-value">3859.00</view>
+          </view>
+          <button class="income-card__withdraw-btn">申请提现</button>
+        </view>
+        <view class="notice-container">
+          <view v-for="(item, index) in visibleNoticeList" :key="index" class="notice-item" @click='listInfo(item)'>
+            <view class="notice-title">{{ item.title }}</view>
+            <view class="notice-content">{{ item.content }}</view>
+          </view>
+        </view>
+        <!-- <u--image class='notice-img' :src="getImageUrl('@img/income/bg2.png')" mode="aspectFit" width="334rpx"
+          height="247rpx">
+        </u--image> -->
+      </view>
+
+      <view class='income-select'>
+        <view class='income-time' @click='showPicker=true'>
+          <view class='income-time-name'>2026 05.10-2026 05.12 </view>
+          <u--image class='income-down' :src="getImageUrl('@img/income/down.png')" mode="aspectFit" width="18rpx"
+            height="18rpx">
+          </u--image>
+        </view>
+        <view class='income-time' @click='classShow=true'>
+          <view class='income-time-name'>{{classItem.label?classItem.label:'选择分类'}} </view>
+          <u--image class='income-down' :src="getImageUrl('@img/income/down.png')" mode="aspectFit" width="18rpx"
+            height="18rpx">
+          </u--image>
+        </view>
+        <view class='income-time'>
+          <view class='income-time-name'>结算状态</view>
+          <u--image class='income-down' :src="getImageUrl('@img/income/down.png')" mode="aspectFit" width="18rpx"
+            height="18rpx">
+          </u--image>
+        </view>
+      </view>
+    </view>
+    <!-- 列表 -->
+    <view class="income-detail-content">
+      <view v-for="item in incomeList" :key="item.id" class="income-card">
+        <view class="card-header">
+          <view class="title-tag">
+            <view class="tag-line"></view>
+            <text class="tag-text">{{ item.title }}</text>
+          </view>
+          <view class="view-detail" @click="handleViewDetail(item)">
+            <text class="detail-text">查看详情</text>
+            <u--image class='income-right' :src="getImageUrl('@img/income/arrow-right.png')" mode="aspectFit"
+              width="32rpx" height="32rpx">
+            </u--image>
+          </view>
+        </view>
+
+        <view class="card-divider"></view>
+
+        <view class="card-body">
+          <view class="amount-info">
+            <view class="amount-row">
+              <text class="amount-label">总收益</text>
+              <view class="amount-value">
+                <text class="currency-symbol">¥</text>
+                <text class="amount-number">{{ item.amount }}</text>
+              </view>
             </view>
-            <!-- 时间筛选 -->
-            <view class="income-menu-time-filter" :style="{ top: `calc(${menuButtonInfo.bottom + 4}px + 90rpx)` }">
-                <!-- 已提现使用日期范围选择 -->
-                <template v-if="activeIndex === 2">
-                    <view class="income-menu-time-filter-text" @click="filterTimeRef?.openPopup()">
-                        <text>{{ formatDateRangeDisplay() }}</text>
-                        <up-icon name="arrow-down" color="#666666" size="14" />
-                    </view>
-                </template>
-                <!-- 待结算/已结算使用年月选择 -->
-                <template v-else>
-                    <view class="income-menu-time-filter-text" @click="pickerShow = true">
-                        <text>{{ changtime(pickerDate) }}</text>
-                        <up-icon name="arrow-down" color="#666666" size="14" />
-                        <up-datetime-picker v-model="pickerDate" :show="pickerShow" mode="year-month" close-on-click-overlay
-                            @confirm="pickerConfirm" @cancel="pickerShow = false" />
-                    </view>
-                </template>
-                <view>
-                    合计:¥{{ totalAmount }}
-                </view>
+            <view :class="['status-tag', `status-${item.status}`]">
+              {{ item.status === 'pending' ? '待结算' : '已结算' }}
             </view>
-            <view class="income-menu-list">
-                <view v-for="item in incomeData" :key="item.userId" class="income-content-item">
-                    <income-item :data="item" :type="activeIndex" @itemClick="handleItemClick" />
-                </view>
-                <!-- 滚动区域内的loading遮罩 -->
-                <view v-if="showLoading && loading" class="loading-mask">
-                    <up-loading-icon color="#ed6b66" text="加载中" />
-                </view>
+          </view>
+          <view class="coupon-info">
+            <text class="coupon-name">{{ item.couponName }}</text>
+            <view class="time-row">
+              <text class="time-label">创建时间</text>
+              <text class="time-value">{{ item.createTime }}</text>
             </view>
-        </mescroll-body>
+          </view>
+        </view>
+      </view>
+    </view>
+  </view>
+
+  <!-- 选择分类 -->
+  <Upopup :isShow="classShow" title='选择分类' @close='classShow=false' :isFooter='false'>
+    <view class="classification-cont">
+      <view class='classification-list' v-for='(item,index) in rewardTabs' :key='index' @click='classClick(index,item)'>
+        <view class='classification-label'>{{ item.label }}</view>
+        <u--image class='selected-icon' v-show='classIndex==index' :src="getImageUrl('@img/income/selected.png')"
+          mode="aspectFit" width="32rpx" height="32rpx">
+        </u--image>
+      </view>
     </view>
-    <FilterTime ref="filterTimeRef" v-model="dateRangeValue" @confirm="handleDateRangeChange" />
-    <!-- <IncomeShareInfo ref="incomeShareInfoRef" :type="activeIndex" /> -->
+  </Upopup>
+
+  <!-- 日期 -->
+  <DateRangePicker v-model:show="showPicker" :startDate="defaultStart" :endDate="defaultEnd" @confirm="handleConfirm"
+    @cancel="handleCancel" />
 </template>
 
 <style lang="scss" scoped>
-.profile-container {
-    background-color: #f5f5f5;
-    line-height: 1;
+.page-container {
+  min-height: 100vh;
+  background-color: #f8f8fa;
+
+  // 顶部渐变背景
+  .page-header-bg {
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    height: 460rpx;
+    background: linear-gradient(to bottom, #0879ff, #f8f8fa);
+
+    z-index: 1;
+  }
+  ::v-deep .top-nav {
+    box-shadow: none !important;
+  }
+  .main-content {
+    position: relative;
+    z-index: 2;
+    margin: 0 20rpx;
+  }
+}
+.income-card {
+  background: #fff;
+  position: relative;
+  border-radius: 32rpx;
+  padding: 40rpx;
+  box-sizing: border-box;
+
+  &__header {
+    width: 100%;
     display: flex;
-    flex-direction: column;
-
-    /* 添加这行,设置容器高度为视口高度 */
-
-    .income-header {
-        height: 526rpx;
-
-        .income-header-avatar-info {
-            display: flex;
-            flex-direction: column;
-            gap: 30rpx;
-            padding: 0 24rpx;
-
-            .income-header-balance {
-                font-weight: 400;
-                font-size: 26rpx;
-                color: #ffffff;
-            }
-
-            .income-header-balance-num {
-                display: flex;
-                justify-content: space-between;
-                align-items: center;
-
-                .income-header-balance-num-amount {
-                    font-weight: 500;
-                    font-size: 65rpx;
-                    color: #ffffff;
-                }
-
-                .income-header-balance-num-btns {
-                    display: flex;
-                    gap: 15rpx;
-
-                    .income-header-balance-num-btn {
-                        padding: 20rpx 40rpx 15rpx;
-                        border-radius: 33rpx;
-                        font-weight: 400;
-                        font-size: 26rpx;
-
-                        &.js {
-                            background: #da4c47;
-                            color: #ffffff;
-                        }
-
-                        &.tx {
-                            background: radial-gradient( 0% 0% at 0% 0%, #F8E1B4 0%, #F9EFC7 100%), #DA4C47;
-                            color: #ED6B66;
-                        }
-                    }
-                }
-            }
-        }
-
-        .income-header-tips {
-            height: 124rpx;
-            display: flex;
-            background: linear-gradient(114deg, #f67873, #fb847f, #f67873);
-            border-radius: 10rpx;
-            margin: 47rpx 24rpx 0 24rpx;
-
-            .income-header-tips-item {
-                flex: 1;
-                display: flex;
-                flex-direction: column;
-                justify-content: center;
-                align-items: center;
-                gap: 15rpx;
-                font-weight: 500;
-                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;
-                // }
-
-                .income-header-tips-item-num {
-                    font-weight: bold;
-                }
-
-                .income-header-tips-item-des {
-                    font-weight: 400;
-                    font-size: 24rpx;
-                    color: #ffffff;
-                }
-            }
-        }
+    align-items: center;
+    justify-content: space-between;
+  }
+
+  &__balance {
+    flex: 1;
+  }
+
+  &__balance-label {
+    color: rgba(0, 0, 0, 0.45);
+    font-size: 24rpx;
+  }
+
+  &__balance-value {
+    font-weight: 600;
+    font-size: 60rpx;
+    color: #333;
+    margin-top: 12rpx;
+  }
+
+  &__withdraw-btn {
+    background: linear-gradient(90deg, #ff9688 0%, #ff505f 58.96%);
+    border-radius: 44rpx;
+    width: 156rpx;
+    height: 66rpx;
+    line-height: 66rpx;
+    text-align: center;
+    font-size: 26rpx;
+    color: #fff;
+    margin: 0;
+    padding: 0;
+    border: none;
+
+    &::after {
+      border: none;
     }
+  }
+}
 
-    .income-header-notice {
-        margin: -50rpx 24rpx 24rpx 24rpx;
-        background: #ffffff;
-        border-radius: 10rpx;
-        display: flex;
-        align-items: center;
-        gap: 20rpx;
-        padding: 28rpx 20rpx;
-
-        .income-header-notice-icon {
-            width: 52rpx;
-            height: 35rpx;
+.notice-container {
+  margin-top: 46rpx;
+  display: grid;
+  grid-template-columns: repeat(3, 1fr); // 两列布局
+  gap: 20rpx; // 图片间距
+  .notice-title {
+    font-size: 24rpx;
+    color: rgba(0, 0, 0, 0.45);
+  }
+  .notice-conten {
+    font-size: 36rpx;
+    font-weight: 600;
+    color: #333;
+  }
+}
+.notice-img {
+  position: absolute;
+  bottom: 0;
+  right: 0;
+}
+.income-select {
+  margin: 40rpx 0 24rpx 0;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  .income-time-name {
+    font-size: 26rpx;
+    color: #333;
+  }
+  .income-time {
+    display: flex;
+  }
+  .income-down {
+    margin-top: -4rpx;
+  }
+}
 
-            image {
-                width: 100%;
-                height: 100%;
-            }
-        }
+.income-detail-content {
+  padding: 20rpx;
+  display: flex;
+  flex-direction: column;
+  gap: 20rpx;
+}
 
-        .income-header-notice-content {
-            font-weight: 400;
-            font-size: 24rpx;
-            color: #333333;
+.income-card {
+  background: #ffffff;
+  border-radius: 16rpx;
+  padding: 32rpx;
 
-            text {
-                color: #c52d27;
-            }
-        }
+  .card-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+
+    .title-tag {
+      display: flex;
+      align-items: center;
+      gap: 18rpx;
+
+      .tag-line {
+        width: 6rpx;
+        height: 32rpx;
+        background: #ff5365;
+      }
+
+      .tag-text {
+        font-family: PingFang SC;
+        font-weight: 600;
+        font-size: 28rpx;
+        color: #2c2c2c;
+        line-height: 32rpx;
+      }
     }
 
-    // 新增:吸顶区域上方的覆盖元素
-    .income-header-overlay {
-        position: fixed;
-        top: 0;
-        left: 0;
-        right: 0;
-        background-color: #ed6b66;
-        border-radius: 10rpx 10rpx 0rpx 0rpx;
-        z-index: 99;
-        pointer-events: none; // 确保不影响点击事件
+    .view-detail {
+      display: flex;
+      align-items: center;
+      gap: 8rpx;
+
+      .detail-text {
+        font-weight: 400;
+        font-size: 24rpx;
+        color: #999999;
+        line-height: 26rpx;
+      }
     }
+  }
+  .income-right {
+    margin-top: -4rpx;
+  }
+  .card-divider {
+    width: 100%;
+    height: 1rpx;
+    background: #eaeaea;
+    margin: 32rpx 0;
+  }
+
+  .card-body {
+    .coupon-info {
+      display: flex;
+      flex-direction: column;
+      gap: 20rpx;
+      margin-top: 18rpx;
+
+      .coupon-name {
+        font-weight: 400;
+        font-size: 24rpx;
+        color: #2c2c2c;
+        line-height: 28rpx;
+      }
 
-    .income-header-menu-filter {
+      .time-row {
         display: flex;
-        justify-content: space-between;
-        align-items: center;
-        font-weight: 500;
-        font-size: 32rpx;
-        color: #333333;
-        height: 90rpx;
-        background: #ffffff;
-        box-shadow: 0rpx 3rpx 7rpx 0rpx rgba(213, 213, 213, 0.29);
-        border-radius: 10rpx 10rpx 0rpx 0rpx;
-        position: sticky;
-        top: 0;
-        z-index: 100;
-
-        .income-header-menu-filter-item {
-            flex: 1;
-            display: flex;
-            justify-content: center;
-            align-items: center;
-            position: relative;
-            height: 100%;
-
-            &.active {
-                color: #ed6b66;
-
-                &:after {
-                    content: '';
-                    position: absolute;
-                    bottom: 0;
-                    left: 50%;
-                    transform: translateX(-50%);
-                    width: 67rpx;
-                    height: 6rpx;
-                    background: #ed6b66;
-                    border-radius: 3rpx;
-                }
-            }
+        gap: 10rpx;
+
+        .time-label {
+          font-family: PingFang SC;
+          font-weight: 400;
+          font-size: 24rpx;
+          color: rgba(0, 0, 0, 0.25);
+          line-height: 24rpx;
         }
-    }
 
-    .income-menu-time-filter {
-        display: flex;
-        justify-content: space-between;
-        align-items: center;
-        font-weight: 400;
-        font-size: 24rpx;
-        color: #333333;
-        padding: 30rpx 20rpx 14rpx;
-        background: #ffffff;
-        margin-top: 4rpx;
-        position: sticky;
-        z-index: 100;
-
-        .income-menu-time-filter-text {
-            font-weight: 400;
-            font-size: 26rpx;
-            color: #000000;
-            display: flex;
-            align-items: center;
-            gap: 12rpx;
+        .time-value {
+          font-family: PingFang SC;
+          font-weight: 400;
+          font-size: 24rpx;
+          color: #bfbfbf;
+          line-height: 24rpx;
         }
+      }
     }
 
-    .income-menu-list {
-        background: #ffffff;
-        // flex: 1;
+    .amount-info {
+      display: flex;
+      align-items: center;
+      .amount-row {
         display: flex;
-        flex-direction: column;
-        position: relative;
-
-        #mescrollContainer {
-            height: 100%;
-            flex: 1;
+        align-items: center;
+        .amount-label {
+          font-weight: 400;
+          font-size: 26rpx;
+          color: #2c2c2c;
+          line-height: 26rpx;
         }
 
-        .income-content-item {
-            margin-top: 20rpx;
-        }
+        .amount-value {
+          display: flex;
+          align-items: baseline;
+          gap: 4rpx;
+          margin: 0 28rpx 0 12rpx;
 
-        // loading遮罩样式
-        .loading-mask {
-            position: absolute;
-            top: 0;
-            left: 0;
-            right: 0;
-            bottom: 0;
-            background-color: rgba(255, 255, 255, 0.8);
-            display: flex;
-            flex-direction: row;
-            flex-wrap: nowrap;
-            justify-content: center;
-            align-items: end;
-            z-index: 999;
+          .currency-symbol {
+            font-weight: 600;
+            font-size: 26rpx;
+            color: #ff5365;
+            line-height: 26rpx;
+          }
+
+          .amount-number {
+            font-family: PingFang SC;
+            font-weight: 600;
+            font-size: 36rpx;
+            color: #ff5365;
+            line-height: 40rpx;
+          }
         }
+      }
 
-        .loading-spinner {
-            width: 40rpx;
-            height: 40rpx;
-            border: 4rpx solid #e0e0e0;
-            border-top-color: #ed6b66;
-            border-radius: 50%;
-            animation: spin 1s linear infinite;
-        }
+      .status-tag {
+        padding: 7rpx 12rpx;
+        border-radius: 4rpx;
+        font-family: PingFang SC;
+        font-weight: 400;
+        font-size: 24rpx;
+        line-height: 140%;
 
-        .loading-text {
-            margin-left: 16rpx;
-            font-size: 24rpx;
-            color: #666;
+        &.status-pending {
+          background: #fff7e8;
+          color: #ec790a;
         }
 
-        @keyframes spin {
-            to {
-                transform: rotate(360deg);
-            }
+        &.status-settled {
+          background: #e8ffea;
+          color: #00b42a;
         }
+      }
     }
+  }
+}
 
-    .income-header-menu-filter,
-    .income-menu-time-filter,
-    .income-menu-list {
-        margin-left: 24rpx;
-        margin-right: 23rpx;
-    }
-
-    :deep(.mescroll-empty) {
-        width: auto;
-        margin-left: 23rpx;
-        margin-left: 24rpx;
-        background-color: #fff;
-        border-bottom-left-radius: 10rpx;
-        border-bottom-right-radius: 10rpx;
-    }
+.classification-cont {
+  .classification-list {
+    display: flex;
+    justify-content: space-between;
+  }
+  .classification-label {
+    color: #2c2c2c;
+    font-size: 26rpx;
+    padding-bottom: 32rpx;
+  }
 }
 </style>

+ 813 - 0
src/pages/income/incomeCopy.vue

@@ -0,0 +1,813 @@
+<script lang="ts" setup>
+import { onReachBottom } from "@dcloudio/uni-app";
+import { usePagination, useRequest } from "alova/client";
+import { storeToRefs } from "pinia";
+import { computed, ref, watch } from "vue";
+import dayjs from "dayjs";
+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";
+import FilterTime from "@/components/FilterTime.vue";
+import { useShare } from "@/hooks/useShare";
+import { useUserStore } from "@/store";
+import { useTokenStore } from "@/store/token";
+import {
+  changtime,
+  menuButtonInfo,
+  rpxToPx,
+  safeAreaInsets,
+  systemInfo,
+} from "@/utils";
+import { getImageUrl } from "@/utils/imageUtil";
+import IncomeShareInfo from "@/components/IncomeShareInfo.vue";
+
+definePage({
+  style: {
+    navigationBarTitleText: "收益",
+    navigationStyle: "custom",
+    onReachBottomDistance: 157,
+  },
+});
+
+const userStore = useUserStore();
+const tokenStore = useTokenStore();
+// 使用storeToRefs解构userInfo
+const { userInfo } = storeToRefs(userStore);
+const { hasLogin } = storeToRefs(tokenStore);
+
+// 发券人账户表-通过userId查询账户信息
+const { send: getAccountCountRequest, data: accountCountData } = useRequest(
+  getAccountCount,
+  {
+    immediate: false,
+  }
+);
+
+// 新增:刷新状态
+const refreshing = ref(false);
+const activeIndex = ref(0);
+const showLoading = ref(false);
+const pickerDate = ref(Date.now());
+const pickerShow = ref(false);
+const totalAmount = ref(0);
+const mescrollHeight = ref(0);
+const incomeShareInfoRef = ref(null);
+const filterTimeRef = ref();
+// 默认日期范围:本月第一天到今天
+const getDefaultDateRange = (): [string, string] => {
+  const now = dayjs();
+  const startOfMonth = now.startOf("month").format("YYYY/MM/DD");
+  const today = now.format("YYYY/MM/DD");
+  return [startOfMonth, today];
+};
+const dateRangeValue = ref<[string, string]>(getDefaultDateRange());
+let mescroll = null;
+
+// 新增:滚动距离和透明度
+const scrollTop = ref(0);
+const headerOverlayOpacity = computed(() => {
+  // 当滚动距离超过150px时,完全显示纯色背景
+  const headerHeightForRPX = 526;
+  const headerHeightForPx = rpxToPx(headerHeightForRPX);
+  const maxScroll =
+    menuButtonInfo.height +
+    safeAreaInsets.top +
+    64 +
+    (headerHeightForPx - (menuButtonInfo.height + safeAreaInsets.top + 64)) / 2;
+  return Math.min(scrollTop.value / maxScroll, 1);
+});
+
+// 新增:监听页面滚动
+onPageScroll((e) => {
+  scrollTop.value = e.scrollTop;
+});
+
+const {
+  send: getIncomeData,
+  data: incomeData,
+  pageCount,
+  loading,
+  page,
+  pageSize,
+  isLastPage,
+  refresh,
+  reload,
+  onSuccess,
+  onError,
+} = usePagination(
+  (page, pageSize) => {
+    const params: Record<string, any> = {
+      pageNo: page,
+      pageSize,
+      type: 1,
+      status: activeIndex.value,
+    };
+    // 根据tab类型选择日期参数
+    if (activeIndex.value === 2) {
+      // 已提现:使用日期范围
+      if (dateRangeValue.value[0] && dateRangeValue.value[1]) {
+        params.startDate = dateRangeValue.value[0];
+        params.endDate = dateRangeValue.value[1];
+      }
+    } else {
+      // 待结算/已结算:使用年月
+      params.year = changtime(pickerDate.value, "YYYY");
+      params.month = changtime(pickerDate.value, "MM");
+    }
+    return getCouponIssuerAccountByPageMap(params);
+  },
+  {
+    initialPage: 1,
+    initialPageSize: 10,
+    append: true,
+    preloadNextPage: false,
+    preloadPreviousPage: false,
+    total: (response) => response?.total || 0,
+    data: (response) => response?.detailList || [],
+    immediate: false,
+  }
+);
+
+watch(
+  () => incomeData.value,
+  (newVal) => {
+    if (newVal.length === 0 && !loading.value) {
+      mescroll.showEmpty();
+    } else {
+      mescroll.removeEmpty();
+    }
+  }
+);
+
+watch(
+  () => headerOverlayOpacity.value,
+  (newVal) => {
+    if (newVal > 0) {
+      mescroll.lockDownScroll(true);
+    } else {
+      mescroll.lockDownScroll(false);
+    }
+  }
+);
+
+watch(
+  () => loading.value,
+  (newVal) => {
+    if (newVal && !showLoading.value) {
+      mescroll.showUpScroll();
+    }
+  }
+);
+
+// ============ mescroll 配置 ============
+const downOption = reactive({
+  use: true,
+  auto: false,
+  bgColor: "#ed6b66",
+  textColor: "#fff",
+  textInOffset: "下拉刷新",
+  textOutOffset: "释放刷新",
+  textLoading: "刷新中...",
+  offset: 80,
+});
+
+const upOption = reactive({
+  use: true,
+  auto: false,
+  noMoreSize: 7,
+  isBounce: true,
+  page: {
+    num: page.value,
+    size: pageSize.value,
+  },
+  empty: {
+    use: true,
+    tip: "暂无数据",
+    btnText: "刷新试试",
+    icon: "/static/images/mescroll-empty.png",
+    fixed: false,
+  },
+  textNoMore: "已经到底了",
+  iconLoadingColor: "#ed6b66",
+  toTop: {
+    src: "/static/images/mescroll-totop.png",
+    offset: 1000,
+    bottom: 120,
+  },
+  isBoth: false,
+  // 开启虚拟列表优化(大数据量时)
+  virtual: {
+    use: false,
+    rowHeight: 300,
+  },
+});
+
+function mescrollInit(ref) {
+  mescroll = ref;
+}
+
+async function downCallback() {
+  // await refresh()
+  await getAccountCountRequest();
+  await refresh();
+  await userStore.fetchUserInfo();
+}
+
+async function onRefresh() {
+  refreshing.value = true;
+  await getAccountCountRequest();
+  await refresh();
+  await userStore.fetchUserInfo();
+  refreshing.value = false;
+}
+
+async function upCallback() {
+  // console.log('上拉加载触发')
+  if (isLastPage.value) {
+    console.log(mescroll);
+    mescroll.endSuccess(0, false);
+    return;
+  }
+
+  page.value++;
+}
+
+onReachBottom(() => {
+  // console.log('上拉加载触发')
+  if (isLastPage.value) {
+    // console.log(mescroll)
+    // mescroll.endSuccess(0, false)
+    return;
+  }
+  mescroll.showUpScroll();
+  page.value++;
+});
+
+onSuccess((response) => {
+  let isNextPage = isLastPage.value;
+  // 此处处理alova isLastPage默认值为true,首次请求出现的无法加载下一页问题
+  if (pageCount.value > page.value && isLastPage.value) {
+    isNextPage = false;
+  }
+  totalAmount.value = response.data?.totalAmount || 0;
+  // mescroll.setPageNum(page.value - 1)
+  mescroll.endSuccess(incomeData.value.length, !isNextPage);
+});
+
+onError(() => {
+  console.log("请求失败");
+  mescroll.endErr();
+  console.log(mescroll);
+  mescroll.hideUpScroll();
+  mescroll.showEmpty();
+});
+
+async function tabChange(index) {
+  // 切换tab时,重置上拉加载
+  // mescroll.resetUpScroll(true)
+  // mescroll.showDownScroll()
+  activeIndex.value = index;
+  mescroll.hideUpScroll();
+  mescroll.scrollTo(0, 0);
+  showLoading.value = true;
+  await reload();
+  showLoading.value = false;
+  console.log(activeIndex.value, "切换tab");
+}
+
+async function pickerConfirm() {
+  pickerShow.value = false;
+  mescroll.scrollTo(0, 0);
+  mescroll.hideUpScroll();
+  showLoading.value = true;
+  await reload();
+  showLoading.value = false;
+}
+
+async function handleDateRangeChange(value: [string, string]) {
+  dateRangeValue.value = value;
+  mescroll.scrollTo(0, 0);
+  mescroll.hideUpScroll();
+  showLoading.value = true;
+  await reload();
+  showLoading.value = false;
+}
+
+function formatDateRangeDisplay() {
+  if (!dateRangeValue.value[0] || !dateRangeValue.value[1]) {
+    return "选择日期范围";
+  }
+  return `${dateRangeValue.value[0]} - ${dateRangeValue.value[1]}`;
+}
+
+onShow(async () => {
+  // 登录后查询收益数据
+  if (hasLogin) {
+    // await getAccountCountRequest()
+    // await reload()
+    // 数据会在useScroll的onMounted中自动加载,这里不需要额外调用
+  }
+});
+
+onLoad(async () => {
+  // 计算mescroll最小高度
+  const systemInfo = uni.getSystemInfoSync();
+  const vh100 = systemInfo.screenHeight - systemInfo.safeAreaInsets.bottom;
+  const tabbarHeight = (systemInfo.screenWidth / 750) * 100;
+  mescrollHeight.value = vh100 - tabbarHeight;
+
+  if (hasLogin) {
+    await getAccountCountRequest();
+    await getIncomeData();
+    // 数据会在useScroll的onMounted中自动加载,这里不需要额外调用
+  }
+});
+
+// 创建分享hook实例
+const { getShareConfig, getTimelineShareConfig } = useShare();
+
+// #ifdef MP-WEIXIN
+// 分享给好友生命周期函数
+onShareAppMessage(async (options) => {
+  return await getShareConfig();
+});
+
+// 分享到朋友圈生命周期函数
+onShareTimeline(async () => {
+  return await getTimelineShareConfig();
+});
+// #endif
+
+const isUnlock = computed(
+  () => accountCountData && accountCountData?.status === 0
+);
+
+function goPage(page: string) {
+  uni.navigateTo({
+    url: `/pages-A/${page}/index`,
+  });
+}
+
+function goToWithdrawal() {
+  // 获取可提现余额
+  const money = accountCountData.value?.withdrawableAmount || 0;
+  const bankNumber = accountCountData.value?.bankNumber || 0;
+  uni.navigateTo({
+    url: `/pages-B/payment/withdrawal?m=${money}&bankNumber=${bankNumber}`,
+  });
+}
+
+function handleItemClick(item: any) {
+  if (activeIndex.value == 2) {
+    // 跳转提现详情页
+    uni.navigateTo({
+      url: `/pages-B/payment/detail-withdraw?id=${item.payoutNo}`,
+    });
+  } else {
+    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>
+  <view class="profile-container">
+    <view class="income-header-overlay"
+      :style="{ opacity: headerOverlayOpacity, height: `calc(${menuButtonInfo.bottom + 4}px + 50rpx)` }" />
+    <mescroll-body id="mescrollContainer" :height="`${mescrollHeight}px`" :sticky="true" :down="downOption"
+      :up="upOption" :fixed="false" :is-show-empty="true" @init="mescrollInit" @down="downCallback" @up="upCallback"
+      @emptyclick="reload">
+      <!-- 顶部区域 -->
+      <view class="income-header"
+        :style="{ background: `url(${getImageUrl('@img/income/income-bg.png')})`, backgroundSize: '100% 110%', backgroundPosition: 'left bottom', backgroundRepeat: 'no-repeat' }">
+        <view class="income-header-avatar-info"
+          :style="{ paddingTop: `calc(${safeAreaInsets.top}px + ${menuButtonInfo.height}px)` }">
+          <view class="income-header-balance">
+            当前账户余额(元)
+          </view>
+          <view class="income-header-balance-num">
+            <view class="income-header-balance-num-amount">
+              {{ accountCountData?.balance || 0 }}
+            </view>
+            <view class="income-header-balance-num-btns">
+              <!-- <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>
+        </view>
+        <view class="income-header-tips">
+          <view class="income-header-tips-item">
+            <view class="income-header-tips-item-num">
+              {{ accountCountData?.totalCommission || 0 }}
+            </view>
+            <view class="income-header-tips-item-des">
+              累计收益
+            </view>
+          </view>
+          <view class="income-header-tips-item">
+            <view class="income-header-tips-item-num">
+              {{ accountCountData?.unsettledAmount || 0 }}
+            </view>
+            <view class="income-header-tips-item-des">
+              待结算
+            </view>
+          </view>
+          <view class="income-header-tips-item">
+            <view class="income-header-tips-item-num">
+              {{ 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?.bankNumber || 0 }}
+            </view>
+            <view class="income-header-tips-item-des">
+              银行卡
+            </view>
+          </view>
+        </view>
+      </view>
+      <!-- 公告 -->
+      <view v-if="isUnlock" class="income-header-notice">
+        <view class="income-header-notice-icon">
+          <image :src="getImageUrl('@img/income/notice.png')" mode="aspectFit" />
+        </view>
+        <view class="income-header-notice-content">
+          在考核周期内未达标已锁定收益,<text>前往查看~</text>
+        </view>
+      </view>
+      <!-- 结算筛选 -->
+      <view class="income-header-menu-filter"
+        :style="{ marginTop: isUnlock ? '24rpx' : '-50rpx', top: `${menuButtonInfo.bottom + 4}px` }">
+        <view class="income-header-menu-filter-item" :class="[activeIndex === 0 ? 'active' : '']" @click="tabChange(0)">
+          待结算
+        </view>
+        <view class="income-header-menu-filter-item" :class="[activeIndex === 1 ? 'active' : '']" @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)` }">
+        <!-- 已提现使用日期范围选择 -->
+        <template v-if="activeIndex === 2">
+          <view class="income-menu-time-filter-text" @click="filterTimeRef?.openPopup()">
+            <text>{{ formatDateRangeDisplay() }}</text>
+            <up-icon name="arrow-down" color="#666666" size="14" />
+          </view>
+        </template>
+        <!-- 待结算/已结算使用年月选择 -->
+        <template v-else>
+          <view class="income-menu-time-filter-text" @click="pickerShow = true">
+            <text>{{ changtime(pickerDate) }}</text>
+            <up-icon name="arrow-down" color="#666666" size="14" />
+            <up-datetime-picker v-model="pickerDate" :show="pickerShow" mode="year-month" close-on-click-overlay
+              @confirm="pickerConfirm" @cancel="pickerShow = false" />
+          </view>
+        </template>
+        <view>
+          合计:¥{{ totalAmount }}
+        </view>
+      </view>
+      <view class="income-menu-list">
+        <view v-for="item in incomeData" :key="item.userId" class="income-content-item">
+          <income-item :data="item" :type="activeIndex" @itemClick="handleItemClick" />
+        </view>
+        <!-- 滚动区域内的loading遮罩 -->
+        <view v-if="showLoading && loading" class="loading-mask">
+          <up-loading-icon color="#ed6b66" text="加载中" />
+        </view>
+      </view>
+    </mescroll-body>
+  </view>
+  <FilterTime ref="filterTimeRef" v-model="dateRangeValue" @confirm="handleDateRangeChange" />
+  <!-- <IncomeShareInfo ref="incomeShareInfoRef" :type="activeIndex" /> -->
+</template>
+
+<style lang="scss" scoped>
+.profile-container {
+  background-color: #f5f5f5;
+  line-height: 1;
+  display: flex;
+  flex-direction: column;
+
+  /* 添加这行,设置容器高度为视口高度 */
+
+  .income-header {
+    height: 526rpx;
+
+    .income-header-avatar-info {
+      display: flex;
+      flex-direction: column;
+      gap: 30rpx;
+      padding: 0 24rpx;
+
+      .income-header-balance {
+        font-weight: 400;
+        font-size: 26rpx;
+        color: #ffffff;
+      }
+
+      .income-header-balance-num {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+
+        .income-header-balance-num-amount {
+          font-weight: 500;
+          font-size: 65rpx;
+          color: #ffffff;
+        }
+
+        .income-header-balance-num-btns {
+          display: flex;
+          gap: 15rpx;
+
+          .income-header-balance-num-btn {
+            padding: 20rpx 40rpx 15rpx;
+            border-radius: 33rpx;
+            font-weight: 400;
+            font-size: 26rpx;
+
+            &.js {
+              background: #da4c47;
+              color: #ffffff;
+            }
+
+            &.tx {
+              background: radial-gradient(
+                  0% 0% at 0% 0%,
+                  #f8e1b4 0%,
+                  #f9efc7 100%
+                ),
+                #da4c47;
+              color: #ed6b66;
+            }
+          }
+        }
+      }
+    }
+
+    .income-header-tips {
+      height: 124rpx;
+      display: flex;
+      background: linear-gradient(114deg, #f67873, #fb847f, #f67873);
+      border-radius: 10rpx;
+      margin: 47rpx 24rpx 0 24rpx;
+
+      .income-header-tips-item {
+        flex: 1;
+        display: flex;
+        flex-direction: column;
+        justify-content: center;
+        align-items: center;
+        gap: 15rpx;
+        font-weight: 500;
+        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;
+        // }
+
+        .income-header-tips-item-num {
+          font-weight: bold;
+        }
+
+        .income-header-tips-item-des {
+          font-weight: 400;
+          font-size: 24rpx;
+          color: #ffffff;
+        }
+      }
+    }
+  }
+
+  .income-header-notice {
+    margin: -50rpx 24rpx 24rpx 24rpx;
+    background: #ffffff;
+    border-radius: 10rpx;
+    display: flex;
+    align-items: center;
+    gap: 20rpx;
+    padding: 28rpx 20rpx;
+
+    .income-header-notice-icon {
+      width: 52rpx;
+      height: 35rpx;
+
+      image {
+        width: 100%;
+        height: 100%;
+      }
+    }
+
+    .income-header-notice-content {
+      font-weight: 400;
+      font-size: 24rpx;
+      color: #333333;
+
+      text {
+        color: #c52d27;
+      }
+    }
+  }
+
+  // 新增:吸顶区域上方的覆盖元素
+  .income-header-overlay {
+    position: fixed;
+    top: 0;
+    left: 0;
+    right: 0;
+    background-color: #ed6b66;
+    border-radius: 10rpx 10rpx 0rpx 0rpx;
+    z-index: 99;
+    pointer-events: none; // 确保不影响点击事件
+  }
+
+  .income-header-menu-filter {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    font-weight: 500;
+    font-size: 32rpx;
+    color: #333333;
+    height: 90rpx;
+    background: #ffffff;
+    box-shadow: 0rpx 3rpx 7rpx 0rpx rgba(213, 213, 213, 0.29);
+    border-radius: 10rpx 10rpx 0rpx 0rpx;
+    position: sticky;
+    top: 0;
+    z-index: 100;
+
+    .income-header-menu-filter-item {
+      flex: 1;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      position: relative;
+      height: 100%;
+
+      &.active {
+        color: #ed6b66;
+
+        &:after {
+          content: "";
+          position: absolute;
+          bottom: 0;
+          left: 50%;
+          transform: translateX(-50%);
+          width: 67rpx;
+          height: 6rpx;
+          background: #ed6b66;
+          border-radius: 3rpx;
+        }
+      }
+    }
+  }
+
+  .income-menu-time-filter {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    font-weight: 400;
+    font-size: 24rpx;
+    color: #333333;
+    padding: 30rpx 20rpx 14rpx;
+    background: #ffffff;
+    margin-top: 4rpx;
+    position: sticky;
+    z-index: 100;
+
+    .income-menu-time-filter-text {
+      font-weight: 400;
+      font-size: 26rpx;
+      color: #000000;
+      display: flex;
+      align-items: center;
+      gap: 12rpx;
+    }
+  }
+
+  .income-menu-list {
+    background: #ffffff;
+    // flex: 1;
+    display: flex;
+    flex-direction: column;
+    position: relative;
+
+    #mescrollContainer {
+      height: 100%;
+      flex: 1;
+    }
+
+    .income-content-item {
+      margin-top: 20rpx;
+    }
+
+    // loading遮罩样式
+    .loading-mask {
+      position: absolute;
+      top: 0;
+      left: 0;
+      right: 0;
+      bottom: 0;
+      background-color: rgba(255, 255, 255, 0.8);
+      display: flex;
+      flex-direction: row;
+      flex-wrap: nowrap;
+      justify-content: center;
+      align-items: end;
+      z-index: 999;
+    }
+
+    .loading-spinner {
+      width: 40rpx;
+      height: 40rpx;
+      border: 4rpx solid #e0e0e0;
+      border-top-color: #ed6b66;
+      border-radius: 50%;
+      animation: spin 1s linear infinite;
+    }
+
+    .loading-text {
+      margin-left: 16rpx;
+      font-size: 24rpx;
+      color: #666;
+    }
+
+    @keyframes spin {
+      to {
+        transform: rotate(360deg);
+      }
+    }
+  }
+
+  .income-header-menu-filter,
+  .income-menu-time-filter,
+  .income-menu-list {
+    margin-left: 24rpx;
+    margin-right: 23rpx;
+  }
+
+  :deep(.mescroll-empty) {
+    width: auto;
+    margin-left: 23rpx;
+    margin-left: 24rpx;
+    background-color: #fff;
+    border-bottom-left-radius: 10rpx;
+    border-bottom-right-radius: 10rpx;
+  }
+}
+</style>

+ 486 - 540
src/pages/my/my.vue

@@ -1,593 +1,539 @@
 <script lang="ts" setup>
-import { useRequest } from 'alova/client'
-import { storeToRefs } from 'pinia'
-import { computed, ref, watch } from 'vue'
-import { getCouponSituation } from '@/api/home'
-import { getCouponIssuerApplyById, getShareState } from '@/api/me'
-import { useShare } from '@/hooks/useShare'
-import { useUserStore } from '@/store'
-import { useInviterStore } from '@/store/inviter'
-import { useTokenStore } from '@/store/token'
-import { changtime, safeAreaInsets } from '@/utils'
-import { getImageUrl } from '@/utils/imageUtil'
-import { delayLoadingMiddleware } from '@/http/tools/delayLoadingMiddleware'
+import { useRequest } from "alova/client";
+import { ref, computed } from "vue";
+import { storeToRefs } from "pinia";
+import { getImageUrl } from "@/utils/imageUtil";
+import { delayLoadingMiddleware } from "@/http/tools/delayLoadingMiddleware";
+import { useUserStore } from "@/store";
+import { useInviterStore } from "@/store/inviter";
+import { useTokenStore } from "@/store/token";
 
-definePage({
-    style: {
-        navigationBarTitleText: '我的',
-        navigationStyle: 'custom',
-    },
-})
+import { getCouponSituation } from "@/api/home";
 
-const userStore = useUserStore()
-const tokenStore = useTokenStore()
-const inviterStore = useInviterStore()
+definePage({
+  style: {
+    navigationStyle: "custom",
+    navigationBarTitleText: "我的",
+  },
+});
+const userStore = useUserStore();
+const tokenStore = useTokenStore();
+const inviterStore = useInviterStore();
 // 使用storeToRefs解构userInfo
-const { userInfo } = storeToRefs(userStore)
-const { hasLogin } = storeToRefs(tokenStore)
-const deadline = ref(Date.now())
-
-// 用户优惠券统计数据
-const { send: getCouponSituationRequest, data: couponSituationData, loading: couponSituationLoading } = useRequest(getCouponSituation, {
-    immediate: false,
-    middleware: delayLoadingMiddleware(0, 500)
-})
-const { send: getShareStateRequest, data: shareStateData, loading: shareStateLoading } = useRequest(getShareState, {
-    immediate: false,
-    middleware: delayLoadingMiddleware(0, 500)
-})
+const { userInfo } = storeToRefs(userStore);
+const { hasLogin } = storeToRefs(tokenStore);
+const deadline = ref(Date.now());
+
+//滚动
+const scrollTop = ref(0);
+onPageScroll((e) => {
+  scrollTop.value = e.scrollTop;
+});
+
+const form = ref({});
+const visibleNoticeList = computed(() => {
+  const data = form.value || {};
+  return [
+    {
+      title: "发券收益(元)",
+      content: data.totalIncome || 0,
+      page: "/pages-C/income/incomeCumulative",
+    },
+    {
+      title: "邀新收益(元)",
+      content: data.totalWithdraw || 0,
+      page: "/pages-C/income/incomeMoney",
+    },
+    {
+      title: "核销收益(元)",
+      content: data.bankCards || 0,
+      page: "/pages-C/my/cumulativeGroup",
+    },
+    {
+      title: "累计提现(元)",
+      content: data.inviteIncome || 0,
+      page: "/pages-C/income/incomeInvitation",
+    },
+  ];
+});
+
+const inviteStatsList = computed(() => {
+  const data = form.value || {};
+  return [
+    {
+      title: "今日邀请",
+      content: data.totalIncome || 0,
+      page: "/pages-C/my/myTodayInvitation",
+    },
+    {
+      title: "累计邀请",
+      content: data.totalWithdraw || 0,
+      page: "/pages-C/my/myCumulativeGroup",
+    },
+    {
+      title: "累计团员",
+      content: data.bankCards || 0,
+      page: "/pages-C/my/myCumulativeGroup",
+    },
+  ];
+});
+
+const inviteLineList = computed(() => {
+  const data = form.value || {};
+  return [
+    {
+      title: "累计发券",
+      content: data.totalIncome || 0,
+      page: "/pages-C/my/sendCoupon",
+    },
+    {
+      title: "累计核销",
+      content: data.totalWithdraw || 0,
+      page: "/pages-C/income/incomeMoney",
+    },
+    {
+      title: "累计未核销",
+      content: data.bankCards || 0,
+      page: "/pages-C/my/cumulativeGroup",
+    },
+    {
+      title: "累计领取人数",
+      content: data.bankCards || 0,
+      page: "/pages-C/my/sendRecipientsNumber",
+    },
+  ];
+});
 
 onLoad(async () => {
-    // 登录后查询收益数据
-    if (hasLogin.value) {
-        await getCouponSituationRequest({ queryType: 'time' })
-        await getShareStateRequest()
-        deadline.value = Date.now()
-    }
-})
+  // 登录后查询收益数据
+  if (hasLogin.value) {
+    await getCouponSituationRequest({ queryType: "time" });
+    deadline.value = Date.now();
+  }
+});
 
 onShow(async () => {
-    console.log('登录判断:', hasLogin.value)
-    // 进入页面后刷新用户信息
-    if (hasLogin.value) {
-        await userStore.fetchUserInfo()
-    }
-})
+  console.log("登录判断:", hasLogin.value);
+  // 进入页面后刷新用户信息
+  if (hasLogin.value) {
+    await userStore.fetchUserInfo();
+  }
+});
 
-function handleLogout() {
-    uni.showModal({
-        title: '提示',
-        content: '确定要退出登录吗?',
-        success: async (res) => {
-            if (res.confirm) {
-                // 清空用户信息
-                await useTokenStore().logout()
-                // 执行退出登录逻辑
-                uni.showToast({
-                    title: '退出登录成功',
-                    icon: 'success',
-                    mask: true,
-                    duration: 2000,
-                    complete: () => {
-                        // #ifdef MP-WEIXIN
-                        // 微信小程序,去首页
-                        uni.reLaunch({ url: '/pages/home/index' })
-                        // #endif
-                        // #ifndef MP-WEIXIN
-                        // 非微信小程序,去登录页
-                        // uni.navigateTo({ url: LOGIN_PAGE })
-                        // #endif
-                    }
-                })
-            }
-        },
-    })
-}
-
-// 顶部导航栏高度,设置banner位置
-const navigationBarHeight = ref(0)
-// 头像默认图片
-const defaultAvatar = '../../static/images/me/me-bg.png'
-// 计算头像显示
-const avatarDisplay = computed(() => {
-    return userInfo.value?.avatar || defaultAvatar
-})
-// #ifdef MP-WEIXIN
-function getNavigationBarHeight() {
-    uni.getSystemInfo({
-        success: (res) => {
-            const menuButtonInfo = uni.getMenuButtonBoundingClientRect()
-            console.log('顶部导航栏高度:', res.statusBarHeight, menuButtonInfo)
-            // 顶部导航栏高度 = 状态栏高度 + 胶囊的高度
-            navigationBarHeight.value = res.statusBarHeight + menuButtonInfo.height
-        },
-    })
-}
-getNavigationBarHeight()
-// #endif
+// 用户优惠券统计数据
+const {
+  send: getCouponSituationRequest,
+  data: couponSituationData,
+  loading: couponSituationLoading,
+} = useRequest(getCouponSituation, {
+  immediate: false,
+  middleware: delayLoadingMiddleware(0, 500),
+});
 
-const show = ref(false)
-const refreshing = ref(false)
-function close() {
-    show.value = false
-}
-function open() {
-    show.value = true
-}
 async function menuClick(page, otherJs?: () => Promise<boolean>) {
-    if (otherJs && typeof otherJs === 'function') {
-        const execRes = await otherJs(page)
-        if (execRes === false) {
-            return
-        }
+  if (otherJs && typeof otherJs === "function") {
+    const execRes = await otherJs(page);
+    if (execRes === false) {
+      return;
     }
-    uni.navigateTo({
-        url: `/pages-A/${page}/index?couponSituation=${JSON.stringify(couponSituationData.value)}`,
-    })
+  }
+  uni.navigateTo({
+    url: `/pages-A/${page}/index?couponSituation=${JSON.stringify(
+      couponSituationData.value
+    )}`,
+  });
 }
 
 // 判断是否是发券人
 function isCouponIssuer(currentPage?: string) {
-    // 发券人相关页面
-    const validPages = ['myInviter', 'shareFriend', 'invitePage']
-    // 判断跳转页面是否是发券人相关页面
-    if (!validPages.includes(currentPage)) {
-        return Promise.resolve(true)
-    }
-    // 是否已经成为发券人
-    if (userStore.isPassIssuer()) {
-        return Promise.resolve(true)
-    }
-    else {
-        uni.showModal({
-            title: '提示',
-            content: '您还不是发券人,请先申请成为发券人',
-            showCancel: false,
-        })
-        return Promise.resolve(false)
-    }
+  // 发券人相关页面
+  const validPages = ["myInviter", "shareFriend", "invitePage"];
+  // 判断跳转页面是否是发券人相关页面
+  if (!validPages.includes(currentPage)) {
+    return Promise.resolve(true);
+  }
+  // 是否已经成为发券人
+  if (userStore.isPassIssuer()) {
+    return Promise.resolve(true);
+  } else {
+    uni.showModal({
+      title: "提示",
+      content: "您还不是发券人,请先申请成为发券人",
+      showCancel: false,
+    });
+    return Promise.resolve(false);
+  }
 }
 
 // 展开弹窗
 function watchUserStatus() {
-    if (userInfo.value.status === '0') {
-        open()
-        return Promise.resolve(false)
-    }
-    return Promise.resolve(true)
+  if (userInfo.value.status === "0") {
+    open();
+    return Promise.resolve(false);
+  }
+  return Promise.resolve(true);
 }
 
 async function getInviteInfo(currentPage?: string) {
-    const isCouponIssuerRes = await isCouponIssuer(currentPage)
-    if (!isCouponIssuerRes) {
-        return isCouponIssuerRes
-    }
-    const isInviter = await inviterStore.fetchInviterInfo()
-    if (!isInviter) {
-        uni.showModal({
-            title: '提示',
-            content: '您没有上级邀请人,无法查看邀请信息',
-            showCancel: false,
-        })
-    }
-    return isInviter
+  const isCouponIssuerRes = await isCouponIssuer(currentPage);
+  if (!isCouponIssuerRes) {
+    return isCouponIssuerRes;
+  }
+  const isInviter = await inviterStore.fetchInviterInfo();
+  if (!isInviter) {
+    uni.showModal({
+      title: "提示",
+      content: "您没有上级邀请人,无法查看邀请信息",
+      showCancel: false,
+    });
+  }
+  return isInviter;
 }
 
-async function onRefresh() {
-    refreshing.value = true
-    await userStore.fetchUserInfo()
-    await getCouponSituationRequest({ queryType: 'time' })
-    await getShareStateRequest()
-    deadline.value = Date.now()
-    refreshing.value = false
+function listInfo(item) {
+  if (item.page) {
+    uni.navigateTo({
+      url: item.page,
+    });
+  }
 }
-
-// 创建分享hook实例
-const { getShareConfig, getTimelineShareConfig } = useShare()
-
-// #ifdef MP-WEIXIN
-// 分享给好友生命周期函数
-onShareAppMessage(async (options) => {
-    return await getShareConfig()
-})
-
-// 分享到朋友圈生命周期函数
-onShareTimeline(async () => {
-    return await getTimelineShareConfig()
-})
-// #endif
 </script>
 
 <template>
-    <view class="profile-container" :style="{ height: `calc(100vh - 100rpx - ${safeAreaInsets?.bottom || 0}rpx)` }">
-        <up-pull-refresh :refreshing="refreshing" :threshold="60" @refresh="onRefresh">
-            <!-- 顶部区域 -->
-            <view class="me-header"
-                :style="{ background: `url(${getImageUrl('@img/me/me-bg.png')})`, backgroundSize: 'cover', backgroundRepeat: 'no-repeat', backgroundPosition: 'center center' }">
-                <view class="me-header-avatar-info" :style="{ paddingTop: `23rpx` }">
-                    <up-image :src="avatarDisplay" width="128rpx" height="128rpx" shape="circle" />
-                    <view class="me-header-info">
-                        <view class="me-header-name">
-                            <!-- #ifdef MP-WEIXIN -->
-                            <!-- <input v-model="userInfo.value.nickname" type="nickname" :disabled="true" placeholder="用户昵称"
-                                placeholder-style="color: #fff; opacity: 0.7;" class="nickname-input"> -->
-                            <text class="nickname-input">{{ userInfo.nickname || '用户' }}</text>
-                            <!-- #endif -->
-                            <!-- #ifndef MP-WEIXIN -->
-                            {{ userInfo.value?.nickname || '用户' }}
-                            <!-- #endif -->
-                        </view>
-                    </view>
-                </view>
-                <view class="me-header-tips">
-                    <view class="me-header-tips-row header-data-time">
-                        <view class="desc-time">
-                            <up-loading-icon v-if="couponSituationLoading" size="23rpx" :loading="true" color="#fff" />
-                            <up-icon v-else name="info-circle" size="23rpx" color="#fff" />
-                            <view v-if="couponSituationLoading || shareStateLoading" class="desc-text">
-                                数据加载中...
-                            </view>
-                            <view v-else class="desc-text">
-                                数据截止到&nbsp;&nbsp;{{ changtime(deadline, 'YYYY-MM-DD HH:mm:ss') }}
-                            </view>
-                        </view>
-                    </view>
-                    <view class="me-header-tips-row header-data-info">
-                        <view class="me-header-tips-item">
-                            <view class="me-header-tips-item-num">
-                                {{ shareStateData?.todayShareCount || 0 }}
-                            </view>
-                            <view class="me-header-tips-item-des">
-                                今日邀请
-                            </view>
-                        </view>
-                        <view class="me-header-tips-item">
-                            <view class="me-header-tips-item-num">
-                                {{ shareStateData?.totalShareCount || 0 }}
-                            </view>
-                            <view class="me-header-tips-item-des">
-                                累计邀请
-                            </view>
-                        </view>
-                        <view class="me-header-tips-item" @click="menuClick('invitePage')">
-                            <view class="me-header-tips-item-num">
-                                {{ shareStateData?.totalTeamMemberCount || 0 }}
-                            </view>
-                            <view class="me-header-tips-item-des">
-                                累计团员
-                            </view>
-                            <view class="me-header-tips-item-icon">
-                                <up-icon name="arrow-right" size="21rpx" color="#FFFFFF" />
-                            </view>
-                        </view>
-                    </view>
-                    <view class="me-header-tips-row header-data-info">
-                        <view class="me-header-tips-item">
-                            <view class="me-header-tips-item-num">
-                                {{ couponSituationData?.quantityForComplimentary || 0 }}
-                            </view>
-                            <view class="me-header-tips-item-des">
-                                今日发券
-                            </view>
-                        </view>
-                        <view class="me-header-tips-item">
-                            <view class="me-header-tips-item-num">
-                                {{ couponSituationData?.quantityToBeUsed || 0 }}
-                            </view>
-                            <view class="me-header-tips-item-des">
-                                今日领取
-                            </view>
-                        </view>
-                        <view class="me-header-tips-item" @click="menuClick('incomePage')">
-                            <view class="me-header-tips-item-num">
-                                {{ couponSituationData?.usedQuantity || 0 }}
-                            </view>
-                            <view class="me-header-tips-item-des">
-                                今日使用
-                            </view>
-                            <view class="me-header-tips-item-icon">
-                                <up-icon name="arrow-right" size="21rpx" color="#FFFFFF" />
-                            </view>
-                        </view>
-                    </view>
-                </view>
+  <view class="page-container">
+    <!-- 顶部蓝色渐变背景 -->
+    <view class="page-header-bg" :style="{
+            backgroundImage: `url(${getImageUrl('@img/income/bg.png')})`,
+            backgroundSize: 'cover',
+            backgroundRepeat: 'no-repeat',
+            backgroundPosition: 'center'
+        }">
+    </view>
+
+    <u-navbar title="我的" :autoBack="true" :placeholder="true" :border-bottom="false"
+      :title-style="{ fontWeight: 'bold', color: scrollTop > 1 ? '#000' : '#fff' }"
+      :bgColor="scrollTop > 1 ? '#fff' : 'transparent'" :leftIconColor="scrollTop > 1 ? '#000' : '#fff'"></u-navbar>
+
+    <!-- 页面主内容 -->
+    <view class="main-content">
+      <view class="head-content">
+        <view class='head-box'>
+          <u--image class='head-icon' :src="getImageUrl('@img/me/head.png')" mode="aspectFit" width="140rpx"
+            shape="circle" height="140rpx">
+          </u--image>
+          <view>奔跑的五花肉</view>
+        </view>
+      </view>
+
+      <view class="stats-card">
+        <view class="stats-main">
+          <text class="stats-label">累计收益(元)</text>
+          <text class="stats-value">222</text>
+        </view>
+        <view class="stats-grid">
+          <view v-for="(item, index) in visibleNoticeList" :key="index" class="stats-item" @click='listInfo(item)'>
+            <text class="stats-item-label">{{ item.title }}</text>
+            <text class="stats-item-value">{{ item.content }}</text>
+          </view>
+        </view>
+      </view>
+
+      <view class="number-content">
+        <view class="invite-card">
+          <view class="invite-header">
+            <view class="invite-icon">
+              <u--image :src="getImageUrl('@img/me/user-transmit-filled.png')" mode="aspectFit" width="44rpx"
+                height="44rpx"></u--image>
+            </view>
+            <text class="invite-title">我的邀请</text>
+            <u--image :src="getImageUrl('@img/income/arrow-right.png')" mode="aspectFit" width="40rpx"
+              height="40rpx"></u--image>
+          </view>
+          <view class="invite-stats">
+            <view v-for="(item, index) in inviteStatsList" :key="index" class="invite-stat-item"
+              @click='listInfo(item)'>
+              <text class="invite-stat-label">{{ item.title }}</text>
+              <text class="invite-stat-value">{{ item.content }}</text>
+            </view>
+          </view>
+        </view>
+
+        <view class="invite-card">
+          <view class="invite-header">
+            <view class="invite-icon">
+              <u--image :src="getImageUrl('@img/me/user-transmit-line.png')" mode="aspectFit" width="44rpx"
+                height="44rpx"></u--image>
+            </view>
+            <text class="invite-title">发券统计</text>
+            <u--image :src="getImageUrl('@img/income/arrow-right.png')" mode="aspectFit" width="40rpx"
+              height="40rpx"></u--image>
+          </view>
+          <view class="invite-stats">
+            <view v-for="(item, index) in inviteLineList" :key="index" class="invite-stat-item" @click='listInfo(item)'>
+              <text class="invite-stat-label">{{ item.title }}</text>
+              <text class="invite-stat-value">{{ item.content }}</text>
+            </view>
+          </view>
+        </view>
+
+        <!-- 菜单 -->
+        <view class="me-header-menu">
+          <view class="me-header-menu-item" @click="menuClick('applyForm', watchUserStatus)">
+            <view class="me-header-menu-icon">
+              <image :src="getImageUrl('@img/me/coupon-need.png')" mode="aspectFill" />
+              <view class="me-header-menu-text">
+                申请发券人
+              </view>
+            </view>
+            <u--image :src="getImageUrl('@img/income/arrow-right.png')" mode="aspectFit" width="40rpx"
+              height="40rpx"></u--image>
+          </view>
+          <view class="me-header-menu-item" @click="menuClick('myInviter', getInviteInfo)">
+            <view class="me-header-menu-icon">
+              <image :src="getImageUrl('@img/me/inviter.png')" mode="aspectFill" />
+              <view class="me-header-menu-text">
+                我的邀请人
+              </view>
             </view>
-            <!-- 菜单 -->
-            <view class="me-header-menu">
-                <view class="me-header-menu-item" @click="menuClick('applyForm', watchUserStatus)">
-                    <view class="me-header-menu-icon">
-                        <image :src="getImageUrl('@img/me/coupon-need.png')" mode="aspectFill" />
-                        <view class="me-header-menu-text">
-                            申请发券人
-                        </view>
-                    </view>
-                    <view class="me-header-menu-left">
-                        <up-icon name="arrow-right" color="#979797" size="12" />
-                    </view>
-                </view>
-                <view class="me-header-menu-item" @click="menuClick('invitePage', isCouponIssuer)">
-                    <view class="me-header-menu-icon">
-                        <image :src="getImageUrl('@img/me/invite.png')" mode="aspectFill" />
-                        <view class="me-header-menu-text">
-                            我的邀请
-                        </view>
-                    </view>
-                    <view class="me-header-menu-left">
-                        <up-icon name="arrow-right" color="#979797" size="12" />
-                    </view>
-                </view>
-                <view class="me-header-menu-item" @click="menuClick('myInviter', getInviteInfo)">
-                    <view class="me-header-menu-icon">
-                        <image :src="getImageUrl('@img/me/inviter.png')" mode="aspectFill" />
-                        <view class="me-header-menu-text">
-                            我的邀请人
-                        </view>
-                    </view>
-                    <view class="me-header-menu-left">
-                        <up-icon name="arrow-right" color="#979797" size="12" />
-                    </view>
-                </view>
-                <view class="me-header-menu-item" @click="menuClick('shareFriend', isCouponIssuer)">
-                    <view class="me-header-menu-icon">
-                        <image :src="getImageUrl('@img/me/share.png')" mode="aspectFill" />
-                        <view class="me-header-menu-text">
-                            邀请好友
-                        </view>
-                    </view>
-                    <view class="me-header-menu-left">
-                        <up-icon name="arrow-right" color="#979797" size="12" />
-                    </view>
-                </view>
-                <view class="me-header-menu-item" @click="menuClick('settingPage')">
-                    <view class="me-header-menu-icon">
-                        <image :src="getImageUrl('@img/me/setting.png')" mode="aspectFill" />
-                        <view class="me-header-menu-text">
-                            设置
-                        </view>
-                    </view>
-                    <view class="me-header-menu-left">
-                        <up-icon name="arrow-right" color="#979797" size="12" />
-                    </view>
-                </view>
-                <view class="me-header-menu-item" @click="handleLogout">
-                    <view class="me-header-menu-icon">
-                        <image :src="getImageUrl('@img/me/loginOut.png')" mode="aspectFill" />
-                        <view class="me-header-menu-text">
-                            退出登录
-                        </view>
-                    </view>
-                    <view class="me-header-menu-left">
-                        <up-icon name="arrow-right" color="#979797" size="12" />
-                    </view>
-                </view>
+            <u--image :src="getImageUrl('@img/income/arrow-right.png')" mode="aspectFit" width="40rpx"
+              height="40rpx"></u--image>
+          </view>
+          <view class="me-header-menu-item" @click="menuClick('shareFriend', isCouponIssuer)">
+            <view class="me-header-menu-icon">
+              <image :src="getImageUrl('@img/me/share.png')" mode="aspectFill" />
+              <view class="me-header-menu-text">
+                邀请好友
+              </view>
             </view>
-        </up-pull-refresh>
-        <!-- 申请中的提示框 -->
-        <up-modal content="您已提交申请,请耐心等待!" title="提示" :show="show">
-            <template #confirmButton>
-                <view class="rounded" style="margin-top: 20rpx;" @click="show = false">
-                    我知道了
-                </view>
-            </template>
-        </up-modal>
+            <u--image :src="getImageUrl('@img/income/arrow-right.png')" mode="aspectFit" width="40rpx"
+              height="40rpx"></u--image>
+          </view>
+          <view class="me-header-menu-item" @click="menuClick('settingPage')">
+            <view class="me-header-menu-icon">
+              <image :src="getImageUrl('@img/me/setting.png')" mode="aspectFill" />
+              <view class="me-header-menu-text">
+                设置
+              </view>
+            </view>
+            <u--image :src="getImageUrl('@img/income/arrow-right.png')" mode="aspectFit" width="40rpx"
+              height="40rpx"></u--image>
+          </view>
+        </view>
+      </view>
     </view>
+
+  </view>
 </template>
 
 <style lang="scss" scoped>
-// 提示框
-::v-deep .u-modal {
-    width: 580rpx !important;
-    //   height: 376rpx !important;
-    border-radius: 20rpx !important;
-}
+.page-container {
+  min-height: 100vh;
+  background-color: #f8f8fa;
+
+  // 顶部渐变背景
+  .page-header-bg {
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    height: 460rpx;
+    background: linear-gradient(to bottom, #0879ff, #f8f8fa);
+
+    z-index: 1;
+  }
+
+  ::v-deep .top-nav {
+    box-shadow: none !important;
+  }
+
+  .main-content {
+    position: relative;
+    z-index: 2;
+    margin: 0 20rpx;
+  }
+
+  .head-content {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    margin-top: 18rpx;
+  }
+
+  .head-box {
+    display: flex;
+    flex-direction: column;
+    gap: 24rpx;
+    align-items: center;
+    justify-content: center;
+    color: #fff;
+    font-size: 36rpx;
+    font-weight: 600;
+  }
+
+  .stats-card {
+    width: 100%;
+    background: #ffffff;
+    border-radius: 32rpx;
+    padding: 40rpx 32rpx;
+    margin: 32rpx auto 0;
+    box-sizing: border-box;
+
+    .stats-main {
+      display: flex;
+      flex-direction: column;
+      gap: 12rpx;
+      margin-bottom: 40rpx;
+
+      .stats-label {
+        font-weight: 400;
+        font-size: 24rpx;
+        color: rgba(0, 0, 0, 0.45);
+        line-height: 1;
+      }
+
+      .stats-value {
+        font-weight: 700;
+        font-size: 60rpx;
+        color: #333333;
+        line-height: 1;
+      }
+    }
 
-::v-deep .u-modal__title {
-    font-weight: 500 !important;
-    font-size: 34rpx !important;
-    color: #333333 !important;
-    padding-top: 29rpx !important;
-    padding-bottom: 28rpx !important;
-}
+    .stats-grid {
+      display: grid;
+      grid-template-columns: repeat(4, 1fr);
+      gap: 0;
 
-::v-deep .u-modal__content {
-    border-top: 1px solid #eeeeee;
-    border-bottom: 1px solid #eeeeee;
-    padding-top: 82rpx !important;
-    padding-bottom: 64rpx !important;
-}
+      .stats-item {
+        display: flex;
+        flex-direction: column;
+        gap: 8rpx;
 
-::v-deep .u-modal__content__text {
-    font-weight: 500 !important;
-    font-size: 32rpx !important;
-    color: #333333 !important;
-    text-align: center !important;
-}
+        .stats-item-label {
+          font-weight: 400;
+          font-size: 22rpx;
+          color: #86909c;
+          line-height: 1;
+        }
 
-.rounded {
-    width: 300rpx;
-    line-height: 69rpx;
-    background: linear-gradient(90deg, #ee6b67 0%, #ff7d78 100%);
-    border-radius: 35rpx;
-    font-weight: 400;
-    font-size: 30rpx;
-    color: #ffffff;
-    text-align: center;
-    margin: 0 auto;
-}
+        .stats-item-value {
+          font-weight: 600;
+          font-size: 32rpx;
+          color: #333333;
+          line-height: 1.1;
+        }
+      }
+    }
+  }
+
+  .main-content {
+    position: relative;
+    z-index: 2;
+    margin: 0 20rpx;
+  }
+
+  .number-content {
+    background: #fff;
+    margin-top: 32rpx;
+    border-radius: 32rpx;
+  }
+
+  .invite-card {
+    background: #ffffff;
+    border-radius: 32rpx;
+    padding: 0 0 32rpx;
+    box-sizing: border-box;
+
+    .invite-header {
+      display: flex;
+      align-items: center;
+      padding: 0 32rpx;
+      height: 88rpx;
+      position: relative;
+
+      .invite-icon {
+        width: 44rpx;
+        height: 44rpx;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+      }
+
+      .invite-title {
+        flex: 1;
+        margin-left: 16rpx;
+        font-weight: 400;
+        font-size: 30rpx;
+        color: #000000;
+        line-height: 1;
+      }
+    }
 
-.profile-container {
-    height: calc(100vh - 100rpx);
-    background-color: #f8f8fa;
+    .invite-stats {
+      display: flex;
+      justify-content: space-around;
+      align-items: center;
+      background: rgba(247, 248, 250, 0.7);
+      margin: 0 32rpx;
+      border-radius: 16rpx;
+      height: 134rpx;
 
-    .me-header {
-        height: 746rpx;
+      .invite-stat-item {
         display: flex;
         flex-direction: column;
-        justify-content: center;
+        gap: 8rpx;
         align-items: center;
 
-        .me-header-avatar-info {
-            display: flex;
-            flex-direction: column;
-            justify-content: center;
-            align-items: center;
-            gap: 24rpx;
-
-            .me-header-info {
-                .me-header-name {
-                    font-weight: 500;
-                    font-size: 30rpx;
-                    color: #ffffff;
-                }
-            }
+        .invite-stat-label {
+          font-weight: 400;
+          font-size: 22rpx;
+          color: #86909c;
+          line-height: 1;
         }
 
-        .me-header-tips {
-            margin-top: 46rpx;
-            height: 317rpx;
-            width: 702rpx;
-            display: flex;
-            flex-direction: column;
-            justify-content: center;
-            align-items: center;
-            gap: 20rpx;
-            background: linear-gradient(90deg, #ff7671, #ff8e8a, #ff7671);
-            border-radius: 8rpx;
-
-            .me-header-tips-row {
-                display: flex;
-
-                .desc-time {
-                    width: 100%;
-                    display: flex;
-                    flex-direction: row;
-                    flex-wrap: nowrap;
-                    gap: 6rpx;
-                    justify-content: flex-end;
-                    padding-right: 21rpx;
-
-                    font-weight: 400;
-                    font-size: 20rpx;
-                    color: #ffffff;
-                    line-height: 20rpx;
-                }
-
-                .me-header-tips-item {
-                    flex: 1;
-                    display: flex;
-                    flex-direction: column;
-                    justify-content: center;
-                    align-items: center;
-                    gap: 15rpx;
-                    font-weight: 500;
-                    font-size: 34rpx;
-                    color: #ffffff;
-                    line-height: 1;
-
-                    .me-header-tips-item-num {
-                        font-weight: bold;
-                    }
-
-                    .me-header-tips-item-des {
-                        font-weight: 400;
-                        font-size: 24rpx;
-                        color: #ffffff;
-                    }
-                }
-            }
-
-            .header-data-time {
-                align-self: flex-end;
-            }
-
-            .me-header-tips-row.header-data-info {
-                align-self: center;
-                width: 642rpx;
-                height: 96rpx;
-                background: rgba($color: #851b17, $alpha: 0.1);
-                border-radius: 10rpx;
-
-                .me-header-tips-item {
-                    font-weight: 400;
-                    font-size: 23rpx;
-                    color: #ffffff;
-                    position: relative;
-
-                    &:not(:last-child):after {
-                        content: '';
-                        position: absolute;
-                        top: 50%;
-                        right: 0;
-                        transform: translateY(-50%);
-                        width: 2px;
-                        height: 40rpx;
-                        background: #ffffff;
-                    }
-
-                    .me-header-tips-item-icon {
-                        position: absolute;
-                        top: 50%;
-                        right: 21rpx;
-                        width: 21rpx;
-                        height: 21rpx;
-                        font-weight: bolder;
-                        transform: translateY(-50%);
-                    }
-                }
-            }
+        .invite-stat-value {
+          font-weight: 600;
+          font-size: 32rpx;
+          color: #333333;
+          line-height: 1.1;
         }
+      }
     }
+  }
+  .me-header-menu {
+    background: #ffffff;
+    padding: 0 32rpx;
+    box-sizing: border-box;
+
+    .me-header-menu-item {
+      height: 88rpx;
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      border-bottom: 1px solid #eeeeee;
+
+      &:last-child {
+        border-bottom: none;
+      }
+
+      .me-header-menu-icon {
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        gap: 13rpx;
 
-    .me-header-menu {
-        background: #ffffff;
-        border-radius: 10rpx;
-        margin: -50rpx 24rpx 0;
-        padding: 0 20rpx;
-
-        .me-header-menu-item {
-            height: 88rpx;
-            display: flex;
-            justify-content: space-between;
-            align-items: center;
-            border-bottom: 1px solid #eeeeee;
-
-            &:last-child {
-                border-bottom: none;
-            }
-
-            .me-header-menu-icon {
-                display: flex;
-                justify-content: center;
-                align-items: center;
-                gap: 13rpx;
-
-                image {
-                    width: 40rpx;
-                    height: 40rpx;
-                }
-
-                .me-header-menu-text {
-                    font-weight: 400;
-                    font-size: 26rpx;
-                    color: #333333;
-                }
-            }
-
-            .me-header-menu-left {}
+        image {
+          width: 40rpx;
+          height: 40rpx;
         }
-    }
-}
 
-/* 昵称输入框样式 */
-.nickname-input {
-    width: 100%;
-    height: 100%;
-    font-weight: 500;
-    font-size: 30rpx;
-    color: #ffffff;
-    background: transparent;
-    border: none;
-    outline: none;
-    text-align: center;
+        .me-header-menu-text {
+          font-weight: 400;
+          font-size: 30rpx;
+          color: #000;
+        }
+      }
+    }
+  }
 }
-</style>
+</style>

+ 603 - 0
src/pages/my/myCopy.vue

@@ -0,0 +1,603 @@
+<script lang="ts" setup>
+import { useRequest } from "alova/client";
+import { storeToRefs } from "pinia";
+import { computed, ref, watch } from "vue";
+import { getCouponSituation } from "@/api/home";
+import { getCouponIssuerApplyById, getShareState } from "@/api/me";
+import { useShare } from "@/hooks/useShare";
+import { useUserStore } from "@/store";
+import { useInviterStore } from "@/store/inviter";
+import { useTokenStore } from "@/store/token";
+import { changtime, safeAreaInsets } from "@/utils";
+import { getImageUrl } from "@/utils/imageUtil";
+import { delayLoadingMiddleware } from "@/http/tools/delayLoadingMiddleware";
+
+definePage({
+  style: {
+    navigationBarTitleText: "我的",
+    navigationStyle: "custom",
+  },
+});
+
+const userStore = useUserStore();
+const tokenStore = useTokenStore();
+const inviterStore = useInviterStore();
+// 使用storeToRefs解构userInfo
+const { userInfo } = storeToRefs(userStore);
+const { hasLogin } = storeToRefs(tokenStore);
+const deadline = ref(Date.now());
+
+// 用户优惠券统计数据
+const {
+  send: getCouponSituationRequest,
+  data: couponSituationData,
+  loading: couponSituationLoading,
+} = useRequest(getCouponSituation, {
+  immediate: false,
+  middleware: delayLoadingMiddleware(0, 500),
+});
+const {
+  send: getShareStateRequest,
+  data: shareStateData,
+  loading: shareStateLoading,
+} = useRequest(getShareState, {
+  immediate: false,
+  middleware: delayLoadingMiddleware(0, 500),
+});
+
+onLoad(async () => {
+  // 登录后查询收益数据
+  if (hasLogin.value) {
+    await getCouponSituationRequest({ queryType: "time" });
+    await getShareStateRequest();
+    deadline.value = Date.now();
+  }
+});
+
+onShow(async () => {
+  console.log("登录判断:", hasLogin.value);
+  // 进入页面后刷新用户信息
+  if (hasLogin.value) {
+    await userStore.fetchUserInfo();
+  }
+});
+
+function handleLogout() {
+  uni.showModal({
+    title: "提示",
+    content: "确定要退出登录吗?",
+    success: async (res) => {
+      if (res.confirm) {
+        // 清空用户信息
+        await useTokenStore().logout();
+        // 执行退出登录逻辑
+        uni.showToast({
+          title: "退出登录成功",
+          icon: "success",
+          mask: true,
+          duration: 2000,
+          complete: () => {
+            // #ifdef MP-WEIXIN
+            // 微信小程序,去首页
+            uni.reLaunch({ url: "/pages/home/index" });
+            // #endif
+            // #ifndef MP-WEIXIN
+            // 非微信小程序,去登录页
+            // uni.navigateTo({ url: LOGIN_PAGE })
+            // #endif
+          },
+        });
+      }
+    },
+  });
+}
+
+// 顶部导航栏高度,设置banner位置
+const navigationBarHeight = ref(0);
+// 头像默认图片
+const defaultAvatar = "../../static/images/me/me-bg.png";
+// 计算头像显示
+const avatarDisplay = computed(() => {
+  return userInfo.value?.avatar || defaultAvatar;
+});
+// #ifdef MP-WEIXIN
+function getNavigationBarHeight() {
+  uni.getSystemInfo({
+    success: (res) => {
+      const menuButtonInfo = uni.getMenuButtonBoundingClientRect();
+      console.log("顶部导航栏高度:", res.statusBarHeight, menuButtonInfo);
+      // 顶部导航栏高度 = 状态栏高度 + 胶囊的高度
+      navigationBarHeight.value = res.statusBarHeight + menuButtonInfo.height;
+    },
+  });
+}
+getNavigationBarHeight();
+// #endif
+
+const show = ref(false);
+const refreshing = ref(false);
+function close() {
+  show.value = false;
+}
+function open() {
+  show.value = true;
+}
+async function menuClick(page, otherJs?: () => Promise<boolean>) {
+  if (otherJs && typeof otherJs === "function") {
+    const execRes = await otherJs(page);
+    if (execRes === false) {
+      return;
+    }
+  }
+  uni.navigateTo({
+    url: `/pages-A/${page}/index?couponSituation=${JSON.stringify(
+      couponSituationData.value
+    )}`,
+  });
+}
+
+// 判断是否是发券人
+function isCouponIssuer(currentPage?: string) {
+  // 发券人相关页面
+  const validPages = ["myInviter", "shareFriend", "invitePage"];
+  // 判断跳转页面是否是发券人相关页面
+  if (!validPages.includes(currentPage)) {
+    return Promise.resolve(true);
+  }
+  // 是否已经成为发券人
+  if (userStore.isPassIssuer()) {
+    return Promise.resolve(true);
+  } else {
+    uni.showModal({
+      title: "提示",
+      content: "您还不是发券人,请先申请成为发券人",
+      showCancel: false,
+    });
+    return Promise.resolve(false);
+  }
+}
+
+// 展开弹窗
+function watchUserStatus() {
+  if (userInfo.value.status === "0") {
+    open();
+    return Promise.resolve(false);
+  }
+  return Promise.resolve(true);
+}
+
+async function getInviteInfo(currentPage?: string) {
+  const isCouponIssuerRes = await isCouponIssuer(currentPage);
+  if (!isCouponIssuerRes) {
+    return isCouponIssuerRes;
+  }
+  const isInviter = await inviterStore.fetchInviterInfo();
+  if (!isInviter) {
+    uni.showModal({
+      title: "提示",
+      content: "您没有上级邀请人,无法查看邀请信息",
+      showCancel: false,
+    });
+  }
+  return isInviter;
+}
+
+async function onRefresh() {
+  refreshing.value = true;
+  await userStore.fetchUserInfo();
+  await getCouponSituationRequest({ queryType: "time" });
+  await getShareStateRequest();
+  deadline.value = Date.now();
+  refreshing.value = false;
+}
+
+// 创建分享hook实例
+const { getShareConfig, getTimelineShareConfig } = useShare();
+
+// #ifdef MP-WEIXIN
+// 分享给好友生命周期函数
+onShareAppMessage(async (options) => {
+  return await getShareConfig();
+});
+
+// 分享到朋友圈生命周期函数
+onShareTimeline(async () => {
+  return await getTimelineShareConfig();
+});
+// #endif
+</script>
+
+<template>
+  <view class="profile-container" :style="{ height: `calc(100vh - 100rpx - ${safeAreaInsets?.bottom || 0}rpx)` }">
+    <up-pull-refresh :refreshing="refreshing" :threshold="60" @refresh="onRefresh">
+      <!-- 顶部区域 -->
+      <view class="me-header"
+        :style="{ background: `url(${getImageUrl('@img/me/me-bg.png')})`, backgroundSize: 'cover', backgroundRepeat: 'no-repeat', backgroundPosition: 'center center' }">
+        <view class="me-header-avatar-info" :style="{ paddingTop: `23rpx` }">
+          <up-image :src="avatarDisplay" width="128rpx" height="128rpx" shape="circle" />
+          <view class="me-header-info">
+            <view class="me-header-name">
+              <!-- #ifdef MP-WEIXIN -->
+              <!-- <input v-model="userInfo.value.nickname" type="nickname" :disabled="true" placeholder="用户昵称"
+                                placeholder-style="color: #fff; opacity: 0.7;" class="nickname-input"> -->
+              <text class="nickname-input">{{ userInfo.nickname || '用户' }}</text>
+              <!-- #endif -->
+              <!-- #ifndef MP-WEIXIN -->
+              {{ userInfo.value?.nickname || '用户' }}
+              <!-- #endif -->
+            </view>
+          </view>
+        </view>
+        <view class="me-header-tips">
+          <view class="me-header-tips-row header-data-time">
+            <view class="desc-time">
+              <up-loading-icon v-if="couponSituationLoading" size="23rpx" :loading="true" color="#fff" />
+              <up-icon v-else name="info-circle" size="23rpx" color="#fff" />
+              <view v-if="couponSituationLoading || shareStateLoading" class="desc-text">
+                数据加载中...
+              </view>
+              <view v-else class="desc-text">
+                数据截止到&nbsp;&nbsp;{{ changtime(deadline, 'YYYY-MM-DD HH:mm:ss') }}
+              </view>
+            </view>
+          </view>
+          <view class="me-header-tips-row header-data-info">
+            <view class="me-header-tips-item">
+              <view class="me-header-tips-item-num">
+                {{ shareStateData?.todayShareCount || 0 }}
+              </view>
+              <view class="me-header-tips-item-des">
+                今日邀请
+              </view>
+            </view>
+            <view class="me-header-tips-item">
+              <view class="me-header-tips-item-num">
+                {{ shareStateData?.totalShareCount || 0 }}
+              </view>
+              <view class="me-header-tips-item-des">
+                累计邀请
+              </view>
+            </view>
+            <view class="me-header-tips-item" @click="menuClick('invitePage')">
+              <view class="me-header-tips-item-num">
+                {{ shareStateData?.totalTeamMemberCount || 0 }}
+              </view>
+              <view class="me-header-tips-item-des">
+                累计团员
+              </view>
+              <view class="me-header-tips-item-icon">
+                <up-icon name="arrow-right" size="21rpx" color="#FFFFFF" />
+              </view>
+            </view>
+          </view>
+          <view class="me-header-tips-row header-data-info">
+            <view class="me-header-tips-item">
+              <view class="me-header-tips-item-num">
+                {{ couponSituationData?.quantityForComplimentary || 0 }}
+              </view>
+              <view class="me-header-tips-item-des">
+                今日发券
+              </view>
+            </view>
+            <view class="me-header-tips-item">
+              <view class="me-header-tips-item-num">
+                {{ couponSituationData?.quantityToBeUsed || 0 }}
+              </view>
+              <view class="me-header-tips-item-des">
+                今日领取
+              </view>
+            </view>
+            <view class="me-header-tips-item" @click="menuClick('incomePage')">
+              <view class="me-header-tips-item-num">
+                {{ couponSituationData?.usedQuantity || 0 }}
+              </view>
+              <view class="me-header-tips-item-des">
+                今日使用
+              </view>
+              <view class="me-header-tips-item-icon">
+                <up-icon name="arrow-right" size="21rpx" color="#FFFFFF" />
+              </view>
+            </view>
+          </view>
+        </view>
+      </view>
+      <!-- 菜单 -->
+      <view class="me-header-menu">
+        <view class="me-header-menu-item" @click="menuClick('applyForm', watchUserStatus)">
+          <view class="me-header-menu-icon">
+            <image :src="getImageUrl('@img/me/coupon-need.png')" mode="aspectFill" />
+            <view class="me-header-menu-text">
+              申请发券人
+            </view>
+          </view>
+          <view class="me-header-menu-left">
+            <up-icon name="arrow-right" color="#979797" size="12" />
+          </view>
+        </view>
+        <view class="me-header-menu-item" @click="menuClick('invitePage', isCouponIssuer)">
+          <view class="me-header-menu-icon">
+            <image :src="getImageUrl('@img/me/invite.png')" mode="aspectFill" />
+            <view class="me-header-menu-text">
+              我的邀请
+            </view>
+          </view>
+          <view class="me-header-menu-left">
+            <up-icon name="arrow-right" color="#979797" size="12" />
+          </view>
+        </view>
+        <view class="me-header-menu-item" @click="menuClick('myInviter', getInviteInfo)">
+          <view class="me-header-menu-icon">
+            <image :src="getImageUrl('@img/me/inviter.png')" mode="aspectFill" />
+            <view class="me-header-menu-text">
+              我的邀请人
+            </view>
+          </view>
+          <view class="me-header-menu-left">
+            <up-icon name="arrow-right" color="#979797" size="12" />
+          </view>
+        </view>
+        <view class="me-header-menu-item" @click="menuClick('shareFriend', isCouponIssuer)">
+          <view class="me-header-menu-icon">
+            <image :src="getImageUrl('@img/me/share.png')" mode="aspectFill" />
+            <view class="me-header-menu-text">
+              邀请好友
+            </view>
+          </view>
+          <view class="me-header-menu-left">
+            <up-icon name="arrow-right" color="#979797" size="12" />
+          </view>
+        </view>
+        <view class="me-header-menu-item" @click="menuClick('settingPage')">
+          <view class="me-header-menu-icon">
+            <image :src="getImageUrl('@img/me/setting.png')" mode="aspectFill" />
+            <view class="me-header-menu-text">
+              设置
+            </view>
+          </view>
+          <view class="me-header-menu-left">
+            <up-icon name="arrow-right" color="#979797" size="12" />
+          </view>
+        </view>
+        <view class="me-header-menu-item" @click="handleLogout">
+          <view class="me-header-menu-icon">
+            <image :src="getImageUrl('@img/me/loginOut.png')" mode="aspectFill" />
+            <view class="me-header-menu-text">
+              退出登录
+            </view>
+          </view>
+          <view class="me-header-menu-left">
+            <up-icon name="arrow-right" color="#979797" size="12" />
+          </view>
+        </view>
+      </view>
+    </up-pull-refresh>
+    <!-- 申请中的提示框 -->
+    <up-modal content="您已提交申请,请耐心等待!" title="提示" :show="show">
+      <template #confirmButton>
+        <view class="rounded" style="margin-top: 20rpx;" @click="show = false">
+          我知道了
+        </view>
+      </template>
+    </up-modal>
+  </view>
+</template>
+
+<style lang="scss" scoped>
+// 提示框
+::v-deep .u-modal {
+  width: 580rpx !important;
+  //   height: 376rpx !important;
+  border-radius: 20rpx !important;
+}
+
+::v-deep .u-modal__title {
+  font-weight: 500 !important;
+  font-size: 34rpx !important;
+  color: #333333 !important;
+  padding-top: 29rpx !important;
+  padding-bottom: 28rpx !important;
+}
+
+::v-deep .u-modal__content {
+  border-top: 1px solid #eeeeee;
+  border-bottom: 1px solid #eeeeee;
+  padding-top: 82rpx !important;
+  padding-bottom: 64rpx !important;
+}
+
+::v-deep .u-modal__content__text {
+  font-weight: 500 !important;
+  font-size: 32rpx !important;
+  color: #333333 !important;
+  text-align: center !important;
+}
+
+.rounded {
+  width: 300rpx;
+  line-height: 69rpx;
+  background: linear-gradient(90deg, #ee6b67 0%, #ff7d78 100%);
+  border-radius: 35rpx;
+  font-weight: 400;
+  font-size: 30rpx;
+  color: #ffffff;
+  text-align: center;
+  margin: 0 auto;
+}
+
+.profile-container {
+  height: calc(100vh - 100rpx);
+  background-color: #f8f8fa;
+
+  .me-header {
+    height: 746rpx;
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    align-items: center;
+
+    .me-header-avatar-info {
+      display: flex;
+      flex-direction: column;
+      justify-content: center;
+      align-items: center;
+      gap: 24rpx;
+
+      .me-header-info {
+        .me-header-name {
+          font-weight: 500;
+          font-size: 30rpx;
+          color: #ffffff;
+        }
+      }
+    }
+
+    .me-header-tips {
+      margin-top: 46rpx;
+      height: 317rpx;
+      width: 702rpx;
+      display: flex;
+      flex-direction: column;
+      justify-content: center;
+      align-items: center;
+      gap: 20rpx;
+      background: linear-gradient(90deg, #ff7671, #ff8e8a, #ff7671);
+      border-radius: 8rpx;
+
+      .me-header-tips-row {
+        display: flex;
+
+        .desc-time {
+          width: 100%;
+          display: flex;
+          flex-direction: row;
+          flex-wrap: nowrap;
+          gap: 6rpx;
+          justify-content: flex-end;
+          padding-right: 21rpx;
+
+          font-weight: 400;
+          font-size: 20rpx;
+          color: #ffffff;
+          line-height: 20rpx;
+        }
+
+        .me-header-tips-item {
+          flex: 1;
+          display: flex;
+          flex-direction: column;
+          justify-content: center;
+          align-items: center;
+          gap: 15rpx;
+          font-weight: 500;
+          font-size: 34rpx;
+          color: #ffffff;
+          line-height: 1;
+
+          .me-header-tips-item-num {
+            font-weight: bold;
+          }
+
+          .me-header-tips-item-des {
+            font-weight: 400;
+            font-size: 24rpx;
+            color: #ffffff;
+          }
+        }
+      }
+
+      .header-data-time {
+        align-self: flex-end;
+      }
+
+      .me-header-tips-row.header-data-info {
+        align-self: center;
+        width: 642rpx;
+        height: 96rpx;
+        background: rgba($color: #851b17, $alpha: 0.1);
+        border-radius: 10rpx;
+
+        .me-header-tips-item {
+          font-weight: 400;
+          font-size: 23rpx;
+          color: #ffffff;
+          position: relative;
+
+          &:not(:last-child):after {
+            content: "";
+            position: absolute;
+            top: 50%;
+            right: 0;
+            transform: translateY(-50%);
+            width: 2px;
+            height: 40rpx;
+            background: #ffffff;
+          }
+
+          .me-header-tips-item-icon {
+            position: absolute;
+            top: 50%;
+            right: 21rpx;
+            width: 21rpx;
+            height: 21rpx;
+            font-weight: bolder;
+            transform: translateY(-50%);
+          }
+        }
+      }
+    }
+  }
+
+  .me-header-menu {
+    background: #ffffff;
+    border-radius: 10rpx;
+    margin: -50rpx 24rpx 0;
+    padding: 0 20rpx;
+
+    .me-header-menu-item {
+      height: 88rpx;
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      border-bottom: 1px solid #eeeeee;
+
+      &:last-child {
+        border-bottom: none;
+      }
+
+      .me-header-menu-icon {
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        gap: 13rpx;
+
+        image {
+          width: 40rpx;
+          height: 40rpx;
+        }
+
+        .me-header-menu-text {
+          font-weight: 400;
+          font-size: 26rpx;
+          color: #333333;
+        }
+      }
+
+      .me-header-menu-left {
+      }
+    }
+  }
+}
+
+/* 昵称输入框样式 */
+.nickname-input {
+  width: 100%;
+  height: 100%;
+  font-weight: 500;
+  font-size: 30rpx;
+  color: #ffffff;
+  background: transparent;
+  border: none;
+  outline: none;
+  text-align: center;
+}
+</style>

+ 1 - 0
vite.config.ts

@@ -74,6 +74,7 @@ export default defineConfig(({ command, mode }) => {
                 subPackages: [
                     'src/pages-fg', // 这个是相对必要的路由,尽量留着(登录页、注册页、404页等)
                     'src/pages-A',
+                    'src/pages-C',
                 ],
                 dts: 'src/types/uni-pages.d.ts',
             }),