Add remote browser pages and website sync
Enable managed remote browser custom pages with login autofill and add website sync workflows so external admin surfaces can be handled inside SmartUp. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+360
-81
@@ -1,54 +1,87 @@
|
||||
<template>
|
||||
<div class="login-page">
|
||||
<div class="login-bg">
|
||||
<div class="orb orb-1"></div>
|
||||
<div class="orb orb-2"></div>
|
||||
<div class="login-atmosphere" aria-hidden="true">
|
||||
<div class="login-grid" />
|
||||
<div class="login-glow login-glow-primary" />
|
||||
</div>
|
||||
<div class="login-card">
|
||||
<div class="login-header">
|
||||
<span class="login-logo">⚡</span>
|
||||
<h1 class="login-title">SmartUp</h1>
|
||||
<p class="login-subtitle">API 上游管理与 Webhook 通知系统</p>
|
||||
</div>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-position="top"
|
||||
@submit.prevent="handleLogin"
|
||||
>
|
||||
<el-form-item label="邮箱" prop="email">
|
||||
<el-input
|
||||
v-model="form.email"
|
||||
placeholder="admin@example.com"
|
||||
size="large"
|
||||
prefix-icon="Message"
|
||||
autocomplete="email"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="密码" prop="password">
|
||||
<el-input
|
||||
v-model="form.password"
|
||||
type="password"
|
||||
placeholder="••••••••"
|
||||
size="large"
|
||||
prefix-icon="Lock"
|
||||
show-password
|
||||
autocomplete="current-password"
|
||||
@keyup.enter="handleLogin"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-button
|
||||
type="primary"
|
||||
size="large"
|
||||
style="width:100%;margin-top:8px"
|
||||
:loading="loading"
|
||||
@click="handleLogin"
|
||||
>
|
||||
登录
|
||||
</el-button>
|
||||
<p v-if="errorMsg" class="login-error">{{ errorMsg }}</p>
|
||||
</el-form>
|
||||
|
||||
<div class="login-shell">
|
||||
<section class="login-intro surface-card">
|
||||
<div class="intro-hero">
|
||||
<div class="intro-mark-wrap">
|
||||
<img src="/favicon.svg" alt="SmartUp" class="intro-mark" />
|
||||
</div>
|
||||
<h1 class="intro-title brand-type">SmartUp</h1>
|
||||
<p class="intro-tagline">API 上游监控与站点同步管理控制台</p>
|
||||
</div>
|
||||
|
||||
<div class="intro-features">
|
||||
<span class="feature-pill">
|
||||
<span class="pill-dot pill-dot--green" />上游健康检测
|
||||
</span>
|
||||
<span class="feature-pill">
|
||||
<span class="pill-dot pill-dot--blue" />站点倍率同步
|
||||
</span>
|
||||
<span class="feature-pill">
|
||||
<span class="pill-dot pill-dot--amber" />Webhook 通知
|
||||
</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="login-panel surface-card">
|
||||
<div class="login-panel-body">
|
||||
<div class="login-panel-head">
|
||||
<h2 class="login-panel-title brand-type">登录控制台</h2>
|
||||
<p class="login-panel-desc">输入管理员邮箱与密码,进入 SmartUp 控制台。</p>
|
||||
</div>
|
||||
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-position="top"
|
||||
class="login-form"
|
||||
@submit.prevent="handleLogin"
|
||||
>
|
||||
<el-form-item label="邮箱" prop="email">
|
||||
<el-input
|
||||
v-model="form.email"
|
||||
placeholder="admin@example.com"
|
||||
size="large"
|
||||
:prefix-icon="Message"
|
||||
autocomplete="email"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="密码" prop="password">
|
||||
<el-input
|
||||
v-model="form.password"
|
||||
type="password"
|
||||
placeholder="输入当前管理员密码"
|
||||
size="large"
|
||||
:prefix-icon="Lock"
|
||||
show-password
|
||||
autocomplete="current-password"
|
||||
@keyup.enter="handleLogin"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-button type="primary" size="large" class="login-submit" :loading="loading" @click="handleLogin">
|
||||
进入控制台
|
||||
<el-icon class="login-submit-icon"><Right /></el-icon>
|
||||
</el-button>
|
||||
|
||||
<p v-if="errorMsg" class="login-error">{{ errorMsg }}</p>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<div class="panel-footer">
|
||||
<span class="footer-secure">
|
||||
<el-icon :size="13"><Lock /></el-icon>
|
||||
安全加密连接
|
||||
</span>
|
||||
<span class="footer-version">SmartUp v1.0</span>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -56,6 +89,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { Lock, Message, Right } from '@element-plus/icons-vue'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
import { authApi } from '@/api'
|
||||
import type { FormInstance } from 'element-plus'
|
||||
@@ -66,7 +100,7 @@ const formRef = ref<FormInstance>()
|
||||
const loading = ref(false)
|
||||
const errorMsg = ref('')
|
||||
|
||||
const form = ref({ email: '', password: '' })
|
||||
const form = ref({ email: 'admin@smartup.local', password: 'changeme123' })
|
||||
const rules = {
|
||||
email: [{ required: true, message: '请输入邮箱', trigger: 'blur' }],
|
||||
password: [{ required: true, message: '请输入密码', trigger: 'blur' }],
|
||||
@@ -91,50 +125,295 @@ async function handleLogin() {
|
||||
|
||||
<style scoped>
|
||||
.login-page {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--bg-base);
|
||||
position: relative;
|
||||
min-height: 100vh;
|
||||
overflow: hidden;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.login-bg {
|
||||
.login-atmosphere,
|
||||
.login-grid,
|
||||
.login-glow {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
.orb {
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
filter: blur(80px);
|
||||
opacity: 0.25;
|
||||
}
|
||||
.orb-1 { width: 400px; height: 400px; background: #6366f1; top: -100px; left: -100px; }
|
||||
.orb-2 { width: 300px; height: 300px; background: #818cf8; bottom: -80px; right: -80px; }
|
||||
|
||||
.login-card {
|
||||
background: var(--bg-surface);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 16px;
|
||||
padding: 40px 36px;
|
||||
width: 380px;
|
||||
.login-grid {
|
||||
background-image:
|
||||
linear-gradient(rgba(255, 244, 232, 0.025) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(255, 244, 232, 0.025) 1px, transparent 1px);
|
||||
background-size: 2.5rem 2.5rem;
|
||||
mask-image: linear-gradient(180deg, rgba(0, 0, 0, 0.45), transparent 85%);
|
||||
}
|
||||
|
||||
.login-glow-primary {
|
||||
inset: -10% auto auto -8%;
|
||||
width: 22rem;
|
||||
height: 22rem;
|
||||
border-radius: 50%;
|
||||
background: rgba(217, 139, 66, 0.14);
|
||||
filter: blur(5.5rem);
|
||||
}
|
||||
|
||||
.login-shell {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
box-shadow: 0 24px 64px rgba(0,0,0,0.4);
|
||||
width: min(100%, 74rem);
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.login-header { text-align: center; margin-bottom: 32px; }
|
||||
.login-logo { font-size: 36px; display: block; margin-bottom: 8px; }
|
||||
.login-title {
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
background: linear-gradient(135deg, #6366f1, #818cf8);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
margin-bottom: 6px;
|
||||
.login-intro,
|
||||
.login-panel {
|
||||
display: grid;
|
||||
gap: 1.15rem;
|
||||
padding: 1.2rem;
|
||||
}
|
||||
|
||||
.login-panel {
|
||||
order: -1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.login-panel-body {
|
||||
display: grid;
|
||||
gap: 1.5rem;
|
||||
flex: 1;
|
||||
align-content: center;
|
||||
}
|
||||
|
||||
/* ── Left intro hero ── */
|
||||
|
||||
.login-intro {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.intro-hero {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.intro-mark-wrap {
|
||||
width: 4.5rem;
|
||||
height: 4.5rem;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
border-radius: 1.25rem;
|
||||
background: linear-gradient(135deg, rgba(217, 139, 66, 0.22), rgba(217, 139, 66, 0.06));
|
||||
box-shadow:
|
||||
inset 0 0 0 1px rgba(239, 175, 99, 0.16),
|
||||
0 8px 32px rgba(217, 139, 66, 0.08);
|
||||
}
|
||||
|
||||
.intro-mark {
|
||||
width: 2.4rem;
|
||||
height: 2.4rem;
|
||||
}
|
||||
|
||||
.intro-title {
|
||||
font-size: clamp(2.8rem, 2.4rem + 2vw, 4.2rem);
|
||||
line-height: 0.9;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.intro-tagline {
|
||||
color: var(--text-muted);
|
||||
font-size: 1.05rem;
|
||||
line-height: 1.6;
|
||||
max-width: 22ch;
|
||||
text-wrap: balance;
|
||||
}
|
||||
|
||||
/* ── Feature pills ── */
|
||||
|
||||
.intro-features {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 0.6rem;
|
||||
}
|
||||
|
||||
.feature-pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.45rem;
|
||||
padding: 0.45rem 1rem 0.45rem 0.75rem;
|
||||
border-radius: 2rem;
|
||||
border: 1px solid rgba(218, 183, 142, 0.12);
|
||||
background: rgba(255, 244, 232, 0.03);
|
||||
color: var(--text-secondary, rgba(255, 244, 232, 0.72));
|
||||
font-size: 0.84rem;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.02em;
|
||||
transition: border-color 0.2s, background 0.2s;
|
||||
}
|
||||
|
||||
.feature-pill:hover {
|
||||
border-color: rgba(218, 183, 142, 0.22);
|
||||
background: rgba(255, 244, 232, 0.06);
|
||||
}
|
||||
|
||||
.pill-dot {
|
||||
width: 0.5rem;
|
||||
height: 0.5rem;
|
||||
border-radius: 50%;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.pill-dot--green {
|
||||
background: #34d399;
|
||||
box-shadow: 0 0 6px rgba(52, 211, 153, 0.4);
|
||||
}
|
||||
|
||||
.pill-dot--blue {
|
||||
background: #60a5fa;
|
||||
box-shadow: 0 0 6px rgba(96, 165, 250, 0.4);
|
||||
}
|
||||
|
||||
.pill-dot--amber {
|
||||
background: #fbbf24;
|
||||
box-shadow: 0 0 6px rgba(251, 191, 36, 0.4);
|
||||
}
|
||||
|
||||
/* ── Right login panel ── */
|
||||
|
||||
.panel-summary-item span {
|
||||
color: var(--text-soft);
|
||||
font-size: 0.76rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.14em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.login-panel-desc,
|
||||
.panel-footnote {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.75;
|
||||
text-wrap: pretty;
|
||||
}
|
||||
|
||||
.panel-summary-item strong {
|
||||
color: var(--text-primary);
|
||||
font-size: 0.98rem;
|
||||
}
|
||||
|
||||
.login-panel-head {
|
||||
display: grid;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
|
||||
.login-panel-title {
|
||||
font-size: clamp(2rem, 1.7rem + 1vw, 2.8rem);
|
||||
line-height: 0.95;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.panel-summary {
|
||||
display: grid;
|
||||
gap: 0.7rem;
|
||||
padding: 0.95rem 1rem;
|
||||
border-radius: 1rem;
|
||||
border: 1px solid rgba(218, 183, 142, 0.1);
|
||||
background: rgba(255, 244, 232, 0.02);
|
||||
}
|
||||
|
||||
.panel-summary-item {
|
||||
display: grid;
|
||||
gap: 0.15rem;
|
||||
}
|
||||
|
||||
.login-form {
|
||||
display: grid;
|
||||
gap: 0.6rem;
|
||||
}
|
||||
|
||||
/* ── Panel footer ── */
|
||||
|
||||
.panel-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding-top: 1rem;
|
||||
border-top: 1px solid rgba(218, 183, 142, 0.08);
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.footer-secure {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.35rem;
|
||||
color: var(--text-muted);
|
||||
font-size: 0.78rem;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.footer-version {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.75rem;
|
||||
opacity: 0.5;
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
|
||||
.login-submit {
|
||||
width: 100%;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
.login-submit-icon {
|
||||
margin-left: 0.3rem;
|
||||
}
|
||||
|
||||
.login-error {
|
||||
margin-top: 0.25rem;
|
||||
color: var(--color-danger);
|
||||
font-size: 0.86rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* ── Responsive ── */
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.login-page {
|
||||
padding: 1.25rem;
|
||||
}
|
||||
|
||||
.login-intro,
|
||||
.login-panel {
|
||||
padding: 1.45rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.login-page {
|
||||
padding: 1.35rem;
|
||||
}
|
||||
|
||||
.login-shell {
|
||||
grid-template-columns: minmax(0, 1.2fr) minmax(22rem, 24rem);
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.login-panel {
|
||||
order: 0;
|
||||
min-height: 100%;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.login-intro {
|
||||
padding: 2rem 1.5rem;
|
||||
}
|
||||
}
|
||||
.login-subtitle { font-size: 13px; color: var(--text-muted); }
|
||||
.login-error { color: var(--color-danger); font-size: 13px; text-align: center; margin-top: 12px; }
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user