bm-bmt/pages/assets/points-convert-list.vue

659 lines
14 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="asset-page asset-theme points-list-page">
<asset-page-shell title="积分转换" />
<view class="asset-scroll points-list-scroll">
<view class="summary-card">
<text class="summary-card__text">
已选择订单
<text class="summary-card__accent asset-number-font">{{
selectedCount
}}</text>
<text class="summary-card__accent asset-number-font">{{
displaySelectedTotal
}}</text>
积分
</text>
</view>
<view class="selection-card">
<text class="selection-card__title">按订单积分区间筛选</text>
<view class="range-tabs">
<view
v-for="tab in rangeTabs"
:key="tab.key"
class="range-tabs__item"
:class="{ 'range-tabs__item--active': activeRange === tab.key }"
@click="activeRange = tab.key"
>
{{ tab.label }}
</view>
</view>
<view v-if="filteredItems.length" class="selection-list">
<view
v-for="item in filteredItems"
:key="item.id"
class="selection-item"
:class="{ 'selection-item--disabled': item.disabled }"
@click="toggleItem(item)"
>
<view
class="selection-item__checkbox"
:class="{
'selection-item__checkbox--checked': isChecked(item),
'selection-item__checkbox--disabled': item.disabled,
}"
>
<text
v-if="isChecked(item)"
class="selection-item__checkbox-mark"
>✓</text
>
</view>
<view class="selection-item__main">
<view class="selection-item__title-row">
<text class="selection-item__title">{{ item.title }}</text>
<view
v-if="item.statusText"
class="selection-item__status"
:class="{
'selection-item__status--warning': item.disabled,
}"
>
{{ item.statusText }}
</view>
</view>
<text class="selection-item__time asset-number-font">{{
item.time
}}</text>
</view>
<view class="selection-item__side">
<text class="selection-item__label">转换积分</text>
<text class="selection-item__amount asset-number-font">{{
item.amount
}}</text>
</view>
</view>
</view>
<view v-else class="selection-empty">
<text class="selection-empty__title">暂无可转换订单</text>
<text class="selection-empty__desc">
当前筛选条件下没有符合条件的积分订单。
</text>
</view>
</view>
</view>
<view class="bottom-bar">
<view class="bottom-bar__check-all" @click="toggleAll">
<view
class="bottom-bar__checkbox"
:class="{ 'bottom-bar__checkbox--checked': allChecked }"
>
<text v-if="allChecked" class="bottom-bar__checkbox-mark">✓</text>
</view>
<text class="bottom-bar__check-all-text">全选</text>
</view>
<view
class="bottom-bar__button"
:class="{ 'bottom-bar__button--disabled': !canSubmit }"
@click="submit"
>
{{ submitting ? "提交中..." : "确认转换" }}
</view>
</view>
</view>
</template>
<script>
import AssetPageShell from "../../components/asset-page-shell.vue";
import {
fetchPointsConvertSelection,
submitAssetPointsConvert,
} from "../../api/assets";
export default {
components: {
AssetPageShell,
},
data() {
return {
hasShown: false,
activeRange: "",
submitting: false,
selectedIds: [],
detail: {
items: [],
},
};
},
computed: {
rangeTabs() {
return [
{
key: "below-20",
label: "20以下",
},
{
key: "20-100",
label: "20-100",
},
{
key: "100-200",
label: "100-200",
},
{
key: "200-500",
label: "200-500",
},
{
key: "500-plus",
label: "500以上",
},
];
},
items() {
return Array.isArray(this.detail.items) ? this.detail.items : [];
},
filteredItems() {
return this.items.filter((item) => this.matchRange(item));
},
selectedItems() {
return this.items.filter(
(item) => !item.disabled && this.selectedIds.indexOf(item.id) > -1,
);
},
selectedCount() {
return this.selectedItems.length;
},
selectedTotal() {
return this.selectedItems.reduce(
(total, item) => total + Number(item.amountValue || 0),
0,
);
},
displaySelectedTotal() {
return this.formatAmount(this.selectedTotal);
},
selectableFilteredItems() {
return this.filteredItems.filter((item) => !item.disabled);
},
allChecked() {
if (!this.selectableFilteredItems.length) {
return false;
}
return this.selectableFilteredItems.every(
(item) => this.selectedIds.indexOf(item.id) > -1,
);
},
canSubmit() {
return this.selectedItems.length > 0 && !this.submitting;
},
},
onLoad() {
this.loadPage(true);
},
onShow() {
if (this.hasShown) {
this.loadPage();
return;
}
this.hasShown = true;
},
methods: {
formatAmount(value) {
const number = Number(value || 0);
if (!Number.isFinite(number)) {
return "0";
}
return Math.round(number)
.toString()
.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
},
matchRange(item) {
const amount = Number(item && item.amountValue ? item.amountValue : 0);
if (!this.activeRange) {
return true;
}
if (this.activeRange === "below-20") {
return amount < 20;
}
if (this.activeRange === "20-100") {
return amount >= 20 && amount < 100;
}
if (this.activeRange === "100-200") {
return amount >= 100 && amount < 200;
}
if (this.activeRange === "200-500") {
return amount >= 200 && amount < 500;
}
if (this.activeRange === "500-plus") {
return amount >= 500;
}
return false;
},
resolveRangeByAmount(amount) {
const numericAmount = Number(amount || 0);
if (numericAmount < 20) {
return "below-20";
}
if (numericAmount < 100) {
return "20-100";
}
if (numericAmount < 200) {
return "100-200";
}
if (numericAmount < 500) {
return "200-500";
}
return "500-plus";
},
isChecked(item) {
return this.selectedIds.indexOf(item.id) > -1;
},
toggleItem(item) {
if (!item || item.disabled) {
return;
}
if (this.isChecked(item)) {
this.selectedIds = this.selectedIds.filter((id) => id !== item.id);
return;
}
this.selectedIds = this.selectedIds.concat(item.id);
},
toggleAll() {
const currentIds = this.selectableFilteredItems.map((item) => item.id);
if (!currentIds.length) {
return;
}
if (this.allChecked) {
this.selectedIds = this.selectedIds.filter(
(id) => currentIds.indexOf(id) === -1,
);
return;
}
this.selectedIds = Array.from(new Set(this.selectedIds.concat(currentIds)));
},
async loadPage(showLoading) {
try {
const result = await fetchPointsConvertSelection(
showLoading
? {
showLoading: true,
loadingText: "加载中",
}
: null,
);
this.detail = result;
const validIds = this.items
.filter((item) => !item.disabled)
.map((item) => item.id);
this.selectedIds = this.selectedIds.filter(
(id) => validIds.indexOf(id) > -1,
);
const rangeKeys = this.rangeTabs.map((item) => item.key);
if (rangeKeys.indexOf(this.activeRange) === -1) {
const firstItem = this.items[0];
this.activeRange = firstItem
? this.resolveRangeByAmount(firstItem.amountValue)
: this.rangeTabs[0].key;
}
} catch (error) {
uni.showToast({
title: error.message || "页面加载失败",
icon: "none",
});
}
},
async submit() {
if (!this.canSubmit) {
uni.showToast({
title: "请选择可转换订单",
icon: "none",
});
return;
}
this.submitting = true;
try {
await submitAssetPointsConvert(
{
ids: this.selectedItems.map((item) => item.sourceId || item.id),
},
{
showLoading: true,
loadingText: "转换中",
},
);
uni.showToast({
title: "转换成功",
icon: "none",
});
setTimeout(() => {
uni.navigateBack();
}, 400);
} catch (error) {
uni.showToast({
title: error.message || "转换失败",
icon: "none",
});
} finally {
this.submitting = false;
}
},
},
};
</script>
<style lang="scss" scoped>
@import "../../styles/tokens.scss";
@import "../../styles/common.scss";
.points-list-page {
min-height: 100vh;
background: #191e32;
}
.points-list-scroll {
min-height: calc(100vh - env(safe-area-inset-top) - 104rpx);
padding: 12rpx 14rpx calc(env(safe-area-inset-bottom) + 150rpx);
}
.summary-card,
.selection-card,
.bottom-bar {
background: #242944;
}
.summary-card,
.selection-card,
.selection-empty {
border-radius: 10rpx;
box-shadow: 0 12rpx 24rpx rgba(8, 13, 30, 0.14);
}
.summary-card {
position: relative;
overflow: hidden;
padding: 28rpx 26rpx;
}
.summary-card::after {
content: "";
position: absolute;
top: -18rpx;
left: 42%;
width: 110rpx;
height: 180rpx;
background: rgba(123, 137, 190, 0.18);
transform: rotate(30deg);
}
.summary-card__text {
position: relative;
z-index: 1;
font-size: 28rpx;
font-weight: 500;
color: #ffffff;
}
.summary-card__accent {
color: #ff8d62;
}
.selection-card {
margin-top: 18rpx;
padding: 28rpx 0 0;
overflow: hidden;
}
.selection-card__title {
display: block;
padding: 0 20rpx;
font-size: 28rpx;
font-weight: 500;
color: #ffffff;
}
.range-tabs {
display: flex;
align-items: center;
margin-top: 26rpx;
padding: 0 18rpx;
border-bottom: 1px solid rgba(129, 141, 183, 0.18);
}
.range-tabs__item {
position: relative;
flex: 1;
padding: 0 4rpx 18rpx;
text-align: center;
font-size: 24rpx;
color: rgba(173, 182, 211, 0.92);
}
.range-tabs__item--active {
color: #ff8d62;
font-weight: 600;
}
.range-tabs__item--active::after {
content: "";
position: absolute;
right: 18rpx;
bottom: 0;
left: 18rpx;
height: 4rpx;
border-radius: 999rpx;
background: #ff8d62;
}
.selection-list {
padding: 0 20rpx;
}
.selection-item {
display: flex;
align-items: flex-start;
padding: 28rpx 0;
border-top: 1px solid rgba(129, 141, 183, 0.18);
}
.selection-item:first-child {
border-top: 0;
}
.selection-item--disabled {
opacity: 0.78;
}
.selection-item__checkbox,
.bottom-bar__checkbox {
display: flex;
align-items: center;
justify-content: center;
width: 32rpx;
height: 32rpx;
border-radius: 6rpx;
border: 2rpx solid rgba(255, 255, 255, 0.9);
background: #ffffff;
flex-shrink: 0;
}
.selection-item__checkbox {
margin-top: 6rpx;
}
.selection-item__checkbox--checked,
.bottom-bar__checkbox--checked {
border-color: #29d39c;
background: #29d39c;
}
.selection-item__checkbox--disabled {
border-color: rgba(205, 212, 235, 0.5);
background: rgba(255, 255, 255, 0.92);
}
.selection-item__checkbox-mark,
.bottom-bar__checkbox-mark {
font-size: 22rpx;
line-height: 1;
color: #ffffff;
}
.selection-item__main {
flex: 1;
min-width: 0;
margin-left: 16rpx;
}
.selection-item__title-row {
display: flex;
align-items: center;
flex-wrap: wrap;
}
.selection-item__title {
font-size: 28rpx;
font-weight: 500;
color: #ffffff;
}
.selection-item__status {
margin-left: 14rpx;
padding: 4rpx 14rpx;
border-radius: 999rpx;
background: rgba(255, 141, 98, 0.14);
font-size: 22rpx;
color: #ff8d62;
}
.selection-item__status--warning {
background: rgba(255, 141, 98, 0.18);
}
.selection-item__time {
display: block;
margin-top: 14rpx;
font-size: 22rpx;
color: rgba(158, 170, 204, 0.9);
}
.selection-item__side {
margin-left: 18rpx;
padding-top: 2rpx;
text-align: right;
}
.selection-item__label {
display: block;
font-size: 26rpx;
color: rgba(240, 243, 255, 0.92);
}
.selection-item__amount {
display: block;
margin-top: 10rpx;
font-size: 28rpx;
font-weight: 700;
color: #ff8d62;
}
.selection-empty {
margin: 22rpx 20rpx 30rpx;
padding: 70rpx 24rpx;
text-align: center;
background: rgba(38, 45, 73, 0.72);
}
.selection-empty__title {
display: block;
font-size: 28rpx;
color: #ffffff;
}
.selection-empty__desc {
display: block;
margin-top: 14rpx;
font-size: 22rpx;
color: rgba(158, 170, 204, 0.86);
}
.bottom-bar {
position: fixed;
right: 0;
bottom: 0;
left: 0;
z-index: 10;
display: flex;
align-items: center;
justify-content: space-between;
padding: 18rpx 20rpx calc(env(safe-area-inset-bottom) + 18rpx);
border-top: 1px solid rgba(129, 141, 183, 0.16);
}
.bottom-bar__check-all {
display: flex;
align-items: center;
}
.bottom-bar__check-all-text {
margin-left: 14rpx;
font-size: 28rpx;
color: #ffffff;
}
.bottom-bar__button {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 170rpx;
height: 78rpx;
padding: 0 34rpx;
border-radius: 999rpx;
background: linear-gradient(135deg, #1ec5ff 0%, #179fe8 100%);
box-shadow: 0 14rpx 24rpx rgba(27, 179, 242, 0.22);
font-size: 32rpx;
font-weight: 600;
color: #ffffff;
}
.bottom-bar__button--disabled {
opacity: 0.5;
}
</style>