Просмотр исходного кода

Merge remote-tracking branch 'origin/zhongqi-rechargeStat' into notice-260707

shish 3 недель назад
Родитель
Сommit
c6d5453a8a

+ 639 - 26
hdApp/src/admin/custom/rechargeChange.vue

@@ -1,26 +1,70 @@
+<!-- 客户充值记录页:承接会员详情和统计页入口,展示可按日期、客户、渠道、金额类型筛选的充值明细。 -->
 <template>
     <view class="app-content">
+            <view class="control-panel">
+                <view class="filter-row">
+                    <view class="filter-chip date-chip">
+                        <image class="filter-icon" :src="iconSrc('order-calendar')" mode="aspectFit" />
+                        <DateSelect class="date-select" top="0" @selectDateFn="selectDateFn" :defaultShowName="currentDateName" :showAllOption="true" />
+                    </view>
+                    <view class="search-box">
+                        <AppSearchModule
+                            ref="searchRef"
+                            :height="72"
+                            :fontSize="26"
+                            placeholder="客户姓名 / 手机尾号4位"
+                            placeholderColor="#8b9098"
+                            backColor="#f2f4f7"
+                            @input="searchFn"
+                        />
+                        <view v-if="customId != 0 || searchText" class="search-clear" @click.stop="clearSearch">清空</view>
+                    </view>
+                    <view class="filter-btn" @click.stop="openFilterPanel">
+                        <image class="filter-icon filter-icon-lg" :src="iconSrc('order-filter')" mode="aspectFit" />
+                        <view v-if="hasActiveFilters" class="filter-dot"></view>
+                    </view>
+                </view>
+
+                <view v-if="showSearch" class="search-suggest">
+                    <block v-if="!$util.isEmpty(searchList)">
+                        <view class="suggest-item" v-for="(item, index) in searchList" :key="index" @click="getCustom(index)">
+                            <view class="suggest-name">{{ item.name }}</view>
+                            <view class="suggest-meta">
+                                <text>{{ item.visitTime ? item.visitTime.substr(5, 11) : '' }}</text>
+                                <text v-if="item.mobile" class="suggest-tag">{{ item.mobile }}</text>
+                            </view>
+                        </view>
+                    </block>
+                </view>
+            </view>
             <block v-if="!$util.isEmpty(list.data)">
                 <view class="recharge-table">
                     <view class="table-header table-row">
                         <view class="table-cell cell-date">日期</view>
-                        <view class="table-cell cell-scene">场景</view>
-                        <view class="table-cell cell-staff">人员</view>
-                        <view class="table-cell cell-status">状态</view>
-                        <view class="table-cell cell-amount">充值</view>
-                        <view class="table-cell cell-give">赠送</view>
+                        <view class="table-cell cell-scene">充值渠道</view>
+                        <view class="table-cell cell-staff">操作人</view>
+                        <view class="table-cell cell-status">客户姓名</view>
+                        <view class="table-cell cell-amount">金额</view>
                     </view>
                     <view v-for="(item, index) in list.data" :key="index" class="table-body">
                         <view class="table-row table-row-data">
                             <view class="table-cell cell-date">{{ item.addTime.substr(5,11) }}</view>
                             <view class="table-cell cell-scene">
-                                <view>{{ item.onlinePay == 2 ? '线上' : '线下'}}</view>
-                                <view>{{ item.payWay==0?'微信':item.payWay == 1 ?'支付宝':item.payWay == 4 ?'现金':item.payWay == 5 ?'银行卡':'其它'}}</view>
+                                <view>{{ getChannelLine(item, 0) }}</view>
+                                <view v-if="getChannelLine(item, 1)">{{ getChannelLine(item, 1) }}</view>
+                                <view v-if="isRefunded(item)" class="refund-tag">已退款</view>
                             </view>
                             <view class="table-cell cell-staff">{{ item.staffName }}</view>
-                            <view class="table-cell cell-status">{{ item.payStatus == 1 ? '已付款' : '待付款' }}</view>
-                            <view class="table-cell cell-amount">{{ item.amount }}</view>
-                            <view class="table-cell cell-give">{{ item.giveAmount>0 ? parseFloat(item.giveAmount) : '-' }}</view>
+                            <view class="table-cell cell-status">{{ item.customName }}</view>
+                            <view class="table-cell cell-amount">
+                                <view class="amount-line">{{ item.amount }}{{ getGiveAmountText(item) }}</view>
+                                <view
+                                    v-if="canRefund(item)"
+                                    class="refund-btn"
+                                    :class="{ disabled: refundingId == item.id }"
+                                    @click.stop="confirmRefund(item)"
+                                >退款</view>
+                            </view>
                         </view>
                         <view class="table-row table-row-extra" v-if="!$util.isEmpty(item.remark)">
                             <view class="table-cell cell-extra">
@@ -34,37 +78,365 @@
             <block v-else>
                 <AppWrapperEmpty title="暂无数据" :is-empty="$util.isEmpty(list.data)" />
             </block>
+
+            <view v-if="showFilterPanel" class="filter-overlay" @click="closeFilterPanel">
+                <view class="filter-panel" @click.stop>
+                    <view class="filter-header">筛选充值记录</view>
+                    <view class="filter-section">
+                        <view class="filter-title">充值渠道</view>
+                        <view class="filter-options">
+                            <view
+                                v-for="(item, index) in channelOptions"
+                                :key="index"
+                                class="filter-option"
+                                :class="{ active: channelKey === item.value }"
+                                @click="selectChannel(item.value)"
+                            >
+                                {{ item.label }}
+                            </view>
+                        </view>
+                    </view>
+                    <view class="filter-section">
+                        <view class="filter-title">金额类型</view>
+                        <view class="filter-options">
+                            <view
+                                v-for="(item, index) in ioOptions"
+                                :key="index"
+                                class="filter-option"
+                                :class="{ active: io === item.value }"
+                                @click="selectIo(item.value)"
+                            >
+                                {{ item.label }}
+                            </view>
+                        </view>
+                    </view>
+                    <view class="filter-actions">
+                        <button class="filter-action-btn clear-btn" @click="clearFilters">重置</button>
+                        <button class="filter-action-btn confirm-btn" @click="confirmFilters">确定</button>
+                    </view>
+                </view>
+            </view>
         </view>
     </template>
     <script>
     import AppWrapperEmpty from "@/components/app-wrapper-empty";
-    import { rechargeList } from '@/api/recharge'
+    import { rechargeList, refundRecharge } from '@/api/recharge'
+    import { getList } from "@/api/member";
+    import { getStaffInfo } from '@/api/shop-admin'
     import list from '@/mixins/list'
