From 3317c84b5e4c563dfdb3de2b169781dad7ffd771 Mon Sep 17 00:00:00 2001 From: Ghost <106998207@qq.com> Date: Sat, 28 Mar 2026 16:46:00 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=AE=A1=E7=90=86=E7=AB=AF=E4=B8=8E?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E7=AB=AF=E6=89=B9=E9=87=8F=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=EF=BC=88=E8=AE=A2=E5=8D=95/=E9=82=80=E8=AF=B7=E5=8F=8C?= =?UTF-8?q?=E7=A0=81/tabBar/=E5=AE=A1=E6=A0=B8=E4=B8=8E=E5=88=86=E9=94=80?= =?UTF-8?q?=E7=AD=89=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 管理端:用户详情、企业看板双小程序码、超管设置与用户列表等 - 后端:订单接口、邀请码企业版+个人版、分析与测试相关调整 - 微信小程序/抖音:自定义 tabBar 居中与拍摄钮上移、相机页与结果页、订单页、支付与统计 - 新增 faceResultDetail 工具与 mp 分析迁移脚本 Made-with: Cursor --- admin/src/components/UserDetailDialog.vue | 273 ++-- admin/src/utils/faceResultDetail.ts | 138 ++ admin/src/views/admin/Dashboard.vue | 47 +- admin/src/views/admin/Users.vue | 277 +++- admin/src/views/superadmin/EnterpriseHub.vue | 11 +- admin/src/views/superadmin/Enterprises.vue | 1290 +++++++++++++---- admin/src/views/superadmin/Settings.vue | 13 +- admin/src/views/superadmin/Users.vue | 269 +++- api/app/controller/admin/AppUser.php | 25 +- api/app/controller/admin/Invite.php | 56 +- api/app/controller/api/Analytics.php | 38 +- api/app/controller/api/Analyze.php | 30 +- api/app/controller/api/Auth.php | 22 +- api/app/controller/api/Order.php | 119 ++ api/app/controller/api/Test.php | 15 +- api/app/controller/superadmin/AppUser.php | 221 ++- .../controller/superadmin/DataMigration.php | 209 ++- api/app/controller/superadmin/Enterprise.php | 130 +- .../migrations/add_mp_analytics_events.sql | 16 +- api/route/api.php | 2 + douyin-miniprogram/app.js | 42 +- douyin-miniprogram/app.json | 1 + douyin-miniprogram/custom-tab-bar/index.ttss | 16 +- douyin-miniprogram/pages/index/camera.js | 3 + douyin-miniprogram/pages/index/result.js | 23 +- douyin-miniprogram/pages/index/result.ttml | 8 - douyin-miniprogram/pages/order/index.js | 79 + douyin-miniprogram/pages/order/index.json | 7 + douyin-miniprogram/pages/order/index.ttml | 32 + douyin-miniprogram/pages/order/index.ttss | 134 ++ douyin-miniprogram/pages/profile/index.js | 14 +- douyin-miniprogram/pages/profile/index.ttml | 23 +- douyin-miniprogram/pages/profile/index.ttss | 1 + douyin-miniprogram/pages/result/disc.js | 314 ++-- douyin-miniprogram/pages/result/mbti.js | 448 +++--- douyin-miniprogram/pages/result/pdp.js | 314 ++-- douyin-miniprogram/pages/test/disc.js | 56 +- douyin-miniprogram/pages/test/mbti.js | 53 +- douyin-miniprogram/pages/test/pdp.js | 56 +- douyin-miniprogram/utils/analytics.js | 110 +- douyin-miniprogram/utils/payment.js | 3 + miniprogram/app.js | 55 +- miniprogram/app.json | 1 + miniprogram/custom-tab-bar/index.wxss | 18 +- miniprogram/pages/index/camera.js | 3 + miniprogram/pages/index/result.js | 26 +- miniprogram/pages/index/result.wxml | 9 - miniprogram/pages/index/result.wxss | 4 +- miniprogram/pages/order/index.js | 81 ++ miniprogram/pages/order/index.json | 7 + miniprogram/pages/order/index.wxml | 32 + miniprogram/pages/order/index.wxss | 134 ++ miniprogram/pages/profile/index.js | 16 +- miniprogram/pages/profile/index.wxml | 23 +- miniprogram/pages/profile/index.wxss | 1 + miniprogram/pages/result/disc.js | 314 ++-- miniprogram/pages/result/disc.wxml | 4 +- miniprogram/pages/result/mbti.wxml | 4 +- miniprogram/pages/result/pdp.js | 314 ++-- miniprogram/pages/result/pdp.wxml | 4 +- miniprogram/pages/test/disc.js | 58 +- miniprogram/pages/test/mbti.js | 55 +- miniprogram/pages/test/pdp.js | 58 +- miniprogram/utils/analytics.js | 113 +- miniprogram/utils/payment.js | 9 +- 65 files changed, 4442 insertions(+), 1839 deletions(-) create mode 100644 admin/src/utils/faceResultDetail.ts create mode 100644 api/app/controller/api/Order.php create mode 100644 douyin-miniprogram/pages/order/index.js create mode 100644 douyin-miniprogram/pages/order/index.json create mode 100644 douyin-miniprogram/pages/order/index.ttml create mode 100644 douyin-miniprogram/pages/order/index.ttss create mode 100644 miniprogram/pages/order/index.js create mode 100644 miniprogram/pages/order/index.json create mode 100644 miniprogram/pages/order/index.wxml create mode 100644 miniprogram/pages/order/index.wxss diff --git a/admin/src/components/UserDetailDialog.vue b/admin/src/components/UserDetailDialog.vue index 8012723..688f78d 100644 --- a/admin/src/components/UserDetailDialog.vue +++ b/admin/src/components/UserDetailDialog.vue @@ -9,142 +9,132 @@ append-to-body @update:model-value="$emit('update:modelValue', $event)" > -
+ +
-
暂无测试记录
-
+
暂无测试记录
+
-
+ -
暂无人脸分析照片
+
暂无人脸分析照片
@@ -277,6 +267,13 @@ const avatarLetter = computed(() => { return (n.charAt(0) || '?').toUpperCase() }) +const detailAvatarUrl = computed(() => { + const u = props.user as Record | null | undefined + if (!u) return '' + const a = u.avatar ?? u.avatarUrl ?? '' + return typeof a === 'string' ? a.trim() : '' +}) + const rawTests = computed(() => (props.user?.testList || []) as any[]) const hasTestScopeCol = computed(() => rawTests.value.some(t => t.testScope != null)) @@ -601,18 +598,16 @@ function openMail(email: string) { } } -.ud-wrap { - min-height: 200px; -} - -.ud-grid { +/* 整行布局:侧栏与主区之间保留视觉空隙(与设计稿一致约 16~24px) */ +.ud-layout { display: flex; - gap: 14px; align-items: stretch; + gap: 20px; + min-height: 200px; max-height: 78vh; } -.ud-side { +.ud-sidebar { width: 200px; flex-shrink: 0; background: linear-gradient(180deg, #faf5ff 0%, #fff 40%); @@ -621,19 +616,19 @@ function openMail(email: string) { padding: 12px; } -.ud-avatar-block { +.ud-profile-avatar { text-align: center; margin-bottom: 10px; } -.ud-avatar-img { +.ud-profile-avatar__img { width: 56px; height: 56px; border-radius: 50%; object-fit: cover; } -.ud-avatar-letter { +.ud-profile-avatar__letter { width: 56px; height: 56px; border-radius: 50%; @@ -646,14 +641,14 @@ function openMail(email: string) { justify-content: center; } -.ud-name { +.ud-profile-name { margin-top: 6px; font-weight: 700; font-size: 14px; color: #111827; } -.ud-meta-row { +.ud-profile-meta__row { display: flex; align-items: center; gap: 6px; @@ -666,14 +661,14 @@ function openMail(email: string) { } } -.ud-stat-icons { +.ud-quick-stats { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; margin-top: 10px; } -.ud-stat-ic { +.ud-quick-stats__tile { background: #fff; border-radius: 8px; padding: 6px 8px; @@ -690,21 +685,21 @@ function openMail(email: string) { } } -.ud-tags { +.ud-dimension-tags { margin-top: 12px; } -.ud-tags-title { +.ud-dimension-tags__title { font-size: 11px; color: #9ca3af; margin-bottom: 6px; } -.ud-tag { +.ud-dimension-tags__item { margin: 0 4px 4px 0; } -.ud-main { +.ud-main-pane { flex: 1; min-width: 0; border: 1px solid #f3f4f6; @@ -730,26 +725,26 @@ function openMail(email: string) { } } -.ud-scroll { +.ud-main-scroll { max-height: calc(78vh - 120px); overflow-y: auto; padding-right: 4px; } -.ud-radar-row { +.ud-chart-row { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; } -.ud-radar-cell { +.ud-chart-panel { background: #fafafa; border-radius: 8px; padding: 6px 4px 4px; text-align: center; } -.ud-radar-title { +.ud-chart-heading { display: flex; align-items: center; justify-content: center; @@ -764,12 +759,12 @@ function openMail(email: string) { } } -.ud-chart { +.ud-chart-echart { height: 150px; width: 100%; } -.ud-chart-empty { +.ud-chart-placeholder { height: 120px; display: flex; align-items: center; @@ -778,19 +773,19 @@ function openMail(email: string) { color: #9ca3af; } -.ud-mini-type { +.ud-chart-footnote { font-size: 11px; color: #6b7280; padding: 0 4px 4px; } -.ud-row2 { +.ud-dual-cards { display: flex; gap: 10px; margin-top: 10px; } -.ud-card { +.ud-panel-card { background: #fff; border: 1px solid #f3f4f6; border-radius: 8px; @@ -799,11 +794,11 @@ function openMail(email: string) { min-width: 0; } -.ud-grow { +.ud-panel-card--wide { flex: 1.2; } -.ud-card-h { +.ud-panel-card__head { display: flex; align-items: center; gap: 6px; @@ -817,14 +812,14 @@ function openMail(email: string) { } } -.ud-hint { +.ud-panel-card__hint { font-weight: 400; font-size: 11px; color: #9ca3af; margin-left: 4px; } -.ud-gallup-item { +.ud-gallup-list__item { display: flex; align-items: center; gap: 8px; @@ -832,7 +827,7 @@ function openMail(email: string) { margin-bottom: 6px; } -.ud-gi-n { +.ud-gallup-list__index { width: 18px; height: 18px; border-radius: 4px; @@ -845,13 +840,13 @@ function openMail(email: string) { justify-content: center; } -.ud-roles { +.ud-rolefit-list { display: flex; flex-direction: column; gap: 6px; } -.ud-role { +.ud-rolefit-list__row { display: grid; grid-template-columns: 72px 1fr 36px; gap: 8px; @@ -859,30 +854,30 @@ function openMail(email: string) { font-size: 11px; } -.ud-role-n { +.ud-rolefit-list__name { color: #4b5563; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } -.ud-role-p { +.ud-rolefit-list__percent { text-align: right; color: #7c3aed; font-weight: 600; } -.ud-match-card { +.ud-enterprise-match { margin-top: 10px; } -.ud-match-list { +.ud-enterprise-match__list { display: flex; flex-direction: column; gap: 8px; } -.ud-match-row { +.ud-enterprise-match__item { display: flex; justify-content: space-between; gap: 10px; @@ -892,26 +887,26 @@ function openMail(email: string) { border-radius: 8px; } -.ud-m-name { +.ud-enterprise-match__name { font-weight: 600; font-size: 13px; color: #111827; } -.ud-m-sub { +.ud-enterprise-match__meta { font-size: 11px; color: #7c3aed; margin-top: 2px; } -.ud-m-reason { +.ud-enterprise-match__desc { font-size: 11px; color: #6b7280; margin-top: 4px; line-height: 1.4; } -.ud-m-contact { +.ud-enterprise-match__contact { display: flex; align-items: center; gap: 4px; @@ -924,34 +919,34 @@ function openMail(email: string) { } } -.ud-m-phone { +.ud-enterprise-match__phone { font-family: ui-monospace, monospace; color: #111827; } -.ud-m-actions { +.ud-enterprise-match__actions { display: flex; flex-direction: column; gap: 4px; flex-shrink: 0; } -.ud-muted { +.ud-empty-hint { font-size: 12px; color: #9ca3af; padding: 8px 0; } -.ud-enterprise-hint { +.ud-enterprise-match__hint { margin-top: 8px; line-height: 1.5; } -.ud-test-tb { +.ud-test-records-table { width: 100%; } -.ud-ticon { +.ud-test-type-icon { font-size: 18px; } .tic-mbti { @@ -970,33 +965,33 @@ function openMail(email: string) { color: #6b7280; } -.ud-pager { +.ud-test-records-pagination { margin-top: 8px; display: flex; justify-content: center; } -.ud-photos { +.ud-photo-gallery { display: flex; flex-wrap: wrap; gap: 8px; } -.ud-ph { +.ud-photo-gallery__thumb { width: 100px; height: 100px; border-radius: 8px; } @media (max-width: 900px) { - .ud-grid { + .ud-layout { flex-direction: column; max-height: none; } - .ud-side { + .ud-sidebar { width: 100%; } - .ud-radar-row { + .ud-chart-row { grid-template-columns: 1fr; } } diff --git a/admin/src/utils/faceResultDetail.ts b/admin/src/utils/faceResultDetail.ts new file mode 100644 index 0000000..837537a --- /dev/null +++ b/admin/src/utils/faceResultDetail.ts @@ -0,0 +1,138 @@ +/** + * 人脸 / AI 分析结果:与微信小程序 pages/index/result 展示字段对齐,供后台「测试详情」使用 + */ + +export interface FaceFeatureItem { + label: string + description: string +} + +export interface FaceBoneIceSummary { + elementType?: string + boneFleshRelation?: string +} + +export interface FaceDetailView { + photos: string[] + mbti: { type?: string; title?: string } | null + disc: { primary?: string; secondary?: string } | null + pdp: { primary?: string; secondary?: string } | null + overview: string + personalitySummary: string + /** 字符串版面相(与小程序 faceAnalysisText) */ + faceAnalysisText: string + /** 结构化面相:面部特征列表 */ + faceFeatures: FaceFeatureItem[] + /** 字符串版骨相正文 */ + boneAnalysisText: string + /** 《冰鉴》结构化摘要(小程序 boneFormSummary) */ + boneIceSummary: FaceBoneIceSummary | null + advantages: string[] + gallupTop3: string[] + relationship: string + careerDevelopment: string + familyParenting: string + partnerCofounder: string + portrait: Record | null + hrView: Record | null + bossView: Record | null + resumeHighlights: string + careers: string[] +} + +function parseMaybeJsonObject(raw: unknown): Record | null { + if (raw && typeof raw === 'object' && !Array.isArray(raw)) { + return raw as Record + } + if (typeof raw === 'string' && raw.trim().startsWith('{')) { + try { + const o = JSON.parse(raw) as unknown + return o && typeof o === 'object' && !Array.isArray(o) ? (o as Record) : null + } catch { + return null + } + } + return null +} + +function extractFaceFeatures(raw: unknown): FaceFeatureItem[] { + const o = parseMaybeJsonObject(raw) + if (!o) return [] + const fe = o.facialFeatures + if (!Array.isArray(fe)) return [] + return fe + .map((item: any) => ({ + label: String(item?.label ?? ''), + description: String(item?.description ?? '') + })) + .filter((f) => f.label || f.description) +} + +function extractBoneIceSummary(raw: unknown): FaceBoneIceSummary | null { + const o = parseMaybeJsonObject(raw) + const sum = o?.boneFormSummary + if (sum && typeof sum === 'object') { + return { + elementType: sum.elementType != null ? String(sum.elementType) : undefined, + boneFleshRelation: sum.boneFleshRelation != null ? String(sum.boneFleshRelation) : undefined + } + } + return null +} + +/** 从 AI 结果 JSON 构建后台展示模型(兼容字符串 / 结构化 / JSON 字符串) */ +export function buildFaceDetailFromParsed(parsed: Record | null | undefined): FaceDetailView | null { + if (!parsed || typeof parsed !== 'object') return null + + const faceRaw = parsed.faceAnalysis + const boneRaw = parsed.boneAnalysis + + const faceFeatures = extractFaceFeatures(faceRaw) + let faceAnalysisText = '' + if (typeof faceRaw === 'string') { + const tryObj = parseMaybeJsonObject(faceRaw) + if (tryObj && Array.isArray(tryObj.facialFeatures) && tryObj.facialFeatures.length) { + faceAnalysisText = '' + } else { + faceAnalysisText = faceRaw.trim() + } + } + + const boneIceSummary = extractBoneIceSummary(boneRaw) + let boneAnalysisText = '' + if (typeof boneRaw === 'string') { + const tryObj = parseMaybeJsonObject(boneRaw) + if (tryObj?.boneFormSummary && typeof tryObj.boneFormSummary === 'object') { + boneAnalysisText = '' + } else { + boneAnalysisText = boneRaw.trim() + } + } + + const strArr = (v: unknown): string[] => + Array.isArray(v) ? (v as unknown[]).map((x) => String(x)).filter(Boolean) : [] + + return { + photos: Array.isArray(parsed.photoUrls) ? (parsed.photoUrls as string[]) : [], + mbti: parsed.mbti && typeof parsed.mbti === 'object' ? parsed.mbti : null, + disc: parsed.disc && typeof parsed.disc === 'object' ? parsed.disc : null, + pdp: parsed.pdp && typeof parsed.pdp === 'object' ? parsed.pdp : null, + overview: String(parsed.overview ?? ''), + personalitySummary: String(parsed.personalitySummary ?? ''), + faceAnalysisText, + faceFeatures, + boneAnalysisText, + boneIceSummary, + advantages: strArr(parsed.advantages), + gallupTop3: strArr(parsed.gallupTop3), + relationship: String(parsed.relationship ?? ''), + careerDevelopment: String(parsed.careerDevelopment ?? ''), + familyParenting: String(parsed.familyParenting ?? ''), + partnerCofounder: String(parsed.partnerCofounder ?? ''), + portrait: parsed.portrait && typeof parsed.portrait === 'object' ? parsed.portrait : null, + hrView: parsed.hrView && typeof parsed.hrView === 'object' ? parsed.hrView : null, + bossView: parsed.bossView && typeof parsed.bossView === 'object' ? parsed.bossView : null, + resumeHighlights: String(parsed.resumeHighlights ?? ''), + careers: strArr(parsed.careers) + } +} diff --git a/admin/src/views/admin/Dashboard.vue b/admin/src/views/admin/Dashboard.vue index 92f3a7c..09aacad 100644 --- a/admin/src/views/admin/Dashboard.vue +++ b/admin/src/views/admin/Dashboard.vue @@ -110,15 +110,24 @@

邀请小程序码

-

员工 / 客户扫码进入企业测评

+

企业版扫码进企业测评;个人版扫码进个人首页

- {{ inviteQrcode ? '刷新' : '生成' }} + {{ inviteQrcodeEnterprise || inviteQrcodePersonal ? '刷新' : '生成' }}
- 邀请码 - 点击生成 + + 点击生成(将同时生成两枚码)
@@ -189,7 +198,8 @@ const faceSubtypeHints = ref<{ const loading = ref(false) const inviteLoading = ref(false) -const inviteQrcode = ref('') +const inviteQrcodeEnterprise = ref('') +const inviteQrcodePersonal = ref('') /** 侧栏表格最大高度:单屏内滚动,不撑开整页 */ const tableMaxH = 220 @@ -388,9 +398,13 @@ const loadInviteQrcode = async () => { inviteLoading.value = true try { const res: any = await request.get('/admin/invite/qrcode') - const qrcode = res?.data?.qrcode - if (qrcode && typeof qrcode === 'string') { - inviteQrcode.value = qrcode + const d = res?.data + const ent = d?.enterprise?.qrcode ?? d?.qrcode + const per = d?.personal?.qrcode + if (typeof ent === 'string' && ent) inviteQrcodeEnterprise.value = ent + if (typeof per === 'string' && per) inviteQrcodePersonal.value = per + if (inviteQrcodeEnterprise.value || inviteQrcodePersonal.value) { + ElMessage.success('已生成企业版与个人版小程序码') } else { ElMessage.error(res?.message || res?.msg || '生成失败,请确认企业绑定') } @@ -807,11 +821,26 @@ const loadInviteQrcode = async () => { .invite-body { display: flex; - align-items: center; + flex-wrap: wrap; + align-items: flex-start; justify-content: center; + gap: 16px; min-height: 112px; } +.invite-card { + display: flex; + flex-direction: column; + align-items: center; + gap: 6px; +} + +.invite-label { + font-size: 12px; + font-weight: 600; + color: #374151; +} + .invite-img { width: 112px; height: 112px; diff --git a/admin/src/views/admin/Users.vue b/admin/src/views/admin/Users.vue index f41a6b4..9a10f59 100644 --- a/admin/src/views/admin/Users.vue +++ b/admin/src/views/admin/Users.vue @@ -35,7 +35,16 @@ -
-
- {{ item.label }} +
+
+
+ {{ item.label }} +
+
+ +
+ + 刷新 + + + 新建企业 +
@@ -124,8 +135,7 @@

创建新企业

-

添加新企业到系统,设置基本信息和配置

+

设置企业基本信息、管理员账号及联系方式

- - - - - - - - + + + + +
基础与账号
+ + + + + + + - 企业管理员账号 + + + - - - + + + - - - + + + +
- - - + + +
联系信息与状态
+ + + - 企业信息 + + + - - - + + + - - - + + + + + + + - - - - - - - - - - - - - - - + + + +
+
-
- -
-

基本信息

-
-
- 企业名称: - {{ viewEnterpriseData.name }} +
+ + - -
-

管理员账号 ({{ viewEnterpriseData.adminAccounts?.length || 0 }})

- - - - - - - - - - - -
- -

暂无管理员账户

-
-
+ +
+ + + +
+
+
+
小程序用户
+
{{ viewEnterpriseData.wechatUsersTotal ?? viewEnterpriseData.wechatUserCount ?? 0 }}
+
登记用户数
+
+
+
测试记录
+
{{ viewEnterpriseData.miniprogramTestResultsTotal ?? 0 }}
+
小程序测评条数
+
+
+
订单
+
{{ viewEnterpriseData.orderStats?.totalCount ?? 0 }}
+
累计订单
+
+
- -
-

用户列表 ({{ viewEnterpriseUsers.length }})

- - - - - - - - - -
- -

暂无用户数据

-
-
+
+
+
订单与收入
+ +
暂无订单数据
+
+
+
近30天埋点
+
+
+ 事件 + + {{ viewEnterpriseData.analyticsStats?.eventTotal ?? 0 }} +
+
+ page_view + + {{ viewEnterpriseData.analyticsStats?.pageViewCount ?? 0 }} +
+
+

{{ viewEnterpriseData.analyticsStats.hint }}

+
+
- -
-

测试记录 ({{ viewEnterpriseTests.length }})

- - - - - - - -
- -

暂无测试数据

-
-
+
+
管理员
+
+ + {{ a.username }} + + …共 {{ viewEnterpriseData.adminAccounts?.length }} 人 +
+
+
+
- -
-

统计数据

-
-
- 用户总数: - {{ viewEnterpriseData.userCount || 0 }} -
-
- 测试总量: - {{ viewEnterpriseData.testUsage || 0 }} -
-
- 管理员数: - {{ viewEnterpriseData.adminAccounts?.length || 0 }} -
-
-
+ + +
+ + + + + + + + + + + + + + +
+ +

暂无用户数据

+
+
+
+ + + +
+ + + + + + + + + + + + + +
+ +

暂无测试数据

+
+
+
+ + + +
+
+
+
订单总数
+
{{ viewEnterpriseData.orderStats?.totalCount ?? 0 }}
+
+
+
已支付
+
{{ viewEnterpriseData.orderStats?.paidCount ?? 0 }}
+
+
+
总金额
+
¥{{ fenToYuan(viewEnterpriseData.orderStats?.paidAmountFen) }}
+
+
+ +
+
最近订单
+ + + + + + + + + + + + + +
+
+
+ + + +
+
+
+ 30天事件总数 + {{ viewEnterpriseData.analyticsStats?.eventTotal ?? 0 }} +
+
+ 页面曝光 (PV) + {{ viewEnterpriseData.analyticsStats?.pageViewCount ?? 0 }} +
+
+

{{ viewEnterpriseData.analyticsStats.hint }}

+ +
+
事件分布
+ + + + +
+
+
+ + + +
+ + + + + + + + +
+ +

暂无管理员账户

+
+
+
+
+
@@ -449,8 +664,24 @@