feat: 支持会员管理筛选

This commit is contained in:
richarjiang
2026-04-07 09:22:58 +08:00
parent 58c7588a96
commit 0ca93ec97e
6 changed files with 129 additions and 8 deletions

View File

@@ -15,6 +15,19 @@
<view v-if="searchQuery" class="search-clear" @tap="onClear">
<text class="search-clear-icon">×</text>
</view>
<picker
class="type-picker"
mode="selector"
:value="cardTypeIndex"
:range="cardTypeOptions"
range-key="label"
@change="onCardTypeChange"
>
<view class="type-picker-inner">
<text class="type-picker-text">{{ cardTypeOptions[cardTypeIndex].label }}</text>
<text class="type-picker-arrow"></text>
</view>
</picker>
<view class="search-btn" @tap="onSearch">
<text class="search-btn-text">搜索</text>
</view>
@@ -115,7 +128,7 @@
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { ref, onMounted, onUnmounted } from 'vue'
import { onReachBottom } from '@dcloudio/uni-app'
import CustomNavBar from '../../components/CustomNavBar.vue'
import { getSystemLayout } from '../../utils/system'
@@ -140,6 +153,29 @@ const detailMember = ref<MemberSummary | null>(null)
const LIMIT = 20
const cardTypeOptions = [
{ label: '全部', value: '' },
{ label: '体验卡', value: 'TRIAL' },
{ label: '次卡', value: 'TIMES' },
{ label: '月卡', value: 'DURATION' },
{ label: '无卡', value: 'NONE' },
]
const cardTypeIndex = ref(0)
let cardTypeDebounceTimer: ReturnType<typeof setTimeout> | null = null
function onCardTypeChange(e: { detail: { value: number } }) {
cardTypeIndex.value = e.detail.value
if (cardTypeDebounceTimer) clearTimeout(cardTypeDebounceTimer)
cardTypeDebounceTimer = setTimeout(() => {
loadMembers(true)
cardTypeDebounceTimer = null
}, 300)
}
onUnmounted(() => {
if (cardTypeDebounceTimer) clearTimeout(cardTypeDebounceTimer)
})
async function loadMembers(reset = false) {
if (loading.value) return
if (reset) {
@@ -149,10 +185,12 @@ async function loadMembers(reset = false) {
loading.value = true
try {
const search = searchQuery.value.trim()
const cardType = cardTypeOptions[cardTypeIndex.value].value
const result = await adminStore.fetchMembers({
page: page.value,
limit: LIMIT,
...(search ? { search } : {}),
...(cardType ? { cardType } : {}),
})
if (reset) {
members.value = [...result.items]
@@ -229,7 +267,7 @@ onMounted(() => loadMembers(true))
.search-clear {
position: absolute;
right: 168rpx;
right: 260rpx;
width: 44rpx;
height: 44rpx;
display: flex;
@@ -251,6 +289,37 @@ onMounted(() => loadMembers(true))
.search-btn-text { font-size: 26rpx; font-weight: 600; color: $accent-color; }
/* ── Type picker ──────────────────────────── */
.type-picker {
flex-shrink: 0;
background: $bg-page;
border-radius: 36rpx;
padding: 0 20rpx;
height: 72rpx;
display: flex;
align-items: center;
}
.type-picker-inner {
display: flex;
align-items: center;
gap: 8rpx;
}
.type-picker-text {
font-size: 24rpx;
color: $text-secondary;
max-width: 80rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.type-picker-arrow {
font-size: 20rpx;
color: $text-hint;
}
/* ── Stats row ───────────────────────────── */
.stats-row {
display: flex;