菜单目录优化
This commit is contained in:
@@ -0,0 +1,41 @@
|
|||||||
|
import { createVNode, render } from "vue";
|
||||||
|
import PromptDialog from "./PromptDialog.vue";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成一个带输入框的弹窗
|
||||||
|
* @author zyplayer-doc
|
||||||
|
* @date 2024-12-08
|
||||||
|
*/
|
||||||
|
let promise = null;
|
||||||
|
function getInstance(config) {
|
||||||
|
if (promise) {
|
||||||
|
try {
|
||||||
|
promise.close();
|
||||||
|
} catch (error) {}
|
||||||
|
}
|
||||||
|
const div = document.createElement("div");
|
||||||
|
const conf = Object.assign({cancelText: '取消', okText: '确定'}, config);
|
||||||
|
const close = () => {
|
||||||
|
try {
|
||||||
|
document.body.removeChild(div);
|
||||||
|
promise.reject();
|
||||||
|
promise = null;
|
||||||
|
} catch (error) {}
|
||||||
|
};
|
||||||
|
conf.close = close;
|
||||||
|
conf.ok = (value) => {
|
||||||
|
promise.resolve(value);
|
||||||
|
};
|
||||||
|
const node = createVNode(PromptDialog, conf);
|
||||||
|
render(node, div);
|
||||||
|
document.body.appendChild(div);
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
promise = {resolve, reject, close};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const MessagePrompt = (opts) => {
|
||||||
|
return getInstance(opts);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MessagePrompt;
|
||||||
|
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
<template>
|
||||||
|
<!--通用输入弹窗-->
|
||||||
|
<a-modal :title="title" v-model:open="editDialogVisible" :destroyOnClose="true" width="420px" class="message-prompt-dialog">
|
||||||
|
<a-form layout="vertical" :model="inputForm" :rules="inputFormRules" ref="inputFormRef" @submit.prevent>
|
||||||
|
<a-form-item :label="label" name="data">
|
||||||
|
<a-input ref="formDataInput" v-model:value="inputForm.data" @keyup.enter.native="editOk" :placeholder="placeholder"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
<template #footer>
|
||||||
|
<a-button @click="editCancel">{{cancelText}}</a-button>
|
||||||
|
<a-button type="primary" @click="editOk">{{okText}}</a-button>
|
||||||
|
</template>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {Modal as AModal, Button as AButton, Form as AForm, FormItem as AFormItem, Input as AInput} from 'ant-design-vue';
|
||||||
|
import {onBeforeUnmount, ref, shallowRef, watch, onMounted, nextTick, useAttrs, defineProps, defineEmits, defineExpose} from 'vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
cancelText: String,
|
||||||
|
okText: String,
|
||||||
|
title: String,
|
||||||
|
label: String,
|
||||||
|
placeholder: String,
|
||||||
|
value: String,
|
||||||
|
validator: Function,
|
||||||
|
ok: Function,
|
||||||
|
close: Function,
|
||||||
|
rules: {type: Object, default: {}}
|
||||||
|
});
|
||||||
|
onMounted(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
formDataInput.value.focus();
|
||||||
|
inputForm.value.data = props.value || '';
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
let editDialogVisible = ref(true);
|
||||||
|
watch(editDialogVisible, () => {
|
||||||
|
if (!editDialogVisible.value) {
|
||||||
|
setTimeout(() => {
|
||||||
|
props.close && props.close();
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let inputForm = ref({data: ''});
|
||||||
|
let inputFormRef = ref();
|
||||||
|
let formDataInput = ref();
|
||||||
|
let inputFormRules = ref({
|
||||||
|
data: [{
|
||||||
|
trigger: 'blur',
|
||||||
|
validator: (rule, value) => {
|
||||||
|
if (props.validator) {
|
||||||
|
let res = props.validator(value);
|
||||||
|
if (res !== true) {
|
||||||
|
return Promise.reject(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
const editCancel = () => {
|
||||||
|
editDialogVisible.value = false;
|
||||||
|
}
|
||||||
|
const editOk = () => {
|
||||||
|
inputFormRef.value.validate().then(() => {
|
||||||
|
if (props.ok) {
|
||||||
|
props.ok(inputForm.value.data);
|
||||||
|
}
|
||||||
|
editDialogVisible.value = false;
|
||||||
|
}).catch(() => {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.message-prompt-dialog {
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -129,69 +129,3 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.space-folder-box {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wiki-page-tree-box {
|
|
||||||
.el-tree-node__content {
|
|
||||||
height: 35px;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
.page-tree-node {
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
.label {
|
|
||||||
.el-icon {
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text {
|
|
||||||
margin-left: 5px;
|
|
||||||
vertical-align: middle;
|
|
||||||
max-width: calc(100% - 40px);
|
|
||||||
display: inline-block;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.rename-input {
|
|
||||||
width: 90%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-action-box {
|
|
||||||
position: absolute;
|
|
||||||
right: 0;
|
|
||||||
top: 0;
|
|
||||||
height: 35px;
|
|
||||||
line-height: 35px;
|
|
||||||
background: #fff;
|
|
||||||
border-radius: 4px;
|
|
||||||
display: none;
|
|
||||||
|
|
||||||
.page-action-dropdown-btn {
|
|
||||||
padding: 0 8px;
|
|
||||||
height: 35px;
|
|
||||||
margin-top: -1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-button + .el-button {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-action-box.renaming {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover .page-action-box {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -3,18 +3,18 @@
|
|||||||
<a-button :icon="h(PlusOutlined)" type="text" style="color: #888;"></a-button>
|
<a-button :icon="h(PlusOutlined)" type="text" style="color: #888;"></a-button>
|
||||||
<template #overlay>
|
<template #overlay>
|
||||||
<a-menu>
|
<a-menu>
|
||||||
<a-menu-item @click="createWiki(1)">
|
<a-menu-item @click="createPage(1)">
|
||||||
<IconParkWord fill="#498ba7"/> 创建富文本
|
<IconParkWord fill="#498ba7"/> 新建富文本
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
<a-menu-item @click="createWiki(2)">
|
<a-menu-item @click="createPage(2)">
|
||||||
<IconDocument fill="#558ff2"/> 创建Markdown
|
<IconDocument fill="#558ff2"/> 新建Markdown
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
<a-menu-item @click="createWiki(0)">
|
<a-menu-item @click="createFolder">
|
||||||
<FolderOpen fill="#ffd149"/> 创建文件夹
|
<FolderOpen fill="#ffd149"/> 新建文件夹
|
||||||
</a-menu-item>
|
|
||||||
<a-menu-item @click="createWikiByTemplate()">
|
|
||||||
<IconParkPageTemplate/> 从模板创建
|
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
|
<!-- <a-menu-item @click="createPageByTemplate()">-->
|
||||||
|
<!-- <IconParkPageTemplate/> 从模板创建-->
|
||||||
|
<!-- </a-menu-item>-->
|
||||||
<!-- <a-menu-item key="3">-->
|
<!-- <a-menu-item key="3">-->
|
||||||
<!-- <el-tooltip content="支持MD,ZIP格式(图片和MD文件请放到同级目录并配置同级相对路径)" placement="right-start" :show-after="300">-->
|
<!-- <el-tooltip content="支持MD,ZIP格式(图片和MD文件请放到同级目录并配置同级相对路径)" placement="right-start" :show-after="300">-->
|
||||||
<!-- <a-upload v-model:file-list="fileList" name="file" :multiple="false" :customRequest="doAUpload" class="import-upload">-->
|
<!-- <a-upload v-model:file-list="fileList" name="file" :multiple="false" :customRequest="doAUpload" class="import-upload">-->
|
||||||
@@ -38,6 +38,7 @@ import axios from "axios";
|
|||||||
import IconDocument from '@/components/base/IconDocument.vue'
|
import IconDocument from '@/components/base/IconDocument.vue'
|
||||||
import {useStorePageData} from "@/store/pageData";
|
import {useStorePageData} from "@/store/pageData";
|
||||||
import {useStoreSpaceData} from "@/store/spaceData";
|
import {useStoreSpaceData} from "@/store/spaceData";
|
||||||
|
import MessagePrompt from "@/components/single/MessagePrompt";
|
||||||
|
|
||||||
let router = useRouter();
|
let router = useRouter();
|
||||||
let storePage = useStorePageData();
|
let storePage = useStorePageData();
|
||||||
@@ -45,7 +46,6 @@ let storeSpace = useStoreSpaceData();
|
|||||||
|
|
||||||
let uploadFileUrl = ref(import.meta.env.VITE_APP_BASE_API + '/zyplayer-doc-wiki/page/file/import/upload');
|
let uploadFileUrl = ref(import.meta.env.VITE_APP_BASE_API + '/zyplayer-doc-wiki/page/file/import/upload');
|
||||||
let fileList = ref([]);
|
let fileList = ref([]);
|
||||||
let emit = defineEmits(['createWikiByTemplate']);
|
|
||||||
let props = defineProps({
|
let props = defineProps({
|
||||||
pageId: Number
|
pageId: Number
|
||||||
});
|
});
|
||||||
@@ -78,30 +78,47 @@ const doAUpload = (data) => {
|
|||||||
ElMessage.error('导入失败:' + e.message);
|
ElMessage.error('导入失败:' + e.message);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const createWikiByTemplate = () => {
|
const createPageByTemplate = () => {
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
const createWiki = (editorType) => {
|
const createPage = (editorType, confirmName) => {
|
||||||
if (storeSpace.chooseSpaceId > 0) {
|
if (!storeSpace.chooseSpaceId) {
|
||||||
let name = "新建文档";
|
|
||||||
if (editorType === 0) name = "新建文件夹";
|
|
||||||
pageApi.updatePage({
|
|
||||||
spaceId: storeSpace.chooseSpaceId,
|
|
||||||
parentId: props.pageId,
|
|
||||||
editorType: editorType,
|
|
||||||
name: name,
|
|
||||||
content: '',
|
|
||||||
preview: ''
|
|
||||||
}).then((json) => {
|
|
||||||
storePage.eventPageListUpdate = !storePage.eventPageListUpdate;
|
|
||||||
ElMessage.success('创建成功');
|
|
||||||
if (editorType !== 0) {
|
|
||||||
router.push({path: '/page/edit', query: {parentId: props.pageId, pageId: json.data.id}});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
ElMessage.warning('请先选择或创建空间');
|
ElMessage.warning('请先选择或创建空间');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
let name = "新建文档";
|
||||||
|
if (editorType === 0) name = confirmName || "新建文件夹";
|
||||||
|
pageApi.updatePage({
|
||||||
|
spaceId: storeSpace.chooseSpaceId,
|
||||||
|
parentId: props.pageId,
|
||||||
|
editorType: editorType,
|
||||||
|
name: name,
|
||||||
|
content: '',
|
||||||
|
preview: ''
|
||||||
|
}).then((json) => {
|
||||||
|
storePage.eventPageListUpdate = !storePage.eventPageListUpdate;
|
||||||
|
ElMessage.success('创建成功');
|
||||||
|
if (editorType !== 0) {
|
||||||
|
router.push({path: '/page/edit', query: {parentId: props.pageId, pageId: json.data.id}});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let createFolder = () => {
|
||||||
|
MessagePrompt({
|
||||||
|
title: '新建文件夹',
|
||||||
|
label: '文件夹名称',
|
||||||
|
placeholder: '请输入文件夹名称',
|
||||||
|
validator: (value) => {
|
||||||
|
if (!value || !value.trim()) return '请输入文件夹名称';
|
||||||
|
if (value && value.length > 40) return '文件夹名称不能超过40个字符';
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}).then((value) => {
|
||||||
|
if (value && value.trim()) {
|
||||||
|
createPage(0, value.trim());
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -58,19 +58,17 @@
|
|||||||
<el-tooltip v-if="data.shareStatus > 0" :content="data.tags" placement="top-start" :show-after="500">
|
<el-tooltip v-if="data.shareStatus > 0" :content="data.tags" placement="top-start" :show-after="500">
|
||||||
<a-tag color="warning" style="margin-inline-end: 4px;padding-inline: 4px;"> {{data.shareStatus === 1 ? '公共模板' : '个人模板'}}</a-tag>
|
<a-tag color="warning" style="margin-inline-end: 4px;padding-inline: 4px;"> {{data.shareStatus === 1 ? '公共模板' : '个人模板'}}</a-tag>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<a-input v-if="data.renaming" v-model:value="data.name" @blur="doRename(node,data)" @click.stop
|
<span style="vertical-align: middle;margin-left: 5px;">
|
||||||
class="rename-input" placeholder="请输入文档名称"/>
|
|
||||||
<span v-else style="vertical-align: middle;margin-left: 5px;">
|
|
||||||
<el-tooltip :content="node.label" placement="top-start" :show-after="700">{{ node.label }}</el-tooltip>
|
<el-tooltip :content="node.label" placement="top-start" :show-after="700">{{ node.label }}</el-tooltip>
|
||||||
</span>
|
</span>
|
||||||
<!--操作-->
|
<!--操作-->
|
||||||
<div class="page-action-box" :class="data.renaming?'renaming':''" @click.stop>
|
<div class="page-action-box">
|
||||||
<AddMenu :pageId="data.id"/>
|
<AddMenu :pageId="data.id"/>
|
||||||
<a-dropdown :trigger="['click']" @click="choosePageIdFunc(data.id)">
|
<a-dropdown :trigger="['click']" @click="choosePageIdFunc(data.id)">
|
||||||
<a-button :icon="h(EllipsisOutlined)" type="text" style="color: #888;"></a-button>
|
<a-button :icon="h(EllipsisOutlined)" type="text" style="color: #888;"></a-button>
|
||||||
<template #overlay>
|
<template #overlay>
|
||||||
<a-menu>
|
<a-menu>
|
||||||
<a-menu-item @click="rename(node,data)">
|
<a-menu-item @click="renamePage(node,data)">
|
||||||
<IconParkEditTwo class="el-icon"/> 重命名
|
<IconParkEditTwo class="el-icon"/> 重命名
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
<a-sub-menu title="移动文档">
|
<a-sub-menu title="移动文档">
|
||||||
@@ -126,6 +124,7 @@ import {ElMessageBox, ElMessage} from 'element-plus'
|
|||||||
import {useStoreSpaceData} from "@/store/spaceData";
|
import {useStoreSpaceData} from "@/store/spaceData";
|
||||||
import Navigation from "@/views/page/show/Navigation.vue";
|
import Navigation from "@/views/page/show/Navigation.vue";
|
||||||
import PageZan from "@/views/page/show/PageZan.vue";
|
import PageZan from "@/views/page/show/PageZan.vue";
|
||||||
|
import MessagePrompt from "@/components/single/MessagePrompt";
|
||||||
|
|
||||||
let route = useRoute();
|
let route = useRoute();
|
||||||
let router = useRouter();
|
let router = useRouter();
|
||||||
@@ -206,8 +205,29 @@ const createWikiByTemplate = () => {
|
|||||||
const choosePageIdFunc = (id) => {
|
const choosePageIdFunc = (id) => {
|
||||||
storePage.optionPageId = id;
|
storePage.optionPageId = id;
|
||||||
}
|
}
|
||||||
const rename = (node, data) => {
|
const renamePage = (node, data) => {
|
||||||
data.renaming = true;
|
MessagePrompt({
|
||||||
|
title: '重命名',
|
||||||
|
label: '文档名称',
|
||||||
|
placeholder: '请输入文档名称',
|
||||||
|
value: data.name,
|
||||||
|
validator: (value) => {
|
||||||
|
if (!value || !value.trim()) return '文档名称不能为空';
|
||||||
|
if (value && value.length > 255) return '文档名称不能超过255个字符';
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}).then((value) => {
|
||||||
|
if (value && value.trim()) {
|
||||||
|
let name = value.trim();
|
||||||
|
if (data.name !== name) {
|
||||||
|
pageApi.renamePage({id: data.id, name: name}).then(json => {
|
||||||
|
data.name = name;
|
||||||
|
ElMessage.success('重命名成功');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
});
|
||||||
}
|
}
|
||||||
const openMoveMenu = (onlyMove) => {
|
const openMoveMenu = (onlyMove) => {
|
||||||
// TODO
|
// TODO
|
||||||
@@ -243,7 +263,6 @@ const doRename = (node, data) => {
|
|||||||
pageApi.renamePage({"id": data.id, "name": data.name}).then((json) => {
|
pageApi.renamePage({"id": data.id, "name": data.name}).then((json) => {
|
||||||
doGetPageList();
|
doGetPageList();
|
||||||
ElMessage.success('重命名成功');
|
ElMessage.success('重命名成功');
|
||||||
data.renaming = false;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const doGetPageList = () => {
|
const doGetPageList = () => {
|
||||||
@@ -347,10 +366,59 @@ defineExpose({searchByKeywords})
|
|||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
padding-bottom: 10px;
|
padding-bottom: 10px;
|
||||||
|
|
||||||
|
.el-tree-node__content {
|
||||||
|
height: 35px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.page-tree-node {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.label {
|
||||||
|
.el-icon {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
margin-left: 5px;
|
||||||
|
vertical-align: middle;
|
||||||
|
max-width: calc(100% - 40px);
|
||||||
|
display: inline-block;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-action-box {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
height: 35px;
|
||||||
|
line-height: 35px;
|
||||||
|
background: #fafafa;
|
||||||
|
border-radius: 4px;
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
button {
|
||||||
|
padding: 5px 8px;
|
||||||
|
height: 35px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover .page-action-box {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-autocomplete-popper {
|
.search-autocomplete-popper {
|
||||||
width: 600px !important;
|
width: 600px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.space-folder-box {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user