54f78e15fe
- 工单模块改为列表形式,支持点击进入详情页回复 - 新增工单列表页面(TicketList.vue)和详情页面(TicketDetail.vue) - 工单详情页支持图片上传、快捷回复、定时刷新 - 消息按ID排序,时间显示优化(今天/昨天/星期/完整日期) - 定时刷新时不显示loading,且只在数据变化时更新UI - 用户列表直接使用API返回的cover字段作为头像,减少HTTP请求 - 修复用户余额页面balance_type参数undefined问题
712 lines
19 KiB
Vue
712 lines
19 KiB
Vue
<template>
|
|
<div class="user-balance-container">
|
|
<!-- 顶部信息栏 -->
|
|
<div class="page-header">
|
|
<div class="header-left">
|
|
<h2 class="page-title">用户余额管理</h2>
|
|
<div class="user-info">
|
|
<span class="user-name">{{ getCurrentUserName() }}</span>
|
|
<span class="user-id">ID: {{ queryParams.user_id }}</span>
|
|
</div>
|
|
</div>
|
|
<el-button type="primary" @click="fetchUserBalance">
|
|
<el-icon><Refresh /></el-icon>刷新数据
|
|
</el-button>
|
|
</div>
|
|
|
|
<!-- 余额卡片 -->
|
|
<div class="balance-cards" v-loading="loading">
|
|
<div
|
|
v-for="balance in balanceList"
|
|
:key="balance.type"
|
|
class="balance-card"
|
|
:class="`balance-card-${balance.type}`"
|
|
>
|
|
<div class="card-header">
|
|
<span class="balance-type">{{ getBalanceTypeName(balance.type) }}</span>
|
|
<span class="balance-id">ID: {{ balance.id }}</span>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="balance-amount">
|
|
<span class="currency">¥</span>
|
|
<span class="amount">{{ (balance.price / 100).toFixed(2) }}</span>
|
|
</div>
|
|
<div class="balance-time">更新于 {{ formatDateTime(balance.UpdatedAt) }}</div>
|
|
</div>
|
|
<div class="card-footer">
|
|
<el-button size="small" @click="handleEditBalance(balance.type)">修改余额</el-button>
|
|
<el-button size="small" @click="handleAddRecord(balance.type)">添加记录</el-button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 余额记录 -->
|
|
<div class="record-section">
|
|
<div class="section-header">
|
|
<h3 class="section-title">余额变动记录</h3>
|
|
</div>
|
|
<div class="table-wrapper">
|
|
<el-table
|
|
:data="recordList"
|
|
v-loading="loading"
|
|
style="width: 100%"
|
|
:header-cell-style="{ background: '#fafafa', color: '#333', fontWeight: 600 }"
|
|
>
|
|
<el-table-column prop="Id" label="记录ID" width="100" />
|
|
<el-table-column label="余额类型" width="120">
|
|
<template #default="{ row }">
|
|
<el-tag size="small">{{ getBalanceTypeByRecordId(row.BalanceId) }}</el-tag>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="变动类型" width="100">
|
|
<template #default="{ row }">
|
|
<el-tag v-if="row.Change" type="success" size="small">增加</el-tag>
|
|
<el-tag v-else type="danger" size="small">减少</el-tag>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="变动金额" width="150">
|
|
<template #default="{ row }">
|
|
<span :class="row.Change ? 'amount-plus' : 'amount-minus'">
|
|
{{ row.Change ? '+' : '-' }}¥{{ (row.Price / 100).toFixed(2) }}
|
|
</span>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="变动后余额" width="150">
|
|
<template #default="{ row }">
|
|
<span class="balance-after">¥{{ ((row.BalanceAfter || 0) / 100).toFixed(2) }}</span>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="Note" label="备注" min-width="200" show-overflow-tooltip />
|
|
<el-table-column label="创建时间" width="180">
|
|
<template #default="{ row }">
|
|
{{ formatDateTime(row.CreatedAt) }}
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
|
|
<div class="pagination-wrapper">
|
|
<el-pagination
|
|
v-model:current-page="recordParams.page"
|
|
v-model:page-size="recordParams.count"
|
|
:page-sizes="[10, 20, 50, 100]"
|
|
layout="total, sizes, prev, pager, next, jumper"
|
|
:total="recordTotal"
|
|
@size-change="handleRecordSizeChange"
|
|
@current-change="handleRecordCurrentChange"
|
|
background
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 修改余额对话框 -->
|
|
<el-dialog v-model="balanceDialogVisible" title="修改用户余额" width="500px">
|
|
<el-form ref="balanceFormRef" :model="balanceForm" :rules="balanceRules" label-width="100px">
|
|
<el-form-item label="余额类型">
|
|
<el-input :value="getBalanceTypeNameByApiType(balanceForm.balance_type)" disabled />
|
|
</el-form-item>
|
|
<el-form-item label="当前余额">
|
|
<el-input :value="currentBalanceDisplay" disabled>
|
|
<template #prepend>¥</template>
|
|
</el-input>
|
|
</el-form-item>
|
|
<el-form-item label="变动类型" prop="state">
|
|
<el-radio-group v-model="balanceForm.state">
|
|
<el-radio label="add">增加</el-radio>
|
|
<el-radio label="sub">减少</el-radio>
|
|
</el-radio-group>
|
|
</el-form-item>
|
|
<el-form-item label="变动金额" prop="price">
|
|
<el-input v-model.number="balanceForm.price" placeholder="请输入变动金额">
|
|
<template #prepend>¥</template>
|
|
</el-input>
|
|
</el-form-item>
|
|
<el-form-item label="备注" prop="note">
|
|
<el-input v-model="balanceForm.note" type="textarea" :rows="3" placeholder="请输入备注" />
|
|
</el-form-item>
|
|
</el-form>
|
|
<template #footer>
|
|
<el-button @click="balanceDialogVisible = false">取消</el-button>
|
|
<el-button type="primary" @click="submitBalanceForm">确定</el-button>
|
|
</template>
|
|
</el-dialog>
|
|
|
|
<!-- 添加记录对话框 -->
|
|
<el-dialog v-model="recordDialogVisible" title="添加消费记录" width="500px">
|
|
<el-form ref="recordFormRef" :model="recordForm" :rules="recordRules" label-width="120px">
|
|
<el-form-item label="余额类型">
|
|
<el-input :value="getBalanceTypeNameByApiType(recordForm.balance_type)" disabled />
|
|
</el-form-item>
|
|
<el-form-item label="是否执行到余额" prop="apply_balance">
|
|
<el-switch v-model="recordForm.apply_balance" active-text="是" inactive-text="否" />
|
|
</el-form-item>
|
|
<el-form-item label="变动类型" prop="state">
|
|
<el-radio-group v-model="recordForm.state">
|
|
<el-radio label="add">增加</el-radio>
|
|
<el-radio label="sub">减少</el-radio>
|
|
</el-radio-group>
|
|
</el-form-item>
|
|
<el-form-item label="变动金额" prop="price">
|
|
<el-input v-model.number="recordForm.price" placeholder="请输入金额">
|
|
<template #prepend>¥</template>
|
|
</el-input>
|
|
</el-form-item>
|
|
<el-form-item label="备注" prop="note">
|
|
<el-input v-model="recordForm.note" type="textarea" :rows="3" placeholder="请输入备注" />
|
|
</el-form-item>
|
|
</el-form>
|
|
<template #footer>
|
|
<el-button @click="recordDialogVisible = false">取消</el-button>
|
|
<el-button type="primary" @click="submitRecordForm">确定</el-button>
|
|
</template>
|
|
</el-dialog>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, reactive, onMounted, computed } from 'vue'
|
|
import { useRoute } from 'vue-router'
|
|
import { ElMessage } from 'element-plus'
|
|
import { Refresh } from '@element-plus/icons-vue'
|
|
import { getUserBalance, getUserBalanceRecord, editUserBalance, addUserConsumption, getUserBalanceCount, getUserList } from '@/api/admin/user'
|
|
|
|
const route = useRoute()
|
|
|
|
// 余额类型映射
|
|
const userBalance = {
|
|
entity: { name: '云钻', type: 'cloud_diamond' },
|
|
general: { name: '云币', type: 'cloud_coin' },
|
|
withdraw: { name: '云点', type: 'cloud_points' }
|
|
}
|
|
|
|
// 查询参数
|
|
const queryParams = reactive({
|
|
user_id: route.query.user_id || ''
|
|
})
|
|
|
|
// 余额记录查询参数
|
|
const recordParams = reactive({
|
|
user_id: '',
|
|
balance_type: '', // 初始化为空字符串,避免 undefined
|
|
page: 1,
|
|
count: 10
|
|
})
|
|
|
|
// 状态数据
|
|
const loading = ref(false)
|
|
const balanceInfo = ref(null)
|
|
const balanceList = ref([])
|
|
const recordList = ref([])
|
|
const recordTotal = ref(0)
|
|
const userList = ref([])
|
|
|
|
// 对话框
|
|
const balanceDialogVisible = ref(false)
|
|
const recordDialogVisible = ref(false)
|
|
const balanceFormRef = ref(null)
|
|
const recordFormRef = ref(null)
|
|
|
|
// 余额表单
|
|
const balanceForm = reactive({
|
|
user_id: '',
|
|
balance_type: '',
|
|
price: 0,
|
|
state: 'add',
|
|
note: ''
|
|
})
|
|
|
|
const balanceRules = {
|
|
price: [
|
|
{ required: true, message: '请输入变动金额', trigger: 'blur' },
|
|
{ type: 'number', message: '请输入数字', trigger: 'blur' }
|
|
],
|
|
state: [{ required: true, message: '请选择变动类型', trigger: 'change' }],
|
|
note: [{ required: true, message: '请输入备注', trigger: 'blur' }]
|
|
}
|
|
|
|
// 记录表单
|
|
const recordForm = reactive({
|
|
user_id: '',
|
|
balance_type: '',
|
|
apply_balance: false,
|
|
price: 0,
|
|
state: 'add',
|
|
note: ''
|
|
})
|
|
|
|
const recordRules = {
|
|
price: [
|
|
{ required: true, message: '请输入金额', trigger: 'blur' },
|
|
{ type: 'number', message: '请输入数字', trigger: 'blur' }
|
|
],
|
|
state: [{ required: true, message: '请选择状态类型', trigger: 'change' }],
|
|
note: [{ required: true, message: '请输入备注', trigger: 'blur' }]
|
|
}
|
|
|
|
// 当前余额显示
|
|
const currentBalanceDisplay = computed(() => {
|
|
const balance = balanceList.value.find(b => userBalance[b.type]?.type === balanceForm.balance_type)
|
|
return balance ? (balance.price / 100).toFixed(2) : '0.00'
|
|
})
|
|
|
|
// 获取用户列表(用于显示用户名)
|
|
const getUserListData = async () => {
|
|
try {
|
|
const res = await getUserList({ page: 1, count: 1000, key: '' })
|
|
if (res.data.code === 200) {
|
|
userList.value = res.data.data.data || []
|
|
}
|
|
} catch (error) {
|
|
console.error('获取用户列表错误:', error)
|
|
}
|
|
}
|
|
|
|
// 获取当前用户名
|
|
const getCurrentUserName = () => {
|
|
if (!queryParams.user_id) return '未知用户'
|
|
const user = userList.value.find(u => u.UserId === queryParams.user_id)
|
|
return user ? user.UserName : `用户${queryParams.user_id}`
|
|
}
|
|
|
|
// 获取余额类型名称
|
|
const getBalanceTypeName = (type) => {
|
|
return userBalance[type]?.name || type
|
|
}
|
|
|
|
// 通过API类型获取余额类型名称
|
|
const getBalanceTypeNameByApiType = (apiType) => {
|
|
const entry = Object.values(userBalance).find(b => b.type === apiType)
|
|
return entry ? entry.name : apiType
|
|
}
|
|
|
|
// 通过记录的BalanceId获取余额类型名称
|
|
const getBalanceTypeByRecordId = (balanceId) => {
|
|
if (!balanceId) return '未知类型'
|
|
const balance = balanceList.value.find(b => b.id === balanceId)
|
|
return balance ? getBalanceTypeName(balance.type) : '未知类型'
|
|
}
|
|
|
|
// 格式化时间
|
|
const formatDateTime = (dateStr) => {
|
|
if (!dateStr) return '-'
|
|
const date = new Date(dateStr)
|
|
return date.toLocaleString('zh-CN', { hour12: false })
|
|
}
|
|
|
|
// 获取用户余额
|
|
const fetchUserBalance = async () => {
|
|
if (!queryParams.user_id) {
|
|
ElMessage.warning('缺少用户ID参数')
|
|
return
|
|
}
|
|
|
|
loading.value = true
|
|
try {
|
|
const res = await getUserBalanceCount({ user_id: queryParams.user_id })
|
|
if (res.data.code === 200) {
|
|
balanceInfo.value = res.data.data
|
|
const returnedData = Array.isArray(res.data.data) ? res.data.data : []
|
|
|
|
// 定义默认余额
|
|
const defaultBalances = [
|
|
{ id: '-', userId: queryParams.user_id, price: 0, type: 'entity', CreatedAt: new Date().toISOString(), UpdatedAt: new Date().toISOString() },
|
|
{ id: '-', userId: queryParams.user_id, price: 0, type: 'general', CreatedAt: new Date().toISOString(), UpdatedAt: new Date().toISOString() },
|
|
{ id: '-', userId: queryParams.user_id, price: 0, type: 'withdraw', CreatedAt: new Date().toISOString(), UpdatedAt: new Date().toISOString() }
|
|
]
|
|
|
|
// 检查缺失的余额类型
|
|
const requiredTypes = ['entity', 'general', 'withdraw']
|
|
const missingTypes = requiredTypes.filter(type => !returnedData.some(item => item.type === type))
|
|
|
|
// 初始化缺失的余额类型
|
|
if (missingTypes.length > 0) {
|
|
const initPromises = missingTypes.map(type =>
|
|
editUserBalance({
|
|
user_id: queryParams.user_id,
|
|
balance_type: userBalance[type].type,
|
|
price: 0,
|
|
state: 'add',
|
|
note: `系统自动初始化${getBalanceTypeName(type)}余额`
|
|
})
|
|
)
|
|
await Promise.all(initPromises)
|
|
|
|
// 重新获取余额
|
|
const newRes = await getUserBalanceCount({ user_id: queryParams.user_id })
|
|
if (newRes.data.code === 200) {
|
|
const newReturnedData = Array.isArray(newRes.data.data) ? newRes.data.data : []
|
|
balanceList.value = defaultBalances.map(defaultItem =>
|
|
newReturnedData.find(item => item.type === defaultItem.type) || defaultItem
|
|
)
|
|
}
|
|
} else {
|
|
balanceList.value = defaultBalances.map(defaultItem =>
|
|
returnedData.find(item => item.type === defaultItem.type) || defaultItem
|
|
)
|
|
}
|
|
|
|
recordParams.user_id = queryParams.user_id
|
|
fetchBalanceRecord()
|
|
} else {
|
|
ElMessage.error(res.data.message || '获取用户余额失败')
|
|
}
|
|
} catch (error) {
|
|
console.error('获取用户余额错误:', error)
|
|
ElMessage.error('获取用户余额失败')
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
// 获取余额记录
|
|
const fetchBalanceRecord = async () => {
|
|
loading.value = true
|
|
try {
|
|
const res = await getUserBalanceRecord(recordParams)
|
|
if (res.data.code === 200) {
|
|
recordList.value = res.data.data.data || []
|
|
recordTotal.value = res.data.data.all_count || 0
|
|
} else {
|
|
ElMessage.error(res.data.message || '获取余额记录失败')
|
|
}
|
|
} catch (error) {
|
|
console.error('获取余额记录错误:', error)
|
|
ElMessage.error('获取余额记录失败')
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
// 修改余额
|
|
const handleEditBalance = (balanceType) => {
|
|
const apiType = userBalance[balanceType]?.type
|
|
balanceFormRef.value?.resetFields()
|
|
Object.assign(balanceForm, {
|
|
user_id: queryParams.user_id,
|
|
balance_type: apiType,
|
|
price: 0,
|
|
state: 'add',
|
|
note: ''
|
|
})
|
|
balanceDialogVisible.value = true
|
|
}
|
|
|
|
// 添加记录
|
|
const handleAddRecord = (balanceType) => {
|
|
const apiType = userBalance[balanceType]?.type
|
|
recordFormRef.value?.resetFields()
|
|
Object.assign(recordForm, {
|
|
user_id: queryParams.user_id,
|
|
balance_type: apiType,
|
|
apply_balance: false,
|
|
price: 0,
|
|
state: 'add',
|
|
note: ''
|
|
})
|
|
recordDialogVisible.value = true
|
|
}
|
|
|
|
// 提交余额修改
|
|
const submitBalanceForm = () => {
|
|
balanceFormRef.value?.validate(async (valid) => {
|
|
if (valid) {
|
|
try {
|
|
const res = await editUserBalance(balanceForm)
|
|
if (res.data.code === 200) {
|
|
ElMessage.success('修改成功')
|
|
balanceDialogVisible.value = false
|
|
fetchUserBalance()
|
|
} else {
|
|
ElMessage.error(res.data.message || '修改失败')
|
|
}
|
|
} catch (error) {
|
|
console.error('修改余额错误:', error)
|
|
ElMessage.error('修改失败')
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
// 提交记录添加
|
|
const submitRecordForm = () => {
|
|
recordFormRef.value?.validate(async (valid) => {
|
|
if (valid) {
|
|
try {
|
|
const res = await addUserConsumption(recordForm)
|
|
if (res.data.code === 200) {
|
|
ElMessage.success('添加成功')
|
|
recordDialogVisible.value = false
|
|
fetchUserBalance()
|
|
} else {
|
|
ElMessage.error(res.data.message || '添加失败')
|
|
}
|
|
} catch (error) {
|
|
console.error('添加记录错误:', error)
|
|
ElMessage.error('添加失败')
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
// 分页
|
|
const handleRecordSizeChange = () => {
|
|
fetchBalanceRecord()
|
|
}
|
|
|
|
const handleRecordCurrentChange = () => {
|
|
fetchBalanceRecord()
|
|
}
|
|
|
|
// 初始化
|
|
onMounted(() => {
|
|
getUserListData()
|
|
if (queryParams.user_id) {
|
|
fetchUserBalance()
|
|
} else {
|
|
ElMessage.warning('缺少用户ID参数')
|
|
}
|
|
})
|
|
</script>
|
|
|
|
<style scoped>
|
|
.user-balance-container {
|
|
padding: 0;
|
|
background: #f5f7fa;
|
|
min-height: 100vh;
|
|
}
|
|
|
|
/* 页面头部 */
|
|
.page-header {
|
|
background: #fff;
|
|
padding: 24px 32px;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
border-bottom: 1px solid #e4e7ed;
|
|
margin-bottom: 24px;
|
|
}
|
|
|
|
.header-left {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 24px;
|
|
}
|
|
|
|
.page-title {
|
|
margin: 0;
|
|
font-size: 20px;
|
|
font-weight: 600;
|
|
color: #303133;
|
|
}
|
|
|
|
.user-info {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
padding: 8px 16px;
|
|
background: #f5f7fa;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.user-name {
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
color: #303133;
|
|
}
|
|
|
|
.user-id {
|
|
font-size: 13px;
|
|
color: #909399;
|
|
}
|
|
|
|
/* 余额卡片 */
|
|
.balance-cards {
|
|
display: grid;
|
|
grid-template-columns: repeat(3, 1fr);
|
|
gap: 20px;
|
|
padding: 0 32px 24px;
|
|
}
|
|
|
|
.balance-card {
|
|
background: #fff;
|
|
border-radius: 8px;
|
|
padding: 24px;
|
|
border: 1px solid #e4e7ed;
|
|
transition: all 0.3s ease;
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.balance-card::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
height: 4px;
|
|
background: linear-gradient(90deg, #409eff, #66b1ff);
|
|
}
|
|
|
|
.balance-card-entity::before {
|
|
background: linear-gradient(90deg, #67c23a, #85ce61);
|
|
}
|
|
|
|
.balance-card-general::before {
|
|
background: linear-gradient(90deg, #409eff, #66b1ff);
|
|
}
|
|
|
|
.balance-card-withdraw::before {
|
|
background: linear-gradient(90deg, #e6a23c, #f0c78a);
|
|
}
|
|
|
|
.balance-card:hover {
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
.balance-card .card-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.balance-type {
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
color: #303133;
|
|
}
|
|
|
|
.balance-id {
|
|
font-size: 12px;
|
|
color: #909399;
|
|
background: #f5f7fa;
|
|
padding: 4px 8px;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.balance-card .card-body {
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.balance-amount {
|
|
display: flex;
|
|
align-items: baseline;
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.balance-amount .currency {
|
|
font-size: 20px;
|
|
color: #606266;
|
|
margin-right: 4px;
|
|
}
|
|
|
|
.balance-amount .amount {
|
|
font-size: 32px;
|
|
font-weight: 600;
|
|
color: #303133;
|
|
}
|
|
|
|
.balance-time {
|
|
font-size: 13px;
|
|
color: #909399;
|
|
}
|
|
|
|
.balance-card .card-footer {
|
|
display: flex;
|
|
gap: 8px;
|
|
}
|
|
|
|
.balance-card .card-footer .el-button {
|
|
flex: 1;
|
|
}
|
|
|
|
/* 记录区域 */
|
|
.record-section {
|
|
background: #fff;
|
|
margin: 0 32px 32px;
|
|
border-radius: 8px;
|
|
border: 1px solid #e4e7ed;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.section-header {
|
|
padding: 20px 24px;
|
|
border-bottom: 1px solid #e4e7ed;
|
|
}
|
|
|
|
.section-title {
|
|
margin: 0;
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
color: #303133;
|
|
}
|
|
|
|
.table-wrapper {
|
|
padding: 0;
|
|
}
|
|
|
|
.table-wrapper :deep(.el-table) {
|
|
border: none;
|
|
}
|
|
|
|
.table-wrapper :deep(.el-table td),
|
|
.table-wrapper :deep(.el-table th) {
|
|
border-bottom: 1px solid #f0f2f5;
|
|
}
|
|
|
|
.table-wrapper :deep(.el-table tr:hover > td) {
|
|
background-color: #fafafa;
|
|
}
|
|
|
|
.amount-plus {
|
|
color: #67c23a;
|
|
font-weight: 600;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.amount-minus {
|
|
color: #f56c6c;
|
|
font-weight: 600;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.balance-after {
|
|
color: #303133;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.pagination-wrapper {
|
|
padding: 16px 24px;
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
border-top: 1px solid #e4e7ed;
|
|
background: #fafafa;
|
|
}
|
|
|
|
/* 响应式 */
|
|
@media (max-width: 1200px) {
|
|
.balance-cards {
|
|
grid-template-columns: repeat(2, 1fr);
|
|
}
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.balance-cards {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.page-header {
|
|
flex-direction: column;
|
|
align-items: flex-start;
|
|
gap: 16px;
|
|
}
|
|
|
|
.header-left {
|
|
flex-direction: column;
|
|
align-items: flex-start;
|
|
gap: 12px;
|
|
}
|
|
}
|
|
</style>
|