+    import AppSearchModule from "@/components/module/app-search";
+    import DateSelect from "@/components/module/dateSelect";
+    import { iconSrc } from "@/utils/iconSrc";
     export default {
         name: "rechargeBalance",
         components: {
-            AppWrapperEmpty
+            AppWrapperEmpty,
+            AppSearchModule,
+            DateSelect
         },
         mixins:[list],
+        computed: {
+            hasActiveFilters() {
+                return this.channelKey !== 'all' || this.io !== '';
+            }
+        },
         data() {
             return {
+                initialCustomId: 0,
+                initialCustomName: '',
+                initialCustomCleared: false,
+                customId: 0,
+                searchTime: '',
+                startTime: '',
+                endTime: '',
+                currentDateName: '全部',
+                queryFiltersHydrated: false,
+                io: '',
+                searchText: '',
+                lastSearchText: '',
+                showSearch: false,
+                searchList: [],
+                showFilterPanel: false,
+                channelKey: 'all',
+                channelOptions: [
+                    { label: '全部', value: 'all' },
+                    { label: '线上微信', value: 'online_0' },
+                    { label: '线上支付宝', value: 'online_1' },
+                    { label: '线下微信', value: 'pay_0' },
+                    { label: '线下支付宝', value: 'pay_1' },
+                    { label: '现金', value: 'pay_4' },
+                    { label: '银行卡', value: 'pay_5' },
+                    { label: '其它', value: 'other' }
+                ],
+                ioOptions: [
+                    { label: '全部金额', value: '' },
+                    { label: '充值金额', value: '1' },
+                    { label: '减少金额', value: '0' }
+                ],
+                hasFinance: 0,
+                refundingId: 0
             };
         },
+        onLoad(option) {
+            this.option = option || {};
+            this.hydrateQueryFilters();
+        },
+        onReady() {
+            this.syncSearchInput();
+        },
         methods: {
+            iconSrc,
+            loadFinancePermission() {
+                getStaffInfo().then(res => {
+                    if (res.code == 1 && res.data && res.data.staff) {
+                        this.hasFinance = res.data.staff.finance ? 1 : 0;
+                    }
+                });
+            },
+            decodeQueryText(text) {
+                try {
+                    return decodeURIComponent(text || '');
+                } catch (e) {
+                    return text || '';
+                }
+            },
+            // 从统计页或会员详情入口恢复初始筛选,保证首次加载明细数据与来源页面一致。
+            hydrateQueryFilters() {
+                if (this.queryFiltersHydrated) {
+                    return;
+                }
+                const option = this.option || {};
+                if (Object.prototype.hasOwnProperty.call(option, 'searchTime')) {
+                    this.searchTime = option.searchTime || '';
+                }
+                if (Object.prototype.hasOwnProperty.call(option, 'startTime')) {
+                    this.startTime = option.startTime || '';
+                }
+                if (Object.prototype.hasOwnProperty.call(option, 'endTime')) {
+                    this.endTime = option.endTime || '';
+                }
+                if (option.dateName) {
+                    this.currentDateName = this.decodeQueryText(option.dateName);
+                } else {
+                    this.currentDateName = this.getDateName();
+                }
+                if (option.channelKey) {
+                    this.channelKey = option.channelKey;
+                }
+                if (Object.prototype.hasOwnProperty.call(option, 'io')) {
+                    this.io = String(option.io);
+                }
+                this.hydrateInitialCustom();
+                this.queryFiltersHydrated = true;
+            },
+            syncSearchInput() {
+                if (this.$refs && this.$refs.searchRef) {
+                    this.$refs.searchRef.search = this.searchText;
+                }
+            },
+            // 初始化入口客户筛选,会员详情和统计页客户行都会带入客户身份。
+            hydrateInitialCustom() {
+                if (this.initialCustomCleared) {
+                    return;
+                }
+                const option = this.option || {};
+                const optionCustomId = option.customId || option.id || 0;
+                const optionCustomName = this.decodeQueryText(option.customName || '');
+                if (optionCustomId && !this.initialCustomId) {
+                    this.initialCustomId = optionCustomId;
+                }
+                if (optionCustomName && !this.initialCustomName) {
+                    this.initialCustomName = optionCustomName;
+                }
+                if (this.initialCustomId && !this.customId) {
+                    this.customId = this.initialCustomId;
+                }
+                if (this.initialCustomName && !this.searchText) {
+                    this.searchText = this.initialCustomName;
+                    this.lastSearchText = this.initialCustomName;
+                    this.syncSearchInput();
+                }
+            },
             async init(){
-                let id = this.option.id ? this.option.id : 0
-                const res = await rechargeList({ page:this.list.page,customId:id })
+                this.hydrateQueryFilters();
+                this.loadFinancePermission();
+                const res = await rechargeList({
+                    page: this.list.page,
+                    customId: this.customId,
+                    searchTime: this.searchTime,
+                    startTime: this.startTime,
+                    endTime: this.endTime,
+                    channelKey: this.channelKey,
+                    io: this.io
+                })
                 if (this.$util.isEmpty(res.data.list)){
                     this.completes(res)
                     return
                 }
                 let currentList = res.data.list
                 currentList.forEach(function(item,index,array){
-                    array[index].amount = item.io == 0 ? '-'+parseFloat(item.amount) : '+'+parseFloat(item.amount)
+                    const prefix = Number(item.isRefund) === 1 ? '-' : '+';
+                    array[index].amount = prefix + parseFloat(item.amount)
                 });
                 res.data.list = currentList
                 this.completes(res)
             },
+            getPayWayName(payWay) {
+                return payWay == 0 ? '微信' : payWay == 1 ? '支付宝' : payWay == 4 ? '现金' : payWay == 5 ? '银行卡' : '其它';
+            },
+            getChannelLine(item, line) {
+                if (item.onlinePay == 2) {
+                    return line === 0 ? '线上' : this.getPayWayName(item.payWay);
+                }
+                return line === 0 ? '线下' : this.getPayWayName(item.payWay);
+            },
+            isRefunded(item) {
+                return Number(item && item.isRefund) === 1;
+            },
+            canRefund(item) {
+                return this.hasFinance == 1
+                    && item
+                    && Number(item.payStatus) === 1
+                    && !this.isRefunded(item)
+                    && Number(item.amount) > 0;
+            },
+            // 格式化金额列的赠送补充文案,只有存在正向赠送金额时才跟随主金额展示。
+            getGiveAmountText(item) {
+                const giveAmount = Number(item && item.giveAmount);
+                if (!giveAmount || giveAmount <= 0) {
+                    return '';
+                }
+                return '(赠送' + parseFloat(item.giveAmount) + ')';
+            },
+            confirmRefund(item) {
+                const amount = Math.abs(parseFloat(item.amount)) || 0;
+                let content = '确认对该笔充值退款 ¥' + amount + ' ?';
+                if (Number(item.onlinePay) === 2) {
+                    content = '此单为线上付款,提交后钱会原路退回给客户,确认退款 ¥' + amount + ' ?';
+                }
+                uni.showModal({
+                    title: '确认退款',
+                    content,
+                    success: (res) => {
+                        if (res.confirm) {
+                            this.submitRefund(item);
+                        }
+                    }
+                });
+            },
+            submitRefund(item) {
+                if (this.refundingId) {
+                    return;
+                }
+                this.refundingId = item.id;
+                refundRecharge({ id: item.id }).then(res => {
+                    if (res.code == 1) {
+                        this.$msg('退款成功');
+                        this.resetList();
+                        this.init();
+                    }
+                    this.refundingId = 0;
+                }).catch(() => {
+                    this.refundingId = 0;
+                });
+            },
+            openFilterPanel() {
+                this.showFilterPanel = true;
+            },
+            closeFilterPanel() {
+                this.showFilterPanel = false;
+            },
+            selectChannel(value) {
+                this.channelKey = value;
+            },
+            // 选择金额类型后由确认按钮统一触发列表刷新。
+            selectIo(value) {
+                this.io = value;
+            },
+            clearFilters() {
+                this.channelKey = 'all';
+                this.io = '';
+                this.closeFilterPanel();
+                this.resetList();
+                this.init();
+            },
+            confirmFilters() {
+                this.closeFilterPanel();
+                this.resetList();
+                this.init();
+            },
+            // 切换日期后刷新列表,保留展示文案供当前页筛选器显示。
+            selectDateFn(val) {
+                this.searchTime = val.searchTime || '';
+                this.startTime = val.startTime || '';
+                this.endTime = val.endTime || '';
+                this.currentDateName = val.showName || this.getDateName();
+                this.resetList();
+                this.init();
+            },
+            // 根据当前日期筛选补全 DateSelect 展示文案。
+            getDateName() {
+                if (!this.searchTime) {
+                    return '全部';
+                }
+                if (this.searchTime === 'byMonth') {
+                    return this.startTime || '选月份';
+                }
+                if (this.searchTime === 'custom') {
+                    return this.startTime === this.endTime ? this.startTime : '选时段';
+                }
+                return '今天';
+            },
+            searchFn(e) {
+                this.searchText = e;
+                if (this.$util.isEmpty(this.searchText) && this.$util.isEmpty(this.lastSearchText)) {
+                    return;
+                }
+                if (this.$util.isEmpty(e)) {
+                    this.lastSearchText = '';
+                    this.showSearch = false;
+                    this.searchList = [];
+                    this.customId = this.initialCustomId || 0;
+                    this.resetList();
+                    this.init();
+                    return;
+                }
+                this.lastSearchText = this.searchText;
+                getList({ page: 1, name: e, type: 0 }).then(res => {
+                    if (!this.$util.isEmpty(res.data) && !this.$util.isEmpty(res.data.list)) {
+                        this.showSearch = true;
+                        this.searchList = res.data.list;
+                    } else {
+                        this.showSearch = false;
+                        this.searchList = [];
+                    }
+                });
+            },
+            getCustom(index) {
+                const item = this.searchList[index];
+                if (!item) return;
+                this.showSearch = false;
+                this.initialCustomCleared = false;
+                this.customId = item.id;
+                this.searchText = item.name || this.searchText;
+                this.lastSearchText = this.searchText;
+                this.syncSearchInput();
+                this.resetList();
+                this.init();
+            },
+            clearSearch() {
+                this.searchText = '';
+                this.lastSearchText = '';
+                this.showSearch = false;
+                this.searchList = [];
+                this.initialCustomCleared = true;
+                this.initialCustomId = 0;
+                this.initialCustomName = '';
+                this.customId = 0;
+                this.syncSearchInput();
+                this.resetList();
+                this.init();
+            },
             goShOrder(item){
                 this.$util.pageTo({ url: '/admin/order/detail?id='+item.shOrderId})
             }
@@ -85,13 +457,134 @@
     };
     </script>
     <style lang="scss" scoped>
+    .control-panel {
+        position: sticky;
+        top: 0;
+        z-index: 30;
+        width: 100%;
+        padding: 22upx 22upx 18upx;
+        background: #fff;
+        box-sizing: border-box;
+    }
+    .filter-row {
+        display: flex;
+        align-items: center;
+    }
+    .filter-chip,
+    .filter-btn {
+        height: 72upx;
+        border-radius: 16upx;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        flex-shrink: 0;
+        box-sizing: border-box;
+    }
+    .filter-chip {
+        padding: 0 8upx;
+        color: #60666d;
+        font-size: 28upx;
+    }
+    .date-chip {
+        min-width: 92upx;
+    }
+    .date-select {
+        display: flex;
+        align-items: center;
+    }
+    .date-chip .filter-icon {
+        margin-right: 4upx;
+    }
+    .filter-icon {
+        width: 32upx;
+        height: 32upx;
+        flex-shrink: 0;
+    }
+    .filter-icon-lg {
+        width: 38upx;
+        height: 38upx;
+    }
+    .search-box {
+        position: relative;
+        flex: 1;
+        min-width: 0;
+        margin-left: 14upx;
+        margin-right: 14upx;
+    }
+    .search-box ::v-deep .app-search-module .map-search {
+        height: 72upx !important;
+        border-radius: 28upx;
+    }
+    .search-box ::v-deep .app-search-module .map-search .input-wrap {
+        height: 72upx;
+    }
+    .filter-btn {
+        position: relative;
+        min-width: 72upx;
+        border: 1upx solid #e3e8e6;
+        background: #fff;
+    }
+    .filter-dot {
+        position: absolute;
+        right: 10upx;
+        top: 8upx;
+        width: 14upx;
+        height: 14upx;
+        border-radius: 50%;
+        background: #ff3b30;
+        border: 2upx solid #fff;
+    }
+    .search-clear {
+        position: absolute;
+        right: 12upx;
+        top: 50%;
+        transform: translateY(-50%);
+        padding: 7upx 14upx;
+        border: 1upx solid #09c567;
+        border-radius: 22upx;
+        background: rgba(255, 255, 255, 0.94);
+        color: #09c567;
+        font-size: 22upx;
+        z-index: 2;
+    }
+    .search-suggest {
+        position: absolute;
+        left: 22upx;
+        right: 22upx;
+        top: 108upx;
+        z-index: 40;
+        max-height: 420upx;
+        padding: 20upx 24upx 0;
+        border-radius: 18upx;
+        background: #fff;
+        box-shadow: 0 18upx 38upx rgba(22, 33, 28, 0.14);
+        overflow-y: auto;
+    }
+    .suggest-item {
+        padding-bottom: 22upx;
+        margin-bottom: 22upx;
+        border-bottom: 1upx solid #eff2f0;
+    }
+    .suggest-name {
+        color: #1f2328;
+        font-size: 32upx;
+        font-weight: 700;
+    }
+    .suggest-meta {
+        margin-top: 8upx;
+        color: #9298a0;
+        font-size: 25upx;
+    }
+    .suggest-tag {
+        margin-left: 18upx;
+        color: #09c567;
+    }
     .recharge-table {
         width: 100%;
         border-radius: 16upx;
         overflow: hidden;
         background: #fff;
         box-shadow: 0 4upx 24upx rgba(0,0,0,0.06);
-        margin: 20upx 0;
         .table-row {
             display: flex;
             align-items: center;
@@ -119,7 +612,9 @@
                     padding: 16upx 20upx;
                     display: flex;
                     flex-direction: column;
-                    gap: 2upx;
+                    view:not(:last-child) {
+                        margin-bottom: 2upx;
+                    }
                 }
             }
         }
