feat:增加移动端布局
This commit is contained in:
@@ -220,49 +220,6 @@ const submitForm = async () => {
|
|||||||
.md-editor {
|
.md-editor {
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.generate-question {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 10px;
|
|
||||||
|
|
||||||
.generate-action {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.question-list {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 10px;
|
|
||||||
|
|
||||||
.question-card {
|
|
||||||
.el-card__body {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.question-title {
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.question-options {
|
|
||||||
padding-left: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.question-answer {
|
|
||||||
color: #67c23a;
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.question-action {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -15,9 +15,11 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-actions">
|
<div class="card-actions">
|
||||||
<el-button type="primary" :icon="Edit" @click="editClick(file)">编辑</el-button>
|
<el-button type="primary" v-if="!appStore.isMobile"
|
||||||
|
:icon="Edit" @click="editClick(file)">编辑</el-button>
|
||||||
<el-button type="primary" :icon="Aim" @click="trainClick(file)">训练</el-button>
|
<el-button type="primary" :icon="Aim" @click="trainClick(file)">训练</el-button>
|
||||||
<el-button type="danger" :icon="Delete">删除</el-button>
|
<el-button type="danger" v-if="!appStore.isMobile"
|
||||||
|
:icon="Delete">删除</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -30,6 +32,7 @@ import { Delete, Edit, Picture, Document, Aim } from '@element-plus/icons-vue'
|
|||||||
import { useLibraryStore } from '@/stores/library.ts'
|
import { useLibraryStore } from '@/stores/library.ts'
|
||||||
import { usePracticeStore } from '@/stores/practice.ts'
|
import { usePracticeStore } from '@/stores/practice.ts'
|
||||||
import { useQuestionStore } from '@/stores/question.ts'
|
import { useQuestionStore } from '@/stores/question.ts'
|
||||||
|
import { useAppStore } from '@/stores/app.ts'
|
||||||
import type { ILibraryFile } from '@/types/library.ts'
|
import type { ILibraryFile } from '@/types/library.ts'
|
||||||
import LibraryFilter from '@/components/Library/LibraryFilter.vue'
|
import LibraryFilter from '@/components/Library/LibraryFilter.vue'
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
@@ -40,6 +43,7 @@ import { ElLoading, ElMessage } from 'element-plus'
|
|||||||
const libraryStore = useLibraryStore()
|
const libraryStore = useLibraryStore()
|
||||||
const practiceStore = usePracticeStore()
|
const practiceStore = usePracticeStore()
|
||||||
const questionStore = useQuestionStore()
|
const questionStore = useQuestionStore()
|
||||||
|
const appStore = useAppStore()
|
||||||
|
|
||||||
const filterLibraryFileList = computed(() => {
|
const filterLibraryFileList = computed(() => {
|
||||||
const subject = libraryStore.selectedSubject
|
const subject = libraryStore.selectedSubject
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ const addMistakeList = async () => {
|
|||||||
gap: 16px;
|
gap: 16px;
|
||||||
|
|
||||||
.question-list {
|
.question-list {
|
||||||
padding: 10px;
|
padding: 0 10px;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
|
|
||||||
|
|||||||
16
src/layout/AppLogo.vue
Normal file
16
src/layout/AppLogo.vue
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<template>
|
||||||
|
<div class="logo" @click="refreshClick">
|
||||||
|
<span v-if="!appStore.isCollapse">🧠 智能学习助手</span>
|
||||||
|
<span v-else>🧠</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useAppStore } from '@/stores/app.ts'
|
||||||
|
|
||||||
|
const appStore = useAppStore()
|
||||||
|
|
||||||
|
const refreshClick = () => {
|
||||||
|
location.reload()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
35
src/layout/AppMenu.vue
Normal file
35
src/layout/AppMenu.vue
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<template>
|
||||||
|
<el-menu
|
||||||
|
:default-active="route.path"
|
||||||
|
:collapse="appStore.isCollapse"
|
||||||
|
:collapse-transition="false"
|
||||||
|
router
|
||||||
|
class="side-menu"
|
||||||
|
>
|
||||||
|
<el-menu-item index="/library">
|
||||||
|
<el-icon><Reading /></el-icon>
|
||||||
|
<template #title>知识库</template>
|
||||||
|
</el-menu-item>
|
||||||
|
<el-menu-item index="/exam">
|
||||||
|
<el-icon><EditPen /></el-icon>
|
||||||
|
<template #title>考试中心</template>
|
||||||
|
</el-menu-item>
|
||||||
|
<el-menu-item index="/mistakes">
|
||||||
|
<el-icon><Notebook /></el-icon>
|
||||||
|
<template #title>错题本</template>
|
||||||
|
</el-menu-item>
|
||||||
|
<el-menu-item index="/stats">
|
||||||
|
<el-icon><DataLine /></el-icon>
|
||||||
|
<template #title>数据统计</template>
|
||||||
|
</el-menu-item>
|
||||||
|
</el-menu>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { DataLine, EditPen, Notebook, Reading } from '@element-plus/icons-vue'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
import { useAppStore } from '@/stores/app.ts'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const appStore = useAppStore()
|
||||||
|
</script>
|
||||||
@@ -1,50 +1,29 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-container class="layout-container">
|
<el-container class="layout-container">
|
||||||
<!-- 侧边栏 -->
|
<el-drawer
|
||||||
<el-aside :width="isCollapse ? '64px' : '150px'" class="aside">
|
v-if="appStore.isMobile"
|
||||||
<!-- Logo -->
|
v-model="appStore.drawerVisible"
|
||||||
<div class="logo" @click="refreshClick">
|
direction="ltr"
|
||||||
<span v-if="!isCollapse">🧠 智能学习助手</span>
|
size="180px"
|
||||||
<span v-else>🧠</span>
|
:with-header="false"
|
||||||
</div>
|
>
|
||||||
|
<app-logo />
|
||||||
|
<app-menu />
|
||||||
|
</el-drawer>
|
||||||
|
|
||||||
<!-- 菜单 -->
|
<el-aside v-else :width="appStore.isCollapse ? '64px' : '150px'" class="aside">
|
||||||
<el-menu
|
<app-logo />
|
||||||
:default-active="route.path"
|
<app-menu />
|
||||||
:collapse="isCollapse"
|
|
||||||
:collapse-transition="false"
|
|
||||||
router
|
|
||||||
class="side-menu"
|
|
||||||
>
|
|
||||||
<el-menu-item index="/library">
|
|
||||||
<el-icon><Reading /></el-icon>
|
|
||||||
<template #title>知识库</template>
|
|
||||||
</el-menu-item>
|
|
||||||
<el-menu-item index="/exam">
|
|
||||||
<el-icon><EditPen /></el-icon>
|
|
||||||
<template #title>考试中心</template>
|
|
||||||
</el-menu-item>
|
|
||||||
<el-menu-item index="/mistakes">
|
|
||||||
<el-icon><Notebook /></el-icon>
|
|
||||||
<template #title>错题本</template>
|
|
||||||
</el-menu-item>
|
|
||||||
<el-menu-item index="/stats">
|
|
||||||
<el-icon><DataLine /></el-icon>
|
|
||||||
<template #title>数据统计</template>
|
|
||||||
</el-menu-item>
|
|
||||||
</el-menu>
|
|
||||||
</el-aside>
|
</el-aside>
|
||||||
|
|
||||||
<el-container>
|
<el-container>
|
||||||
<!-- 顶部栏 -->
|
<!-- 顶部栏 -->
|
||||||
<el-header class="header">
|
<el-header class="header">
|
||||||
<div class="header-left">
|
<div class="header-left">
|
||||||
<!-- 折叠按钮 -->
|
<el-icon class="collapse-btn" @click="collapseClick">
|
||||||
<el-icon class="collapse-btn" @click="isCollapse = !isCollapse">
|
<Fold v-if="!appStore.isCollapse" />
|
||||||
<Fold v-if="!isCollapse" />
|
|
||||||
<Expand v-else />
|
<Expand v-else />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
<!-- 面包屑 -->
|
|
||||||
<el-breadcrumb separator="/">
|
<el-breadcrumb separator="/">
|
||||||
<el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
|
<el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
|
||||||
<el-breadcrumb-item>{{ route.meta.title }}</el-breadcrumb-item>
|
<el-breadcrumb-item>{{ route.meta.title }}</el-breadcrumb-item>
|
||||||
@@ -52,13 +31,11 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<!-- 通知铃铛 -->
|
|
||||||
<el-badge :value="3">
|
<el-badge :value="3">
|
||||||
<el-icon :size="18"><Bell /></el-icon>
|
<el-icon :size="18"><Bell /></el-icon>
|
||||||
</el-badge>
|
</el-badge>
|
||||||
|
|
||||||
<!-- 用户信息 -->
|
<el-dropdown trigger="click">
|
||||||
<el-dropdown>
|
|
||||||
<el-avatar :size="32" class="avatar">潘</el-avatar>
|
<el-avatar :size="32" class="avatar">潘</el-avatar>
|
||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
<el-dropdown-menu>
|
<el-dropdown-menu>
|
||||||
@@ -84,16 +61,33 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue'
|
import AppLogo from '@/layout/AppLogo.vue'
|
||||||
|
import AppMenu from '@/layout/AppMenu.vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
|
import { useAppStore } from '@/stores/app.ts'
|
||||||
import { ElMessageBox } from 'element-plus'
|
import { ElMessageBox } from 'element-plus'
|
||||||
import { Reading, EditPen, Notebook, DataLine, Fold, Expand, Bell } from '@element-plus/icons-vue'
|
import { Fold, Expand, Bell } from '@element-plus/icons-vue'
|
||||||
|
import { onMounted, onUnmounted } from 'vue'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const isCollapse = ref(false)
|
const appStore = useAppStore()
|
||||||
|
|
||||||
const refreshClick = () => {
|
const checkScreen = () => {
|
||||||
location.reload()
|
appStore.isMobile = window.innerWidth < 768
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
checkScreen()
|
||||||
|
window.addEventListener('resize', checkScreen)
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener('resize', checkScreen)
|
||||||
|
})
|
||||||
|
|
||||||
|
const collapseClick = () => {
|
||||||
|
// appStore.isCollapse = !appStore.isCollapse
|
||||||
|
appStore.drawerVisible = !appStore.drawerVisible
|
||||||
}
|
}
|
||||||
|
|
||||||
// 退出登录
|
// 退出登录
|
||||||
@@ -116,21 +110,27 @@ const handleLogout = () => {
|
|||||||
border-right: 1px solid #e2e8f0;
|
border-right: 1px solid #e2e8f0;
|
||||||
transition: width 0.3s;
|
transition: width 0.3s;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
.logo {
|
.logo {
|
||||||
height: 56px;
|
height: 56px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #14b8a6;
|
color: #14b8a6;
|
||||||
border-bottom: 1px solid #e2e8f0;
|
border-bottom: 1px solid #e2e8f0;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.side-menu {
|
.side-menu {
|
||||||
border-right: none;
|
border-right: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-drawer {
|
||||||
|
.el-drawer__body {
|
||||||
|
padding: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
11
src/stores/app.ts
Normal file
11
src/stores/app.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
|
export const useAppStore = defineStore('app', {
|
||||||
|
state: () => ({
|
||||||
|
isMobile: false,
|
||||||
|
isCollapse: false,
|
||||||
|
drawerVisible: false
|
||||||
|
}),
|
||||||
|
actions: {
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
<div class="view-header">
|
<div class="view-header">
|
||||||
<div class="view-content">
|
<div class="view-content">
|
||||||
<h2 class="view-title">考试中心</h2>
|
<h2 class="view-title">考试中心</h2>
|
||||||
<p class="view-subtitle">按学科进行综合检验,模拟真实考试环境</p>
|
<p class="view-subtitle">模拟真实考试环境</p>
|
||||||
</div>
|
</div>
|
||||||
<el-button type="primary" :icon="Aim" @click="examClick">开始考试</el-button>
|
<el-button type="primary" :icon="Aim" @click="examClick">开始考试</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,9 +3,10 @@
|
|||||||
<div class="view-header">
|
<div class="view-header">
|
||||||
<div class="view-content">
|
<div class="view-content">
|
||||||
<h2 class="view-title">知识库</h2>
|
<h2 class="view-title">知识库</h2>
|
||||||
<p class="view-subtitle">管理学习资料,查看 AI 解析结果</p>
|
<p class="view-subtitle">管理学习资料</p>
|
||||||
</div>
|
</div>
|
||||||
<el-button type="primary" :icon="Plus" @click="addFileClick">新增资料</el-button>
|
<el-button v-if="!appStore.isMobile"
|
||||||
|
type="primary" :icon="Plus" @click="addFileClick">新增资料</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<library-file-list />
|
<library-file-list />
|
||||||
@@ -24,10 +25,12 @@ import LibraryFileDialog from '@/components/Library/LibraryFileDialog.vue'
|
|||||||
import LibraryFileTrainDialog from '@/components/Library/LibraryFileTrainDialog.vue'
|
import LibraryFileTrainDialog from '@/components/Library/LibraryFileTrainDialog.vue'
|
||||||
import LibraryFileContentDialog from '@/components/Library/LibraryFileContentDialog.vue'
|
import LibraryFileContentDialog from '@/components/Library/LibraryFileContentDialog.vue'
|
||||||
import { useLibraryStore } from '@/stores/library.ts'
|
import { useLibraryStore } from '@/stores/library.ts'
|
||||||
|
import { useAppStore } from '@/stores/app.ts'
|
||||||
import { Plus } from '@element-plus/icons-vue'
|
import { Plus } from '@element-plus/icons-vue'
|
||||||
import { onMounted } from 'vue'
|
import { onMounted } from 'vue'
|
||||||
|
|
||||||
const libraryStore = useLibraryStore()
|
const libraryStore = useLibraryStore()
|
||||||
|
const appStore = useAppStore()
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await libraryStore.queryFileList()
|
await libraryStore.queryFileList()
|
||||||
|
|||||||
Reference in New Issue
Block a user