<script setup>
import { onBeforeUnmount, ref, shallowRef } from 'vue'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import '@wangeditor/editor/dist/css/style.css'
import router from "@/router/index.js";
// 编辑器实例
const editorRef = shallowRef()
// 内容 HTML
const valueHtml = ref('')
// 编辑器配置
const editorConfig = {
placeholder: '请输入内容...',
MENU_CONF: {
insertImage: {
checkImage(src) {
if (src.indexOf("http") !== 0) {
return "图片网址必须以 http/https 开头";
}
return true;
},
},
}
}
// 工具栏配置
const toolbarConfig = {
toolbarKeys: [
'headerSelect', 'bold', 'italic', 'underline', 'through',
'color', 'bgColor', 'fontSize', 'fontFamily', 'lineHeight',
'bulletedList', 'numberedList', 'todo', 'justifyLeft', 'justifyRight',
'justifyCenter', 'insertLink', 'insertImage', 'insertTable',
'codeBlock', 'blockquote', 'divider', 'emotion', 'undo', 'redo'
]
}
// 历史记录相关状态
const showHistory = ref(false)
const historyItems = ref([
{ title: 'AI生成的学习笔记', date: '2023-10-15 14:30' },
{ title: '项目会议记录', date: '2023-10-14 09:45' },
{ title: '技术方案设计', date: '2023-10-12 16:20' },
{ title: '读书笔记 - 人工智能导论', date: '2023-10-10 11:15' },
{ title: '周计划安排', date: '2023-10-08 08:30' },
{ title: '周计划安排', date: '2023-10-08 08:30' },
{ title: '周计划安排', date: '2023-10-08 08:30' },
{ title: '周计划安排', date: '2023-10-08 08:30' },
{ title: '周计划安排', date: '2023-10-08 08:30' },
{ title: '周计划安排', date: '2023-10-08 08:30' },
{ title: '周计划安排', date: '2023-10-08 08:30' },
{ title: '周计划安排', date: '2023-10-08 08:30' }
])
// 编辑器回调函数
const handleCreated = (editor) => {
editorRef.value = editor
console.log("编辑器已创建", editor)
}
const handleChange = (editor) => {
console.log("内容变化:", editor.children)
}
const handleDestroyed = (editor) => {
console.log('编辑器已销毁', editor)
}
const handleFocus = (editor) => {
console.log('编辑器获得焦点', editor)
}
const handleBlur = (editor) => {
console.log('编辑器失去焦点', editor)
}
const customAlert = (info, type) => {
alert(`【系统提示】${type} - ${info}`)
}
const customPaste = (editor, event, callback) => {
console.log('粘贴事件', event)
callback(true) // 继续默认的粘贴行为
}
// 及时销毁编辑器
onBeforeUnmount(() => {
const editor = editorRef.value
if (editor == null) return
editor.destroy()
})
// 头部按钮功能
const handleBack = () => {
router.back() // 返回上一页
}
const toggleHistory = () => {
showHistory.value = !showHistory.value
}
const goToProfile = () => {
alert('跳转到个人用户管理界面')
}
</script>
<template>
<div class="editor-layout">
<!-- 固定头部 -->
<header class="app-header">
<button class="back-btn" @click="handleBack">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M19 12H5M12 19l-7-7 7-7"/>
</svg>
</button>
<h1 class="app-title">Edulens AI_<span class="gradient-text">AI笔记</span></h1>
<div class="header-right">
<button class="history-btn" @click="toggleHistory">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="10"></circle>
<polyline points="12 6 12 12 16 14"></polyline>
</svg>
<span>历史记录</span>
</button>
<div class="user-avatar" @click="goToProfile">
<div class="avatar-placeholder">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
<circle cx="12" cy="7" r="4"></circle>
</svg>
</div>
</div>
</div>
</header>
<!-- 固定工具栏 -->
<div class="fixed-toolbar">
<Toolbar
:editor="editorRef"
:defaultConfig="toolbarConfig"
/>
</div>
<!-- 编辑器区域 -->
<!-- <div class="page-container">-->
<div class="editor-container" >
<Editor
v-model="valueHtml"
:defaultConfig="editorConfig"
@onChange="handleChange"
@onCreated="handleCreated"
@onDestroyed="handleDestroyed"
@onFocus="handleFocus"
@onBlur="handleBlur"
@customAlert="customAlert"
@customPaste="customPaste"
/>
</div>
<!-- </div>-->
<!-- 历史记录侧边栏 -->
<div class="history-sidebar" :class="{ active: showHistory }">
<div class="sidebar-header">
<h2>历史记录</h2>
<button class="close-btn" @click="toggleHistory">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<line x1="18" y1="6" x2="6" y2="18"></line>
<line x1="6" y1="6" x2="18" y2="18"></line>
</svg>
</button>
</div>
<div class="history-list">
<div v-for="(item, index) in historyItems" :key="index" class="history-item">
<div class="history-title">{{ item.title }}</div>
<div class="history-date">{{ item.date }}</div>
</div>
</div>
</div>
<!-- 历史记录遮罩 -->
<div v-if="showHistory" class="sidebar-mask" @click="toggleHistory"></div>
</div>
</template>
<style>
/* 基础样式重置 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.editor-layout {
position: relative;
height: 100vh;
overflow: hidden;
background: linear-gradient(135deg, #f5f7fa 0%, #e4edf5 100%);
}
/* 固定头部样式 */
.app-header {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 60px;
display: flex;
align-items: center;
padding: 0 20px;
background: #ffffff;
box-shadow: 0 2px 15px rgba(0, 0, 0, 0.1);
z-index: 1000;
transition: all 0.3s ease;
}
.back-btn {
width: 40px;
height: 40px;
border: none;
background: none;
cursor: pointer;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s ease;
}
.back-btn:hover {
background: #f0f5ff;
transform: translateX(-2px);
}
.back-btn svg {
width: 20px;
height: 20px;
color: #4a6cf7;
}
.app-title {
flex: 1;
text-align: left;
font-size: 1.4rem;
font-weight: 600;
color: #1a1a1a;
letter-spacing: 0.5px;
}
.header-right {
display: flex;
align-items: center;
gap: 15px;
}
.history-btn {
display: flex;
align-items: center;
gap: 6px;
padding: 8px 15px;
background: #f0f5ff;
border: none;
border-radius: 20px;
color: #4a6cf7;
font-weight: 500;
font-size: 0.9rem;
cursor: pointer;
transition: all 0.2s ease;
}
.history-btn:hover {
background: #e1e9ff;
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(74, 108, 247, 0.2);
}
.history-btn svg {
width: 18px;
height: 18px;
}
.user-avatar {
width: 40px;
height: 40px;
border-radius: 50%;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 4px 10px rgba(118, 75, 162, 0.3);
}
.user-avatar:hover {
transform: scale(1.05);
box-shadow: 0 6px 15px rgba(118, 75, 162, 0.4);
}
.avatar-placeholder {
width: 32px;
height: 32px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
background: rgba(255, 255, 255, 0.2);
}
.avatar-placeholder svg {
width: 18px;
height: 18px;
color: white;
}
/* 固定工具栏样式 */
.fixed-toolbar {
position: fixed;
top: 60px; /* 在头部下方 */
left: 0;
right: 0;
z-index: 999;
background: white;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
border-bottom: 1px solid #eaeef5;
padding: 0 10px;
}
/* 编辑器容器样式 */
.editor-container {
margin-top: 110px; /* 头部高度 + 工具栏高度 */
height: calc(100vh - 150px);
/*overflow-y: auto;*/
padding: 20px;
background: white;
border-radius: 12px;
/*margin-left: 20px;
margin-right: 20px;*/
margin-left: auto;
margin-right: auto;
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.05);
width: 1000px; /* 固定宽度 */
}
/* 历史记录侧边栏 */
.history-sidebar {
position: fixed;
top: 0;
right: -400px;
width: 380px;
height: 100vh;
background: white;
z-index: 2000;
box-shadow: -5px 0 25px rgba(0, 0, 0, 0.1);
transition: right 0.4s cubic-bezier(0.23, 1, 0.32, 1);
display: flex;
flex-direction: column;
}
.history-sidebar.active {
right: 0;
}
.sidebar-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px;
border-bottom: 1px solid #eee;
}
.sidebar-header h2 {
color: #333;
font-weight: 600;
}编辑器里粘贴整段代码会放入代码块中,但不会自动换行
.close-btn {
background: none;
border: none;
width: 36px;
height: 36px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s ease;
}
.close-btn:hover {
background: #f5f7fa;
}
.close-btn svg {
width: 20px;
height: 20px;
color: #666;
}
.history-list {
flex: 1;
overflow-y: auto;
padding: 15px;
}
.history-item {
padding: 15px;
border-radius: 8px;
margin-bottom: 10px;
background: #f9fbfd;
transition: all 0.2s ease;
cursor: pointer;
border-left: 3px solid #4a6cf7;
}
.history-item:hover {
background: #edf3ff;
transform: translateX(5px);
}
.history-title {
font-weight: 500;
color: #1a1a1a;
margin-bottom: 5px;
}
.history-date {
font-size: 0.85rem;
color: #666;
}
/* 历史记录遮罩 */
.sidebar-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.4);
z-index: 1500;
backdrop-filter: blur(2px);
}
/* 响应式设计 */
@media (max-width: 768px) {
.app-header {
padding: 0 10px;
}
.app-title {
font-size: 1.1rem;
}
.history-btn span {
display: none;
}
.editor-container {
margin-left: 10px;
margin-right: 10px;
padding: 15px;
}
.history-sidebar {
width: 85%;
}
}
/* 滚动条美化 */
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 4px;
}
::-webkit-scrollbar-thumb {
background: #c1c1c1;
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: #a8a8a8;
}
.page-container {
overflow-y: auto; /* 启用页面级滚动 */
}
.gradient-text {
/* 渐变背景(与头像的渐变颜色保持一致) */
background: linear-gradient(135deg, #00ffe9 0%, #e70fff 100%);
/* 将背景裁剪为文字形状(兼容 Safari/Chrome) */
-webkit-background-clip: text;
background-clip: text;
/* 文字透明,显示背景渐变 */
color: transparent;
/* 可选:增强文字重量,让渐变更明显 */
font-weight: 700;
}
</style>
最新发布