feat(activity&admin): 优化组局起止时段与性别限制,PC后台核销看板与登录页体验升级

这个提交包含在:
xcbyy
2026-11-05 16:06:18 +08:00
父节点 a1907f18a8
当前提交 f3da2e4a63
共有 21 个文件被更改,包括 1990 次插入 和 203 次删除

查看文件

@@ -5,7 +5,7 @@
<link rel="icon" type="image/png" href="/admin/favicon.png">
<link rel="shortcut icon" type="image/png" href="/admin/favicon.png">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>陪玩与同城生活陪伴平台 - PC运营管理控制台</title>
<title>同城陪玩管理后台 - 运营管理控制台</title>
</head>
<body>
<div id="app"></div>

查看文件

@@ -1,5 +1,7 @@
<template>
<a-layout-footer class="footer">Arco Pro</a-layout-footer>
<a-layout-footer class="footer">
同城陪玩运营管理平台 © 2026 PeiWan Pro · 生活陪伴与电竞社交运营系统
</a-layout-footer>
</template>
<script lang="ts" setup></script>
@@ -10,7 +12,10 @@
align-items: center;
justify-content: center;
height: 40px;
color: var(--color-text-2);
color: var(--color-text-3);
font-size: 13px;
text-align: center;
letter-spacing: 0.3px;
user-select: none;
}
</style>

查看文件

