1.前端代码拆分整理

2.合并统一配色方案
3.修改所有功能按钮为右上角图标
4.修改附件到文档相关按钮内
This commit is contained in:
Sh1yu
2023-09-20 09:31:38 +08:00
parent 35b4e2806b
commit eda883a9bf
25 changed files with 1382 additions and 1135 deletions

View File

@@ -1,585 +1,33 @@
<template>
<div class="global-layout-vue">
<el-container>
<el-aside v-show="leftCollapse" :style="{ width: rightAsideWidth + 'px' }">
<LeftSidebar
ref="leftSidebarDir"
:readOnly=false
:wikiPageList="wikiPageList"
:spaceOptions="spaceOptions"
:nowPageId="nowPageId"
:choiceSpace="choiceSpace"
@setNowPageId="setNowPageId"
@doGetPageList="doGetPageList"
@spaceChangeEvents="spaceChangeEvents">
<template v-slot:addMenuDir>
<AddMenu
:choiceSpace="choiceSpace"
:choosePageId="choosePageId"
:nowPageId = "nowPageId"
:funcId = "0"
@createWikiByTemplate="createWikiByTemplate"
@choosePageIdFunc="choosePageIdFunc"
@doGetPageList="doGetPageList"
/>
</template>
<template v-slot:addMenuNode="{node,data}">
<div class="page-tree-node" @mouseover="changeNodeOptionStatus(data) ">
<div style="width: calc(100% - 30px);overflow: hidden;text-overflow: ellipsis;white-space: nowrap;">
<!--图标-->
<el-icon v-if="data.editorType === 0" class="clickAddIcon" style="margin-right: 5px;vertical-align: middle;">
<svg width="1em" height="1em" viewBox="0 0 48 48" fill="none"><path d="M5 8C5 6.89543 5.89543 6 7 6H19L24 12H41C42.1046 12 43 12.8954 43 14V40C43 41.1046 42.1046 42 41 42H7C5.89543 42 5 41.1046 5 40V8Z" fill="none" stroke="currentColor" stroke-width="4" stroke-linejoin="round"></path><path d="M43 22H5" stroke="currentColor" stroke-width="4" stroke-linejoin="round"></path><path d="M5 16V28" stroke="currentColor" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"></path><path d="M43 16V28" stroke="currentColor" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"></path></svg>
</el-icon>
<el-icon v-else-if="data.editorType === 1" class="clickAddIcon" style="margin-right: 5px;vertical-align: middle;">
<svg width="1em" height="1em" viewBox="0 0 48 48" fill="none"><rect x="6" y="6" width="36" height="36" rx="3" fill="none" stroke="currentColor" stroke-width="4"></rect><path d="M14 16L18 32L24 19L30 32L34 16" stroke="currentColor" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"></path></svg>
</el-icon>
<el-icon v-else-if="data.editorType === 2" class="clickAddIcon" style="margin-right: 5px;vertical-align: middle;">
<el-icon-document/>
</el-icon>
<!--标题-->
<el-tooltip :content="data.tags" placement="top-start" :show-after="500">
<a-tag color="#f50" v-if="data.shareStatus !== undefined">{{filterShareStatus(data.shareStatus)}}</a-tag>
</el-tooltip>
<a-input v-if="data.renaming" v-model:value="data.name" class="rename-input" placeholder="请输入文档名称" @blur="doRename(node,data)" @click.stop/>
<span v-else style="vertical-align: middle;margin-right: 5px">
<el-tooltip :content="node.label" placement="top-start" :show-after="700">{{ node.label }}</el-tooltip>
</span>
<!--操作-->
<div class="page-action-box" :class="data.renaming?'renaming':''" @click.stop>
<AddMenu
:choiceSpace="choiceSpace"
:choosePageId="choosePageId"
:nowPageId = "nowPageId"
:funcId = "data.id"
@createWikiByTemplate="createWikiByTemplate"
@choosePageIdFunc="choosePageIdFunc"
@doGetPageList="doGetPageList"
/>
<a-dropdown :trigger="['click']" @click="choosePageIdFunc(data.id)">
<el-button :icon="MoreFilled" text class="page-action-dropdown-btn"></el-button>
<template #overlay>
<a-menu>
<a-menu-item key="0" @click="rename(node,data)">
<el-icon class="clickAddIcon" style="margin-right: 5px">
<svg width="1em" height="1em" viewBox="0 0 48 48" fill="none"><path d="M42 26V40C42 41.1046 41.1046 42 40 42H8C6.89543 42 6 41.1046 6 40V8C6 6.89543 6.89543 6 8 6L22 6" stroke="currentColor" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"></path><path d="M14 26.7199V34H21.3172L42 13.3081L34.6951 6L14 26.7199Z" fill="none" stroke="currentColor" stroke-width="4" stroke-linejoin="round"></path></svg>
</el-icon>
重命名
</a-menu-item>
<a-menu-item key="1" @click="deleteWikiPage(data.shareStatus)">
<el-icon class="clickAddIcon" style="margin-right: 5px">
<svg width="1em" height="1em" viewBox="0 0 48 48" fill="none"><path d="M9 10V44H39V10H9Z" fill="none" stroke="currentColor" stroke-width="4" stroke-linejoin="round"></path><path d="M20 20V33" stroke="currentColor" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"></path><path d="M28 20V33" stroke="currentColor" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"></path><path d="M4 10H44" stroke="currentColor" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"></path><path d="M16 10L19.289 4H28.7771L32 10H16Z" fill="none" stroke="currentColor" stroke-width="4" stroke-linejoin="round"></path></svg>
</el-icon>
删除
</a-menu-item>
<a-sub-menu key="2" title="移动文档" >
<template #icon>
<BlockOutlined />
</template>
<a-menu-item key="3" @click="openMoveMenu(false)">
<el-icon class="clickAddIcon" style="margin-right: 5px">
<DocumentCopy/>
</el-icon>
复制文档
</a-menu-item>
<a-menu-item key="4" @click="openMoveMenu(true)">
<el-icon class="clickAddIcon" style="margin-right: 5px">
<Scissor/>
</el-icon>
迁移文档
</a-menu-item>
</a-sub-menu>
<a-menu-item key="5" @click="openTemplateCreate(data.shareStatus !== undefined)" v-if="data.editorType !== 0">
<BuildOutlined />
设为模板
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</div>
</div>
</div>
</template>
</LeftSidebar>
<el-aside v-show="storeDisplay.showMenu" :style="{ width: storeDisplay.rightAsideWidth + 'px' }">
<LeftSideBar ref="leftSideBarRef"/>
</el-aside>
<RightResize v-show="leftCollapse" v-model:value="rightAsideWidth" @change="rightAsideWidthChange"></RightResize>
<RightResize v-show="storeDisplay.showMenu" v-model:value="storeDisplay.rightAsideWidth" @change="rightAsideWidthChange"></RightResize>
<el-container>
<el-header>
<el-row>
<el-col :span="12">
<el-button @click="turnLeftCollapse" v-if="leftCollapse" text :icon="ElIconFold" class="fold-btn"></el-button>
<el-button @click="turnLeftCollapse" v-else text :icon="ElIconExpand" class="fold-btn"></el-button>
</el-col>
<el-col :span="12" style="text-align: right;">
<span class="header-right-user-name">{{userSelfInfo.userName}}</span>
<el-popover v-model:visible="userMessagePopVisible" placement="bottom" trigger="click" width="600">
<template v-slot:reference>
<el-badge :is-dot="haveNotReadUserMessage" style="margin: 0 20px">
<el-icon class="head-icon" style="margin-right: 0">
<el-icon-bell/>
</el-icon>
</el-badge>
</template>
<div style="margin-bottom: 10px">
<span style="font-size: 14px; font-weight: bold">通知</span>
<el-link v-if="haveNotReadUserMessage" :icon="ElIconCheck" style="float: right" type="primary" @click="readAllUserMessage">本页标记已读</el-link>
</div>
<div class="header-user-message">
<el-table :data="userMessageList" border max-height="500" size="small" style="width: 100%; margin-bottom: 5px">
<el-table-column label="操作人" prop="operatorUserName" width="100px"></el-table-column>
<el-table-column label="操作时间" prop="creationTime" width="140px"></el-table-column>
<el-table-column label="内容">
<template v-slot="scope">
{{ scope.row.msgContent }}
<el-badge :is-dot="scope.row.msgStatus == 0" style="line-height: 10px; padding-right: 5px">
<el-link type="primary" @click="showUserMessage(scope.row)">查看</el-link>
</el-badge>
</template>
</el-table-column>
</el-table>
<div class="page-info-box">
<el-pagination
:current-page="userMsgParam.pageNum"
:page-size="userMsgParam.pageSize"
:total="userMsgTotalCount"
layout="prev, pager, next, total"
@current-change="handleCurrentChange">
</el-pagination>
</div>
</div>
</el-popover>
<el-dropdown trigger="click" @command="userSettingDropdown" style="vertical-align: middle;">
<el-icon class="head-icon">
<el-icon-setting/>
</el-icon>
<template v-slot:dropdown>
<el-dropdown-menu>
<el-dropdown-item command="console">控制台</el-dropdown-item>
<el-dropdown-item command="aboutDoc">关于</el-dropdown-item>
<el-dropdown-item command="userSignOut" divided>退出登录</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</el-col>
</el-row>
<el-header v-if="storeDisplay.showHeader">
<RightHeader ref="rightHeaderRef"/>
</el-header>
<el-main style="padding: 0; border-left: 1px solid #dcdfe6">
<router-view
v-slot="{ Component }"
:spaceId="choiceSpace"
:spaceInfo="getSpaceInfo(choiceSpace)"
@changeExpandedKeys="changeWikiPageExpandedKeys"
@loadPageList="loadPageList"
@loadSpace="loadSpaceList"
@switchSpace="switchSpacePage">
<component :is="Component"></component>
</router-view>
<router-view/>
</el-main>
</el-container>
</el-container>
<templateManage ref="templateManageRef" :pageId="choosePageId" :spaceId="choiceSpace" @doGetPageList="doGetPageList" />
<create-space ref="createSpaceRef" @success="loadSpaceList"></create-space>
<a-modal
v-model:open="visibleMoveMenu"
title="选择"
@ok="handleOk"
@cancel="handleCancel"
ok-text="确认"
cancel-text="取消"
:confirm-loading="aModalWaiting"
:destroyOnClose=true
:closable=false>
<LeftSidebar
:readOnly=true
:wikiPageList="moveToWikiPageList"
:spaceOptions="spaceOptions"
:nowPageId="moveToPageId"
:choiceSpace="moveToSpaceId"
@setNowPageId="setNowPageId"
@doGetPageList="doGetPageList"
@spaceChangeEvents="spaceChangeEvents"/>
</a-modal>
<about-dialog ref="aboutDialogRef"></about-dialog>
</div>
</template>
<script setup>
import {
Document as ElIconDocument,
Fold as ElIconFold,
Expand as ElIconExpand,
Upload as ElIconUpload,
Bell as ElIconBell,
Setting as ElIconSetting,
Plus as ElIconPlus,
Check as ElIconCheck,
Files,
Scissor,
DocumentCopy,
MoreFilled
} from '@element-plus/icons-vue'
import {onBeforeUnmount, toRefs, ref, reactive, onMounted, watch, defineProps, nextTick, defineEmits, defineExpose, computed} from 'vue';
import {onBeforeRouteUpdate, useRouter, useRoute} from "vue-router";
import {ElMessageBox, ElMessage} from 'element-plus'
import userApi from '../../assets/api/user'
import pageApi from '../../assets/api/page'
import CreateSpace from '../space/CreateSpace'
import TemplateManage from '../template/TemplateManage'
import RightResize from './RightResize.vue'
import AddMenu from './AddMenu.vue'
import LeftSidebar from './LeftSidebar.vue'
import AboutDialog from '../../views/common/AboutDialog'
import {useStoreDisplay} from '@/store/wikiDisplay.js'
import {useStoreUserData} from "@/store/userData";
import {useStorePageData} from "@/store/pageData";
import { defineComponent } from 'vue';
import { DownOutlined,BuildOutlined,BlockOutlined } from '@ant-design/icons-vue';
let route = useRoute();
let router = useRouter();
let storePage = useStorePageData();
import LeftSideBar from './LeftSideBar'
import RightHeader from './RightHeader'
import RightResize from './RightResize'
let storeDisplay = useStoreDisplay();
let leftCollapse = ref(true);
let rightContentLoading = ref(false);
let pathIndex = ref([]);
let defaultProps = ref({children: 'children', label: 'name',});
// 空间搜索相关
let spaceOptions = ref();
let spaceList = ref([]);
let choiceSpace = ref(undefined);
let nowSpaceShow = ref({});
let nowPageId = ref(0);
let moveToPageId = ref(0);
let moveToSpaceId = ref(0);
let moveToWikiPageList = ref([]);
let leftSideBarDir = ref(null);
// 页面展示相关
let wikiPageList = ref();
let wikiPage = ref({});
let wikiPageExpandedKeys = ref([]);
let userSelfInfo = ref({});
let userMessageList = ref([]);
let haveNotReadUserMessage = ref(false);
let userMessagePopVisible = ref(false);
let userMsgTotalCount = ref(0);
let userMsgParam = ref({sysType: 2, pageNum: 1, pageSize: 20,});
let rightAsideWidth = ref(300);
let optionPageId = ref('');
let choosePageId = ref(0);
let visibleMoveMenu = ref(false);
let onlyMoveMode = ref(false);
let aModalWaiting = ref(false);
let templateManageRef=ref(null)
watch(()=>nowPageId ,()=>{
leftSideBarDir.value.assisSetCurrentKey();
})
//useless function
/*
// 依据目录树存储的map全局对象
let treePathDataMap = ref(new Map());
let wikiPageTreeRef = ref();
const searchByKeywords = () => {
wikiPageTreeRef.value.filter(searchKeywords.value)
}
const searchByKeywordsNewPage = () => {
var routeUrl = router.resolve({
path: '/page/search',
query: {keywords: searchKeywords.value}
})
window.open(routeUrl.href, '_blank')
}
*/
onMounted(() => {
loadSpaceList()
loadUserMessageList()
getSelfUserInfo()
});
const openTemplateCreate = (exsit) =>{
templateManageRef.value.showTemplateCreate(exsit)
const rightAsideWidthChange = (width) =>{
storeDisplay.rightAsideWidth = width
storeDisplay.commentShow =width
}
const createWikiByTemplate = () =>{
templateManageRef.value.showTemplateManage()
}
const filterShareStatus = (data) =>{
if (data === 1){
return '公共模板'
}
return '个人模板'
}
const openMoveMenu = (onlyMove) =>{
onlyMoveMode.value = onlyMove
visibleMoveMenu.value =true
moveToPageId.value = nowPageId.value
moveToSpaceId.value = choiceSpace.value
moveToWikiPageList.value = wikiPageList.value
}
const handleOk = (onlyMove) =>{
aModalWaiting.value = true
if (onlyMoveMode.value){
pageApi.movePage({"id": choosePageId.value,"spaceId": choiceSpace.value,"moveToPageId":moveToPageId.value,"moveToSpaceId":moveToSpaceId.value})
.then((json) => {
doGetPageList(null)
ElMessage.success('迁移成功')
handleCancel()
aModalWaiting.value = false
}).catch((e) => {
aModalWaiting.value = false
})
return
}
pageApi.copyPage({"id": choosePageId.value,"spaceId": choiceSpace.value,"moveToPageId":moveToPageId.value,"moveToSpaceId":moveToSpaceId.value})
.then((json) => {
doGetPageList(null)
ElMessage.success('复制成功')
handleCancel()
aModalWaiting.value = false
}).catch((e) => {
aModalWaiting.value = false
})
return
}
const handleCancel = () =>{
visibleMoveMenu.value =false
moveToPageId.value = 0
moveToSpaceId.value = 0
moveToWikiPageList.value = []
}
const deleteWikiPage = (share) => {
let msg = '确定要删除此页面及其所有子页面吗?'
if (share !== undefined){
msg='选中的页面是:' +filterShareStatus(share)+'删除后无法使用此模板! 确定要删除此页面及其所有子页面吗?'
}
ElMessageBox.confirm(msg, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
let param = {pageId: optionPageId.value};
pageApi.pageDelete(param).then(() => {
ElMessage.success('已删除')
doGetPageList(null)
});
}).catch(() => {});
}
const choosePageIdFunc = (id) => {
choosePageId.value = id
}
const setNowPageId = (id,readOnly)=>{
if (readOnly){
moveToPageId.value = id
return
}
nowPageId.value = id
}
const rename = (node,data) => {
data.renaming = true
}
const doRename = (node,data) => {
pageApi.renamePage({"id": data.id,"name": data.name})
.then((json) => {
doGetPageList(null)
ElMessage.success('重命名成功')
data.renaming = false
})
}
const loadPageList = (param) => {
param = param || {}
doGetPageList(param.parentId, param.node)
}
const changeNodeOptionStatus = (param) => {
optionPageId.value = param.id
}
const turnLeftCollapse = () => {
leftCollapse.value = !leftCollapse.value
setTimeout(() => {
if (leftCollapse.value) {
rightAsideWidthChange(rightAsideWidth.value + 1)
} else {
rightAsideWidthChange(1)
}
}, 100)
}
const changeWikiPageExpandedKeys = (pageId) => {
// 展开没有触发子节点的加载如果去加载子节点有还找不到当前的node暂不展开
// wikiPageExpandedKeys.value= [pageId];
}
const loadUserMessageIfPopVisible = () => {
if (!userMessagePopVisible.value) {
loadUserMessageList()
}
}
const loadUserMessageList = () => {
userApi.getUserMessageList(userMsgParam.value).then((res) => {
userMessageList.value = res.data || []
userMsgTotalCount.value = res.total || 0
haveNotReadUserMessage.value =
userMessageList.value.filter((item) => item.msgStatus == 0).length > 0
})
}
const showUserMessage = (row) => {
if (row.msgStatus == 0) {
userApi.readUserMessage({ids: row.id}).then(() => {
loadUserMessageList()
})
}
if (row.msgType >= 2 && row.msgType <= 14) {
router.push({path: '/page/show', query: {pageId: row.dataId}})
userMessagePopVisible.value = false
}
}
const readAllUserMessage = () => {
let msgIds = []
userMessageList.value
.filter((item) => item.msgStatus == 0)
.forEach((item) => {
msgIds.push(item.id)
})
if (msgIds.length <= 0) return
userApi.readUserMessage({ids: msgIds.join(',')}).then(() => {
ElMessage.success('标记成功')
loadUserMessageList()
})
}
const handleCurrentChange = (val) => {
userMsgParam.value.pageNum = val
loadUserMessageList()
}
let createSpaceRef = ref();
const spaceChangeEvents = (data,readonly) => {
if (readonly){
moveToSpaceId.value = data
setNowPageId(0,readonly)
let param = {spaceId:moveToSpaceId.value}
pageApi.pageList(param).then((json)=>{
moveToWikiPageList.value = json.data||[]
})
return
}
if (data === 0) {
// 新建空间
createSpaceRef.value.show();
} else if (data === -1) {
// 管理空间
router.push({path: '/space/manage'});
} else {
nowPageId.value = 0;
choiceSpace.value = Number(data);
nowSpaceShow.value = spaceList.value.find((item) => item.id === data);
storePage.spaceInfo = nowSpaceShow.value;
doGetPageList(null);
router.push({path: '/home', query: {spaceId: data}});
}
}
const loadSpaceList = (spaceId) => {
pageApi.spaceList({}).then((json) => {
spaceList.value = json.data || [];
let spaceOptionsNew = [];
spaceList.value.forEach((item) => spaceOptionsNew.push({label: item.name, value: item.id}));
spaceOptions.value = spaceOptionsNew;
if (spaceList.value.length > 0) {
let nowSpaceId = spaceId;
let nowSpaceShowTemp = spaceList.value.find((item) => item.id === spaceId);
if (!nowSpaceShowTemp) {
nowSpaceShowTemp = spaceList.value[0];
nowSpaceId = nowSpaceShowTemp.id;
}
nowSpaceShow.value = nowSpaceShowTemp;
storePage.spaceInfo = nowSpaceShowTemp;
choiceSpace.value = nowSpaceId;
nowPageId.value = 0;
doGetPageList(null);
// TODO 在首页时跳转
try {
if (route.path === '/home') {
router.push({path: '/home', query: {spaceId: nowSpaceId}});
}
} catch (e) {
console.log(e);
}
}
})
}
const doGetPageList = (parentId, node) => {
let param = {spaceId: choiceSpace.value}
pageApi.pageList(param).then((json) => {
wikiPageList.value = json.data || []
})
}
let aboutDialogRef = ref();
const userSettingDropdown = (command) => {
console.log('command:' + command)
if (command == 'userSignOut') {
userSignOut()
} else if (command == 'aboutDoc') {
aboutDialogRef.value.show()
} else if (command == 'myInfo') {
router.push({path: '/user/myInfo'})
} else if (command == 'console') {
window.open(import.meta.env.VITE_APP_BASE_API, '_blank')
} else {
ElMessage.warning('暂未开放')
}
}
const userSignOut = () => {
userApi.userLogout().then(() => {
location.reload()
})
}
let storeUser = useStoreUserData();
const getSelfUserInfo = () => {
userApi.getSelfUserInfo().then((json) => {
userSelfInfo.value = json.data;
storeUser.userInfo = json.data;
})
}
const getSpaceInfo = (spaceId) => {
for (let i = 0; i < spaceList.value.length; i++) {
if (spaceList.value[i].id == spaceId) {
return spaceList.value[i]
}
}
return {}
}
const switchSpacePage = (spaceId) => {
spaceId = parseInt(spaceId)
if (choiceSpace.value == spaceId) {
return
}
choiceSpace.value = spaceId
doGetPageList(null)
}
const rightAsideWidthChange = (width) => {
storeDisplay.viewMenuWidth = width
}
</script>
<style>
@@ -605,19 +53,20 @@
}
.el-header {
background-color: #1d4e89 !important;
background-color: #fff !important;
}
.header-right-user-name {
color: #fff;
color: #000000;
padding-right: 5px;
vertical-align: middle;
}
.el-header {
color: #333;
line-height: 40px;
height: 40px !important;
line-height: 100px;
height: 60px !important;
border-bottom: 0.5px solid #eaeaea;
}
.fold-btn {
@@ -627,9 +76,10 @@
.head-icon {
margin-right: 15px;
margin-top: 15px;
font-size: 16px;
cursor: pointer;
color: #fff;
color: #000000;
vertical-align: middle;
}

View File

@@ -1,182 +1,402 @@
<template>
<div style="padding: 10px;height: 100%;box-sizing: border-box;background: #fafafa;">
<div style="margin-bottom: 5px">
<el-select :model-value="choiceSpace" filterable placeholder="选择空间" style="width: 100%"
@change="spaceChangeEvents">
<el-option-group label="" v-if="!props.readOnly">
<el-option :key="0" label="创建空间" :value="0"></el-option>
<el-option :key="-1" label="空间管理" :value="-1"></el-option>
</el-option-group>
<el-option-group label=""></el-option-group>
<el-option v-for="item in spaceOptions" :key="item.value" :label="item.label"
:value="item.value"></el-option>
</el-select>
</div>
<el-autocomplete v-model="searchKeywords" v-if="!props.readOnly" :fetch-suggestions="doSearchByKeywords"
placeholder="在当前空间搜索"
popper-class="search-autocomplete" style="width: 100%; margin: 10px 0"
@select="handleSearchKeywordsSelect">
<template v-slot="{ item }">
<div class="search-option-item">
<div class="title">
<span v-html="item.pageTitle || '-'"></span>
<LeftSidebarCli
:readOnly=false
:wikiPageList="storePage.wikiPageList"
:spaceOptions="storeSpace.spaceOptions"
:nowPageId="storePage.choosePageId"
:choiceSpace="storeSpace.chooseSpaceId"
@setNowPageId="setNowPageId"
@doGetPageList="doGetPageList"
@spaceChangeEvents="spaceChangeEvents">
<template v-slot:addMenuDir>
<AddMenu
:choiceSpace="storeSpace.chooseSpaceId"
:choosePageId="storePage.optionPageId"
:nowPageId="storePage.choosePageId"
:funcId="0"
@createWikiByTemplate="createWikiByTemplate"
@choosePageIdFunc="choosePageIdFunc"
@doGetPageList="doGetPageList"
/>
</template>
<template v-slot:addMenuNode="{node,data}">
<div class="page-tree-node" @mouseover="changeNodeOptionStatus(data) ">
<div style="width: calc(100% - 30px);overflow: hidden;text-overflow: ellipsis;white-space: nowrap;">
<!--图标-->
<el-icon v-if="data.editorType === 0" class="clickAddIcon"
style="margin-right: 5px;vertical-align: middle;">
<svg width="1em" height="1em" viewBox="0 0 48 48" fill="none">
<path d="M5 8C5 6.89543 5.89543 6 7 6H19L24 12H41C42.1046 12 43 12.8954 43 14V40C43 41.1046 42.1046 42 41 42H7C5.89543 42 5 41.1046 5 40V8Z"
fill="none" stroke="currentColor" stroke-width="4" stroke-linejoin="round"></path>
<path d="M43 22H5" stroke="currentColor" stroke-width="4" stroke-linejoin="round"></path>
<path d="M5 16V28" stroke="currentColor" stroke-width="4" stroke-linecap="round"
stroke-linejoin="round"></path>
<path d="M43 16V28" stroke="currentColor" stroke-width="4" stroke-linecap="round"
stroke-linejoin="round"></path>
</svg>
</el-icon>
<el-icon v-else-if="data.editorType === 1" class="clickAddIcon"
style="margin-right: 5px;vertical-align: middle;">
<svg width="1em" height="1em" viewBox="0 0 48 48" fill="none">
<rect x="6" y="6" width="36" height="36" rx="3" fill="none" stroke="currentColor"
stroke-width="4"></rect>
<path d="M14 16L18 32L24 19L30 32L34 16" stroke="currentColor" stroke-width="4"
stroke-linecap="round" stroke-linejoin="round"></path>
</svg>
</el-icon>
<el-icon v-else-if="data.editorType === 2" class="clickAddIcon"
style="margin-right: 5px;vertical-align: middle;">
<el-icon-document/>
</el-icon>
<!--标题-->
<el-tooltip :content="data.tags" placement="top-start" :show-after="500"
v-if="data.shareStatus !== undefined">
<a-tag color="#f50">{{filterShareStatus(data.shareStatus)}}</a-tag>
</el-tooltip>
<a-input v-if="data.renaming" v-model:value="data.name" class="rename-input" placeholder="请输入文档名称"
@blur="doRename(node,data)" @click.stop/>
<span v-else style="vertical-align: middle;margin-right: 5px">
<el-tooltip :content="node.label" placement="top-start" :show-after="700">{{ node.label }}</el-tooltip>
</span>
<!--操作-->
<div class="page-action-box" :class="data.renaming?'renaming':''" @click.stop>
<AddMenu
:choiceSpace="storeSpace.chooseSpaceId"
:choosePageId="storePage.optionPageId"
:nowPageId="storePage.choosePageId"
:funcId="data.id"
@createWikiByTemplate="createWikiByTemplate"
@choosePageIdFunc="choosePageIdFunc"
@doGetPageList="doGetPageList"
/>
<a-dropdown :trigger="['click']" @click="choosePageIdFunc(data.id)">
<el-button :icon="MoreFilled" text class="page-action-dropdown-btn"></el-button>
<template #overlay>
<a-menu>
<a-menu-item key="0" @click="rename(node,data)">
<el-icon class="clickAddIcon" style="margin-right: 5px">
<svg width="1em" height="1em" viewBox="0 0 48 48" fill="none">
<path d="M42 26V40C42 41.1046 41.1046 42 40 42H8C6.89543 42 6 41.1046 6 40V8C6 6.89543 6.89543 6 8 6L22 6"
stroke="currentColor" stroke-width="4" stroke-linecap="round"
stroke-linejoin="round"></path>
<path d="M14 26.7199V34H21.3172L42 13.3081L34.6951 6L14 26.7199Z"
fill="none" stroke="currentColor" stroke-width="4"
stroke-linejoin="round"></path>
</svg>
</el-icon>
重命名
</a-menu-item>
<a-menu-item key="1" @click="deleteWikiPage(data.shareStatus)">
<el-icon class="clickAddIcon" style="margin-right: 5px">
<svg width="1em" height="1em" viewBox="0 0 48 48" fill="none">
<path d="M9 10V44H39V10H9Z" fill="none" stroke="currentColor"
stroke-width="4" stroke-linejoin="round"></path>
<path d="M20 20V33" stroke="currentColor" stroke-width="4"
stroke-linecap="round" stroke-linejoin="round"></path>
<path d="M28 20V33" stroke="currentColor" stroke-width="4"
stroke-linecap="round" stroke-linejoin="round"></path>
<path d="M4 10H44" stroke="currentColor" stroke-width="4"
stroke-linecap="round" stroke-linejoin="round"></path>
<path d="M16 10L19.289 4H28.7771L32 10H16Z" fill="none"
stroke="currentColor" stroke-width="4"
stroke-linejoin="round"></path>
</svg>
</el-icon>
删除
</a-menu-item>
<a-sub-menu key="2" title="移动文档">
<template #icon>
<BlockOutlined/>
</template>
<a-menu-item key="3" @click="openMoveMenu(false)">
<el-icon class="clickAddIcon" style="margin-right: 5px">
<DocumentCopy/>
</el-icon>
复制文档
</a-menu-item>
<a-menu-item key="4" @click="openMoveMenu(true)">
<el-icon class="clickAddIcon" style="margin-right: 5px">
<Scissor/>
</el-icon>
迁移文档
</a-menu-item>
</a-sub-menu>
<a-menu-item key="5" @click="openTemplateCreate(data.shareStatus !== undefined)"
v-if="data.editorType !== 0">
<BuildOutlined/>
设为模板
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</div>
<span class="content" v-html="item.previewContent || '-'"></span>
</div>
</template>
</el-autocomplete>
<div class="space-folder-box" v-if="!props.readOnly">
<el-tooltip style="margin: 4px" effect="dark" :content="descriptorForTree" placement="top">
<span style="color:#888;font-size: 12px;cursor: pointer" @click="changeDropWownStatus">空间目录</span>
</el-tooltip>
<slot name="addMenuDir"/>
</div>
<div class="wiki-page-tree-box">
<el-tree
:class="explanClass"
ref="wikiPageTreeRef"
:current-node-key="props.nowPageId"
:data="props.wikiPageList"
:default-expanded-keys="wikiPageExpandedKeys"
:expand-on-click-node="true"
:filter-node-method="filterPageNode"
:props="defaultProps"
:draggable="!props.readOnly"
highlight-current
node-key="id"
style="background-color: #fafafa"
@node-click="handleNodeClick"
@node-expand="handleNodeExpand"
@node-drop="handlePageDrop">
<template v-if="!props.readOnly" v-slot="{ node, data }" >
<slot name="addMenuNode" :node="node" :data="data"></slot>
</template>
</el-tree>
</div>
</div>
</div>
</template>
</LeftSidebarCli>
<templateManage ref="templateManageRef" :pageId="storePage.optionPageId" :spaceId="storeSpace.chooseSpaceId"
@doGetPageList="doGetPageList"/>
<create-space ref="createSpaceRef" @success="loadSpaceList"></create-space>
<a-modal
v-model:open="visibleMoveMenu"
title="选择"
@ok="handleOk"
@cancel="handleCancel"
ok-text="确认"
cancel-text="取消"
:confirm-loading="aModalWaiting"
:destroyOnClose=true
:closable=false>
<LeftSidebarCli
:readOnly=true
:wikiPageList="moveToWikiPageList"
:spaceOptions="storeSpace.spaceOptions"
:nowPageId="moveToPageId"
:choiceSpace="moveToSpaceId"
@setNowPageId="setNowPageId"
@doGetPageList="doGetPageList"
@spaceChangeEvents="spaceChangeEvents"/>
</a-modal>
</template>
<script setup>
import {ref, defineProps, defineEmits, defineExpose} from 'vue';
import {useRouter, useRoute} from "vue-router";
import {
Document as ElIconDocument,
Fold as ElIconFold,
Expand as ElIconExpand,
Upload as ElIconUpload,
Bell as ElIconBell,
Setting as ElIconSetting,
Plus as ElIconPlus,
Check as ElIconCheck,
Files,
Scissor,
DocumentCopy,
MoreFilled
} from '@element-plus/icons-vue'
import {ref, onMounted,} from 'vue';
import { useRouter, useRoute} from "vue-router";
import {ElMessageBox, ElMessage} from 'element-plus'
import pageApi from '../../assets/api/page'
let emit = defineEmits(['doGetPageList', 'spaceChangeEvents', 'setNowPageId'])
let searchKeywords = ref('');
let descriptorForTree = ref("点击收起目录");
let explan = ref(false);
let explanClass = ref("el-tree");
let wikiPageExpandedKeys = ref([]);
import CreateSpace from '../space/CreateSpace'
import TemplateManage from '../template/TemplateManage'
import RightResize from './RightResize.vue'
import AddMenu from '../LeftSidebar/AddMenu.vue'
import LeftSidebarCli from '../LeftSidebar/LeftSidebarCli.vue'
import {useStoreDisplay} from '@/store/wikiDisplay.js'
import {useStorePageData} from "@/store/pageData";
import {DownOutlined, BuildOutlined, BlockOutlined} from '@ant-design/icons-vue';
import {useStoreSpaceData} from "@/store/spaceData";
let route = useRoute();
let router = useRouter();
let defaultProps = ref({children: 'children', label: 'name',});
let storePage = useStorePageData();
let storeDisplay = useStoreDisplay();
let storeSpace = useStoreSpaceData();
// 空间搜索相关
let nowSpaceShow = ref({});
let moveToPageId = ref(0);
let moveToSpaceId = ref(0);
let moveToWikiPageList = ref([]);
// 页面展示相关
let wikiPage = ref({});
let wikiPageTreeRef = ref();
let wikiPageExpandedKeys = ref([]);
let rightAsideWidth = ref(300);
let optionPageId = ref('');
let visibleMoveMenu = ref(false);
let onlyMoveMode = ref(false);
let aModalWaiting = ref(false);
let templateManageRef = ref(null)
let props = defineProps({
wikiPageList: Array,
spaceOptions: Array,
nowPageId: Number,
choiceSpace: Number,
readOnly: Boolean
onMounted(()=>{
init()
})
const init = () => {
loadSpaceList()
}
const openTemplateCreate = (exsit) => {
templateManageRef.value.showTemplateCreate(exsit)
}
const assisSetCurrentKey = () => {
emit('setNowPageId', route.query.pageId, props.readOnly)
if (props.nowPageId) {
wikiPageTreeRef.value.setCurrentKey(nowPageId.value)
const createWikiByTemplate = () => {
templateManageRef.value.showTemplateManage()
}
const filterShareStatus = (data) => {
if (data === 1) {
return '公共模板'
}
return '个人模板'
}
const changeWikiPageExpandedKeys = (pageId) => {
// 展开没有触发子节点的加载如果去加载子节点有还找不到当前的node暂不展开
// wikiPageExpandedKeys.value= [pageId];
const openMoveMenu = (onlyMove) => {
onlyMoveMode.value = onlyMove
visibleMoveMenu.value = true
moveToPageId.value = storePage.choosePageId
moveToSpaceId.value = storeSpace.chooseSpaceId
moveToWikiPageList.value = storePage.wikiPageList
}
const spaceChangeEvents = (data) => {
emit('spaceChangeEvents', data, props.readOnly)
}
const doSearchByKeywords = (queryString, callback) => {
if (!queryString || !queryString.trim()) {
callback([])
const handleOk = (onlyMove) => {
aModalWaiting.value = true
if (onlyMoveMode.value) {
pageApi.movePage({
"id": storePage.optionPageId,
"spaceId": storeSpace.chooseSpaceId,
"moveToPageId": moveToPageId.value,
"moveToSpaceId": moveToSpaceId.value
})
.then((json) => {
doGetPageList(null)
ElMessage.success('迁移成功')
handleCancel()
aModalWaiting.value = false
}).catch((e) => {
aModalWaiting.value = false
})
return
}
pageApi
.pageNews({spaceId: props.choiceSpace, keywords: queryString})
pageApi.copyPage({
"id": storePage.optionPageId,
"spaceId": storeSpace.chooseSpaceId,
"moveToPageId": moveToPageId.value,
"moveToSpaceId": moveToSpaceId.value
})
.then((json) => {
let spacePageNews = json.data || []
callback(spacePageNews)
doGetPageList(null)
ElMessage.success('复制成功')
handleCancel()
aModalWaiting.value = false
}).catch((e) => {
aModalWaiting.value = false
})
return
}
const handleCancel = () => {
visibleMoveMenu.value = false
moveToPageId.value = 0
moveToSpaceId.value = 0
moveToWikiPageList.value = []
}
const deleteWikiPage = (share) => {
let msg = '确定要删除此页面及其所有子页面吗?'
if (share !== undefined) {
msg = '选中的页面是:' + filterShareStatus(share) + '删除后无法使用此模板! 确定要删除此页面及其所有子页面吗?'
}
ElMessageBox.confirm(msg, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
let param = {pageId: optionPageId.value};
pageApi.pageDelete(param).then(() => {
ElMessage.success('已删除')
doGetPageList(null)
});
}).catch(() => {
});
}
const choosePageIdFunc = (id) => {
storePage.optionPageId = id
}
const setNowPageId = (id, readOnly) => {
if (readOnly) {
moveToPageId.value = id
return
}
storePage.choosePageId = id
}
const rename = (node, data) => {
data.renaming = true
}
const doRename = (node, data) => {
pageApi.renamePage({"id": data.id, "name": data.name})
.then((json) => {
doGetPageList(null)
ElMessage.success('重命名成功')
data.renaming = false
})
}
const handleSearchKeywordsSelect = (item) => {
searchKeywords.value = ''
router.push({path: '/page/show', query: {pageId: item.pageId}})
}
const changeDropWownStatus = () => {
if (explan.value) {
explanClass.value = "el-tree"
descriptorForTree.value = "点击收起目录"
explan.value = false
const changeNodeOptionStatus = (param) => {
optionPageId.value = param.id
}
let createSpaceRef = ref();
const spaceChangeEvents = (data, readonly) => {
storePage.pageInfo = {}
if (readonly) {
moveToSpaceId.value = data
setNowPageId(0, readonly)
let param = {spaceId: moveToSpaceId.value}
pageApi.pageList(param).then((json) => {
moveToWikiPageList.value = json.data || []
})
return
}
if (data === 0) {
// 新建空间
createSpaceRef.value.show();
} else if (data === -1) {
// 管理空间
router.push({path: '/space/manage'});
} else {
explanClass.value = "hidTree"
descriptorForTree.value = "点击展开目录"
explan.value = true
storePage.choosePageId = 0;
storeSpace.chooseSpaceId = Number(data);
nowSpaceShow.value = storeSpace.spaceList.find((item) => item.id === data);
storeSpace.spaceInfo = nowSpaceShow.value;
doGetPageList(null);
router.push({path: '/home', query: {spaceId: data}});
}
}
const filterPageNode = (value, data) => {
if (!value || !data.name) return true;
// issues:I2CG72 忽略大小写
let name = data.name.toLowerCase();
return name.indexOf(value.toLowerCase()) !== -1;
}
const searchByKeywords = () => {
wikiPageTreeRef.value.filter(searchKeywords.value)
}
const handleNodeClick = (data) => {
//console.log('点击节点:', data, props.nowPageId)
emit('setNowPageId', data.id, props.readOnly)
if (props.readOnly) {
return
}
if (data.editorType !== 0) {
router.push({path: '/page/show', query: {pageId: data.id}})
}
handleNodeExpand(data)
}
const handleNodeExpand = (node) => {
if (props.readOnly) {
return
}
if (
node.children &&
node.children.length > 0 &&
node.children[0].needLoad
) {
console.log('加载节点:', node)
emit('doGetPageList',node.id, node)
}
}
const handlePageDrop = (draggingNode, dropNode, dropType, ev) => {
console.log('tree drop: ', draggingNode.data, dropNode.data, dropType)
// 'prev'、'inner'、'next'
// before、after、inner
var param = {id: draggingNode.data.id, parentId: dropNode.data.parentId}
if (dropType == 'inner') {
param.parentId = dropNode.data.id
} else if (dropType == 'before') {
param.beforeSeq = dropNode.data.seqNo
} else if (dropType == 'after') {
param.afterSeq = dropNode.data.seqNo
}
pageApi.pageChangeParent(param).then((res) => {
emit('doGetPageList',node.id, node)
const loadSpaceList = (spaceId) => {
pageApi.spaceList({}).then((json) => {
storeSpace.spaceList = json.data || [];
let spaceOptionsNew = [];
storeSpace.spaceList.forEach((item) => spaceOptionsNew.push({label: item.name, value: item.id}));
storeSpace.spaceOptions = spaceOptionsNew;
if (storeSpace.spaceList.length > 0) {
let nowSpaceId = spaceId;
let nowSpaceShowTemp = storeSpace.spaceList.find((item) => item.id === spaceId);
if (!nowSpaceShowTemp) {
nowSpaceShowTemp = storeSpace.spaceList[0];
nowSpaceId = nowSpaceShowTemp.id;
}
nowSpaceShow.value = nowSpaceShowTemp;
storeSpace.spaceInfo = nowSpaceShowTemp;
storeSpace.chooseSpaceId = nowSpaceId;
storePage.choosePageId = 0;
doGetPageList(null);
// TODO 在首页时跳转
try {
if (route.path === '/home') {
router.push({path: '/home', query: {spaceId: nowSpaceId}});
}
} catch (e) {
console.log(e);
}
}
})
}
defineExpose({searchByKeywords})
const doGetPageList = (parentId, node) => {
let param = {spaceId: storeSpace.chooseSpaceId}
pageApi.pageList(param).then((json) => {
storePage.wikiPageList = json.data || []
})
}
defineExpose({init})
</script>

View File

@@ -0,0 +1,420 @@
<template>
<el-row class="right-header-box">
<el-col :span="1">
<el-button @click="turnLeftCollapse" v-if="storeDisplay.showMenu" text :icon="ElIconFold" class="fold-btn"></el-button>
<el-button @click="turnLeftCollapse" v-else text :icon="ElIconExpand" class="fold-btn"></el-button>
</el-col>
<el-col :span="9" class="title-info-view">
<div class="wikititle" ref="wikiTitleRef" v-if="storePage.pageInfo.name">
<span class="create-user-time">
{{storePage.pageInfo.name}}
</span>
<br/>
<span v-if="storePage.pageInfo.updateUserName">
{{ storePage.pageInfo.updateUserName }}
<span class="split"></span>
{{ storePage.pageInfo.updateTime }}
<span class="split">修改</span>
</span>
<span v-else class="create-user-time">
{{ storePage.pageInfo.createUserName }}
<span class="split"></span>
{{ storePage.pageInfo.createTime }}
<span class="split">创建</span>
</span>
</div>
</el-col>
<el-col :span="12" style="text-align: right">
<el-tooltip v-if="storePage.pageAuth.canEdit === 1 && storePage.pageInfo.name && storePage.pageInfo.editorType !== 0" effect="dark" content="编辑文档"
placement="top">
<ElIconEdit @click="editWiki" type="primary" class="right-header-icon"></ElIconEdit>
</el-tooltip>
<el-tooltip v-if="storePage.pageAuth.canEdit === 1 && storePage.pageInfo.name && storePage.pageInfo.editorType !== 0" effect="dark" content="文档相关"
placement="top">
<ElIconChatLineRound @click="showCommentWiki" class="right-header-icon"></ElIconChatLineRound>
</el-tooltip>
<el-tooltip v-if="storePage.pageInfo.name" effect="dark" content="更多操作" placement="top">
<el-dropdown trigger="click" class="action-btn more-dropdown" >
<el-icon class="right-header-icon">
<el-icon-arrow-down/>
</el-icon>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="editWikiAuth" v-if="storePage.pageAuth.canConfigAuth === 1 && storePage.pageInfo.editorType !== 0"
:icon="ElIconSCheck">权限设置
</el-dropdown-item>
<el-dropdown-item @click="showOpenPage" v-if="storeSpace.spaceInfo.openDoc === 1 && storePage.pageInfo.editorType !== 0"
:icon="ElIconShare">查看开放文档
</el-dropdown-item>
<el-dropdown-item @click="showMobileView" v-if="storeSpace.spaceInfo.openDoc === 1 && storePage.pageInfo.editorType !== 0"
:icon="ElIconMobilePhone">手机端查看
</el-dropdown-item>
<el-dropdown-item @click="exportWord" :icon="ElIconDownload" v-if="storePage.pageInfo.editorType !== 0">导出为Word文档</el-dropdown-item>
<el-dropdown-item @click="deleteWikiPage" v-if="storePage.pageAuth.canDelete === 1"
:icon="ElIconDelete">删除
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</el-tooltip>
<span class="header-right-user-name">{{userSelfInfo.userName}}</span>
<el-popover v-model:visible="userMessagePopVisible" placement="bottom" trigger="click" width="600">
<template v-slot:reference>
<el-badge :is-dot="haveNotReadUserMessage" style="margin: 0 20px">
<el-icon class="right-header-icon" style="margin-right: 0">
<el-icon-bell/>
</el-icon>
</el-badge>
</template>
<div style="margin-bottom: 10px">
<span style="font-size: 14px; font-weight: bold">通知</span>
<el-link v-if="haveNotReadUserMessage" :icon="ElIconCheck" style="float: right" type="primary"
@click="readAllUserMessage">本页标记已读
</el-link>
</div>
<div class="header-user-message">
<el-table :data="userMessageList" border max-height="500" size="small"
style="width: 100%; margin-bottom: 5px">
<el-table-column label="操作人" prop="operatorUserName" width="100px"></el-table-column>
<el-table-column label="操作时间" prop="creationTime" width="140px"></el-table-column>
<el-table-column label="内容">
<template v-slot="scope">
{{ scope.row.msgContent }}
<el-badge :is-dot="scope.row.msgStatus == 0"
style="line-height: 10px; padding-right: 5px">
<el-link type="primary" @click="showUserMessage(scope.row)">查看</el-link>
</el-badge>
</template>
</el-table-column>
</el-table>
<div class="page-info-box">
<el-pagination
:current-page="userMsgParam.pageNum"
:page-size="userMsgParam.pageSize"
:total="userMsgTotalCount"
layout="prev, pager, next, total"
@current-change="handleCurrentChange">
</el-pagination>
</div>
</div>
</el-popover>
<el-dropdown trigger="click" @command="userSettingDropdown" style="vertical-align: middle;">
<el-icon class="right-header-icon">
<el-icon-setting/>
</el-icon>
<template v-slot:dropdown>
<el-dropdown-menu>
<el-dropdown-item command="console">控制台</el-dropdown-item>
<el-dropdown-item command="aboutDoc">关于</el-dropdown-item>
<el-dropdown-item command="userSignOut" divided>退出登录</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</el-col>
</el-row>
<MobileQrScanDialog v-model:visible="mobileScanDialogVisible"/>
<PageAuthDialog v-model:visible="pageAuthDialogVisible"/>
<about-dialog ref="aboutDialogRef"></about-dialog>
<form method="post" ref="downloadFormRef" :action="downloadFormParam.url" target="_blank">
<input type="hidden" :name="key" :value="val" v-for="(val, key) in downloadFormParam.param"/>
</form>
</template>
<script setup>
import {
Setting as ElIconSetting,
ArrowDown as ElIconArrowDown,
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,
Bell as ElIconBell,
Share as ElIconShare,
Iphone as ElIconMobilePhone,
Download as ElIconDownload,
Fold as ElIconFold,
Expand as ElIconExpand,
Check as ElIconCheck,
Back as ElIconBack,
} from '@element-plus/icons-vue'
import {toRefs, ref, reactive, onMounted, watch, computed} from 'vue';
import {onBeforeRouteUpdate, useRoute, useRouter} from "vue-router";
import {ElMessageBox, ElMessage, ElNotification} from 'element-plus';
import pageApi from '@/assets/api/page'
import PageAuthDialog from '../../views/page/show/PageAuthDialog.vue'
import MobileQrScanDialog from '../../views/page/show/MobileQrScanDialog.vue'
import {useStorePageData} from "@/store/pageData";
import {useStoreDisplay} from "@/store/wikiDisplay";
import {useStoreSpaceData} from "@/store/spaceData";
import {useStoreUserData} from "@/store/userData";
import userApi from "@/assets/api/user";
import AboutDialog from "../../views/common/AboutDialog"
let storePage = useStorePageData();
let storeDisplay = useStoreDisplay();
let router = useRouter();
let storeSpace = useStoreSpaceData();
let storeUser = useStoreUserData();
let userSelfInfo = ref({});
let userMessageList = ref([]);
let haveNotReadUserMessage = ref(false);
let userMessagePopVisible = ref(false);
let userMsgTotalCount = ref(0);
let userMsgParam = ref({sysType: 2, pageNum: 1, pageSize: 20,});
let titleInput = ref('')
onMounted(()=>{
init()
})
const init=()=>{
getSelfUserInfo()
loadUserMessageList();
}
let pageAuthDialogVisible = ref(false);
const editWikiAuth = () => {
pageAuthDialogVisible.value = true;
}
const showOpenPage = () => {
if (storePage.spaceInfo.openDoc !== 1) {
ElMessage.warning('该空间未开放,无法查看开放文档地址');
} else {
let routeUrl = router.resolve({
path: '/page/share/view',
query: {pageId: storePage.pageInfo.id, space: storePage.spaceInfo.uuid}
});
window.open(routeUrl.href, '_blank');
}
}
const deleteWikiPage = () => {
ElMessageBox.confirm('确定要删除此页面及其所有子页面吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
let param = {pageId: storeSpace.pageInfo.id};
pageApi.pageDelete(param).then(() => {
// 重新加载左侧列表,跳转到展示页面
// emit('loadPageList'); TODO
router.push({path: '/home', query: {spaceId: storeSpace.pageInfo.spaceId}});
});
}).catch(() => {
});
}
// 下载为Word
let downloadFormRef = ref();
let downloadFormParam = ref({url: 'zyplayer-doc-wiki/page/download', param: {}});
const exportWord = () => {
downloadFormParam.value.param = {pageId: storePage.pageInfo.id};
setTimeout(() => downloadFormRef.value.submit(), 0);
}
// 手机扫码
let mobileScanDialogVisible = ref(false);
const showMobileView = () => {
if (storeSpace.spaceInfo.openDoc !== 1) {
ElMessage.warning('该空间未开放,无法查看开放文档地址');
} else {
mobileScanDialogVisible.value = true;
}
}
const showCommentWiki = () => {
storePage.commentShow = true;
storePage.commentActiveTab = 'comment';
}
const editWiki = () => {
// 锁定页面并进入编辑页面
storePage.pageIsUnlock = false
titleInput.value = storePage.pageInfo.name
let param = {pageId: storePage.pageInfo.id};
pageApi.pageLock(param).then(() => {
router.push({path: '/page/edit', query: {pageId: storePage.pageInfo.id}});
});
}
const turnLeftCollapse = () => {
storeDisplay.showMenu = !storeDisplay.showMenu
setTimeout(() => {
if (storeDisplay.showMenu) {
storeDisplay.rightAsideWidth = 301
} else {
storeDisplay.rightAsideWidth = 1
}
}, 100)
}
const loadUserMessageIfPopVisible = () => {
if (!userMessagePopVisible.value) {
loadUserMessageList()
}
}
const loadUserMessageList = () => {
userApi.getUserMessageList(userMsgParam.value).then((res) => {
userMessageList.value = res.data || []
userMsgTotalCount.value = res.total || 0
haveNotReadUserMessage.value =
userMessageList.value.filter((item) => item.msgStatus == 0).length > 0
})
}
let aboutDialogRef = ref()
const userSettingDropdown = (command) => {
console.log('command:' + command)
if (command == 'userSignOut') {
userSignOut()
} else if (command == 'aboutDoc') {
aboutDialogRef.value.show()
} else if (command == 'myInfo') {
router.push({path: '/user/myInfo'})
} else if (command == 'console') {
window.open(import.meta.env.VITE_APP_BASE_API, '_blank')
} else {
ElMessage.warning('暂未开放')
}
}
const userSignOut = () => {
userApi.userLogout().then(() => {
location.reload()
})
}
const readAllUserMessage = () => {
let msgIds = []
userMessageList.value
.filter((item) => item.msgStatus == 0)
.forEach((item) => {
msgIds.push(item.id)
})
if (msgIds.length <= 0) return
userApi.readUserMessage({ids: msgIds.join(',')}).then(() => {
ElMessage.success('标记成功')
loadUserMessageList()
})
}
const showUserMessage = (row) => {
if (row.msgStatus == 0) {
userApi.readUserMessage({ids: row.id}).then(() => {
loadUserMessageList()
})
}
if (row.msgType >= 2 && row.msgType <= 14) {
router.push({path: '/page/show', query: {pageId: row.dataId}})
userMessagePopVisible.value = false
}
}
const handleCurrentChange = (val) => {
userMsgParam.value.pageNum = val
loadUserMessageList()
}
const getSelfUserInfo = () => {
userApi.getSelfUserInfo().then((json) => {
userSelfInfo.value = json.data
storeUser.userInfo = json.data
})
}
defineExpose({init})
</script>
<style scoped lang="scss">
.page-action-box {
padding: 30px 0;
.page-create-info {
font-size: 14px;
color: #888;
.split {
padding: 0 4px;
}
}
.page-action-list {
text-align: right;
}
}
</style>
<style lang="scss">
.right-header-box {
.page-action-list {
text-align: right;
.el-icon {
margin-right: 4px;
}
.action-btn + .action-btn {
margin-left: 15px;
}
.upload-page-file {
display: inline;
vertical-align: middle;
}
.more-dropdown {
vertical-align: middle;
}
}
}
</style>
<style lang="scss">
.right-header-box{
padding: 5px 0;
overflow: hidden;;
white-space: nowrap;
-o-text-overflow: ellipsis;
text-overflow: ellipsis;
line-height: 28px;
.fold-btn{
margin-top: 7px;
padding: 10px 0;
color: #3d3a3a !important;
font-size: 18px;
}
.title-info-view{
font-size: 14px;
color: #454343;
.split{
padding: 0 4px;
}
}
.title-info-view-right{
text-align: right;
margin-left: 5px;
font-size: 14px;
color: #454343;
.split{
padding: 0 4px;
}
}
.page-title-input{
margin-top: 3px;
margin-left: 5px;
width: 100%;
height: 45px;
}
.page-info-news{
font-size: 14px;
line-height: 20px;
margin-top: 3px
}
.right-header-icon{
cursor: pointer;
width: 20px;
margin: 15px 10px;
}
}
</style>

View File

@@ -18,7 +18,7 @@ import {onBeforeUnmount, ref, onMounted, watch, defineProps, nextTick, defineEmi
import {onBeforeRouteUpdate, useRouter, useRoute} from "vue-router";
import {ElMessageBox, ElMessage} from 'element-plus'
import pageApi from '../../assets/api/page'
import PageTree from './PageTree'
import PageTree from '../shareLayout/PageTree'
import 'vant/es/icon/style/index';
import 'vant/es/popup/style/index';
import 'vant/es/cell/style/index';

View File

@@ -0,0 +1,191 @@
<template>
<div style="padding: 10px;height: 100%;box-sizing: border-box;background: #fafafa;">
<div style="margin-bottom: 5px">
<el-select :model-value="choiceSpace" filterable placeholder="选择空间" style="width: 100%"
@change="spaceChangeEvents">
<el-option-group label="" v-if="!props.readOnly">
<el-option :key="0" label="创建空间" :value="0"></el-option>
<el-option :key="-1" label="空间管理" :value="-1"></el-option>
</el-option-group>
<el-option-group label=""></el-option-group>
<el-option v-for="item in spaceOptions" :key="item.value" :label="item.label"
:value="item.value"></el-option>
</el-select>
</div>
<el-autocomplete v-model="searchKeywords" v-if="!props.readOnly" :fetch-suggestions="doSearchByKeywords"
placeholder="在当前空间搜索"
popper-class="search-autocomplete" style="width: 100%; margin: 10px 0"
@select="handleSearchKeywordsSelect">
<template v-slot="{ item }">
<div class="search-option-item">
<div class="title">
<span v-html="item.pageTitle || '-'"></span>
</div>
<span class="content" v-html="item.previewContent || '-'"></span>
</div>
</template>
</el-autocomplete>
<div class="space-folder-box" v-if="!props.readOnly">
<el-tooltip style="margin: 4px" effect="dark" :content="descriptorForTree" placement="top">
<span style="color:#888;font-size: 12px;cursor: pointer" @click="changeDropWownStatus">空间目录</span>
</el-tooltip>
<slot name="addMenuDir"/>
</div>
<div class="wiki-page-tree-box">
<el-tree
:class="explanClass"
ref="wikiPageTreeRef"
:current-node-key="props.nowPageId"
:data="props.wikiPageList"
:default-expanded-keys="wikiPageExpandedKeys"
:expand-on-click-node="true"
:filter-node-method="filterPageNode"
:props="defaultProps"
:draggable="!props.readOnly"
highlight-current
node-key="id"
style="background-color: #fafafa"
@node-click="handleNodeClick"
@node-expand="handleNodeExpand"
@node-drop="handlePageDrop">
<template v-if="!props.readOnly" v-slot="{ node, data }" >
<slot name="addMenuNode" :node="node" :data="data"></slot>
</template>
</el-tree>
</div>
</div>
</template>
<script setup>
import {ref, defineProps, defineEmits, defineExpose} from 'vue';
import {useRouter, useRoute} from "vue-router";
import pageApi from '../../assets/api/page'
import {useStoreDisplay} from "@/store/wikiDisplay";
import {useStorePageData} from "@/store/pageData";
let emit = defineEmits(['doGetPageList', 'spaceChangeEvents', 'setNowPageId'])
let searchKeywords = ref('');
let descriptorForTree = ref("点击收起目录");
let explan = ref(false);
let explanClass = ref("el-tree");
let wikiPageExpandedKeys = ref([]);
let route = useRoute();
let router = useRouter();
let defaultProps = ref({children: 'children', label: 'name',});
let wikiPage = ref({});
let wikiPageTreeRef = ref();
let storeDisplay = useStoreDisplay();
let props = defineProps({
wikiPageList: Array,
spaceOptions: Array,
nowPageId: Number,
choiceSpace: Number,
readOnly: Boolean
})
const assisSetCurrentKey = () => {
emit('setNowPageId', route.query.pageId, props.readOnly)
if (props.nowPageId) {
wikiPageTreeRef.value.setCurrentKey(nowPageId.value)
}
}
const changeWikiPageExpandedKeys = (pageId) => {
// 展开没有触发子节点的加载如果去加载子节点有还找不到当前的node暂不展开
// wikiPageExpandedKeys.value= [pageId];
}
const spaceChangeEvents = (data) => {
emit('spaceChangeEvents', data, props.readOnly)
}
const doSearchByKeywords = (queryString, callback) => {
if (!queryString || !queryString.trim()) {
callback([])
return
}
pageApi
.pageNews({spaceId: props.choiceSpace, keywords: queryString})
.then((json) => {
let spacePageNews = json.data || []
callback(spacePageNews)
})
}
const handleSearchKeywordsSelect = (item) => {
searchKeywords.value = ''
router.push({path: '/page/show', query: {pageId: item.pageId}})
}
const changeDropWownStatus = () => {
if (explan.value) {
explanClass.value = "el-tree"
descriptorForTree.value = "点击收起目录"
explan.value = false
} else {
explanClass.value = "hidTree"
descriptorForTree.value = "点击展开目录"
explan.value = true
}
}
const filterPageNode = (value, data) => {
if (!value || !data.name) return true;
// issues:I2CG72 忽略大小写
let name = data.name.toLowerCase();
return name.indexOf(value.toLowerCase()) !== -1;
}
const searchByKeywords = () => {
wikiPageTreeRef.value.filter(searchKeywords.value)
}
let storePage = useStorePageData();
const handleNodeClick = (data) => {
//console.log('点击节点:', data, props.nowPageId)
storeDisplay.showHeader = true
emit('setNowPageId', data.id, props.readOnly)
if (props.readOnly) {
return
}
if (data.editorType !== 0) {
router.push({path: '/page/show', query: {pageId: data.id}})
}
if (data.editorType === 0) {
storePage.pageInfo = data
router.push({path: '/home', query: {spaceId:props.choiceSpace,dirId: data.id}})
}
handleNodeExpand(data)
}
const handleNodeExpand = (node) => {
if (props.readOnly) {
return
}
if (
node.children &&
node.children.length > 0 &&
node.children[0].needLoad
) {
console.log('加载节点:', node)
emit('doGetPageList',node.id, node)
}
}
const handlePageDrop = (draggingNode, dropNode, dropType, ev) => {
console.log('tree drop: ', draggingNode.data, dropNode.data, dropType)
// 'prev'、'inner'、'next'
// before、after、inner
var param = {id: draggingNode.data.id, parentId: dropNode.data.parentId}
if (dropType == 'inner') {
param.parentId = dropNode.data.id
} else if (dropType == 'before') {
param.beforeSeq = dropNode.data.seqNo
} else if (dropType == 'after') {
param.afterSeq = dropNode.data.seqNo
}
pageApi.pageChangeParent(param).then((res) => {
emit('doGetPageList',node.id, node)
})
}
defineExpose({searchByKeywords})
</script>