fix: ui
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 1m5s

This commit is contained in:
sexygoat
2026-04-25 15:39:06 +08:00
parent 42a7d1410d
commit 88e9cbbf19
2 changed files with 342 additions and 556 deletions

View File

@@ -4,55 +4,40 @@ import { getFundSummary, getCommissionRecords, getCommissionStats } from '@/api/
import type { FundSummary, CommissionRecord, CommissionStats } from '@/api/commission'
import SimplePicker from '@/components/SimplePicker.vue'
// 佣金概览
const commissionSummary = ref<FundSummary | null>(null)
// 店铺ID (从登录信息获取)
const shopId = ref<number>(0)
// 从本地存储获取用户信息
function getUserInfo() {
const userInfo = uni.getStorageSync('user_info')
return userInfo || null
}
// 佣金明细列表
const commissionRecords = ref<CommissionRecord[]>([])
// 佣金统计
const commissionStats = ref<CommissionStats | null>(null)
// 加载状态
const loading = ref(false)
const loadingRecords = ref(false)
// 分页参数
const page = ref(1)
const pageSize = 20
const total = ref(0)
// 搜索关键词
const searchKeyword = ref('')
// 佣金来源筛选
const sourceFilter = ref<string | undefined>(undefined)
const showSourcePicker = ref(false)
const showSearchTypePicker = ref(false)
// 佣金来源选项
const sourceOptions = [
{ label: '全部来源', value: undefined },
{ label: '成本价差', value: 'cost_diff' },
{ label: '一次性佣金', value: 'one_time' },
]
// 获取当前选中的来源名称
const searchType = ref<'iccid' | 'virtual_no' | 'order_no'>('iccid')
const searchTypeOptions = [
{ label: 'ICCID', value: 'iccid' },
{ label: '虚拟号', value: 'virtual_no' },
{ label: '订单号', value: 'order_no' }
]
const selectedSourceName = computed(() => {
if (sourceFilter.value === undefined) return '佣金来源'
if (sourceFilter.value === undefined) return '来源'
const option = sourceOptions.find(o => o.value === sourceFilter.value)
return option?.label || '佣金来源'
return option?.label || '来源'
})
// 格式化金额(分转元,带千分号)
function formatAmount(amount: number) {
const yuan = (amount / 100).toFixed(2)
const parts = yuan.split('.')
@@ -60,7 +45,6 @@ function formatAmount(amount: number) {
return `¥${parts.join('.')}`
}
// 获取佣金来源文本
function getSourceText(source: string) {
const map: Record<string, string> = {
cost_diff: '成本价差',
@@ -70,316 +54,257 @@ function getSourceText(source: string) {
return map[source] || source
}
// 获取佣金状态文本
function getStatusText(status: number) {
const map: Record<number, string> = {
1: '已入账',
2: '已失效',
}
return map[status] || '未知'
return status === 1 ? '已入账' : '已失效'
}
// 获取佣金状态颜色
function getStatusColor(status: number) {
const map: Record<number, { bg: string, text: string }> = {
1: { bg: '#e8f5e9', text: '#2e7d32' }, // 已入账
2: { bg: '#f5f5f5', text: '#999' }, // 已失效
}
return map[status] || { bg: '#f5f5f5', text: '#999' }
}
// 格式化时间
function formatTime(time: string) {
if (!time) return '-'
const date = new Date(time)
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
const hour = String(date.getHours()).padStart(2, '0')
const minute = String(date.getMinutes()).padStart(2, '0')
return `${month}-${day} ${hour}:${minute}`
return `${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')} ${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`
}
// 页面加载
onMounted(() => {
// 从登录信息获取 shop_id
const userInfo = getUserInfo()
const userInfo = uni.getStorageSync('user_info')
if (userInfo?.shop_id) {
shopId.value = userInfo.shop_id
}
loadCommissionSummary()
loadCommissionStats()
loadCommissionRecords()
})
// 加载佣金概览
async function loadCommissionSummary() {
try {
const summary = await getFundSummary()
commissionSummary.value = summary
}
catch (error) {
commissionSummary.value = await getFundSummary()
} catch (error) {
console.error('加载佣金概览失败:', error)
}
}
// 加载佣金统计
async function loadCommissionStats() {
if (!shopId.value) return
try {
const stats = await getCommissionStats(shopId.value)
commissionStats.value = stats
}
catch (error) {
commissionStats.value = await getCommissionStats(shopId.value)
} catch (error) {
console.error('加载佣金统计失败:', error)
}
}
// 加载佣金明细
async function loadCommissionRecords() {
async function loadCommissionRecords(isRefresh = false) {
if (!shopId.value) return
if (isRefresh) {
page.value = 1
commissionRecords.value = []
}
loadingRecords.value = true
try {
const params: any = {
page: page.value,
page_size: pageSize,
}
// 佣金来源筛选
const params: any = { page: page.value, page_size: pageSize }
if (sourceFilter.value) {
params.commission_source = sourceFilter.value
}
// 搜索关键词(支持 ICCID、虚拟号、订单号
if (searchKeyword.value) {
const keyword = searchKeyword.value.trim()
// 根据关键词类型判断搜索字段
if (/^\d{19,20}$/.test(keyword)) {
// ICCID 通常是19-20位数字
params.iccid = keyword
}
else if (/^\d{15}$/.test(keyword)) {
// 虚拟号通常是15位数字
params.virtual_no = keyword
}
else {
// 订单号
params.order_no = keyword
}
if (searchType.value === 'iccid') params.iccid = keyword
else if (searchType.value === 'virtual_no') params.virtual_no = keyword
else params.order_no = keyword
}
const response = await getCommissionRecords(shopId.value, params)
if (page.value === 1) {
commissionRecords.value = response.items || []
}
else {
} else {
commissionRecords.value.push(...(response.items || []))
}
total.value = response.total || 0
}
catch (error) {
} catch (error) {
console.error('加载佣金明细失败:', error)
}
finally {
} finally {
loadingRecords.value = false
}
}
// 搜索
function handleSearch() {
page.value = 1
commissionRecords.value = []
loadCommissionRecords()
loadCommissionRecords(true)
}
// 清空搜索
function clearSearch() {
searchKeyword.value = ''
page.value = 1
commissionRecords.value = []
loadCommissionRecords()
}
// 来源筛选变化
function onSourceConfirm(option: { label: string, value: any }) {
sourceFilter.value = option.value
page.value = 1
commissionRecords.value = []
loadCommissionRecords()
loadCommissionRecords(true)
}
function onSearchTypeConfirm(option: { label: string, value: any }) {
searchType.value = option.value
}
// 加载更多
function loadMore() {
if (commissionRecords.value.length >= total.value) {
return
if (commissionRecords.value.length < total.value) {
page.value++
loadCommissionRecords()
}
page.value++
loadCommissionRecords()
}
// 刷新
function refreshList() {
page.value = 1
commissionRecords.value = []
loadCommissionSummary()
loadCommissionStats()
loadCommissionRecords()
function onScroll(e: any) {
const { scrollTop, scrollHeight, clientHeight } = e.target
if (scrollHeight - scrollTop - clientHeight < 100) {
loadMore()
}
}
</script>
<template>
<view class="bg-[var(--bg-page)] min-h-screen pb-safe">
<view v-if="loading" class="pt-20 center">
<text class="text-14px text-[var(--text-secondary)]">加载中...</text>
</view>
<view v-else>
<view class="bg-brand px-4 py-3 mx-4 mt-4 rounded-12px">
<text class="text-11px text-white/60">可提现佣金</text>
<text class="text-24px font-700 text-white block mt-1">
{{ commissionSummary ? formatAmount(commissionSummary.available_commission) : '--' }}
</text>
<view class="bg-page min-h-screen">
<!-- 顶部搜索区域 -->
<view class="bg-white px-4 pt-4 pb-3">
<!-- 搜索框 -->
<view class="relative flex items-center">
<input
v-model="searchKeyword"
class="flex-1 h-10 bg-[#f1f5f9] rounded-full pl-4 pr-12 text-sm text-[#1e293b] placeholder-[#94a3b8]"
:placeholder="searchType === 'iccid' ? '搜索ICCID' : searchType === 'virtual_no' ? '搜索虚拟号' : '搜索订单号'"
@confirm="handleSearch"
/>
<view class="absolute right-1 flex items-center">
<view v-if="searchKeyword" class="w-8 h-8 flex items-center justify-center text-[#94a3b8]"
@click="searchKeyword = ''">
<i class="i-mdi-close-circle text-lg" />
</view>
<view class="w-8 h-8 flex items-center justify-center text-brand" @click="handleSearch">
<i class="i-mdi-magnify text-xl" />
</view>
</view>
</view>
<view class="bg-[var(--bg-container)] rounded-12px p-4 mx-4 mt-3">
<view class="grid grid-cols-3 gap-4">
<view class="text-center">
<text class="text-11px text-[var(--text-tertiary)] block">累计佣金</text>
<text class="text-16px font-700 text-[var(--text-primary)] mt-1">
<!-- 筛选器 -->
<view class="flex items-center gap-2 mt-3">
<view class="flex items-center gap-1 px-3 py-1.5 bg-[#f8fafc] rounded-full" @click="showSearchTypePicker = true">
<text class="text-12px text-[#64748b]">{{ searchType === 'iccid' ? 'ICCID' : searchType === 'virtual_no' ? '虚拟号' : '订单号' }}</text>
<i class="i-mdi-chevron-down text-12px text-[#94a3b8]" />
</view>
<view class="flex items-center gap-1 px-3 py-1.5 bg-[#f8fafc] rounded-full" @click="showSourcePicker = true">
<text class="text-12px" :class="sourceFilter !== undefined ? 'text-brand font-medium' : 'text-[#64748b]'">{{ selectedSourceName }}</text>
<i class="i-mdi-chevron-down text-12px text-[#94a3b8]" />
</view>
<view v-if="sourceFilter !== undefined" class="flex items-center gap-1 px-2 py-1.5 rounded-full bg-[#ff4d4f]/10 text-[#ff4d4f]"
@click="sourceFilter = undefined; loadCommissionRecords(true)">
<i class="i-mdi-close text-12px" />
<text class="text-12px font-medium">清除</text>
</view>
</view>
</view>
<!-- 内容区域 -->
<view class="px-4 pb-4">
<!-- 概览卡片 -->
<view class="bg-white rounded-16px p-5 mt-4 shadow-[0_2px_12px_rgba(0,0,0,0.08)]">
<view class="flex items-center justify-between">
<view>
<text class="text-12px text-[#999]">可提现佣金</text>
<text class="text-32px font-700 text-[#212121] block mt-1">
{{ commissionSummary ? formatAmount(commissionSummary.available_commission) : '--' }}
</text>
</view>
<image src="@/static/icons/佣金中心.png" class="w-12 h-12 rounded-12px" mode="aspectFit" />
</view>
<view class="flex gap-3 mt-4 pt-4 border-t border-[#f5f5f5]">
<view class="flex-1 text-center">
<text class="text-11px text-[#999]">累计佣金</text>
<text class="text-14px font-600 text-[#212121] block mt-1">
{{ commissionSummary ? formatAmount(commissionSummary.total_commission) : '--' }}
</text>
</view>
<view class="text-center">
<text class="text-11px text-[var(--text-tertiary)] block">冻结</text>
<text class="text-16px font-700 text-[var(--text-tertiary)] mt-1">
{{ commissionSummary ? formatAmount(commissionSummary.frozen_commission) : '--' }}
</text>
</view>
<view class="text-center">
<text class="text-11px text-[var(--text-tertiary)] block">已提现</text>
<text class="text-16px font-700 text-[#52c41a] mt-1">
<view class="flex-1 text-center border-l border-[#f5f5f5]">
<text class="text-11px text-[#999]">已提现</text>
<text class="text-14px font-600 text-[#52c41a] block mt-1">
{{ commissionSummary ? formatAmount(commissionSummary.withdrawn_commission) : '--' }}
</text>
</view>
</view>
</view>
<view class="px-4 mt-4">
<text class="text-14px font-600 text-[var(--text-primary)] block mb-2">佣金统计</text>
<view class="bg-[var(--bg-container)] rounded-12px p-4">
<view class="flex items-center justify-between">
<text class="text-13px text-[var(--text-secondary)]">成本价差</text>
<view class="flex items-center gap-2">
<text class="text-14px font-600 text-[var(--text-primary)]">
{{ commissionStats ? formatAmount(commissionStats.cost_diff_amount) : '--' }}
</text>
<text class="text-11px text-[var(--text-tertiary)]">
{{ commissionStats ? (commissionStats.cost_diff_percent / 10).toFixed(1) + '%' : '--' }}
</text>
</view>
</view>
<view class="flex items-center justify-between mt-3">
<text class="text-13px text-[var(--text-secondary)]">一次性佣金</text>
<view class="flex items-center gap-2">
<text class="text-14px font-600 text-[var(--text-primary)]">
{{ commissionStats ? formatAmount(commissionStats.one_time_amount) : '--' }}
</text>
<text class="text-11px text-[var(--text-tertiary)]">
{{ commissionStats ? (commissionStats.one_time_percent / 10).toFixed(1) + '%' : '--' }}
</text>
</view>
</view>
<view class="flex items-center justify-between mt-3 pt-3 border-t border-[var(--border-secondary)]">
<text class="text-13px font-600 text-[var(--text-primary)]">总计</text>
<text class="text-16px font-700 text-brand">
{{ commissionStats ? formatAmount(commissionStats.total_amount) : '--' }}
<view class="flex-1 text-center border-l border-[#f5f5f5]">
<text class="text-11px text-[#999]">冻结</text>
<text class="text-14px font-600 text-[#ff4d4f] block mt-1">
{{ commissionSummary ? formatAmount(commissionSummary.frozen_commission) : '--' }}
</text>
</view>
</view>
</view>
<view class="px-4 mt-4">
<text class="text-14px font-600 text-[var(--text-primary)] block mb-2">佣金明细</text>
<view class="flex items-center gap-2 mb-3">
<input
v-model="searchKeyword"
class="flex-1 h-40px bg-[var(--bg-container)] rounded-8px px-3 text-14px text-[var(--text-primary)]"
placeholder="搜索 ICCID / 虚拟号 / 订单号"
placeholder-class="text-[var(--text-quaternary)]"
@confirm="handleSearch"
>
<view
class="px-4 h-40px bg-brand text-white text-14px rounded-8px flex items-center justify-center"
@click="handleSearch"
>
搜索
</view>
</view>
<view class="flex items-center gap-2 mb-3">
<view class="flex-1 bg-[var(--bg-container)] rounded-8px px-3 h-40px flex items-center" @click="showSourcePicker = true">
<text class="flex-1 text-14px text-[var(--text-primary)]">{{ selectedSourceName }}</text>
<text class="text-12px text-[var(--text-tertiary)]"></text>
<!-- 佣金统计 -->
<view class="bg-white rounded-16px p-4 mt-3 shadow-[0_2px_8px_rgba(0,0,0,0.04)]">
<text class="text-14px font-600 text-[#212121]">佣金统计</text>
<view class="mt-3 space-y-3">
<view class="flex items-center justify-between">
<view class="flex items-center gap-2">
<view class="w-2 h-2 rounded-full bg-[#2196f3]" />
<text class="text-13px text-[#666]">成本价差</text>
</view>
<view class="flex items-center gap-2">
<span class="text-11px text-[#999]">{{ commissionStats ? (commissionStats.cost_diff_percent / 10).toFixed(1) + '%' : '--' }}</span>
<text class="text-14px font-600 text-[#212121]">{{ commissionStats ? formatAmount(commissionStats.cost_diff_amount) : '--' }}</text>
</view>
</view>
<view class="flex items-center justify-between">
<view class="flex items-center gap-2">
<view class="w-2 h-2 rounded-full bg-[#ff9800]" />
<text class="text-13px text-[#666]">一次性佣金</text>
</view>
<view class="flex items-center gap-2">
<span class="text-11px text-[#999]">{{ commissionStats ? (commissionStats.one_time_percent / 10).toFixed(1) + '%' : '--' }}</span>
<text class="text-14px font-600 text-[#212121]">{{ commissionStats ? formatAmount(commissionStats.one_time_amount) : '--' }}</text>
</view>
</view>
<view class="flex items-center justify-between pt-3 border-t border-[#f0f0f0]">
<text class="text-13px font-600 text-[#212121]">总计</text>
<text class="text-16px font-700 text-brand">{{ commissionStats ? formatAmount(commissionStats.total_amount) : '--' }}</text>
</view>
</view>
</view>
<!-- 佣金明细 -->
<view class="mt-4">
<view class="flex items-center justify-between mb-3">
<text class="text-12px text-[var(--text-secondary)]"> {{ total }} 条记录</text>
<text class="text-14px font-600 text-[#212121]">佣金明细</text>
<text class="text-12px text-[#999]"> {{ total }} </text>
</view>
<view v-if="loadingRecords && page === 1" class="pt-10 center">
<text class="text-14px text-[var(--text-secondary)]">加载中...</text>
<view v-if="loadingRecords && page === 1" class="pt-20 flex justify-center">
<text class="text-14px text-[#64748b]">加载中...</text>
</view>
<view v-else-if="commissionRecords.length > 0" class="space-y-2">
<view
v-for="record in commissionRecords"
:key="record.id"
class="bg-[var(--bg-container)] rounded-12px p-4"
class="bg-white rounded-12px p-4"
>
<view class="flex items-center justify-between">
<view class="flex items-center justify-between mb-2">
<view class="flex items-center gap-2">
<text class="text-14px font-600 text-[var(--text-primary)]">
{{ getSourceText(record.commission_source) }}
</text>
<view
:style="{
backgroundColor: getStatusColor(record.status).bg,
color: getStatusColor(record.status).text,
}"
class="px-2 py-0.5 rounded text-11px font-600"
>
<text class="text-14px font-500 text-[#212121]">{{ getSourceText(record.commission_source) }}</text>
<view :class="record.status === 1 ? 'bg-[#e8f5e9] text-[#2e7d32]' : 'bg-[#f5f5f5] text-[#999]'"
class="px-2 py-0.5 rounded text-11px">
{{ getStatusText(record.status) }}
</view>
</view>
<text class="text-18px font-700 text-brand">
+{{ formatAmount(record.amount) }}
</text>
<text class="text-16px font-600 text-[#52c41a]">+{{ formatAmount(record.amount) }}</text>
</view>
<view class="flex items-center justify-between mt-2">
<text class="text-12px text-[var(--text-tertiary)]">{{ formatTime(record.created_at) }}</text>
<text v-if="record.order_id" class="text-11px text-[var(--text-tertiary)]">订单ID: {{ record.order_id }}</text>
<view class="flex items-center justify-between text-12px text-[#999]">
<text>{{ formatTime(record.created_at) }}</text>
<text v-if="record.order_id">订单 {{ record.order_id }}</text>
</view>
</view>
<view v-if="commissionRecords.length < total" class="py-3 center">
<text v-if="loadingRecords" class="text-12px text-[var(--text-secondary)]">加载中...</text>
<view v-if="commissionRecords.length < total" class="py-4 text-center">
<text v-if="loadingRecords" class="text-12px text-[#64748b]">加载中...</text>
<text v-else class="text-12px text-brand" @click="loadMore">加载更多</text>
</view>
<view v-else class="py-4 text-center">
<text class="text-12px text-[#cbd5e1]">没有更多了</text>
</view>
</view>
<view v-else class="pt-10 center">
<text class="text-14px text-[var(--text-secondary)]">暂无佣金记录</text>
<view v-else class="pt-16 flex flex-col items-center">
<i class="i-mdi-file-document-outline text-5xl text-[#e0e0e0] mb-3" />
<text class="text-14px text-[#999]">{{ searchKeyword ? '未找到相关结果' : '暂无佣金记录' }}</text>
</view>
</view>
<view class="h-16" />
</view>
<SimplePicker
@@ -388,30 +313,21 @@ function refreshList() {
title="选择佣金来源"
@confirm="onSourceConfirm"
/>
<SimplePicker
v-model:show="showSearchTypePicker"
:options="searchTypeOptions"
title="选择搜索类型"
@confirm="onSearchTypeConfirm"
/>
</view>
</template>
<style scoped>
.center {
display: flex;
align-items: center;
justify-content: center;
}
.space-y-2 > view:not(:last-child) {
margin-bottom: 8px;
}
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
.space-y-3 > view:not(:last-child) {
margin-bottom: 12px;
}
.animate-spin {
animation: spin 1s linear infinite;
}
</style>
</style>