| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560 |
- <template>
- <view class="page">
- <u-navbar title="储值管理" :autoBack="true" :placeholder="true"></u-navbar>
- <!-- 总开关 -->
- <view class="card switch-card">
- <text class="label">储值功能</text>
- <u-switch v-model="enabled" activeColor="#2979ff" :disabled="toggling" @change="onToggle"></u-switch>
- </view>
- <!-- 数据指标 -->
- <view class="stats-card" :style="{ backgroundImage: 'url(' + statsBg + ')' }">
- <view class="balance-block">
- <text class="balance-label">储值余额(元)</text>
- <text class="balance-value">¥ {{ balanceText }}</text>
- </view>
- <view class="stats-panel">
- <view class="stat" v-for="(item, index) in stats" :key="index">
- <view class="value">{{ item.value }}</view>
- <view class="name">{{ item.name }}</view>
- </view>
- </view>
- </view>
- <!-- 操作入口 -->
- <view class="actions card">
- <view class="action-btn" @click="goSettings">
- <image class="action-icon" :src="iconSettings" mode="aspectFit"></image>
- <text class="action-text">储值设置</text>
- <image class="action-more" :src="iconMore" mode="aspectFit"></image>
- </view>
- <view class="action-divider"></view>
- <view class="action-btn" @click="goShareStore">
- <image class="action-icon" :src="iconShareStore" mode="aspectFit"></image>
- <text class="action-text">共享门店</text>
- <image class="action-more" :src="iconMore" mode="aspectFit"></image>
- </view>
- </view>
- <!-- 账户明细 -->
- <view class="card detail-card">
- <view class="detail-head">
- <text class="title">账户明细</text>
- <view class="more" @click="goAll">
- 全部
- <u-icon name="arrow-right" color="#999" size="12"></u-icon>
- </view>
- </view>
- <view class="list" v-if="records.length">
- <view class="item" v-for="(item, index) in records" :key="item.id || index" @click="goDetail(item)">
- <image class="left-icon" :src="getTypeIcon(item.type)" mode="aspectFit"></image>
- <view class="info">
- <view class="row1">
- <text class="name">{{ item.title }}</text>
- <text class="amount" :class="{ plus: item.amount > 0 }">
- {{ item.amount > 0 ? '+' : '' }}{{ formatMoney(item.amount) }}
- </text>
- </view>
- <view class="row2">
- <text class="store">{{ item.storeName }}</text>
- <text class="time">{{ item.time }}</text>
- </view>
- </view>
- </view>
- </view>
- <view class="empty" v-else>暂无明细</view>
- </view>
- <!-- 开启/关闭确认弹窗 -->
- <u-popup v-model="confirmVisible" mode="center" border-radius="24" :mask-close-able="false">
- <view class="confirm-dialog">
- <view class="confirm-title">提示</view>
- <view class="confirm-body">
- <text class="confirm-text">{{ confirmContent }}</text>
- </view>
- <view class="confirm-footer">
- <view class="btn cancel" @click="onConfirmCancel">取消</view>
- <view class="btn ok" @click="onConfirmOk">确定</view>
- </view>
- </view>
- </u-popup>
- </view>
- </template>
- <script>
- import prepaidApi, { getMerchantId } from '@/api/memberPrepaid.js'
- export default {
- data() {
- return {
- enabled: false,
- toggling: false,
- confirmVisible: false,
- confirmContent: '',
- pendingEnabled: null,
- balance: 0,
- balanceText: '0.00',
- statsBg: '/static/topUp/indexBg.png',
- iconSettings: '/static/topUp/prepaidsettings.png',
- iconShareStore: '/static/topUp/sharedstores.png',
- iconMore: '/static/topUp/more.png',
- iconRecharge: '/static/topUp/recharge.png',
- iconSpend: '/static/topUp/expenditure.png',
- iconGive: '/static/topUp/give.png',
- stats: [
- { name: '储值总额(元)', value: '0.00' },
- { name: '储值总用户', value: '0' },
- { name: '储值余额用户', value: '0' }
- ],
- records: []
- };
- },
- onShow() {
- this.bootstrap();
- },
- methods: {
- formatMoney(val) {
- const n = Number(val);
- if (Number.isNaN(n)) return '0.00';
- return n.toFixed(2);
- },
- formatCount(val) {
- return String(Number(val) || 0);
- },
- getTypeIcon(type) {
- if (type === 'spend') return this.iconSpend;
- if (type === 'gift') return this.iconGive;
- return this.iconRecharge;
- },
- mapLedgerItem(item) {
- const txType = String(item.transactionType || '').toUpperCase();
- const isRecharge = txType === 'RECHARGE';
- const isGift = txType === 'GIFT' || txType === 'GIVE';
- const userName = item.userName || '用户';
- const changeAmount = Number(item.changeAmount);
- let type = 'spend';
- let titlePrefix = '支出';
- if (isGift) {
- type = 'gift';
- titlePrefix = '赠送';
- } else if (isRecharge) {
- type = 'recharge';
- titlePrefix = '充值';
- }
- return {
- id: item.ledgerId,
- type,
- title: `${titlePrefix}-${userName}`,
- storeName: item.storeName || '',
- amount: Number.isNaN(changeAmount)
- ? isRecharge || isGift
- ? Number(item.payAmount || 0) + Number(item.giftAmount || 0)
- : -Math.abs(Number(item.payAmount || 0))
- : changeAmount,
- time: item.transactionTime || ''
- };
- },
- bootstrap() {
- const merchantId = getMerchantId();
- if (!merchantId) {
- uni.showToast({ title: '未获取到门店信息', icon: 'none' });
- return;
- }
- prepaidApi.getFeatureStatus(merchantId).then((res) => {
- if (res.data.code !== 200) {
- uni.showToast({ title: res.data.message || '加载失败', icon: 'none' });
- return;
- }
- const data = res.data.result || {};
- const auditStatus = Number(data.auditStatus);
- if (auditStatus !== 2) {
- uni.redirectTo({ url: '/pages/topUp/index' });
- return;
- }
- this.enabled = Number(data.operationStatus) === 1;
- this.loadOverview(merchantId);
- });
- },
- loadOverview(merchantId) {
- prepaidApi.getOverview(merchantId).then((res) => {
- if (res.data.code !== 200 || !res.data.result) return;
- const data = res.data.result;
- console.log('11111data',data)
- this.balance = Number(data.balance) || 0;
- this.balanceText = this.formatMoney(data.balance);
- this.stats = [
- { name: '储值总额(元)', value: this.formatMoney(data.totalRechargeAmount) },
- { name: '储值总用户', value: this.formatCount(data.totalUserCount) },
- { name: '储值余额用户', value: this.formatCount(data.balanceUserCount) }
- ];
- const list = Array.isArray(data.latestLedgers) ? data.latestLedgers : [];
- this.records = list.map((item) => this.mapLedgerItem(item));
- });
- },
- onToggle(val) {
- this.enabled = !val;
- this.pendingEnabled = val;
- if (val) {
- this.confirmContent = '是否确认开启门店储值功能?';
- } else if (this.balance > 0) {
- this.confirmContent = '用户账户余额未使用完是否确认关闭门店储值功能?';
- } else {
- this.confirmContent = '是否确认关闭门店储值功能?';
- }
- this.confirmVisible = true;
- },
- onConfirmCancel() {
- this.confirmVisible = false;
- this.pendingEnabled = null;
- },
- onConfirmOk() {
- const val = this.pendingEnabled;
- this.confirmVisible = false;
- this.pendingEnabled = null;
- if (val === null || val === undefined || this.toggling) return;
- const merchantId = getMerchantId();
- this.toggling = true;
- const req = val
- ? prepaidApi.openFeature(merchantId)
- : prepaidApi.closeFeature({}, merchantId);
- req
- .then((res) => {
- if (res.data.code === 200) {
- const data = res.data.result || {};
- this.enabled = Number(data.operationStatus) === 1;
- uni.showToast({
- title: this.enabled ? '已开启储值功能' : '已关闭储值功能',
- icon: 'none'
- });
- } else {
- uni.showToast({ title: res.data.message || '操作失败', icon: 'none' });
- }
- })
- .catch(() => {
- uni.showToast({ title: '网络异常,请稍后重试', icon: 'none' });
- })
- .finally(() => {
- this.toggling = false;
- });
- },
- goSettings() {
- uni.navigateTo({ url: '/pages/topUp/settings' });
- },
- goShareStore() {
- uni.navigateTo({ url: '/pages/topUp/shareStore' });
- },
- goAll() {
- uni.navigateTo({ url: '/pages/topUp/records' });
- },
- goDetail(item) {
- const type = item.type === 'spend' ? 'spend' : 'recharge';
- uni.navigateTo({
- url: `/pages/topUp/recordDetail?id=${item.id || ''}&type=${type}`
- });
- }
- }
- };
- </script>
- <style scoped lang="scss">
- .page {
- min-height: 100vh;
- background: #f5f6f8;
- padding: 24rpx;
- padding-bottom: calc(40rpx + env(safe-area-inset-bottom));
- }
- .card {
- background: #fff;
- border-radius: 16rpx;
- margin-bottom: 24rpx;
- }
- .switch-card {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 28rpx 32rpx;
- .label {
- font-size: 30rpx;
- color: #333;
- font-weight: 500;
- }
- }
- .stats-card {
- border-radius: 20rpx;
- margin-bottom: 24rpx;
- padding: 36rpx 28rpx 24rpx;
- background-color: #3a9bff;
- background-repeat: no-repeat;
- background-size: 100% 100%;
- background-position: center;
- overflow: hidden;
- box-sizing: border-box;
- .balance-block {
- padding: 8rpx 8rpx 32rpx;
- .balance-label {
- display: block;
- font-size: 26rpx;
- color: rgba(255, 255, 255, 0.9);
- margin-bottom: 16rpx;
- }
- .balance-value {
- display: block;
- font-size: 56rpx;
- font-weight: 600;
- color: #fff;
- line-height: 1.2;
- text-align: center;
- }
- }
- .stats-panel {
- display: flex;
- align-items: stretch;
- background: #fff;
- border-radius: 16rpx;
- padding: 28rpx 8rpx;
- .stat {
- flex: 1;
- text-align: center;
- position: relative;
- &:not(:last-child)::after {
- content: '';
- position: absolute;
- right: 0;
- top: 50%;
- transform: translateY(-50%);
- width: 1px;
- height: 56rpx;
- background: #f0f0f0;
- }
- .value {
- font-size: 32rpx;
- font-weight: 600;
- color: #333;
- line-height: 1.3;
- }
- .name {
- font-size: 22rpx;
- color: #999;
- margin-top: 10rpx;
- }
- }
- }
- }
- .actions {
- display: flex;
- align-items: center;
- padding: 8rpx 0;
- margin-bottom: 24rpx;
- .action-btn {
- flex: 1;
- display: flex;
- align-items: center;
- justify-content: center;
- padding: 28rpx 16rpx;
- gap: 12rpx;
- }
- .action-icon {
- width: 44rpx;
- height: 44rpx;
- flex-shrink: 0;
- }
- .action-text {
- font-size: 28rpx;
- color: #333;
- font-weight: 500;
- }
- .action-more {
- width: 28rpx;
- height: 28rpx;
- flex-shrink: 0;
- }
- .action-divider {
- width: 1px;
- height: 40rpx;
- background: #ebebeb;
- flex-shrink: 0;
- }
- }
- .detail-card {
- padding: 28rpx 24rpx 8rpx;
- .detail-head {
- display: flex;
- align-items: center;
- justify-content: space-between;
- margin-bottom: 8rpx;
- .title {
- font-size: 30rpx;
- font-weight: 600;
- color: #333;
- }
- .more {
- display: flex;
- align-items: center;
- font-size: 24rpx;
- color: #999;
- gap: 4rpx;
- }
- }
- .item {
- display: flex;
- align-items: center;
- padding: 28rpx 0;
- border-bottom: 1px solid #f5f5f5;
- &:last-child {
- border-bottom: none;
- }
- .left-icon {
- width: 72rpx;
- height: 72rpx;
- margin-right: 20rpx;
- flex-shrink: 0;
- border-radius: 50%;
- }
- .info {
- flex: 1;
- min-width: 0;
- }
- .row1,
- .row2 {
- display: flex;
- justify-content: space-between;
- align-items: center;
- }
- .row1 {
- margin-bottom: 10rpx;
- .name {
- font-size: 28rpx;
- color: #333;
- font-weight: 500;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- max-width: 360rpx;
- }
- .amount {
- font-size: 30rpx;
- font-weight: 600;
- color: #333;
- flex-shrink: 0;
- &.plus {
- color: #2f7bff;
- }
- }
- }
- .row2 {
- .store,
- .time {
- font-size: 22rpx;
- color: #999;
- }
- .store {
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- max-width: 360rpx;
- }
- }
- }
- .empty {
- padding: 60rpx 0;
- text-align: center;
- color: #ccc;
- font-size: 26rpx;
- }
- }
- .confirm-dialog {
- width: 560rpx;
- background: #fff;
- border-radius: 24rpx;
- overflow: hidden;
- padding: 48rpx 40rpx 40rpx;
- box-sizing: border-box;
- .confirm-title {
- text-align: center;
- font-size: 34rpx;
- font-weight: 600;
- color: #333;
- line-height: 1.4;
- margin-bottom: 28rpx;
- }
- .confirm-body {
- padding: 0 8rpx 40rpx;
- .confirm-text {
- display: block;
- text-align: center;
- font-size: 28rpx;
- color: #333;
- line-height: 1.6;
- }
- }
- .confirm-footer {
- display: flex;
- align-items: center;
- justify-content: space-between;
- gap: 24rpx;
- .btn {
- flex: 1;
- height: 80rpx;
- line-height: 80rpx;
- text-align: center;
- border-radius: 40rpx;
- font-size: 30rpx;
- box-sizing: border-box;
- }
- .cancel {
- color: #999;
- background: #fff;
- border: 2rpx solid #dcdcdc;
- }
- .ok {
- color: #fff;
- background: linear-gradient(90deg, #3ab3ff 0%, #2472ff 100%);
- border: none;
- }
- }
- }
- </style>
|