909 lines
20 KiB
Plaintext
909 lines
20 KiB
Plaintext
<!-- 管理端 - 用户详情页 -->
|
|
<template>
|
|
<view class="user-detail-page">
|
|
<!-- 用户基本信息 -->
|
|
<view class="user-profile">
|
|
<view class="profile-header">
|
|
<image :src="user.avatar_url || '/static/default-avatar.png'" class="user-avatar" />
|
|
<view class="user-basic">
|
|
<text class="user-name">{{ user.nickname || user.phone }}</text>
|
|
<text class="user-id">ID: {{ user.id }}</text>
|
|
<view class="user-status">
|
|
<text class="status-badge" :class="{ active: user.status === 1, inactive: user.status === 0 }">
|
|
{{ user.status === 1 ? '正常' : '冻结' }}
|
|
</text>
|
|
<text class="user-type">{{ getUserTypeText() }}</text>
|
|
</view>
|
|
</view>
|
|
<view class="action-menu" @click="showActionMenu">⋮</view>
|
|
</view>
|
|
|
|
<view class="profile-details">
|
|
<view class="detail-item">
|
|
<text class="detail-label">手机号码</text>
|
|
<text class="detail-value">{{ user.phone }}</text>
|
|
</view>
|
|
<view class="detail-item">
|
|
<text class="detail-label">邮箱地址</text>
|
|
<text class="detail-value">{{ user.email || '未设置' }}</text>
|
|
</view>
|
|
<view class="detail-item">
|
|
<text class="detail-label">性别</text>
|
|
<text class="detail-value">{{ getGenderText() }}</text>
|
|
</view>
|
|
<view class="detail-item">
|
|
<text class="detail-label">注册时间</text>
|
|
<text class="detail-value">{{ formatTime(user.created_at) }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 用户统计 -->
|
|
<view class="user-stats">
|
|
<view class="section-title">用户统计</view>
|
|
<view class="stats-grid">
|
|
<view class="stat-item">
|
|
<text class="stat-value">{{ userStats.total_orders }}</text>
|
|
<text class="stat-label">总订单数</text>
|
|
</view>
|
|
<view class="stat-item">
|
|
<text class="stat-value">¥{{ userStats.total_amount }}</text>
|
|
<text class="stat-label">消费总额</text>
|
|
</view>
|
|
<view class="stat-item">
|
|
<text class="stat-value">{{ userStats.total_reviews }}</text>
|
|
<text class="stat-label">评价数量</text>
|
|
</view>
|
|
<view class="stat-item">
|
|
<text class="stat-value">{{ userStats.avg_rating.toFixed(1) }}</text>
|
|
<text class="stat-label">平均评分</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 最近订单 -->
|
|
<view class="recent-orders">
|
|
<view class="section-header">
|
|
<text class="section-title">最近订单</text>
|
|
<text class="view-all" @click="viewAllOrders">查看全部</text>
|
|
</view>
|
|
|
|
<view v-if="recentOrders.length === 0" class="empty-orders">
|
|
<text class="empty-text">暂无订单记录</text>
|
|
</view>
|
|
|
|
<view v-for="order in recentOrders" :key="order.id" class="order-item" @click="viewOrderDetail(order)">
|
|
<view class="order-header">
|
|
<text class="order-no">{{ order.order_no }}</text>
|
|
<text class="order-status" :class="getOrderStatusClass(order.status)">{{ getOrderStatusText(order.status) }}</text>
|
|
</view>
|
|
<view class="order-info">
|
|
<text class="order-amount">¥{{ order.actual_amount }}</text>
|
|
<text class="order-time">{{ formatTime(order.created_at) }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 用户行为记录 -->
|
|
<view class="user-activities">
|
|
<view class="section-header">
|
|
<text class="section-title">行为记录</text>
|
|
<text class="view-all" @click="viewAllActivities">查看全部</text>
|
|
</view>
|
|
|
|
<view v-for="activity in userActivities" :key="activity.id" class="activity-item">
|
|
<view class="activity-icon" :class="activity.type">{{ getActivityIcon(activity.type) }}</view>
|
|
<view class="activity-content">
|
|
<text class="activity-desc">{{ activity.description }}</text>
|
|
<text class="activity-time">{{ formatTime(activity.created_at) }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 风险评估 -->
|
|
<view class="risk-assessment">
|
|
<view class="section-title">风险评估</view>
|
|
<view class="risk-score">
|
|
<view class="score-circle" :class="getRiskLevel()">
|
|
<text class="score-value">{{ riskData.score }}</text>
|
|
<text class="score-max">/100</text>
|
|
</view>
|
|
<view class="risk-info">
|
|
<text class="risk-level">{{ getRiskLevelText() }}</text>
|
|
<text class="risk-desc">{{ getRiskDescription() }}</text>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="risk-factors">
|
|
<view v-for="factor in riskData.factors" :key="factor.type" class="factor-item">
|
|
<text class="factor-label">{{ factor.label }}</text>
|
|
<view class="factor-bar">
|
|
<view class="factor-fill" :style="{ width: factor.value + '%' }" :class="factor.level"></view>
|
|
</view>
|
|
<text class="factor-value">{{ factor.value }}%</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 操作记录 -->
|
|
<view class="admin-logs">
|
|
<view class="section-header">
|
|
<text class="section-title">操作记录</text>
|
|
<text class="add-log" @click="addAdminLog">添加记录</text>
|
|
</view>
|
|
|
|
<view v-for="log in adminLogs" :key="log.id" class="log-item">
|
|
<view class="log-content">
|
|
<text class="log-action">{{ log.action }}</text>
|
|
<text class="log-reason">{{ log.reason }}</text>
|
|
</view>
|
|
<view class="log-meta">
|
|
<text class="log-admin">{{ log.admin_name }}</text>
|
|
<text class="log-time">{{ formatTime(log.created_at) }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 操作菜单弹窗 -->
|
|
<view v-if="showMenu" class="action-modal" @click="hideActionMenu">
|
|
<view class="action-list" @click.stop>
|
|
<view class="action-item" @click="toggleUserStatus">
|
|
{{ user.status === 1 ? '冻结用户' : '解冻用户' }}
|
|
</view>
|
|
<view class="action-item" @click="resetPassword">重置密码</view>
|
|
<view class="action-item" @click="sendMessage">发送消息</view>
|
|
<view class="action-item danger" @click="deleteUser">删除用户</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
import { UserType, OrderType } from '@/types/mall-types.uts'
|
|
|
|
type UserStatsType = {
|
|
total_orders: number
|
|
total_amount: number
|
|
total_reviews: number
|
|
avg_rating: number
|
|
}
|
|
|
|
type UserActivityType = {
|
|
id: string
|
|
type: string
|
|
description: string
|
|
created_at: string
|
|
}
|
|
|
|
type RiskFactorType = {
|
|
type: string
|
|
label: string
|
|
value: number
|
|
level: string
|
|
}
|
|
|
|
type RiskDataType = {
|
|
score: number
|
|
level: string
|
|
factors: Array<RiskFactorType>
|
|
}
|
|
|
|
type AdminLogType = {
|
|
id: string
|
|
action: string
|
|
reason: string
|
|
admin_name: string
|
|
created_at: string
|
|
}
|
|
|
|
export default {
|
|
data() {
|
|
return {
|
|
user: {
|
|
id: '',
|
|
phone: '',
|
|
email: '',
|
|
nickname: '',
|
|
avatar_url: '',
|
|
gender: 0,
|
|
user_type: 0,
|
|
status: 0,
|
|
created_at: ''
|
|
} as UserType,
|
|
userStats: {
|
|
total_orders: 0,
|
|
total_amount: 0,
|
|
total_reviews: 0,
|
|
avg_rating: 0
|
|
} as UserStatsType,
|
|
recentOrders: [] as Array<OrderType>,
|
|
userActivities: [] as Array<UserActivityType>,
|
|
riskData: {
|
|
score: 0,
|
|
level: '',
|
|
factors: []
|
|
} as RiskDataType,
|
|
adminLogs: [] as Array<AdminLogType>,
|
|
showMenu: false
|
|
}
|
|
},
|
|
onLoad(options: any) {
|
|
const userId = options.userId as string
|
|
if (userId) {
|
|
this.loadUserDetail(userId)
|
|
}
|
|
},
|
|
methods: {
|
|
loadUserDetail(userId: string) {
|
|
// 模拟加载用户详情数据
|
|
this.user = {
|
|
id: userId,
|
|
phone: '13800138000',
|
|
email: 'user@example.com',
|
|
nickname: '张三',
|
|
avatar_url: '/static/avatar1.jpg',
|
|
gender: 1,
|
|
user_type: 1,
|
|
status: 1,
|
|
created_at: '2023-06-15T10:30:00'
|
|
}
|
|
|
|
this.userStats = {
|
|
total_orders: 23,
|
|
total_amount: 5680.50,
|
|
total_reviews: 18,
|
|
avg_rating: 4.3
|
|
}
|
|
|
|
this.recentOrders = [
|
|
{
|
|
id: 'order_001',
|
|
order_no: 'ORD202401150001',
|
|
user_id: userId,
|
|
merchant_id: 'merchant_001',
|
|
status: 4,
|
|
total_amount: 299.98,
|
|
discount_amount: 30.00,
|
|
delivery_fee: 8.00,
|
|
actual_amount: 277.98,
|
|
payment_method: 1,
|
|
payment_status: 1,
|
|
delivery_address: {},
|
|
created_at: '2024-01-15T14:30:00'
|
|
}
|
|
]
|
|
|
|
this.userActivities = [
|
|
{
|
|
id: 'activity_001',
|
|
type: 'login',
|
|
description: '用户登录系统',
|
|
created_at: '2024-01-15T09:30:00'
|
|
},
|
|
{
|
|
id: 'activity_002',
|
|
type: 'order',
|
|
description: '创建订单 ORD202401150001',
|
|
created_at: '2024-01-15T14:30:00'
|
|
},
|
|
{
|
|
id: 'activity_003',
|
|
type: 'review',
|
|
description: '对商品进行评价',
|
|
created_at: '2024-01-14T16:20:00'
|
|
}
|
|
]
|
|
|
|
this.riskData = {
|
|
score: 25,
|
|
level: 'low',
|
|
factors: [
|
|
{ type: 'refund', label: '退款率', value: 15, level: 'low' },
|
|
{ type: 'complaint', label: '投诉率', value: 5, level: 'low' },
|
|
{ type: 'chargeback', label: '拒付率', value: 0, level: 'low' },
|
|
{ type: 'fraud', label: '欺诈风险', value: 10, level: 'low' }
|
|
]
|
|
}
|
|
|
|
this.adminLogs = [
|
|
{
|
|
id: 'log_001',
|
|
action: '账户验证',
|
|
reason: '用户实名认证通过',
|
|
admin_name: '管理员小李',
|
|
created_at: '2024-01-10T11:00:00'
|
|
}
|
|
]
|
|
},
|
|
|
|
getUserTypeText(): string {
|
|
const types = ['普通用户', '普通用户', '商家用户', '配送员', '管理员']
|
|
return types[this.user.user_type] || '未知'
|
|
},
|
|
|
|
getGenderText(): string {
|
|
const genders = ['未知', '男', '女']
|
|
return genders[this.user.gender] || '未知'
|
|
},
|
|
|
|
getOrderStatusText(status: number): string {
|
|
const statusTexts = ['异常', '待支付', '待发货', '待收货', '已完成', '已取消']
|
|
return statusTexts[status] || '未知'
|
|
},
|
|
|
|
getOrderStatusClass(status: number): string {
|
|
const statusClasses = ['error', 'pending', 'processing', 'shipping', 'completed', 'cancelled']
|
|
return statusClasses[status] || 'error'
|
|
},
|
|
|
|
getActivityIcon(type: string): string {
|
|
const icons: Record<string, string> = {
|
|
login: '🔐',
|
|
order: '🛍️',
|
|
review: '⭐',
|
|
refund: '💰',
|
|
complaint: '⚠️'
|
|
}
|
|
return icons[type] || '📝'
|
|
},
|
|
|
|
getRiskLevel(): string {
|
|
if (this.riskData.score < 30) return 'low'
|
|
if (this.riskData.score < 70) return 'medium'
|
|
return 'high'
|
|
},
|
|
|
|
getRiskLevelText(): string {
|
|
const level = this.getRiskLevel()
|
|
const levelTexts: Record<string, string> = {
|
|
low: '低风险',
|
|
medium: '中风险',
|
|
high: '高风险'
|
|
}
|
|
return levelTexts[level] || '未知'
|
|
},
|
|
|
|
getRiskDescription(): string {
|
|
const level = this.getRiskLevel()
|
|
const descriptions: Record<string, string> = {
|
|
low: '用户行为正常,风险较低',
|
|
medium: '存在一定风险,需要关注',
|
|
high: '高风险用户,建议重点监控'
|
|
}
|
|
return descriptions[level] || ''
|
|
},
|
|
|
|
formatTime(timeStr: string): string {
|
|
return timeStr.replace('T', ' ').split('.')[0]
|
|
},
|
|
|
|
showActionMenu() {
|
|
this.showMenu = true
|
|
},
|
|
|
|
hideActionMenu() {
|
|
this.showMenu = false
|
|
},
|
|
|
|
toggleUserStatus() {
|
|
const action = this.user.status === 1 ? '冻结' : '解冻'
|
|
uni.showModal({
|
|
title: `${action}用户`,
|
|
content: `确定要${action}用户 ${this.user.nickname} 吗?`,
|
|
success: (res) => {
|
|
if (res.confirm) {
|
|
this.user.status = this.user.status === 1 ? 0 : 1
|
|
this.hideActionMenu()
|
|
uni.showToast({
|
|
title: `${action}成功`,
|
|
icon: 'success'
|
|
})
|
|
}
|
|
}
|
|
})
|
|
},
|
|
|
|
resetPassword() {
|
|
uni.showModal({
|
|
title: '重置密码',
|
|
content: '确定要重置用户密码吗?新密码将发送到用户手机。',
|
|
success: (res) => {
|
|
if (res.confirm) {
|
|
this.hideActionMenu()
|
|
uni.showToast({
|
|
title: '密码重置成功',
|
|
icon: 'success'
|
|
})
|
|
}
|
|
}
|
|
})
|
|
},
|
|
|
|
sendMessage() {
|
|
this.hideActionMenu()
|
|
uni.navigateTo({
|
|
url: `/pages/mall/admin/send-message?userId=${this.user.id}`
|
|
})
|
|
},
|
|
|
|
deleteUser() {
|
|
uni.showModal({
|
|
title: '删除用户',
|
|
content: '删除用户将无法恢复,确定要删除吗?',
|
|
success: (res) => {
|
|
if (res.confirm) {
|
|
this.hideActionMenu()
|
|
uni.showToast({
|
|
title: '用户已删除',
|
|
icon: 'success'
|
|
})
|
|
setTimeout(() => {
|
|
uni.navigateBack()
|
|
}, 1500)
|
|
}
|
|
}
|
|
})
|
|
},
|
|
|
|
viewAllOrders() {
|
|
uni.navigateTo({
|
|
url: `/pages/mall/admin/user-orders?userId=${this.user.id}`
|
|
})
|
|
},
|
|
|
|
viewOrderDetail(order: OrderType) {
|
|
uni.navigateTo({
|
|
url: `/pages/mall/admin/order-detail?orderId=${order.id}`
|
|
})
|
|
},
|
|
|
|
viewAllActivities() {
|
|
uni.navigateTo({
|
|
url: `/pages/mall/admin/user-activities?userId=${this.user.id}`
|
|
})
|
|
},
|
|
|
|
addAdminLog() {
|
|
uni.navigateTo({
|
|
url: `/pages/mall/admin/add-log?userId=${this.user.id}`
|
|
})
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style>
|
|
.user-detail-page {
|
|
background-color: #f5f5f5;
|
|
min-height: 100vh;
|
|
}
|
|
|
|
.user-profile, .user-stats, .recent-orders, .user-activities, .risk-assessment, .admin-logs {
|
|
background-color: #fff;
|
|
margin-bottom: 20rpx;
|
|
padding: 30rpx;
|
|
}
|
|
|
|
.profile-header {
|
|
display: flex;
|
|
align-items: center;
|
|
margin-bottom: 30rpx;
|
|
}
|
|
|
|
.user-avatar {
|
|
width: 120rpx;
|
|
height: 120rpx;
|
|
border-radius: 60rpx;
|
|
margin-right: 25rpx;
|
|
}
|
|
|
|
.user-basic {
|
|
flex: 1;
|
|
}
|
|
|
|
.user-name {
|
|
font-size: 32rpx;
|
|
font-weight: bold;
|
|
color: #333;
|
|
margin-bottom: 8rpx;
|
|
}
|
|
|
|
.user-id {
|
|
font-size: 24rpx;
|
|
color: #666;
|
|
margin-bottom: 10rpx;
|
|
}
|
|
|
|
.user-status {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 15rpx;
|
|
}
|
|
|
|
.status-badge {
|
|
padding: 8rpx 16rpx;
|
|
border-radius: 12rpx;
|
|
font-size: 22rpx;
|
|
color: #fff;
|
|
}
|
|
|
|
.status-badge.active {
|
|
background-color: #4caf50;
|
|
}
|
|
|
|
.status-badge.inactive {
|
|
background-color: #ff4444;
|
|
}
|
|
|
|
.user-type {
|
|
font-size: 22rpx;
|
|
color: #666;
|
|
background-color: #f0f0f0;
|
|
padding: 8rpx 16rpx;
|
|
border-radius: 12rpx;
|
|
}
|
|
|
|
.action-menu {
|
|
font-size: 32rpx;
|
|
color: #666;
|
|
padding: 10rpx;
|
|
}
|
|
|
|
.profile-details {
|
|
border-top: 1rpx solid #f5f5f5;
|
|
padding-top: 25rpx;
|
|
}
|
|
|
|
.detail-item {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 15rpx 0;
|
|
}
|
|
|
|
.detail-label {
|
|
font-size: 26rpx;
|
|
color: #666;
|
|
width: 150rpx;
|
|
}
|
|
|
|
.detail-value {
|
|
flex: 1;
|
|
font-size: 26rpx;
|
|
color: #333;
|
|
text-align: right;
|
|
}
|
|
|
|
.section-title {
|
|
font-size: 30rpx;
|
|
font-weight: bold;
|
|
color: #333;
|
|
margin-bottom: 25rpx;
|
|
}
|
|
|
|
.section-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 25rpx;
|
|
}
|
|
|
|
.view-all, .add-log {
|
|
font-size: 24rpx;
|
|
color: #007aff;
|
|
}
|
|
|
|
.stats-grid {
|
|
display: flex;
|
|
gap: 20rpx;
|
|
}
|
|
|
|
.stat-item {
|
|
flex: 1;
|
|
text-align: center;
|
|
padding: 30rpx 0;
|
|
background-color: #f8f9fa;
|
|
border-radius: 10rpx;
|
|
}
|
|
|
|
.stat-value {
|
|
font-size: 32rpx;
|
|
font-weight: bold;
|
|
color: #333;
|
|
margin-bottom: 8rpx;
|
|
}
|
|
|
|
.stat-label {
|
|
font-size: 22rpx;
|
|
color: #666;
|
|
}
|
|
|
|
.empty-orders {
|
|
text-align: center;
|
|
padding: 60rpx 0;
|
|
}
|
|
|
|
.empty-text {
|
|
font-size: 24rpx;
|
|
color: #999;
|
|
}
|
|
|
|
.order-item, .log-item {
|
|
padding: 25rpx 0;
|
|
border-bottom: 1rpx solid #f5f5f5;
|
|
}
|
|
|
|
.order-item:last-child, .log-item:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.order-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 10rpx;
|
|
}
|
|
|
|
.order-no {
|
|
font-size: 26rpx;
|
|
color: #333;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.order-status {
|
|
font-size: 22rpx;
|
|
padding: 6rpx 12rpx;
|
|
border-radius: 10rpx;
|
|
color: #fff;
|
|
}
|
|
|
|
.order-status.pending {
|
|
background-color: #ffa726;
|
|
}
|
|
|
|
.order-status.processing {
|
|
background-color: #2196f3;
|
|
}
|
|
|
|
.order-status.shipping {
|
|
background-color: #9c27b0;
|
|
}
|
|
|
|
.order-status.completed {
|
|
background-color: #4caf50;
|
|
}
|
|
|
|
.order-status.cancelled {
|
|
background-color: #999;
|
|
}
|
|
|
|
.order-info {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
}
|
|
|
|
.order-amount {
|
|
font-size: 24rpx;
|
|
color: #ff4444;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.order-time {
|
|
font-size: 22rpx;
|
|
color: #999;
|
|
}
|
|
|
|
.activity-item {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
padding: 20rpx 0;
|
|
border-bottom: 1rpx solid #f5f5f5;
|
|
}
|
|
|
|
.activity-item:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.activity-icon {
|
|
font-size: 28rpx;
|
|
margin-right: 15rpx;
|
|
margin-top: 5rpx;
|
|
}
|
|
|
|
.activity-content {
|
|
flex: 1;
|
|
}
|
|
|
|
.activity-desc {
|
|
font-size: 26rpx;
|
|
color: #333;
|
|
margin-bottom: 5rpx;
|
|
}
|
|
|
|
.activity-time {
|
|
font-size: 22rpx;
|
|
color: #999;
|
|
}
|
|
|
|
.risk-score {
|
|
display: flex;
|
|
align-items: center;
|
|
margin-bottom: 30rpx;
|
|
}
|
|
|
|
.score-circle {
|
|
width: 140rpx;
|
|
height: 140rpx;
|
|
border-radius: 50%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin-right: 30rpx;
|
|
border: 6rpx solid;
|
|
}
|
|
|
|
.score-circle.low {
|
|
border-color: #4caf50;
|
|
background-color: #e8f5e8;
|
|
}
|
|
|
|
.score-circle.medium {
|
|
border-color: #ffa726;
|
|
background-color: #fff8e1;
|
|
}
|
|
|
|
.score-circle.high {
|
|
border-color: #ff4444;
|
|
background-color: #ffebee;
|
|
}
|
|
|
|
.score-value {
|
|
font-size: 36rpx;
|
|
font-weight: bold;
|
|
color: #333;
|
|
}
|
|
|
|
.score-max {
|
|
font-size: 20rpx;
|
|
color: #666;
|
|
margin-top: -5rpx;
|
|
}
|
|
|
|
.risk-info {
|
|
flex: 1;
|
|
}
|
|
|
|
.risk-level {
|
|
font-size: 28rpx;
|
|
font-weight: bold;
|
|
color: #333;
|
|
margin-bottom: 8rpx;
|
|
}
|
|
|
|
.risk-desc {
|
|
font-size: 24rpx;
|
|
color: #666;
|
|
line-height: 1.4;
|
|
}
|
|
|
|
.risk-factors {
|
|
margin-top: 30rpx;
|
|
}
|
|
|
|
.factor-item {
|
|
display: flex;
|
|
align-items: center;
|
|
margin-bottom: 20rpx;
|
|
}
|
|
|
|
.factor-label {
|
|
font-size: 24rpx;
|
|
color: #666;
|
|
width: 120rpx;
|
|
}
|
|
|
|
.factor-bar {
|
|
flex: 1;
|
|
height: 12rpx;
|
|
background-color: #f0f0f0;
|
|
border-radius: 6rpx;
|
|
margin: 0 20rpx;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.factor-fill {
|
|
height: 100%;
|
|
border-radius: 6rpx;
|
|
}
|
|
|
|
.factor-fill.low {
|
|
background-color: #4caf50;
|
|
}
|
|
|
|
.factor-fill.medium {
|
|
background-color: #ffa726;
|
|
}
|
|
|
|
.factor-fill.high {
|
|
background-color: #ff4444;
|
|
}
|
|
|
|
.factor-value {
|
|
font-size: 22rpx;
|
|
color: #333;
|
|
width: 60rpx;
|
|
text-align: right;
|
|
}
|
|
|
|
.log-content {
|
|
margin-bottom: 10rpx;
|
|
}
|
|
|
|
.log-action {
|
|
font-size: 26rpx;
|
|
color: #333;
|
|
font-weight: bold;
|
|
margin-bottom: 5rpx;
|
|
}
|
|
|
|
.log-reason {
|
|
font-size: 24rpx;
|
|
color: #666;
|
|
}
|
|
|
|
.log-meta {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
}
|
|
|
|
.log-admin {
|
|
font-size: 22rpx;
|
|
color: #007aff;
|
|
}
|
|
|
|
.log-time {
|
|
font-size: 22rpx;
|
|
color: #999;
|
|
}
|
|
|
|
.action-modal {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background-color: rgba(0, 0, 0, 0.5);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
z-index: 999;
|
|
}
|
|
|
|
.action-list {
|
|
background-color: #fff;
|
|
border-radius: 20rpx;
|
|
padding: 30rpx 0;
|
|
margin: 0 60rpx;
|
|
max-width: 500rpx;
|
|
}
|
|
|
|
.action-item {
|
|
padding: 25rpx 40rpx;
|
|
font-size: 28rpx;
|
|
color: #333;
|
|
text-align: center;
|
|
border-bottom: 1rpx solid #f5f5f5;
|
|
}
|
|
|
|
.action-item:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.action-item.danger {
|
|
color: #ff4444;
|
|
}
|
|
</style>
|