@@ -152,15 +647,133 @@
             }
             &.cell-amount {
                 text-align: right;
-                min-width: 100upx;
-                flex: 1;
-            }
-            &.cell-give {
-                text-align: right;
-                min-width: 80upx;
-                padding-right: 32upx;
-                flex: 1;
+                min-width: 152upx;
+                padding-right: 18upx;
+                flex: 1.45;
             }
         }
     }
-    </style>
+    .refund-tag {
+        display: inline-flex;
+        align-items: center;
+        justify-content: center;
+        min-width: 64upx;
+        height: 30upx;
+        margin-top: 6upx;
+        padding: 0 8upx;
+        border-radius: 6upx;
+        background: #fff1f0;
+        color: #e5484d;
+        font-size: 20upx;
+        line-height: 30upx;
+        box-sizing: border-box;
+    }
+    .amount-line {
+        line-height: 34upx;
+        word-break: break-all;
+    }
+    .refund-btn {
+        display: inline-flex;
+        align-items: center;
+        justify-content: center;
+        min-width: 70upx;
+        height: 44upx;
+        margin-top: 8upx;
+        padding: 0 12upx;
+        border-radius: 8upx;
+        background: #e5484d;
+        color: #fff;
+        font-size: 24upx;
+        line-height: 44upx;
+        box-sizing: border-box;
+        &.disabled {
+            opacity: 0.55;
+        }
+    }
+    .filter-overlay {
+        position: fixed;
+        top: 0;
+        left: 0;
+        right: 0;
+        bottom: 0;
+        z-index: 9999;
+        display: flex;
+        align-items: flex-start;
+        justify-content: center;
+        background-color: rgba(0, 0, 0, 0.5);
+    }
+    .filter-panel {
+        width: 100%;
+        max-height: 90vh;
+        overflow-y: auto;
+        border-radius: 0 0 24upx 24upx;
+        background: #fff;
+    }
+    .filter-header {
+        padding: 24upx 0;
+        text-align: center;
+        color: #202329;
+        font-size: 32upx;
+        font-weight: 800;
+        border-bottom: 1upx solid #f0f0f0;
+    }
+    .filter-section {
+        padding: 30upx 40upx;
+        border-bottom: 1upx solid #f0f0f0;
+    }
+    .filter-title {
+        margin-bottom: 20upx;
+        color: #202329;
+        font-size: 28upx;
+        font-weight: 700;
+    }
+    .filter-options {
+        display: flex;
+        flex-wrap: wrap;
+        margin-right: -18upx;
+        margin-bottom: -18upx;
+    }
+    .filter-option {
+        min-width: 120upx;
+        padding: 15upx 28upx;
+        margin-right: 18upx;
+        margin-bottom: 18upx;
+        border: 1upx solid #dce2e0;
+        border-radius: 28upx;
+        color: #60666d;
+        background: #fff;
+        font-size: 26upx;
+        text-align: center;
+    }
+    .filter-option.active {
+        color: #09c567;
+        border-color: #09c567;
+        background: #effbf4;
+        font-weight: 700;
+    }
+    .filter-actions {
+        display: flex;
+        padding: 30upx 40upx;
+    }
+    .filter-action-btn {
+        flex: 1;
+        height: 76upx;
+        border-radius: 14upx;
+        font-size: 28upx;
+        font-weight: 700;
+    }
+    .filter-action-btn:not(:last-child) {
+        margin-right: 20upx;
+    }
+    .filter-action-btn::after {
+        border: 0;
+    }
+    .clear-btn {
+        color: #626970;
+        background: #f3f5f4;
+    }
+    .confirm-btn {
+        color: #fff;
+        background: #09c567;
+    }
+    </style>

