微头条Router工程代码
- 创建工程
npm creat vite
cd ./工程目录
npm install
npm install vue-router
2.src/router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import Login from '../views/Login.vue'
import Home from '../views/Home.vue'
const routes = [
{ path: '/', redirect: '/login' },
{ path: '/login', component: Login },
{ path: '/home', component: Home }
]
const router = createRouter({
history: createWebHistory(),
routes
})
// 简易路由守卫
router.beforeEach((to, from, next) => {
const token = localStorage.getItem('token')
console.log("路由守卫")
if (to.path === '/home' && !token) {
next('/login')
} else {
next()
}
})
export default router
3.src/views/Home.vue
<template>
<div class="home-container">
<!-- 固定头部 -->
<div class="header">
<h2>微头条首页</h2>
<button @click="logout">退出登录</button>
</div>
<!-- 可滚动的内容区域(发布区 + 新闻列表) -->
<div class="scroll-area">
<div class="content-wrapper">
<!-- 发布区域 -->
<div class="publish-area">
<input type="text" v-model="newTitle" placeholder="新闻标题" @keyup.enter="addNews" />
<input type="text" v-model="newContent" placeholder="新闻内容" />
<button @click="addNews">+ 发布微头条</button>
</div>
<!-- 新闻列表 -->
<div class="news-list">
<div v-if="newsList.length === 0" class="empty">
暂无新闻,快去发布一条吧
</div>
<div v-for="item in newsList" :key="item.id" class="news-item">
<h3>{{ item.title }}</h3>
<div class="time">{{ item.time }}</div>
<div class="content">{{ item.content }}</div>
<div class="actions">
<a href="javascript:;">📚 查看详情</a>
<button @click="deleteNews(item.id)">🗑 删除</button>
<button @click="likeNews(item.id)">💗 点赞 {{ item.likes }}</button>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { useRouter } from 'vue-router'
import { ref, onMounted, onUnmounted } from 'vue'
const newsList = ref([
{
id: 1,
title: '微头条1.0 正式发布',
time: '2025-06-01 10:30',
content: '微头条是一个轻量级信息分享平台,采用 Vue3 + SpringBoot 技术栈。',
likes: 0
},
{
id: 2,
title: 'Vue3 组合式 API 学习心得',
time: '2025-06-02 15:20',
content: 'setup 语法糖非常简洁,ref和reactive让数据响应式变得容易。',
likes: 2
}
])
const newTitle = ref('')
const newContent = ref('')
let nextId = 3
const addNews = () => {
if (!newTitle.value.trim()) {
alert('请输入标题')
return
}
const newNews = {
id: nextId++,
title: newTitle.value,
content: newContent.value || '暂无内容',
time: new Date().toLocaleString(),
likes: 0
}
newsList.value.unshift(newNews)
newTitle.value = ''
newContent.value = ''
}
const deleteNews = (id) => {
newsList.value = newsList.value.filter(item => item.id !== id)
}
const likeNews = (id) => {
const item = newsList.value.find(item => item.id === id)
if (item) {
item.likes++
}
}
const router = useRouter()
const logout = () => {
localStorage.removeItem('token')
router.push('/login')
}
onMounted(() => {
document.body.style.overflow = 'hidden'
})
onUnmounted(() => {
document.body.style.overflow = ''
})
</script>
<style scoped>
/* 整体容器:占满视口,flex 纵向排列 */
.home-container {
height: 100vh;
display: flex;
flex-direction: column;
background: #f5f5f5;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
/* 固定头部 */
.header {
flex-shrink: 0;
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px 24px;
background: #ffffff;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04), 0 1px 2px rgba(0, 0, 0, 0.02);
border-bottom: 5px solid #e0e0e0;
}
.header h2 {
margin: 0;
color: #333;
}
.header button {
background: #ff4d4f;
color: white;
border: none;
border-radius: 20px;
padding: 6px 16px;
cursor: pointer;
}
.header button:hover {
background: #ff7875;
}
/* 滚动区域:占据剩余高度,内部滚动 */
.scroll-area {
flex: 1;
overflow-y: auto;
padding: 20px 20px 20px 20px;
/* 左右内边距,底部留白 */
}
/* 内容包装器:限制最大宽度并居中 */
.content-wrapper {
max-width: 800px;
margin: 0 auto;
}
/* 发布区域 */
.publish-area {
display: flex;
gap: 10px;
margin-top: 20px;
margin-bottom: 20px;
}
.publish-area input {
flex: 1;
padding: 10px;
border: 1px solid #ddd;
border-radius: 24px;
}
.publish-area button {
display: block;
width: 180px;
background: #1890ff;
color: white;
border: none;
border-radius: 24px;
padding: 10px 0;
font-size: 16px;
cursor: pointer;
transition: background 0.3s;
}
.publish-area button:hover {
background: #0c7bdf;
}
/* 新闻列表 */
.news-list {
margin-top: 20px;
}
/* 新闻卡片 */
.news-item {
background: white;
border-radius: 12px;
padding: 20px;
margin-bottom: 16px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
transition: transform 0.2s, box-shadow 0.2s;
}
.news-item:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.news-item h3 {
margin-bottom: 8px;
color: #333;
}
.time {
font-size: 12px;
color: #999;
margin-bottom: 12px;
}
.content {
font-size: 14px;
color: #666;
line-height: 1.5;
margin-bottom: 12px;
}
.actions a {
color: #1890ff;
text-decoration: none;
margin-right: 10px;
font-size: 14px;
}
.actions a:hover {
text-decoration: underline;
}
.actions button {
margin-right: 10px;
background: none;
border: none;
color: #e5152d;
cursor: pointer;
}
.actions button:hover {
text-decoration: underline;
}
.actions button:last-child {
color: #0ae411;
}
.empty {
text-align: center;
color: #999;
padding: 40px;
}
</style>
4.src/views/Login.vue
<template>
<div class="login-page">
<div class="login-card">
<h2>微头条登录</h2>
<input type="text" placeholder="用户名" v-model="username" />
<input type="password" placeholder="密码" v-model="password" />
<button @click="handleLogin">登录</button>
<button @click="clearForm">清空</button>
<div class="register-link">
<a href="#">立即注册</a>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import { useRouter } from 'vue-router'
const username = ref('')
const password = ref('')
const router = useRouter()
const handleLogin = () => {
if (username.value === 'w' && password.value === '1') {
localStorage.setItem('token', 'fake-token')
router.push('/home')
} else {
alert('用户名或密码错误')
}
}
const clearForm = () => {
username.value = ''
password.value = ''
}
onMounted(() => {
document.body.style.overflow = 'hidden'
})
onUnmounted(() => {
document.body.style.overflow = ''
})
</script>
<style scoped>
.login-page {
height: 100vh;
width: 100%;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
display: flex;
justify-content: center;
align-items: center;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.login-card {
width: 360px;
background: white;
padding: 30px;
border-radius: 16px;
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
}
.login-card h2 {
text-align: center;
margin-bottom: 24px;
color: #333;
}
.login-card input {
width: 100%;
padding: 12px;
margin-bottom: 16px;
border: 1px solid #ddd;
border-radius: 8px;
font-size: 14px;
box-sizing: border-box;
transition: border-color 0.2s;
}
.login-card input:focus {
outline: none;
border-color: #667eea;
}
.login-card button {
width: 100%;
padding: 12px;
border: none;
border-radius: 8px;
font-size: 16px;
cursor: pointer;
transition: background 0.3s;
}
.login-card button:first-of-type {
background: #667eea;
color: white;
}
.login-card button:first-of-type:hover {
background: #5a67d8;
}
.login-card button:last-of-type {
background: #e2e8f0;
color: #333;
margin-top: 8px;
}
.login-card button:last-of-type:hover {
background: #cbd5e0;
}
.register-link {
text-align: center;
margin-top: 20px;
font-size: 14px;
color: #666;
}
.register-link a {
color: #667eea;
text-decoration: none;
}
.register-link a:hover {
text-decoration: underline;
}
</style>
5.src/App.vue
<template>
<router-view />
</template>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
</style>
6.src/main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
const app = createApp(App)
app.use(router)
app.mount('#app')
浙公网安备 33010602011771号