feat: improve ChatView, add LoginView and demo reference page
- ChatView: enhanced AI chat interface with improved message rendering - LoginView: new login page component - demo/main_demo.html: reference implementation for Chart.js dashboards Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
497
src/views/LoginView.vue
Normal file
497
src/views/LoginView.vue
Normal file
@@ -0,0 +1,497 @@
|
||||
<template>
|
||||
<div class="login-page">
|
||||
<!-- Left: Enterprise Brand Hero -->
|
||||
<aside class="hero">
|
||||
<div class="hero-grid"></div>
|
||||
<div class="hero-content">
|
||||
<div class="brand">
|
||||
<div class="logo-box">
|
||||
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 2L2 7L12 12L22 7L12 2Z" fill="currentColor"/>
|
||||
<path d="M2 17L12 22L22 17" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M2 12L12 17L22 12" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
</div>
|
||||
<span class="brand-name">X-Financial Ops</span>
|
||||
</div>
|
||||
|
||||
<header class="hero-header">
|
||||
<h1>智能财务报销<br/><span class="text-gradient">全流程运营中台</span></h1>
|
||||
<p>基于 AI Agent 的合规预审与自动化处理引擎,助力企业财务实现数字化治理与运营效率的跨越式提升。</p>
|
||||
</header>
|
||||
|
||||
<div class="value-prop">
|
||||
<div v-for="item in features" :key="item.title" class="prop-item">
|
||||
<div class="prop-icon" :style="{ color: item.color }">
|
||||
<component :is="item.icon" />
|
||||
</div>
|
||||
<div class="prop-text">
|
||||
<strong>{{ item.title }}</strong>
|
||||
<p>{{ item.desc }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hero-footer">
|
||||
<div class="trust-badge">
|
||||
<div class="avatars">
|
||||
<div v-for="i in 3" :key="i" class="avatar-circle"></div>
|
||||
</div>
|
||||
<span>已为 500+ 大中型企业提供智能化财务转型支持</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Preview UI Element: Simplified Dashboard Part -->
|
||||
<div class="hero-preview">
|
||||
<div class="preview-card">
|
||||
<div class="preview-header">
|
||||
<div class="dots"><span></span><span></span><span></span></div>
|
||||
<div class="preview-title">系统合规性看板</div>
|
||||
</div>
|
||||
<div class="preview-body">
|
||||
<div class="preview-stat">
|
||||
<div class="stat-circle">
|
||||
<svg viewBox="0 0 36 36">
|
||||
<path class="circle-bg" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" />
|
||||
<path class="circle" stroke-dasharray="82, 100" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" />
|
||||
</svg>
|
||||
<div class="stat-val">82%</div>
|
||||
</div>
|
||||
<div class="stat-info">
|
||||
<strong>综合自动通过率</strong>
|
||||
<p>较上月提升 12.4%</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="preview-list">
|
||||
<div v-for="i in 3" :key="i" class="list-item">
|
||||
<div class="item-bar" :style="{ width: [85, 62, 45][i-1] + '%' }"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- Right: Login Form (Refined) -->
|
||||
<main class="login-main">
|
||||
<div class="login-form-wrap">
|
||||
<header class="login-header">
|
||||
<h2>系统登录</h2>
|
||||
<p>请输入您的凭据以访问控制台</p>
|
||||
</header>
|
||||
|
||||
<form class="login-form" @submit.prevent="emit('login', { username, password })">
|
||||
<div class="input-group">
|
||||
<label>用户名</label>
|
||||
<div class="input-control">
|
||||
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
|
||||
<input v-model="username" type="text" placeholder="账户名称 / 手机号" required />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label>密码</label>
|
||||
<div class="input-control">
|
||||
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>
|
||||
<input v-model="password" type="password" placeholder="请输入登录密码" required />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<label class="remember-me">
|
||||
<input type="checkbox" v-model="remember" />
|
||||
<span>记住我</span>
|
||||
</label>
|
||||
<a href="#" class="forgot-link">忘记密码?</a>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="submit-btn">
|
||||
<span>登录系统</span>
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<footer class="login-footer">
|
||||
<p>© 2026 X-Financial Technology. All Rights Reserved.</p>
|
||||
</footer>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, markRaw } from 'vue'
|
||||
|
||||
const emit = defineEmits(['login'])
|
||||
|
||||
const username = ref('')
|
||||
const password = ref('')
|
||||
const remember = ref(false)
|
||||
|
||||
const ShieldCheck = {
|
||||
template: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/><path d="m9 12 2 2 4-4"/></svg>`
|
||||
}
|
||||
const Activity = {
|
||||
template: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 12h-4l-3 9L9 3l-3 9H2"/></svg>`
|
||||
}
|
||||
const Zap = {
|
||||
template: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg>`
|
||||
}
|
||||
|
||||
const features = [
|
||||
{ title: '多维合规引擎', desc: '内置 500+ 企业财务制度,实现毫秒级自动预审。', color: '#3b82f6', icon: markRaw(ShieldCheck) },
|
||||
{ title: '全链路风控监控', desc: '覆盖票据验真、重复报销及异常交易的深度挖掘。', color: '#10b981', icon: markRaw(Activity) },
|
||||
{ title: '流程自动化编排', desc: 'AI Agent 驱动的自动分类与审批建议,节省 80% 人力。', color: '#f59e0b', icon: markRaw(Zap) }
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* ── Main Layout ─────────────────────────────────── */
|
||||
.login-page {
|
||||
min-height: 100dvh;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(600px, 1.2fr) minmax(400px, 0.8fr);
|
||||
background: #fff;
|
||||
font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
||||
}
|
||||
|
||||
/* ── Hero Side ───────────────────────────────────── */
|
||||
.hero {
|
||||
position: relative;
|
||||
background: #0f172a; /* Slate 900 */
|
||||
color: #f8fafc;
|
||||
padding: 60px 80px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.hero-grid {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-image:
|
||||
linear-gradient(rgba(59, 130, 246, 0.05) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(59, 130, 246, 0.05) 1px, transparent 1px);
|
||||
background-size: 40px 40px;
|
||||
mask-image: radial-gradient(circle at 20% 30%, black, transparent 70%);
|
||||
}
|
||||
|
||||
.hero-content {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
max-width: 560px;
|
||||
}
|
||||
|
||||
.brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 60px;
|
||||
}
|
||||
|
||||
.logo-box {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background: #3b82f6;
|
||||
border-radius: 10px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
color: #fff;
|
||||
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4);
|
||||
}
|
||||
.logo-box svg { width: 24px; height: 24px; }
|
||||
.brand-name {
|
||||
font-size: 18px;
|
||||
font-weight: 800;
|
||||
letter-spacing: -0.02em;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.hero-header h1 {
|
||||
font-size: 42px;
|
||||
line-height: 1.2;
|
||||
font-weight: 800;
|
||||
margin-bottom: 20px;
|
||||
color: #fff;
|
||||
}
|
||||
.text-gradient {
|
||||
background: linear-gradient(135deg, #60a5fa, #3b82f6);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
|
||||
.hero-header p {
|
||||
font-size: 16px;
|
||||
line-height: 1.6;
|
||||
color: #94a3b8;
|
||||
margin-bottom: 48px;
|
||||
}
|
||||
|
||||
.value-prop {
|
||||
display: grid;
|
||||
gap: 32px;
|
||||
}
|
||||
|
||||
.prop-item {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.prop-icon {
|
||||
flex-shrink: 0;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
.prop-icon svg { width: 100%; height: 100%; }
|
||||
|
||||
.prop-text strong {
|
||||
display: block;
|
||||
font-size: 15px;
|
||||
color: #f1f5f9;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.prop-text p {
|
||||
font-size: 13px;
|
||||
color: #64748b;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.hero-footer {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
margin-top: 60px;
|
||||
}
|
||||
|
||||
.trust-badge {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 12px 20px;
|
||||
background: rgba(255,255,255,0.03);
|
||||
border: 1px solid rgba(255,255,255,0.06);
|
||||
border-radius: 99px;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.avatars {
|
||||
display: flex;
|
||||
}
|
||||
.avatar-circle {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%;
|
||||
background: #334155;
|
||||
border: 2px solid #0f172a;
|
||||
margin-left: -8px;
|
||||
}
|
||||
.avatar-circle:first-child { margin-left: 0; }
|
||||
|
||||
.trust-badge span {
|
||||
font-size: 12px;
|
||||
color: #94a3b8;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* ── Hero Preview Element ────────────────────────── */
|
||||
.hero-preview {
|
||||
position: absolute;
|
||||
right: -40px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 440px;
|
||||
opacity: 0.8;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.preview-card {
|
||||
background: #1e293b;
|
||||
border: 1px solid rgba(255,255,255,0.1);
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 40px 80px rgba(0,0,0,0.4);
|
||||
overflow: hidden;
|
||||
animation: floatPreview 6s ease-in-out infinite alternate;
|
||||
}
|
||||
|
||||
@keyframes floatPreview {
|
||||
from { transform: translateY(0); }
|
||||
to { transform: translateY(-20px); }
|
||||
}
|
||||
|
||||
.preview-header {
|
||||
padding: 12px 16px;
|
||||
background: rgba(255,255,255,0.03);
|
||||
border-bottom: 1px solid rgba(255,255,255,0.05);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
.dots { display: flex; gap: 6px; }
|
||||
.dots span { width: 8px; height: 8px; border-radius: 50%; background: #334155; }
|
||||
.preview-title { font-size: 11px; color: #94a3b8; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; }
|
||||
|
||||
.preview-body { padding: 24px; }
|
||||
|
||||
.preview-stat {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.stat-circle {
|
||||
position: relative;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
}
|
||||
.stat-circle svg { transform: rotate(-90deg); }
|
||||
.circle-bg { fill: none; stroke: #334155; stroke-width: 3; }
|
||||
.circle { fill: none; stroke: #3b82f6; stroke-width: 3; stroke-linecap: round; }
|
||||
.stat-val {
|
||||
position: absolute; inset: 0; display: grid; place-items: center;
|
||||
font-size: 14px; font-weight: 800; color: #fff;
|
||||
}
|
||||
|
||||
.stat-info strong { display: block; font-size: 16px; color: #fff; }
|
||||
.stat-info p { font-size: 12px; color: #10b981; margin-top: 2px; }
|
||||
|
||||
.preview-list { display: grid; gap: 12px; }
|
||||
.list-item { height: 12px; background: #334155; border-radius: 6px; overflow: hidden; }
|
||||
.item-bar { height: 100%; background: linear-gradient(90deg, #3b82f6, #60a5fa); border-radius: inherit; }
|
||||
|
||||
/* ── Login Main ──────────────────────────────────── */
|
||||
.login-main {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
.login-form-wrap {
|
||||
width: 100%;
|
||||
max-width: 360px;
|
||||
}
|
||||
|
||||
.login-header {
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
.login-header h2 {
|
||||
font-size: 28px;
|
||||
font-weight: 800;
|
||||
color: #0f172a;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.login-header p {
|
||||
font-size: 14px;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.login-form {
|
||||
display: grid;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.input-group label {
|
||||
display: block;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
color: #1e293b;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.input-control {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.input-control .icon {
|
||||
position: absolute;
|
||||
left: 12px;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
.input-control input {
|
||||
width: 100%;
|
||||
height: 44px;
|
||||
padding-left: 40px;
|
||||
padding-right: 12px;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.input-control input:focus {
|
||||
outline: none;
|
||||
border-color: #3b82f6;
|
||||
box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.1);
|
||||
}
|
||||
|
||||
.form-actions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.remember-me {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.forgot-link {
|
||||
font-size: 13px;
|
||||
color: #3b82f6;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
width: 100%;
|
||||
height: 48px;
|
||||
background: #0f172a;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.submit-btn:hover {
|
||||
background: #1e293b;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.submit-btn svg {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.login-footer {
|
||||
margin-top: 48px;
|
||||
text-align: center;
|
||||
}
|
||||
.login-footer p {
|
||||
font-size: 12px;
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.hero { display: none; }
|
||||
.login-page { grid-template-columns: 1fr; }
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user