@@ -12,8 +12,12 @@
<a-select
v-model="selectedActivityId"
placeholder="请选择核销对应的组局活动"
style="width: 100%"
@change="loadParticipants"
>
<a-option value="all">
<span style="font-weight: 600; color: #165dff">全部 (查看所有活动核销状态)</span>
</a-option>
<a-option
v-for="act in activityOptions"
:key="act.id"
@@ -29,13 +33,14 @@
placeholder="输入现场4位纯数字核销码 (如 8392)"
maxlength="4"
allow-clear
@press-enter="() => handleManualVerify()"
>
<template #prefix><icon-safe /></template>
</a-input>
</a-col>
<a-col :span="6">
<a-col :span="8">
<a-space>
<a-button type="primary" :loading="verifying" @click="handleManualVerify">
<a-button type="primary" :loading="verifying" @click="() => handleManualVerify()">
<template #icon><icon-check-circle /></template>
立即核销到场
</a-button>
@@ -52,7 +57,18 @@
<a-card class="general-card" title="参与者4位核销码全景列表与到场签到追踪">
<template #extra>
<a-space>
<a-tag color="blue">当前活动: {{ currentActivity?.title || '未选择' }}</a-tag>
<a-radio-group
v-model="filterStatus"
type="button"
size="small"
>
<a-radio value="ALL">全部 ({{ participantsList.length }})</a-radio>
<a-radio value="UNCHECKED">待核销 ({{ uncheckedCount }})</a-radio>
<a-radio value="CHECKED">已核销 ({{ verifiedCount }})</a-radio>
</a-radio-group>
<a-tag :color="selectedActivityId === 'all' ? 'arcoblue' : 'blue'">
当前查看: {{ currentActivityText }}
</a-tag>
<a-tag color="green">已核销: {{ verifiedCount }} / {{ participantsList.length }}人</a-tag>
<a-button size="small" @click="loadParticipants">
<template #icon><icon-refresh /></template>
@@ -62,18 +78,37 @@
</template>
<a-table
:data="participantsList"
:data="filteredParticipants"
:loading="loading"
:pagination="false"
:pagination="{ pageSize: 10, showTotal: true, showJumper: true }"
row-key="id"
>
<template #columns>
<!-- 全量视图下展示所属活动列 -->
<a-table-column
v-if="selectedActivityId === 'all'"
title="所属活动 / 组局"
:width="180"
>
<template #cell="{ record }">
<div style="font-weight: 600; color: var(--color-text-1)">
{{ record.activityTitle || '未知组局' }}
</div>
<div style="font-size: 12px; color: var(--color-text-3)">
{{ record.gameCategory || '同城组局' }}
</div>
</template>
</a-table-column>
<a-table-column title="参与者用户" :width="160">
<template #cell="{ record }">
<div style="font-weight: 600">用户 ID: {{ record.userId }}</div>
<div style="font-size: 12px; color: var(--color-text-3)">同城组局参与者</div>
<div style="font-size: 12px; color: var(--color-text-3)">
{{ record.nickname || '同城组局参与者' }}
</div>
</template>
</a-table-column>
<a-table-column title="支付报名费" :width="120">
<template #cell="{ record }">
<span style="color: #ff5722; font-weight: 600">
@@ -81,7 +116,8 @@
</span>
</template>
</a-table-column>
<a-table-column title="4位纯数字核销码" :width="200">
<a-table-column title="4位纯数字核销码" :width="180">
<template #cell="{ record }">
<div class="verify-badge">
<span class="code-number">{{ record.verifyCode }}</span>
@@ -89,7 +125,8 @@
</div>
</template>
</a-table-column>
<a-table-column title="核销到场状态" :width="150">
<a-table-column title="核销到场状态" :width="140">
<template #cell="{ record }">
<a-badge
:status="record.checkinStatus ? 'success' : 'processing'"
@@ -97,15 +134,19 @@
/>
</template>
</a-table-column>
<a-table-column title="核销时间 / 操作人" :width="220">
<a-table-column title="核销时间 / 操作人" :width="200">
<template #cell="{ record }">
<div v-if="record.checkinStatus">
<div>{{ formatDate(record.verifiedAt) }}</div>
<div style="font-size: 12px; color: var(--color-text-3)">核销人ID: {{ record.verifiedBy || 1001 }}</div>
<div style="font-size: 12px; color: var(--color-text-3)">
核销人ID: {{ record.verifiedBy || 1001 }}
</div>
</div>
<div v-else style="color: var(--color-text-4)">暂未签到</div>
</template>
</a-table-column>
<a-table-column title="操作" :width="130" fixed="right">
<template #cell="{ record }">
<a-button
@@ -113,7 +154,7 @@
type="outline"
status="success"
size="small"
@click="verifyDirect(record.verifyCode)"
@click="verifyDirect(record.verifyCode, record.activityId)"
>
直接核销
</a-button>
@@ -132,6 +173,17 @@
@cancel="joinModalVisible = false"
>
<a-form :model="joinForm" layout="vertical">
<a-form-item v-if="selectedActivityId === 'all'" label="选择报名的组局活动" required>
<a-select v-model="joinForm.activityId" placeholder="请选择报名活动">
<a-option
v-for="act in activityOptions"
:key="act.id"
:value="act.id"
>
{{ act.title }} ({{ act.gameCategory }})
</a-option>
</a-select>
</a-form-item>
<a-form-item label="模拟报名用户ID" required>
<a-input-number v-model="joinForm.userId" :min="1000" :max="99999" />
</a-form-item>
@@ -153,25 +205,43 @@
const loading = ref(false);
const verifying = ref(false);
const activityOptions = ref<any[]>([]);
const selectedActivityId = ref<string>('');
const selectedActivityId = ref<string>('all');
const participantsList = ref<any[]>([]);
const inputVerifyCode = ref('');
const filterStatus = ref<'ALL' | 'UNCHECKED' | 'CHECKED'>('ALL');
// 模拟报名
const joinModalVisible = ref(false);
const joinForm = ref({
activityId: '',
userId: 2008,
payAmount: 50,
});
const currentActivity = computed(() => {
return activityOptions.value.find((act) => act.id === selectedActivityId.value);
const currentActivityText = computed(() => {
if (selectedActivityId.value === 'all') return '全部活动';
const found = activityOptions.value.find((act) => act.id === selectedActivityId.value);
return found ? `${found.title} (${found.gameCategory})` : '未选择';
});
const verifiedCount = computed(() => {
return participantsList.value.filter((p) => p.checkinStatus).length;
});
const uncheckedCount = computed(() => {
return participantsList.value.filter((p) => !p.checkinStatus).length;
});
const filteredParticipants = computed(() => {
if (filterStatus.value === 'UNCHECKED') {
return participantsList.value.filter((p) => !p.checkinStatus);
}
if (filterStatus.value === 'CHECKED') {
return participantsList.value.filter((p) => p.checkinStatus);
}
return participantsList.value;
});
const fetchActivities = async () => {
try {
const res: any = await axios.get('/api/activities');
@@ -181,7 +251,8 @@
if (queryId && activityOptions.value.some((a) => a.id === queryId)) {
selectedActivityId.value = queryId;
} else {
selectedActivityId.value = activityOptions.value[0].id;
// 默认选中"全部",可直接查看所有活动的核销状态
selectedActivityId.value = 'all';
}
loadParticipants();
}
@@ -205,18 +276,27 @@
}
};
const handleManualVerify = async () => {
if (!inputVerifyCode.value || inputVerifyCode.value.length !== 4) {
const handleManualVerify = async (codeToVerify?: string, targetActId?: string) => {
const code = codeToVerify || inputVerifyCode.value;
if (!code || code.length !== 4) {
Message.warning('请输入4位纯数字核销码');
return;
}
verifying.value = true;
try {
await axios.post(`/api/activities/${selectedActivityId.value}/verify`, {
operatorId: 1001,
verifyCode: inputVerifyCode.value,
});
Message.success(`核销成功!4位核销码【${inputVerifyCode.value}】已通过到场核验`);
const actId = targetActId || selectedActivityId.value;
if (!actId || actId === 'all') {
await axios.post('/api/verify/universal', {
operatorId: 1001,
verifyCode: code,
});
} else {
await axios.post(`/api/activities/${actId}/verify`, {
operatorId: 1001,
verifyCode: code,
});
}
Message.success(`核销成功!4位核销码【${code}】已通过到场核验`);
inputVerifyCode.value = '';
loadParticipants();
} catch (err: any) {
@@ -226,24 +306,34 @@
}
};
const verifyDirect = (code: string) => {
const verifyDirect = (code: string, actId?: string) => {
inputVerifyCode.value = code;
handleManualVerify();
handleManualVerify(code, actId || selectedActivityId.value);
};
const openJoinModal = () => {
const defaultActId = selectedActivityId.value === 'all'
? (activityOptions.value[0]?.id || '')
: selectedActivityId.value;
const targetAct = activityOptions.value.find((a) => a.id === defaultActId);
joinForm.value = {
activityId: defaultActId,
userId: Math.floor(2000 + Math.random() * 7000),
payAmount: (currentActivity.value?.perPersonFeeCents || 5000) / 100,
payAmount: (targetAct?.perPersonFeeCents || 5000) / 100,
};
joinModalVisible.value = true;
};
const handleJoinSubmit = async () => {
try {
await axios.post(`/api/activities/${selectedActivityId.value}/join`, {
const actId = joinForm.value.activityId || selectedActivityId.value;
if (!actId || actId === 'all') {
Message.warning('请选择需要报名的活动');
return;
}
await axios.post(`/api/activities/${actId}/join`, {
userId: joinForm.value.userId,
payAmountCents: joinForm.value.payAmount * 100,
payAmountCents: Math.round(joinForm.value.payAmount * 100),
});
Message.success('用户报名成功,已分配独占4位纯数字核销码');
joinModalVisible.value = false;

查看文件

@@ -10,7 +10,7 @@
</a-tag>
</div>
<div class="banner-desc">
陪玩与同城陪伴运营平台 · 在线接单大神 86 位 · 今日支付订单 142 笔 · 4位纯数字核销防重履约中
同城陪玩运营管理平台 · 在线接单大神 86 位 · 今日支付订单 142 笔 · 4位纯数字核销防重履约中
</div>
</div>

查看文件

@@ -1,12 +1,262 @@
<template>
<div class="banner">
<!-- 背景流光氛围光斑 -->
<div class="ambient-glow glow-1"></div>
<div class="ambient-glow glow-2"></div>
<div class="ambient-glow glow-3"></div>
<div class="banner-inner">
<a-carousel class="carousel" animation-name="fade">
<a-carousel-item v-for="item in carouselItem" :key="item.slogan">
<div :key="item.slogan" class="carousel-item">
<div class="carousel-title">{{ item.slogan }}</div>
<div class="carousel-sub-title">{{ item.subSlogan }}</div>
<img class="carousel-image" :src="item.image" />
<a-carousel
class="carousel"
animation-name="fade"
:auto-play="{ interval: 4500 }"
indicator-type="line"
indicator-position="bottom"
>
<!-- Slide 1: 金牌陪玩与智能调度 -->
<a-carousel-item>
<div class="carousel-item">
<div class="carousel-badge">
<span class="badge-dot pulse"></span>
<span>智能派单调度引擎</span>
</div>
<div class="carousel-title">{{ $t('login.banner.slogan1') }}</div>
<div class="carousel-sub-title">{{ $t('login.banner.subSlogan1') }}</div>
<!-- 陪玩师调度卡片演示 -->
<div class="glass-card dispatcher-card">
<div class="card-header">
<div class="header-left">
<span class="status-dot"></span>
<span class="header-title">实时调度中心</span>
<span class="tag-live">LIVE</span>
</div>
<div class="header-right">860+ 导师待命中</div>
</div>
<div class="expert-list">
<div class="expert-row">
<div class="expert-avatar pink">
<icon-fire />
</div>
<div class="expert-info">
<div class="expert-name-row">
<span class="expert-name">小鹿酱</span>
<span class="expert-game">王者荣耀 · LOL</span>
<span class="status-pill free">空闲中</span>
</div>
<div class="expert-meta">
<span class="rating">★ 5.0 (1,280单)</span>
<span class="tag">甜美声优</span>
<span class="tag">意识带飞</span>
</div>
</div>
</div>
<div class="expert-row">
<div class="expert-avatar indigo">
<icon-trophy />
</div>
<div class="expert-info">
<div class="expert-name-row">
<span class="expert-name">狂澜大神</span>
<span class="expert-game">无畏契约 · 永劫</span>
<span class="status-pill busy">连麦中</span>
</div>
<div class="expert-meta">
<span class="rating">★ 4.9 (2,150单)</span>
<span class="tag">千分绝活</span>
<span class="tag">极速带包</span>
</div>
</div>
</div>
<div class="expert-row">
<div class="expert-avatar amber">
<icon-star-fill />
</div>
<div class="expert-info">
<div class="expert-name-row">
<span class="expert-name">晴天向导</span>
<span class="expert-game">同城桌游 · 密室向导</span>
<span class="status-pill verified">已认证</span>
</div>
<div class="expert-meta">
<span class="rating">★ 5.0 (740次)</span>
<span class="tag">细心体贴</span>
<span class="tag">线下同城</span>
</div>
</div>
</div>
</div>
<div class="card-footer-chips">
<span class="chip">#电竞开黑</span>
<span class="chip">#语音连麦</span>
<span class="chip">#同城向导</span>
<span class="chip">#叫醒陪伴</span>
<span class="chip">#聊天倾听</span>
</div>
</div>
</div>
</a-carousel-item>
<!-- Slide 2: 资金担保与服务履约 -->
<a-carousel-item>
<div class="carousel-item">
<div class="carousel-badge safe-badge">
<span class="badge-dot safe-dot"></span>
<span>全链路交易风控矩阵</span>
</div>
<div class="carousel-title">{{ $t('login.banner.slogan2') }}</div>
<div class="carousel-sub-title">{{ $t('login.banner.subSlogan2') }}</div>
<!-- 安全保障卡片演示 -->
<div class="glass-card security-card">
<div class="security-shield-hero">
<div class="shield-icon-wrap">
<icon-safe class="shield-icon" />
</div>
<div class="shield-text">
<div class="shield-title">银行级资金托管与双因子风控体系</div>
<div class="shield-sub">全流程交易担保 · 杜绝私下飞单 · 履约验收结算</div>
</div>
</div>
<div class="security-grid">
<div class="sec-item">
<div class="sec-item-icon green">
<icon-check-circle-fill />
</div>
<div class="sec-item-body">
<div class="sec-name">实名活体认证</div>
<div class="sec-desc">公安数据库核验,100% 真人认证</div>
</div>
</div>
<div class="sec-item">
<div class="sec-item-icon blue">
<icon-lock />
</div>
<div class="sec-item-body">
<div class="sec-name">资金官方存管</div>
<div class="sec-desc">订单资金双向托管,完单核销结算</div>
</div>
</div>
<div class="sec-item">
<div class="sec-item-icon purple">
<icon-thunderbolt />
</div>
<div class="sec-item-body">
<div class="sec-name">AI 实时风控防飞单</div>
<div class="sec-desc">7×24H 敏感拦截与异常巡检</div>
</div>
</div>
<div class="sec-item">
<div class="sec-item-icon orange">
<icon-customer-service />
</div>
<div class="sec-item-body">
<div class="sec-name">极速纠纷仲裁</div>
<div class="sec-desc">专属运营团队,10分钟极速响应</div>
</div>
</div>
</div>
<div class="security-footer-banner">
<icon-check-circle-fill class="sec-foot-icon" />
<span>平台设立 100 万元专项履约保障金,全程护航双方权益</span>
</div>
</div>
</div>
</a-carousel-item>
<!-- Slide 3: 全链路数据大盘 -->
<a-carousel-item>
<div class="carousel-item">
<div class="carousel-badge analytics-badge">
<span class="badge-dot analytics-dot"></span>
<span>实时运营决策中心</span>
</div>
<div class="carousel-title">{{ $t('login.banner.slogan3') }}</div>
<div class="carousel-sub-title">{{ $t('login.banner.subSlogan3') }}</div>
<!-- 数据大盘卡片演示 -->
<div class="glass-card analytics-card">
<div class="metrics-row">
<div class="metric-col">
<div class="metric-label">今日流水 GMV</div>
<div class="metric-val">¥98,420</div>
<div class="metric-trend up"><icon-arrow-rise /> +28.4%</div>
</div>
<div class="metric-divider"></div>
<div class="metric-col">
<div class="metric-label">履约订单量</div>
<div class="metric-val">1,532 笔</div>
<div class="metric-trend up"><icon-arrow-rise /> +15.2%</div>
</div>
<div class="metric-divider"></div>
<div class="metric-col">
<div class="metric-label">综合满意度</div>
<div class="metric-val">99.8%</div>
<div class="metric-trend star"><icon-star-fill /> 卓越</div>
</div>
</div>
<!-- 迷你趋势图柱形 -->
<div class="mini-chart-box">
<div class="chart-title-bar">
<span>近 7 日订单趋势监控</span>
<span class="chart-badge">持续高增长</span>
</div>
<div class="bars-container">
<div class="bar-col">
<div class="bar-fill" style="height: 48%;"></div>
<span class="bar-day">周一</span>
</div>
<div class="bar-col">
<div class="bar-fill" style="height: 60%;"></div>
<span class="bar-day">周二</span>
</div>
<div class="bar-col">
<div class="bar-fill" style="height: 54%;"></div>
<span class="bar-day">周三</span>
</div>
<div class="bar-col">
<div class="bar-fill" style="height: 72%;"></div>
<span class="bar-day">周四</span>
</div>
<div class="bar-col">
<div class="bar-fill" style="height: 85%;"></div>
<span class="bar-day">周五</span>
</div>
<div class="bar-col highlight">
<div class="bar-fill" style="height: 94%;"></div>
<span class="bar-day">周六</span>
</div>
<div class="bar-col highlight-main">
<div class="bar-fill" style="height: 100%;"></div>
<span class="bar-day">周日</span>
</div>
</div>
</div>
<!-- 品类流水分步条 -->
<div class="category-distribution">
<div class="distrib-bar">
<div class="seg seg-1" style="width: 55%;" title="电竞开黑 55%"></div>
<div class="seg seg-2" style="width: 27%;" title="语音连麦 27%"></div>
<div class="seg seg-3" style="width: 18%;" title="同城向导 18%"></div>
</div>
<div class="distrib-labels">
<span class="legend"><i class="dot dot-1"></i>电竞开黑 55%</span>
<span class="legend"><i class="dot dot-2"></i>语音连麦 27%</span>
<span class="legend"><i class="dot dot-3"></i>同城向导 18%</span>
</div>
</div>
</div>
</div>
</a-carousel-item>
</a-carousel>
@@ -15,70 +265,614 @@
</template>
<script lang="ts" setup>
import { computed } from 'vue';
import { useI18n } from 'vue-i18n';
import bannerImage from '@/assets/images/login-banner.png';
const { t } = useI18n();
const carouselItem = computed(() => [
{
slogan: t('login.banner.slogan1'),
subSlogan: t('login.banner.subSlogan1'),
image: bannerImage,
},
{
slogan: t('login.banner.slogan2'),
subSlogan: t('login.banner.subSlogan2'),
image: bannerImage,
},
{
slogan: t('login.banner.slogan3'),
subSlogan: t('login.banner.subSlogan3'),
image: bannerImage,
},
]);
// 保持脚本精炼,使用统一国际化多语言与 Arco 全局注册图标
</script>
<style lang="less" scoped>
.banner {
position: relative;
display: flex;
align-items: center;
justify-content: center;
height: 100%;
overflow: hidden;
background: linear-gradient(150deg, #070a14 0%, #0e1224 45%, #171131 100%);
&-inner {
position: relative;
z-index: 2;
flex: 1;
height: 100%;
padding-top: 60px;
}
}
// 氛围光斑
.ambient-glow {
position: absolute;
border-radius: 50%;
pointer-events: none;
z-index: 1;
&.glow-1 {
width: 380px;
height: 380px;
top: -60px;
left: -60px;
background: radial-gradient(circle, rgba(139, 92, 246, 0.22) 0%, transparent 70%);
filter: blur(50px);
}
&.glow-2 {
width: 440px;
height: 440px;
bottom: -100px;
right: -80px;
background: radial-gradient(circle, rgba(236, 72, 153, 0.18) 0%, transparent 70%);
filter: blur(60px);
}
&.glow-3 {
width: 260px;
height: 260px;
top: 40%;
left: 30%;
background: radial-gradient(circle, rgba(59, 130, 246, 0.15) 0%, transparent 70%);
filter: blur(55px);
}
}
.carousel {
height: 100%;
:deep(.arco-carousel-indicator-wrapper-bottom) {
bottom: 28px;
}
:deep(.arco-carousel-indicator-item) {
width: 16px;
height: 4px;
border-radius: 2px;
background: rgba(255, 255, 255, 0.25);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
&.arco-carousel-indicator-item-active {
width: 32px;
background: linear-gradient(90deg, #ec4899 0%, #8b5cf6 100%);
}
}
&-item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
padding: 0 40px;
box-sizing: border-box;
user-select: none;
}
&-badge {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 4px 12px;
background: rgba(139, 92, 246, 0.18);
border: 1px solid rgba(139, 92, 246, 0.35);
border-radius: 20px;
font-size: 12px;
font-weight: 500;
color: #c4b5fd;
margin-bottom: 14px;
&.safe-badge {
background: rgba(16, 185, 129, 0.16);
border-color: rgba(16, 185, 129, 0.35);
color: #6ee7b7;
}
&.analytics-badge {
background: rgba(236, 72, 153, 0.16);
border-color: rgba(236, 72, 153, 0.35);
color: #f9a8d4;
}
.badge-dot {
width: 6px;
height: 6px;
border-radius: 50%;
background-color: #a78bfa;
&.safe-dot {
background-color: #34d399;
}
&.analytics-dot {
background-color: #f472b6;
}
&.pulse {
box-shadow: 0 0 0 0 rgba(167, 139, 250, 0.7);
animation: pulse-ring 2s infinite;
}
}
}
&-title {
color: var(--color-fill-1);
font-weight: 500;
font-size: 20px;
line-height: 28px;
color: #ffffff;
font-weight: 700;
font-size: 23px;
line-height: 32px;
text-align: center;
letter-spacing: 0.5px;
}
&-sub-title {
margin-top: 8px;
color: var(--color-text-3);
font-size: 14px;
line-height: 22px;
color: rgba(226, 232, 240, 0.75);
font-size: 13px;
line-height: 20px;
text-align: center;
max-width: 440px;
}
}
// 玻璃态展示卡片基础
.glass-card {
width: 410px;
margin-top: 26px;
padding: 20px;
background: rgba(255, 255, 255, 0.045);
border: 1px solid rgba(255, 255, 255, 0.12);
border-radius: 18px;
backdrop-filter: blur(16px);
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.38), inset 0 1px 0 rgba(255, 255, 255, 0.1);
box-sizing: border-box;
}
// 卡片 1: 调度池卡片
.dispatcher-card {
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
padding-bottom: 12px;
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
margin-bottom: 14px;
.header-left {
display: flex;
align-items: center;
gap: 6px;
.status-dot {
width: 7px;
height: 7px;
border-radius: 50%;
background-color: #10b981;
box-shadow: 0 0 8px #10b981;
}
.header-title {
font-size: 13px;
font-weight: 600;
color: #f1f5f9;
}
.tag-live {
font-size: 10px;
font-weight: 700;
background: rgba(16, 185, 129, 0.2);
color: #34d399;
padding: 1px 5px;
border-radius: 4px;
}
}
.header-right {
font-size: 12px;
color: #94a3b8;
}
}
&-image {
width: 320px;
margin-top: 30px;
.expert-list {
display: flex;
flex-direction: column;
gap: 10px;
}
.expert-row {
display: flex;
align-items: center;
gap: 12px;
padding: 8px 10px;
background: rgba(255, 255, 255, 0.035);
border: 1px solid rgba(255, 255, 255, 0.06);
border-radius: 12px;
transition: background 0.2s ease;
&:hover {
background: rgba(255, 255, 255, 0.07);
}
.expert-avatar {
display: flex;
align-items: center;
justify-content: center;
width: 36px;
height: 36px;
border-radius: 10px;
font-size: 18px;
color: #ffffff;
flex-shrink: 0;
&.pink {
background: linear-gradient(135deg, #f43f5e 0%, #ec4899 100%);
box-shadow: 0 4px 10px rgba(236, 72, 153, 0.35);
}
&.indigo {
background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
box-shadow: 0 4px 10px rgba(99, 102, 241, 0.35);
}
&.amber {
background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
box-shadow: 0 4px 10px rgba(245, 158, 11, 0.35);
}
}
.expert-info {
flex: 1;
min-width: 0;
.expert-name-row {
display: flex;
align-items: center;
gap: 6px;
.expert-name {
font-size: 13px;
font-weight: 600;
color: #ffffff;
}
.expert-game {
font-size: 11px;
color: #94a3b8;
}
.status-pill {
margin-left: auto;
font-size: 10px;
padding: 1px 6px;
border-radius: 4px;
&.free {
background: rgba(16, 185, 129, 0.18);
color: #34d399;
}
&.busy {
background: rgba(245, 158, 11, 0.18);
color: #fbbf24;
}
&.verified {
background: rgba(99, 102, 241, 0.18);
color: #818cf8;
}
}
}
.expert-meta {
display: flex;
align-items: center;
gap: 6px;
margin-top: 3px;
.rating {
font-size: 11px;
color: #fbbf24;
font-weight: 500;
}
.tag {
font-size: 10px;
color: rgba(226, 232, 240, 0.7);
background: rgba(255, 255, 255, 0.06);
padding: 0 5px;
border-radius: 3px;
}
}
}
}
.card-footer-chips {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-top: 14px;
padding-top: 10px;
border-top: 1px solid rgba(255, 255, 255, 0.08);
.chip {
font-size: 11px;
color: rgba(203, 213, 225, 0.85);
background: rgba(255, 255, 255, 0.05);
padding: 3px 8px;
border-radius: 6px;
}
}
}
// 卡片 2: 安全风控卡片
.security-card {
.security-shield-hero {
display: flex;
align-items: center;
gap: 12px;
padding: 12px;
background: linear-gradient(135deg, rgba(99, 102, 241, 0.15) 0%, rgba(236, 72, 153, 0.12) 100%);
border: 1px solid rgba(139, 92, 246, 0.25);
border-radius: 12px;
margin-bottom: 14px;
.shield-icon-wrap {
display: flex;
align-items: center;
justify-content: center;
width: 42px;
height: 42px;
border-radius: 10px;
background: linear-gradient(135deg, #10b981 0%, #059669 100%);
box-shadow: 0 4px 12px rgba(16, 185, 129, 0.4);
font-size: 22px;
color: #fff;
flex-shrink: 0;
}
.shield-text {
.shield-title {
font-size: 13px;
font-weight: 600;
color: #ffffff;
}
.shield-sub {
font-size: 11px;
color: #94a3b8;
margin-top: 2px;
}
}
}
.security-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
}
.sec-item {
display: flex;
align-items: flex-start;
gap: 8px;
padding: 10px;
background: rgba(255, 255, 255, 0.035);
border: 1px solid rgba(255, 255, 255, 0.06);
border-radius: 10px;
.sec-item-icon {
font-size: 16px;
margin-top: 1px;
flex-shrink: 0;
&.green { color: #34d399; }
&.blue { color: #60a5fa; }
&.purple { color: #c084fc; }
&.orange { color: #fb923c; }
}
.sec-name {
font-size: 12px;
font-weight: 600;
color: #f1f5f9;
}
.sec-desc {
font-size: 10px;
color: #94a3b8;
margin-top: 2px;
line-height: 14px;
}
}
.security-footer-banner {
display: flex;
align-items: center;
gap: 6px;
margin-top: 14px;
padding-top: 10px;
border-top: 1px solid rgba(255, 255, 255, 0.08);
font-size: 11px;
color: #6ee7b7;
.sec-foot-icon {
font-size: 14px;
}
}
}
// 卡片 3: 数据运营大盘卡片
.analytics-card {
.metrics-row {
display: flex;
justify-content: space-between;
align-items: center;
padding-bottom: 14px;
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
.metric-col {
flex: 1;
text-align: center;
.metric-label {
font-size: 11px;
color: #94a3b8;
}
.metric-val {
font-size: 17px;
font-weight: 700;
color: #ffffff;
margin: 3px 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
.metric-trend {
font-size: 11px;
font-weight: 600;
&.up {
color: #34d399;
}
&.star {
color: #fbbf24;
}
}
}
.metric-divider {
width: 1px;
height: 28px;
background: rgba(255, 255, 255, 0.08);
}
}
.mini-chart-box {
margin-top: 14px;
.chart-title-bar {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 12px;
color: #cbd5e1;
margin-bottom: 12px;
.chart-badge {
font-size: 10px;
color: #f472b6;
background: rgba(244, 114, 182, 0.15);
padding: 1px 6px;
border-radius: 4px;
}
}
.bars-container {
display: flex;
align-items: flex-end;
justify-content: space-between;
height: 80px;
padding: 0 4px 6px;
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
.bar-col {
display: flex;
flex-direction: column;
align-items: center;
width: 28px;
height: 100%;
justify-content: flex-end;
.bar-fill {
width: 14px;
border-radius: 4px 4px 1px 1px;
background: linear-gradient(180deg, #6366f1 0%, rgba(99, 102, 241, 0.25) 100%);
transition: height 0.4s ease;
}
.bar-day {
margin-top: 6px;
font-size: 10px;
color: #94a3b8;
}
&.highlight {
.bar-fill {
background: linear-gradient(180deg, #a855f7 0%, rgba(168, 85, 247, 0.3) 100%);
}
.bar-day {
color: #d8b4fe;
}
}
&.highlight-main {
.bar-fill {
background: linear-gradient(180deg, #ec4899 0%, rgba(236, 72, 153, 0.35) 100%);
box-shadow: 0 0 10px rgba(236, 72, 153, 0.5);
}
.bar-day {
color: #f472b6;
font-weight: 600;
}
}
}
}
}
.category-distribution {
margin-top: 14px;
.distrib-bar {
display: flex;
height: 6px;
border-radius: 3px;
overflow: hidden;
background: rgba(255, 255, 255, 0.08);
.seg-1 { background: #6366f1; }
.seg-2 { background: #ec4899; }
.seg-3 { background: #f59e0b; }
}
.distrib-labels {
display: flex;
justify-content: space-between;
margin-top: 8px;
.legend {
display: flex;
align-items: center;
gap: 4px;
font-size: 11px;
color: #94a3b8;
.dot {
width: 6px;
height: 6px;
border-radius: 50%;
&.dot-1 { background: #6366f1; }
&.dot-2 { background: #ec4899; }
&.dot-3 { background: #f59e0b; }
}
}
}
}
}
@keyframes pulse-ring {
0% {
box-shadow: 0 0 0 0 rgba(167, 139, 250, 0.7);
}
70% {
box-shadow: 0 0 0 6px rgba(167, 139, 250, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(167, 139, 250, 0);
}
}
</style>

查看文件

@@ -1,8 +1,22 @@
<template>
<div class="login-form-wrapper">
<div class="login-form-title">{{ $t('login.form.title') }}</div>
<div class="login-form-sub-title">{{ $t('login.form.title') }}</div>
<div class="login-form-error-msg">{{ errorMessage }}</div>
<div class="login-card">
<!-- 顶部状态标识 -->
<div class="form-tag-badge">
<span class="badge-dot"></span>
<span>运营总控端 · 管理员验证</span>
</div>
<h2 class="login-form-title">{{ $t('login.form.title') }}</h2>
<p class="login-form-sub-title">{{ $t('login.form.subTitle') }}</p>
<!-- 错误信息反馈 -->
<transition name="fade">
<div v-if="errorMessage" class="login-form-error-msg">
<icon-close-circle-fill class="error-icon" />
<span>{{ errorMessage }}</span>
</div>
</transition>
<a-form
ref="loginForm"
:model="userInfo"
@@ -18,13 +32,17 @@
>
<a-input
v-model="userInfo.username"
size="large"
class="custom-input"
:placeholder="$t('login.form.userName.placeholder')"
allow-clear
>
<template #prefix>
<icon-user />
<icon-user class="field-icon" />
</template>
</a-input>
</a-form-item>
<a-form-item
field="password"
:rules="[{ required: true, message: $t('login.form.password.errMsg') }]"
@@ -33,32 +51,56 @@
>
<a-input-password
v-model="userInfo.password"
size="large"
class="custom-input"
:placeholder="$t('login.form.password.placeholder')"
allow-clear
>
<template #prefix>
<icon-lock />
<icon-lock class="field-icon" />
</template>
</a-input-password>
</a-form-item>
<a-space :size="16" direction="vertical">
<div class="login-form-password-actions">
<a-checkbox
checked="rememberPassword"
:model-value="loginConfig.rememberPassword"
@change="setRememberPassword as any"
>
{{ $t('login.form.rememberPassword') }}
</a-checkbox>
<a-link>{{ $t('login.form.forgetPassword') }}</a-link>
</div>
<a-button type="primary" html-type="submit" long :loading="loading">
{{ $t('login.form.login') }}
<div class="login-form-actions-row">
<a-checkbox
:model-value="loginConfig.rememberPassword"
class="custom-checkbox"
@change="setRememberPassword as any"
>
{{ $t('login.form.rememberPassword') }}
</a-checkbox>
<a-link class="forget-link" @click="handleForgetPassword">
{{ $t('login.form.forgetPassword') }}
</a-link>
</div>
<div class="submit-wrap">
<a-button
type="primary"
html-type="submit"
size="large"
long
:loading="loading"
class="submit-btn"
>
<span class="btn-text">{{ $t('login.form.login') }}</span>
<icon-arrow-right class="btn-arrow" />
</a-button>
<a-button type="text" long class="login-form-register-btn">
</div>
<div class="form-footer-tips">
<span class="tips-label">暂无运营管理账号?</span>
<a-link class="apply-link" @click="handleApplyAccount">
{{ $t('login.form.register') }}
</a-button>
</a-space>
</a-link>
</div>
<!-- 底部安全背书标签 -->
<div class="security-endorse">
<icon-safe class="sec-icon" />
<span>平台已启用 SSL 传输加密与多因子动态审计</span>
</div>
</a-form>
</div>
</template>
@@ -100,6 +142,7 @@
if (loading.value) return;
if (!errors) {
setLoading(true);
errorMessage.value = '';
try {
await userStore.login(values as LoginData);
const { redirect, ...othersQuery } = router.currentRoute.value.query;
@@ -112,54 +155,248 @@
Message.success(t('login.form.login.success'));
const { rememberPassword } = loginConfig.value;
const { username, password } = values;
// 实际生产环境需要进行加密存储。
// The actual production environment requires encrypted storage.
loginConfig.value.username = rememberPassword ? username : '';
loginConfig.value.password = rememberPassword ? password : '';
} catch (err) {
errorMessage.value = (err as Error).message;
errorMessage.value = (err as Error).message || t('login.form.login.errMsg');
} finally {
setLoading(false);
}
}
};
const setRememberPassword = (value: boolean) => {
loginConfig.value.rememberPassword = value;
};
const handleForgetPassword = () => {
Message.info('管理员账号密码找回或重置,请联系系统超级管理员进行安全验证。');
};
const handleApplyAccount = () => {
Message.info('本系统为运营管理总控平台,暂未开放自主注册,需由超级管理员统一开通权限。');
};
</script>
<style lang="less" scoped>
.login-form {
&-wrapper {
width: 320px;
}
.login-card {
width: 420px;
max-width: 90vw;
padding: 40px 36px 32px;
background: #ffffff;
border-radius: 20px;
box-shadow: 0 20px 45px -12px rgba(15, 23, 42, 0.12),
0 0 0 1px rgba(226, 232, 240, 0.85);
box-sizing: border-box;
transition: transform 0.3s ease, box-shadow 0.3s ease;
&-title {
color: var(--color-text-1);
font-weight: 500;
font-size: 24px;
line-height: 32px;
}
&-sub-title {
color: var(--color-text-3);
font-size: 16px;
line-height: 24px;
}
&-error-msg {
height: 32px;
color: rgb(var(--red-6));
line-height: 32px;
}
&-password-actions {
display: flex;
justify-content: space-between;
}
&-register-btn {
color: var(--color-text-3) !important;
&:hover {
box-shadow: 0 25px 55px -12px rgba(15, 23, 42, 0.15),
0 0 0 1px rgba(203, 213, 225, 0.9);
}
}
.form-tag-badge {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 3px 10px;
background: rgba(99, 102, 241, 0.08);
border: 1px solid rgba(99, 102, 241, 0.2);
border-radius: 20px;
font-size: 12px;
font-weight: 500;
color: #6366f1;
margin-bottom: 12px;
.badge-dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: #6366f1;
}
}
.login-form-title {
margin: 0;
color: #0f172a;
font-weight: 700;
font-size: 24px;
line-height: 32px;
letter-spacing: -0.3px;
}
.login-form-sub-title {
margin: 6px 0 16px;
color: #64748b;
font-size: 13px;
line-height: 20px;
}
.login-form-error-msg {
display: flex;
align-items: center;
gap: 6px;
padding: 8px 12px;
margin-bottom: 16px;
border-radius: 8px;
background: #fef2f2;
border: 1px solid #fecaca;
color: #ef4444;
font-size: 12px;
line-height: 18px;
.error-icon {
font-size: 14px;
flex-shrink: 0;
}
}
.custom-input {
height: 44px;
border-radius: 10px;
background-color: #f8fafc;
border: 1px solid #e2e8f0;
font-size: 14px;
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
&:hover {
background-color: #ffffff;
border-color: #cbd5e1;
}
&:focus-within {
background-color: #ffffff;
border-color: #8b5cf6;
box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.15);
}
.field-icon {
color: #94a3b8;
font-size: 16px;
margin-right: 4px;
transition: color 0.2s;
}
&:focus-within .field-icon {
color: #8b5cf6;
}
}
.login-form-actions-row {
display: flex;
justify-content: space-between;
align-items: center;
margin: 4px 0 20px;
.custom-checkbox {
color: #64748b;
font-size: 13px;
user-select: none;
}
.forget-link {
color: #64748b;
font-size: 13px;
cursor: pointer;
transition: color 0.2s;
&:hover {
color: #8b5cf6;
}
}
}
.submit-wrap {
margin-bottom: 16px;
.submit-btn {
height: 46px;
border-radius: 10px;
font-size: 15px;
font-weight: 600;
letter-spacing: 0.5px;
background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 50%, #ec4899 100%) !important;
border: none !important;
box-shadow: 0 8px 20px -4px rgba(139, 92, 246, 0.45);
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
display: flex;
align-items: center;
justify-content: center;
gap: 6px;
&:hover {
opacity: 0.95;
transform: translateY(-1px);
box-shadow: 0 12px 24px -4px rgba(139, 92, 246, 0.55);
}
&:active {
transform: translateY(0);
box-shadow: 0 4px 12px -2px rgba(139, 92, 246, 0.4);
}
.btn-arrow {
font-size: 14px;
transition: transform 0.2s;
}
&:hover .btn-arrow {
transform: translateX(3px);
}
}
}
.form-footer-tips {
display: flex;
align-items: center;
justify-content: center;
gap: 6px;
margin-top: 14px;
font-size: 13px;
.tips-label {
color: #94a3b8;
}
.apply-link {
color: #8b5cf6;
font-weight: 500;
cursor: pointer;
transition: color 0.2s;
&:hover {
color: #7c3aed;
text-decoration: underline;
}
}
}
.security-endorse {
display: flex;
align-items: center;
justify-content: center;
gap: 5px;
margin-top: 24px;
padding-top: 16px;
border-top: 1px solid #f1f5f9;
font-size: 11px;
color: #94a3b8;
user-select: none;
.sec-icon {
font-size: 13px;
color: #10b981;
}
}
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.2s ease, transform 0.2s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
transform: translateY(-4px);
}
</style>

查看文件

@@ -1,18 +1,34 @@
<template>
<div class="container">
<!-- 顶栏品牌标识 -->
<div class="logo">
<img
alt="logo"
:src="logoImg"
style="width: 32px; height: 32px; border-radius: 8px; object-fit: cover;"
class="logo-img"
/>
<div class="logo-text">同城生活陪伴 · 运营管理平台</div>
<div class="logo-title-group">
<span class="logo-main-title">同城陪玩</span>
<span class="logo-sub-title">运营管理总控平台</span>
</div>
<span class="logo-badge">PRO</span>
</div>
<LoginBanner />
<!-- 左侧陪玩主题视觉展示栏 -->
<div class="banner">
<LoginBanner />
</div>
<!-- 右侧登录表单与背景质感区 -->
<div class="content">
<!-- 背景光晕装饰,消除空旷感 -->
<div class="bg-orb orb-top"></div>
<div class="bg-orb orb-bottom"></div>
<div class="content-inner">
<LoginForm />
</div>
<div class="footer">
<Footer />
</div>
@@ -31,10 +47,15 @@
.container {
display: flex;
height: 100vh;
width: 100vw;
overflow: hidden;
background-color: #070a14;
.banner {
width: 550px;
background: linear-gradient(163.85deg, #1d2129 0%, #00308f 100%);
width: 560px;
height: 100%;
flex-shrink: 0;
position: relative;
}
.content {
@@ -44,6 +65,15 @@
align-items: center;
justify-content: center;
padding-bottom: 40px;
background-color: #f8fafc;
background-image: radial-gradient(#cbd5e1 1.2px, transparent 1.2px);
background-size: 26px 26px;
overflow: hidden;
}
.content-inner {
position: relative;
z-index: 2;
}
.footer {
@@ -51,32 +81,109 @@
right: 0;
bottom: 0;
width: 100%;
z-index: 2;
}
}
// 顶栏 Logo 与品牌
.logo {
position: fixed;
top: 24px;
left: 22px;
z-index: 1;
left: 28px;
z-index: 20;
display: inline-flex;
align-items: center;
gap: 10px;
user-select: none;
&-text {
margin-right: 4px;
margin-left: 4px;
color: var(--color-fill-1);
font-size: 20px;
&-img {
width: 36px;
height: 36px;
border-radius: 10px;
object-fit: cover;
box-shadow: 0 4px 14px rgba(0, 0, 0, 0.45);
border: 1px solid rgba(255, 255, 255, 0.18);
}
&-title-group {
display: flex;
flex-direction: column;
}
&-main-title {
color: #ffffff;
font-size: 16px;
font-weight: 700;
line-height: 20px;
letter-spacing: 0.5px;
}
&-sub-title {
color: rgba(226, 232, 240, 0.7);
font-size: 11px;
line-height: 14px;
}
&-badge {
font-size: 10px;
font-weight: 700;
padding: 2px 7px;
border-radius: 12px;
background: linear-gradient(135deg, #ec4899 0%, #8b5cf6 100%);
color: #ffffff;
box-shadow: 0 2px 6px rgba(236, 72, 153, 0.35);
letter-spacing: 0.5px;
}
}
</style>
<style lang="less" scoped>
// responsive
@media (max-width: @screen-lg) {
// 背景装饰氛围
.bg-orb {
position: absolute;
border-radius: 50%;
pointer-events: none;
z-index: 1;
&.orb-top {
width: 520px;
height: 520px;
top: -140px;
right: -100px;
background: radial-gradient(circle, rgba(236, 72, 153, 0.08) 0%, transparent 70%);
filter: blur(70px);
}
&.orb-bottom {
width: 480px;
height: 480px;
bottom: -120px;
left: 10%;
background: radial-gradient(circle, rgba(99, 102, 241, 0.08) 0%, transparent 70%);
filter: blur(65px);
}
}
// 响应式适配
@media (max-width: 1080px) {
.container {
.banner {
width: 25%;
width: 440px;
}
}
}
@media (max-width: 768px) {
.container {
.banner {
display: none;
}
.logo {
&-main-title {
color: #0f172a;
}
&-sub-title {
color: #64748b;
}
}
}
}

查看文件

@@ -1,21 +1,20 @@
export default {
'login.form.title': 'Login to Arco Design Pro',
'login.form.title': 'Companion Admin Console',
'login.form.subTitle': 'One-stop management platform for esports & lifestyle companionship',
'login.form.userName.errMsg': 'Username cannot be empty',
'login.form.password.errMsg': 'Password cannot be empty',
'login.form.login.errMsg': 'Login error, refresh and try again',
'login.form.login.success': 'welcome to use',
'login.form.userName.placeholder': 'Username',
'login.form.login.errMsg': 'Login error, please refresh and try again',
'login.form.login.success': 'Welcome to Companion Admin Console',
'login.form.userName.placeholder': 'Username or phone number',
'login.form.password.placeholder': 'Password',
'login.form.rememberPassword': 'Remember password',
'login.form.forgetPassword': 'Forgot password',
'login.form.login': 'login',
'login.form.register': 'register account',
'login.banner.slogan1': 'Out-of-the-box high-quality template',
'login.banner.subSlogan1':
'Rich page templates, covering most typical business scenarios',
'login.banner.slogan2': 'Built-in solutions to common problems',
'login.banner.subSlogan2':
'Internationalization, routing configuration, state management everything',
'login.banner.slogan3': 'Access visualization enhancement tool AUX',
'login.banner.subSlogan3': 'Realize flexible block development',
'login.form.rememberPassword': 'Remember account',
'login.form.forgetPassword': 'Forgot password?',
'login.form.login': 'Sign In',
'login.form.register': 'Apply for account',
'login.banner.slogan1': 'Top Companions & Intelligent Dispatching',
'login.banner.subSlogan1': 'Gathering esports experts and lifestyle companions with real-time matching',
'login.banner.slogan2': 'Escrow Guarantee & Service Compliance',
'login.banner.subSlogan2': 'Platform escrow settlement and verified real-name protection for trust and safety',
'login.banner.slogan3': 'Omnichannel Analytics & Business Growth',
'login.banner.subSlogan3': 'Full visibility into GMV, companion ratings, and user retention to empower operations',
};

查看文件

@@ -1,19 +1,20 @@
export default {
'login.form.title': '登录同城陪伴运营控制台',
'login.form.title': '同城陪玩运营控制台',
'login.form.subTitle': '同城电竞陪玩与生活陪伴一站式管理平台',
'login.form.userName.errMsg': '用户名不能为空',
'login.form.password.errMsg': '密码不能为空',
'login.form.login.errMsg': '登录出错,轻刷新重试',
'login.form.login.success': '欢迎使用',
'login.form.userName.placeholder': '请输入管理员用户名',
'login.form.password.placeholder': '请输入管理员密码',
'login.form.rememberPassword': '记住密码',
'login.form.forgetPassword': '忘记密码',
'login.form.login': '登录',
'login.form.register': '注册账号',
'login.banner.slogan1': '开箱即用的高质量模板',
'login.banner.subSlogan1': '丰富的的页面模板,覆盖大多数典型业务场景',
'login.banner.slogan2': '内置了常见问题的解决方案',
'login.banner.subSlogan2': '国际化,路由配置,状态管理应有尽有',
'login.banner.slogan3': '接入可视化增强工具AUX',
'login.banner.subSlogan3': '实现灵活的区块式开发',
'login.form.login.errMsg': '登录出错,请刷新重试',
'login.form.login.success': '欢迎登录同城陪玩运营控制台',
'login.form.userName.placeholder': '请输入管理员账号 / 手机号',
'login.form.password.placeholder': '请输入管理员登录密码',
'login.form.rememberPassword': '记住账号',
'login.form.forgetPassword': '忘记密码?',
'login.form.login': '立即登录系统',
'login.form.register': '申请开通账号',
'login.banner.slogan1': '全城金牌陪玩 · 智能精准调度',
'login.banner.subSlogan1': '汇聚高技术电竞大神与温馨生活陪伴,多维标签实时高效匹配',
'login.banner.slogan2': '资金双向担保 · 履约服务保障',
'login.banner.subSlogan2': '平台资金存管结算、实名双重认证核验,构筑健康可信赖的陪玩生态',
'login.banner.slogan3': '全链路数据大盘 · 驱动业务增长',
'login.banner.subSlogan3': '订单流水分成、陪玩师画像热度与用户留存全景可视,赋能精细化决策',
};

查看文件

@@ -339,7 +339,7 @@
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="弹窗主标题">
<a-input v-model="homePopupForm.valueJson.title" placeholder="如:新人专享同城陪伴好礼" />
<a-input v-model="homePopupForm.valueJson.title" placeholder="如:新人专享同城陪玩好礼" />
</a-form-item>
</a-col>
<a-col :span="12">
@@ -1233,7 +1233,7 @@ const homePopupForm = reactive({
status: 1,
valueJson: {
enabled: true,
title: '新人专享同城陪伴好礼',
title: '新人专享同城陪玩好礼',
subtitle: '注册即送20元无门槛立减券,首单专享特惠',
imageUrl: 'https://images.unsplash.com/photo-1511671782779-c97d3d27a1d4?w=800',
link: '/pages/activity/list',