feat(activity&admin): 优化组局起止时段与性别限制,PC后台核销看板与登录页体验升级
这个提交包含在:
@@ -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',
|
||||
|
||||
在新工单中引用
屏蔽一个用户