界面展示优化

This commit is contained in:
sswiki
2024-12-06 22:46:50 +08:00
parent 6cdcaf51ad
commit 858e7d6f21
12 changed files with 465 additions and 255 deletions

View File

@@ -7,6 +7,7 @@
"serve": "vite preview" "serve": "vite preview"
}, },
"dependencies": { "dependencies": {
"@ant-design/icons-vue": "^7.0.1",
"@element-plus/icons-vue": "^2.0.10", "@element-plus/icons-vue": "^2.0.10",
"@icon-park/vue-next": "^1.4.2", "@icon-park/vue-next": "^1.4.2",
"@soerenmartius/vue3-clipboard": "^0.1.2", "@soerenmartius/vue3-clipboard": "^0.1.2",

View File

@@ -23,17 +23,22 @@ body {
} }
::-webkit-scrollbar { ::-webkit-scrollbar {
width: 6px; height: 11px;
height: 9px; width: 11px !important;
-webkit-appearance: none; background-color: unset !important;
} }
::-webkit-scrollbar-thumb { ::-webkit-scrollbar-thumb {
background: #ddd; cursor: pointer;
border-radius: 10px; border-radius: 11px;
border-style: dashed;
border-color: transparent;
border-width: 3px;
background-color: rgba(173, 180, 195, 0.4);
background-clip: padding-box;
} }
::-webkit-scrollbar-track-piece { ::-webkit-scrollbar-thumb:hover {
background: #eee; background: rgba(173, 180, 195, 0.5);
} }
</style> </style>

View File

@@ -0,0 +1,51 @@
<template>
<el-image-viewer
v-if="imagePreviewVisible"
:url-list="showImagePreviewList"
:initial-index="previewInitialIndex"
@close="closeImagePreview"
hide-on-click-modal
/>
</template>
<script setup>
import {onBeforeUnmount, ref, h, shallowRef, nextTick, onMounted, watch, defineProps, defineExpose} from 'vue'
import {showImagePreview} from 'vant';
import {useStoreDisplay} from "@/store/wikiDisplay";
let storeDisplay = useStoreDisplay();
onMounted(() => {
});
let imagePreviewVisible = ref(false);
let previewInitialIndex = ref(0);
let showImagePreviewList = ref([]);
const closeImagePreview = () => {
imagePreviewVisible.value = false;
}
const initViewer = (dom) => {
if (!dom) return;
const imgDomArr = [];
const imgSelector = dom.querySelectorAll('img');
imgSelector.forEach((item) => {
if (item.hasAttribute('not-allow-click')) return;
imgDomArr.push(item);
let index = imgDomArr.length - 1;
item.onclick = () => {
let imgArr = [];
// 点击后再去获取最新的url防止中途有修改比如plantuml的图
imgDomArr.forEach(dom => imgArr.push(dom.src));
if (storeDisplay.isMobile) {
showImagePreview({
images: imgArr,
startPosition: index,
});
} else {
previewInitialIndex.value = index;
showImagePreviewList.value = imgArr;
imagePreviewVisible.value = true;
}
}
});
}
defineExpose({initViewer});
</script>

View File

@@ -0,0 +1,24 @@
import {ref, onMounted, onUnmounted, watch} from 'vue'
import {useStoreDisplay} from "@/store/wikiDisplay";
import { useWindowSize, useWindowScroll } from '@vueuse/core'
export function useScroll(callback) {
const {x, y} = useWindowScroll();
watch([x, y], () => {
if (callback) {
callback();
}
});
}
export function useResizeEvent(callback) {
const {width, height} = useWindowSize();
watch([width, height], () => {
setTimeout(callback, 0);
});
let storeDisplay = useStoreDisplay();
watch(storeDisplay, () => {
setTimeout(callback, 0);
});
}

View File

@@ -7,7 +7,7 @@ import NoAuth from './views/common/NoAuth.vue'
import Home from './views/home/Home.vue' import Home from './views/home/Home.vue'
import MyInfo from './views/user/MyInfo.vue' import MyInfo from './views/user/MyInfo.vue'
import Show from './views/page/Show.vue' import Show from './views/page/View.vue'
import Edit from './views/page/Edit.vue' import Edit from './views/page/Edit.vue'
import spaceManage from './views/space/Manage.vue' import spaceManage from './views/space/Manage.vue'

View File

@@ -13,6 +13,7 @@ export const useStoreDisplay = defineStore('wikiDisplay', {
showHeader:true, showHeader:true,
// 当前页面 view、space // 当前页面 view、space
currentPage: '', currentPage: '',
isMobile: false,
} }
}, },
}) })

