视图路径修改
This commit is contained in:
@@ -70,7 +70,7 @@ import { ElButton, ElInput, ElTable, ElTableColumn, ElTag, ElPagination, ElMessa
|
||||
import { useRouter } from 'vue-router';
|
||||
import { resourceApi, resourceTagApi } from '@/apis/resource'
|
||||
import type { PageParam, ResourceSearchParams, Resource, Tag } from '@/types';
|
||||
import { ArticleShowView } from '@/views/article';
|
||||
import { ArticleShowView } from '@/views/public/article';
|
||||
import { ArticleStatus } from '@/types/enums';
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { CourseList, CourseAdd } from '@/views/course/components';
|
||||
import { CourseList, CourseAdd } from '@/views/public/course/components';
|
||||
import type { Course } from '@/types/study';
|
||||
|
||||
type ViewType = 'list' | 'add' | 'edit';
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { LearningTaskList, LearningTaskAdd } from '@/views/task';
|
||||
import { LearningTaskList, LearningTaskAdd } from '@/views/public/task';
|
||||
import type { LearningTask } from '@/types/study';
|
||||
|
||||
defineOptions({
|
||||
|
||||
@@ -184,7 +184,6 @@ function initCharts() {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.system-overview {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
|
||||
@@ -1,466 +0,0 @@
|
||||
<template>
|
||||
<div class="workplace">
|
||||
<div class="workplace-header">
|
||||
<div class="welcome-info">
|
||||
<h1 class="welcome-title">
|
||||
欢迎回来,{{ userInfo?.realName || userInfo?.username }}!
|
||||
</h1>
|
||||
<p class="welcome-subtitle">
|
||||
今天是 {{ currentDate }},{{ greetingText }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="user-stats">
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon">📝</div>
|
||||
<div class="stat-content">
|
||||
<div class="stat-number">{{ todayNews }}</div>
|
||||
<div class="stat-label">今日新闻</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon">👥</div>
|
||||
<div class="stat-content">
|
||||
<div class="stat-number">{{ onlineUsers }}</div>
|
||||
<div class="stat-label">在线用户</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-card" v-permission="'system:news:view'">
|
||||
<div class="stat-icon">📊</div>
|
||||
<div class="stat-content">
|
||||
<div class="stat-number">{{ totalViews }}</div>
|
||||
<div class="stat-label">总阅读量</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="workplace-content">
|
||||
<!-- 快捷操作 -->
|
||||
<div class="quick-actions">
|
||||
<h2 class="section-title">快捷操作</h2>
|
||||
<div class="action-grid">
|
||||
<div class="action-card" @click="goToCreateNews" v-permission="'news:create'">
|
||||
<div class="action-icon">✏️</div>
|
||||
<div class="action-title">发布新闻</div>
|
||||
<div class="action-desc">创建新的校园新闻</div>
|
||||
</div>
|
||||
|
||||
<div class="action-card" @click="goToUserManage" v-permission="'system:user:view'">
|
||||
<div class="action-icon">👤</div>
|
||||
<div class="action-title">用户管理</div>
|
||||
<div class="action-desc">管理系统用户</div>
|
||||
</div>
|
||||
|
||||
<div class="action-card" @click="goToSystemSettings" v-permission="'system:settings:view'">
|
||||
<div class="action-icon">⚙️</div>
|
||||
<div class="action-title">系统设置</div>
|
||||
<div class="action-desc">配置系统参数</div>
|
||||
</div>
|
||||
|
||||
<div class="action-card" @click="goToProfile">
|
||||
<div class="action-icon">📋</div>
|
||||
<div class="action-title">个人资料</div>
|
||||
<div class="action-desc">编辑个人信息</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 最近活动 -->
|
||||
<div class="recent-activities">
|
||||
<h2 class="section-title">最近活动</h2>
|
||||
<div class="activity-list">
|
||||
<div class="activity-item" v-for="activity in recentActivities" :key="activity.id">
|
||||
<div class="activity-icon" :class="activity.type">
|
||||
{{ getActivityIcon(activity.type) }}
|
||||
</div>
|
||||
<div class="activity-content">
|
||||
<div class="activity-title">{{ activity.title }}</div>
|
||||
<div class="activity-time">{{ formatTime(activity.time) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 系统公告 -->
|
||||
<div class="system-notice">
|
||||
<h2 class="section-title">系统公告</h2>
|
||||
<div class="notice-list">
|
||||
<div class="notice-item" v-for="notice in systemNotices" :key="notice.id">
|
||||
<div class="notice-type" :class="notice.type">
|
||||
{{ getNoticeTypeText(notice.type) }}
|
||||
</div>
|
||||
<div class="notice-content">
|
||||
<div class="notice-title">{{ notice.title }}</div>
|
||||
<div class="notice-time">{{ formatTime(notice.time) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useStore } from 'vuex';
|
||||
// import { usePermission } from '@/directives/permission'; // 暂时注释,后续权限功能开发时启用
|
||||
|
||||
// 数据
|
||||
const todayNews = ref(12);
|
||||
const onlineUsers = ref(56);
|
||||
const totalViews = ref(8924);
|
||||
|
||||
const recentActivities = ref([
|
||||
{
|
||||
id: 1,
|
||||
type: 'news',
|
||||
title: '发布了新闻《学校举办科技创新大赛》',
|
||||
time: new Date(Date.now() - 10 * 60 * 1000) // 10分钟前
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
type: 'user',
|
||||
title: '新用户注册:张三',
|
||||
time: new Date(Date.now() - 30 * 60 * 1000) // 30分钟前
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
type: 'system',
|
||||
title: '系统维护完成',
|
||||
time: new Date(Date.now() - 2 * 60 * 60 * 1000) // 2小时前
|
||||
}
|
||||
]);
|
||||
|
||||
const systemNotices = ref([
|
||||
{
|
||||
id: 1,
|
||||
type: 'info',
|
||||
title: '系统将于本周日进行例行维护',
|
||||
time: new Date(Date.now() - 24 * 60 * 60 * 1000) // 1天前
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
type: 'warning',
|
||||
title: '请及时更新个人资料信息',
|
||||
time: new Date(Date.now() - 3 * 24 * 60 * 60 * 1000) // 3天前
|
||||
}
|
||||
]);
|
||||
|
||||
// Composition API
|
||||
const router = useRouter();
|
||||
const store = useStore();
|
||||
// const { hasPermission } = usePermission(); // 暂时注释,后续权限功能开发时启用
|
||||
|
||||
// 计算属性
|
||||
const userInfo = computed(() => store.getters['auth/user']);
|
||||
|
||||
const currentDate = computed(() => {
|
||||
return new Date().toLocaleDateString('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
weekday: 'long'
|
||||
});
|
||||
});
|
||||
|
||||
const greetingText = computed(() => {
|
||||
const hour = new Date().getHours();
|
||||
if (hour < 6) return '夜深了,注意休息';
|
||||
if (hour < 12) return '早上好';
|
||||
if (hour < 18) return '下午好';
|
||||
return '晚上好';
|
||||
});
|
||||
|
||||
// 方法
|
||||
function goToCreateNews() {
|
||||
router.push('/news/create');
|
||||
}
|
||||
|
||||
function goToUserManage() {
|
||||
router.push('/system/user');
|
||||
}
|
||||
|
||||
function goToSystemSettings() {
|
||||
router.push('/system/settings');
|
||||
}
|
||||
|
||||
function goToProfile() {
|
||||
router.push('/profile');
|
||||
}
|
||||
|
||||
function getActivityIcon(type: string) {
|
||||
const icons = {
|
||||
news: '📰',
|
||||
user: '👤',
|
||||
system: '⚙️'
|
||||
};
|
||||
return icons[type as keyof typeof icons] || '📝';
|
||||
}
|
||||
|
||||
function getNoticeTypeText(type: string) {
|
||||
const types = {
|
||||
info: '通知',
|
||||
warning: '提醒',
|
||||
error: '警告'
|
||||
};
|
||||
return types[type as keyof typeof types] || '公告';
|
||||
}
|
||||
|
||||
function formatTime(time: Date) {
|
||||
const now = new Date();
|
||||
const diff = now.getTime() - time.getTime();
|
||||
|
||||
if (diff < 60 * 1000) return '刚刚';
|
||||
if (diff < 60 * 60 * 1000) return `${Math.floor(diff / 60 / 1000)}分钟前`;
|
||||
if (diff < 24 * 60 * 60 * 1000) return `${Math.floor(diff / 60 / 60 / 1000)}小时前`;
|
||||
if (diff < 7 * 24 * 60 * 60 * 1000) return `${Math.floor(diff / 24 / 60 / 60 / 1000)}天前`;
|
||||
|
||||
return time.toLocaleDateString('zh-CN');
|
||||
}
|
||||
|
||||
// 生命周期
|
||||
onMounted(() => {
|
||||
// 可以在这里加载统计数据
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.workplace {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.workplace-header {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
padding: 32px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 24px;
|
||||
|
||||
.welcome-info {
|
||||
margin-bottom: 24px;
|
||||
|
||||
.welcome-title {
|
||||
font-size: 28px;
|
||||
font-weight: 600;
|
||||
margin: 0 0 8px 0;
|
||||
}
|
||||
|
||||
.welcome-subtitle {
|
||||
font-size: 16px;
|
||||
opacity: 0.9;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.user-stats {
|
||||
display: flex;
|
||||
gap: 24px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
min-width: 150px;
|
||||
backdrop-filter: blur(10px);
|
||||
|
||||
.stat-icon {
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.stat-content {
|
||||
.stat-number {
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 14px;
|
||||
opacity: 0.8;
|
||||
margin-top: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.workplace-content {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 24px;
|
||||
|
||||
@media (max-width: 768px) {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
margin: 0 0 16px 0;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.quick-actions {
|
||||
background: white;
|
||||
padding: 24px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
|
||||
.action-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.action-card {
|
||||
padding: 20px;
|
||||
border: 1px solid #e8e8e8;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
text-align: center;
|
||||
|
||||
&:hover {
|
||||
border-color: #1890ff;
|
||||
box-shadow: 0 2px 8px rgba(24, 144, 255, 0.2);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.action-icon {
|
||||
font-size: 32px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.action-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.action-desc {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.recent-activities {
|
||||
background: white;
|
||||
padding: 24px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
grid-column: 1;
|
||||
|
||||
.activity-list {
|
||||
.activity-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 12px 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.activity-icon {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
|
||||
&.news { background: #e6f7ff; }
|
||||
&.user { background: #f6ffed; }
|
||||
&.system { background: #fff7e6; }
|
||||
}
|
||||
|
||||
.activity-content {
|
||||
flex: 1;
|
||||
|
||||
.activity-title {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.activity-time {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.system-notice {
|
||||
background: white;
|
||||
padding: 24px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
grid-column: 2;
|
||||
|
||||
.notice-list {
|
||||
.notice-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 12px 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.notice-type {
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
|
||||
&.info {
|
||||
background: #e6f7ff;
|
||||
color: #1890ff;
|
||||
}
|
||||
|
||||
&.warning {
|
||||
background: #fff7e6;
|
||||
color: #fa8c16;
|
||||
}
|
||||
|
||||
&.error {
|
||||
background: #fff2f0;
|
||||
color: #f5222d;
|
||||
}
|
||||
}
|
||||
|
||||
.notice-content {
|
||||
flex: 1;
|
||||
|
||||
.notice-title {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.notice-time {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,39 +0,0 @@
|
||||
<template>
|
||||
<div class="home-page">
|
||||
<!-- 个人学习数据概览 -->
|
||||
<LearningDataOverview />
|
||||
|
||||
<!-- 书报馆件 -->
|
||||
<BookHallSection />
|
||||
|
||||
<!-- TOP音乐推荐 -->
|
||||
<TopMusicRecommend />
|
||||
|
||||
<!-- 新闻概览 -->
|
||||
<NewsOverview />
|
||||
|
||||
<!-- 导航栏 -->
|
||||
<NavigationBar />
|
||||
|
||||
<!-- 破破栏索 -->
|
||||
<SearchIndex />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import LearningDataOverview from './components/LearningDataOverview.vue';
|
||||
import BookHallSection from './components/BookHallSection.vue';
|
||||
import TopMusicRecommend from './components/TopMusicRecommend.vue';
|
||||
import NewsOverview from './components/NewsOverview.vue';
|
||||
import NavigationBar from './components/NavigationBar.vue';
|
||||
import SearchIndex from './components/SearchIndex.vue';
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.home-page {
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
<template>
|
||||
<div class="book-hall-section">
|
||||
<h2 class="section-title">书报馆件</h2>
|
||||
<div class="book-list">
|
||||
<div class="book-item" v-for="book in books" :key="book.id">
|
||||
<div class="book-cover">
|
||||
<img :src="book.cover" :alt="book.title" />
|
||||
</div>
|
||||
<div class="book-info">
|
||||
<h3>{{ book.title }}</h3>
|
||||
<p>{{ book.author }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue';
|
||||
|
||||
const books = ref<any[]>([]);
|
||||
|
||||
onMounted(() => {
|
||||
// TODO: 加载书籍数据
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.book-hall-section {
|
||||
padding: 40px;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
color: #141F38;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.book-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.book-item {
|
||||
cursor: pointer;
|
||||
transition: transform 0.3s;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
}
|
||||
|
||||
.book-cover {
|
||||
width: 100%;
|
||||
height: 280px;
|
||||
background: #f5f5f5;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
.book-info {
|
||||
margin-top: 12px;
|
||||
|
||||
h3 {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #141F38;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
<template>
|
||||
<div class="learning-data-overview">
|
||||
<h2 class="section-title">个人学习数据概览</h2>
|
||||
<div class="data-cards">
|
||||
<div class="data-card" v-for="item in dataItems" :key="item.label">
|
||||
<div class="card-icon">{{ item.icon }}</div>
|
||||
<div class="card-content">
|
||||
<div class="card-value">{{ item.value }}</div>
|
||||
<div class="card-label">{{ item.label }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue';
|
||||
|
||||
const dataItems = ref([
|
||||
{ icon: '📚', label: '学习时长', value: '0小时' },
|
||||
{ icon: '✅', label: '完成任务', value: '0个' },
|
||||
{ icon: '⭐', label: '获得成就', value: '0个' },
|
||||
{ icon: '📖', label: '阅读文章', value: '0篇' }
|
||||
]);
|
||||
|
||||
onMounted(() => {
|
||||
// TODO: 加载用户学习数据
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.learning-data-overview {
|
||||
padding: 40px;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
color: #141F38;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.data-cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.data-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
background: #f5f5f5;
|
||||
border-radius: 8px;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.card-icon {
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.card-value {
|
||||
font-size: 28px;
|
||||
font-weight: 600;
|
||||
color: #C62828;
|
||||
}
|
||||
|
||||
.card-label {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
margin-top: 4px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
<template>
|
||||
<div class="navigation-bar">
|
||||
<div class="nav-item" v-for="item in navItems" :key="item.id" @click="navigate(item)">
|
||||
<div class="nav-icon">{{ item.icon }}</div>
|
||||
<div class="nav-label">{{ item.label }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const navItems = ref([
|
||||
{ id: 1, icon: '📚', label: '资源中心', path: '/resource-center' },
|
||||
{ id: 2, icon: '📝', label: '学习计划', path: '/study-plan' },
|
||||
{ id: 3, icon: '👤', label: '个人中心', path: '/user-center' },
|
||||
{ id: 4, icon: '🤖', label: 'AI助手', path: '/ai-assistant' }
|
||||
]);
|
||||
|
||||
function navigate(item: any) {
|
||||
router.push(item.path);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.navigation-bar {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 40px;
|
||||
padding: 40px;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
cursor: pointer;
|
||||
transition: transform 0.3s;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
}
|
||||
|
||||
.nav-icon {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
background: linear-gradient(135deg, #C62828, #E53935);
|
||||
border-radius: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.nav-label {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #141F38;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
<template>
|
||||
<div class="news-overview">
|
||||
<h2 class="section-title">新闻概览</h2>
|
||||
<div class="news-list">
|
||||
<div class="news-item" v-for="news in newsList" :key="news.id" @click="goToDetail(news)">
|
||||
<div class="news-image">
|
||||
<img :src="news.image" :alt="news.title" />
|
||||
</div>
|
||||
<div class="news-content">
|
||||
<h3>{{ news.title }}</h3>
|
||||
<p class="news-summary">{{ news.summary }}</p>
|
||||
<div class="news-meta">
|
||||
<span class="news-date">{{ news.publishDate }}</span>
|
||||
<span class="news-views">{{ news.views }} 阅读</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
const router = useRouter();
|
||||
const newsList = ref<any[]>([]);
|
||||
|
||||
onMounted(() => {
|
||||
// TODO: 加载新闻数据
|
||||
});
|
||||
|
||||
function goToDetail(news: any) {
|
||||
router.push(`/news/${news.id}`);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.news-overview {
|
||||
padding: 40px;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
color: #141F38;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.news-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.news-item {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
padding: 16px;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: background 0.3s;
|
||||
|
||||
&:hover {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
}
|
||||
|
||||
.news-image {
|
||||
width: 200px;
|
||||
height: 140px;
|
||||
flex-shrink: 0;
|
||||
background: #f5f5f5;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
.news-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
h3 {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #141F38;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.news-summary {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
margin-bottom: auto;
|
||||
}
|
||||
|
||||
.news-meta {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
margin-top: 12px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
<template>
|
||||
<div class="search-index">
|
||||
<h2 class="section-title">搜索索引</h2>
|
||||
<div class="search-tags">
|
||||
<span
|
||||
class="tag"
|
||||
v-for="tag in popularTags"
|
||||
:key="tag"
|
||||
@click="handleTagClick(tag)"
|
||||
>
|
||||
{{ tag }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const popularTags = ref([
|
||||
'红色思政', '党史学习', '新时代精神', '爱国主义',
|
||||
'社会主义核心价值观', '中国梦', '改革开放', '脱贫攻坚'
|
||||
]);
|
||||
|
||||
function handleTagClick(tag: string) {
|
||||
router.push(`/search?keyword=${encodeURIComponent(tag)}`);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.search-index {
|
||||
padding: 40px;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
color: #141F38;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.search-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tag {
|
||||
padding: 8px 16px;
|
||||
background: #f5f5f5;
|
||||
border-radius: 20px;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
|
||||
&:hover {
|
||||
background: #C62828;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
<template>
|
||||
<div class="top-music-recommend">
|
||||
<h2 class="section-title">TOP音乐推荐</h2>
|
||||
<div class="music-list">
|
||||
<div class="music-item" v-for="music in musicList" :key="music.id">
|
||||
<div class="music-cover">
|
||||
<img :src="music.cover" :alt="music.title" />
|
||||
<div class="play-btn">▶</div>
|
||||
</div>
|
||||
<div class="music-info">
|
||||
<h3>{{ music.title }}</h3>
|
||||
<p>{{ music.artist }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue';
|
||||
|
||||
const musicList = ref<any[]>([]);
|
||||
|
||||
onMounted(() => {
|
||||
// TODO: 加载音乐推荐数据
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.top-music-recommend {
|
||||
padding: 40px;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
color: #141F38;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.music-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.music-item {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.music-cover {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
aspect-ratio: 1;
|
||||
background: #f5f5f5;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.play-btn {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
background: rgba(198, 40, 40, 0.9);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
&:hover .play-btn {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.music-info {
|
||||
margin-top: 12px;
|
||||
|
||||
h3 {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #141F38;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -219,7 +219,7 @@ import {
|
||||
Clock,
|
||||
Download
|
||||
} from '@element-plus/icons-vue';
|
||||
import { ArticleShowView } from '@/views/article';
|
||||
import { ArticleShowView } from '@/views/public/article';
|
||||
import { courseApi } from '@/apis/study';
|
||||
import { learningRecordApi, learningHistoryApi } from '@/apis/study';
|
||||
import { resourceApi } from '@/apis/resource';
|
||||
@@ -10,7 +10,7 @@
|
||||
{
|
||||
path: '/editor',
|
||||
name: 'RichTextEditor',
|
||||
component: () => import('@/views/editor/RichTextEditorView.vue'),
|
||||
component: () => import('@/views/public/editor/RichTextEditorView.vue'),
|
||||
meta: {
|
||||
title: '富文本编辑器',
|
||||
requiresAuth: true
|
||||
@@ -54,7 +54,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import RichTextEditorView from '@/views/editor/RichTextEditorView.vue';
|
||||
import RichTextEditorView from '@/views/public/editor/RichTextEditorView.vue';
|
||||
</script>
|
||||
```
|
||||
|
||||
7
schoolNewsWeb/src/views/user/home/HomeView.vue
Normal file
7
schoolNewsWeb/src/views/user/home/HomeView.vue
Normal file
@@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<div class="home-view"></div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
@@ -18,7 +18,7 @@
|
||||
import { computed } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { FloatingSidebar } from '@/components/base';
|
||||
import { UserCard } from '@/views/user-center/components';
|
||||
import { UserCard } from '@/views/user/user-center/components';
|
||||
import { getParentChildrenRoutes } from '@/utils/routeUtils';
|
||||
import type { SysMenu } from '@/types/menu';
|
||||
|
||||
@@ -24,8 +24,8 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue';
|
||||
import { ArticleShowView } from '@/views/article';
|
||||
import { ResouceCollect, ResouceBottom } from '@/views/resource-center/components';
|
||||
import { ArticleShowView } from '@/views/public/article';
|
||||
import { ResouceCollect, ResouceBottom } from '@/views/user/resource-center/components';
|
||||
import { resourceApi } from '@/apis/resource';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import type { Resource } from '@/types/resource';
|
||||
@@ -101,7 +101,7 @@ import { ElMessage } from 'element-plus';
|
||||
import { Search, VideoPlay } from '@element-plus/icons-vue';
|
||||
import { courseApi } from '@/apis/study';
|
||||
import type { Course, PageParam } from '@/types';
|
||||
import { StudyPlanLayout } from '@/views/study-plan';
|
||||
import { StudyPlanLayout } from '@/views/user/study-plan';
|
||||
import defaultCover from '@/assets/imgs/default-course-bg.png'
|
||||
import { FILE_DOWNLOAD_URL } from '@/config';
|
||||
|
||||
@@ -155,7 +155,7 @@ async function loadCourseList() {
|
||||
|
||||
// 搜索
|
||||
function handleSearch() {
|
||||
pageParam.value.page = 1;
|
||||
pageParam.value.pageNumber = 1;
|
||||
loadCourseList();
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import { CourseDetail } from '@/views/course/components';
|
||||
import { CourseDetail } from '@/views/public/course/components';
|
||||
|
||||
defineOptions({
|
||||
name: 'CourseDetailView'
|
||||
@@ -12,7 +12,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import { CourseLearning } from '@/views/course/components';
|
||||
import { CourseLearning } from '@/views/public/course/components';
|
||||
|
||||
defineOptions({
|
||||
name: 'CourseStudyView'
|
||||
@@ -10,7 +10,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import { LearingTaskDetail } from '@/views/task';
|
||||
import { LearingTaskDetail } from '@/views/public/task';
|
||||
|
||||
defineOptions({
|
||||
name: 'LearningTaskDetailView'
|
||||
@@ -114,7 +114,7 @@ import { DocumentCopy, DocumentChecked } from '@element-plus/icons-vue';
|
||||
import { useStore } from 'vuex';
|
||||
import { learningTaskApi } from '@/apis/study';
|
||||
import type { LearningTask, TaskItemVO } from '@/types';
|
||||
import { StudyPlanLayout } from '@/views/study-plan';
|
||||
import { StudyPlanLayout } from '@/views/user/study-plan';
|
||||
|
||||
defineOptions({
|
||||
name: 'StudyTasksView'
|
||||
@@ -18,7 +18,7 @@
|
||||
import { computed } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { FloatingSidebar } from '@/components/base';
|
||||
import { UserCard } from '@/views/user-center/components';
|
||||
import { UserCard } from '@/views/user/user-center/components';
|
||||
import { getParentChildrenRoutes } from '@/utils/routeUtils';
|
||||
import type { SysMenu } from '@/types/menu';
|
||||
|
||||
Reference in New Issue
Block a user