chore: establish V1.1 recovery baseline
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
<template>
|
||||
<div class="page-container">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="6" v-for="card in statCards" :key="card.key">
|
||||
<el-card shadow="hover" class="stat-card">
|
||||
<div class="stat-value" :style="{ color: card.color }">{{ card.value }}</div>
|
||||
<div class="stat-label">{{ card.label }}</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="16" style="margin-top:16px">
|
||||
<el-col :span="12">
|
||||
<el-card shadow="never">
|
||||
<template #header><span>用户统计</span></template>
|
||||
<el-descriptions :column="2" border size="small">
|
||||
<el-descriptions-item label="累计注册">{{ stats.totalUsers }}</el-descriptions-item>
|
||||
<el-descriptions-item label="已实名">{{ stats.verifiedUsers }}</el-descriptions-item>
|
||||
<el-descriptions-item label="活跃用户(7天)">{{ stats.activeUsers }}</el-descriptions-item>
|
||||
<el-descriptions-item label="待审核举报">{{ stats.pendingReports }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-card shadow="never">
|
||||
<template #header><span>婚恋服务</span></template>
|
||||
<el-descriptions :column="2" border size="small">
|
||||
<el-descriptions-item label="总喜欢数">{{ stats.totalLikes }}</el-descriptions-item>
|
||||
<el-descriptions-item label="双向Match">{{ stats.totalMatches }}</el-descriptions-item>
|
||||
<el-descriptions-item label="正在了解">{{ stats.gettingToKnow }}</el-descriptions-item>
|
||||
<el-descriptions-item label="成功牵手">{{ stats.successPairs }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="16" style="margin-top:16px">
|
||||
<el-col :span="12">
|
||||
<el-card shadow="never">
|
||||
<template #header><span>安全风险</span></template>
|
||||
<el-descriptions :column="2" border size="small">
|
||||
<el-descriptions-item label="封禁账号">{{ stats.bannedUsers }}</el-descriptions-item>
|
||||
<el-descriptions-item label="处理中举报">{{ stats.processingReports }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-card shadow="never">
|
||||
<template #header><span>公益红娘</span></template>
|
||||
<el-descriptions :column="2" border size="small">
|
||||
<el-descriptions-item label="红娘服务申请">{{ stats.matchmakerServices }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { get } from '@/api/client'
|
||||
|
||||
const stats = ref<any>({})
|
||||
const loading = ref(false)
|
||||
|
||||
const statCards = [
|
||||
{ key: 'totalUsers', label: '累计注册', value: 0, color: '#409EFF' },
|
||||
{ key: 'verifiedUsers', label: '已实名', value: 0, color: '#67C23A' },
|
||||
{ key: 'activeUsers', label: '近7天活跃', value: 0, color: '#E59A32' },
|
||||
{ key: 'successPairs', label: '成功牵手', value: 0, color: '#E96B6B' }
|
||||
]
|
||||
|
||||
async function loadStats() {
|
||||
loading.value = true
|
||||
try {
|
||||
const token = localStorage.getItem('accessToken')
|
||||
const res: any = await get('/operator/analytics/dashboard', {}, { Authorization: `Bearer ${token}` })
|
||||
stats.value = res || {}
|
||||
statCards[0].value = res.totalUsers || 0
|
||||
statCards[1].value = res.verifiedUsers || 0
|
||||
statCards[2].value = res.activeUsers || 0
|
||||
statCards[3].value = res.successPairs || 0
|
||||
} catch (e) { console.error(e) }
|
||||
finally { loading.value = false }
|
||||
}
|
||||
|
||||
onMounted(loadStats)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-container { padding: 24px; }
|
||||
.stat-card { text-align: center; }
|
||||
.stat-value { font-size: 36px; font-weight: bold; }
|
||||
.stat-label { font-size: 14px; color: #666; margin-top: 8px; }
|
||||
</style>
|
||||
@@ -0,0 +1,73 @@
|
||||
<template>
|
||||
<div class="login-container">
|
||||
<div class="login-card">
|
||||
<div class="login-header">
|
||||
<h1>丹阳公益婚恋服务平台</h1>
|
||||
<p>工作人员登录</p>
|
||||
</div>
|
||||
<el-form ref="formRef" :model="form" :rules="rules" class="login-form" @submit.prevent="handleLogin">
|
||||
<el-form-item prop="username">
|
||||
<el-input v-model="form.username" placeholder="用户名" prefix-icon="User" size="large" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="password">
|
||||
<el-input v-model="form.password" type="password" placeholder="密码" prefix-icon="Lock"
|
||||
size="large" show-password @keyup.enter="handleLogin" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" size="large" :loading="loading" style="width:100%;background:#E96B6B;border-color:#E96B6B"
|
||||
@click="handleLogin">登录</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div class="login-footer">
|
||||
<p>丹阳公益婚恋服务平台 · 运营管理后台</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import type { FormInstance } from 'element-plus'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
|
||||
const router = useRouter()
|
||||
const formRef = ref<FormInstance>()
|
||||
const loading = ref(false)
|
||||
const authStore = useAuthStore()
|
||||
|
||||
const form = reactive({ username: '', password: '' })
|
||||
const rules = {
|
||||
username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
|
||||
password: [{ required: true, message: '请输入密码', trigger: 'blur' }],
|
||||
}
|
||||
|
||||
async function handleLogin() {
|
||||
if (!formRef.value) return
|
||||
await formRef.value.validate(async (valid) => {
|
||||
if (!valid) return
|
||||
loading.value = true
|
||||
try {
|
||||
await authStore.login(form.username, form.password)
|
||||
ElMessage.success('登录成功')
|
||||
router.push('/dashboard')
|
||||
} catch (err: any) {
|
||||
ElMessage.error(err.message || '登录失败,请检查用户名和密码')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.login-container { width: 100%; height: 100vh; display: flex; align-items: center; justify-content: center;
|
||||
background: linear-gradient(135deg, #FFF3F2 0%, #FFF8F7 100%); }
|
||||
.login-card { width: 400px; padding: 40px; background: #fff; border-radius: 12px; box-shadow: 0 4px 24px rgba(233,107,107,0.1); }
|
||||
.login-header { text-align: center; margin-bottom: 32px; }
|
||||
.login-header h1 { font-size: 24px; color: #E96B6B; margin-bottom: 8px; }
|
||||
.login-header p { font-size: 14px; color: #999; }
|
||||
.login-form { margin-top: 24px; }
|
||||
.login-footer { text-align: center; margin-top: 24px; font-size: 12px; color: #bbb; }
|
||||
</style>
|
||||
Reference in New Issue
Block a user