界面优化,markdown渲染优化
This commit is contained in:
@@ -19,10 +19,10 @@
|
||||
|
||||
<script setup>
|
||||
import {toRefs, ref, reactive, onMounted, onBeforeUnmount, defineProps, watch, defineEmits, computed, defineExpose} from 'vue';
|
||||
import {useStoreDisplay} from '@/store/wikiDisplay.js'
|
||||
import LeftAside from './aside/LeftAside.vue'
|
||||
import RightHeader from './aside/RightHeader.vue'
|
||||
import RightResize from './aside/RightResize.vue'
|
||||
import {useStoreDisplay} from '@/store/wikiDisplay.js';
|
||||
import LeftAside from './aside/LeftAside.vue';
|
||||
import RightHeader from './aside/RightHeader.vue';
|
||||
import RightResize from './aside/RightResize.vue';
|
||||
import userApi from "@/assets/api/user";
|
||||
import {useStoreUserData} from "@/store/userData";
|
||||
|
||||
@@ -50,82 +50,21 @@ const getSelfUserInfo = () => {
|
||||
border-right: 1px solid #eee;
|
||||
background: #fafafa;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
html,
|
||||
body {
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.global-layout-vue {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.hidTree {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#app, .el-container, .el-menu {
|
||||
#app, .el-container, .el-menu, .global-layout-vue {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.el-header {
|
||||
background-color: #fff !important;
|
||||
}
|
||||
|
||||
.header-right-user-name {
|
||||
color: #000000;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.el-header {
|
||||
color: #333;
|
||||
height: 60px !important;
|
||||
background-color: #fff !important;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.head-icon {
|
||||
margin-right: 15px;
|
||||
margin-top: 15px;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
color: #000000;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.header-user-message .page-info-box {
|
||||
text-align: right;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.upgrade-info {
|
||||
max-height: 150px;
|
||||
overflow-y: auto;
|
||||
word-break: break-all;
|
||||
white-space: pre-wrap;
|
||||
line-height: 26px;
|
||||
}
|
||||
|
||||
.search-option-item {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.search-option-item .title {
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.search-option-item .content {
|
||||
font-size: 12px;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<div class="page-show-vue" v-if="storePage.pageInfo.editorType !== 0">
|
||||
<a-row class="view-body-comment-box">
|
||||
<a-col flex="auto" class="view-body-outer-box">
|
||||
<a-col id="pageContentScrollBox" flex="auto" class="view-body-outer-box">
|
||||
<div class="view-body-box">
|
||||
<div class="wiki-title" ref="wikiTitleRef">{{ storePage.pageInfo.name }}</div>
|
||||
<div id="pageContentBox" ref="pageContentRef" class="wiki-page-content">
|
||||
<div v-if="wikiPage.editorType === 2" v-html="pageShowDetail" class="markdown-body" v-highlight></div>
|
||||
<div v-else v-html="pageShowDetail" class="wang-editor-body"></div>
|
||||
<div v-if="wikiPage.editorType === 2" v-html="pageContentShow" class="page-view-content markdown-body" v-highlight></div>
|
||||
<div v-else v-html="pageContentShow" class="page-view-content wang-editor-body"></div>
|
||||
</div>
|
||||
<PageZan></PageZan>
|
||||
</div>
|
||||
@@ -18,16 +18,10 @@
|
||||
<Comment></Comment>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane tab="附件" key="files">
|
||||
<Annex/>
|
||||
<Files/>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane tab="修改历史" key="history">
|
||||
<PageHistory
|
||||
:pageHistoryList="pageHistoryList"
|
||||
:pageHistoryChoice="pageHistoryChoice"
|
||||
:pageHistoryDetail="pageHistoryDetail"
|
||||
@historyClickHandle="historyClickHandle"
|
||||
@previewPageImage="previewPageImage"
|
||||
@createNavigationHeading="createNavigationHeading"/>
|
||||
<PageHistory :history="pageHistoryList" @choice="historyClickHandle"/>
|
||||
</a-tab-pane>
|
||||
<template #rightExtra>
|
||||
<el-tooltip content="关闭" placement="top">
|
||||
@@ -46,19 +40,18 @@ import { CloseOutlined } from '@ant-design/icons-vue';
|
||||
import {toRefs, ref, reactive, onMounted, watch, defineProps, h, nextTick, defineEmits, defineExpose, computed} from 'vue';
|
||||
import {onBeforeRouteUpdate, useRoute, useRouter} from "vue-router";
|
||||
import { ElMessageBox, ElMessage, ElNotification } from 'element-plus';
|
||||
import QRCode from 'qrcode'
|
||||
import unitUtil from '../../assets/lib/UnitUtil.js'
|
||||
import htmlUtil from '../../assets/lib/HtmlUtil.js'
|
||||
import pageApi from '../../assets/api/page'
|
||||
import userApi from '../../assets/api/user'
|
||||
import Navigation from './show/Navigation.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'
|
||||
import QRCode from 'qrcode';
|
||||
import unitUtil from '../../assets/lib/UnitUtil.js';
|
||||
import htmlUtil from '../../assets/lib/HtmlUtil.js';
|
||||
import pageApi from '../../assets/api/page';
|
||||
import userApi from '../../assets/api/user';
|
||||
import Navigation from './show/Navigation.vue';
|
||||
import Files from './show/Files.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/css/index.css';
|
||||
import {useStorePageData} from "@/store/pageData";
|
||||
import {useStoreDisplay} from "@/store/wikiDisplay";
|
||||
import ImageViewer from "@/components/base/ImageViewer.vue";
|
||||
@@ -81,15 +74,9 @@ onBeforeRouteUpdate((to) => {
|
||||
// 页面展示相关
|
||||
let wikiPage = ref({});
|
||||
let wikiPageAuth = ref({});
|
||||
let pageContent = ref({});
|
||||
let selfUserId = ref(0);
|
||||
// 右侧标签页
|
||||
let pageHistoryDetail = ref('');
|
||||
let pageShowDetail = ref('');
|
||||
let pageHistoryChoice = ref({});
|
||||
let pageContent = ref('');
|
||||
let pageContentShow = ref('');
|
||||
let pageHistoryList = ref([]);
|
||||
let pageHistoryPageNum = ref(1);
|
||||
// 左侧导航菜单
|
||||
let navigationList = ref([]);
|
||||
let actionTabActiveName = ref('comment');
|
||||
let imageViewerRef = ref();
|
||||
@@ -102,45 +89,39 @@ const closeActionTab = () => {
|
||||
storeDisplay.commentShow = false;
|
||||
clearHistory();
|
||||
}
|
||||
const getPageHistory = (pageId, pageNum) => {
|
||||
if (pageNum === 1) {
|
||||
pageHistoryList.value = [];
|
||||
pageHistoryPageNum.value = 1;
|
||||
}
|
||||
let param = {pageId: pageId, pageNum: pageNum};
|
||||
pageApi.pageHistoryList(param).then((json) => {
|
||||
const getPageHistory = (pageId) => {
|
||||
pageHistoryList.value = [];
|
||||
pageApi.pageHistoryList({pageId: pageId}).then((json) => {
|
||||
let historyList = json.data || [];
|
||||
if (historyList.length <= 0) {
|
||||
pageHistoryPageNum.value = 0;
|
||||
} else {
|
||||
historyList.forEach((item) => (item.loading = 0));
|
||||
pageHistoryList.value = pageHistoryList.value.concat(historyList);
|
||||
}
|
||||
})
|
||||
historyList.forEach((item) => (item.loading = 0));
|
||||
pageHistoryList.value = historyList;
|
||||
});
|
||||
}
|
||||
const historyClickHandle = (history) => {
|
||||
pageHistoryChoice.value.loading = 0;
|
||||
pageHistoryChoice.value = history;
|
||||
pageHistoryDetail.value = history.content;
|
||||
pageShowDetail.value =history.content;
|
||||
pageContentShow.value = history.content;
|
||||
afterLoadPage();
|
||||
}
|
||||
const clearHistory = () => {
|
||||
pageHistoryChoice.value.loading = 0;
|
||||
pageHistoryDetail.value = '';
|
||||
pageHistoryChoice.value = {};
|
||||
pageHistoryList.value.forEach((item) => (item.loading = 0));
|
||||
pageShowDetail.value = pageContent.value.content;
|
||||
pageContentShow.value = pageContent.value;
|
||||
afterLoadPage();
|
||||
}
|
||||
const afterLoadPage = () => {
|
||||
setTimeout(() => {
|
||||
previewPageImage();
|
||||
createNavigationHeading();
|
||||
}, 500);
|
||||
}
|
||||
const loadPageDetail = (pageId) => {
|
||||
clearHistory();
|
||||
pageContent.value = '';
|
||||
pageApi.pageDetail({id: pageId}).then(async (json) => {
|
||||
let result = json.data || {};
|
||||
let wikiPageRes = result.wikiPage || {};
|
||||
wikiPageRes.selfZan = result.selfZan || 0;
|
||||
wikiPageRes.zanNum = wikiPageRes.zanNum || 0;
|
||||
wikiPage.value = wikiPageRes;
|
||||
pageContent.value = result.pageContent || {};
|
||||
storePage.fileList = result.fileList || [];
|
||||
selfUserId.value = result.selfUserId || 0;
|
||||
wikiPageAuth.value = {
|
||||
canEdit: result.canEdit,
|
||||
canDelete: result.canDelete,
|
||||
@@ -148,10 +129,13 @@ const loadPageDetail = (pageId) => {
|
||||
canDeleteFile: result.canDeleteFile,
|
||||
canConfigAuth: result.canConfigAuth,
|
||||
}
|
||||
if (wikiPage.value.editorType === 2) {
|
||||
pageContent.value.content = mavonEditor.getMarkdownIt().render(pageContent.value.content);
|
||||
if (result.pageContent) {
|
||||
pageContent.value = result.pageContent.content || '';
|
||||
}
|
||||
pageShowDetail.value = pageContent.value.content;
|
||||
if (wikiPage.value.editorType === 2) {
|
||||
pageContent.value = mavonEditor.getMarkdownIt().render(pageContent.value);
|
||||
}
|
||||
pageContentShow.value = pageContent.value;
|
||||
// 修改标题
|
||||
document.title = wikiPageRes.name || 'WIKI-内容展示';
|
||||
// 修改最后点击的项,保证刷新后点击编辑能展示编辑的项
|
||||
@@ -162,18 +146,15 @@ const loadPageDetail = (pageId) => {
|
||||
emit('switchSpace', wikiPage.value.spaceId);
|
||||
// 调用父方法展开目录树
|
||||
emit('changeExpandedKeys', pageId);
|
||||
setTimeout(() => {
|
||||
previewPageImage();
|
||||
createNavigationHeading();
|
||||
}, 500);
|
||||
storePage.pageInfo = wikiPageRes;
|
||||
storePage.pageAuth = wikiPageAuth.value;
|
||||
})
|
||||
getPageHistory(pageId, 1)
|
||||
afterLoadPage();
|
||||
});
|
||||
getPageHistory(pageId);
|
||||
}
|
||||
let wikiTitleRef = ref();
|
||||
const createNavigationHeading = () => {
|
||||
let navigationListVal = htmlUtil.createNavigationHeading()
|
||||
let navigationListVal = htmlUtil.createNavigationHeading();
|
||||
// 标题加到导航里面去
|
||||
if (navigationListVal.length > 0) {
|
||||
let wikiTile = wikiPage.value.name || 'WIKI-内容展示'
|
||||
@@ -181,7 +162,7 @@ const createNavigationHeading = () => {
|
||||
level: 1,
|
||||
node: wikiTitleRef.value,
|
||||
text: wikiTile,
|
||||
})
|
||||
});
|
||||
}
|
||||
navigationList.value = navigationListVal;
|
||||
}
|
||||
@@ -204,6 +185,7 @@ const initQueryParam = (to) => {
|
||||
|
||||
.view-body-comment-box {
|
||||
height: 100%;
|
||||
flex-flow: row nowrap;
|
||||
|
||||
.view-body-outer-box {
|
||||
height: 100%;
|
||||
@@ -230,8 +212,6 @@ const initQueryParam = (to) => {
|
||||
</style>
|
||||
|
||||
<style>
|
||||
@import '../../assets/lib/wangEditor.css';
|
||||
|
||||
.page-show-vue .icon-collapse {
|
||||
float: left;
|
||||
font-size: 25px;
|
||||
|
||||
@@ -29,13 +29,13 @@
|
||||
|
||||
<script setup>
|
||||
import { PlusOutlined } from '@ant-design/icons-vue';
|
||||
import {FolderOpen, Word as IconParkWord, Afferent as IconParkAfferent, PageTemplate as IconParkPageTemplate,} from '@icon-park/vue-next'
|
||||
import {FolderOpen, Word as IconParkWord, Afferent as IconParkAfferent, PageTemplate as IconParkPageTemplate,} from '@icon-park/vue-next';
|
||||
import {ref, defineProps, defineEmits, h} from 'vue';
|
||||
import {useRouter} from "vue-router";
|
||||
import {ElMessage} from 'element-plus'
|
||||
import pageApi from '@/assets/api/page'
|
||||
import {ElMessage} from 'element-plus';
|
||||
import pageApi from '@/assets/api/page';
|
||||
import axios from "axios";
|
||||
import IconDocument from '@/components/base/IconDocument.vue'
|
||||
import IconDocument from '@/components/base/IconDocument.vue';
|
||||
import {useStorePageData} from "@/store/pageData";
|
||||
import {useStoreSpaceData} from "@/store/spaceData";
|
||||
import MessagePrompt from "@/components/single/MessagePrompt";
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
<el-tooltip :content="node.label" placement="top-start" :show-after="700">{{ node.label }}</el-tooltip>
|
||||
</span>
|
||||
<!--操作-->
|
||||
<div class="page-action-box">
|
||||
<div @click.stop class="page-action-box">
|
||||
<AddMenu :pageId="data.id"/>
|
||||
<a-dropdown :trigger="['click']" @click="choosePageIdFunc(data.id)">
|
||||
<a-button :icon="h(EllipsisOutlined)" type="text" style="color: #888;"></a-button>
|
||||
@@ -115,12 +115,12 @@ import {
|
||||
import { EllipsisOutlined } from '@ant-design/icons-vue';
|
||||
import {ref, defineProps, defineEmits, defineExpose, onMounted, h, watch} from 'vue';
|
||||
import {useRouter, useRoute} from "vue-router";
|
||||
import pageApi from '@/assets/api/page'
|
||||
import pageApi from '@/assets/api/page';
|
||||
import {useStoreDisplay} from "@/store/wikiDisplay";
|
||||
import {useStorePageData} from "@/store/pageData";
|
||||
import AddMenu from "./AddMenu.vue";
|
||||
import IconDocument from "@/components/base/IconDocument.vue";
|
||||
import {ElMessageBox, ElMessage} from 'element-plus'
|
||||
import {ElMessageBox, ElMessage} from 'element-plus';
|
||||
import {useStoreSpaceData} from "@/store/spaceData";
|
||||
import Navigation from "@/views/page/show/Navigation.vue";
|
||||
import PageZan from "@/views/page/show/PageZan.vue";
|
||||
@@ -181,7 +181,7 @@ const loadSpaceList = (spaceId) => {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
let optionPageId = ref('');
|
||||
const changeNodeOptionStatus = (param) => {
|
||||
@@ -200,7 +200,7 @@ const changeWikiPageExpandedKeys = (pageId) => {
|
||||
}
|
||||
const createWikiByTemplate = () => {
|
||||
// TODO
|
||||
// templateManageRef.value.showTemplateManage()
|
||||
// templateManageRef.value.showTemplateManage();
|
||||
}
|
||||
const choosePageIdFunc = (id) => {
|
||||
storePage.optionPageId = id;
|
||||
@@ -231,15 +231,15 @@ const renamePage = (node, data) => {
|
||||
}
|
||||
const openMoveMenu = (onlyMove) => {
|
||||
// TODO
|
||||
// onlyMoveMode.value = onlyMove
|
||||
// visibleMoveMenu.value = true
|
||||
// onlyMoveMode.value = onlyMove;
|
||||
// visibleMoveMenu.value = true;
|
||||
// moveToPageId.value = storePage.choosePageId
|
||||
// moveToSpaceId.value = storeSpace.chooseSpaceId
|
||||
// moveToWikiPageList.value = storePage.wikiPageList
|
||||
}
|
||||
const openTemplateCreate = (exsit) => {
|
||||
// TODO
|
||||
// templateManageRef.value.showTemplateCreate(exsit)
|
||||
// templateManageRef.value.showTemplateCreate(exsit);
|
||||
}
|
||||
const deleteWikiPage = (data) => {
|
||||
let msg = '确定要删除此页面及其所有子页面吗?'
|
||||
@@ -299,7 +299,7 @@ const searchByKeywords = () => {
|
||||
wikiPageTreeRef.value.filter(searchKeywords.value);
|
||||
}
|
||||
const handleNodeClick = (data) => {
|
||||
//console.log('点击节点:', data, props.nowPageId)
|
||||
//console.log('点击节点:', data, props.nowPageId);
|
||||
if (data.editorType !== 0) {
|
||||
router.push({path: `/view/${data.spaceId}/${data.id}`});
|
||||
}
|
||||
@@ -320,7 +320,7 @@ const handlePageDrop = (draggingNode, dropNode, dropType, ev) => {
|
||||
doGetPageList();
|
||||
});
|
||||
}
|
||||
defineExpose({searchByKeywords})
|
||||
defineExpose({searchByKeywords});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
@@ -27,10 +27,8 @@
|
||||
<a-button class="hover-button hover-bg" @click="showCommentWiki" size="large" :icon="h(MessageOutlined)"></a-button>
|
||||
</a-tooltip>
|
||||
<UserMessagePopover/>
|
||||
<a-dropdown trigger="click" placement="bottom" overlayClassName="header-action-more-dropdown">
|
||||
<span style="display:inline-block;margin: 0 12px;">
|
||||
<a-button class="hover-button hover-bg" size="large" :icon="h(EllipsisOutlined)"></a-button>
|
||||
</span>
|
||||
<a-dropdown trigger="click" placement="bottom" arrow overlayClassName="header-action-more-dropdown">
|
||||
<a-button class="hover-button hover-bg" size="large" :icon="h(EllipsisOutlined)"></a-button>
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item @click="editWikiAuth" v-if="storePage.pageAuth.canConfigAuth === 1"><el-icon><ElIconSCheck/></el-icon> 权限设置</a-menu-item>
|
||||
@@ -43,10 +41,8 @@
|
||||
</template>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
<a-dropdown trigger="click" placement="bottom" overlayClassName="header-action-user-dropdown">
|
||||
<span style="display:inline-block;">
|
||||
<a-button class="hover-button hover-bg" size="large" :icon="h(UserOutlined)"></a-button>
|
||||
</span>
|
||||
<a-dropdown trigger="click" placement="bottomRight" arrow overlayClassName="header-action-user-dropdown">
|
||||
<a-button class="hover-button hover-bg" size="large" :icon="h(UserOutlined)"></a-button>
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item @click="showAbout">关于</a-menu-item>
|
||||
@@ -86,17 +82,17 @@ import {
|
||||
} from '@ant-design/icons-vue';
|
||||
import {toRefs, ref, reactive, onMounted, watch, defineEmits, h, computed} from 'vue';
|
||||
import {useRouter, useRoute} from "vue-router";
|
||||
import { ElMessageBox, ElMessage } from 'element-plus'
|
||||
import { useStoreDisplay } from '@/store/wikiDisplay.js'
|
||||
import { useStorePageData } from '@/store/pageData.js'
|
||||
import { useStoreUserData } from '@/store/userData.js'
|
||||
import { ElMessageBox, ElMessage } from 'element-plus';
|
||||
import { useStoreDisplay } from '@/store/wikiDisplay.js';
|
||||
import { useStorePageData } from '@/store/pageData.js';
|
||||
import { useStoreUserData } from '@/store/userData.js';
|
||||
import pageApi from "@/assets/api/page";
|
||||
import {useStoreSpaceData} from "@/store/spaceData";
|
||||
import userApi from "@/assets/api/user";
|
||||
import PageAuthDialog from '@/views/page/show/PageAuthDialog.vue'
|
||||
import MobileQrScanDialog from '@/views/page/show/MobileQrScanDialog.vue'
|
||||
import AboutDialog from "@/views/common/AboutDialog.vue"
|
||||
import UserMessagePopover from "../../../components/layouts/UserMessagePopover.vue"
|
||||
import PageAuthDialog from '@/views/page/show/PageAuthDialog.vue';
|
||||
import MobileQrScanDialog from '@/views/page/show/MobileQrScanDialog.vue';
|
||||
import AboutDialog from "@/views/common/AboutDialog.vue";
|
||||
import UserMessagePopover from "../../../components/layouts/UserMessagePopover.vue";
|
||||
import {fixRequestUrl} from "@/assets/api/hostUtils";
|
||||
|
||||
let router = useRouter();
|
||||
@@ -150,13 +146,13 @@ const deleteWikiPage = () => {
|
||||
}).then(() => {
|
||||
pageApi.pageDelete({pageId: storePage.pageInfo.id}).then(() => {
|
||||
pageApi.pageList({spaceId: storeSpace.chooseSpaceId}).then((json) => {
|
||||
storePage.wikiPageList = json.data || []
|
||||
storePage.wikiPageList = json.data || [];
|
||||
}).then(()=>{
|
||||
router.push({path: '/home', query: {spaceId: storePage.pageInfo.spaceId}});
|
||||
})
|
||||
});
|
||||
});
|
||||
}).catch((e) => {
|
||||
console.log(e)
|
||||
console.log(e);
|
||||
});
|
||||
}
|
||||
// 下载为Word
|
||||
@@ -190,7 +186,7 @@ const showAbout = () => {
|
||||
aboutDialogVisible.value = true;
|
||||
}
|
||||
const showConsole = () => {
|
||||
window.open(import.meta.env.VITE_APP_BASE_API, '_blank')
|
||||
window.open(import.meta.env.VITE_APP_BASE_API, '_blank');
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
<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>
|
||||
@@ -45,7 +45,7 @@ import {
|
||||
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 pageApi from '@/assets/api/page';
|
||||
import {useStorePageData} from "@/store/pageData";
|
||||
import {useStoreUserData} from "@/store/userData";
|
||||
|
||||
@@ -67,55 +67,55 @@ watch(() => storePage.pageInfo, (newVal) => {
|
||||
if (storePage.pageInfo.editorType !== 0){
|
||||
loadCommentList();
|
||||
}
|
||||
})
|
||||
});
|
||||
onMounted(() => {
|
||||
loadCommentList();
|
||||
});
|
||||
let actionTabCommentRef = ref();
|
||||
const scrollActionTabComment = () => {
|
||||
setTimeout(() => {
|
||||
let actionTabComment = actionTabCommentRef.value
|
||||
actionTabComment.scrollTop = actionTabComment.scrollHeight
|
||||
}, 0)
|
||||
let actionTabComment = actionTabCommentRef.value;
|
||||
actionTabComment.scrollTop = actionTabComment.scrollHeight;
|
||||
}, 0);
|
||||
}
|
||||
const loadCommentList = () => {
|
||||
if (!storePage.pageInfo || !storePage.pageInfo.id) {
|
||||
return;
|
||||
}
|
||||
cancelCommentUser()
|
||||
cancelCommentUser();
|
||||
pageApi.pageCommentList({pageId: storePage.pageInfo.id}).then((json) => {
|
||||
let commentListRes = json.data || []
|
||||
let commentListRes = json.data || [];
|
||||
for (let i = 0; i < commentListRes.length; i++) {
|
||||
commentListRes[i].color = getUserHeadBgColor(commentListRes[i].createUserId)
|
||||
let subCommentList = commentListRes[i].commentList || []
|
||||
commentListRes[i].color = getUserHeadBgColor(commentListRes[i].createUserId);
|
||||
let subCommentList = commentListRes[i].commentList || [];
|
||||
for (let j = 0; j < subCommentList.length; j++) {
|
||||
let subItem = subCommentList[j]
|
||||
subItem.color = getUserHeadBgColor(subItem.createUserId)
|
||||
let subItem = subCommentList[j];
|
||||
subItem.color = getUserHeadBgColor(subItem.createUserId);
|
||||
}
|
||||
commentListRes[i].commentList = subCommentList
|
||||
commentListRes[i].visible = false
|
||||
commentListRes[i].commentList = subCommentList;
|
||||
commentListRes[i].visible = false;
|
||||
}
|
||||
commentList.value = commentListRes
|
||||
scrollActionTabComment()
|
||||
})
|
||||
commentList.value = commentListRes;
|
||||
scrollActionTabComment();
|
||||
});
|
||||
}
|
||||
let canDeleteComment = (row) => {
|
||||
return storeUser.userInfo.id === row.createUserId
|
||||
|| storeUser.userInfo.id === storePage.pageInfo.createUserId
|
||||
|| storeUser.userInfo.id === storePage.pageInfo.createUserId;
|
||||
}
|
||||
const deleteComment = (id) => {
|
||||
pageApi.deletePageComment({id: id}).then(() => {
|
||||
// ElMessage.success("删除成功!");
|
||||
loadCommentList()
|
||||
})
|
||||
loadCommentList();
|
||||
});
|
||||
}
|
||||
const cancelCommentUser = () => {
|
||||
recommentInfo.value = {}
|
||||
recommentInfo.value = {};
|
||||
}
|
||||
const submitPageComment = () => {
|
||||
if (commentTextInput.value.length <= 0) {
|
||||
ElMessage.error('请输入评论内容')
|
||||
return
|
||||
ElMessage.error('请输入评论内容');
|
||||
return;
|
||||
}
|
||||
let param = {
|
||||
pageId: storePage.pageInfo.id,
|
||||
@@ -123,26 +123,26 @@ const submitPageComment = () => {
|
||||
parentId: recommentInfo.value.id,
|
||||
}
|
||||
pageApi.updatePageComment(param).then((json) => {
|
||||
let data = json.data
|
||||
data.color = getUserHeadBgColor(data.createUserId)
|
||||
commentTextInput.value = ''
|
||||
loadCommentList()
|
||||
})
|
||||
let data = json.data;
|
||||
data.color = getUserHeadBgColor(data.createUserId);
|
||||
commentTextInput.value = '';
|
||||
loadCommentList();
|
||||
});
|
||||
}
|
||||
const getUserHeadBgColor = (userId) => {
|
||||
let color = page.userHeadColor[userId]
|
||||
let color = page.userHeadColor[userId];
|
||||
if (!color) {
|
||||
color = page.colorArr[Math.ceil(Math.random() * page.colorArr.length) - 1]
|
||||
page.userHeadColor[userId] = color
|
||||
color = page.colorArr[Math.ceil(Math.random() * page.colorArr.length) - 1];
|
||||
page.userHeadColor[userId] = color;
|
||||
}
|
||||
return color
|
||||
return color;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.comment-box {
|
||||
padding: 8px;
|
||||
height: calc(100vh - 315px);
|
||||
height: calc(100vh - 275px);
|
||||
overflow: auto;
|
||||
|
||||
.comment-card {
|
||||
|
||||
196
zyplayer-doc-ui/wiki-ui/src/views/view/show/Files.vue
Normal file
196
zyplayer-doc-ui/wiki-ui/src/views/view/show/Files.vue
Normal file
@@ -0,0 +1,196 @@
|
||||
<template>
|
||||
<div class="files-box">
|
||||
<div class="upload-file-box">
|
||||
<el-upload class="upload-page-file" :action="uploadFileUrl" ref="uploadFileRef"
|
||||
:with-credentials="true" :before-upload="beforeUploadFile"
|
||||
:on-success="uploadFileSuccess" :on-error="uploadFileError"
|
||||
name="files" show-file-list multiple :data="uploadFormData" :limit="999"
|
||||
style="display: inline;vertical-align: sub;">
|
||||
<a-button :icon="h(UploadOutlined)" type="primary" style="width: 260px;">上传附件</a-button>
|
||||
</el-upload>
|
||||
</div>
|
||||
<div v-if="storePage.fileList.length <= 0" class="action-box-empty">
|
||||
<el-empty description="暂无附件" />
|
||||
</div>
|
||||
<div v-else class="files-list">
|
||||
<template v-for="file in storePage.fileList">
|
||||
<a-card class="box-card files-card" :body-style="{ padding: '10px' }">
|
||||
<div class="head">{{file.createUserName}}</div>
|
||||
<div class="files-user-name">
|
||||
<span>{{file.createUserName}}</span>
|
||||
<el-tooltip :content="file.createTime" placement="top-start" :show-after="1000">
|
||||
<span class="time">{{ file.createTime }}</span>
|
||||
</el-tooltip>
|
||||
<el-icon v-if="storePage.pageAuth.canDeleteFile === 1" @click="deleteFile(file)" class="icon-delete"><ElIconDelete /></el-icon>
|
||||
</div>
|
||||
<div class="files-name">
|
||||
<a target="_blank" :href="file.fileUrl">{{file.fileName || '--'}}</a>
|
||||
</div>
|
||||
<div class="files-size">
|
||||
<IconParkMemoryOne/><span class="value">{{computeFileSize(file.fileSize)}}</span>
|
||||
<IconParkDownload style="margin-left: 15px;"/> <span class="value">{{file.downloadNum || 0}}</span>
|
||||
</div>
|
||||
</a-card>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {Delete as ElIconDelete,} from '@element-plus/icons-vue'
|
||||
import {UploadOutlined} from '@ant-design/icons-vue';
|
||||
import {toRefs, ref, reactive, onMounted, watch, h, defineEmits, computed} from 'vue';
|
||||
import {useRouter, useRoute} from "vue-router";
|
||||
import { message, Modal } from 'ant-design-vue';
|
||||
import pageApi from "@/assets/api/page";
|
||||
import {useStorePageData} from "@/store/pageData";
|
||||
import unitUtil from "@/assets/lib/UnitUtil";
|
||||
import {
|
||||
MemoryOne as IconParkMemoryOne,
|
||||
Download as IconParkDownload,
|
||||
} from '@icon-park/vue-next'
|
||||
|
||||
let storePage = useStorePageData();
|
||||
|
||||
let uploadFileUrl = ref(import.meta.env.VITE_APP_BASE_API + '/zyplayer-doc-wiki/page/file/upload');
|
||||
const deleteFile = (file) => {
|
||||
Modal.confirm({
|
||||
maskClosable: true,
|
||||
title: '提示',
|
||||
content: '确定要删除此文件吗?',
|
||||
okText: '删除',
|
||||
cancelText: '确定',
|
||||
onOk: () => {
|
||||
pageApi.deletePageFile({id: file.id}).then(() => {
|
||||
storePage.fileList = storePage.fileList.filter(item => item !== file);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
let uploadFileRef = ref();
|
||||
let uploadFormData = computed(() => {
|
||||
return {pageId: storePage.pageInfo.id};
|
||||
});
|
||||
const beforeUploadFile = (file) => {
|
||||
}
|
||||
const uploadFileError = (err) => {
|
||||
message.error('上传失败,' + err);
|
||||
}
|
||||
let uploadOverStatus = ['success', 'fail'];
|
||||
const uploadFileSuccess = (response, file, fileList) => {
|
||||
if (response.errCode !== 200) {
|
||||
message('上传失败:' + (response.errMsg || '未知错误'));
|
||||
} else {
|
||||
storePage.fileList.push(response.data);
|
||||
}
|
||||
// 是否全部上传完成
|
||||
if (fileList.every(file => uploadOverStatus.includes(file.status))) {
|
||||
message.success('上传成功!');
|
||||
uploadFileRef.value.clearFiles();
|
||||
}
|
||||
}
|
||||
const computeFileSize = (fileSize) => {
|
||||
return unitUtil.computeFileSize(fileSize);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.files-box {
|
||||
.upload-page-file {
|
||||
.el-upload-list {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.files-box {
|
||||
padding: 50px 8px 8px;
|
||||
height: calc(100vh - 130px);
|
||||
overflow: auto;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
|
||||
.upload-file-box {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
text-align: center;
|
||||
background: #fff;
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.files-card {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.files-user-name {
|
||||
margin-bottom: 6px;
|
||||
font-size: 13px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.files-card .files-user-name .icon-delete {
|
||||
color: #888;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
float: right;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.files-card .files-user-name .icon-delete {
|
||||
color: #888;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
display: none;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 2px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.files-card:hover .files-user-name .icon-delete {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.head {
|
||||
float: left;
|
||||
margin-right: 10px;
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
.files-user-name .time {
|
||||
color: #888;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.files-name {
|
||||
padding: 0;
|
||||
color: #666;
|
||||
margin: 0;
|
||||
line-height: 20px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.files-size {
|
||||
margin-left: 50px;
|
||||
margin-top: 5px;
|
||||
font-size: 14px;
|
||||
|
||||
.value {
|
||||
margin-left: 4px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -15,14 +15,25 @@
|
||||
import {
|
||||
Search as ElIconSearch,
|
||||
} from '@element-plus/icons-vue'
|
||||
import {markRaw} from 'vue'
|
||||
import {toRefs, ref, reactive, onMounted, onBeforeUnmount, defineProps, watch, defineEmits, computed, defineExpose} from 'vue';
|
||||
import {markRaw} from 'vue';
|
||||
import {
|
||||
toRefs,
|
||||
ref,
|
||||
reactive,
|
||||
onMounted,
|
||||
onBeforeUnmount,
|
||||
defineProps,
|
||||
watch,
|
||||
defineEmits,
|
||||
computed,
|
||||
defineExpose
|
||||
} from 'vue';
|
||||
import {useRouter, useRoute} from "vue-router";
|
||||
import {ElMessageBox, ElMessage, ElLoading, ElNotification} from 'element-plus'
|
||||
import {ElMessageBox, ElMessage, ElLoading, ElNotification} from 'element-plus';
|
||||
import pageApi from "@/assets/api/page";
|
||||
import {useStorePageData} from "@/store/pageData";
|
||||
import userApi from "@/assets/api/user";
|
||||
import QRCode from 'qrcode'
|
||||
import QRCode from 'qrcode';
|
||||
import {useStoreSpaceData} from "@/store/spaceData";
|
||||
|
||||
let storePage = useStorePageData();
|
||||
@@ -59,18 +70,13 @@ const initMobileQrScan = () => {
|
||||
});
|
||||
let hostPath = window.location.href.split('#')[0];
|
||||
setTimeout(() => {
|
||||
qrCodeUrl.value = hostPath + routeUrl.href
|
||||
qrCodeUrl.value = hostPath + routeUrl.href;
|
||||
QRCode.toCanvas(qrCodeDivRef.value, qrCodeUrl.value, {
|
||||
scale: 5, height: 250, wight: 250,
|
||||
}, (error) => {
|
||||
if (error) console.error(error);
|
||||
}
|
||||
)
|
||||
);
|
||||
}, 0);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.page-auth-dialog {
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
<template>
|
||||
<div class="navigation-box">
|
||||
<div class="navigation-content-box">
|
||||
<div></div>
|
||||
<div class="nav-heading" :style="{ width: navigationWidth }">
|
||||
<div :style="navigationStyle" class="navigation-box">
|
||||
<div v-if="navigationMin">
|
||||
<a-button @click="navigationToMax" class="navigation-max-btn" size="large" type="text" shape="circle">
|
||||
<IconParkViewList :size="18"/>
|
||||
</a-button>
|
||||
</div>
|
||||
<div v-else :style="navigationStyle" @mouseover="navigationMouseover" @mouseleave="navigationMouseleave"
|
||||
:class="navigationShow?'':'box-shadow'" class="navigation-content-box">
|
||||
<div class="nav-heading">
|
||||
<div v-for="item in heading" :class="'heading-item heading-' + item.level" @click="headingItemClick(item)">
|
||||
{{ item.text }}
|
||||
</div>
|
||||
@@ -12,14 +17,10 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {toRefs, ref, reactive, onMounted, watch, defineEmits, defineProps, defineExpose,} from 'vue'
|
||||
import {useStoreDisplay} from '@/store/wikiDisplay.js'
|
||||
import {useStorePageData} from "@/store/pageData";
|
||||
import {ViewList as IconParkViewList} from '@icon-park/vue-next'
|
||||
import {toRefs, ref, reactive, onMounted, watch, defineEmits, defineProps, defineExpose,} from 'vue';
|
||||
import {useResizeEvent} from "@/composable/windowsScroll";
|
||||
|
||||
let storePage = useStorePageData();
|
||||
const storeDisplay = useStoreDisplay();
|
||||
let navigationWidth = ref('100px');
|
||||
const props = defineProps({
|
||||
heading: {type: Array, default: []},
|
||||
});
|
||||
@@ -29,8 +30,44 @@ onMounted(() => {
|
||||
useResizeEvent(() => {
|
||||
computeNavigationWidth();
|
||||
});
|
||||
let isLeave = false;
|
||||
let leaveTimer = undefined;
|
||||
const navigationMouseover = () => {
|
||||
isLeave = false;
|
||||
if (leaveTimer) {
|
||||
clearTimeout(leaveTimer);
|
||||
leaveTimer = undefined;
|
||||
}
|
||||
}
|
||||
const navigationMouseleave = () => {
|
||||
if (isLeave) return;
|
||||
isLeave = true;
|
||||
leaveTimer = setTimeout(() => {
|
||||
isLeave = false;
|
||||
navigationMin.value = !navigationShow.value;
|
||||
}, 500);
|
||||
}
|
||||
const navigationToMax = () => {
|
||||
navigationMin.value = false;
|
||||
}
|
||||
let navigationMin = ref(false);
|
||||
let navigationShow = ref(true);
|
||||
let navigationStyle = ref({width: '200px'});
|
||||
const computeNavigationWidth = () => {
|
||||
|
||||
let pageViewContent = document.getElementById('pageContentBox');
|
||||
let pageContentScrollBox = document.getElementById('pageContentScrollBox');
|
||||
// pageContentScrollBox的宽度减去pageViewContent的宽度除以2
|
||||
if (pageViewContent && pageContentScrollBox) {
|
||||
let outerWidth = pageContentScrollBox.clientWidth - pageViewContent.clientWidth;
|
||||
if (outerWidth > 160) {
|
||||
navigationShow.value = true;
|
||||
navigationStyle.value.width = (outerWidth / 2 - 50) + 'px';
|
||||
} else {
|
||||
navigationStyle.value.width = '200px';
|
||||
navigationShow.value = false;
|
||||
}
|
||||
navigationMin.value = !navigationShow.value;
|
||||
}
|
||||
}
|
||||
const headingItemClick = (item) => {
|
||||
// 滚动到指定节点
|
||||
@@ -40,29 +77,43 @@ const headingItemClick = (item) => {
|
||||
inline: 'nearest',
|
||||
});
|
||||
// 距离顶部高度
|
||||
//console.log(item.node.offsetTop - item.node.scrollHeight)
|
||||
//console.log(item.node.offsetTop - item.node.scrollHeight);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.navigation-box {
|
||||
width: 100px;
|
||||
position: absolute;
|
||||
top: 150px;
|
||||
top: 60px;
|
||||
right: 10px;
|
||||
z-index: 4;
|
||||
|
||||
.navigation-max-btn {
|
||||
position: fixed;
|
||||
margin-left: 160px;
|
||||
opacity: 0.7;
|
||||
box-shadow: var(--el-box-shadow-light);
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
background: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.navigation-content-box {
|
||||
position: fixed;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
margin-left: -40px;
|
||||
box-shadow: var(--el-box-shadow-lighter);
|
||||
margin-left: -30px;
|
||||
|
||||
&.box-shadow {
|
||||
box-shadow: var(--el-box-shadow-lighter);
|
||||
}
|
||||
}
|
||||
|
||||
.nav-heading {
|
||||
max-height: calc(100vh - 250px);
|
||||
max-height: calc(100vh - 200px);
|
||||
overflow-y: auto;
|
||||
|
||||
.heading-item {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<!--人员权限弹窗-->
|
||||
<el-dialog title="页面权限" v-model="dataItemEditVisible" width="800px" class="page-auth-dialog">
|
||||
<el-row>
|
||||
<el-select v-model="pageAuthNewUser" filterable remote reserve-keyword autoComplete="new-password" placeholder="请输入名字、邮箱、账号搜索用户" :remote-method="getSearchUserList" :loading="pageAuthUserLoading" style="width: 690px; margin-right: 10px">
|
||||
<el-select v-model="pageAuthNewUser" filterable remote reserve-keyword autoComplete="new-password" placeholder="请输入名字、邮箱、账号搜索用户" :remote-method="getSearchUserList" :loading="pageAuthUserLoading" style="width: 690px; margin-right: 10px;">
|
||||
<el-option v-for="item in searchUserList" :key="item.id" :label="item.userName" :value="item.id"></el-option>
|
||||
</el-select>
|
||||
<el-button @click="addPageAuthUser">添加</el-button>
|
||||
@@ -35,10 +35,10 @@
|
||||
import {
|
||||
Search as ElIconSearch,
|
||||
} from '@element-plus/icons-vue'
|
||||
import {markRaw} from 'vue'
|
||||
import {markRaw} from 'vue';
|
||||
import {toRefs, ref, reactive, onMounted, onBeforeUnmount, defineProps, watch, defineEmits, computed, defineExpose} from 'vue';
|
||||
import {useRouter, useRoute} from "vue-router";
|
||||
import {ElMessageBox, ElMessage, ElLoading, ElNotification} from 'element-plus'
|
||||
import {ElMessageBox, ElMessage, ElLoading, ElNotification} from 'element-plus';
|
||||
import pageApi from "@/assets/api/page";
|
||||
import {useStorePageData} from "@/store/pageData";
|
||||
import userApi from "@/assets/api/user";
|
||||
@@ -106,7 +106,7 @@ const addPageAuthUser = () => {
|
||||
pageFileUpload: 0,
|
||||
pageFileDelete: 0,
|
||||
pageAuthManage: 0,
|
||||
})
|
||||
});
|
||||
pageAuthNewUser.value = '';
|
||||
}
|
||||
const getSearchUserList = (query) => {
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
<template>
|
||||
<div class="action-tab-box">
|
||||
<div v-if="props.pageHistoryList.length <= 0" class="action-box-empty">
|
||||
<div v-if="history.length <= 0" class="action-box-empty">
|
||||
暂无修改历史记录
|
||||
</div>
|
||||
<el-timeline v-else>
|
||||
<el-timeline-item v-for="history in props.pageHistoryList">
|
||||
<el-tag :type="props.pageHistoryChoice.id === history.id ? history.loading === 3 ? 'danger' : 'success' : 'info'"
|
||||
<el-timeline-item v-for="history in history">
|
||||
<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 class="history-loading-status" v-if="history.loading===1">
|
||||
<el-icon-loading/>
|
||||
</el-icon>
|
||||
<el-icon class="history-loading-status" v-show="history.loading===2">
|
||||
<el-icon class="history-loading-status" v-else-if="history.loading===2">
|
||||
<el-icon-circle-check/>
|
||||
</el-icon>
|
||||
<el-icon class="history-loading-status" v-show="history.loading===3">
|
||||
<el-icon class="history-loading-status" v-else-if="history.loading===3">
|
||||
<el-icon-circle-close/>
|
||||
</el-icon>
|
||||
</el-timeline-item>
|
||||
@@ -31,50 +31,40 @@ import {
|
||||
Loading as ElIconLoading,
|
||||
} from '@element-plus/icons-vue'
|
||||
|
||||
import pageApi from '@/assets/api/page'
|
||||
import pageApi from '@/assets/api/page';
|
||||
import {mavonEditor} from "mavon-editor";
|
||||
import {ref, defineProps, defineEmits} from 'vue';
|
||||
import {useStorePageData} from "@/store/pageData";
|
||||
let storePage = useStorePageData();
|
||||
|
||||
let props= defineProps({
|
||||
pageHistoryList:Array,
|
||||
pageHistoryChoice:Object,
|
||||
pageHistoryDetail:String,
|
||||
})
|
||||
let emit = defineEmits(['historyClickHandle','previewPageImage','createNavigationHeading'])
|
||||
let props = defineProps({
|
||||
history: Array,
|
||||
});
|
||||
let emit = defineEmits(['choice']);
|
||||
|
||||
let pageHistoryChoice = ref({});
|
||||
const historyClick = (history) => {
|
||||
if (props.pageHistoryChoice.id === history.id && !!props.pageHistoryDetail.value) {
|
||||
if (pageHistoryChoice.value.id === history.id && !!pageHistoryChoice.value.content) {
|
||||
return;
|
||||
}
|
||||
pageHistoryChoice.value.loading = 0;
|
||||
pageHistoryChoice.value = history;
|
||||
// 缓存一下,但如果历史页面多了而且很大就占内存,也可以每次去拉取,先这样吧
|
||||
if (history.content) {
|
||||
history.loading = 2;
|
||||
emit('historyClickHandle',history)
|
||||
setTimeout(() => {
|
||||
emit('previewPageImage',history)
|
||||
emit('createNavigationHeading',history)
|
||||
}, 500)
|
||||
emit('choice', history);
|
||||
} else {
|
||||
history.loading = 1
|
||||
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);
|
||||
emit('choice', history);
|
||||
}).catch(() => {
|
||||
history.loading = 3;
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
</style>
|
||||
|
||||
@@ -1,25 +1,29 @@
|
||||
<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 class="zan-box">
|
||||
<div class="zan-btn-box">
|
||||
<VueStarPlus v-model="starActive" animate="animated tada">
|
||||
<template #icon>
|
||||
<a-button @click="zanPage()" :type="starActive?'primary':'default'" shape="circle" class="zan-btn">
|
||||
<IconParkThumbsUp :size="30"/>
|
||||
</a-button>
|
||||
</template>
|
||||
</VueStarPlus>
|
||||
</div>
|
||||
<template v-if="storePage.pageInfo.zanNum <= 0">
|
||||
<el-divider>为它点赞</el-divider>
|
||||
</template>
|
||||
<template v-else-if="storePage.pageInfo.zanNum > 0">
|
||||
<template v-if="storePage.pageInfo.selfZan === 1">
|
||||
<template v-if="storePage.pageInfo.zanNum === 1">
|
||||
<el-divider>我赞了它</el-divider>
|
||||
</template>
|
||||
<el-divider v-else>我和<a @click="showZanPageUser">{{storePage.pageInfo.zanNum - 1}} 人</a> 赞了它</el-divider>
|
||||
</template>
|
||||
<el-divider v-else><a @click="showZanPageUser">{{storePage.pageInfo.zanNum}} 人</a> 赞了它</el-divider>
|
||||
</template>
|
||||
</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 :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>
|
||||
@@ -27,29 +31,69 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import pageApi from '../../../assets/api/page'
|
||||
import {ref} from 'vue';
|
||||
import {ThumbsUp as IconParkThumbsUp} from '@icon-park/vue-next'
|
||||
import pageApi from '../../../assets/api/page';
|
||||
import {ref, watch, onMounted} from 'vue';
|
||||
import {useStorePageData} from "@/store/pageData";
|
||||
import {
|
||||
View as ElIconView,
|
||||
} from '@element-plus/icons-vue'
|
||||
let zanUserList = ref([]);
|
||||
let zanUserDialogVisible = ref(false);
|
||||
import VueStarPlus from 'vue-star-plus'
|
||||
import 'vue-star-plus/style.css'
|
||||
|
||||
let storePage = useStorePageData();
|
||||
|
||||
const zanPage = (yn) => {
|
||||
let param = {yn: yn, pageId: storePage.pageInfo.id}
|
||||
let zanUserList = ref([]);
|
||||
let zanUserDialogVisible = ref(false);
|
||||
let starActive = ref(false);
|
||||
watch(() => storePage.pageInfo, () => {
|
||||
starActive.value = (storePage.pageInfo.selfZan === 1);
|
||||
});
|
||||
onMounted(() => {
|
||||
starActive.value = (storePage.pageInfo.selfZan === 1);
|
||||
});
|
||||
const zanPage = () => {
|
||||
let yn = storePage.pageInfo.selfZan === 1 ? 0 : 1;
|
||||
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)
|
||||
})
|
||||
starActive.value = (yn === 1);
|
||||
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}
|
||||
zanUserDialogVisible.value = true;
|
||||
zanUserList.value = [];
|
||||
let param = {pageId: storePage.pageInfo.id};
|
||||
pageApi.pageZanList(param).then((json) => {
|
||||
zanUserList.value = json.data
|
||||
})
|
||||
zanUserList.value = json.data || [];
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.zan-box {
|
||||
margin-top: 50px;
|
||||
text-align: center;
|
||||
|
||||
.zan-btn-box {
|
||||
position: relative;
|
||||
|
||||
.vue-star-plus {
|
||||
width: 100%;
|
||||
position: unset;
|
||||
|
||||
.vue-star-plus__ground {
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
.zan-btn {
|
||||
height: 60px;
|
||||
width: 60px;
|
||||
}
|
||||
}
|
||||
|
||||
.el-divider {
|
||||
width: 300px;
|
||||
margin: 20px auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user