commit
672415881c
File diff suppressed because it is too large
Load Diff
@ -1,72 +0,0 @@
|
|||||||
<template>
|
|
||||||
<ContentBase>
|
|
||||||
<div class="file-item" v-for="item in items" :key="item.name">
|
|
||||||
<div class="file-item">
|
|
||||||
<div @dblclick="handledouleclick(item.name,item.type)" class="file-item-icon">
|
|
||||||
<img v-if="item.type==='folder'" src="@/assets/folder_icon.png" alt="">
|
|
||||||
<img v-else src="@/assets/file_icon.png" alt="">
|
|
||||||
</div>
|
|
||||||
<div class="file-item-title">
|
|
||||||
{{ item.name }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ContentBase>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import ContentBase from './ContentBase';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'ContentShow',
|
|
||||||
components:{
|
|
||||||
ContentBase,
|
|
||||||
},
|
|
||||||
props:{
|
|
||||||
items:{
|
|
||||||
type:Object,
|
|
||||||
required:true,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
setup(props,context){
|
|
||||||
const handledouleclick = (name,type) => {
|
|
||||||
context.emit("handledoubleclick",name,type);
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
handledouleclick,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.file-item{
|
|
||||||
width: 8vw;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-item {
|
|
||||||
width: 8vw;
|
|
||||||
height: 8vw;
|
|
||||||
overflow: hidden;
|
|
||||||
display: inline-block;
|
|
||||||
margin: 0 0.1vw 1vw 0.1vw;
|
|
||||||
border: 0.1vh solid white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-item-icon > img {
|
|
||||||
width: 6vw;
|
|
||||||
height: 6vw;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.file-item-title{
|
|
||||||
width: 6vw;
|
|
||||||
height: 4vw;
|
|
||||||
text-align: center;
|
|
||||||
overflow: hidden;
|
|
||||||
font-size: 1.2vw;
|
|
||||||
word-break: break-all;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -0,0 +1,86 @@
|
|||||||
|
<template>
|
||||||
|
<div @click.stop="leftclick()" @dblclick="doubleclick()" @contextmenu.stop.prevent="rightclick($event)"
|
||||||
|
:class="{ 'file-item-selected' : item.is_selected }"
|
||||||
|
>
|
||||||
|
<div class="file-item-icon" >
|
||||||
|
<img src="@/assets/file_icon.png" alt="">
|
||||||
|
</div>
|
||||||
|
<FileTitle>
|
||||||
|
{{ item.name }}
|
||||||
|
</FileTitle>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import FileTitle from './FileTitle.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'FileFiled',
|
||||||
|
components:{
|
||||||
|
FileTitle,
|
||||||
|
},
|
||||||
|
props:{
|
||||||
|
item:{
|
||||||
|
type:Object,
|
||||||
|
required:true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setup(props,context){
|
||||||
|
const leftclick = () => {
|
||||||
|
context.emit('fileleftclick',props.item);
|
||||||
|
}
|
||||||
|
|
||||||
|
const rightclick = (event) => {
|
||||||
|
context.emit('filerightclick',{
|
||||||
|
item:props.item,
|
||||||
|
menuposition:{
|
||||||
|
top: event.clientY + 'px',
|
||||||
|
left: event.clientX + 'px',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const doubleclick = () => {
|
||||||
|
context.emit('open_file');
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
leftclick,
|
||||||
|
rightclick,
|
||||||
|
doubleclick,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.file-item {
|
||||||
|
width: 8vw;
|
||||||
|
height: 8vw;
|
||||||
|
overflow: hidden;
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0 0.1vw 1vw 0.1vw;
|
||||||
|
border: 0.1vh solid white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-item-icon > img {
|
||||||
|
width: 6vw;
|
||||||
|
height: 6vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.file-item-title{
|
||||||
|
width: 6vw;
|
||||||
|
height: 4vw;
|
||||||
|
text-align: center;
|
||||||
|
overflow: hidden;
|
||||||
|
font-size: 1.2vw;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-item-selected {
|
||||||
|
border: 0.1vh solid #99d1ff;
|
||||||
|
background: #cce8ff;
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,43 @@
|
|||||||
|
<template>
|
||||||
|
<div class="file-item-title">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'FileTitle',
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.file-item {
|
||||||
|
width: 8vw;
|
||||||
|
height: 8vw;
|
||||||
|
overflow: hidden;
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0 0.1vw 1vw 0.1vw;
|
||||||
|
border: 0.1vh solid white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-item-icon > img {
|
||||||
|
width: 6vw;
|
||||||
|
height: 6vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.file-item-title{
|
||||||
|
width: 6vw;
|
||||||
|
height: 4vw;
|
||||||
|
text-align: center;
|
||||||
|
overflow: hidden;
|
||||||
|
font-size: 1.2vw;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-item-selected {
|
||||||
|
border: 0.1vh solid #99d1ff;
|
||||||
|
background: #cce8ff;
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,90 @@
|
|||||||
|
<template>
|
||||||
|
<div @click.stop="leftclick()" @dblclick="doubleclick()" @contextmenu.stop.prevent="rightclick($event)"
|
||||||
|
:class="{ 'file-item-selected' : item.is_selected }"
|
||||||
|
>
|
||||||
|
<div class="file-item-icon" >
|
||||||
|
<img src="@/assets/folder_icon.png" alt="">
|
||||||
|
</div>
|
||||||
|
<FileTitle>
|
||||||
|
{{ item.name }}
|
||||||
|
</FileTitle>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import FileTitle from './FileTitle.vue';
|
||||||
|
import { useStore } from 'vuex';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'FolderFiled',
|
||||||
|
components:{
|
||||||
|
FileTitle,
|
||||||
|
},
|
||||||
|
props:{
|
||||||
|
item:{
|
||||||
|
type:Object,
|
||||||
|
required:true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setup(props,context){
|
||||||
|
let store = useStore();
|
||||||
|
|
||||||
|
const leftclick = () => {
|
||||||
|
context.emit('fileleftclick',props.item);
|
||||||
|
}
|
||||||
|
|
||||||
|
const rightclick = (event) => {
|
||||||
|
context.emit('filerightclick',{
|
||||||
|
item:props.item,
|
||||||
|
menuposition:{
|
||||||
|
top: event.clientY + 'px',
|
||||||
|
left: event.clientX + 'px',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const doubleclick = () => {
|
||||||
|
store.commit('forwardPath',props.item.name + '/');
|
||||||
|
context.emit('ls');
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
leftclick,
|
||||||
|
rightclick,
|
||||||
|
doubleclick,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.file-item {
|
||||||
|
width: 8vw;
|
||||||
|
height: 8vw;
|
||||||
|
overflow: hidden;
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0 0.1vw 1vw 0.1vw;
|
||||||
|
border: 0.1vh solid white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-item-icon > img {
|
||||||
|
width: 6vw;
|
||||||
|
height: 6vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.file-item-title{
|
||||||
|
width: 6vw;
|
||||||
|
height: 4vw;
|
||||||
|
text-align: center;
|
||||||
|
overflow: hidden;
|
||||||
|
font-size: 1.2vw;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-item-selected {
|
||||||
|
border: 0.1vh solid #99d1ff;
|
||||||
|
background: #cce8ff;
|
||||||
|
}
|
||||||
|
</style>
|
@ -1,21 +1,44 @@
|
|||||||
<template>
|
<template>
|
||||||
<nav class="nav">
|
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||||
<a class="nav-link" href="#">
|
<div class="container">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-left" viewBox="0 0 16 16">
|
<router-link class="navbar-brand" :to="{name: 'home'}">Coeditor</router-link>
|
||||||
<path fill-rule="evenodd" d="M15 8a.5.5 0 0 0-.5-.5H2.707l3.147-3.146a.5.5 0 1 0-.708-.708l-4 4a.5.5 0 0 0 0 .708l4 4a.5.5 0 0 0 .708-.708L2.707 8.5H14.5A.5.5 0 0 0 15 8z"/>
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarText" aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
</svg>
|
<span class="navbar-toggler-icon"></span>
|
||||||
</a>
|
</button>
|
||||||
<a class="nav-link" href="#">
|
<div class="collapse navbar-collapse" id="navbarText">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-right" viewBox="0 0 16 16">
|
<ul class="navbar-nav ms-auto" >
|
||||||
<path fill-rule="evenodd" d="M1 8a.5.5 0 0 1 .5-.5h11.793l-3.147-3.146a.5.5 0 0 1 .708-.708l4 4a.5.5 0 0 1 0 .708l-4 4a.5.5 0 0 1-.708-.708L13.293 8.5H1.5A.5.5 0 0 1 1 8z"/>
|
<li class="nav-item">
|
||||||
</svg>
|
<router-link v-if="!$store.state.user.is_login" class="nav-link" :to="{name: 'login'}">登录</router-link>
|
||||||
</a>
|
<router-link v-else class="nav-link" :to="{name: 'userprofile',params:{userId: $store.state.user.id}}">{{ $store.state.user.username }}</router-link>
|
||||||
</nav>
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<router-link v-if="!$store.state.user.is_login" class="nav-link" :to="{name: 'register'}">注册</router-link>
|
||||||
|
<a v-else class="nav-link" style="cursor: pointer" @click="logout">退出</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { useStore } from 'vuex';
|
||||||
|
import router from '../router/index.js';
|
||||||
|
|
||||||
export default ({
|
export default ({
|
||||||
name:"NavBar",
|
name:"NavBar",
|
||||||
|
setup(){
|
||||||
|
const store = useStore();
|
||||||
|
// store.commit('dd');
|
||||||
|
const logout = () => {
|
||||||
|
store.commit('logout');
|
||||||
|
router.push('/');
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
store,
|
||||||
|
logout,
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
@ -0,0 +1,37 @@
|
|||||||
|
<template>
|
||||||
|
<nav class="navbar navbar-expand-lg bg-body-tertiary">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="collapse navbar-collapse" id="navbarNav">
|
||||||
|
<ul class="navbar-nav col-md-10 offset-md-1">
|
||||||
|
<li v-for="(path,index) in $store.state.user.path" :key="index" @click="jump(index)" class="nav-item">
|
||||||
|
<a class="nav-link" href="#">{{ path }}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { useStore } from 'vuex'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'PathBar',
|
||||||
|
setup(props,context){
|
||||||
|
let store = useStore();
|
||||||
|
|
||||||
|
const jump = (index) => {
|
||||||
|
while (store.state.user.path.length > index + 1) {
|
||||||
|
store.commit('backPath');
|
||||||
|
}
|
||||||
|
context.emit('ls');
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
store,
|
||||||
|
jump,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
@ -0,0 +1,120 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<ul v-if="menutype === 'filemenu'" class="file-menu" :style="{top:menuposition.top,left:menuposition.left}">
|
||||||
|
<li>
|
||||||
|
<div @click="open_file">
|
||||||
|
打开
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<div>
|
||||||
|
复制
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<div>
|
||||||
|
粘贴
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<div>
|
||||||
|
重命名
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<div>
|
||||||
|
删除
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<ul v-else class="context-menu" :style="{top:menuposition.top,left:menuposition.left}">
|
||||||
|
<li>
|
||||||
|
<div>
|
||||||
|
新建
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<div>
|
||||||
|
刷新
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<div>
|
||||||
|
粘贴
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
// import { computed } from 'vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name:"RightMenu",
|
||||||
|
props:{
|
||||||
|
menuposition:{
|
||||||
|
type:Object,
|
||||||
|
required:true,
|
||||||
|
},
|
||||||
|
menutype:{
|
||||||
|
type:String,
|
||||||
|
required:true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setup(props,context){
|
||||||
|
const open_file = () => {
|
||||||
|
context.emit('open_file');
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
open_file,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.file-menu {
|
||||||
|
/* 菜单样式 */
|
||||||
|
background-color: pink;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
position: fixed;
|
||||||
|
padding: 0; /* 移除默认的内边距 */
|
||||||
|
margin: 0;
|
||||||
|
list-style: none;
|
||||||
|
/* 其他样式 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-menu > li {
|
||||||
|
padding: 10px; /* 添加内边距以确保内容不紧贴边框 */
|
||||||
|
text-align: left; /* 内容左对齐 */
|
||||||
|
width: 100%; /* 列表项占据父容器的全部宽度 */
|
||||||
|
box-sizing: border-box; /* 包含内边距和边框在内计算元素的总宽度 */
|
||||||
|
background-color: #f9f9f9; /* 可选:为列表项添加背景色 */
|
||||||
|
border-bottom: 1px solid #ddd; /* 可选:为列表项添加底部边框 */
|
||||||
|
cursor:pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.context-menu {
|
||||||
|
/* 菜单样式 */
|
||||||
|
background-color: pink;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
position: fixed;
|
||||||
|
padding: 0; /* 移除默认的内边距 */
|
||||||
|
margin: 0;
|
||||||
|
list-style: none;
|
||||||
|
/* 其他样式 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.context-menu > li {
|
||||||
|
padding: 10px; /* 添加内边距以确保内容不紧贴边框 */
|
||||||
|
text-align: left; /* 内容左对齐 */
|
||||||
|
width: 100%; /* 列表项占据父容器的全部宽度 */
|
||||||
|
box-sizing: border-box; /* 包含内边距和边框在内计算元素的总宽度 */
|
||||||
|
background-color: #f9f9f9; /* 可选:为列表项添加背景色 */
|
||||||
|
border-bottom: 1px solid #ddd; /* 可选:为列表项添加底部边框 */
|
||||||
|
cursor:pointer;
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,212 @@
|
|||||||
|
import {
|
||||||
|
Plugin,
|
||||||
|
ButtonView,
|
||||||
|
createDropdown,
|
||||||
|
Collection,
|
||||||
|
addListToDropdown,
|
||||||
|
} from 'ckeditor5';
|
||||||
|
import { asBlob } from 'html-docx-js-typescript'
|
||||||
|
import { saveAs } from 'file-saver';
|
||||||
|
import {
|
||||||
|
getStyle,
|
||||||
|
getPageContent
|
||||||
|
} from './utils';
|
||||||
|
|
||||||
|
// 导出为docx插件
|
||||||
|
class Export2Word extends Plugin {
|
||||||
|
init() {
|
||||||
|
const editor = this.editor;
|
||||||
|
|
||||||
|
editor.ui.componentFactory.add('ExportToWord', () => {
|
||||||
|
// The button will be an instance of ButtonView.
|
||||||
|
const button = new ButtonView();
|
||||||
|
|
||||||
|
button.set({
|
||||||
|
label: '导出为docx',
|
||||||
|
// withText: true
|
||||||
|
tooltip: true,
|
||||||
|
|
||||||
|
// 图标 直接插入svg文件
|
||||||
|
icon: '<svg t="1730216969869" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2612" width="200" height="200"><path d="M576 832H224c-17.6 0-32-14.4-32-32V192h608c17.6 0 32 14.4 32 32v288c0 17.7 14.3 32 32 32s32-14.3 32-32V224c0-52.9-43.1-96-96-96H160c-17.7 0-32 14.3-32 32v640c0 52.9 43.1 96 96 96h352c17.7 0 32-14.3 32-32s-14.3-32-32-32z" p-id="2613"></path><path d="M951.7 757.5c0.2-0.2 0.4-0.5 0.6-0.7 0.1-0.2 0.3-0.3 0.4-0.5 0.2-0.3 0.4-0.6 0.7-0.8 0.1-0.1 0.2-0.3 0.3-0.4l0.6-0.9c0.1-0.1 0.2-0.2 0.3-0.4l0.6-0.9c0.1-0.1 0.2-0.3 0.2-0.4 0.2-0.3 0.4-0.6 0.5-0.9 0.1-0.1 0.2-0.3 0.3-0.4 0.2-0.3 0.3-0.6 0.4-0.9 0.1-0.2 0.2-0.4 0.3-0.5 0.1-0.3 0.2-0.5 0.4-0.8 0.1-0.2 0.2-0.4 0.3-0.7 0.1-0.2 0.2-0.5 0.3-0.7 0.1-0.3 0.2-0.5 0.3-0.8 0.1-0.2 0.1-0.4 0.2-0.6l0.3-0.9c0.1-0.2 0.1-0.4 0.2-0.6 0.1-0.3 0.2-0.6 0.3-1 0-0.2 0.1-0.3 0.1-0.5 0.1-0.3 0.2-0.7 0.2-1 0-0.2 0.1-0.4 0.1-0.5 0.1-0.3 0.1-0.7 0.2-1 0-0.2 0.1-0.4 0.1-0.6 0-0.3 0.1-0.6 0.1-0.9 0-0.3 0-0.5 0.1-0.8 0-0.2 0-0.5 0.1-0.7 0.1-1.1 0.1-2.1 0-3.2 0-0.3 0-0.5-0.1-0.7 0-0.3 0-0.5-0.1-0.8 0-0.3-0.1-0.6-0.1-0.9 0-0.2 0-0.4-0.1-0.6 0-0.3-0.1-0.7-0.2-1 0-0.2-0.1-0.4-0.1-0.5-0.1-0.3-0.1-0.7-0.2-1 0-0.2-0.1-0.3-0.1-0.5-0.1-0.3-0.2-0.6-0.3-1-0.1-0.2-0.1-0.4-0.2-0.6l-0.3-0.9c-0.1-0.2-0.1-0.4-0.2-0.6-0.1-0.3-0.2-0.5-0.3-0.8-0.1-0.2-0.2-0.5-0.3-0.7-0.1-0.2-0.2-0.4-0.3-0.7-0.1-0.3-0.2-0.5-0.4-0.8-0.1-0.2-0.2-0.4-0.3-0.5-0.1-0.3-0.3-0.6-0.4-0.9-0.1-0.2-0.2-0.3-0.3-0.4-0.2-0.3-0.3-0.6-0.5-0.9-0.1-0.1-0.2-0.3-0.2-0.4l-0.6-0.9c-0.1-0.1-0.2-0.2-0.3-0.4l-0.6-0.9c-0.1-0.1-0.2-0.3-0.3-0.4-0.2-0.3-0.4-0.6-0.7-0.8-0.1-0.2-0.3-0.3-0.4-0.5-0.2-0.2-0.4-0.5-0.6-0.7-0.2-0.2-0.4-0.5-0.7-0.7l-0.4-0.4-128-128c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l73.4 73.4H592c-17.7 0-32 14.3-32 32s14.3 32 32 32h258.7l-73.4 73.4c-12.5 12.5-12.5 32.8 0 45.3 6.2 6.2 14.4 9.4 22.6 9.4s16.4-3.1 22.6-9.4l128-128 0.4-0.4c0.4-0.6 0.6-0.9 0.8-1.1z" p-id="2614"></path><path d="M709.3 320.4c-17.4-2.9-33.9 8.9-36.8 26.3l-42.8 256.9-87.3-261.8c-4.4-13-16.6-21.8-30.4-21.8-13.8 0-26 8.8-30.4 21.9l-87.3 261.8-42.8-256.9c-2.9-17.4-19.4-29.2-36.8-26.3-17.4 2.9-29.2 19.4-26.3 36.8l64 384c2.4 14.5 14.4 25.5 29.1 26.6 14.7 1.1 28.2-7.8 32.9-21.8L512 453.2l66.4 199.1c3.9 11.8 15 19.8 27.4 19.8h52.9c14.1 0 26.2-10.2 28.5-24.1l48.4-290.6c2.9-17.6-8.9-34.1-26.3-37z" p-id="2615"></path></svg>'
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// Execute a callback function when the button is clicked
|
||||||
|
button.on('execute', () => {
|
||||||
|
const pageContent = getPageContent();
|
||||||
|
const style = getStyle();
|
||||||
|
const page = '<!DOCTYPE html><html><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><style>@media print{@page{size:A4 portrait;margin:0cm 3cm}@page:left{margin-left:2.5cm;margin-right:2.7cm;}@page:right{margin-left:2.7cm;margin-right:2.5cm;}}</style>' + style + '</head><body>' + pageContent + '</body></html>'
|
||||||
|
|
||||||
|
// console.log(page);
|
||||||
|
asBlob(page).then(data => {
|
||||||
|
saveAs(data, 'file.docx') // save as docx file
|
||||||
|
}); // asBlob() return Promise<Blob|Buffer>
|
||||||
|
});
|
||||||
|
|
||||||
|
return button;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 增加菜单栏? 不显示按钮
|
||||||
|
// editor.ui.extendMenuBar({
|
||||||
|
// menu: {
|
||||||
|
// menuId: 'export',
|
||||||
|
// label: '导出',
|
||||||
|
// groups: [
|
||||||
|
// {
|
||||||
|
// groupId: 'export',
|
||||||
|
// items: [
|
||||||
|
// 'ExportToWord'
|
||||||
|
// ]
|
||||||
|
// }
|
||||||
|
// ]
|
||||||
|
// },
|
||||||
|
// position: 'after:help'
|
||||||
|
// }
|
||||||
|
// );
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 导出为PDF插件
|
||||||
|
class Export2PDF extends Plugin {
|
||||||
|
init() {
|
||||||
|
const editor = this.editor;
|
||||||
|
|
||||||
|
editor.ui.componentFactory.add('ExportToPDF', () => {
|
||||||
|
// The button will be an instance of ButtonView.
|
||||||
|
const button = new ButtonView();
|
||||||
|
|
||||||
|
button.set({
|
||||||
|
label: '导出为PDF',
|
||||||
|
// withText: true
|
||||||
|
tooltip: true,
|
||||||
|
|
||||||
|
// 图标 直接插入svg文件
|
||||||
|
icon: '<svg t="1730309942693" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2589" width="200" height="200"><path d="M576 832H224c-17.6 0-32-14.4-32-32V192h608c17.6 0 32 14.4 32 32v288c0 17.7 14.3 32 32 32s32-14.3 32-32V224c0-52.9-43.1-96-96-96H160c-17.7 0-32 14.3-32 32v640c0 52.9 43.1 96 96 96h352c17.7 0 32-14.3 32-32s-14.3-32-32-32z" p-id="2590"></path><path d="M960 734.4c0-0.3 0-0.5-0.1-0.7 0-0.3 0-0.5-0.1-0.8 0-0.3-0.1-0.6-0.1-0.9 0-0.2 0-0.4-0.1-0.6 0-0.3-0.1-0.7-0.2-1 0-0.2-0.1-0.4-0.1-0.5-0.1-0.3-0.1-0.7-0.2-1 0-0.2-0.1-0.3-0.1-0.5-0.1-0.3-0.2-0.6-0.3-1-0.1-0.2-0.1-0.4-0.2-0.6l-0.3-0.9c-0.1-0.2-0.1-0.4-0.2-0.6-0.1-0.3-0.2-0.5-0.3-0.8-0.1-0.2-0.2-0.5-0.3-0.7-0.1-0.2-0.2-0.4-0.3-0.7-0.1-0.3-0.2-0.5-0.4-0.8-0.1-0.2-0.2-0.4-0.3-0.5-0.1-0.3-0.3-0.6-0.4-0.9-0.1-0.2-0.2-0.3-0.3-0.4-0.2-0.3-0.3-0.6-0.5-0.9-0.1-0.1-0.2-0.3-0.2-0.4l-0.6-0.9c-0.1-0.1-0.2-0.2-0.3-0.4l-0.6-0.9c-0.1-0.1-0.2-0.3-0.3-0.4-0.2-0.3-0.4-0.6-0.7-0.8-0.1-0.2-0.3-0.3-0.4-0.5-0.2-0.2-0.4-0.5-0.6-0.7-0.2-0.2-0.4-0.5-0.7-0.7l-0.4-0.4-128-128c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l73.4 73.4H592c-17.7 0-32 14.3-32 32s14.3 32 32 32h258.7l-73.4 73.4c-12.5 12.5-12.5 32.8 0 45.3 6.2 6.2 14.4 9.4 22.6 9.4s16.4-3.1 22.6-9.4l128-128 0.4-0.4 0.7-0.7c0.2-0.2 0.4-0.5 0.6-0.7 0.1-0.2 0.3-0.3 0.4-0.5 0.2-0.3 0.4-0.6 0.7-0.8 0.1-0.1 0.2-0.3 0.3-0.4l0.6-0.9c0.1-0.1 0.2-0.2 0.3-0.4l0.6-0.9c0.1-0.1 0.2-0.3 0.2-0.4 0.2-0.3 0.4-0.6 0.5-0.9 0.1-0.1 0.2-0.3 0.3-0.4 0.2-0.3 0.3-0.6 0.4-0.9 0.1-0.2 0.2-0.4 0.3-0.5 0.1-0.3 0.2-0.5 0.4-0.8 0.1-0.2 0.2-0.4 0.3-0.7 0.1-0.2 0.2-0.5 0.3-0.7 0.1-0.3 0.2-0.5 0.3-0.8 0.1-0.2 0.1-0.4 0.2-0.6l0.3-0.9c0.1-0.2 0.1-0.4 0.2-0.6 0.1-0.3 0.2-0.6 0.3-1 0-0.2 0.1-0.3 0.1-0.5 0.1-0.3 0.2-0.7 0.2-1 0-0.2 0.1-0.4 0.1-0.5 0.1-0.3 0.1-0.7 0.2-1 0-0.2 0.1-0.4 0.1-0.6 0-0.3 0.1-0.6 0.1-0.9 0-0.3 0-0.5 0.1-0.8 0-0.2 0-0.5 0.1-0.7-0.1-1.5-0.1-2.5-0.1-3.6zM352 320c-17.7 0-32 14.3-32 32v384c0 17.7 14.3 32 32 32s32-14.3 32-32V576h192c70.6 0 128-57.4 128-128s-57.4-128-128-128H352z m288 128c0 35.3-28.7 64-64 64H384V384h192c35.3 0 64 28.7 64 64z" p-id="2591"></path></svg>'
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// Execute a callback function when the button is clicked
|
||||||
|
button.on('execute', () => {
|
||||||
|
const pageContent = getPageContent();
|
||||||
|
console.log(pageContent);
|
||||||
|
const style = getStyle();
|
||||||
|
// 去掉element中的 ck-focused ck-weight_selected消除页面和图片的蓝边
|
||||||
|
const page = '<!DOCTYPE html><html><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><style>@media print{@page{size:A4 portrait;margin:0cm 3cm}@page:left{margin-left:2.5cm;margin-right:2.7cm;}@page:right{margin-left:2.7cm;margin-right:2.5cm;}}</style>' + style + '</head><body>' + pageContent.replaceAll('ck-focused', 'ck-blurred').replaceAll('ck-weight_selected', '') + '</body></html>'
|
||||||
|
const newWindow = window.open('', 'PrintDocument', 'height=600,width=700,top=50,left=50');
|
||||||
|
newWindow.document.write(page);
|
||||||
|
newWindow.document.close();
|
||||||
|
newWindow.print();
|
||||||
|
newWindow.onafterprint = function () {
|
||||||
|
newWindow.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return button;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 智能润色插件
|
||||||
|
class Translation extends Plugin {
|
||||||
|
init() {
|
||||||
|
// console.log('Translation initialized!');
|
||||||
|
|
||||||
|
this.editor.ui.componentFactory.add('translate', (locale) => {
|
||||||
|
const dropdownView = createDropdown(locale);
|
||||||
|
dropdownView.buttonView.set({
|
||||||
|
label: '智能助手',
|
||||||
|
withText: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const items = new Collection();
|
||||||
|
items.add({
|
||||||
|
type: 'button',
|
||||||
|
model: {
|
||||||
|
id: 'summary',
|
||||||
|
withText: true,
|
||||||
|
label: '摘要',
|
||||||
|
}
|
||||||
|
});
|
||||||
|
items.add({
|
||||||
|
type: 'button',
|
||||||
|
model: {
|
||||||
|
id: 'decoration',
|
||||||
|
withText: true,
|
||||||
|
label: '润色'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
items.add({
|
||||||
|
type: 'button',
|
||||||
|
model: {
|
||||||
|
id: 'extension',
|
||||||
|
withText: true,
|
||||||
|
label: '续写'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
items.add({
|
||||||
|
type: 'button',
|
||||||
|
model: {
|
||||||
|
id: 'correction',
|
||||||
|
withText: true,
|
||||||
|
label: '修改'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
items.add({
|
||||||
|
type: 'button',
|
||||||
|
model: {
|
||||||
|
id: 'translation',
|
||||||
|
withText: true,
|
||||||
|
label: '翻译'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
addListToDropdown(dropdownView, items);
|
||||||
|
|
||||||
|
dropdownView.on('execute', (eventInfo) => {
|
||||||
|
const { id, label } = eventInfo.source;
|
||||||
|
// 获取选中的文本,用来进行后续操作
|
||||||
|
const selectionText = window.getSelection().toString();
|
||||||
|
if (id === 'summary') {
|
||||||
|
// this.editor.execute('ExportToWord');
|
||||||
|
console.log('Object (en):', label, selectionText);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return dropdownView;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 侧边栏
|
||||||
|
class ToggleSideBar extends Plugin{
|
||||||
|
// constructor(toggleSidebar) {
|
||||||
|
// super();
|
||||||
|
// this.toggleSidebar = toggleSidebar;
|
||||||
|
// }
|
||||||
|
init() {
|
||||||
|
const editor = this.editor;
|
||||||
|
|
||||||
|
editor.ui.componentFactory.add('SideBar', () => {
|
||||||
|
// The button will be an instance of ButtonView.
|
||||||
|
const button = new ButtonView();
|
||||||
|
|
||||||
|
button.set({
|
||||||
|
label: '侧边栏',
|
||||||
|
// withText: true
|
||||||
|
tooltip: true,
|
||||||
|
// 图标 直接插入svg文件
|
||||||
|
icon: '<svg t="1730903613822" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2614" width="200" height="200"><path d="M849.92 981.333333H175.658667A132.736 132.736 0 0 1 42.666667 848.597333V175.36A132.736 132.736 0 0 1 175.658667 42.666667H849.92C921.898667 42.666667 981.333333 102.016 981.333333 175.402667v673.152C981.333333 922.026667 921.898667 981.333333 849.92 981.333333zM175.658667 136.362667a38.698667 38.698667 0 0 0-39.125334 39.04v673.152c0 21.888 17.194667 39.082667 39.125334 39.082666H849.92a38.698667 38.698667 0 0 0 39.125333-39.04V175.36c-1.578667-21.845333-18.773333-39.04-39.125333-39.04H175.658667z" fill="#172B4D" p-id="2615"></path><path d="M374.314667 957.866667a46.08 46.08 0 0 1-46.933334-46.805334V111.36a46.08 46.08 0 0 1 46.933334-46.890667c26.624 0 46.933333 20.309333 46.933333 46.890667v799.658667a46.08 46.08 0 0 1-46.933333 46.848z" fill="#172B4D" p-id="2616"></path></svg>'
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// Execute a callback function when the button is clicked
|
||||||
|
button.on('execute', () => {
|
||||||
|
// 打开sidebar
|
||||||
|
const bt = document.getElementById("toggleSidebarButton");
|
||||||
|
console.log(bt);
|
||||||
|
bt.click();
|
||||||
|
});
|
||||||
|
|
||||||
|
return button;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Export2Word, Export2PDF, Translation, ToggleSideBar };
|
@ -1,6 +1,13 @@
|
|||||||
import { createApp } from 'vue'
|
import { createApp } from 'vue'
|
||||||
|
import './public/style.css';
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
import store from './store'
|
import store from './store'
|
||||||
import router from './router'
|
import router from './router'
|
||||||
|
import { CkeditorPlugin } from '@ckeditor/ckeditor5-vue';
|
||||||
|
|
||||||
createApp(App).use(router).use(store).mount('#app')
|
const app = createApp(App);
|
||||||
|
|
||||||
|
app.use(router);
|
||||||
|
app.use(store);
|
||||||
|
app.use(CkeditorPlugin);
|
||||||
|
app.mount('#app');
|
After Width: | Height: | Size: 4.2 KiB |
@ -0,0 +1,17 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||||
|
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||||
|
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>
|
||||||
|
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||||
|
</noscript>
|
||||||
|
<div id="app"></div>
|
||||||
|
<!-- built files will be auto injected -->
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,281 @@
|
|||||||
|
@import url('https://fonts.googleapis.com/css2?family=Oswald&family=PT+Serif:ital,wght@0,400;0,700;1,400&display=swap');
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,400;0,700;1,400;1,700&display=swap');
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
body {
|
||||||
|
margin: 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-container {
|
||||||
|
--ckeditor5-preview-height: 650px;
|
||||||
|
position: relative;
|
||||||
|
font-family: '宋体';
|
||||||
|
width: fit-content;
|
||||||
|
height: 100%;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ck-content {
|
||||||
|
font-family: '宋体';
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 1.6;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-container__editor-wrapper {
|
||||||
|
display: flex;
|
||||||
|
width: fit-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-container_document-editor {
|
||||||
|
border: 1px solid var(--ck-color-base-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-container_document-editor .editor-container__toolbar {
|
||||||
|
display: flex;
|
||||||
|
position: relative;
|
||||||
|
box-shadow: 0 2px 3px hsla(0, 0%, 0%, 0.078);
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-container_document-editor .editor-container__toolbar>.ck.ck-toolbar {
|
||||||
|
flex-grow: 1;
|
||||||
|
width: 0;
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
border-top: 0;
|
||||||
|
border-left: 0;
|
||||||
|
border-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-container_document-editor .editor-container__menu-bar>.ck.ck-menu-bar {
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
border-top: 0;
|
||||||
|
border-left: 0;
|
||||||
|
border-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-container_document-editor .editor-container__editor-wrapper {
|
||||||
|
max-height: var(--ckeditor5-preview-height);
|
||||||
|
min-height: var(--ckeditor5-preview-height);
|
||||||
|
overflow-y: scroll;
|
||||||
|
background: var(--ck-color-base-foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-container_document-editor .editor-container__editor {
|
||||||
|
margin-top: 28px;
|
||||||
|
margin-bottom: 28px;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-container_document-editor .editor-container__editor .ck.ck-editor__editable {
|
||||||
|
box-sizing: border-box;
|
||||||
|
min-width: calc(210mm + 2px);
|
||||||
|
max-width: calc(210mm + 2px);
|
||||||
|
min-height: 297mm;
|
||||||
|
height: fit-content;
|
||||||
|
padding: 20mm 12mm;
|
||||||
|
border: 1px hsl(0, 0%, 82.7%) solid;
|
||||||
|
background: hsl(0, 0%, 100%);
|
||||||
|
box-shadow: 0 2px 3px hsla(0, 0%, 0%, 0.078);
|
||||||
|
flex: 1 1 auto;
|
||||||
|
margin-left: 72px;
|
||||||
|
margin-right: 72px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*侧边栏出现时其他右移*/
|
||||||
|
.main-container.sidebar-open .editor-container__menu-bar,
|
||||||
|
.main-container.sidebar-open .editor-container__toolbar,
|
||||||
|
.main-container.sidebar-open .editor-container__editor-wrapper {
|
||||||
|
transition: margin-left 0.3s ease;
|
||||||
|
margin-left: 350px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*用户定义的style插件样式*/
|
||||||
|
.ck-content h3.category {
|
||||||
|
font-family: 'Oswald';
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #555;
|
||||||
|
letter-spacing: 10px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ck-content h2.document-title {
|
||||||
|
font-family: 'Oswald';
|
||||||
|
font-size: 50px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ck-content h3.document-subtitle {
|
||||||
|
font-family: 'Oswald';
|
||||||
|
font-size: 20px;
|
||||||
|
color: #555;
|
||||||
|
margin: 0 0 1em;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ck-content p.info-box {
|
||||||
|
--background-size: 30px;
|
||||||
|
--background-color: #e91e63;
|
||||||
|
padding: 1.2em 2em;
|
||||||
|
border: 1px solid var(--background-color);
|
||||||
|
background: linear-gradient(135deg,
|
||||||
|
var(--background-color) 0%,
|
||||||
|
var(--background-color) var(--background-size),
|
||||||
|
transparent var(--background-size)),
|
||||||
|
linear-gradient(135deg,
|
||||||
|
transparent calc(100% - var(--background-size)),
|
||||||
|
var(--background-color) calc(100% - var(--background-size)),
|
||||||
|
var(--background-color));
|
||||||
|
border-radius: 10px;
|
||||||
|
margin: 1.5em 2em;
|
||||||
|
box-shadow: 5px 5px 0 #ffe6ef;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ck-content blockquote.side-quote {
|
||||||
|
font-family: 'Oswald';
|
||||||
|
font-style: normal;
|
||||||
|
float: right;
|
||||||
|
width: 35%;
|
||||||
|
position: relative;
|
||||||
|
border: 0;
|
||||||
|
overflow: visible;
|
||||||
|
z-index: 1;
|
||||||
|
margin-left: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ck-content blockquote.side-quote::before {
|
||||||
|
content: '“';
|
||||||
|
position: absolute;
|
||||||
|
top: -37px;
|
||||||
|
left: -10px;
|
||||||
|
display: block;
|
||||||
|
font-size: 200px;
|
||||||
|
color: #e7e7e7;
|
||||||
|
z-index: -1;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ck-content blockquote.side-quote p {
|
||||||
|
font-size: 2em;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ck-content blockquote.side-quote p:last-child:not(:first-child) {
|
||||||
|
font-size: 1.3em;
|
||||||
|
text-align: right;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ck-content span.marker {
|
||||||
|
background: yellow;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ck-content span.spoiler {
|
||||||
|
background: #000;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ck-content span.spoiler:hover {
|
||||||
|
background: #000;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ck-content pre.fancy-code {
|
||||||
|
border: 0;
|
||||||
|
margin-left: 2em;
|
||||||
|
margin-right: 2em;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ck-content pre.fancy-code::before {
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
height: 13px;
|
||||||
|
background: url(data:image/svg+xml;base64,PHN2ZyBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1NCAxMyI+CiAgPGNpcmNsZSBjeD0iNi41IiBjeT0iNi41IiByPSI2LjUiIGZpbGw9IiNGMzZCNUMiLz4KICA8Y2lyY2xlIGN4PSIyNi41IiBjeT0iNi41IiByPSI2LjUiIGZpbGw9IiNGOUJFNEQiLz4KICA8Y2lyY2xlIGN4PSI0Ny41IiBjeT0iNi41IiByPSI2LjUiIGZpbGw9IiM1NkM0NTMiLz4KPC9zdmc+Cg==);
|
||||||
|
margin-bottom: 8px;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ck-content pre.fancy-code-dark {
|
||||||
|
background: #272822;
|
||||||
|
color: #fff;
|
||||||
|
box-shadow: 5px 5px 0 #0000001f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ck-content pre.fancy-code-bright {
|
||||||
|
background: #dddfe0;
|
||||||
|
color: #000;
|
||||||
|
box-shadow: 5px 5px 0 #b3b3b3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ck-content p.gradientborder {
|
||||||
|
--borderWidth: 12px;
|
||||||
|
--bRadius: 5px;
|
||||||
|
width: 60%;
|
||||||
|
height: 60%;
|
||||||
|
position: relative;
|
||||||
|
z-index: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 2rem;
|
||||||
|
z-index: 0;
|
||||||
|
border-radius: --bRadius;
|
||||||
|
|
||||||
|
&::after,
|
||||||
|
&::before {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: -50%;
|
||||||
|
top: -50%;
|
||||||
|
width: 200%;
|
||||||
|
height: 200%;
|
||||||
|
z-index: -2;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: 50% 50%, 50% 50%;
|
||||||
|
background-position: 0 0, 100% 0, 100% 100%, 0 100%;
|
||||||
|
background-image: linear-gradient(#399953, #399953), linear-gradient(#fbb300, #fbb300), linear-gradient(#d53e33, #d53e33), linear-gradient(#377af5, #377af5);
|
||||||
|
animation: rotate 4s linear infinite;
|
||||||
|
|
||||||
|
@keyframes rotate {
|
||||||
|
100% {
|
||||||
|
transform: rotate(1turn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
z-index: -1;
|
||||||
|
left: calc(var(--borderWidth) / 2);
|
||||||
|
top: calc(var(--borderWidth) / 2);
|
||||||
|
width: calc(100% - var(--borderWidth));
|
||||||
|
height: calc(100% - var(--borderWidth));
|
||||||
|
background: white;
|
||||||
|
border-radius: --bRadius;
|
||||||
|
/* 这一行是为了方便查看原来的样子的 */
|
||||||
|
animation: opacityChange 3s infinite alternate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes opacityChange {
|
||||||
|
50% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
opacity: .5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,73 @@
|
|||||||
|
<template>
|
||||||
|
<ContentBase>
|
||||||
|
<div class="row justify-content-md-center">
|
||||||
|
<div class="col-3">
|
||||||
|
<form @submit.prevent="login">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="username" class="form-label">用户名</label>
|
||||||
|
<input v-model="username" type="text" class="form-control" id="username">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="password" class="form-label">密码</label>
|
||||||
|
<input v-model="password" type="password" class="form-control" id="password">
|
||||||
|
</div>
|
||||||
|
<div class="error-message">{{ error_message }}</div>
|
||||||
|
<button type="submit" class="btn btn-primary">登录</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ContentBase>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ContentBase from '../components/ContentBase';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useStore } from 'vuex';
|
||||||
|
import router from '@/router/index.js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'LoginView',
|
||||||
|
components: {
|
||||||
|
ContentBase,
|
||||||
|
},
|
||||||
|
setup(){
|
||||||
|
let store = useStore();
|
||||||
|
let username = ref('123');
|
||||||
|
let password = ref('123');
|
||||||
|
let error_message = ref('');
|
||||||
|
|
||||||
|
const login = () => {
|
||||||
|
// http://47.106.113.194:8000/token/
|
||||||
|
store.dispatch('login',{
|
||||||
|
username:username.value,
|
||||||
|
password:password.value,
|
||||||
|
success(){
|
||||||
|
error_message.value = '';
|
||||||
|
router.push({name:'home'});
|
||||||
|
},
|
||||||
|
error(){
|
||||||
|
error_message.value = "用户名或密码错误";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
error_message,
|
||||||
|
login,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
button {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-message {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,19 @@
|
|||||||
|
<template>
|
||||||
|
<ContentBase>
|
||||||
|
404
|
||||||
|
</ContentBase>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ContentBase from '../components/ContentBase';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'NotFoundView',
|
||||||
|
components: {
|
||||||
|
ContentBase,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
</style>
|
@ -0,0 +1,96 @@
|
|||||||
|
<template>
|
||||||
|
<ContentBase>
|
||||||
|
<div class="row justify-content-md-center">
|
||||||
|
<div class="col-3">
|
||||||
|
<form @submit.prevent="register">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="username" class="form-label">用户名</label>
|
||||||
|
<input v-model="username" type="text" class="form-control" id="username">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="password" class="form-label">密码</label>
|
||||||
|
<input v-model="password" type="password" class="form-control" id="password">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="password" class="form-label">确认密码</label>
|
||||||
|
<input v-model="password_confirm" type="password" class="form-control" id="password_confirm">
|
||||||
|
</div>
|
||||||
|
<div class="error-message">{{ error_message }}</div>
|
||||||
|
<button type="submit" class="btn btn-primary">注册</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ContentBase>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ContentBase from '../components/ContentBase';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useStore } from 'vuex';
|
||||||
|
import $ from 'jquery';
|
||||||
|
import router from '@/router/index.js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'RegisterView',
|
||||||
|
components: {
|
||||||
|
ContentBase,
|
||||||
|
},
|
||||||
|
setup(){
|
||||||
|
let store = useStore();
|
||||||
|
let username = ref('');
|
||||||
|
let password = ref('');
|
||||||
|
let password_confirm = ref('');
|
||||||
|
let error_message = '';
|
||||||
|
|
||||||
|
const register = () => {
|
||||||
|
$.ajax({
|
||||||
|
url:'http://47.106.113.194:8000/register/',
|
||||||
|
type:'post',
|
||||||
|
data:{
|
||||||
|
username:username.value,
|
||||||
|
password:password.value,
|
||||||
|
password_confirm:password_confirm.value,
|
||||||
|
},
|
||||||
|
success(resp){
|
||||||
|
console.log(resp);
|
||||||
|
if (resp.result === 'success'){
|
||||||
|
store.dispatch('login',{
|
||||||
|
username:username.value,
|
||||||
|
password:password.value,
|
||||||
|
success(){
|
||||||
|
error_message = '';
|
||||||
|
router.push({name:'home'});
|
||||||
|
},
|
||||||
|
error(){
|
||||||
|
error_message = "系统异常";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
error_message = resp.result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
password_confirm,
|
||||||
|
error_message,
|
||||||
|
register,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
button {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-message {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,33 @@
|
|||||||
|
<template>
|
||||||
|
<ContentBase>
|
||||||
|
<div>
|
||||||
|
{{ $store.state.user.username }}
|
||||||
|
</div>
|
||||||
|
</ContentBase>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ContentBase from '../components/ContentBase';
|
||||||
|
import { useStore } from 'vuex';
|
||||||
|
import router from '../router/index.js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'UserProfileView',
|
||||||
|
components: {
|
||||||
|
ContentBase,
|
||||||
|
},
|
||||||
|
setup(){
|
||||||
|
let store = useStore();
|
||||||
|
if (!store.state.user.is_login){
|
||||||
|
router.push({name:'login'});
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
store,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
</style>
|
||||||
|
|
@ -1,4 +1,21 @@
|
|||||||
const { defineConfig } = require('@vue/cli-service')
|
const { defineConfig } = require('@vue/cli-service')
|
||||||
|
|
||||||
|
const AutoImport = require('unplugin-auto-import/webpack')
|
||||||
|
const Components = require('unplugin-vue-components/webpack')
|
||||||
|
const { ElementPlusResolver } = require('unplugin-vue-components/resolvers')
|
||||||
|
|
||||||
module.exports = defineConfig({
|
module.exports = defineConfig({
|
||||||
transpileDependencies: true
|
transpileDependencies: true,
|
||||||
|
configureWebpack: {
|
||||||
|
plugins: [
|
||||||
|
AutoImport({
|
||||||
|
resolvers: [ElementPlusResolver()]
|
||||||
|
}),
|
||||||
|
Components({
|
||||||
|
resolvers: [ElementPlusResolver()]
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in new issue