+ 2 - 1
hdApp/src/admin/home/homeMenus.js

@@ -78,7 +78,8 @@ export const APPLY_MENU_GROUPS = [
       { name: '采购花材', icon: homeIcon('procurement'), url: '/admin/stat/cgItem', pf: 1 },
       { name: '绿植统计', icon: homeIcon('plant'), url: '/admin/stat/kindLz', pf: 1 },
       { name: '损耗总览', icon: homeIcon('breakage'), url: '/admin/stat/waste', pf: 1 },
-      { name: '挂账汇总', icon: homeIcon('pay-code'), url: '/admin/custom/showTotalBalance', pf: 1 }
+      { name: '挂账汇总', icon: homeIcon('pay-code'), url: '/admin/custom/showTotalBalance', pf: 1 },
+      { name: '充值统计', icon: homeIcon('stats-2'), url: '/admin/stat/rechargeStat', pf: 1 },
     ]
   },
   {

+ 85 - 2
hdApp/src/admin/home/member.vue

@@ -53,6 +53,11 @@
     </view>
 
     <view class="list-wrap">
+      <view v-if="showBalanceStat" class="balance-stat">
+        <view class="balance-stat-title">总余额</view>
+        <view class="balance-stat-count">{{ balanceStat.count }}人</view>
+        <view class="balance-stat-amount">¥{{ balanceStat.totalBalanceText }}</view>
+      </view>
       <block v-if="!$util.isEmpty(list.data)">
         <view
           class="member-card"
@@ -110,7 +115,7 @@
 import AppWrapperEmpty from "@/components/app-wrapper-empty";
 import AppSearchModule from "@/components/module/app-search";
 import { list } from "@/mixins";
-import { getList } from "@/api/member";
+import { getBalanceStat, getList } from "@/api/member";
 import NotLogin from "@/components/not-login";
 import { iconSrc } from "@/utils/iconSrc";
 
@@ -137,9 +142,19 @@ export default {
       customType: 0,
       searchStyle: 0,
       searchText: "",
-      lastSearchText: ""
+      lastSearchText: "",
+      balanceStat: {
+        count: 0,
+        totalBalance: 0,
+        totalBalanceText: 0
+      }
     };
   },
