登录页开发,控制台页面开发,页面跳转逻辑调整
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import axios from 'axios';
|
||||
import router from '@/routes.js'
|
||||
import {ElMessageBox, ElMessage} from 'element-plus';
|
||||
|
||||
const service = axios.create({
|
||||
@@ -24,7 +25,6 @@ service.interceptors.request.use((config) => {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
let lastToastLoginTime = new Date().getTime();
|
||||
service.interceptors.response.use(
|
||||
(response) => {
|
||||
if (!!response.message) {
|
||||
@@ -33,13 +33,9 @@ service.interceptors.response.use(
|
||||
if (!response.config.needValidateResult || response.data.errCode === 200) {
|
||||
return response.data;
|
||||
} else if (response.data.errCode === 400) {
|
||||
// 两秒钟只提示一次
|
||||
if (new Date().getTime() - lastToastLoginTime > 2000) {
|
||||
ElMessage.warning('请先登录');
|
||||
lastToastLoginTime = new Date().getTime();
|
||||
}
|
||||
let href = encodeURIComponent(window.location.href);
|
||||
window.location = import.meta.env.VITE_APP_BASE_API + '#/user/login?redirect=' + href;
|
||||
let redirectUrl = getRedirectUrl();
|
||||
router.push({path: `/user/login`, query: {redirect: redirectUrl}});
|
||||
return Promise.reject(response.data);
|
||||
} else if (response.data.errCode !== 200) {
|
||||
ElMessage.error(response.data.errMsg || '未知错误');
|
||||
}
|
||||
@@ -51,4 +47,16 @@ service.interceptors.response.use(
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
function getRedirectUrl() {
|
||||
let redirectUrl = '';
|
||||
let locationHref = window.location.href;
|
||||
if (locationHref.indexOf('?') >= 0) {
|
||||
let reg = new RegExp('(^|&)redirect=([^&]*)(&|$)', 'i');
|
||||
let r = locationHref.substring(locationHref.indexOf('?') + 1).match(reg);
|
||||
if (r != null) {
|
||||
redirectUrl = unescape(r[2]);
|
||||
}
|
||||
}
|
||||
return redirectUrl || encodeURIComponent(window.location.href);
|
||||
}
|
||||
export default service
|
||||
|
||||
BIN
zyplayer-doc-ui/wiki-ui/src/assets/img/login-bg.jpg
Normal file
BIN
zyplayer-doc-ui/wiki-ui/src/assets/img/login-bg.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 124 KiB |
BIN
zyplayer-doc-ui/wiki-ui/src/assets/img/space-bg-1.png
Normal file
BIN
zyplayer-doc-ui/wiki-ui/src/assets/img/space-bg-1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 64 KiB |
@@ -1,7 +1,6 @@
|
||||
import hljs from 'highlight.js';
|
||||
import {createApp} from 'vue';
|
||||
import App from './App.vue';
|
||||
import {createRouter, createWebHashHistory} from 'vue-router';
|
||||
import ElementUI from 'element-plus';
|
||||
import Antd from 'ant-design-vue';
|
||||
import zhCn from 'element-plus/es/locale/lang/zh-cn';
|
||||
@@ -15,10 +14,6 @@ import './assets/scss/markdown.scss';
|
||||
import './assets/scss/pageView.scss';
|
||||
import './assets/scss/base.scss';
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
routes,
|
||||
});
|
||||
const app = createApp(App);
|
||||
app.config.productionTip = false;
|
||||
app.use(Antd);
|
||||
@@ -26,7 +21,7 @@ app.use(ElementUI, {
|
||||
locale: zhCn,
|
||||
});
|
||||
app.use(Vant);
|
||||
app.use(router);
|
||||
app.use(routes);
|
||||
app.use(createPinia());
|
||||
app.mount('#app');
|
||||
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import Login from './views/user/Login.vue';
|
||||
import PageLayout from './views/view/PageLayout.vue';
|
||||
import WikiLayout from './views/wiki/Layout.vue';
|
||||
|
||||
import WikiSpace from './views/wiki/Wiki.vue';
|
||||
// import ShareLayout from './components/layouts/ShareLayout.vue';
|
||||
// import ShareMobileLayout from './components/layouts/ShareMobileLayout.vue';
|
||||
|
||||
@@ -16,51 +20,63 @@ import Edit from './views/view/Edit.vue';
|
||||
// import sharePcView from './views/page/share/pc/View.vue';
|
||||
// import shareMobileView from './views/page/share/mobile/View.vue';
|
||||
|
||||
let routes = [
|
||||
{path: '/', redirect: '/home'},
|
||||
{path: '/page/search', name: 'WIKI-全局搜索', component: NoAuth},
|
||||
{path: '/common/noAuth', name: 'WIKI-没有权限', component: NoAuth},
|
||||
{
|
||||
path: '/',
|
||||
name: '文档管理',
|
||||
component: PageLayout,
|
||||
children: [
|
||||
{path: '/home', name: 'WIKI文档管理', component: NoAuth},
|
||||
{path: '/user/myInfo', name: 'WIKI-我的信息', component: NoAuth},
|
||||
{path: '/view/:spaceId?/:pageId?', name: 'WIKI-页面查看', component: Show},
|
||||
{path: '/edit/:spaceId/:pageId', name: 'WIKI-编辑内容', component: Edit},
|
||||
{path: '/space/manage', name: 'WIKI-空间管理', component: NoAuth},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/',
|
||||
name: 'PC端开放文档',
|
||||
component: NoAuth,
|
||||
children: [
|
||||
{
|
||||
path: '/page/share/home',
|
||||
name: 'WIKI-开放文档',
|
||||
component: NoAuth,
|
||||
},
|
||||
{
|
||||
path: '/page/share/view',
|
||||
name: 'WIKI-内容展示',
|
||||
component: NoAuth,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/',
|
||||
name: 'APP端开放文档',
|
||||
component: NoAuth,
|
||||
children: [
|
||||
{
|
||||
path: '/page/share/mobile/view',
|
||||
name: 'WIKI-开放文档-APP',
|
||||
component: NoAuth,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
import {createRouter, createWebHashHistory} from 'vue-router';
|
||||
|
||||
export default routes;
|
||||
export default createRouter({
|
||||
history: createWebHashHistory(),
|
||||
routes: [
|
||||
{path: '/', redirect: '/wiki/space'},
|
||||
{path: '/user/login', name: 'systemLogin', component: Login},
|
||||
{path: '/page/search', name: 'WIKI-全局搜索', component: NoAuth},
|
||||
{path: '/common/noAuth', name: 'WIKI-没有权限', component: NoAuth},
|
||||
{
|
||||
path: '/',
|
||||
name: 'WikiLayout',
|
||||
component: WikiLayout,
|
||||
children: [
|
||||
{path: '/wiki/space', name: 'WIKI文档管理', component: WikiSpace},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/',
|
||||
name: '文档管理',
|
||||
component: PageLayout,
|
||||
children: [
|
||||
// {path: '/home', name: 'WIKI文档管理', component: NoAuth},
|
||||
{path: '/user/myInfo', name: 'WIKI-我的信息', component: NoAuth},
|
||||
{path: '/view/:spaceId?/:pageId?', name: 'WIKI-页面查看', component: Show},
|
||||
{path: '/edit/:spaceId/:pageId', name: 'WIKI-编辑内容', component: Edit},
|
||||
{path: '/space/manage', name: 'WIKI-空间管理', component: NoAuth},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/',
|
||||
name: 'PC端开放文档',
|
||||
component: NoAuth,
|
||||
children: [
|
||||
{
|
||||
path: '/page/share/home',
|
||||
name: 'WIKI-开放文档',
|
||||
component: NoAuth,
|
||||
},
|
||||
{
|
||||
path: '/page/share/view',
|
||||
name: 'WIKI-内容展示',
|
||||
component: NoAuth,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/',
|
||||
name: 'APP端开放文档',
|
||||
component: NoAuth,
|
||||
children: [
|
||||
{
|
||||
path: '/page/share/mobile/view',
|
||||
name: 'WIKI-开放文档-APP',
|
||||
component: NoAuth,
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
});
|
||||
|
||||
@@ -3,10 +3,11 @@ import {defineStore} from 'pinia';
|
||||
export const useStoreSpaceData = defineStore('spaceData', {
|
||||
state: () => {
|
||||
return {
|
||||
spaceInfo:{},
|
||||
chooseSpaceId:1,
|
||||
spaceInfo: {},
|
||||
chooseSpaceId: 1,
|
||||
spaceOptions: [],
|
||||
spaceList:[]
|
||||
spaceList: [],
|
||||
wholeSpaceList: [],
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -6,6 +6,9 @@ export const useStoreUserData = defineStore('userData', {
|
||||
// 用户信息
|
||||
userInfo: {},
|
||||
upgradeInfo: {},
|
||||
// 左侧菜单的路由和收起状态
|
||||
menuRouteKey: [],
|
||||
menuCollapsed: false,
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
108
zyplayer-doc-ui/wiki-ui/src/views/user/Login.vue
Normal file
108
zyplayer-doc-ui/wiki-ui/src/views/user/Login.vue
Normal file
@@ -0,0 +1,108 @@
|
||||
<template>
|
||||
<div class="login-page-view login-background" style="background-image: url('src/assets/img/login-bg.jpg')">
|
||||
<div class="login-warp-flex">
|
||||
<div class="login-content">
|
||||
<a-form :model="loginParam" :rules="loginRules" layout="vertical" ref="loginParamRef" label-position="left" label-width="0px" class="login-form">
|
||||
<h3 class="login-title">文档管理系统 - 账号登录</h3>
|
||||
<a-form-item name="username">
|
||||
<a-input type="text" v-model:value="loginParam.username" auto-complete="off" size="large"
|
||||
placeholder="请输入账号" @keyup.enter.native="loginSubmit()"/>
|
||||
</a-form-item>
|
||||
<a-form-item name="password">
|
||||
<a-input type="password" v-model:value="loginParam.password" auto-complete="off" size="large"
|
||||
placeholder="请输入密码" @keyup.enter.native="loginSubmit()"/>
|
||||
</a-form-item>
|
||||
<a-form-item style="width: 100%">
|
||||
<a-button type="primary" style="width: 100%" size="large" @click.native.prevent="loginSubmit()" :loading="loginLoading">登录</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
</div>
|
||||
<LoginFooter/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {onMounted, ref, computed, h} from 'vue';
|
||||
import {useRoute, useRouter} from "vue-router";
|
||||
import userApi from '@/assets/api/user.js'
|
||||
import {useStoreUserData} from "@/store/userData";
|
||||
import LoginFooter from './LoginFooter.vue'
|
||||
|
||||
let storeUser = useStoreUserData();
|
||||
let route = useRoute();
|
||||
let router = useRouter();
|
||||
onMounted(() => {
|
||||
redirect.value = route.query.redirect;
|
||||
});
|
||||
let loginLoading = ref(false);
|
||||
let redirect = ref('');
|
||||
let loginParam = ref({
|
||||
username: '',
|
||||
password: '',
|
||||
});
|
||||
let loginRules = computed(() => ({
|
||||
username: [{required: true, message: '请输入账号或邮箱', trigger: 'blur'}],
|
||||
password: [{required: true, message: '请输入密码', trigger: 'blur'}],
|
||||
}));
|
||||
let loginParamRef = ref();
|
||||
const loginSubmit = () => {
|
||||
loginParamRef.value.validate().then(() => {
|
||||
loginLoading.value = true;
|
||||
userApi.userLogin(loginParam.value).then(() => {
|
||||
// 跳转回之前的页面
|
||||
loginLoading.value = false;
|
||||
if (!!redirect.value) {
|
||||
location.href = decodeURIComponent(redirect.value);
|
||||
} else {
|
||||
// 没有目标页面跳至文档首页
|
||||
router.replace({path: '/'});
|
||||
}
|
||||
}).catch((e) => {
|
||||
console.log('登录失败', e);
|
||||
loginLoading.value = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.login-page-view {
|
||||
.el-form-item {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.login-page-view {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
.login-warp-flex {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 80%;
|
||||
|
||||
.login-content {
|
||||
width: 400px;
|
||||
max-width: 96%;
|
||||
padding: 35px;
|
||||
background: #ffffff99;
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 10px;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0 1px 2px -2px rgba(0, 0, 0, 0.16), 0 3px 6px 0 rgba(0, 0, 0, 0.12), 0 5px 12px 4px rgba(0, 0, 0, 0.09);
|
||||
|
||||
.login-form {
|
||||
.login-title {
|
||||
margin: 0 auto 40px auto;
|
||||
text-align: center;
|
||||
color: #505458;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
40
zyplayer-doc-ui/wiki-ui/src/views/user/LoginFooter.vue
Normal file
40
zyplayer-doc-ui/wiki-ui/src/views/user/LoginFooter.vue
Normal file
@@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<div class="login-footer">
|
||||
<span class="item">
|
||||
Powered by <a target="_blank" href="https://doc.zyplayer.com">zyplayer-doc</a>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {toRefs, ref, reactive, onMounted, watch, defineEmits} from 'vue';
|
||||
import { useRouter, useRoute } from "vue-router";
|
||||
import {useStoreUserData} from "@/store/userData";
|
||||
|
||||
let storeUser = useStoreUserData();
|
||||
let route = useRoute();
|
||||
let router = useRouter();
|
||||
onMounted(() => {
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.login-footer {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
text-align: center;
|
||||
padding: 15px 0;
|
||||
|
||||
.item + .item {
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #666;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -17,10 +17,10 @@
|
||||
<div v-if="wikiPage.editorType === 1">
|
||||
<WangEditor ref="wangEditorRef" :pageId="pageId"></WangEditor>
|
||||
</div>
|
||||
<div v-else-if="wikiPage.editorType === 2" style="padding: 10px; background: #fff;">
|
||||
<div v-else-if="wikiPage.editorType === 2" style="padding: 5px 8px 5px; background: #fff;">
|
||||
<mavonEditor ref="mavonEditorRef" v-model="markdownContent" :toolbars="toolbars" :externalLink="false"
|
||||
@save="createWikiSave(0)" @imgAdd="addMarkdownImage" placeholder="请录入文档内容"
|
||||
class="page-content-editor wang-editor-body" style="height: calc(100vh - 100px);z-index: 1;"/>
|
||||
class="page-content-editor wang-editor-body" style="height: calc(100vh - 80px);z-index: 1;"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -219,8 +219,8 @@ const addMarkdownImage = (pos, file) => {
|
||||
<style lang="scss">
|
||||
.fake-header {
|
||||
color: #333;
|
||||
height: 60px !important;
|
||||
line-height: 60px !important;
|
||||
height: 50px !important;
|
||||
line-height: 50px !important;
|
||||
|
||||
.fold-btn {
|
||||
font-size: 18px;
|
||||
|
||||
@@ -173,10 +173,19 @@ let pageContentRef = ref();
|
||||
const initQueryParam = (to) => {
|
||||
spaceId = parseInt(to.params.spaceId);
|
||||
pageId = parseInt(to.params.pageId);
|
||||
clearPageData();
|
||||
if (!!pageId) {
|
||||
loadPageDetail(pageId);
|
||||
}
|
||||
}
|
||||
const clearPageData = () => {
|
||||
wikiPage.value = {};
|
||||
wikiPageAuth.value = {};
|
||||
pageContent.value = '';
|
||||
pageContentShow.value = '';
|
||||
storePage.pageInfo = {};
|
||||
storePage.pageAuth = {};
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
<template>
|
||||
<div class="left-aside-box">
|
||||
<div class="left-aside-top-box">
|
||||
<el-select :model-value="storeSpace.chooseSpaceId" @change="spaceChangeEvents" filterable
|
||||
placeholder="选择空间" class="choice-space-select">
|
||||
<el-option-group label="" v-if="!props.readOnly">
|
||||
<el-option :key="-1" label="空间管理" :value="-1"></el-option>
|
||||
</el-option-group>
|
||||
<el-option-group label=""></el-option-group>
|
||||
<el-option v-for="item in storeSpace.spaceOptions" :key="item.value" :label="item.label" :value="item.value"></el-option>
|
||||
</el-select>
|
||||
<a-row type="flex" style="flex-flow: row nowrap;">
|
||||
<a-col flex="32px" style="margin-right: -1px;">
|
||||
<a-tooltip title="返回首页" placement="right" :mouseEnterDelay="0.5">
|
||||
<a-button @click="openHomePage" :icon="h(HomeOutlined)" class="home-page-btn"/>
|
||||
</a-tooltip>
|
||||
</a-col>
|
||||
<a-col flex="auto">
|
||||
<el-select v-model="storeSpace.chooseSpaceId" @change="spaceChangeEvents" filterable
|
||||
placeholder="选择空间" class="choice-space-select">
|
||||
<el-option v-for="item in storeSpace.spaceOptions" :key="item.value" :label="item.label" :value="item.value"></el-option>
|
||||
</el-select>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<el-autocomplete v-model="searchKeywords" v-if="!props.readOnly" :fetch-suggestions="doSearchByKeywords"
|
||||
@select="handleSearchKeywordsSelect" placeholder="在当前空间搜索"
|
||||
popper-class="search-autocomplete-popper" class="search-autocomplete">
|
||||
@@ -38,7 +43,7 @@
|
||||
</div>
|
||||
<div v-show="!spaceTreeIsClose" class="wiki-page-tree-box">
|
||||
<el-tree ref="wikiPageTreeRef" :current-node-key="props.nowPageId" :data="storePage.wikiPageList"
|
||||
:default-expanded-keys="wikiPageExpandedKeys" :expand-on-click-node="true" :class="explanClass"
|
||||
:default-expanded-keys="wikiPageExpandedKeys" :expand-on-click-node="true"
|
||||
:filter-node-method="filterPageNode" :props="defaultProps" :draggable="!props.readOnly"
|
||||
@node-click="handleNodeClick" @node-drop="handlePageDrop" node-key="id" highlight-current
|
||||
style="background-color: #fafafa;">
|
||||
@@ -112,7 +117,7 @@ import {
|
||||
EditTwo as IconParkEditTwo,
|
||||
PageTemplate as IconParkPageTemplate,
|
||||
} from '@icon-park/vue-next'
|
||||
import { EllipsisOutlined } from '@ant-design/icons-vue';
|
||||
import { EllipsisOutlined, HomeOutlined } 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';
|
||||
@@ -122,8 +127,6 @@ import AddMenu from "./AddMenu.vue";
|
||||
import IconDocument from "@/components/base/IconDocument.vue";
|
||||
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";
|
||||
import MessagePrompt from "@/components/single/MessagePrompt";
|
||||
|
||||
let route = useRoute();
|
||||
@@ -132,11 +135,7 @@ let storePage = useStorePageData();
|
||||
let storeDisplay = useStoreDisplay();
|
||||
let storeSpace = useStoreSpaceData();
|
||||
|
||||
let emit = defineEmits(['spaceChangeEvents', 'setNowPageId']);
|
||||
let searchKeywords = ref('');
|
||||
let descriptorForTree = ref("点击收起目录");
|
||||
let explan = ref(false);
|
||||
let explanClass = ref("el-tree");
|
||||
let wikiPageExpandedKeys = ref([]);
|
||||
let defaultProps = ref({children: 'children', label: 'name',});
|
||||
let wikiPageTreeRef = ref();
|
||||
@@ -153,8 +152,20 @@ onMounted(() => {
|
||||
watch(() => storePage.eventPageListUpdate, () => {
|
||||
loadSpaceList();
|
||||
});
|
||||
watch(() => storeSpace.spaceInfo, () => {
|
||||
doGetPageList();
|
||||
});
|
||||
let openHomePage = (event) => {
|
||||
if (event.ctrlKey) {
|
||||
let routeUrl = router.resolve({path: `/wiki/space`});
|
||||
window.open(routeUrl.href, '_blank');
|
||||
} else {
|
||||
router.push({path: '/wiki/space'});
|
||||
}
|
||||
}
|
||||
let nowSpaceShow = ref({});
|
||||
const loadSpaceList = (spaceId) => {
|
||||
const loadSpaceList = () => {
|
||||
let spaceId = parseInt(route.params.spaceId);
|
||||
pageApi.spaceList({}).then((json) => {
|
||||
storeSpace.spaceList = json.data || [];
|
||||
let spaceOptionsNew = [];
|
||||
@@ -172,14 +183,6 @@ const loadSpaceList = (spaceId) => {
|
||||
storeSpace.chooseSpaceId = nowSpaceId;
|
||||
storePage.choosePageId = 0;
|
||||
doGetPageList();
|
||||
// TODO 在首页时跳转
|
||||
try {
|
||||
if (route.path === '/home') {
|
||||
router.push({path: '/home', query: {spaceId: nowSpaceId}});
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -188,7 +191,7 @@ const changeNodeOptionStatus = (param) => {
|
||||
optionPageId.value = param.id;
|
||||
}
|
||||
const assisSetCurrentKey = () => {
|
||||
emit('setNowPageId', route.query.pageId, props.readOnly);
|
||||
// emit('setNowPageId', route.query.pageId, props.readOnly);
|
||||
if (props.nowPageId) {
|
||||
wikiPageTreeRef.value.setCurrentKey(nowPageId.value);
|
||||
}
|
||||
@@ -257,7 +260,12 @@ const deleteWikiPage = (data) => {
|
||||
});
|
||||
}
|
||||
const spaceChangeEvents = (data) => {
|
||||
emit('spaceChangeEvents', data, props.readOnly);
|
||||
let nowSpaceShowTemp = storeSpace.spaceList.find((item) => item.id === data);
|
||||
nowSpaceShow.value = nowSpaceShowTemp;
|
||||
storeSpace.spaceInfo = nowSpaceShowTemp;
|
||||
storeSpace.chooseSpaceId = data;
|
||||
storePage.choosePageId = 0;
|
||||
router.push({path: `/view/${data}`});
|
||||
}
|
||||
const doRename = (node, data) => {
|
||||
pageApi.renamePage({"id": data.id, "name": data.name}).then((json) => {
|
||||
@@ -266,6 +274,8 @@ const doRename = (node, data) => {
|
||||
});
|
||||
}
|
||||
const doGetPageList = () => {
|
||||
storePage.pageList = [];
|
||||
storePage.favoritePageList = [];
|
||||
let param = {spaceId: storeSpace.chooseSpaceId};
|
||||
pageApi.pageList(param).then((json) => {
|
||||
storePage.wikiPageList = json.data || [];
|
||||
@@ -334,9 +344,22 @@ defineExpose({searchByKeywords});
|
||||
.left-aside-top-box {
|
||||
padding: 10px;
|
||||
|
||||
.home-page-btn {
|
||||
border-radius: 4px 0 0 4px;
|
||||
z-index: 0;
|
||||
|
||||
&:hover, &:focus, &:active {
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.choice-space-select {
|
||||
width: 100%;
|
||||
margin-bottom: 5px;
|
||||
|
||||
.el-input__wrapper {
|
||||
border-radius: 0 4px 4px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.search-autocomplete {
|
||||
|
||||
@@ -9,14 +9,8 @@ import {toRefs, ref, reactive, onMounted, onBeforeUnmount, watch, defineEmits, c
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: Number,
|
||||
max: {
|
||||
type: Number,
|
||||
default: 600
|
||||
},
|
||||
min: {
|
||||
type: Number,
|
||||
default: 300
|
||||
}
|
||||
max: {type: Number, default: 600},
|
||||
min: {type: Number, default: 200}
|
||||
});
|
||||
let emit = defineEmits(['update:modelValue', 'change']);
|
||||
|
||||
|
||||
@@ -124,7 +124,7 @@ defineExpose({setContent, getContent, getPreview});
|
||||
.wang-editor-box .wang-editor-content {
|
||||
padding: 20px 0;
|
||||
overflow: auto;
|
||||
height: calc(100vh - 140px);
|
||||
height: calc(100vh - 130px);
|
||||
}
|
||||
|
||||
.wang-editor-box .w-e-bar-item {
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
<div class="comment-input-box">
|
||||
<textarea rows="5" placeholder="发表评论" v-model="commentTextInput" :maxlength="500"></textarea>
|
||||
<div class="comment-btn-box">
|
||||
<el-button type="primary" size="small" @click="submitPageComment">发送</el-button>
|
||||
<a-button @click="submitPageComment">发送</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -56,7 +56,6 @@ let page = {
|
||||
// 评论相关
|
||||
let commentTextInput = ref('');
|
||||
let commentList = ref([]);
|
||||
let recommentInfo = ref({});
|
||||
|
||||
let route = useRoute();
|
||||
let router = useRouter();
|
||||
@@ -82,7 +81,6 @@ const loadCommentList = () => {
|
||||
if (!storePage.pageInfo || !storePage.pageInfo.id) {
|
||||
return;
|
||||
}
|
||||
cancelCommentUser();
|
||||
pageApi.pageCommentList({pageId: storePage.pageInfo.id}).then((json) => {
|
||||
let commentListRes = json.data || [];
|
||||
for (let i = 0; i < commentListRes.length; i++) {
|
||||
@@ -109,9 +107,6 @@ const deleteComment = (id) => {
|
||||
loadCommentList();
|
||||
});
|
||||
}
|
||||
const cancelCommentUser = () => {
|
||||
recommentInfo.value = {};
|
||||
}
|
||||
const submitPageComment = () => {
|
||||
if (commentTextInput.value.length <= 0) {
|
||||
ElMessage.error('请输入评论内容');
|
||||
@@ -120,7 +115,6 @@ const submitPageComment = () => {
|
||||
let param = {
|
||||
pageId: storePage.pageInfo.id,
|
||||
content: commentTextInput.value,
|
||||
parentId: recommentInfo.value.id,
|
||||
}
|
||||
pageApi.updatePageComment(param).then((json) => {
|
||||
let data = json.data;
|
||||
|
||||
125
zyplayer-doc-ui/wiki-ui/src/views/wiki/Layout.vue
Normal file
125
zyplayer-doc-ui/wiki-ui/src/views/wiki/Layout.vue
Normal file
@@ -0,0 +1,125 @@
|
||||
<template>
|
||||
<a-layout class="wiki-container">
|
||||
<a-layout-header theme="light" class="wiki-container-header">
|
||||
<HeaderView/>
|
||||
</a-layout-header>
|
||||
<a-layout>
|
||||
<a-layout-sider v-model:collapsed="storeUser.menuCollapsed" collapsible theme="light" class="left-layout-sider">
|
||||
<ConsoleMenu></ConsoleMenu>
|
||||
<template #trigger>
|
||||
<div class="bottom-toggle-btn">
|
||||
<a-button v-if="storeUser.menuCollapsed" :icon="h(DoubleRightOutlined)" type="text" size="large" block></a-button>
|
||||
<a-button v-else :icon="h(DoubleLeftOutlined)" type="text" size="large" block></a-button>
|
||||
</div>
|
||||
</template>
|
||||
</a-layout-sider>
|
||||
<a-layout>
|
||||
<div class="right-content-box">
|
||||
<router-view></router-view>
|
||||
</div>
|
||||
</a-layout>
|
||||
</a-layout>
|
||||
</a-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { message } from 'ant-design-vue';
|
||||
import { DoubleLeftOutlined, DoubleRightOutlined } from '@ant-design/icons-vue';
|
||||
import {toRefs, ref, reactive, onMounted, watch, h, defineEmits, computed} from 'vue';
|
||||
import {onBeforeRouteUpdate, onBeforeRouteLeave, useRouter, useRoute} from "vue-router";
|
||||
import HeaderView from "./aside/HeaderView.vue";
|
||||
import ConsoleMenu from "./aside/ConsoleMenu.vue";
|
||||
import pageApi from "@/assets/api/page";
|
||||
import {useStoreUserData} from "@/store/userData";
|
||||
import {useStoreSpaceData} from "@/store/spaceData";
|
||||
import userApi from "@/assets/api/user";
|
||||
|
||||
let route = useRoute();
|
||||
let router = useRouter();
|
||||
let storeUser = useStoreUserData();
|
||||
let storeSpace = useStoreSpaceData();
|
||||
onMounted(() => {
|
||||
getSelfUserInfo();
|
||||
loadSpaceList();
|
||||
computeMenuKey(route);
|
||||
});
|
||||
onBeforeRouteUpdate((updateGuard) => {
|
||||
computeMenuKey(updateGuard);
|
||||
});
|
||||
// 空间创建或修改
|
||||
watch(() => storeSpace.eventSpaceUpdate, (newVal) => {
|
||||
loadSpaceList();
|
||||
});
|
||||
const computeMenuKey = (to) => {
|
||||
// 末级菜单key,暂时只有控制台需要
|
||||
if (to.matched && to.matched.length >= 2) {
|
||||
let keyIndex = to.matched.length - 1;
|
||||
let pathArr = to.matched[keyIndex].path.split('/');
|
||||
let menuKey = '', menuKeyArr = [];
|
||||
pathArr.filter(item => !!item && item.length > 0).forEach((item, index) => {
|
||||
if (index > 0) menuKey += '-';
|
||||
menuKey += item;
|
||||
menuKeyArr.push(menuKey);
|
||||
});
|
||||
storeUser.menuRouteKey = menuKeyArr;
|
||||
}
|
||||
}
|
||||
const getSelfUserInfo = () => {
|
||||
userApi.getSelfUserInfo().then((json) => {
|
||||
storeUser.userInfo = json.data || {};
|
||||
}).catch((e) => {
|
||||
});
|
||||
}
|
||||
const loadSpaceList = () => {
|
||||
pageApi.spaceList().then(json => {
|
||||
let resList = json.data || [];
|
||||
resList.forEach(item => {
|
||||
item.searchText = ((item.name || '') + ' ' + (item.spaceExplain || '')).toLowerCase();
|
||||
});
|
||||
storeSpace.spaceList = resList;
|
||||
storeSpace.wholeSpaceList = resList;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.wiki-container {
|
||||
height: 100%;
|
||||
|
||||
.bottom-toggle-btn {
|
||||
padding: 0 4px;
|
||||
border-right: 1px solid #eee;
|
||||
|
||||
.ant-btn {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.wiki-container-header {
|
||||
padding-inline: 0;
|
||||
height: 60px;
|
||||
line-height: 60px;
|
||||
}
|
||||
|
||||
.ant-layout {
|
||||
background: #fff;
|
||||
|
||||
.left-layout-sider {
|
||||
height: 100%;
|
||||
background: #fff;
|
||||
border-right: 1px solid #eee;
|
||||
transition: unset;
|
||||
|
||||
> .ant-layout-sider-children {
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.right-content-box {
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
background: #fff;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
32
zyplayer-doc-ui/wiki-ui/src/views/wiki/Wiki.vue
Normal file
32
zyplayer-doc-ui/wiki-ui/src/views/wiki/Wiki.vue
Normal file
@@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<div class="space-create-box">
|
||||
<CreateRow></CreateRow>
|
||||
</div>
|
||||
<div class="space-view-box">
|
||||
<Whole></Whole>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import CreateRow from './space/CreateRow.vue'
|
||||
import Whole from './space/Whole.vue'
|
||||
import {onMounted, watch} from 'vue';
|
||||
import {useRoute} from "vue-router";
|
||||
|
||||
let route = useRoute();
|
||||
onMounted(() => {
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.space-create-box {
|
||||
z-index: 101;
|
||||
position: relative;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.space-view-box {
|
||||
height: calc(100vh - 130px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
</style>
|
||||
78
zyplayer-doc-ui/wiki-ui/src/views/wiki/aside/ConsoleMenu.vue
Normal file
78
zyplayer-doc-ui/wiki-ui/src/views/wiki/aside/ConsoleMenu.vue
Normal file
@@ -0,0 +1,78 @@
|
||||
<template>
|
||||
<a-menu v-model:selectedKeys="storeUser.menuRouteKey" mode="inline" class="menu-view-box" style="border-right: 0;" >
|
||||
<a-menu-item key="wiki-space" :icon="h(IconParkNotebook)">
|
||||
<router-link to="/wiki/space">知识库</router-link>
|
||||
</a-menu-item>
|
||||
<a-sub-menu vxxxx-if="storeUser.userInfo.isManager" title="系统管理" key="manage" :icon="h(IconParkSettingConfig)">
|
||||
<a-menu-item key="user-userList" :icon="h(IconParkUser)">
|
||||
<router-link to="/user/userList">用户管理</router-link>
|
||||
</a-menu-item>
|
||||
<a-menu-item key="user-department" :icon="h(IconParkPeoples)">
|
||||
<router-link to="/user/department">部门管理</router-link>
|
||||
</a-menu-item>
|
||||
<a-menu-item key="system-setting" :icon="h(IconParkSettingTwo)">
|
||||
<router-link to="/system/setting">系统配置</router-link>
|
||||
</a-menu-item>
|
||||
</a-sub-menu>
|
||||
</a-menu>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {
|
||||
Peoples as IconParkPeoples,
|
||||
User as IconParkUser,
|
||||
SettingConfig as IconParkSettingConfig,
|
||||
Notebook as IconParkNotebook,
|
||||
SettingTwo as IconParkSettingTwo,
|
||||
} from '@icon-park/vue-next'
|
||||
import {toRefs, ref, reactive, onMounted, onBeforeUnmount, watch, h, defineEmits, computed} from 'vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import {onBeforeRouteUpdate, useRoute, useRouter} from "vue-router";
|
||||
import {useStoreUserData} from "@/store/userData";
|
||||
|
||||
let storeUser = useStoreUserData();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
onMounted(() => {
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.menu-badge .el-badge__content {
|
||||
margin-top: 14px;
|
||||
right: 0 !important;
|
||||
}
|
||||
|
||||
.menu-view-box {
|
||||
.i-icon {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
// 左侧菜单收缩时的样式,上面图标、下面文字,样式强制覆盖
|
||||
.left-layout-sider.ant-layout-sider-collapsed {
|
||||
.ant-menu {
|
||||
> .ant-menu-item, > .ant-menu-submenu .ant-menu-submenu-title {
|
||||
height: 60px;
|
||||
line-height: 40px;
|
||||
padding-inline: unset;
|
||||
|
||||
.ant-menu-item-icon {
|
||||
display: block;
|
||||
line-height: 20px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.ant-menu-title-content {
|
||||
display: block;
|
||||
opacity: 1;
|
||||
margin-inline-start: 8px;
|
||||
line-height: 24px;
|
||||
width: 56px;
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
60
zyplayer-doc-ui/wiki-ui/src/views/wiki/aside/HeaderView.vue
Normal file
60
zyplayer-doc-ui/wiki-ui/src/views/wiki/aside/HeaderView.vue
Normal file
@@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<div class="wiki-header-view">
|
||||
<a-row>
|
||||
<a-col :span="18">
|
||||
<span class="logo-box">
|
||||
<span class="system-name">文档管理系统</span>
|
||||
<el-divider direction="vertical" style="margin: 0 10px;"/>
|
||||
<span class="company-name">开源版</span>
|
||||
</span>
|
||||
</a-col>
|
||||
<a-col :span="6" style="text-align: right;">
|
||||
<UserHead/>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {toRefs, ref, reactive, onMounted, watch, defineEmits, computed} from 'vue';
|
||||
import {useRouter, useRoute} from "vue-router";
|
||||
import { message } from 'ant-design-vue';
|
||||
import UserHead from './UserHead.vue'
|
||||
|
||||
let router = useRouter();
|
||||
onMounted(() => {
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.wiki-header-view {
|
||||
padding: 0 20px;
|
||||
height: 60px;
|
||||
line-height: 60px;
|
||||
background: #2876d7;
|
||||
|
||||
.logo-box {
|
||||
.image {
|
||||
height: 41px;
|
||||
vertical-align: middle;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.system-name {
|
||||
font-size: 21px;
|
||||
color: #fff;
|
||||
vertical-align: middle;
|
||||
margin-top: -2px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.company-name {
|
||||
font-size: 16px;
|
||||
color: #fff;
|
||||
vertical-align: middle;
|
||||
margin-top: -2px;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
38
zyplayer-doc-ui/wiki-ui/src/views/wiki/aside/UserHead.vue
Normal file
38
zyplayer-doc-ui/wiki-ui/src/views/wiki/aside/UserHead.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<a-dropdown trigger="click" placement="bottomRight" arrow overlayClassName="header-action-user-dropdown">
|
||||
<a-button :icon="h(UserOutlined)" size="large" type="text" style="color: #fff;"></a-button>
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item @click="showAbout">关于</a-menu-item>
|
||||
<a-menu-divider />
|
||||
<a-menu-item @click="userSignOut" danger>退出登录</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
<AboutDialog v-model:visible="aboutDialogVisible"/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {UserOutlined} from '@ant-design/icons-vue';
|
||||
import {toRefs, ref, reactive, onMounted, watch, defineEmits, h, computed} from 'vue';
|
||||
import {useRouter, useRoute} from "vue-router";
|
||||
import userApi from "@/assets/api/user";
|
||||
import AboutDialog from "@/views/common/AboutDialog.vue";
|
||||
|
||||
let router = useRouter();
|
||||
const userSignOut = () => {
|
||||
userApi.userLogout().then(() => {
|
||||
location.reload();
|
||||
});
|
||||
}
|
||||
let aboutDialogVisible = ref(false);
|
||||
const showAbout = () => {
|
||||
aboutDialogVisible.value = true;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.header-action-user-dropdown {
|
||||
width: 120px;
|
||||
}
|
||||
</style>
|
||||
35
zyplayer-doc-ui/wiki-ui/src/views/wiki/space/CreateRow.vue
Normal file
35
zyplayer-doc-ui/wiki-ui/src/views/wiki/space/CreateRow.vue
Normal file
@@ -0,0 +1,35 @@
|
||||
<template>
|
||||
<div class="wiki-create-row">
|
||||
<a-row>
|
||||
<a-col :span="12"></a-col>
|
||||
<a-col :span="12" style="text-align: right;">
|
||||
<a-button @click="showCreateSpace" :icon="h(PlusOutlined)" type="primary">新建空间</a-button>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
<CreateSpace ref="createSpaceRef"></CreateSpace>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {SwapOutlined, PlusOutlined} from '@ant-design/icons-vue';
|
||||
import {toRefs, ref, reactive, onMounted, watch, defineEmits, h, computed} from 'vue';
|
||||
import CreateSpace from './CreateSpace.vue'
|
||||
import {useStoreDisplay} from "@/store/wikiDisplay";
|
||||
import {useStoreUserData} from "@/store/userData";
|
||||
|
||||
let storeUser = useStoreUserData();
|
||||
let storeDisplay = useStoreDisplay();
|
||||
onMounted(() => {
|
||||
});
|
||||
let createSpaceRef = ref();
|
||||
const showCreateSpace = () => {
|
||||
createSpaceRef.value.show();
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.wiki-create-row {
|
||||
margin: 20px 0 10px 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
106
zyplayer-doc-ui/wiki-ui/src/views/wiki/space/CreateSpace.vue
Normal file
106
zyplayer-doc-ui/wiki-ui/src/views/wiki/space/CreateSpace.vue
Normal file
@@ -0,0 +1,106 @@
|
||||
<template>
|
||||
<!--新建空间弹窗-->
|
||||
<a-modal :title="newSpaceForm.id?'编辑空间':'新建空间'" v-model:open="newSpaceDialogVisible" width="600px" :maskClosable="false" class="create-space-vue">
|
||||
<a-form :model="newSpaceForm" :rules="newSpaceFormRules" ref="newSpaceFormRef" @submit.prevent :label-col="{span: 5}" :wrapper-col="{span: 19}">
|
||||
<a-form-item label="空间名" name="name">
|
||||
<a-input v-model:value="newSpaceForm.name" placeholder="请输入空间名" :maxlength="25"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="空间描述" name="spaceExplain">
|
||||
<a-textarea v-model:value="newSpaceForm.spaceExplain" placeholder="请输入空间简介" :maxlength="250" :autoSize="{ minRows: 6}" type="textarea" resize="none"/>
|
||||
</a-form-item>
|
||||
<a-form-item label="空间类型">
|
||||
<a-radio-group v-model:value="newSpaceForm.type">
|
||||
<a-radio :value="2">仅空间成员可见</a-radio>
|
||||
<a-radio :value="1">所有登录用户可见</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item label="空间唯一编码" name="uuid">
|
||||
<a-input v-model:value="newSpaceForm.uuid" placeholder="请输入空间唯一编码" :disabled="!!newSpaceForm.id" :maxlength="30"/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<template #footer>
|
||||
<a-button @click="onNewSpaceCancel">取消</a-button>
|
||||
<a-button type="primary" v-if="newSpaceForm.id" @click="onNewSpaceSubmit()" :loading="newSpaceSubmitLoading">保存修改</a-button>
|
||||
<a-button type="primary" v-else @click="onNewSpaceSubmit()" :loading="newSpaceSubmitLoading">立即创建</a-button>
|
||||
</template>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import pageApi from '@/assets/api/page'
|
||||
import { message } from 'ant-design-vue';
|
||||
import {toRefs, ref, defineExpose, reactive, onMounted, watch, defineEmits, computed} from 'vue';
|
||||
import { useStoreSpaceData } from '@/store/spaceData.js'
|
||||
|
||||
let storeSpaceData = useStoreSpaceData();
|
||||
let newSpaceDialogVisible = ref(false);
|
||||
let newSpaceForm = ref({id: '', name: '', spaceExplain: '', treeLazyLoad: 0, openDoc: 0, uuid: '', type: 1});
|
||||
let newSpaceFormRules = computed(() => ({
|
||||
name: [
|
||||
{required: true, message: '请输入空间名', trigger: 'blur'},
|
||||
{max: 25, message: '最多25个字符', trigger: 'blur'},
|
||||
],
|
||||
uuid: [{
|
||||
required: false,
|
||||
trigger: 'blur',
|
||||
validator: (rule, value, callback) => {
|
||||
if (!value || /^[a-zA-Z0-9-_]+$/.test(value)) {
|
||||
callback();
|
||||
}
|
||||
callback(new Error('空间编码只能由大小写字母、数字、中横线和下划线组成'));
|
||||
}
|
||||
}],
|
||||
}));
|
||||
const show = (spaceId) => {
|
||||
newSpaceForm.value = {id: '', name: '', spaceExplain: '', treeLazyLoad: 0, openDoc: 0, uuid: '', type: 2};
|
||||
let editSpaceId = spaceId || '';
|
||||
if (!!editSpaceId) {
|
||||
pageApi.spaceDetail({id: editSpaceId}).then(json => {
|
||||
newSpaceForm.value = json.data || {};
|
||||
newSpaceForm.value.groupId = newSpaceForm.value.groupId || '';
|
||||
});
|
||||
}
|
||||
newSpaceDialogVisible.value = true;
|
||||
}
|
||||
defineExpose({show});
|
||||
let newSpaceFormRef = ref();
|
||||
let newSpaceSubmitLoading = ref(false);
|
||||
const onNewSpaceSubmit = () => {
|
||||
newSpaceFormRef.value.validate().then(() => {
|
||||
let param = {
|
||||
id: newSpaceForm.value.id,
|
||||
name: newSpaceForm.value.name,
|
||||
type: newSpaceForm.value.type,
|
||||
uuid: newSpaceForm.value.uuid,
|
||||
openDoc: newSpaceForm.value.openDoc,
|
||||
groupId: newSpaceForm.value.groupId,
|
||||
spaceExplain: newSpaceForm.value.spaceExplain,
|
||||
treeLazyLoad: newSpaceForm.value.treeLazyLoad,
|
||||
};
|
||||
newSpaceSubmitLoading.value = true;
|
||||
pageApi.updateSpace(param).then(json => {
|
||||
message.success(newSpaceForm.value.id ? '修改成功' : '创建成功');
|
||||
newSpaceSubmitLoading.value = false;
|
||||
newSpaceDialogVisible.value = false;
|
||||
storeSpaceData.eventSpaceUpdate = !storeSpaceData.eventSpaceUpdate;
|
||||
}).catch(() => {
|
||||
newSpaceSubmitLoading.value = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
const onNewSpaceCancel = () => {
|
||||
newSpaceDialogVisible.value = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.create-space-vue {
|
||||
.warning-icon {
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
margin-right: 5px;
|
||||
margin-top: 1px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
177
zyplayer-doc-ui/wiki-ui/src/views/wiki/space/SpaceCard.vue
Normal file
177
zyplayer-doc-ui/wiki-ui/src/views/wiki/space/SpaceCard.vue
Normal file
@@ -0,0 +1,177 @@
|
||||
<template>
|
||||
<div @click="openSpace(space)" class="wiki-space-card can-click black-bg">
|
||||
<div class="top-badge external" v-if="space.openDoc === 1">互联网公开</div>
|
||||
<div class="top-badge open" v-else-if="space.type === 1">企业公开</div>
|
||||
<div class="img-box">
|
||||
<img src="@/assets/img/space-bg-1.png"/>
|
||||
</div>
|
||||
<div class="title ellipsis-multi" :title="space.name">{{ space.name }}</div>
|
||||
<div class="desc ellipsis-multi" :title="space.spaceExplain">{{ space.spaceExplain }}</div>
|
||||
<div v-if="space.isManager" @click.stop="openSpaceSetting(space)" class="open-space-box">
|
||||
<el-icon style="vertical-align: middle;"><ElIconSetting/></el-icon> 空间设置
|
||||
</div>
|
||||
<div v-else @click.stop="openSpace(space)" class="open-space-box">
|
||||
进入空间 <el-icon style="vertical-align: middle;"><ElIconArrowRight/></el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {Setting as ElIconSetting, ArrowRight as ElIconArrowRight} from '@element-plus/icons-vue'
|
||||
import {toRefs, ref, reactive, onMounted, watch, defineProps, defineEmits, h, computed} from 'vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import {useRouter, useRoute} from "vue-router";
|
||||
import pageApi from "@/assets/api/page";
|
||||
import {useStorePageData} from "@/store/pageData";
|
||||
|
||||
const router = useRouter();
|
||||
let storePage = useStorePageData();
|
||||
|
||||
const props = defineProps({
|
||||
space: {type: Object},
|
||||
});
|
||||
const openSpace = (row) => {
|
||||
storePage.currentSpace = {};
|
||||
router.push({path: `/view/${row.id}`});
|
||||
}
|
||||
const openSpaceSetting = (row) => {
|
||||
storePage.currentSpace = {};
|
||||
router.push({path: `/view/setting/${row.id}`});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.wiki-space-card {
|
||||
position: relative;
|
||||
width: 140px;
|
||||
height: 200px;
|
||||
margin: 0 20px 20px 0;
|
||||
cursor: pointer;
|
||||
padding: 16px;
|
||||
flex-shrink: 0;
|
||||
|
||||
.top-badge {
|
||||
position: absolute;
|
||||
border-radius: 8px 0 8px 0;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
background: #1384e5;
|
||||
padding: 1px 10px;
|
||||
font-size: 12px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.top-badge.open {
|
||||
background: #67C23A;
|
||||
}
|
||||
|
||||
.top-badge.external {
|
||||
background: #409EFF;
|
||||
}
|
||||
|
||||
.img-box {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
-webkit-transition: all .3s ease-out;
|
||||
-o-transition: all .3s ease-out;
|
||||
transition: all .3s ease-out;
|
||||
-webkit-transform-origin: center center;
|
||||
-ms-transform-origin: center center;
|
||||
transform-origin: center center;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
img.hide-compute-img {
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
|
||||
.ellipsis-multi {
|
||||
display: -webkit-box;
|
||||
overflow: hidden;
|
||||
-o-text-overflow: ellipsis;
|
||||
text-overflow: ellipsis;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.title {
|
||||
position: relative;
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
margin-top: 12px;
|
||||
letter-spacing: normal;
|
||||
}
|
||||
|
||||
.desc {
|
||||
position: relative;
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
font-weight: 400;
|
||||
margin: 6px 0;
|
||||
letter-spacing: .5px;
|
||||
}
|
||||
|
||||
.open-space-box {
|
||||
display: none;
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
padding: 8px 0;
|
||||
background: #00000055;
|
||||
color: #fff;
|
||||
font-size: 13px;
|
||||
vertical-align: middle;
|
||||
transition: all .3s ease-out;
|
||||
border-radius: 0 0 8px 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.wiki-space-card.white-bg {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.wiki-space-card.black-bg {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.wiki-space-card.can-click:hover {
|
||||
cursor: pointer;
|
||||
box-shadow: var(--el-box-shadow-light);
|
||||
|
||||
.img-box:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
-webkit-box-shadow: 0 0 0 2px #0cabed;
|
||||
box-shadow: 0 0 0 2px #0cabed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.open-space-box {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
67
zyplayer-doc-ui/wiki-ui/src/views/wiki/space/Whole.vue
Normal file
67
zyplayer-doc-ui/wiki-ui/src/views/wiki/space/Whole.vue
Normal file
@@ -0,0 +1,67 @@
|
||||
<template>
|
||||
<div class="search-space-box">
|
||||
<a-input v-model:value="searchValue" @input="searchSpaceInput" class="search-space-input" placeholder="搜索空间" clearable style="width: 250px;">
|
||||
<template #prefix>
|
||||
<SearchOutlined style="color: #aaa;"/>
|
||||
</template>
|
||||
</a-input>
|
||||
</div>
|
||||
<div v-if="storeSpaceData.spaceList.length <= 0" class="wiki-whole-empty-box">
|
||||
<el-empty description="暂无空间"/>
|
||||
</div>
|
||||
<div v-else class="wiki-whole-box">
|
||||
<SpaceCard :space="item" v-for="item in storeSpaceData.spaceList"></SpaceCard>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {SearchOutlined, SettingOutlined} from '@ant-design/icons-vue';
|
||||
import {toRefs, ref, reactive, onMounted, watch, h, defineProps, defineEmits, computed} from 'vue';
|
||||
import SpaceCard from './SpaceCard.vue'
|
||||
import {useStoreSpaceData} from "@/store/spaceData";
|
||||
|
||||
let storeSpaceData = useStoreSpaceData();
|
||||
onMounted(() => {
|
||||
});
|
||||
let searchValue = ref('');
|
||||
const searchSpaceInput = () => {
|
||||
if (!searchValue.value) {
|
||||
storeSpaceData.spaceList = storeSpaceData.wholeSpaceList;
|
||||
} else {
|
||||
let searchText = searchValue.value.toLowerCase();
|
||||
storeSpaceData.spaceList = storeSpaceData.wholeSpaceList.filter(item => item.searchText.indexOf(searchText) >= 0);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.search-space-box {
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 20px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
text-align: right;
|
||||
|
||||
.search-space-input {
|
||||
max-width: 300px;
|
||||
}
|
||||
}
|
||||
|
||||
.wiki-whole-box {
|
||||
padding: 0 20px;
|
||||
display: flex;
|
||||
-webkit-box-flex: 1;
|
||||
-ms-flex: 1;
|
||||
flex: 1;
|
||||
-ms-flex-wrap: wrap;
|
||||
flex-wrap: wrap;
|
||||
-ms-flex-line-pack: start;
|
||||
align-content: flex-start;
|
||||
-webkit-box-orient: horizontal;
|
||||
-webkit-box-direction: normal;
|
||||
-ms-flex-direction: row;
|
||||
flex-direction: row;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user