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

@@ -21,7 +21,7 @@
"highlight.js": "^11.7.0",
"js-cookie": "^2.2.1",
"mavon-editor": "^3.0.0-beta",
"pinia": "^2.1.4",
"pinia": "^2.1.6",
"pouchdb": "^7.3.1",
"qrcode": "^1.5.1",
"qs": "^6.11.2",
@@ -7355,9 +7355,9 @@
}
},
"node_modules/pinia": {
"version": "2.1.4",
"resolved": "https://registry.npmmirror.com/pinia/-/pinia-2.1.4.tgz",
"integrity": "sha512-vYlnDu+Y/FXxv1ABo1vhjC+IbqvzUdiUC3sfDRrRyY2CQSrqqaa+iiHmqtARFxJVqWQMCJfXx1PBvFs9aJVLXQ==",
"version": "2.1.6",
"resolved": "https://registry.npmmirror.com/pinia/-/pinia-2.1.6.tgz",
"integrity": "sha512-bIU6QuE5qZviMmct5XwCesXelb5VavdOWKWaB17ggk++NUwQWWbP5YnsONTk3b752QkW9sACiR81rorpeOMSvQ==",
"dependencies": {
"@vue/devtools-api": "^6.5.0",
"vue-demi": ">=0.14.5"
@@ -14793,9 +14793,9 @@
"optional": true
},
"pinia": {
"version": "2.1.4",
"resolved": "https://registry.npmmirror.com/pinia/-/pinia-2.1.4.tgz",
"integrity": "sha512-vYlnDu+Y/FXxv1ABo1vhjC+IbqvzUdiUC3sfDRrRyY2CQSrqqaa+iiHmqtARFxJVqWQMCJfXx1PBvFs9aJVLXQ==",
"version": "2.1.6",
"resolved": "https://registry.npmmirror.com/pinia/-/pinia-2.1.6.tgz",
"integrity": "sha512-bIU6QuE5qZviMmct5XwCesXelb5VavdOWKWaB17ggk++NUwQWWbP5YnsONTk3b752QkW9sACiR81rorpeOMSvQ==",
"requires": {
"@vue/devtools-api": "^6.5.0",
"vue-demi": ">=0.14.5"

View File

@@ -21,7 +21,7 @@
"highlight.js": "^11.7.0",
"js-cookie": "^2.2.1",
"mavon-editor": "^3.0.0-beta",
"pinia": "^2.1.4",
"pinia": "^2.1.6",
"pouchdb": "^7.3.1",
"qrcode": "^1.5.1",
"qs": "^6.11.2",

View File

@@ -1,9 +1,11 @@
export default {
createNavigationHeading() {
let headArr = []
let headNodeArr = document
.querySelector('.wiki-page-content')
.querySelectorAll('h1,h2,h3,h4,h5,h6')
let headNode = document.querySelector('.wiki-page-content')
if (null === headNode){
return headArr
}
let headNodeArr = headNode.querySelectorAll('h1,h2,h3,h4,h5,h6')
if (headNodeArr.length <= 0) {
return []
}

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>

View File

@@ -3,10 +3,13 @@ import {defineStore} from 'pinia'
export const useStorePageData = defineStore('pageData', {
state: () => {
return {
spaceInfo: {},
pageInfo: {},
pageAuth: {},
fileList: [],
choosePageId: 0,
optionPageId: 0,
wikiPageList: [],
pageIsUnLock: false,
}
},
});

View File

@@ -0,0 +1,12 @@
import {defineStore} from 'pinia'
export const useStoreSpaceData = defineStore('spaceData', {
state: () => {
return {
spaceInfo:{},
chooseSpaceId:1,
spaceOptions: [],
spaceList:[]
}
},
});

View File

@@ -8,6 +8,9 @@ export const useStoreDisplay = defineStore('wikiDisplay', {
// 是否显示右边评论栏
commentShow: false,
commentActiveTab: 'comment',
showMenu: true,
rightAsideWidth: 300,
showHeader:true
}
},
})

View File

@@ -78,6 +78,7 @@ const initQueryParam = (to) => {
newsType: 1,
pageNum: 1,
pageSize: 20,
dirId: to.query.dirId
}
if (!!searchParam.value.spaceId) {
getSpacePageNews()

View File

@@ -1,29 +1,30 @@
<template>
<div style="height: 100%" class="page-edit-vue">
<div style="box-sizing: border-box; background: #f5f5f5; overflow: hidden">
<div style="padding: 5px; font-size: 14px; background: #fff">
<el-row>
<el-col :span="16" style="text-align: left">
<el-input v-if="wikiPageEdit.editorType===2" v-model="wikiPageEdit.pageTitle" :maxlength="40" placeholder="请输入标题" class="page-title-input" ></el-input>
</el-col>
<el-col :span="8" style="text-align: right;margin-top: 4px;">
<el-button type="primary" @click="createWikiSave(1)" size="small" :icon="ElIconDocumentChecked">保存并查看</el-button>
<el-button type="success" @click="createWikiSave(0)" size="small" :icon="ElIconCheck">仅保存</el-button>
<el-button @click="createWikiCancel" size="small" :icon="ElIconBack" style="margin-right: 5px">取消</el-button>
</el-col>
</el-row>
</div>
<el-row class="fake-header">
<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="17" style="text-align: left">
<el-input v-if="wikiPageEdit.editorType===2" v-model="wikiPageEdit.pageTitle" :maxlength="40" placeholder="请输入标题" class="page-title-input" ></el-input>
</el-col>
<el-col :span="5" class="title-info-view-right">
<el-button type="primary" @click="createWikiSave(1)" size="large" :icon="ElIconDocumentChecked">保存</el-button>
<el-button @click="createWikiCancel" size="large" :icon="ElIconBack" style="margin-right: 5px">取消</el-button>
</el-col>
</el-row>
<div style="box-sizing: border-box;background: #f5f5f5;overflow: hidden">
<div v-show="wikiPageEdit.editorType === 2" style="padding: 0 10px 10px 10px; background: #fff">
<mavonEditor
ref="mavonEditorRef"
v-model="markdownContent"
:toolbars="toolbars"
:externalLink="false"
style="height: calc(100vh - 155px)"
@save="createWikiSave(0)"
@imgAdd="addMarkdownImage"
placeholder="请录入文档内容"
class="page-content-editor wang-editor-body"
ref="mavonEditorRef"
v-model="markdownContent"
:toolbars="toolbars"
:externalLink="false"
style="height: calc(100vh - 155px)"
@save="createWikiSave(0)"
@imgAdd="addMarkdownImage"
placeholder="请录入文档内容"
class="page-content-editor wang-editor-body"
/>
</div>
<div v-show="wikiPageEdit.editorType === 1">
@@ -39,6 +40,8 @@ import {onBeforeRouteUpdate, useRouter, useRoute} from "vue-router";
import {ElMessageBox, ElMessage} from 'element-plus'
import {
DocumentChecked as ElIconDocumentChecked,
Fold as ElIconFold,
Expand as ElIconExpand,
Check as ElIconCheck,
Back as ElIconBack,
} from '@element-plus/icons-vue'
@@ -48,6 +51,9 @@ import 'mavon-editor/dist/markdown/github-markdown.min.css'
import 'mavon-editor/dist/css/index.css'
import axios from 'axios'
import WangEditor from './editor/WangEditor.vue'
import {useStoreSpaceData} from "@/store/spaceData";
import {useStorePageData} from "@/store/pageData";
import {useStoreDisplay} from "@/store/wikiDisplay";
let editor = ref({});
// 编辑相关
@@ -99,6 +105,9 @@ let toolbars = {
const props = defineProps({
spaceId: Number,
});
let storeSpace = useStoreSpaceData()
let storePage = useStorePageData()
let storeDisplay = useStoreDisplay()
let emit = defineEmits(['loadPageList']);
onBeforeRouteUpdate((to) => {
initQueryParam(to);
@@ -118,7 +127,18 @@ onMounted(() => {
window.onbeforeunload = function () {
that.unlockPage()
}
storeDisplay.showHeader = false
})
const turnLeftCollapse = () => {
storeDisplay.showMenu = !storeDisplay.showMenu
setTimeout(() => {
if (storeDisplay.showMenu) {
storeDisplay.rightAsideWidth = 301
} else {
storeDisplay.rightAsideWidth = 1
}
}, 100)
}
const unlockPage = () => {
// 防止各种事件重复调这个接口,只需要调一次就好了
if (isUnlock.value) return
@@ -133,6 +153,7 @@ const createWikiCancel = () => {
}).then(() => {
unlockPage()
router.back()
storeDisplay.showHeader = true
})
}
let wangEditorRef = ref();
@@ -170,18 +191,27 @@ const createWikiSave = (saveAfter) => {
pageApi.updatePage(param).then((json) => {
ElMessage.success('保存成功!')
// 重新加载左侧列表,跳转到展示页面
emit('loadPageList')
doGetPageList()
pageId.value = json.data.id
if (saveAfter == 1) {
router.push({
path: '/page/show',
query: {pageId: pageId.value},
}).then(()=>{
storeDisplay.showHeader = true
})
} else {
loadPageDetail(pageId.value)
}
})
}
const doGetPageList = () => {
let param = {spaceId: storeSpace.chooseSpaceId}
pageApi.pageList(param).then((json) => {
storePage.wikiPageList = json.data || []
})
}
const loadPageDetail = (pageId) => {
pageApi.pageDetail({id: pageId}).then((json) => {
wikiPage.value = json.data.wikiPage || {}
@@ -282,7 +312,33 @@ const initEditor = () => {
}
</style>
<style lang="scss">
<style lang="scss" scoped>
.fake-header{
color: #333;
height: 60px !important;
border-bottom: 0.5px solid #eaeaea;
.fold-btn {
padding: 28px 19px;
line-height: 28px;
color: #3d3a3a !important;
font-size: 18px;
}
.page-title-input {
margin-top: 10px;
margin-left: 5px;
width: 99%;
height: 50px;
}
.title-info-view-right {
text-align: right;
margin-top: 10px;
font-size: 14px;
color: #454343;
.split {
padding:0 4px;
}
}
}
.page-edit-vue {
.page-content-editor {
ol {
@@ -361,7 +417,8 @@ const initEditor = () => {
.page-edit-vue .page-title-input {
padding-bottom: 10px;
margin-left: 5px;
width: 100%;
margin-right: 5px;
width: 50%;
}
.page-edit-vue .markdown-body table {

View File

@@ -1,57 +1,15 @@
<template>
<div class="page-show-vue">
<div class="page-show-vue" v-if="storePage.pageInfo.editorType !== 0">
<el-row type="border-card" style="height: 100%">
<el-col :span="storePage.commentShow ? 18 : 24" style="padding: 20px;border-right: 1px solid #f1f1f1;height: 100%;overflow: auto;">
<el-row>
<el-col :span="navigationList.length > 0 ? 18 : 24">
<div style="max-width: 1000px; margin: 0 auto; padding-left: 10px">
<div class="wiki-title" ref="wikiTitleRef">{{ wikiPage.name }}</div>
<PageAction/>
<div class="wiki-files">
<el-table v-show="storePage.fileList.length > 0" :data="storePage.fileList" border style="width: 100%; margin-bottom: 5px">
<el-table-column label="文件名" show-overflow-tooltip>
<template v-slot="scope">
<el-link target="_blank" :href="scope.row.fileUrl" type="primary">{{scope.row.fileName }}</el-link>
</template>
</el-table-column>
<el-table-column prop="createUserName" label="创建人" width="110px" show-overflow-tooltip></el-table-column>
<el-table-column label="文件大小" width="120px">
<template v-slot="scope">{{computeFileSize(scope.row.fileSize) }}</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" width="160px"></el-table-column>
<el-table-column prop="downloadNum" label="下载次数" width="90px">
<template v-slot="scope">{{scope.row.downloadNum || 0}}</template>
</el-table-column>
<el-table-column label="操作" width="90px" v-if="wikiPageAuth.canDeleteFile == 1">
<template v-slot="scope">
<el-button @click="deletePageFile(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div ref="pageContentRef" class="wiki-page-content">
<div v-html="pageShowDetail" class="markdown-body" v-if="wikiPage.editorType == 2" v-highlight></div>
<div v-html="pageShowDetail" class="wang-editor-body" v-else></div>
</div>
<div style="margin-top: 40px; font-size: 14px">
<span style="vertical-align: top" class="is-link">
<span v-show="wikiPage.selfZan == 0" @click="zanPage(1)"><img src="../../assets/img/zan.png" style="vertical-align: middle"/> </span>
<span v-show="wikiPage.selfZan == 1" @click="zanPage(0)"><img src="../../assets/img/zan.png" style="vertical-align: middle; transform: rotateX(180deg)"/> </span>
</span>
<span style="margin-left: 10px; vertical-align: top">
<span v-if="wikiPage.selfZan == 0 && wikiPage.zanNum <= 0">成为第一个赞同者</span>
<span v-else-if="wikiPage.selfZan == 0 && wikiPage.zanNum > 0">
<span class="is-link" @click="showZanPageUser">{{ wikiPage.zanNum }}</span>赞了它
</span>
<span v-else-if="wikiPage.selfZan == 1 && wikiPage.zanNum <= 1">我赞了它</span>
<span v-else-if="wikiPage.selfZan == 1 && wikiPage.zanNum > 1">
<span class="is-link" @click="showZanPageUser">我和{{ wikiPage.zanNum - 1 }}个其他人</span>赞了它
</span>
</span>
<span style="margin-left: 10px">
<el-icon style="font-size: 16px; color: #666;vertical-align: middle;"><el-icon-view/></el-icon> {{ wikiPage.viewNum }}次阅读
</span>
</div>
<PageZan></PageZan>
</div>
</el-col>
<el-col :span="navigationList.length > 0 ? 6 : 0" v-if="navigationList.length > 0">
@@ -67,34 +25,21 @@
<el-tab-pane label="评论" name="comment">
<Comment/>
</el-tab-pane>
<el-tab-pane label="附件" name="annex">
<Annex/>
</el-tab-pane>
<el-tab-pane label="修改历史" name="history">
<div class="action-tab-box">
<div v-if="pageHistoryList.length <= 0" class="action-box-empty">
暂无修改历史记录
</div>
<el-timeline v-else>
<el-timeline-item v-for="history in pageHistoryList">
<el-tag :type="pageHistoryChoice.id === history.id ? history.loading === 3 ? 'danger' : 'success' : 'info'" class="history-item" @click="historyClick(history)">
<div>{{ history.createUserName }}</div>
<div>{{ history.createTime }}</div>
</el-tag>
<el-icon class="history-loading-status" v-show="history.loading===1"><el-icon-loading/></el-icon>
<el-icon class="history-loading-status" v-show="history.loading===2"><el-icon-circle-check/></el-icon>
<el-icon class="history-loading-status" v-show="history.loading===3"><el-icon-circle-close/></el-icon>
</el-timeline-item>
</el-timeline>
</div>
<PageHistory
:pageHistoryList="pageHistoryList"
:pageHistoryChoice="pageHistoryChoice"
:pageHistoryDetail="pageHistoryDetail"
@historyClickHandle="historyClickHandle"
@previewPageImage="previewPageImage"
@createNavigationHeading="createNavigationHeading"/>
</el-tab-pane>
</el-tabs>
</el-col>
</el-row>
<!--点赞人员弹窗-->
<el-dialog title="赞了它的人" v-model="zanUserDialogVisible" width="600px">
<el-table :data="zanUserList" border :show-header="false" style="width: 100%; margin-bottom: 5px">
<el-table-column prop="createUserName" label="用户"></el-table-column>
<el-table-column prop="createTime" label="时间"></el-table-column>
</el-table>
</el-dialog>
<el-image-viewer
v-if="showImagePreview"
:url-list="showImagePreviewList"
@@ -132,8 +77,10 @@ import htmlUtil from '../../assets/lib/HtmlUtil.js'
import pageApi from '../../assets/api/page'
import userApi from '../../assets/api/user'
import Navigation from './components/Navigation.vue'
import PageAction from './show/PageAction.vue'
import Annex from './show/Annex.vue'
import PageHistory from './show/PageHistory.vue'
import Comment from './show/Comment.vue'
import PageZan from './show/PageZan.vue'
import {mavonEditor} from 'mavon-editor'
import 'mavon-editor/dist/markdown/github-markdown.min.css'
import 'mavon-editor/dist/css/index.css'
@@ -150,14 +97,10 @@ let wikiPageAuth = ref({});
let pageContent = ref({});
let selfUserId = ref(0);
let uploadFileList = ref([]);
let uploadFormData = ref({pageId: 0});
let zanUserDialogVisible = ref(false);
let zanUserList = ref([]);
let parentPath = ref({});
// 手机扫码
let qrCodeUrl = ref('');
let mobileScanDialogVisible = ref(false);
let uploadFileUrl = ref(import.meta.env.VITE_APP_BASE_API + '/zyplayer-doc-wiki/page/file/upload');
// 页面权限
let pageAuthDialogVisible = ref(false);
let pageAuthUserList = ref([]);
@@ -196,15 +139,6 @@ onMounted(() => {
initQueryParam(route);
});
const editWiki = () => {
let param = {pageId: parentPath.value.pageId}
pageApi.pageLock(param).then(() => {
router.push({
path: '/page/edit',
query: {pageId: parentPath.value.pageId}
})
})
}
const getSearchUserList = (query) => {
if (query == '') return
pageAuthUserLoading.value = true
@@ -294,39 +228,11 @@ const getPageHistory = (pageId, pageNum) => {
}
})
}
const historyClick = (history) => {
if (pageHistoryChoice.value.id === history.id && !!pageHistoryDetail.value) {
return;
}
const historyClickHandle = (history) => {
pageHistoryChoice.value.loading = 0;
pageHistoryChoice.value = history;
// 缓存一下,但如果历史页面多了而且很大就占内存,也可以每次去拉取,先这样吧
if (history.content) {
history.loading = 2;
pageHistoryDetail.value = history.content;
pageShowDetail.value = history.content;
setTimeout(() => {
previewPageImage();
createNavigationHeading();
}, 500)
} else {
history.loading = 1
pageApi.pageHistoryDetail({id: history.id}).then((json) => {
history.loading = 2;
history.content = json.data || '--';
if (wikiPage.value.editorType === 2) {
history.content = mavonEditor.getMarkdownIt().render(history.content);
}
pageHistoryDetail.value = history.content;
pageShowDetail.value = history.content;
setTimeout(() => {
previewPageImage();
createNavigationHeading();
}, 500);
}).catch(() => {
history.loading = 3;
});
}
pageHistoryDetail.value = history.content;
pageShowDetail.value =history.content;
}
const clearHistory = () => {
pageHistoryChoice.value.loading = 0;
@@ -348,7 +254,6 @@ const loadPageDetail = (pageId) => {
pageContent.value = result.pageContent || {};
storePage.fileList = result.fileList || [];
selfUserId.value = result.selfUserId || 0;
uploadFormData.value = {pageId: wikiPage.value.id};
wikiPageAuth.value = {
canEdit: result.canEdit,
canDelete: result.canDelete,
@@ -371,7 +276,9 @@ const loadPageDetail = (pageId) => {
// 调用父方法展开目录树
emit('changeExpandedKeys', pageId);
setTimeout(() => {
previewPageImage();
if (storePage.pageInfo.editorType !== 0){
previewPageImage();
}
createNavigationHeading();
}, 500);
storePage.pageInfo = wikiPageRes;
@@ -409,33 +316,7 @@ const previewPageImage = () => {
}
})
}
const zanPage = (yn) => {
let param = {yn: yn, pageId: wikiPage.value.id}
pageApi.updatePageZan(param).then(() => {
wikiPage.value.selfZan = yn
wikiPage.value.zanNum = wikiPage.value.zanNum + (yn == 1 ? 1 : -1)
})
}
const showZanPageUser = () => {
zanUserDialogVisible.value = true
zanUserList.value = []
let param = {pageId: wikiPage.value.id}
pageApi.pageZanList(param).then((json) => {
zanUserList.value = json.data
})
}
const deletePageFile = (row) => {
ElMessageBox.confirm('确定要删除此文件吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
let param = {id: row.id};
pageApi.deletePageFile(param).then(() => {
storePage.fileList = storePage.fileList.filter(item => item.id !== row.id);
});
})
}
const getUserHeadBgColor = (userId) => {
let color = page.userHeadColor[userId]
if (!color) {

View File

@@ -150,7 +150,7 @@ defineExpose({setTitle,setPageId, setHtml, getPageData});
.wang-editor-box .wang-editor-content {
padding: 20px 0;
overflow: auto;
height: calc(100vh - 136px);
height: calc(100vh - 156px);
}
.wang-editor-box .w-e-bar-item {
height: 39px;

View File

@@ -0,0 +1,80 @@
<template>
<div class="wiki-file">
<el-upload v-if="storePage.pageAuth.canUploadFile === 1"
:on-success="uploadFileSuccess"
:on-error="uploadFileError"
:before-upload="beforeUpload"
:action="uploadFileUrl"
:data="uploadFormData"
:with-credentials="true" class="action-btn upload-page-file" name="files"
show-file-list multiple :limit="999">
<el-button type="primary" :underline="false" :icon="ElIconUpload" style="margin: 10px;width: 100%"> 上传附件</el-button>
</el-upload>
<el-table v-show="storePage.fileList.length > 0" :data="storePage.fileList" border
style="width: 100%; margin-bottom: 5px">
<el-table-column label="文件名" show-overflow-tooltip>
<template v-slot="scope">
<el-link target="_blank" :href="scope.row.fileUrl" type="primary">{{scope.row.fileName }}
</el-link>
</template>
</el-table-column>
<el-table-column prop="createUserName" label="创建人" width="110px"
show-overflow-tooltip></el-table-column>
<el-table-column label="文件大小" width="120px">
<template v-slot="scope">{{computeFileSize(scope.row.fileSize) }}</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" width="160px"></el-table-column>
<el-table-column prop="downloadNum" label="下载次数" width="90px">
<template v-slot="scope">{{scope.row.downloadNum || 0}}</template>
</el-table-column>
<el-table-column label="操作" width="90px" v-if="storePage.pageAuth.canDeleteFile == 1">
<template v-slot="scope">
<el-button @click="deletePageFile(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script setup>
import {Upload as ElIconUpload} from '@element-plus/icons-vue'
import {useStorePageData} from "@/store/pageData";
let storePage = useStorePageData();
import {ref} from 'vue';
import {ElMessageBox, ElMessage} from 'element-plus';
import pageApi from "@/assets/api/page";
import unitUtil from "@/assets/lib/UnitUtil";
let uploadFormData = ref({pageId: 0});
let uploadFileUrl = ref(import.meta.env.VITE_APP_BASE_API + '/zyplayer-doc-wiki/page/file/upload');
const beforeUpload = () => {
uploadFormData.value.pageId = storePage.pageInfo.id;
}
const uploadFileError = (err) => {
ElMessage.error('上传失败,' + err);
}
const uploadFileSuccess = (response) => {
if (response.errCode === 200) {
storePage.fileList.push(response.data);
ElMessage.success('上传成功!');
} else {
ElMessage('上传失败:' + (response.errMsg || '未知错误'));
}
}
const deletePageFile = (row) => {
ElMessageBox.confirm('确定要删除此文件吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
let param = {id: row.id};
pageApi.deletePageFile(param).then(() => {
storePage.fileList = storePage.fileList.filter(item => item.id !== row.id);
});
})
}
const computeFileSize = (fileSize) => {
return unitUtil.computeFileSize(fileSize)
}
</script>

View File

@@ -62,7 +62,9 @@ let storePage = useStorePageData();
let storeUser = useStoreUserData();
watch(() => storePage.pageInfo, (newVal) => {
loadCommentList();
if (storePage.pageInfo.editorType !== 0){
loadCommentList();
}
})
onMounted(() => {
loadCommentList();
@@ -146,7 +148,7 @@ const getUserHeadBgColor = (userId) => {
<style lang="scss">
.comment-box {
padding: 8px;
height: calc(100vh - 100px);
height: calc(100vh - 115px);
overflow: auto;
.comment-list {

View File

@@ -23,8 +23,10 @@ import pageApi from "@/assets/api/page";
import {useStorePageData} from "@/store/pageData";
import userApi from "@/assets/api/user";
import QRCode from 'qrcode'
import {useStoreSpaceData} from "@/store/spaceData";
let storePage = useStorePageData();
let storeSpace = useStoreSpaceData();
const route = useRoute();
const router = useRouter();
@@ -53,7 +55,7 @@ const initMobileQrScan = () => {
if (!dataItemEditVisible.value) return;
let routeUrl = router.resolve({
path: '/page/share/mobile/view',
query: {pageId: storePage.pageInfo.id, space: storePage.spaceInfo.uuid}
query: {pageId: storePage.pageInfo.id, space: storeSpace.spaceInfo.uuid}
});
let hostPath = window.location.href.split('#')[0];
setTimeout(() => {

View File

@@ -1,201 +0,0 @@
<template>
<el-row class="page-action-box">
<el-col :span="12" class="page-create-info">
<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>
</el-col>
<el-col :span="12" class="page-action-list">
<el-link v-if="storePage.pageAuth.canEdit === 1" @click="editWiki" type="primary" :underline="false" :icon="ElIconEdit" class="action-btn">编辑</el-link>
<el-link type="primary" :icon="ElIconChatLineRound" :underline="false" @click="showCommentWiki" class="action-btn">评论</el-link>
<el-upload v-if="storePage.pageAuth.canUploadFile === 1"
:on-success="uploadFileSuccess"
:on-error="uploadFileError"
:before-upload="beforeUpload"
:action="uploadFileUrl"
:data="uploadFormData"
:with-credentials="true" class="action-btn upload-page-file" name="files"
show-file-list multiple :limit="999">
<el-link type="primary" :underline="false" :icon="ElIconUpload"> 上传附件</el-link>
</el-upload>
<el-dropdown trigger="click" class="action-btn more-dropdown">
<el-link type="primary" :underline="false">
更多 <el-icon class="el-icon--right"><el-icon-arrow-down/></el-icon>
</el-link>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="showPageHistory" :icon="ElIconTime">查看历史版本</el-dropdown-item>
<el-dropdown-item @click="editWikiAuth" v-if="storePage.pageAuth.canConfigAuth === 1" :icon="ElIconSCheck">权限设置</el-dropdown-item>
<el-dropdown-item @click="showOpenPage" v-if="storePage.spaceInfo.openDoc === 1" :icon="ElIconShare">查看开放文档</el-dropdown-item>
<el-dropdown-item @click="showMobileView" v-if="storePage.spaceInfo.openDoc === 1" :icon="ElIconMobilePhone">手机端查看</el-dropdown-item>
<el-dropdown-item @click="exportWord" :icon="ElIconDownload">导出为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-col>
<MobileQrScanDialog v-model:visible="mobileScanDialogVisible"/>
<PageAuthDialog v-model:visible="pageAuthDialogVisible"/>
<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>
</el-row>
</template>
<script setup>
import {
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,
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 { ElMessageBox, ElMessage, ElNotification } from 'element-plus';
import pageApi from '@/assets/api/page'
import PageAuthDialog from './PageAuthDialog.vue'
import MobileQrScanDialog from './MobileQrScanDialog.vue'
import {useStorePageData} from "@/store/pageData";
let storePage = useStorePageData();
let router = useRouter();
const editWiki = () => {
// 锁定页面并进入编辑页面
let param = {pageId: storePage.pageInfo.id};
pageApi.pageLock(param).then(() => {
router.push({path: '/page/edit', query: {pageId: storePage.pageInfo.id}});
});
}
const showCommentWiki = () => {
storePage.commentShow = true;
storePage.commentActiveTab = 'comment';
}
let pageAuthDialogVisible = ref(false);
const editWikiAuth = () => {
pageAuthDialogVisible.value = true;
}
const showPageHistory = () => {
storePage.commentShow = true;
storePage.commentActiveTab = 'history';
}
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: storePage.pageInfo.id};
pageApi.pageDelete(param).then(() => {
// 重新加载左侧列表,跳转到展示页面
// emit('loadPageList'); TODO
router.push({path: '/home', query: {spaceId: storePage.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 (storePage.spaceInfo.openDoc !== 1) {
ElMessage.warning('该空间未开放,无法查看开放文档地址');
} else {
mobileScanDialogVisible.value = true;
}
}
// 上传相关
let uploadFormData = ref({pageId: 0});
let uploadFileUrl = ref(import.meta.env.VITE_APP_BASE_API + '/zyplayer-doc-wiki/page/file/upload');
const beforeUpload = () => {
uploadFormData.value.pageId = storePage.pageInfo.id;
}
const uploadFileError = (err) => {
ElMessage.error('上传失败,' + err);
}
const uploadFileSuccess = (response) => {
if (response.errCode === 200) {
storePage.fileList.push(response.data);
ElMessage.success('上传成功!');
} else {
ElMessage('上传失败:' + (response.errMsg || '未知错误'));
}
}
</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">
.page-action-box {
.page-action-list {
.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>

View File

@@ -1,17 +1,24 @@
<template>
<div class="action-tab-box">
<div v-if="pageHistoryList.length <= 0" class="action-box-empty">
<div v-if="props.pageHistoryList.length <= 0" class="action-box-empty">
暂无修改历史记录
</div>
<el-timeline v-else>
<el-timeline-item v-for="history in pageHistoryList">
<el-tag :type="pageHistoryChoice.id === history.id ? history.loading === 3 ? 'danger' : 'success' : 'info'" class="history-item" @click="historyClick(history)">
<el-timeline-item v-for="history in props.pageHistoryList">
<el-tag :type="props.pageHistoryChoice.id === history.id ? history.loading === 3 ? 'danger' : 'success' : 'info'"
class="history-item" @click="historyClick(history)">
<div>{{ history.createUserName }}</div>
<div>{{ history.createTime }}</div>
</el-tag>
<el-icon class="history-loading-status" v-show="history.loading===1"><el-icon-loading/></el-icon>
<el-icon class="history-loading-status" v-show="history.loading===2"><el-icon-circle-check/></el-icon>
<el-icon class="history-loading-status" v-show="history.loading===3"><el-icon-circle-close/></el-icon>
<el-icon class="history-loading-status" v-show="history.loading===1">
<el-icon-loading/>
</el-icon>
<el-icon class="history-loading-status" v-show="history.loading===2">
<el-icon-circle-check/>
</el-icon>
<el-icon class="history-loading-status" v-show="history.loading===3">
<el-icon-circle-close/>
</el-icon>
</el-timeline-item>
</el-timeline>
</div>
@@ -19,30 +26,54 @@
<script setup>
import {
Delete as ElIconDelete,
CircleCheck as ElIconCircleCheck,
CircleClose as ElIconCircleClose,
Loading as ElIconLoading,
} 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 { ElMessageBox, ElMessage, ElNotification } from 'element-plus';
import pageApi from '@/assets/api/page'
import {mavonEditor} from "mavon-editor";
import {ref, defineProps, defineEmits} from 'vue';
import {useStorePageData} from "@/store/pageData";
import {useStoreUserData} from "@/store/userData";
let route = useRoute();
let router = useRouter();
let storePage = useStorePageData();
let storeUser = useStoreUserData();
let pageHistoryDetail = ref('');
let pageHistoryChoice = ref({});
let pageHistoryList = ref([]);
let pageHistoryPageNum = ref(1);
watch(() => storePage.pageInfo, (newVal) => {
let props= defineProps({
pageHistoryList:Array,
pageHistoryChoice:Object,
pageHistoryDetail:String,
})
onMounted(() => {
});
let emit = defineEmits(['historyClickHandle','previewPageImage','createNavigationHeading'])
const historyClick = (history) => {
if (props.pageHistoryChoice.id === history.id && !!props.pageHistoryDetail.value) {
return;
}
// 缓存一下,但如果历史页面多了而且很大就占内存,也可以每次去拉取,先这样吧
if (history.content) {
history.loading = 2;
emit('historyClickHandle',history)
setTimeout(() => {
emit('previewPageImage',history)
emit('createNavigationHeading',history)
}, 500)
} else {
history.loading = 1
pageApi.pageHistoryDetail({id: history.id}).then((json) => {
history.loading = 2;
history.content = json.data || '--';
if (storePage.pageInfo.editorType === 2) {
history.content = mavonEditor.getMarkdownIt().render(history.content);
}
emit('historyClickHandle',history)
setTimeout(() => {
emit('previewPageImage',history)
emit('createNavigationHeading',history)
}, 500);
}).catch(() => {
history.loading = 3;
});
}
}
</script>
<style lang="scss">

View File

@@ -0,0 +1,55 @@
<template>
<div style="margin-top: 40px; font-size: 14px">
<span style="vertical-align: top" class="is-link">
<span v-show="storePage.pageInfo.selfZan == 0" @click="zanPage(1)"><img src="../../../assets/img/zan.png" style="vertical-align: middle"/> </span>
<span v-show="storePage.pageInfo.selfZan == 1" @click="zanPage(0)"><img src="../../../assets/img/zan.png" style="vertical-align: middle; transform: rotateX(180deg)"/> </span>
</span>
<span style="margin-left: 10px; vertical-align: top">
<span v-if="storePage.pageInfo.selfZan == 0 && storePage.pageInfo.zanNum <= 0">成为第一个赞同者</span>
<span v-else-if="storePage.pageInfo.selfZan == 0 && storePage.pageInfo.zanNum > 0">
<span class="is-link" @click="showZanPageUser">{{ storePage.pageInfo.zanNum }}</span>赞了它
</span>
<span v-else-if="storePage.pageInfo.selfZan == 1 && storePage.pageInfo.zanNum <= 1">我赞了它</span>
<span v-else-if="storePage.pageInfo.selfZan == 1 && storePage.pageInfo.zanNum > 1">
<span class="is-link" @click="showZanPageUser">我和{{ storePage.pageInfo.zanNum - 1 }}个其他人</span>赞了它
</span>
</span>
<span style="margin-left: 10px">
<el-icon style="font-size: 16px; color: #666;vertical-align: middle;"><el-icon-view/></el-icon> {{ storePage.pageInfo.viewNum }}次阅读
</span>
</div>
<el-dialog title="赞了它的人" v-model="zanUserDialogVisible" width="600px">
<el-table :data="zanUserList" border :show-header="false" style="width: 100%; margin-bottom: 5px">
<el-table-column prop="createUserName" label="用户"></el-table-column>
<el-table-column prop="createTime" label="时间"></el-table-column>
</el-table>
</el-dialog>
</template>
<script setup>
import pageApi from '../../../assets/api/page'
import {ref} from 'vue';
import {useStorePageData} from "@/store/pageData";
import {
View as ElIconView,
} from '@element-plus/icons-vue'
let zanUserList = ref([]);
let zanUserDialogVisible = ref(false);
let storePage = useStorePageData();
const zanPage = (yn) => {
let param = {yn: yn, pageId: storePage.pageInfo.id}
pageApi.updatePageZan(param).then(() => {
storePage.pageInfo.selfZan = yn
storePage.pageInfo.zanNum = storePage.pageInfo.zanNum + (yn == 1 ? 1 : -1)
})
}
const showZanPageUser = () => {
zanUserDialogVisible.value = true
zanUserList.value = []
let param = {pageId: storePage.pageInfo.id}
pageApi.pageZanList(param).then((json) => {
zanUserList.value = json.data
})
}
</script>

View File

@@ -110,6 +110,8 @@ import {
import pageApi from '../../assets/api/page'
import userApi from '../../assets/api/user'
import CreateSpace from '../../components/space/CreateSpace'
import {useStoreSpaceData}from '@/store/spaceData'
import {useStorePageData}from '@/store/pageData'
let spaceListLoading = ref(false);
let spaceOptions = ref([]);
@@ -130,8 +132,8 @@ let userSetting = ref({wiki_only_show_favorite: 0,});
let route = useRoute();
let router = useRouter();
let emit = defineEmits('loadSpace');
let storePage = useStorePageData();
let storeSpace = useStoreSpaceData();
onMounted(() => {
loadSpaceList()
getSelfUserInfo()
@@ -223,7 +225,7 @@ const deleteSpaceInfo = (row) => {
pageApi.updateSpace(param).then(() => {
ElMessage.success('删除成功')
loadSpaceList()
emit('loadSpace')
loadSpace()
})
})
}
@@ -257,7 +259,7 @@ const wikiOnlyShowFavoriteChange = () => {
value: userSetting.value.wiki_only_show_favorite,
}
pageApi.spaceSettingUpdate(param).then((json) => {
emit('loadSpace')
loadSpace()
})
}
const getSpaceSettingList = () => {
@@ -273,6 +275,42 @@ const getSelfUserInfo = () => {
userSelfInfo.value = json.data
})
}
const loadSpace = (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 (spaceList.value.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);
}
}
})
}
const doGetPageList = (parentId, node) => {
let param = {spaceId: storeSpace.chooseSpaceId}
pageApi.pageList(param).then((json) => {
storePage.wikiPageList = json.data || []
})
}
</script>
<style lang="scss">

View File

@@ -4340,10 +4340,10 @@
"resolved" "https://registry.npmmirror.com/pify/-/pify-4.0.1.tgz"
"version" "4.0.1"
"pinia@^2.1.4":
"integrity" "sha512-vYlnDu+Y/FXxv1ABo1vhjC+IbqvzUdiUC3sfDRrRyY2CQSrqqaa+iiHmqtARFxJVqWQMCJfXx1PBvFs9aJVLXQ=="
"resolved" "https://registry.npmmirror.com/pinia/-/pinia-2.1.4.tgz"
"version" "2.1.4"
"pinia@^2.1.6":
"integrity" "sha512-bIU6QuE5qZviMmct5XwCesXelb5VavdOWKWaB17ggk++NUwQWWbP5YnsONTk3b752QkW9sACiR81rorpeOMSvQ=="
"resolved" "https://registry.npmmirror.com/pinia/-/pinia-2.1.6.tgz"
"version" "2.1.6"
dependencies:
"@vue/devtools-api" "^6.5.0"
"vue-demi" ">=0.14.5"