+  computed: {
+    showBalanceStat () {
+      return Number(this.customType) == 2;
+    }
+  },
   onLoad () {
     this.setStatusBarHeight();
   },
@@ -210,6 +225,7 @@ export default {
       this.getMyCustomList();
     },
     getMyCustomList () {
+      const shouldRefreshBalanceStat = Number(this.customType) == 2 && Number(this.list.page) == 1;
       return getList({
         page: this.list.page,
         type: this.customType,
@@ -220,9 +236,36 @@ export default {
           this.completes(res);
           this.decorateMemberList();
           this.syncTabs(res.data || {});
+          if (shouldRefreshBalanceStat) {
+            return this.getBalanceStat();
+          } else if (Number(this.customType) != 2) {
+            this.resetBalanceStat();
+          }
+        }
+      });
+    },
+    getBalanceStat () {
+      return getBalanceStat({
+        searchText: this.searchText,
+        searchStyle: this.searchStyle
+      }).then(res => {
+        if (res.code == 1 && Number(this.customType) == 2) {
+          const totalBalance = Number(res.data.totalBalance) || 0;
+          this.balanceStat = {
+            count: Number(res.data.count) || 0,
+            totalBalance,
+            totalBalanceText: this.formatMoney(totalBalance)
+          };
         }
       });
     },
