优化路由逻辑,根据用户类型动态重定向到不同的默认页面,增强登录后的用户体验;更新退单管理界面,改进搜索功能和响应式设计,提升移动端用户体验。

This commit is contained in:
zyh
2025-08-28 12:23:05 +08:00
parent 602d88a5a2
commit 2065b062e3
4 changed files with 307 additions and 180 deletions

View File

@@ -177,3 +177,4 @@ src/
--- ---
*最后更新时间: 2024年1月* *最后更新时间: 2024年1月*

View File

@@ -20,7 +20,20 @@ export const routes = [
path: '/', path: '/',
component: AdminLayout, component: AdminLayout,
children: [ children: [
{ path: '', redirect: '/users' }, {
path: '',
redirect: (to) => {
// 根据用户类型重定向到不同的默认页面
const { getCurrentUserType } = require('@/utils/permission')
const userType = getCurrentUserType()
if (userType?.toLowerCase() === 'agent') {
return '/links' // 代理商跳转到链接管理
} else {
return '/users' // 管理员跳转到用户管理
}
}
},
{ path: 'users', name: 'Users', component: UserList, meta: { title: '用户管理' } }, { path: 'users', name: 'Users', component: UserList, meta: { title: '用户管理' } },
{ path: 'settings', name: 'Settings', component: Settings, meta: { title: '系统设置' } }, { path: 'settings', name: 'Settings', component: Settings, meta: { title: '系统设置' } },
{ path: 'links', name: 'Links', component: LinkGenerate, meta: { title: '链接管理' } }, { path: 'links', name: 'Links', component: LinkGenerate, meta: { title: '链接管理' } },
@@ -44,7 +57,17 @@ router.beforeEach((to, from, next) => {
// 检查路由权限 // 检查路由权限
if (to.name && !canAccessRoute(to.name)) { if (to.name && !canAccessRoute(to.name)) {
return next({ name: 'Users' }) // 无权限时跳转到用户管理 // 根据用户类型跳转到有权限的默认页面
const { getCurrentUserType } = require('@/utils/permission')
const userType = getCurrentUserType()
if (userType?.toLowerCase() === 'agent') {
// 代理商跳转到链接管理页面
return next({ name: 'Links' })
} else {
// 管理员或其他用户跳转到用户管理
return next({ name: 'Users' })
}
} }
next() next()

View File

@@ -127,8 +127,19 @@ async function onSubmit() {
showSuccessMessage('登录成功') showSuccessMessage('登录成功')
persistRemember() persistRemember()
console.debug('login response:', res.data) console.debug('login response:', res.data)
const redirect = route.query.redirect || '/'
router.replace(String(redirect)) // 如果有 redirect 参数,直接跳转
if (route.query.redirect) {
router.replace(String(route.query.redirect))
} else {
// 根据用户类型跳转到合适的默认页面
const userType = data?.userType?.toLowerCase()
if (userType === 'agent') {
router.replace('/links') // 代理商跳转到链接管理
} else {
router.replace('/users') // 管理员跳转到用户管理
}
}
} catch (e) { } catch (e) {
showErrorMessage(e, '登录失败') showErrorMessage(e, '登录失败')
} finally { } finally {

View File

@@ -5,47 +5,74 @@
<p class="page-description">管理用户的退单申请支持按链接编号查询和执行退单操作</p> <p class="page-description">管理用户的退单申请支持按链接编号查询和执行退单操作</p>
</div> </div>
<!-- 搜索区域 --> <!-- 搜索区域响应式 -->
<div class="search-section"> <div class="search-section">
<el-card> <el-card>
<el-row :gutter="20"> <el-form :model="searchForm" @submit.prevent>
<el-col :span="8"> <el-row :gutter="12">
<el-input <!-- 链接编号手机端独占一行桌面端占较宽比例 -->
v-model="searchForm.codeNo" <el-col :xs="24" :sm="12" :md="10" :lg="10">
placeholder="请输入链接编号" <div class="code-input-wrap">
clearable <el-input
@keyup.enter="handleSearch" v-model="searchForm.codeNo"
> size="large"
<template #prepend>链接编号</template> clearable
</el-input> placeholder="请输入链接编号(支持粘贴后回车)"
</el-col> @keyup.enter="handleSearch"
<el-col :span="8"> >
<el-select <template #prefix>
v-model="searchForm.status" <el-icon><Link /></el-icon>
placeholder="选择状态" </template>
clearable </el-input>
style="width: 100%" <el-tooltip content="从剪贴板粘贴" placement="top">
> <el-button
<el-option label="全部状态" value="" /> class="paste-btn"
<el-option label="新建" value="NEW" /> :icon="DocumentCopy"
<el-option label="使用中" value="USING" /> size="large"
<el-option label="已登录" value="LOGGED_IN" /> @click="pasteFromClipboard"
<el-option label="已完成" value="COMPLETED" /> />
<el-option label="已退单" value="REFUNDED" /> </el-tooltip>
<el-option label="已过期" value="EXPIRED" /> </div>
</el-select> </el-col>
</el-col>
<el-col :span="8"> <!-- 状态筛选可选主要用于查看信息时做个提示筛选不影响后端查询 -->
<el-button type="primary" @click="handleSearch" :loading="loading"> <el-col :xs="24" :sm="8" :md="8" :lg="6">
<el-icon><Search /></el-icon> <el-select
查询 v-model="searchForm.status"
</el-button> size="large"
<el-button @click="handleReset"> placeholder="选择状态"
<el-icon><Refresh /></el-icon> clearable
重置 style="width: 100%"
</el-button> >
</el-col> <el-option label="全部状态" value="" />
</el-row> <el-option label="新建" value="NEW" />
<el-option label="使用中" value="USING" />
<el-option label="已登录" value="LOGGED_IN" />
<el-option label="已完成" value="COMPLETED" />
<el-option label="已退单" value="REFUNDED" />
<el-option label="已过期" value="EXPIRED" />
</el-select>
</el-col>
<!-- 按钮区手机端纵向铺满 -->
<el-col :xs="24" :sm="4" :md="6" :lg="8">
<div class="btn-group">
<el-button
type="primary"
size="large"
@click="handleSearch"
:loading="loading"
:disabled="!searchForm.codeNo.trim()"
>
<el-icon class="mr-6"><Search /></el-icon> 查询
</el-button>
<el-button size="large" @click="handleReset">
<el-icon class="mr-6"><Refresh /></el-icon> 重置
</el-button>
</div>
</el-col>
</el-row>
</el-form>
</el-card> </el-card>
</div> </div>
@@ -54,7 +81,20 @@
<el-card> <el-card>
<template #header> <template #header>
<div class="card-header"> <div class="card-header">
<span>链接信息</span> <div class="code-and-status">
<span>链接信息</span>
<span class="code-chip">
<span class="mono">{{ linkInfo.codeNo }}</span>
<el-tooltip content="复制链接编号" placement="top">
<el-button
:icon="DocumentCopy"
circle
size="small"
@click="copyCodeNo(linkInfo.codeNo)"
/>
</el-tooltip>
</span>
</div>
<el-tag :type="getStatusTagType(linkInfo.status)"> <el-tag :type="getStatusTagType(linkInfo.status)">
{{ getStatusText(linkInfo.status) }} {{ getStatusText(linkInfo.status) }}
</el-tag> </el-tag>
@@ -62,7 +102,9 @@
</template> </template>
<el-descriptions :column="2" border> <el-descriptions :column="2" border>
<el-descriptions-item label="链接编号">{{ linkInfo.codeNo }}</el-descriptions-item> <el-descriptions-item label="链接编号">
<span class="mono">{{ linkInfo.codeNo }}</span>
</el-descriptions-item>
<el-descriptions-item label="当前状态"> <el-descriptions-item label="当前状态">
<el-tag :type="getStatusTagType(linkInfo.status)"> <el-tag :type="getStatusTagType(linkInfo.status)">
{{ getStatusText(linkInfo.status) }} {{ getStatusText(linkInfo.status) }}
@@ -70,10 +112,14 @@
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="创建时间">{{ formatDateTime(linkInfo.createdAt) }}</el-descriptions-item> <el-descriptions-item label="创建时间">{{ formatDateTime(linkInfo.createdAt) }}</el-descriptions-item>
<el-descriptions-item label="更新时间">{{ formatDateTime(linkInfo.updatedAt) }}</el-descriptions-item> <el-descriptions-item label="更新时间">{{ formatDateTime(linkInfo.updatedAt) }}</el-descriptions-item>
<el-descriptions-item label="代理ID">{{ linkInfo.agentId || '-' }}</el-descriptions-item> <el-descriptions-item label="代理ID">
<el-descriptions-item label="关联设备">{{ linkInfo.machineId || '-' }}</el-descriptions-item> <span class="mono">{{ linkInfo.agentId || '-' }}</span>
<el-descriptions-item label="总点数">{{ linkInfo.totalPoints || '-' }}</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="当前点数">{{ linkInfo.currentPoints || '-' }}</el-descriptions-item> <el-descriptions-item label="关联设备">
<span class="mono">{{ linkInfo.machineId || '-' }}</span>
</el-descriptions-item>
<el-descriptions-item label="总点数">{{ linkInfo.totalPoints ?? '-' }}</el-descriptions-item>
<el-descriptions-item label="当前点数">{{ linkInfo.currentPoints ?? '-' }}</el-descriptions-item>
<el-descriptions-item v-if="linkInfo.refundAt" label="退单时间"> <el-descriptions-item v-if="linkInfo.refundAt" label="退单时间">
{{ formatDateTime(linkInfo.refundAt) }} {{ formatDateTime(linkInfo.refundAt) }}
</el-descriptions-item> </el-descriptions-item>
@@ -87,24 +133,13 @@
@click="handleRefund" @click="handleRefund"
:loading="refunding" :loading="refunding"
> >
<el-icon><RefreshLeft /></el-icon> <el-icon class="mr-6"><RefreshLeft /></el-icon> 执行退单
执行退单
</el-button> </el-button>
<el-button <el-button v-else-if="linkInfo.status === 'REFUNDED'" disabled type="info">
v-else-if="linkInfo.status === 'REFUNDED'" <el-icon class="mr-6"><Check /></el-icon> 已退单
disabled
type="info"
>
<el-icon><Check /></el-icon>
已退单
</el-button> </el-button>
<el-button <el-button v-else disabled type="info">
v-else <el-icon class="mr-6"><Warning /></el-icon> 当前状态不允许退单
disabled
type="info"
>
<el-icon><Warning /></el-icon>
当前状态不允许退单
</el-button> </el-button>
</div> </div>
</el-card> </el-card>
@@ -162,18 +197,22 @@
</el-card> </el-card>
</div> </div>
<!-- 退单确认对话框 --> <!-- 退单确认对话框移动端全屏 -->
<el-dialog <el-dialog
v-model="refundDialogVisible" v-model="refundDialogVisible"
title="确认退单" title="确认退单"
width="500px" :width="isMobile ? '96vw' : '500px'"
:fullscreen="isMobile"
:append-to-body="true"
:close-on-click-modal="false"
:before-close="handleRefundDialogClose" :before-close="handleRefundDialogClose"
top="8vh"
> >
<div class="refund-confirm"> <div class="refund-confirm">
<el-icon class="warning-icon"><WarningFilled /></el-icon> <el-icon class="warning-icon"><WarningFilled /></el-icon>
<div class="confirm-content"> <div class="confirm-content">
<h3>确认要对以下链接执行退单操作吗</h3> <h3>确认要对以下链接执行退单操作吗</h3>
<p class="link-code">链接编号<strong>{{ linkInfo?.codeNo }}</strong></p> <p class="link-code">链接编号<strong class="mono">{{ linkInfo?.codeNo }}</strong></p>
<p class="warning-text"> <p class="warning-text">
<el-icon><Warning /></el-icon> <el-icon><Warning /></el-icon>
退单操作不可逆执行后链接将无法继续使用 退单操作不可逆执行后链接将无法继续使用
@@ -183,22 +222,25 @@
<template #footer> <template #footer>
<el-button @click="refundDialogVisible = false">取消</el-button> <el-button @click="refundDialogVisible = false">取消</el-button>
<el-button <el-button type="danger" @click="confirmRefund" :loading="refunding">确认退单</el-button>
type="danger"
@click="confirmRefund"
:loading="refunding"
>
确认退单
</el-button>
</template> </template>
</el-dialog> </el-dialog>
</div> </div>
</template> </template>
<script> <script>
import { ref, reactive } from 'vue' import { ref, reactive, onMounted, onUnmounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage } from 'element-plus'
import { Search, Refresh, RefreshLeft, Check, Warning, WarningFilled } from '@element-plus/icons-vue' import {
Search,
Refresh,
RefreshLeft,
Check,
Warning,
WarningFilled,
Link,
DocumentCopy
} from '@element-plus/icons-vue'
import { getLinkStatus, refundLink } from '@/api/links' import { getLinkStatus, refundLink } from '@/api/links'
export default { export default {
@@ -209,92 +251,135 @@ export default {
RefreshLeft, RefreshLeft,
Check, Check,
Warning, Warning,
WarningFilled WarningFilled,
Link,
DocumentCopy
}, },
setup() { setup() {
// 响应式数据 // 视口与状态
const isMobile = ref(false)
const loading = ref(false) const loading = ref(false)
const refunding = ref(false) const refunding = ref(false)
const refundDialogVisible = ref(false) const refundDialogVisible = ref(false)
const linkInfo = ref(null) const linkInfo = ref(null)
// 表单
const searchForm = reactive({ const searchForm = reactive({
codeNo: '', codeNo: '',
status: '' status: ''
}) })
// 查询链接信息 // 自适应
const updateIsMobile = () => {
isMobile.value = window.innerWidth <= 768
}
onMounted(() => {
updateIsMobile()
window.addEventListener('resize', updateIsMobile)
})
onUnmounted(() => {
window.removeEventListener('resize', updateIsMobile)
})
// 粘贴剪贴板
const pasteFromClipboard = async () => {
try {
if (!navigator.clipboard) {
ElMessage.warning('当前环境不支持剪贴板读取')
return
}
const text = await navigator.clipboard.readText()
if (!text) {
ElMessage.info('剪贴板为空')
return
}
searchForm.codeNo = text.trim()
// 自动触发查询(可按需注释)
if (searchForm.codeNo) handleSearch()
} catch (err) {
ElMessage.error('读取剪贴板失败,请手动粘贴')
}
}
// 复制编号
const copyCodeNo = async (code) => {
try {
await navigator.clipboard.writeText(code || '')
ElMessage.success('已复制链接编号')
} catch {
ElMessage.error('复制失败,请手动选择复制')
}
}
// 查询
const handleSearch = async () => { const handleSearch = async () => {
if (!searchForm.codeNo.trim()) { if (!searchForm.codeNo.trim()) {
ElMessage.warning('请输入链接编号') ElMessage.warning('请输入链接编号')
return return
} }
loading.value = true loading.value = true
try { try {
const response = await getLinkStatus(searchForm.codeNo) const response = await getLinkStatus(searchForm.codeNo.trim())
linkInfo.value = response.data linkInfo.value = response.data
// 如果选择了状态筛选,仅做前端提示
if (linkInfo.value && searchForm.status && linkInfo.value.status !== searchForm.status) {
ElMessage.info(`已找到链接,但状态为「${getStatusText(linkInfo.value.status)}`)
}
if (!linkInfo.value) { if (!linkInfo.value) {
ElMessage.warning('未找到相关链接信息') ElMessage.warning('未找到相关链接信息')
} }
} catch (error) { } catch (error) {
console.error('查询链接失败:', error) console.error('查询链接失败:', error)
linkInfo.value = null linkInfo.value = null
if (error?.response?.status === 404) {
// 根据错误状态显示不同消息
if (error.response?.status === 404) {
ElMessage.error('链接不存在') ElMessage.error('链接不存在')
} else if (error.response?.status === 403) { } else if (error?.response?.status === 403) {
ElMessage.error('无权限查看此链接') ElMessage.error('无权限查看此链接')
} else { } else {
ElMessage.error('查询失败,请稍后重试') ElMessage.error(error?.response?.data?.message || '查询失败,请稍后重试')
} }
} finally { } finally {
loading.value = false loading.value = false
} }
} }
// 重置搜索 // 重置
const handleReset = () => { const handleReset = () => {
searchForm.codeNo = '' searchForm.codeNo = ''
searchForm.status = '' searchForm.status = ''
linkInfo.value = null linkInfo.value = null
} }
// 判断是否可以退单 // 资格判断
const canRefund = (status) => { const canRefund = (status) => ['NEW', 'USING', 'LOGGED_IN'].includes(status)
return ['NEW', 'USING', 'LOGGED_IN'].includes(status)
}
// 处理退单按钮点击 // 打开确认弹窗
const handleRefund = () => { const handleRefund = () => {
if (!linkInfo.value) return if (!linkInfo.value) return
refundDialogVisible.value = true refundDialogVisible.value = true
} }
// 确认退单 // 确认退单
const confirmRefund = async () => { const confirmRefund = async () => {
if (!linkInfo.value) return if (!linkInfo.value) return
refunding.value = true refunding.value = true
try { try {
await refundLink(linkInfo.value.codeNo) await refundLink(linkInfo.value.codeNo)
// 更新本地状态 // 更新本地状态
linkInfo.value.status = 'REFUNDED' const nowISO = new Date().toISOString()
linkInfo.value.refundAt = new Date().toISOString() linkInfo.value = {
linkInfo.value.updatedAt = new Date().toISOString() ...linkInfo.value,
status: 'REFUNDED',
refundAt: nowISO,
updatedAt: nowISO
}
refundDialogVisible.value = false refundDialogVisible.value = false
ElMessage.success('退单操作成功') ElMessage.success('退单操作成功')
} catch (error) { } catch (error) {
console.error('退单失败:', error) console.error('退单失败:', error)
if (error?.response?.status === 400) {
// 根据错误状态显示不同消息
if (error.response?.status === 400) {
const errorCode = error.response.data?.code const errorCode = error.response.data?.code
switch (errorCode) { switch (errorCode) {
case 'LINK_003': case 'LINK_003':
@@ -309,53 +394,50 @@ export default {
default: default:
ElMessage.error(error.response.data?.message || '退单失败') ElMessage.error(error.response.data?.message || '退单失败')
} }
} else if (error.response?.status === 403) { } else if (error?.response?.status === 403) {
ElMessage.error('无权限操作此链接') ElMessage.error('无权限操作此链接')
} else { } else {
ElMessage.error('退单失败,请稍后重试') ElMessage.error(error?.response?.data?.message || '退单失败,请稍后重试')
} }
} finally { } finally {
refunding.value = false refunding.value = false
} }
} }
// 关闭退单对话框 // 关闭弹窗
const handleRefundDialogClose = () => { const handleRefundDialogClose = () => {
if (!refunding.value) { if (!refunding.value) {
refundDialogVisible.value = false refundDialogVisible.value = false
} }
} }
// 获取状态标签类型 // 状态展示
const getStatusTagType = (status) => { const getStatusTagType = (status) => {
const statusTypes = { const map = {
'NEW': 'info', NEW: 'info',
'USING': 'warning', USING: 'warning',
'LOGGED_IN': 'primary', LOGGED_IN: 'primary',
'COMPLETED': 'success', COMPLETED: 'success',
'REFUNDED': 'info', REFUNDED: 'info',
'EXPIRED': 'danger' EXPIRED: 'danger'
} }
return statusTypes[status] || 'info' return map[status] || 'info'
} }
// 获取状态文本
const getStatusText = (status) => { const getStatusText = (status) => {
const statusTexts = { const map = {
'NEW': '新建', NEW: '新建',
'USING': '使用中', USING: '使用中',
'LOGGED_IN': '已登录', LOGGED_IN: '已登录',
'COMPLETED': '已完成', COMPLETED: '已完成',
'REFUNDED': '已退单', REFUNDED: '已退单',
'EXPIRED': '已过期' EXPIRED: '已过期'
} }
return statusTexts[status] || status return map[status] || status
} }
// 格式化时间 // 时间格式化
const formatDateTime = (dateTime) => { const formatDateTime = (dateTime) => {
if (!dateTime) return '-' if (!dateTime) return '-'
try { try {
const date = new Date(dateTime) const date = new Date(dateTime)
return date.toLocaleString('zh-CN', { return date.toLocaleString('zh-CN', {
@@ -366,23 +448,29 @@ export default {
minute: '2-digit', minute: '2-digit',
second: '2-digit' second: '2-digit'
}) })
} catch (error) { } catch {
return dateTime return dateTime
} }
} }
return { return {
// state
isMobile,
loading, loading,
refunding, refunding,
refundDialogVisible, refundDialogVisible,
linkInfo, linkInfo,
searchForm, searchForm,
// actions
pasteFromClipboard,
copyCodeNo,
handleSearch, handleSearch,
handleReset, handleReset,
canRefund, canRefund,
handleRefund, handleRefund,
confirmRefund, confirmRefund,
handleRefundDialogClose, handleRefundDialogClose,
// helpers
getStatusTagType, getStatusTagType,
getStatusText, getStatusText,
formatDateTime formatDateTime
@@ -399,35 +487,60 @@ export default {
.page-header { .page-header {
margin-bottom: 20px; margin-bottom: 20px;
} }
.page-header h2 { .page-header h2 {
margin: 0 0 8px 0; margin: 0 0 8px 0;
color: #303133; color: #303133;
font-size: 24px; font-size: 24px;
font-weight: 600; font-weight: 600;
} }
.page-description { .page-description {
margin: 0; margin: 0;
color: #909399; color: #909399;
font-size: 14px; font-size: 14px;
} }
/* 搜索区样式 */
.search-section { .search-section {
margin-bottom: 20px; margin-bottom: 20px;
} }
.code-input-wrap {
display: flex;
gap: 8px;
align-items: center;
}
.code-input-wrap .paste-btn {
padding: 0 12px;
}
/* 结果卡片 */
.link-info-section { .link-info-section {
margin-bottom: 20px; margin-bottom: 20px;
} }
.card-header { .card-header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
font-weight: 600; font-weight: 600;
gap: 8px;
}
.code-and-status {
display: flex;
align-items: center;
gap: 12px;
}
.code-chip {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 2px 8px;
border-radius: 6px;
background: #f5f7fa;
}
.mono {
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Courier New", monospace;
} }
/* 操作按钮 */
.action-buttons { .action-buttons {
margin-top: 20px; margin-top: 20px;
padding-top: 20px; padding-top: 20px;
@@ -435,24 +548,23 @@ export default {
text-align: right; text-align: right;
} }
/* 空状态 */
.empty-state { .empty-state {
margin-bottom: 20px; margin-bottom: 20px;
} }
/* 帮助卡片 */
.help-section { .help-section {
margin-bottom: 20px; margin-bottom: 20px;
} }
.help-list { .help-list {
margin: 0; margin: 0;
padding-left: 20px; padding-left: 20px;
} }
.help-list li { .help-list li {
margin-bottom: 8px; margin-bottom: 8px;
line-height: 1.6; line-height: 1.6;
} }
.help-list .el-tag { .help-list .el-tag {
margin-right: 8px; margin-right: 8px;
} }
@@ -463,87 +575,67 @@ export default {
align-items: flex-start; align-items: flex-start;
gap: 16px; gap: 16px;
} }
.warning-icon { .warning-icon {
font-size: 24px; font-size: 24px;
color: #e6a23c; color: #e6a23c;
flex-shrink: 0; flex-shrink: 0;
margin-top: 4px; margin-top: 4px;
} }
.confirm-content { .confirm-content {
flex: 1; flex: 1;
} }
.confirm-content h3 { .confirm-content h3 {
margin: 0 0 12px 0; margin: 0 0 12px 0;
font-size: 16px; font-size: 16px;
color: #303133; color: #303133;
} }
.link-code { .link-code {
margin: 8px 0; margin: 8px 0;
font-size: 14px; font-size: 14px;
color: #606266; color: #606266;
} }
.link-code strong { .link-code strong {
color: #409eff; color: #409eff;
font-family: 'Courier New', monospace;
} }
.warning-text { /* 按钮组(响应式) */
margin: 12px 0 0 0; .btn-group {
color: #e6a23c; display: grid;
font-size: 14px; grid-template-columns: 1fr 1fr;
display: flex; gap: 8px;
align-items: center;
gap: 6px;
} }
/* 响应式设计 */
@media (max-width: 768px) { @media (max-width: 768px) {
.refund-management { .refund-management {
padding: 12px; padding: 12px;
} }
.page-header h2 { .page-header h2 {
font-size: 20px; font-size: 20px;
} }
.action-buttons { .action-buttons {
text-align: center; text-align: center;
} }
.el-descriptions { .el-descriptions {
font-size: 12px; font-size: 12px;
} }
.refund-confirm { .refund-confirm {
flex-direction: column; flex-direction: column;
text-align: center; text-align: center;
} }
.warning-icon { .warning-icon {
align-self: center; align-self: center;
} }
/* 手机端按钮纵向排列更易点按 */
.btn-group {
grid-template-columns: 1fr;
}
} }
/* 深色主题适配 */ /* 深色主题适配 */
.dark .page-header h2 { .dark .page-header h2 { color: #e5eaf3; }
color: #e5eaf3; .dark .page-description { color: #a3a6ad; }
} .dark .confirm-content h3 { color: #e5eaf3; }
.dark .link-code { color: #a3a6ad; }
.dark .page-description { /* 小图标间隔 */
color: #a3a6ad; .mr-6 { margin-right: 6px; }
}
.dark .confirm-content h3 {
color: #e5eaf3;
}
.dark .link-code {
color: #a3a6ad;
}
</style> </style>