feat: 开户备注、账单展示优化与后台代理管理增强
- 新增初始上分备注(日常上分/开户赠金/自定义)及前后台校验与展示 - 优化钱包流水类型与备注显示,区分管理员/代理/玩家上下分 - 修复登录后语言被后端覆盖的问题,登录时同步当前语言到服务端 - 后台代理/玩家表格操作栏重构,充值订单增加备注列 - 前台个人中心、充值、账单与验证码组件体验优化 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import { ref, computed, onMounted, onUnmounted } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import api from '../api';
|
||||
@@ -31,7 +31,13 @@ type CashbackRecord = {
|
||||
};
|
||||
|
||||
const items = ref<CashbackRecord[]>([]);
|
||||
const loading = ref(true);
|
||||
const loading = ref(false);
|
||||
const initialLoading = ref(true);
|
||||
const page = ref(1);
|
||||
const hasMore = ref(true);
|
||||
|
||||
const sentinel = ref<HTMLElement | null>(null);
|
||||
let observer: IntersectionObserver | null = null;
|
||||
|
||||
const totalAmount = computed(() =>
|
||||
items.value.reduce((sum, row) => sum + Math.abs(parseFloat(row.amount) || 0), 0),
|
||||
@@ -61,25 +67,59 @@ function formatTime(v: string | null) {
|
||||
});
|
||||
}
|
||||
|
||||
async function fetchRecords() {
|
||||
async function fetchRecords(p = 1) {
|
||||
if (loading.value) return;
|
||||
loading.value = true;
|
||||
try {
|
||||
const { data } = await api.get('/player/cashbacks');
|
||||
items.value = data.data ?? [];
|
||||
const { data } = await api.get('/player/cashbacks', { params: { page: p } });
|
||||
const result = data.data ?? { items: [], total: 0, pageSize: 20 };
|
||||
const newItems = result.items ?? [];
|
||||
|
||||
if (p === 1) {
|
||||
items.value = newItems;
|
||||
} else {
|
||||
items.value = [...items.value, ...newItems];
|
||||
}
|
||||
|
||||
const pageSize = result.pageSize ?? 20;
|
||||
hasMore.value = newItems.length >= pageSize;
|
||||
page.value = p;
|
||||
} catch {
|
||||
items.value = [];
|
||||
if (p === 1) items.value = [];
|
||||
} finally {
|
||||
loading.value = false;
|
||||
initialLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
useOnLocaleChange(fetchRecords);
|
||||
|
||||
const { pullDistance, spinning, progress } = usePullToRefresh({
|
||||
onRefresh: fetchRecords,
|
||||
useOnLocaleChange(() => {
|
||||
items.value = [];
|
||||
page.value = 1;
|
||||
hasMore.value = true;
|
||||
initialLoading.value = true;
|
||||
fetchRecords(1);
|
||||
});
|
||||
|
||||
onMounted(fetchRecords);
|
||||
const { pullDistance, spinning, progress } = usePullToRefresh({
|
||||
onRefresh: async () => { await fetchRecords(1); },
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
fetchRecords(1);
|
||||
observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
if (entries[0].isIntersecting && hasMore.value && !loading.value) {
|
||||
fetchRecords(page.value + 1);
|
||||
}
|
||||
},
|
||||
{ rootMargin: '200px' },
|
||||
);
|
||||
if (sentinel.value) observer.observe(sentinel.value);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
observer?.disconnect();
|
||||
});
|
||||
|
||||
const pullIndicatorStyle = () => ({
|
||||
height: `${pullDistance.value}px`,
|
||||
@@ -144,6 +184,16 @@ const pullIndicatorStyle = () => ({
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<div ref="sentinel" class="sentinel" />
|
||||
|
||||
<div v-if="loading && items.length > 0" class="load-more-spinner">
|
||||
<GoldSpinner :size="24" />
|
||||
</div>
|
||||
|
||||
<div v-else-if="!hasMore && items.length > 0" class="end-hint">
|
||||
{{ t('common.no_more') }}
|
||||
</div>
|
||||
|
||||
<div v-else class="empty">
|
||||
<p class="empty-title">{{ t('cashback.empty') }}</p>
|
||||
<p class="empty-hint">{{ t('cashback.empty_hint') }}</p>
|
||||
@@ -351,4 +401,23 @@ const pullIndicatorStyle = () => ({
|
||||
line-height: 1.5;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.sentinel {
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
.load-more-spinner {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 20px 0 8px;
|
||||
}
|
||||
|
||||
.end-hint {
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
color: #555;
|
||||
font-weight: 600;
|
||||
padding: 16px 0 4px;
|
||||
letter-spacing: 0.03em;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user