+    resetBalanceStat () {
+      this.balanceStat = {
+        count: 0,
+        totalBalance: 0,
+        totalBalanceText: 0
+      };
+    },
     syncTabs (data) {
       this.tabs.forEach(item => {
         if (item.type == 0) {
@@ -274,6 +317,10 @@ export default {
     formatNumber (value) {
       return value ? parseFloat(value) : 0;
     },
+    formatMoney (value) {
+      const amount = Number(value) || 0;
+      return parseFloat(amount.toFixed(2));
+    },
     statusText (item) {
       if (item.passStatus != 1) {
         return "待审核";
@@ -516,6 +563,42 @@ export default {
   padding: 8upx 0 14upx;
 }
 
+.balance-stat {
+  min-height: 104upx;
+  padding: 0 26upx;
+  display: flex;
+  align-items: center;
+  background: #fff;
+  border-bottom: 1upx solid #edf0ef;
+  box-sizing: border-box;
+}
+
+.balance-stat-title {
+  flex: 1;
+  min-width: 0;
+  color: #2f3338;
+  font-size: 35upx;
+  font-weight: 700;
+  line-height: 42upx;
+}
+
+.balance-stat-count {
+  flex-shrink: 0;
+  margin-left: 24upx;
+  color: #2f3338;
+  font-size: 31upx;
+  line-height: 38upx;
+}
+
+.balance-stat-amount {
+  flex-shrink: 0;
+  margin-left: 30upx;
+  color: #159be8;
+  font-size: 35upx;
+  font-weight: 700;
+  line-height: 42upx;
+}
+
 .member-card {
   position: relative;
   display: flex;

+ 15 - 4
hdApp/src/admin/member/detail.vue

@@ -169,7 +169,7 @@
         <tui-list-cell @click="pageTo({ url: '/admin/custom/balanceChange?id='+data.id })" class="line-cell" :hover="false" :arrow="true" >
             <view class="tui-title">余额变动</view>
         </tui-list-cell>
-        <tui-list-cell @click="pageTo({ url: '/admin/custom/rechargeChange?id='+data.id })" class="line-cell" :hover="false" :arrow="true" >
+        <tui-list-cell @click="goRechargeChange()" class="line-cell" :hover="false" :arrow="true" >
             <view class="tui-title">充值记录</view>
         </tui-list-cell>
         <tui-list-cell @click="pageTo({ url: '/admin/order/itemOrder?customId='+data.id })" class="line-cell" :hover="false" :arrow="true" >
@@ -589,6 +589,15 @@ export default {
   },
   methods: {
     init(){},
+    goRechargeChange() {
+      const customId = this.data.id || 0;
+      const customName = encodeURIComponent(this.data.name || '');
+      const mobile = encodeURIComponent(this.data.mobile || '');
+      this.pageTo({
+        url: '/admin/custom/rechargeChange',
+        query: { customId, customName, mobile }
+      });
+    },
     // 切换余额详情展开/隐藏
     toggleBalanceDetail() {
       this.showBalanceDetail = !this.showBalanceDetail
@@ -916,9 +925,11 @@ export default {
         }
         modifyBirthday(params).then(res=>{
           if(res.code == 1){
-            this.$msg('修改成功')
+            this.$msg(res.msg ||'修改成功')
             this.birthdayModal = false
-            this.initInfo()
+            if(res.msg && res.msg == '生日修改成功') {
+              this.initInfo()
+            }
           }
         })
       }
@@ -1715,4 +1726,4 @@ export default {
        }
      }
   }
-</style>
+</style>

+ 480 - 0
hdApp/src/admin/stat/rechargeStat.vue

@@ -0,0 +1,480 @@
+<!-- 充值统计页:供花店管理员按时间查看充值汇总,并从金额入口跳转到充值明细。 -->
+<template>
+  <view class="recharge-stat-page app-content">
+    <view class="filter-row">
+      <view class="filter-chip date-chip">
+        <image class="filter-icon" :src="iconSrc('order-calendar')" mode="aspectFit" />
+        <DateSelect class="date-select" top="0" @selectDateFn="selectDateFn" defaultShowName="今天" :showAllOption="true" />
+      </view>
+    </view>
+
+    <view class="summary-grid">
+      <view class="summary-card">
+        <view class="summary-title">充值金额</view>
+        <view class="summary-amount clickable-money" @click.stop="goSummaryRechargeDetail">¥{{ moneyText(summary.rechargeAmount) }}</view>
+        <view class="summary-count">{{ summary.rechargeCount || 0 }}笔</view>
+      </view>
+      <view class="summary-card">
+        <view class="summary-title">减少金额</view>
+        <view class="summary-amount clickable-money" @click.stop="goSummaryReduceDetail">¥{{ moneyText(summary.reduceAmount) }}</view>
+        <view class="summary-count">{{ summary.reduceCount || 0 }}笔</view>
+      </view>
+      <view class="summary-card">
+        <view class="summary-title">实充金额</view>
+        <view class="summary-amount clickable-money" @click.stop="goSummaryRealDetail">¥{{ moneyText(summary.realAmount) }}</view>
+      </view>
+    </view>
+
+    <view class="stat-panel">
+      <view class="panel-title">渠道统计</view>
+      <view class="table-head stat-row">
+        <view class="name-cell">渠道</view>
+        <view class="amount-cell">充值金额</view>
+        <view class="amount-cell">减少金额</view>
+        <view class="real-cell">实充金额</view>
+      </view>
+      <view v-for="(item, index) in channelList" :key="index" class="stat-row data-row">
+        <view class="name-cell strong-text">{{ item.name }}</view>
+        <view class="amount-cell clickable-cell" :data-index="index" @click.stop="goChannelRechargeDetail">
+          <view class="money">{{ moneyText(item.rechargeAmount) }}</view>
+          <view class="count">{{ item.rechargeCount || 0 }}笔</view>
+        </view>
+        <view class="amount-cell clickable-cell" :data-index="index" @click.stop="goChannelReduceDetail">
+          <view class="money">{{ moneyText(item.reduceAmount) }}</view>
+          <view class="count">{{ item.reduceCount || 0 }}笔</view>
+        </view>
+        <view class="real-cell money clickable-money" :data-index="index" @click.stop="goChannelRealDetail">{{ moneyText(item.realAmount) }}</view>
+      </view>
+    </view>
+
+    <view class="stat-panel">
+      <view class="panel-title">客户统计</view>
+      <view class="table-head stat-row">
+        <view class="name-cell">名称</view>
+        <view class="amount-cell">充值金额</view>
+        <view class="amount-cell">减少金额</view>
+        <view class="real-cell">实充金额</view>
+      </view>
+      <block v-if="customerList.length > 0">
+        <view v-for="(item, index) in customerList" :key="index" class="stat-row data-row">
+          <view class="name-cell strong-text">{{ item.name }}</view>
+          <view class="amount-cell clickable-cell" :data-index="index" @click.stop="goCustomerRechargeDetail">
+            <view class="money">{{ moneyText(item.rechargeAmount) }}</view>
+            <view class="count">{{ item.rechargeCount || 0 }}笔</view>
+          </view>
+          <view class="amount-cell clickable-cell" :data-index="index" @click.stop="goCustomerReduceDetail">
+            <view class="money">{{ moneyText(item.reduceAmount) }}</view>
+            <view class="count">{{ item.reduceCount || 0 }}笔</view>
+          </view>
+          <view class="real-cell money clickable-money" :data-index="index" @click.stop="goCustomerRealDetail">{{ moneyText(item.realAmount) }}</view>
+        </view>
+      </block>
+      <view v-else class="empty-text">暂无数据</view>
+    </view>
+
+    <NotLogin></NotLogin>
+  </view>
+</template>
+
+<script>
+import DateSelect from "@/components/module/dateSelect";
+import NotLogin from "@/components/not-login";
+import { rechargeStat } from "@/api/recharge";
+import { iconSrc } from "@/utils/iconSrc";
+
+const EMPTY_SUMMARY = {
+  rechargeAmount: "0.00",
+  rechargeCount: 0,
+  reduceAmount: "0.00",
+  reduceCount: 0,
+  realAmount: "0.00"
+};
+
+export default {
+  name: "rechargeStat",
+  components: {
+    DateSelect,
+    NotLogin
+  },
+  data() {
+    return {
+      params: {
+        searchTime: "today",
+        startTime: "",
+        endTime: ""
+      },
+      currentDateName: "今天",
+      summary: { ...EMPTY_SUMMARY },
+      channelList: [],
+      customerList: [],
+      loading: false
+    };
+  },
+  onPullDownRefresh() {
+    this.getStat().then(() => {
+      uni.stopPullDownRefresh();
+    });
+  },
+  methods: {
+    iconSrc,
+    // 初始化统计数据,供项目全局混入在页面加载时调用。
+    init() {
+      return this.getStat();
+    },
+    // 处理统计页日期筛选,保留当前选项用于跳转明细页复用。
+    selectDateFn(val) {
+      this.params = {
+        searchTime: val.searchTime || "",
+        startTime: val.startTime || "",
+        endTime: val.endTime || ""
+      };
+      this.currentDateName = val.showName || this.getDateName(this.params);
+      this.getStat();
+    },
+    // 汇总、渠道、客户金额点击后,组装明细页可识别的筛选参数。
+    goRechargeDetail(scope, item, amountType) {
+      const query = this.getDetailQuery(scope, item || {}, amountType);
+      this.pageTo({
+        url: "/admin/custom/rechargeChange",
+        query
+      });
+    },
+    // 进入全部客户的充值金额明细。
+    goSummaryRechargeDetail() {
+      this.goRechargeDetail("summary", this.summary, "recharge");
+    },
+    // 进入全部客户的减少金额明细。
+    goSummaryReduceDetail() {
+      this.goRechargeDetail("summary", this.summary, "reduce");
+    },
+    // 进入全部客户的实充金额关联流水。
+    goSummaryRealDetail() {
+      this.goRechargeDetail("summary", this.summary, "real");
+    },
+    // 进入指定渠道的充值金额明细。
+    goChannelRechargeDetail(event) {
+      const item = this.getEventListItem(event, this.channelList);
+      this.goRechargeDetail("channel", item, "recharge");
+    },
+    // 进入指定渠道的减少金额明细。
+    goChannelReduceDetail(event) {
+      const item = this.getEventListItem(event, this.channelList);
+      this.goRechargeDetail("channel", item, "reduce");
+    },
+    // 进入指定渠道的实充金额关联流水。
+    goChannelRealDetail(event) {
+      const item = this.getEventListItem(event, this.channelList);
+      this.goRechargeDetail("channel", item, "real");
+    },
+    // 进入指定客户的充值金额明细。
+    goCustomerRechargeDetail(event) {
+      const item = this.getEventListItem(event, this.customerList);
+      this.goRechargeDetail("customer", item, "recharge");
+    },
+    // 进入指定客户的减少金额明细。
+    goCustomerReduceDetail(event) {
+      const item = this.getEventListItem(event, this.customerList);
+      this.goRechargeDetail("customer", item, "reduce");
+    },
+    // 进入指定客户的实充金额关联流水。
+    goCustomerRealDetail(event) {
+      const item = this.getEventListItem(event, this.customerList);
+      this.goRechargeDetail("customer", item, "real");
+    },
+    // 从小程序事件 dataset 中取回当前行,避免模板事件传对象导致 WXML 编译异常。
+    getEventListItem(event, list) {
+      const dataset = event && event.currentTarget ? event.currentTarget.dataset || {} : {};
+      const index = Number(dataset.index);
+      return list[index] || {};
+    },
+    // 生成跳转参数,日期条件始终跟随当前 DateSelect 选项。
+    getDetailQuery(scope, item, amountType) {
+      const query = {
+        searchTime: this.params.searchTime,
+        startTime: this.params.startTime,
+        endTime: this.params.endTime,
+        dateName: encodeURIComponent(this.currentDateName || this.getDateName(this.params))
+      };
+      const io = this.getAmountIo(amountType);
+      if (io !== "") {
+        query.io = io;
+      }
+      if (scope === "channel") {
+        const channelKey = this.getChannelKey(item);
+        if (channelKey) {
+          query.channelKey = channelKey;
+        }
+      }
+      if (scope === "customer") {
+        const customId = item.customId || item.customerId || item.id || 0;
+        if (customId) {
+          query.customId = customId;
+          query.customName = encodeURIComponent(item.customName || item.customerName || item.name || "");
+        }
+      }
+      return query;
+    },
+    // 金额类型映射到充值记录的收支方向,实充金额查看当前筛选下的全部流水。
+    getAmountIo(amountType) {
+      if (amountType === "recharge") {
+        return 1;
+      }
+      if (amountType === "reduce") {
+        return 0;
+      }
+      return "";
+    },
+    // 兼容统计接口可能返回的不同渠道字段,缺失时按名称兜底映射。
+    getChannelKey(item) {
+      const rawKey = item.channelKey || item.key || item.value || item.type || "";
+      if (rawKey) {
+        return rawKey;
+      }
+      const name = item.name || item.channelName || "";
+      if (name.indexOf("线上微信") > -1) {
+        return "online_0";
+      }
+      if (name.indexOf("线上支付宝") > -1) {
+        return "online_1";
+      }
+      if (name.indexOf("系统") > -1) {
+        return "online_0";
+      }
+      if (name.indexOf("线下微信") > -1 || name.indexOf("微信") > -1) {
+        return "pay_0";
+      }
+      if (name.indexOf("线下支付宝") > -1 || name.indexOf("支付宝") > -1) {
+        return "pay_1";
+      }
+      if (name.indexOf("现金") > -1) {
+        return "pay_4";
+      }
+      if (name.indexOf("银行卡") > -1) {
+        return "pay_5";
+      }
+      if (name.indexOf("其它") > -1 || name.indexOf("其他") > -1) {
+        return "other";
+      }
+      return "";
+    },
+    // 根据当前查询边界补全 DateSelect 的展示名称。
+    getDateName(params) {
+      if (!params.searchTime) {
+        return "全部";
+      }
+      if (params.searchTime === "byMonth") {
+        return params.startTime || "选月份";
+      }
+      if (params.searchTime === "custom") {
+        return params.startTime === params.endTime ? params.startTime : "选时段";
+      }
+      return "今天";
+    },
+    getStat() {
+      this.loading = true;
+      uni.showLoading({ title: "加载中" });
+      return rechargeStat(this.params).then(res => {
+        const data = res.data || {};
+        this.summary = data.summary || { ...EMPTY_SUMMARY };
+        this.channelList = data.channelList || [];
+        this.customerList = data.customerList || [];
+        this.loading = false;
+        uni.hideLoading();
+      }).catch(() => {
+        this.loading = false;
+        uni.hideLoading();
+        this.$msg("加载失败");
+      });
+    },
+    moneyText(value) {
+      const num = Number(value || 0);
+      if (isNaN(num)) {
+        return "0";
+      }
+      const fixed = num.toFixed(2);
+      const text = fixed.replace(/\.00$/, "").replace(/(\.\d)0$/, "$1");
+      const parts = text.split(".");
+      parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");
+      return parts.join(".");
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.recharge-stat-page {
+  min-height: 100vh;
+  box-sizing: border-box;
+  padding: 24upx 28upx 36upx;
+  background: #f6f7f9;
+}
+
+.filter-row {
+  display: flex;
+  align-items: center;
+  margin-bottom: 22upx;
+}
+
+.filter-chip {
+  display: flex;
+  align-items: center;
+  height: 56upx;
+  padding: 0 22upx;
+  background: #ffffff;
+  border: 1upx solid #eef1f4;
+  border-radius: 28upx;
+  box-shadow: 0 8upx 22upx rgba(21, 34, 52, 0.04);
+}
+
+.filter-icon {
+  width: 28upx;
+  height: 28upx;
+  margin-right: 8upx;
+}
+
+.date-select {
+  min-width: 82upx;
+}
+
+.summary-grid {
+  display: flex;
+  margin-bottom: 36upx;
+}
+
+.summary-card {
+  flex: 1;
+  min-width: 0;
+  min-height: 126upx;
+  box-sizing: border-box;
+  padding: 18upx 20upx;
+  background: #ffffff;
+  border-radius: 16upx;
+}
+
+.summary-card:not(:last-child) {
+  margin-right: 20upx;
+}
+
+.summary-title {
+  font-size: 28upx;
+  line-height: 36upx;
+  color: #05070b;
+  font-weight: 700;
+}
+
+.summary-amount {
+  margin-top: 22upx;
+  font-size: 34upx;
+  line-height: 40upx;
+  color: #1a202b;
+  font-weight: 800;
+  white-space: nowrap;
+}
+
+.summary-count {
+  margin-top: 4upx;
+  font-size: 24upx;
+  line-height: 28upx;
+  color: #707784;
+  font-weight: 600;
+}
+
+.clickable-money,
+.clickable-cell {
+  cursor: pointer;
+}
+
+.clickable-money {
+  color: #09a85a;
+}
+
+.stat-panel {
+  margin-bottom: 28upx;
+  padding: 28upx 28upx 8upx;
+  background: #ffffff;
+  border-radius: 28upx;
+}
+
+.panel-title {
+  margin-bottom: 18upx;
+  font-size: 34upx;
+  line-height: 44upx;
+  color: #151922;
+  font-weight: 800;
+}
+
+.stat-row {
+  display: flex;
+  align-items: center;
+  min-height: 78upx;
+  border-bottom: 1upx solid #eef0f3;
+}
+
+.table-head {
+  min-height: 44upx;
+  color: #b2bac6;
+  font-size: 24upx;
+  font-weight: 700;
+}
+
+.data-row {
+  padding: 12upx 0;
+}
+
+.name-cell,
+.amount-cell,
+.real-cell {
+  min-width: 0;
+  box-sizing: border-box;
+}
+
+.name-cell {
+  width: 26%;
+  padding-right: 12upx;
+}
+
+.amount-cell {
+  width: 30%;
+}
+
+.real-cell {
+  width: 22%;
+}
+
+.strong-text {
+  color: #11151c;
+  font-size: 28upx;
+  line-height: 36upx;
+  font-weight: 800;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+.money {
+  color: #0f1319;
+  font-size: 28upx;
+  line-height: 34upx;
+  font-weight: 800;
+  white-space: nowrap;
+}
+
+.count {
+  margin-top: 6upx;
+  color: #c4c8cf;
+  font-size: 23upx;
+  line-height: 28upx;
+  font-weight: 600;
+}
+
+.real-cell {
+  text-align: right;
+}
+
+.empty-text {
+  padding: 44upx 0;
+  color: #a2a9b3;
+  text-align: center;
+  font-size: 26upx;
+}
+</style>

+ 6 - 1
hdApp/src/api/member/index.js

@@ -37,6 +37,11 @@ export const getList = data => {
 	return https.get("/custom/list", data);
 };
 
+//客户余额统计
+export const getBalanceStat = data => {
+	return https.get("/custom/balance-stat", data);
+};
+
 //商家端客户详情,包括资产、订单和备注
 export const getUserDet = data => {
 	return https.get("/custom/detail", data);
@@ -55,4 +60,4 @@ export const add = data => {
 // 延期付
 export const debt = data => {
 	return https.get("/custom/debt", data);
-};
+};

+ 11 - 1
hdApp/src/api/recharge/index.js

@@ -20,6 +20,16 @@ export const rechargeList = data => {
 	return https.get('/recharge/list', data)
 }
 
+//充值统计
+export const rechargeStat = data => {
+	return https.get('/recharge/stat', data)
+}
+
+//充值记录退款
+export const refundRecharge = data => {
+	return https.post('/recharge/refund', data)
+}
+
 // 活动页
 export const rechargeRenew = data => {
 	return https.get('/recharge/renew', data)
@@ -42,4 +52,4 @@ export const getGhsHb = data => {
 
 export const getMallRechargeShortLink = data => {
 	return https.get('/recharge/get-mall-recharge-short-link', data)
-}
+}

+ 21 - 5
hdApp/src/components/module/dateSelect.vue

@@ -1,3 +1,4 @@
+<!-- 通用日期筛选组件:提供快捷、月份、单日和时段选择,向业务页面输出统一查询参数。 -->
 <template>
     <view class="select-bg_bx">
         <view v-if="isShowTit" @click="switchCut" :class="['select-title',dropdownShow?'select_corlor':'']">
@@ -32,7 +33,7 @@
                     <view class="title">快捷选项</view>
                     <view class="item-child_box">
                         <button
-                            v-for="(item,index) in getDictionariesInfo.dateDefaultOption"
+                            v-for="(item,index) in dateDefaultOptions"
                             :key="index"
                             @click="changeDateOption(item)"
                             :class="['admin-button-com','mini-btn',selectItemId === item.id?'blue':'default']">
@@ -88,6 +89,10 @@ export default {
         defaultShowName:{
             type: String,
             default: '今天'
+        },
+        showAllOption: {
+            type: Boolean,
+            default: false
         }
     },
     data(){
@@ -112,7 +117,15 @@ export default {
         }
     },
     computed:{
-        ...mapGetters(["getDictionariesInfo"])
+        ...mapGetters(["getDictionariesInfo"]),
+        // 快捷日期项按业务需要追加“全部”,默认保持旧页面选项不变。
+        dateDefaultOptions() {
+            const options = this.getDictionariesInfo.dateDefaultOption || [];
+            if (!this.showAllOption) {
+                return options;
+            }
+            return options.concat([{name: '全部', value: 'all' }]);
+        }
     },
     mounted(){
         let nowDate = new Date();
@@ -122,13 +135,14 @@ export default {
     onLoad(){
     },
     methods:{
+        // 选择月份后同步展示文案,并把月份查询值回传给业务页面。
         bindMonthChange(e){
             this.dropdownShow = !this.dropdownShow
             this.dropdownShow = false;
             let currentMonth = e.target.value
             this.defaultDateName = currentMonth
             this.showMonth = currentMonth
-            this.$emit('selectDateFn',{searchTime:'byMonth',startTime:currentMonth})
+            this.$emit('selectDateFn',{searchTime:'byMonth',startTime:currentMonth,showName:currentMonth})
         },
         showDatePicker(index){//显示
             this.showPicker = true;
@@ -148,6 +162,7 @@ export default {
                 this.endDate = e.value
             }
         },
+        // 确认自定义日期,统一输出单日或时段查询边界。
         customDateConfirm() {
             if(this.$util.isEmpty(this.startDate)){
                 this.$msg('请选择开始日期')
@@ -159,7 +174,7 @@ export default {
             }
             this.dropdownShow = false
             this.defaultDateName = this.startDate == this.endDate ?  this.startDate : '选时段' 
-            this.$emit('selectDateFn',{searchTime:'custom',startTime:this.startDate,endTime:this.endDate})
+            this.$emit('selectDateFn',{searchTime:'custom',startTime:this.startDate,endTime:this.endDate,showName:this.defaultDateName})
         },
         cancelPicker(){
             this.showPicker = false;
@@ -167,10 +182,11 @@ export default {
         switchCut(){
             this.dropdownShow = !this.dropdownShow
         },
+        // 选择快捷日期项,保留展示名称供跳转页面复用。
         changeDateOption(val){
             this.dropdownShow = false;
             this.defaultDateName = val.name;
-            this.$emit('selectDateFn',{searchTime:val.value})
+            this.$emit('selectDateFn',{searchTime:val.value,showName:val.name})
         }
     }
 }

+ 2 - 1
hdApp/src/pages.json

@@ -390,7 +390,8 @@
 				{"path": "kind","style": {"navigationBarTitleText": "生产统计"}},
 				{"path": "kindLz","style": {"navigationBarTitleText": "绿植统计"}},
 				{"path": "makeStat","style": {"navigationBarTitleText": "员工生产统计"}},
-				{"path": "waste","style": {"navigationBarTitleText": "损耗总览"}}
+				{"path": "waste","style": {"navigationBarTitleText": "损耗总览"}},
+				{"path": "rechargeStat","style": {"navigationBarTitleText": "充值统计"}}
 			]
 		},
 		{

+ 4 - 1
mallApp/src/pages/user/edit.vue

@@ -51,6 +51,9 @@ export default {
       let prevPage = pages[ pages.length - 2 ];
       prevPage.$vm.pageAction = {refresh:1}
 	},
+	onShow(){
+		this.getLoginInfo();
+	},
 	methods: {
 		logout(){
 			let that = this
@@ -72,7 +75,7 @@ export default {
 			})
 		},
 		init(){
-			this.getLoginInfo();
+			//this.getLoginInfo();
 		},
 		getLoginInfo(){
 			currentInfo().then(res=>{