feat: 首页消息的界面搭建

This commit is contained in:
zoujing
2026-01-05 18:14:45 +08:00
parent 4b46484cfe
commit 7288b94996
4 changed files with 943 additions and 5 deletions

View File

@@ -0,0 +1,121 @@
<template>
<div class="chat-guide">
<div class="hero-area">
<h1 class="greet">你好<br/>我今天能帮你什么</h1>
<div class="input-wrap">
<div class="tag">智能问数</div>
<div class="fake-input">
<div class="placeholder">给我发布或者布置任务</div>
<div class="actions">
<button class="link">🔗</button>
<button class="send"></button>
</div>
</div>
</div>
<div class="task-header">
<h3>任务中心</h3>
<a class="edit">编辑</a>
</div>
</div>
<div class="task-area">
<div class="task-list">
<div class="task-card" v-for="n in 14" :key="n">
<div class="task-icon"></div>
<div class="task-body">
<div class="task-title">每日销售数据</div>
<div class="task-desc">分析用于销售渠道每日数据汇总及简要展示</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
</script>
<style scoped>
.chat-guide {
height: 100%;
overflow: visible;
background: #fff;
padding: 40px 48px;
box-sizing: border-box;
}
.hero-area {
position: sticky;
top: 0;
background: #ffffff;
padding-top: 24px;
padding-bottom: 24px;
z-index: 10;
}
.greet {
font-size: 28px;
font-weight: 700;
margin: 0 0 28px 0;
}
.input-wrap { display:flex; flex-direction:column; align-items:stretch; gap:12px }
.tag {
display:inline-flex;
align-items:center;
justify-content:center;
width: 108px;
padding:6px 10px;
border-radius: 16px;
background: transparent;
color:#333;
font-size:13px;
border: 1px solid #E5E8EE;
box-sizing: border-box;
}
.fake-input {
height: 174px;
background: #fff;
border-radius: 8px;
padding: 18px;
border: 1px solid #eef2f6;
box-shadow: 0 1px 0 rgba(0,0,0,0.03);
display:flex;
flex-direction: column;
justify-content: space-between;
}
.placeholder { color:#bfc9d4 }
.actions { display:flex; justify-content: space-between; align-items: center; width:100% }
.actions .link { background:transparent; border:none; color:#9fb0c4 }
.actions .send { background:#f1f6fb; border:none; padding:6px 10px; border-radius:6px }
.task-area { margin-top: 20px }
.task-header { display:flex; justify-content:space-between; align-items:center; margin:16px 0 }
.task-header h3 { margin:0; font-size:16px }
.task-header .edit { color:#3b82f6; font-size:13px }
.task-list { display:grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: 16px }
.task-card {
display:flex;
gap:12px;
align-items:flex-start;
padding: 14px;
border-radius: 10px;
border: 1px solid #dfeaf6;
background:#fff;
}
.task-icon { width:44px;height:44px;background: #EFF6FF; border-radius:8px;border:1px dashed #9fc0e8;display:flex;align-items:center;justify-content:center;color:#3b82f6; font-size: 23px; }
.task-title { font-weight:600 }
.task-desc { color:#9aa5b1; font-size:13px; margin-top:6px }
/* make content under hero scroll while hero stays sticky */
.chat-guide::before{ content:''; display:block; height:8px }
@media (max-width: 800px){
.chat-guide { padding:20px }
.task-list { grid-template-columns: 1fr }
}
</style>

View File

@@ -1,8 +1,37 @@
<template>
<div class="bg-white h-full w-full p-[20px]">
<h1>首页 Dashboard</h1>
<div class="home-tab">
<message-list />
<div class="main-area">
<chat-guide />
</div>
</div>
</div>
</template>
<script setup lang="ts">
</script>
import MessageList from './MessageList.vue'
import ChatGuide from './ChatGuide.vue'
</script>
<style scoped>
.home-tab {
display: flex;
height: 100%;
width: 100%;
}
.home-tab > :first-child { flex: 0 0 260px }
.main-area { flex: 1 1 auto; overflow: auto; padding: 20px }
/* match MessageList scrollbar: 8px, subtle thumb */
.main-area { scrollbar-width: auto }
.main-area::-webkit-scrollbar { width: 8px }
.main-area::-webkit-scrollbar-thumb { background: rgba(0,0,0,0.05); border-radius: 8px }
.main-area::-webkit-scrollbar-track { background: transparent }
@media (max-width: 900px) {
.home-tab { flex-direction: column }
.home-tab > :first-child { flex: none; width: 100% }
.main-area { padding: 12px }
}
</style>

View File

@@ -0,0 +1,198 @@
<template>
<aside class="msg-list">
<header class="msg-list__header">
<div class="brand">
<img class="logo" src="@assets/images/login/white_logo.png"/>
<div class="title">YINIAN</div>
</div>
</header>
<div class="new-row">
<button class="new-btn"> 新对话</button>
</div>
<div class="sections">
<section v-for="group in groups" :key="group.title" class="group">
<div class="group__title">
<span>{{ group.title }}</span>
<span class="toggle"></span>
</div>
<ul class="items">
<li v-for="item in group.items" :key="item.id" :class="['item', { active: item.id === selectedId }]" @click="select(item.id)">
<span class="dot"></span>
<div class="content">
<div class="text">{{ item.title }}</div>
</div>
<button v-if="item.id === selectedId" class="more"></button>
</li>
</ul>
</section>
</div>
</aside>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const selectedId = ref<number | null>(2)
const groups = ref([
{
title: '近3天',
items: [
{ id: 1, title: '这是一段对话' },
{ id: 2, title: '这是一段对话' },
{ id: 3, title: '这是一段对话这是一段对话这是一段对话' },
{ id: 4, title: '这是一段对话这是一段对话' },
{ id: 5, title: '这是一段对话这是一段对话' }
]
},
{
title: '近7天',
items: [
{ id: 6, title: '这是一段对话' },
{ id: 7, title: '这是一段对话' },
{ id: 8, title: '这是一段对话这是一段对话' },
{ id: 9, title: '这是一段对话这是一段对话' }
]
},
{
title: '近15天',
items: [
{ id: 1, title: '这是一段对话' },
{ id: 2, title: '这是一段对话' },
{ id: 3, title: '这是一段对话这是一段对话这是一段对话' },
{ id: 4, title: '这是一段对话这是一段对话' },
{ id: 5, title: '这是一段对话这是一段对话' }
]
},
{
title: '近30天',
items: [
{ id: 6, title: '这是一段对话' },
{ id: 7, title: '这是一段对话' },
{ id: 8, title: '这是一段对话这是一段对话' },
{ id: 9, title: '这是一段对话这是一段对话' }
]
},
{
title: '近60天',
items: [
{ id: 1, title: '这是一段对话' },
{ id: 2, title: '这是一段对话' },
{ id: 3, title: '这是一段对话这是一段对话这是一段对话' },
{ id: 4, title: '这是一段对话这是一段对话' },
{ id: 5, title: '这是一段对话这是一段对话' }
]
},
{
title: '近90天',
items: [
{ id: 6, title: '这是一段对话' },
{ id: 7, title: '这是一段对话' },
{ id: 8, title: '这是一段对话这是一段对话' },
{ id: 9, title: '这是一段对话这是一段对话' }
]
},
])
function select(id: number) {
selectedId.value = id
}
</script>
<style scoped>
:root {
--bg: red;
--muted: #1c4b7d;
--dot: #BEDBFF;
--border: #E5E6EB;
--active-bg: #ffffff;
--accent: #4fc3f7;
}
.msg-list {
width: 260px;
height: 100vh;
background: var(--bg);
padding: 16px;
box-sizing: border-box;
display: flex;
flex-direction: column;
}
.msg-list__header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 12px;
}
.brand {
display: flex;
align-items: center;
gap: 8px;
}
.logo {
width: 40px;
height: 40px;
border-radius: 8px;
object-fit:cover;
}
.title { font-weight:700; color:#2b2f33 }
.chev { color:var(--muted); transform: rotate(180deg); font-size:14px }
.new-btn {
background: white;
border-radius: 8px;
padding: 8px 12px;
border: none;
box-shadow: 0 1px 0 rgba(0,0,0,0.04);
cursor: pointer;
}
.new-row { margin: 12px }
.new-row .new-btn { width: 100%; display: block; text-align: center; border: 1px solid #E5E6EB; }
.sections { overflow-y: auto; padding-right: 6px }
.group { margin-bottom: 12px }
.group__title {
color: var(--muted);
font-size: 13px;
display:flex;
align-items:center;
justify-content: space-between;
padding: 8px 2px;
}
.items { list-style:none; padding: 4px 0; margin:0 }
.item {
display:flex;
align-items:center;
gap:8px;
padding: 10px 8px;
color:#4a5560;
border-radius: 8px;
cursor: pointer;
transition: background .12s;
}
.item:not(.active):hover { background: rgba(0,0,0,0.02) }
.item.active { background: var(--active-bg); box-shadow: 0 4px 8px rgba(17,24,39,0.06); border: 1px solid #E5E6EB; }
.dot {
width:8px;
height:8px;
border-radius:50%;
background: #BEDBFF;
flex: 0 0 8px;
}
.content { flex:1; min-width:0 }
.text { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size:14px }
.more {
background: transparent;
border: none;
color: var(--muted);
font-size: 18px;
padding: 4px 6px;
cursor: pointer;
}
/* scrollbar small */
.sections::-webkit-scrollbar { width: 8px }
.sections::-webkit-scrollbar-thumb { background: rgba(0,0,0,0.05); border-radius: 8px }
</style>