界面优化,markdown渲染优化
This commit is contained in:
@@ -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