View File

@@ -1,10 +1,10 @@
<template> <template>
<div class="global-layout-vue"> <div class="global-layout-vue">
<el-container> <el-container>
<el-aside v-show="storeDisplay.showMenu" :style="leftAsideStyle"> <el-aside v-show="storeDisplay.showMenu" :style="leftAsideStyle" class="left-aside-outer-box">
<LeftAside/> <LeftAside/>
</el-aside> </el-aside>
<RightResize v-show="storeDisplay.showMenu" v-model:value="storeDisplay.rightAsideWidth" @change="rightAsideWidthChange"></RightResize> <RightResize v-show="storeDisplay.showMenu" v-model="storeDisplay.rightAsideWidth" @change="rightAsideWidthChange"></RightResize>
<el-container> <el-container>
<el-header v-if="storeDisplay.showHeader"> <el-header v-if="storeDisplay.showHeader">
<RightHeader ref="rightHeaderRef"/> <RightHeader ref="rightHeaderRef"/>
@@ -23,8 +23,14 @@ import {useStoreDisplay} from '@/store/wikiDisplay.js'
import LeftAside from './aside/LeftAside.vue' import LeftAside from './aside/LeftAside.vue'
import RightHeader from './aside/RightHeader.vue' import RightHeader from './aside/RightHeader.vue'
import RightResize from './aside/RightResize.vue' import RightResize from './aside/RightResize.vue'
import userApi from "@/assets/api/user";
import {useStoreUserData} from "@/store/userData";
let storeUser = useStoreUserData();
let storeDisplay = useStoreDisplay(); let storeDisplay = useStoreDisplay();
onMounted(() => {
getSelfUserInfo();
});
const rightAsideWidthChange = (width) => { const rightAsideWidthChange = (width) => {
storeDisplay.rightAsideWidth = width; storeDisplay.rightAsideWidth = width;
storeDisplay.commentShow = width; storeDisplay.commentShow = width;
@@ -32,8 +38,20 @@ const rightAsideWidthChange = (width) => {
let leftAsideStyle = computed(() => { let leftAsideStyle = computed(() => {
return {width: storeDisplay.rightAsideWidth + 'px'}; return {width: storeDisplay.rightAsideWidth + 'px'};
}); });
const getSelfUserInfo = () => {
userApi.getSelfUserInfo().then((json) => {
storeUser.userInfo = json.data || {};
});
}
</script> </script>
<style lang="scss">
.left-aside-outer-box {
border-right: 1px solid #eee;
background: #fafafa;
}
</style>
<style> <style>
html, html,
body { body {
@@ -66,7 +84,8 @@ body {
.el-header { .el-header {
color: #333; color: #333;
height: 60px !important; height: 60px !important;
border-bottom: 0.5px solid #eaeaea; background-color: #fff !important;
border-bottom: 1px solid #eee;
} }
.head-icon { .head-icon {
@@ -117,15 +136,10 @@ body {
<style lang="scss"> <style lang="scss">
.space-folder-box { .space-folder-box {
margin-left: 10px; margin-left: 10px;
margin-bottom: 10px;
position: relative; position: relative;
} }
.wiki-page-tree-box { .wiki-page-tree-box {
overflow-y: auto;
overflow-x: hidden;
padding-bottom: 30px;
.el-tree-node__content { .el-tree-node__content {
height: 35px; height: 35px;
position: relative; position: relative;
@@ -141,7 +155,6 @@ body {
.text { .text {
margin-left: 5px; margin-left: 5px;
vertical-align: middle; vertical-align: middle;
max-width: calc(100% - 40px); max-width: calc(100% - 40px);
display: inline-block; display: inline-block;
overflow: hidden; overflow: hidden;

View File

@@ -1,35 +1,26 @@
<template> <template>
<div class="page-show-vue" v-if="storePage.pageInfo.editorType !== 0"> <div class="page-show-vue" v-if="storePage.pageInfo.editorType !== 0">
<el-row type="border-card" style="height: 100%"> <a-row class="view-body-comment-box">
<el-col :span="storeDisplay.commentShow ? 18 : 24" style="padding: 20px;border-right: 1px solid #f1f1f1;height: 100%;overflow: auto;"> <a-col flex="auto" class="view-body-outer-box">
<el-row> <div class="view-body-box">
<el-col :span="navigationList.length > 0 ? 18 : 24"> <div class="wiki-title" ref="wikiTitleRef">{{ storePage.pageInfo.name }}</div>
<div style="max-width: 1000px; margin: 0 auto; padding-left: 10px"> <div id="pageContentBox" ref="pageContentRef" class="wiki-page-content">
<div class="wiki-title" ref="wikiTitleRef">{{ storePage.pageInfo.name }}</div> <div v-if="wikiPage.editorType === 2" v-html="pageShowDetail" class="markdown-body" v-highlight></div>
<div id="pageContentBox" ref="pageContentRef" class="wiki-page-content"> <div v-else v-html="pageShowDetail" class="wang-editor-body"></div>
<div v-html="pageShowDetail" class="markdown-body" v-if="wikiPage.editorType == 2" v-highlight></div> </div>
<div v-html="pageShowDetail" class="wang-editor-body" v-else></div> <PageZan></PageZan>
</div> </div>
<PageZan></PageZan> <Navigation :heading="navigationList"></Navigation>
</div> </a-col>
</el-col> <a-col v-if="storeDisplay.commentShow" flex="280px">
<el-col :span="navigationList.length > 0 ? 6 : 0" v-if="navigationList.length > 0"> <a-tabs v-model:activeKey="actionTabActiveName" class="action-tabs-box">
<Navigation :heading="navigationList"></Navigation> <a-tab-pane tab="评论" key="comment">
</el-col> <Comment></Comment>
</el-row> </a-tab-pane>
</el-col> <a-tab-pane tab="附件" key="files">
<el-col :span="6" style="height: 100%" v-show="storeDisplay.commentShow">
<el-icon @click="closeActionTab" class="close-action-tab">
<el-icon-close/>
</el-icon>
<el-tabs v-model="storeDisplay.commentActiveTab">
<el-tab-pane label="评论" name="comment">
<Comment/>
</el-tab-pane>
<el-tab-pane label="附件" name="annex">
<Annex/> <Annex/>
</el-tab-pane> </a-tab-pane>
<el-tab-pane label="修改历史" name="history"> <a-tab-pane tab="修改历史" key="history">
<PageHistory <PageHistory
:pageHistoryList="pageHistoryList" :pageHistoryList="pageHistoryList"
:pageHistoryChoice="pageHistoryChoice" :pageHistoryChoice="pageHistoryChoice"
@@ -37,39 +28,22 @@
@historyClickHandle="historyClickHandle" @historyClickHandle="historyClickHandle"
@previewPageImage="previewPageImage" @previewPageImage="previewPageImage"
@createNavigationHeading="createNavigationHeading"/> @createNavigationHeading="createNavigationHeading"/>
</el-tab-pane> </a-tab-pane>
</el-tabs> <template #rightExtra>
</el-col> <el-tooltip content="关闭" placement="top">
</el-row> <a-button @click="closeActionTab" type="text" :icon="h(CloseOutlined)"></a-button>
<el-image-viewer </el-tooltip>
v-if="showImagePreview" </template>
:url-list="showImagePreviewList" </a-tabs>
:initial-index="previewInitialIndex" </a-col>
@close="closeImagePreview" </a-row>
hide-on-click-modal <ImageViewer ref="imageViewerRef"/>
/>
</div> </div>
</template> </template>
<script setup> <script setup>
import { import { CloseOutlined } from '@ant-design/icons-vue';
ArrowDown as ElIconArrowDown, import {toRefs, ref, reactive, onMounted, watch, defineProps, h, nextTick, defineEmits, defineExpose, computed} from 'vue';
View as ElIconView,
Close as ElIconClose,
Delete as ElIconDelete,
Loading as ElIconLoading,
CircleCheck as ElIconCircleCheck,
CircleClose as ElIconCircleClose,
ChatLineRound as ElIconChatLineRound,
Upload as ElIconUpload,
Edit as ElIconEdit,
Timer as ElIconTime,
Stamp as ElIconSCheck,
Share as ElIconShare,
Iphone as ElIconMobilePhone,
Download as ElIconDownload,
} from '@element-plus/icons-vue'
import {toRefs, ref, reactive, onMounted, watch, defineProps, defineEmits, defineExpose, computed} from 'vue';
import {onBeforeRouteUpdate, useRoute, useRouter} from "vue-router"; import {onBeforeRouteUpdate, useRoute, useRouter} from "vue-router";
import { ElMessageBox, ElMessage, ElNotification } from 'element-plus'; import { ElMessageBox, ElMessage, ElNotification } from 'element-plus';
import QRCode from 'qrcode' import QRCode from 'qrcode'
@@ -77,7 +51,7 @@ import unitUtil from '../../assets/lib/UnitUtil.js'
import htmlUtil from '../../assets/lib/HtmlUtil.js' import htmlUtil from '../../assets/lib/HtmlUtil.js'
import pageApi from '../../assets/api/page' import pageApi from '../../assets/api/page'
import userApi from '../../assets/api/user' import userApi from '../../assets/api/user'
import Navigation from './components/Navigation.vue' import Navigation from './show/Navigation.vue'
import Annex from './show/Annex.vue' import Annex from './show/Annex.vue'
import PageHistory from './show/PageHistory.vue' import PageHistory from './show/PageHistory.vue'
import Comment from './show/Comment.vue' import Comment from './show/Comment.vue'
@@ -87,6 +61,7 @@ import 'mavon-editor/dist/markdown/github-markdown.min.css'
import 'mavon-editor/dist/css/index.css' import 'mavon-editor/dist/css/index.css'
import {useStorePageData} from "@/store/pageData"; import {useStorePageData} from "@/store/pageData";
import {useStoreDisplay} from "@/store/wikiDisplay"; import {useStoreDisplay} from "@/store/wikiDisplay";
import ImageViewer from "@/components/base/ImageViewer.vue";
let page = { let page = {
colorArr: ['#67C23A', '#409EFF', '#E6A23C', '#F56C6C', '#909399', '#303133'], colorArr: ['#67C23A', '#409EFF', '#E6A23C', '#F56C6C', '#909399', '#303133'],
@@ -142,6 +117,13 @@ onMounted(() => {
storeDisplay.currentPage = 'view'; storeDisplay.currentPage = 'view';
initQueryParam(route); initQueryParam(route);
}); });
let actionTabActiveName = ref('comment');
let imageViewerRef = ref();
const previewPageImage = () => {
if (imageViewerRef.value) {
imageViewerRef.value.initViewer(pageContentRef.value);
}
}
const getSearchUserList = (query) => { const getSearchUserList = (query) => {
if (query == '') return if (query == '') return
@@ -250,7 +232,7 @@ const computeFileSize = (fileSize) => {
} }
const loadPageDetail = (pageId) => { const loadPageDetail = (pageId) => {
clearHistory() clearHistory()
pageApi.pageDetail({id: pageId}).then((json) => { pageApi.pageDetail({id: pageId}).then(async (json) => {
let result = json.data || {}; let result = json.data || {};
let wikiPageRes = result.wikiPage || {}; let wikiPageRes = result.wikiPage || {};
wikiPageRes.selfZan = result.selfZan || 0; wikiPageRes.selfZan = result.selfZan || 0;
@@ -280,9 +262,7 @@ const loadPageDetail = (pageId) => {
// //
emit('changeExpandedKeys', pageId); emit('changeExpandedKeys', pageId);
setTimeout(() => { setTimeout(() => {
if (storePage.pageInfo.editorType !== 0){ previewPageImage();
previewPageImage();
}
createNavigationHeading(); createNavigationHeading();
}, 500); }, 500);
storePage.pageInfo = wikiPageRes; storePage.pageInfo = wikiPageRes;
@@ -308,18 +288,6 @@ const closeImagePreview = () => {
showImagePreview.value = false showImagePreview.value = false
} }
let pageContentRef = ref(); let pageContentRef = ref();
const previewPageImage = () => {
const imgArr = []
const imgSelector = pageContentRef.value.querySelectorAll('img')
imgSelector.forEach((item, index) => {
imgArr.push(item.src)
item.onclick = () => {
previewInitialIndex.value = index
showImagePreviewList.value = imgArr
showImagePreview.value = true
}
})
}
const getUserHeadBgColor = (userId) => { const getUserHeadBgColor = (userId) => {
let color = page.userHeadColor[userId] let color = page.userHeadColor[userId]
@@ -338,28 +306,35 @@ const initQueryParam = (to) => {
} }
</script> </script>
<style lang="scss" scoped>
.page-show-vue {
.wiki-page-content {
margin-top: 20px;
}
}
</style>
<style lang="scss"> <style lang="scss">
.page-show-vue { .page-show-vue {
height: 100%; height: 100%;
overflow: hidden; overflow: hidden;
.wiki-page-content { .view-body-comment-box {
ol { height: 100%;
list-style: decimal;
}
ul { .view-body-outer-box {
list-style: disc; height: 100%;
overflow: auto;
padding: 30px 20px;
position: relative;
border-right: 1px solid #eee;
.view-body-box {
max-width: 840px;
margin: 0 auto;
.wiki-page-content {
margin-top: 30px;
}
}
}
.ant-tabs-nav {
padding: 0 15px;
}
} }
}
} }
</style> </style>

View File

@@ -1,45 +1,45 @@
<template> <template>
<div style="padding: 10px;height: 100%;box-sizing: border-box;background: #fafafa;"> <div class="left-aside-box">
<div style="margin-bottom: 5px"> <div class="left-aside-top-box">
<el-select :model-value="storeSpace.chooseSpaceId" @change="spaceChangeEvents" filterable <el-select :model-value="storeSpace.chooseSpaceId" @change="spaceChangeEvents" filterable
placeholder="选择空间" style="width: 100%"> placeholder="选择空间" style="width: 100%;margin-bottom: 5px;">
<el-option-group label="" v-if="!props.readOnly"> <el-option-group label="" v-if="!props.readOnly">
<el-option :key="-1" label="空间管理" :value="-1"></el-option> <el-option :key="-1" label="空间管理" :value="-1"></el-option>
</el-option-group> </el-option-group>
<el-option-group label=""></el-option-group> <el-option-group label=""></el-option-group>
<el-option v-for="item in storeSpace.spaceOptions" :key="item.value" :label="item.label" :value="item.value"></el-option> <el-option v-for="item in storeSpace.spaceOptions" :key="item.value" :label="item.label" :value="item.value"></el-option>
</el-select> </el-select>
</div> <el-autocomplete v-model="searchKeywords" v-if="!props.readOnly" :fetch-suggestions="doSearchByKeywords"
<el-autocomplete v-model="searchKeywords" v-if="!props.readOnly" :fetch-suggestions="doSearchByKeywords" @select="handleSearchKeywordsSelect" popper-class="search-autocomplete"
@select="handleSearchKeywordsSelect" popper-class="search-autocomplete" placeholder="在当前空间搜索" style="width: 100%; margin: 10px 0">
placeholder="在当前空间搜索" style="width: 100%; margin: 10px 0"> <template v-slot="{ item }">
<template v-slot="{ item }"> <div class="search-option-item">
<div class="search-option-item"> <div class="title">
<div class="title"> <span v-html="item.pageTitle || '-'"></span>
<span v-html="item.pageTitle || '-'"></span> </div>
<span class="content" v-html="item.previewContent || '-'"></span>
</div> </div>
<span class="content" v-html="item.previewContent || '-'"></span> </template>
</div> </el-autocomplete>
</template> <div class="space-folder-box" v-if="!props.readOnly">
</el-autocomplete> <el-row justify="space-between">
<div class="space-folder-box" v-if="!props.readOnly"> <el-col :span="12">
<el-row justify="space-between"> <el-tooltip style="margin: 4px" effect="dark" :content="descriptorForTree" placement="top">
<el-col :span="12"> <span style="color:#888;font-size: 12px;cursor: pointer;line-height: 32px;" @click="changeDropWownStatus">空间目录</span>
<el-tooltip style="margin: 4px" effect="dark" :content="descriptorForTree" placement="top"> </el-tooltip>
<span style="color:#888;font-size: 12px;cursor: pointer;line-height: 32px;" @click="changeDropWownStatus">空间目录</span> </el-col>
</el-tooltip> <el-col :span="12" style="text-align: right;">
</el-col> <AddMenu/>
<el-col :span="12" style="text-align: right;"> </el-col>
<AddMenu/> </el-row>
</el-col> </div>
</el-row>
</div> </div>
<div class="wiki-page-tree-box"> <div class="wiki-page-tree-box">
<el-tree ref="wikiPageTreeRef" :current-node-key="props.nowPageId" :data="storePage.wikiPageList" <el-tree ref="wikiPageTreeRef" :current-node-key="props.nowPageId" :data="storePage.wikiPageList"
:default-expanded-keys="wikiPageExpandedKeys" :expand-on-click-node="true" :class="explanClass" :default-expanded-keys="wikiPageExpandedKeys" :expand-on-click-node="true" :class="explanClass"
:filter-node-method="filterPageNode" :props="defaultProps" :draggable="!props.readOnly" :filter-node-method="filterPageNode" :props="defaultProps" :draggable="!props.readOnly"
@node-click="handleNodeClick" @node-drop="handlePageDrop" node-key="id" highlight-current @node-click="handleNodeClick" @node-drop="handlePageDrop" node-key="id" highlight-current
style="background-color: #fafafa"> style="background-color: #fafafa;">
<template v-slot="{ node, data }"> <template v-slot="{ node, data }">
<div class="page-tree-node" @mouseover="changeNodeOptionStatus(data) "> <div class="page-tree-node" @mouseover="changeNodeOptionStatus(data) ">
<div class="node-content"> <div class="node-content">
@@ -58,7 +58,7 @@
</el-tooltip> </el-tooltip>
<a-input v-if="data.renaming" v-model:value="data.name" @blur="doRename(node,data)" @click.stop <a-input v-if="data.renaming" v-model:value="data.name" @blur="doRename(node,data)" @click.stop
class="rename-input" placeholder="请输入文档名称"/> class="rename-input" placeholder="请输入文档名称"/>
<span v-else style="vertical-align: middle;margin-right: 5px"> <span v-else style="vertical-align: middle;margin-left: 5px;">
<el-tooltip :content="node.label" placement="top-start" :show-after="700">{{ node.label }}</el-tooltip> <el-tooltip :content="node.label" placement="top-start" :show-after="700">{{ node.label }}</el-tooltip>
</span> </span>
<!--操作--> <!--操作-->
@@ -125,6 +125,8 @@ import AddMenu from "./AddMenu.vue";
import IconDocument from "@/components/base/IconDocument.vue"; import IconDocument from "@/components/base/IconDocument.vue";
import {ElMessageBox, ElMessage} from 'element-plus' import {ElMessageBox, ElMessage} from 'element-plus'
import {useStoreSpaceData} from "@/store/spaceData"; import {useStoreSpaceData} from "@/store/spaceData";
import Navigation from "@/views/page/show/Navigation.vue";
import PageZan from "@/views/page/show/PageZan.vue";
let route = useRoute(); let route = useRoute();
let router = useRouter(); let router = useRouter();
@@ -321,4 +323,22 @@ const handlePageDrop = (draggingNode, dropNode, dropType, ev) => {
defineExpose({searchByKeywords}) defineExpose({searchByKeywords})
</script> </script>
<style lang="scss">
.left-aside-box {
height: 100%;
box-sizing: border-box;
background: #fafafa;
display: flex;
flex-direction: column;
.left-aside-top-box {
padding: 10px;
}
.wiki-page-tree-box {
overflow-y: auto;
overflow-x: hidden;
padding-bottom: 10px;
}
}
</style>

View File

@@ -1,55 +1,76 @@
<template> <template>
<div ref="rightResizeRef" class="right-resize"></div> <div ref="rightResizeRef" class="right-resize hide-on-mp" :style="{left: (modelValue-4)+'px'}">
<div class="line"></div>
</div>
</template> </template>
<script setup> <script setup>
import {onBeforeUnmount, ref, onMounted, watch, defineProps, nextTick, defineEmits, defineExpose, computed} from 'vue'; import {toRefs, ref, reactive, onMounted, onBeforeUnmount, watch, defineEmits, computed} from 'vue';
const props = defineProps({
modelValue: Number,
max: {
type: Number,
default: 600
},
min: {
type: Number,
default: 300
}
});
let emit = defineEmits(['update:modelValue', 'change']);
let rightAsideWidth = 300;
let emit = defineEmits(['update:value', 'change']);
onMounted(() => { onMounted(() => {
dragChangeRightAsideWidth(); dragChangeRightAsideWidth();
}); });
let rightResizeRef = ref(); let rightResizeRef = ref();
const dragChangeRightAsideWidth = () => { const dragChangeRightAsideWidth = () => {
// 保留this引用 let resize = rightResizeRef.value;
let resize = rightResizeRef.value resize.onmousedown = e => {
resize.onmousedown = (e) => { let startX = e.clientX;
let startX = e.clientX let rightAsideWidth = props.modelValue;
// 颜色改变提醒 // 颜色改变提醒
resize.left = resize.offsetLeft resize.classList.add('active');
document.onmousemove = (e2) => { document.onmousemove = e2 => {
// 计算并应用位移量 // 计算并应用位移量
let endX = e2.clientX let endX = e2.clientX;
let moveLen = startX - endX let moveLen = startX - endX;
if ((moveLen < 0 && rightAsideWidth < 600) || (moveLen > 0 && rightAsideWidth > 300)) { if ((moveLen < 0 && rightAsideWidth < props.max) || (moveLen > 0 && rightAsideWidth > props.min)) {
startX = endX startX = endX;
rightAsideWidth -= moveLen rightAsideWidth -= moveLen;
if (rightAsideWidth < 300) { rightAsideWidth = Math.max(rightAsideWidth, props.min);
rightAsideWidth = 300 rightAsideWidth = Math.min(rightAsideWidth, props.max);
} emit('update:modelValue', rightAsideWidth);
emit('update:value', rightAsideWidth) emit('change', rightAsideWidth);
emit('change', rightAsideWidth)
} }
} };
document.onmouseup = () => { document.onmouseup = () => {
document.onmousemove = null resize.classList.remove('active');
document.onmouseup = null document.onmousemove = null;
} document.onmouseup = null;
return false };
} return false;
};
} }
</script> </script>
<style scoped lang="scss"> <style scoped>
.right-resize { .right-resize {
width: 3px; height: 100%;
height: 100%; padding: 0 4px;
cursor: w-resize; cursor: w-resize;
background: #fafafa; z-index: 200;
position: absolute;
}
&:hover { .right-resize .line {
background: #2a85f6; width: 2px;
} height: 100%;
}
.right-resize:hover .line,
.right-resize.active .line {
background: #2876d7;
} }
</style> </style>

View File

@@ -1,38 +1,40 @@
<template> <template>
<div class="comment-box" ref="actionTabCommentRef"> <div class="comment-outer-box">
<div v-if="commentList.length <= 0" class="action-box-empty"> <div class="comment-box" ref="actionTabCommentRef">
暂无评论 <div v-if="commentList.length <= 0" class="action-box-empty">暂无评论</div>
<div v-else class="comment-list">
<el-timeline>
<el-timeline-item :timestamp="comment.createTime" placement="top" v-for="comment in commentList">
<el-card class="box-card comment-card" :body-style="{ padding: '10px' }">
<div :style="'background-color: ' + comment.color" class="head">
{{ comment.createUserName.substr(0, 1) }}
</div>
<div class="comment-user-name">
{{ comment.createUserName }}
<el-popconfirm v-if="canDeleteComment(comment)"
placement="top" width="160" trigger="click"
confirm-button-text="删除"
cancel-button-text="取消"
@confirm="deleteComment(comment.id)"
title="确定要删除此评论吗?">
<template #reference>
<el-icon class="icon-delete"><ElIconDelete /></el-icon>
</template>
</el-popconfirm>
</div>
<pre class="comment-content">{{ comment.content }}</pre>
</el-card>
</el-timeline-item>
</el-timeline>
</div>
</div> </div>
<div v-else class="comment-list"> <div class="comment-input-box">
<el-timeline> <textarea rows="5" placeholder="发表评论" v-model="commentTextInput" :maxlength="500"></textarea>
<el-timeline-item :timestamp="comment.createTime" placement="top" v-for="comment in commentList"> <div class="comment-btn-box">
<el-card class="box-card comment-card" :body-style="{ padding: '10px' }"> <el-button type="primary" size="small" @click="submitPageComment">发送</el-button>
<div :style="'background-color: ' + comment.color" class="head"> </div>
{{ comment.createUserName.substr(0, 1) }}
</div>
<div class="comment-user-name">
{{ comment.createUserName }}
<el-popconfirm v-if="canDeleteComment(comment)"
placement="top" width="160" trigger="click"
confirm-button-text="删除"
cancel-button-text="取消"
@confirm="deleteComment(comment.id)"
title="确定要删除此评论吗?">
<template #reference>
<el-icon class="icon-delete"><ElIconDelete /></el-icon>
</template>
</el-popconfirm>
</div>
<pre class="comment-content">{{ comment.content }}</pre>
</el-card>
</el-timeline-item>
</el-timeline>
</div> </div>
</div> </div>
<div class="comment-input-box">
<textarea rows="5" placeholder="发表评论" v-model="commentTextInput" :maxlength="500"></textarea>
<el-button style="float: right; margin: 2px 5px" type="primary" size="small" @click="submitPageComment">发送</el-button>
</div>
</template> </template>
<script setup> <script setup>
@@ -97,17 +99,9 @@ const loadCommentList = () => {
scrollActionTabComment() scrollActionTabComment()
}) })
} }
const recommentUser = (id, index) => {
recommentInfo.value = {
id: id,
index: index,
placeholder: '回复' + (index + 1) + '楼',
}
}
let canDeleteComment = (row) => { let canDeleteComment = (row) => {
return ( return storeUser.userInfo.id === row.createUserId
storeUser.userInfo.id === row.createUserId || storeUser.userInfo.id === storePage.pageInfo.createUserId || storeUser.userInfo.id === storePage.pageInfo.createUserId
)
} }
const deleteComment = (id) => { const deleteComment = (id) => {
pageApi.deletePageComment({id: id}).then(() => { pageApi.deletePageComment({id: id}).then(() => {
@@ -147,58 +141,57 @@ const getUserHeadBgColor = (userId) => {
<style lang="scss"> <style lang="scss">
.comment-box { .comment-box {
padding: 8px; padding: 8px;
height: calc(100vh - 115px); height: calc(100vh - 315px);
overflow: auto; overflow: auto;
.comment-list { .comment-card {
padding-bottom: 130px; .comment-user-name {
} margin-bottom: 10px;
.comment-card { .icon-delete {
.comment-user-name { color: #888;
margin-bottom: 10px; font-size: 13px;
cursor: pointer;
float: right;
display: none;
}
}
.icon-delete { .comment-content {
color: #888; padding: 0;
font-size: 13px; color: #666;
cursor: pointer; margin: 0;
float: right; white-space: pre-wrap;
display: none; word-wrap: break-word;
} line-height: 20px;
}
} }
.comment-content { .comment-card:hover {
padding: 0; .icon-delete {
color: #666; display: inline-block;
margin: 0; }
white-space: pre-wrap;
word-wrap: break-word;
line-height: 20px;
} }
}
.comment-card:hover {
.icon-delete {
display: inline-block;
}
}
} }
.comment-input-box { .comment-input-box {
position: absolute;
bottom: 0;
width: 100%;
background: #fff;
border-top: 1px solid #f1f1f1;
textarea {
resize: none;
width: 100%; width: 100%;
box-sizing: border-box; background: #fff;
border: 0; border-top: 1px solid #f1f1f1;
outline: none !important;
padding: 10px; textarea {
} resize: none;
width: 100%;
box-sizing: border-box;
border: 0;
outline: none !important;
padding: 10px;
}
.comment-btn-box {
text-align: right;
padding: 4px 15px 6px 0;
}
} }
</style> </style>

View File

@@ -0,0 +1,106 @@
<template>
<div class="navigation-box">
<div class="navigation-content-box">
<div>dasdas</div>
<div class="nav-heading" :style="{ width: navigationWidth }">
<div v-for="item in heading" :class="'heading-item heading-' + item.level" @click="headingItemClick(item)">
{{ item.text }}
</div>
</div>
</div>
</div>
</template>
<script setup>
import {toRefs, ref, reactive, onMounted, watch, defineEmits, defineProps, defineExpose,} from 'vue'
import {useStoreDisplay} from '@/store/wikiDisplay.js'
import {useStorePageData} from "@/store/pageData";
import {useResizeEvent} from "@/composable/windowsScroll";
let storePage = useStorePageData();
const storeDisplay = useStoreDisplay();
let navigationWidth = ref('100px');
const props = defineProps({
heading: {type: Array, default: []},
});
onMounted(() => {
setTimeout(() => computeNavigationWidth(), 100);
});
useResizeEvent(() => {
computeNavigationWidth();
});
const computeNavigationWidth = () => {
}
const headingItemClick = (item) => {
// 滚动到指定节点
item.node.scrollIntoView({
behavior: 'smooth',
block: 'start',
inline: 'nearest',
});
// 距离顶部高度
//console.log(item.node.offsetTop - item.node.scrollHeight)
}
</script>
<style lang="scss">
.navigation-box {
width: 100px;
position: absolute;
top: 150px;
right: 10px;
z-index: 4;
.navigation-content-box {
position: fixed;
background: #fff;
border-radius: 8px;
padding: 16px;
margin-left: -40px;
box-shadow: var(--el-box-shadow-lighter);
}
.nav-heading {
max-height: calc(100vh - 250px);
overflow-y: auto;
.heading-item {
padding: 5px 0;
cursor: pointer;
color: #646a73;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
&:hover {
color: #3370ff;
}
}
.heading-1 {
padding-left: 0;
}
.heading-2 {
padding-left: 16px;
}
.heading-3 {
padding-left: 32px;
}
.heading-4 {
padding-left: 48px;
}
.heading-5 {
padding-left: 64px;
}
.heading-6 {
padding-left: 80px;
}
}
}
</style>