fix : 修复实施图片

This commit is contained in:
2025-07-09 22:56:09 +08:00
parent 64e2ff0647
commit 973dc0f2e4
338 changed files with 62195 additions and 62193 deletions

View File

@@ -1,78 +1,78 @@
<template>
<div class="app-main-container">
<router-view v-slot="{ Component, route }">
<transition name="fade-transform" type="transition" appear mode="out-in">
<div>
<template v-if="route.meta.noCache">
<keep-alive>
<suspense>
<component :is="Component" :key="route.fullPath" />
<template #fallback>
<div>Loading...</div>
</template>
</suspense>
</keep-alive>
</template>
<template v-else>
<suspense>
<component :is="Component" :key="route.fullPath" />
<template #fallback>
<div>Loading...</div>
</template>
</suspense>
</template>
</div>
</transition>
</router-view>
</div>
</template>
<script setup>
</script>
<style lang="scss">
.app-main-container {
height:calc(100vh - 130px);
padding:0 15px;
max-height: calc(100vh - 96px);
overflow: auto;
background-color: #fff;
border-radius: 10px;
}
.app-main-container::-webkit-scrollbar {
width:6px;
height: 6px;
}
// 滚动条轨道
.app-main-container::-webkit-scrollbar-track {
background: rgb(239, 239, 239);
border-radius: 2px;
}
// 小滑块
.app-main-container::-webkit-scrollbar-thumb {
background: rgba(80, 81, 82, 0.29);
border-radius: 10px;
}
/* fade-transform */
.fade-transform-leave-active,
.fade-transform-enter-active {
transition: all 0.5s;
}
/* 可能为enter失效拆分为 enter-from和enter-to */
.fade-transform-enter-from {
opacity: 0;
transform: translateX(-30px);
}
.fade-transform-enter-to {
opacity: 1;
transform: translateX(0px);
}
.fade-transform-leave-to {
opacity: 0;
transform: translateX(30px);
}
</style>
<template>
<div class="app-main-container">
<router-view v-slot="{ Component, route }">
<transition name="fade-transform" type="transition" appear mode="out-in">
<div>
<template v-if="route.meta.noCache">
<keep-alive>
<suspense>
<component :is="Component" :key="route.fullPath" />
<template #fallback>
<div>Loading...</div>
</template>
</suspense>
</keep-alive>
</template>
<template v-else>
<suspense>
<component :is="Component" :key="route.fullPath" />
<template #fallback>
<div>Loading...</div>
</template>
</suspense>
</template>
</div>
</transition>
</router-view>
</div>
</template>
<script setup>
</script>
<style lang="scss">
.app-main-container {
height:calc(100vh - 130px);
padding:0 15px;
max-height: calc(100vh - 96px);
overflow: auto;
background-color: #fff;
border-radius: 10px;
}
.app-main-container::-webkit-scrollbar {
width:6px;
height: 6px;
}
// 滚动条轨道
.app-main-container::-webkit-scrollbar-track {
background: rgb(239, 239, 239);
border-radius: 2px;
}
// 小滑块
.app-main-container::-webkit-scrollbar-thumb {
background: rgba(80, 81, 82, 0.29);
border-radius: 10px;
}
/* fade-transform */
.fade-transform-leave-active,
.fade-transform-enter-active {
transition: all 0.5s;
}
/* 可能为enter失效拆分为 enter-from和enter-to */
.fade-transform-enter-from {
opacity: 0;
transform: translateX(-30px);
}
.fade-transform-enter-to {
opacity: 1;
transform: translateX(0px);
}
.fade-transform-leave-to {
opacity: 0;
transform: translateX(30px);
}
</style>

View File

@@ -1,50 +1,50 @@
<template>
<el-container>
<el-aside
:class="siderbarStore.isCollapse ? 'collapse' : 'expand'"
>
<SiderBar></SiderBar>
</el-aside>
<el-main :class="siderbarStore.isCollapse ? 'main-collapse' : ''">
<NavBar></NavBar>
<TagsView></TagsView>
<AppMain></AppMain>
</el-main>
</el-container>
</template>
<script setup>
import SiderBar from './siderbar/index.vue'
import NavBar from './navbar/index.vue'
import TagsView from './tagsview/index.vue'
import AppMain from './appmain/AppMain.vue';
import { useSiderBar } from '../stores/siderbar';
const siderbarStore = useSiderBar()
</script>
<style lang="scss" scoped>
.expand {
animation: Expand 0.15s ease forwards;
}
@keyframes Expand {
from {
width: 64px;
}
to {
width: 200px;
}
}
.collapse {
animation: Collapse 0.15s ease forwards;
}
@keyframes Collapse {
from {
width: 200px;
}
to {
width: 64px;
}
}
<template>
<el-container>
<el-aside
:class="siderbarStore.isCollapse ? 'collapse' : 'expand'"
>
<SiderBar></SiderBar>
</el-aside>
<el-main :class="siderbarStore.isCollapse ? 'main-collapse' : ''">
<NavBar></NavBar>
<TagsView></TagsView>
<AppMain></AppMain>
</el-main>
</el-container>
</template>
<script setup>
import SiderBar from './siderbar/index.vue'
import NavBar from './navbar/index.vue'
import TagsView from './tagsview/index.vue'
import AppMain from './appmain/AppMain.vue';
import { useSiderBar } from '../stores/siderbar';
const siderbarStore = useSiderBar()
</script>
<style lang="scss" scoped>
.expand {
animation: Expand 0.15s ease forwards;
}
@keyframes Expand {
from {
width: 64px;
}
to {
width: 200px;
}
}
.collapse {
animation: Collapse 0.15s ease forwards;
}
@keyframes Collapse {
from {
width: 200px;
}
to {
width: 64px;
}
}
</style>

View File

@@ -1,273 +1,273 @@
<template>
<div>
<!-- <el-popover-->
<!-- :width="300"-->
<!-- trigger="click"-->
<!-- placement="bottom"-->
<!-- popper-style="box-shadow: rgb(14 18 22 / 35%) 0px 10px 38px -10px, rgb(14 18 22 / 20%) 0px 10px 20px -15px; padding: 10px;"-->
<!-- >-->
<!-- <template #reference>-->
<!-- <el-badge :hidden="total===0" :value="total" class="item">-->
<!-- <el-icon size="22px" style="cursor: pointer">-->
<!-- <Bell/>-->
<!-- </el-icon>-->
<!-- </el-badge>-->
<!-- </template>-->
<!-- <template #default>-->
<!-- <div v-if="total===0" style="height: 100px;display: flex;align-items: center;justify-content: center">-->
<!-- 暂无数据~-->
<!-- </div>-->
<!-- <ul v-else>-->
<!-- <li v-for="(notice,index) in noticeList" :key="index">-->
<!-- <span @click="handleToNotifyDetail(notice,index)">{{ notice.noticeTitle }}</span>-->
<!-- <span v-if="notice.state==='0'" @click="handleRead(notice)">已读</span>-->
<!-- </li>-->
<!-- </ul>-->
<!-- <div class="notify-btn">-->
<!-- <el-button type="primary" @click="handlePrevious" :disabled="pageInfo.pageNum===1" link>上一页</el-button>-->
<!-- <span @click="handleMoreRead">本页已读</span>-->
<!-- <el-button type="primary" @click="handleNext" :disabled="pageInfo.pageNum*pageInfo.pageSize>total" link>下一页-->
<!-- </el-button>-->
<!-- </div>-->
<!-- </template>-->
<!-- </el-popover>-->
<el-dialog width="1200px" title="通知公告详情" v-model="visible" @close="visible=false">
<el-form :model="viewForm" label-width="100px">
<el-row>
<el-col :span="24" class="title-block">
<el-text class="title">{{ viewForm.noticeTitle }}</el-text>
</el-col>
<el-col :span="24">
<el-text v-if="viewForm.contentType === 'text'">{{ viewForm.noticeContent }}</el-text>
<span v-else v-html="viewForm.noticeContent"></span>
</el-col>
</el-row>
</el-form>
</el-dialog>
</div>
</template>
<script setup>
import {getNotifyList, getNotifyDetail, readAllNotify, readSingleNotify} from "@/api/notice/notify";
import {getToken} from '@/utils/auth'
import {ElMessage} from "element-plus";
import {useRouter} from "vue-router";
const router = useRouter()
const pageInfo = reactive({
pageNum: 1,
pageSize: 8
})
let send = {
type: "ping"
}
const viewForm = ref();
const visible = ref(false);
const showNotify = ref(false);
const total = ref();
const noticeList = ref();
//查看详情
const handleViewDetails = (noticeId) => {
getNotifyDetail(noticeId).then(res => {
visible.value = true
viewForm.value = res.data
})
}
// const setWsUrl=(url)=>{
// return (window.location.protocol === 'http:' ? "ws://" : "wss://")+window.location.host + import.meta.env.VITE_BASE_URL + url;
// }
// const initWebSocket = () => {
// try {
// //怎么区分http https /url(全局url) 封装url 只填个url
// const wsUrl=setWsUrl('/notice-ws/notice')
// const socket = new WebSocket(wsUrl)
// // 2. ws.send()给服务器发送信息
// //连接发生错误的回调方法
// socket.onerror = function () {
// console.log("ws连接发生错误");
// };
// //连接成功建立的回调方法
// socket.onopen = function () {
// let authInfo = {
// token: getToken(),
// type: "auth",
// cluster: "notice"
// }
// socket.send(JSON.stringify(authInfo))
// console.log("ws连接成功");
// }
// //接收到消息的回调方法
// socket.onmessage = function (event) {
// let data = JSON.parse(event.data)
// console.log('测试铃铛',data)
// if (data.type === 'notice') {
// noticeList.value.push(data.notice)
// total.value += 1
// } else if(!data.type&&data.cluster==="notice"){
// noticeList.value.push(data)
// total.value += 1
// }
// // console.log("服务器返回的信息: ", JSON.parse(event.data));
// }
// //连接关闭的回调方法
// socket.onclose = function () {
// // initWebSocket()
// console.log("ws连接关闭");
// }
// setInterval(() => {
// socket.send(JSON.stringify(send))
// }, 30000)
// } catch (e) {
// console.log(e)
// console.log("ws连接失败");
// }
// }
// initWebSocket()
const searchNotifyList = () => {
let params = {
cluster: "notice",
state: 0,
...pageInfo
}
getNotifyList(params).then(res => {
console.log("获取到个人公告", res)
if (res.data) {
noticeList.value = res.data.rows
total.value = res.data.total
}
// initWebSocket()
})
}
searchNotifyList()
//点击名字跳转到详情页
const handleToNotifyDetail = (notice, index) => {
noticeList.value.splice(index, 1)
total.value -= 1
viewForm.value = {
noticeTitle: '',
noticeContent: ''
}
// router.push({path: `/system/notice/inform/index/${notice.noticeId}`})
handleViewDetails(notice.noticeId)
}
//单个已读
const handleRead = (notice) => {
readSingleNotify(notice.noticeId).then(res => {
if (res.code === 1000) {
ElMessage.success(res.msg)
searchNotifyList()
} else {
ElMessage.error(res.msg)
}
})
}
//本页已读
const handleMoreRead = () => {
let notifyIds = []
noticeList.value.forEach(item => {
notifyIds.push(item.noticeId)
})
readAllNotify(notifyIds).then(res => {
if (res.code === 1000) {
ElMessage.success(res.msg)
searchNotifyList()
} else {
ElMessage.error(res.msg)
}
})
}
//上一页
const handlePrevious = () => {
if (pageInfo.pageNum !== 1) {
pageInfo.pageNum -= 1
searchNotifyList()
}
}
//下一页
const handleNext = () => {
if (pageInfo.pageNum * pageInfo.pageSize <= total.value) {
pageInfo.pageNum += 1
searchNotifyList()
}
}
// onMounted(() => {
//
// });
// 组件被销毁之前,清空 sock 对象
// onBeforeUnmount(() => {
// // 关闭连接
// // 销毁 websocket 实例对象
// // socket = null;
// });
// defineExpose({
// searchNotifyList
// })
</script>
<style lang="scss" scoped>
.title-block {
text-align: center;
padding-bottom: 30px;
.title {
font-size: 28px;
font-weight: bold;
}
}
ul::-webkit-scrollbar {
width: 6px;
}
// 滚动条轨道
ul::-webkit-scrollbar-track {
background: rgb(239, 239, 239);
border-radius: 2px;
}
// 小滑块
ul::-webkit-scrollbar-thumb {
background: rgba(80, 81, 82, 0.29);
border-radius: 10px;
}
ul {
height: 100px;
overflow-y: auto;
padding: 0 10px;
margin-bottom: 10px;
li {
height: 25px;
line-height: 25px;
overflow-y: auto;
display: flex;
justify-content: space-between;
span:first-child:hover {
color: #2a99ff;
cursor: pointer;
}
span:last-child {
color: #2a99ff;
cursor: pointer;
}
}
}
.notify-btn {
display: flex;
justify-content: space-between;
align-items: center;
span {
color: #2a99ff;
cursor: pointer;
}
}
</style>
<template>
<div>
<!-- <el-popover-->
<!-- :width="300"-->
<!-- trigger="click"-->
<!-- placement="bottom"-->
<!-- popper-style="box-shadow: rgb(14 18 22 / 35%) 0px 10px 38px -10px, rgb(14 18 22 / 20%) 0px 10px 20px -15px; padding: 10px;"-->
<!-- >-->
<!-- <template #reference>-->
<!-- <el-badge :hidden="total===0" :value="total" class="item">-->
<!-- <el-icon size="22px" style="cursor: pointer">-->
<!-- <Bell/>-->
<!-- </el-icon>-->
<!-- </el-badge>-->
<!-- </template>-->
<!-- <template #default>-->
<!-- <div v-if="total===0" style="height: 100px;display: flex;align-items: center;justify-content: center">-->
<!-- 暂无数据~-->
<!-- </div>-->
<!-- <ul v-else>-->
<!-- <li v-for="(notice,index) in noticeList" :key="index">-->
<!-- <span @click="handleToNotifyDetail(notice,index)">{{ notice.noticeTitle }}</span>-->
<!-- <span v-if="notice.state==='0'" @click="handleRead(notice)">已读</span>-->
<!-- </li>-->
<!-- </ul>-->
<!-- <div class="notify-btn">-->
<!-- <el-button type="primary" @click="handlePrevious" :disabled="pageInfo.pageNum===1" link>上一页</el-button>-->
<!-- <span @click="handleMoreRead">本页已读</span>-->
<!-- <el-button type="primary" @click="handleNext" :disabled="pageInfo.pageNum*pageInfo.pageSize>total" link>下一页-->
<!-- </el-button>-->
<!-- </div>-->
<!-- </template>-->
<!-- </el-popover>-->
<el-dialog width="1200px" title="通知公告详情" v-model="visible" @close="visible=false">
<el-form :model="viewForm" label-width="100px">
<el-row>
<el-col :span="24" class="title-block">
<el-text class="title">{{ viewForm.noticeTitle }}</el-text>
</el-col>
<el-col :span="24">
<el-text v-if="viewForm.contentType === 'text'">{{ viewForm.noticeContent }}</el-text>
<span v-else v-html="viewForm.noticeContent"></span>
</el-col>
</el-row>
</el-form>
</el-dialog>
</div>
</template>
<script setup>
import {getNotifyList, getNotifyDetail, readAllNotify, readSingleNotify} from "@/api/notice/notify";
import {getToken} from '@/utils/auth'
import {ElMessage} from "element-plus";
import {useRouter} from "vue-router";
const router = useRouter()
const pageInfo = reactive({
pageNum: 1,
pageSize: 8
})
let send = {
type: "ping"
}
const viewForm = ref();
const visible = ref(false);
const showNotify = ref(false);
const total = ref();
const noticeList = ref();
//查看详情
const handleViewDetails = (noticeId) => {
getNotifyDetail(noticeId).then(res => {
visible.value = true
viewForm.value = res.data
})
}
// const setWsUrl=(url)=>{
// return (window.location.protocol === 'http:' ? "ws://" : "wss://")+window.location.host + import.meta.env.VITE_BASE_URL + url;
// }
// const initWebSocket = () => {
// try {
// //怎么区分http https /url(全局url) 封装url 只填个url
// const wsUrl=setWsUrl('/notice-ws/notice')
// const socket = new WebSocket(wsUrl)
// // 2. ws.send()给服务器发送信息
// //连接发生错误的回调方法
// socket.onerror = function () {
// console.log("ws连接发生错误");
// };
// //连接成功建立的回调方法
// socket.onopen = function () {
// let authInfo = {
// token: getToken(),
// type: "auth",
// cluster: "notice"
// }
// socket.send(JSON.stringify(authInfo))
// console.log("ws连接成功");
// }
// //接收到消息的回调方法
// socket.onmessage = function (event) {
// let data = JSON.parse(event.data)
// console.log('测试铃铛',data)
// if (data.type === 'notice') {
// noticeList.value.push(data.notice)
// total.value += 1
// } else if(!data.type&&data.cluster==="notice"){
// noticeList.value.push(data)
// total.value += 1
// }
// // console.log("服务器返回的信息: ", JSON.parse(event.data));
// }
// //连接关闭的回调方法
// socket.onclose = function () {
// // initWebSocket()
// console.log("ws连接关闭");
// }
// setInterval(() => {
// socket.send(JSON.stringify(send))
// }, 30000)
// } catch (e) {
// console.log(e)
// console.log("ws连接失败");
// }
// }
// initWebSocket()
const searchNotifyList = () => {
let params = {
cluster: "notice",
state: 0,
...pageInfo
}
getNotifyList(params).then(res => {
console.log("获取到个人公告", res)
if (res.data) {
noticeList.value = res.data.rows
total.value = res.data.total
}
// initWebSocket()
})
}
searchNotifyList()
//点击名字跳转到详情页
const handleToNotifyDetail = (notice, index) => {
noticeList.value.splice(index, 1)
total.value -= 1
viewForm.value = {
noticeTitle: '',
noticeContent: ''
}
// router.push({path: `/system/notice/inform/index/${notice.noticeId}`})
handleViewDetails(notice.noticeId)
}
//单个已读
const handleRead = (notice) => {
readSingleNotify(notice.noticeId).then(res => {
if (res.code === 1000) {
ElMessage.success(res.msg)
searchNotifyList()
} else {
ElMessage.error(res.msg)
}
})
}
//本页已读
const handleMoreRead = () => {
let notifyIds = []
noticeList.value.forEach(item => {
notifyIds.push(item.noticeId)
})
readAllNotify(notifyIds).then(res => {
if (res.code === 1000) {
ElMessage.success(res.msg)
searchNotifyList()
} else {
ElMessage.error(res.msg)
}
})
}
//上一页
const handlePrevious = () => {
if (pageInfo.pageNum !== 1) {
pageInfo.pageNum -= 1
searchNotifyList()
}
}
//下一页
const handleNext = () => {
if (pageInfo.pageNum * pageInfo.pageSize <= total.value) {
pageInfo.pageNum += 1
searchNotifyList()
}
}
// onMounted(() => {
//
// });
// 组件被销毁之前,清空 sock 对象
// onBeforeUnmount(() => {
// // 关闭连接
// // 销毁 websocket 实例对象
// // socket = null;
// });
// defineExpose({
// searchNotifyList
// })
</script>
<style lang="scss" scoped>
.title-block {
text-align: center;
padding-bottom: 30px;
.title {
font-size: 28px;
font-weight: bold;
}
}
ul::-webkit-scrollbar {
width: 6px;
}
// 滚动条轨道
ul::-webkit-scrollbar-track {
background: rgb(239, 239, 239);
border-radius: 2px;
}
// 小滑块
ul::-webkit-scrollbar-thumb {
background: rgba(80, 81, 82, 0.29);
border-radius: 10px;
}
ul {
height: 100px;
overflow-y: auto;
padding: 0 10px;
margin-bottom: 10px;
li {
height: 25px;
line-height: 25px;
overflow-y: auto;
display: flex;
justify-content: space-between;
span:first-child:hover {
color: #2a99ff;
cursor: pointer;
}
span:last-child {
color: #2a99ff;
cursor: pointer;
}
}
}
.notify-btn {
display: flex;
justify-content: space-between;
align-items: center;
span {
color: #2a99ff;
cursor: pointer;
}
}
</style>

View File

@@ -1,55 +1,55 @@
<template>
<el-breadcrumb separator="/">
<el-breadcrumb-item v-for="(item, index) in breadcrumbList" :key="item.path">
<span v-if="item.meta.noRedirect || index === breadcrumbList.length-1" class="no-redirect font">
{{ item.meta.title }}
</span>
<router-link class="font" v-else :to="item.redirect || item.path">
{{ item.meta.title }}
</router-link>
</el-breadcrumb-item>
</el-breadcrumb>
</template>
<script setup>
import {useRoute, useRouter} from 'vue-router';
const route = useRoute()
const router = useRouter()
watch(route, () => {
getBreadcrumb()
})
//面包屑导航数据
const breadcrumbList = ref([])
//获取面包屑导航数据
const getBreadcrumb = () => {
let matched = route.matched.filter(item => item.meta && item.meta.title)
const first = matched[0]
if (!isDashboard(first)) {
matched = [{path: '/home', meta: {title: '首页'}}].concat(matched)
}
breadcrumbList.value.length = 0;
const reBreadcrumbList = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false)
breadcrumbList.value.push(...reBreadcrumbList)
}
const isDashboard = (meta) => {
const name = meta && meta.name
if (!name) {
return
}
return name.trim().toLocaleLowerCase() === 'Home'.toLocaleLowerCase()
}
getBreadcrumb()
</script>
<style scoped lang="scss">
.font {
// font-size: 18px;
}
</style>
<template>
<el-breadcrumb separator="/">
<el-breadcrumb-item v-for="(item, index) in breadcrumbList" :key="item.path">
<span v-if="item.meta.noRedirect || index === breadcrumbList.length-1" class="no-redirect font">
{{ item.meta.title }}
</span>
<router-link class="font" v-else :to="item.redirect || item.path">
{{ item.meta.title }}
</router-link>
</el-breadcrumb-item>
</el-breadcrumb>
</template>
<script setup>
import {useRoute, useRouter} from 'vue-router';
const route = useRoute()
const router = useRouter()
watch(route, () => {
getBreadcrumb()
})
//面包屑导航数据
const breadcrumbList = ref([])
//获取面包屑导航数据
const getBreadcrumb = () => {
let matched = route.matched.filter(item => item.meta && item.meta.title)
const first = matched[0]
if (!isDashboard(first)) {
matched = [{path: '/home', meta: {title: '首页'}}].concat(matched)
}
breadcrumbList.value.length = 0;
const reBreadcrumbList = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false)
breadcrumbList.value.push(...reBreadcrumbList)
}
const isDashboard = (meta) => {
const name = meta && meta.name
if (!name) {
return
}
return name.trim().toLocaleLowerCase() === 'Home'.toLocaleLowerCase()
}
getBreadcrumb()
</script>
<style scoped lang="scss">
.font {
// font-size: 18px;
}
</style>

View File

@@ -1,37 +1,37 @@
<template>
<div class="toggle" @click="toggleClick">
<component :is="siderbarStore.getSiderBarStatus() ? 'Fold' : 'Expand'" class="icon"></component>
</div>
<span class="sys-name">科技创新项目管理平台</span>
</template>
<script setup>
import {useSiderBar} from '@/stores/siderbar.js'
const siderbarStore = useSiderBar()
const toggleClick = () => {
siderbarStore.setSiderBarStatus()
}
</script>
<style lang="scss" scoped>
.sys-name {
padding: 0 10px;
font-weight: bold;
}
.toggle{
line-height: 65px;
border-top-left-radius: 10px;
border-bottom-left-radius: 10px;
padding: 0 15px;
cursor: pointer;
&:hover {
background-color: #79bbff;
}
}
.icon {
width: 20px;
height: 20px;
vertical-align: middle;
transform: translateY(-2px);
}
<template>
<div class="toggle" @click="toggleClick">
<component :is="siderbarStore.getSiderBarStatus() ? 'Fold' : 'Expand'" class="icon"></component>
</div>
<span class="sys-name">科技创新项目管理平台</span>
</template>
<script setup>
import {useSiderBar} from '@/stores/siderbar.js'
const siderbarStore = useSiderBar()
const toggleClick = () => {
siderbarStore.setSiderBarStatus()
}
</script>
<style lang="scss" scoped>
.sys-name {
padding: 0 10px;
font-weight: bold;
}
.toggle{
line-height: 65px;
border-top-left-radius: 10px;
border-bottom-left-radius: 10px;
padding: 0 15px;
cursor: pointer;
&:hover {
background-color: #79bbff;
}
}
.icon {
width: 20px;
height: 20px;
vertical-align: middle;
transform: translateY(-2px);
}
</style>

View File

@@ -1,289 +1,289 @@
<template>
<div class="navbar">
<Hamburger></Hamburger>
<Breadcrumb></Breadcrumb>
<div class="right-bar">
<!-- <bell-socket/>-->
<div class="user-box">
<div @click.stop="handleVisitedP">
<el-avatar>{{ userInfo.nickName }}</el-avatar>
<div>{{ userInfo.nickName }}
<el-icon style="margin-left: 5px">
<ArrowDownBold/>
</el-icon>
</div>
</div>
<div class="person" v-if="visitedP">
<ul>
<li>主次账号切换</li>
<li class="avatar-li" v-for="item in accountList" @click="accountChange(item.userId)">
<el-badge :value="item.taskCount" v-if="item.taskCount!==0">
<el-avatar>{{ item.nickName }}</el-avatar>
</el-badge>
<el-avatar v-else>{{ item.nickName }}</el-avatar>
<div class="right-li">
<div class="name-line">
<span v-if="item.accountType==='0'" class="zhu"></span>
<span class="nickName">{{ item.nickName }}</span>
<span :title="item.jobActivityDesc">{{ item.jobActivityDesc }}</span>
</div>
<div>
<span :title="item.companyName+'/'+item.departmentName">{{ item.companyName }}/{{
item.departmentName
}}</span>
</div>
</div>
<div>
<el-icon color="#3f89dc" size="20" v-if="item.current">
<SuccessFilled/>
</el-icon>
</div>
<!-- <li v-for="item in accountList" :label="item.userName" :value="item.userId"/>-->
</li>
<li @click="handleToAuth">
<el-icon color="gray" size="20" style="margin-right: 5px">
<UserFilled/>
</el-icon>
个人中心
</li>
<li @click="handleLogout">
<el-icon color="gray" size="20" style="margin-right: 5px">
<SwitchButton/>
</el-icon>
退出登录
</li>
</ul>
</div>
</div>
</div>
</div>
</template>
<script setup>
import {useRouter} from 'vue-router';
import Breadcrumb from './Breadcrumb.vue';
import Hamburger from './Hamburger.vue';
import {useAuthStore} from '@/stores/userstore.js'
import {usePermisstionStroe} from '@/stores/permisstion'
import {useTagsView} from '@/stores/tagsview';
import {getUserAccount} from "@/api/user/user";
import {switchAccount} from "@/api/login";
import {setToken} from "../../utils/auth";
import {ElNotification} from "element-plus";
const authStore = useAuthStore()
const permisstionStore = usePermisstionStroe()
const tagsViewStore = useTagsView()
const userInfo = ref({})
const visitedP = ref(false)
const accountList = ref([])
const route = useRoute()
const router = useRouter()
onMounted(() => {
setUserInfo()
document.addEventListener('click', nullBlockClick)
})
onBeforeUnmount(() => {
document.removeEventListener('click', nullBlockClick)
})
const setUserInfo = () => {
userInfo.value = authStore.userinfo
}
const nullBlockClick = () => {
visitedP.value = false
}
const handleVisitedP = () => {
getUserAccount().then(res => {
if (res.code !== 1000) {
ElNotification({
title: '提示',
message: res.msg,
type: 'error'
})
}
accountList.value = res.data
nextTick(() => {
visitedP.value = !visitedP.value
})
})
}
const accountChange = (userId) => {
switchAccount(userId).then(res => {
if (res.code === 1000) {
visitedP.value = !visitedP.value
authStore.userLogout()
setToken(res.data)
window.location.href = "/home"
}else{
ElNotification({
title: '提示',
message: res.msg,
type: 'error'
})
}
})
}
const handleToAuth = () => {
visitedP.value = !visitedP.value
router.push('/auth')
}
const handleLogout = () => {
visitedP.value = !visitedP.value
authStore.userLogout()
permisstionStore.removeMenu()
permisstionStore.setIsSuccessReq()
tagsViewStore.removeAllTagView()
router.push('/login')
}
</script>
<style lang="scss" scoped>
:deep(.el-avatar--circle) {
display: inline-block;
line-height: 40px;
margin-right: 14px;
background-color: #8a7243;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
:deep(.el-badge) {
padding: 0 2px;
}
:deep(.el-badge__content.is-fixed) {
position: absolute;
right: 26px;
top: 2px;
}
.navbar {
height: 65px;
padding: 0 15px 0 0;
display: flex;
justify-content: flex-start;
align-items: center;
background-color: #fff;
border-radius: 10px;
.right-bar {
margin-left: auto;
display: flex;
justify-content: flex-start;
align-items: center;
.user-box {
cursor: pointer;
margin-left: 10px;
position: relative;
> div:first-child {
display: flex;
align-items: center;
> div {
display: flex;
align-items: center;
}
img {
width: 40px;
height: 40px;
border-radius: 50%;
cursor: pointer;
border: 1px solid #418DFF;
}
}
.person {
font-size: 14px;
color: #666666;
position: absolute;
width: 280px;
right: 0;
z-index: 3000;
top: 54px;
padding: 5px 0;
border-radius: 4px;
background-color: #fff;
box-shadow: 2px 2px 2px 1px rgb(171, 167, 167);
.avatar-li {
display: flex;
height: 60px;
.right-li {
color: #909090;
display: flex;
flex-direction: column;
.name-line {
margin-bottom: 5px;
width: 184px;
-webkit-line-clamp: 1;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
.zhu {
display: inline-block;
width: 20px;
height: 20px;
line-height: 20px;
background-color: #fa0;
color: #fff;
text-align: center;
}
.nickName {
color: #4d7ad8;
}
> span {
margin-right: 5px;
}
}
> div:last-child {
width: 194px;
-webkit-line-clamp: 1;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
}
}
li {
padding: 0 6px;
height: 28px;
display: flex;
align-items: center;
text-align: left;
font-size: 14px;
cursor: pointer;
border-bottom: 1px solid #e6e6e6;
&:hover {
color: #666666 !important;
background-color: #eaeaea;
}
&:first-child:hover {
background-color: #fff;
}
&:last-child {
border-bottom: none;
}
}
}
}
}
}
</style>
<template>
<div class="navbar">
<Hamburger></Hamburger>
<Breadcrumb></Breadcrumb>
<div class="right-bar">
<!-- <bell-socket/>-->
<div class="user-box">
<div @click.stop="handleVisitedP">
<el-avatar>{{ userInfo.nickName }}</el-avatar>
<div>{{ userInfo.nickName }}
<el-icon style="margin-left: 5px">
<ArrowDownBold/>
</el-icon>
</div>
</div>
<div class="person" v-if="visitedP">
<ul>
<li>主次账号切换</li>
<li class="avatar-li" v-for="item in accountList" @click="accountChange(item.userId)">
<el-badge :value="item.taskCount" v-if="item.taskCount!==0">
<el-avatar>{{ item.nickName }}</el-avatar>
</el-badge>
<el-avatar v-else>{{ item.nickName }}</el-avatar>
<div class="right-li">
<div class="name-line">
<span v-if="item.accountType==='0'" class="zhu"></span>
<span class="nickName">{{ item.nickName }}</span>
<span :title="item.jobActivityDesc">{{ item.jobActivityDesc }}</span>
</div>
<div>
<span :title="item.companyName+'/'+item.departmentName">{{ item.companyName }}/{{
item.departmentName
}}</span>
</div>
</div>
<div>
<el-icon color="#3f89dc" size="20" v-if="item.current">
<SuccessFilled/>
</el-icon>
</div>
<!-- <li v-for="item in accountList" :label="item.userName" :value="item.userId"/>-->
</li>
<li @click="handleToAuth">
<el-icon color="gray" size="20" style="margin-right: 5px">
<UserFilled/>
</el-icon>
个人中心
</li>
<li @click="handleLogout">
<el-icon color="gray" size="20" style="margin-right: 5px">
<SwitchButton/>
</el-icon>
退出登录
</li>
</ul>
</div>
</div>
</div>
</div>
</template>
<script setup>
import {useRouter} from 'vue-router';
import Breadcrumb from './Breadcrumb.vue';
import Hamburger from './Hamburger.vue';
import {useAuthStore} from '@/stores/userstore.js'
import {usePermisstionStroe} from '@/stores/permisstion'
import {useTagsView} from '@/stores/tagsview';
import {getUserAccount} from "@/api/user/user";
import {switchAccount} from "@/api/login";
import {setToken} from "../../utils/auth";
import {ElNotification} from "element-plus";
const authStore = useAuthStore()
const permisstionStore = usePermisstionStroe()
const tagsViewStore = useTagsView()
const userInfo = ref({})
const visitedP = ref(false)
const accountList = ref([])
const route = useRoute()
const router = useRouter()
onMounted(() => {
setUserInfo()
document.addEventListener('click', nullBlockClick)
})
onBeforeUnmount(() => {
document.removeEventListener('click', nullBlockClick)
})
const setUserInfo = () => {
userInfo.value = authStore.userinfo
}
const nullBlockClick = () => {
visitedP.value = false
}
const handleVisitedP = () => {
getUserAccount().then(res => {
if (res.code !== 1000) {
ElNotification({
title: '提示',
message: res.msg,
type: 'error'
})
}
accountList.value = res.data
nextTick(() => {
visitedP.value = !visitedP.value
})
})
}
const accountChange = (userId) => {
switchAccount(userId).then(res => {
if (res.code === 1000) {
visitedP.value = !visitedP.value
authStore.userLogout()
setToken(res.data)
window.location.href = "/home"
}else{
ElNotification({
title: '提示',
message: res.msg,
type: 'error'
})
}
})
}
const handleToAuth = () => {
visitedP.value = !visitedP.value
router.push('/auth')
}
const handleLogout = () => {
visitedP.value = !visitedP.value
authStore.userLogout()
permisstionStore.removeMenu()
permisstionStore.setIsSuccessReq()
tagsViewStore.removeAllTagView()
router.push('/login')
}
</script>
<style lang="scss" scoped>
:deep(.el-avatar--circle) {
display: inline-block;
line-height: 40px;
margin-right: 14px;
background-color: #8a7243;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
:deep(.el-badge) {
padding: 0 2px;
}
:deep(.el-badge__content.is-fixed) {
position: absolute;
right: 26px;
top: 2px;
}
.navbar {
height: 65px;
padding: 0 15px 0 0;
display: flex;
justify-content: flex-start;
align-items: center;
background-color: #fff;
border-radius: 10px;
.right-bar {
margin-left: auto;
display: flex;
justify-content: flex-start;
align-items: center;
.user-box {
cursor: pointer;
margin-left: 10px;
position: relative;
> div:first-child {
display: flex;
align-items: center;
> div {
display: flex;
align-items: center;
}
img {
width: 40px;
height: 40px;
border-radius: 50%;
cursor: pointer;
border: 1px solid #418DFF;
}
}
.person {
font-size: 14px;
color: #666666;
position: absolute;
width: 280px;
right: 0;
z-index: 3000;
top: 54px;
padding: 5px 0;
border-radius: 4px;
background-color: #fff;
box-shadow: 2px 2px 2px 1px rgb(171, 167, 167);
.avatar-li {
display: flex;
height: 60px;
.right-li {
color: #909090;
display: flex;
flex-direction: column;
.name-line {
margin-bottom: 5px;
width: 184px;
-webkit-line-clamp: 1;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
.zhu {
display: inline-block;
width: 20px;
height: 20px;
line-height: 20px;
background-color: #fa0;
color: #fff;
text-align: center;
}
.nickName {
color: #4d7ad8;
}
> span {
margin-right: 5px;
}
}
> div:last-child {
width: 194px;
-webkit-line-clamp: 1;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
}
}
li {
padding: 0 6px;
height: 28px;
display: flex;
align-items: center;
text-align: left;
font-size: 14px;
cursor: pointer;
border-bottom: 1px solid #e6e6e6;
&:hover {
color: #666666 !important;
background-color: #eaeaea;
}
&:first-child:hover {
background-color: #fff;
}
&:last-child {
border-bottom: none;
}
}
}
}
}
}
</style>

View File

@@ -1,71 +1,71 @@
<template>
<!-- 有子菜单 -->
<template v-for="item in menuItem" :key="item.path">
<el-sub-menu v-if="checkMenuItem(item)" :index="item.path">
<template #title>
<svg-icon :name="item.icon"/>
<span>{{ item.title }}</span>
</template>
<Item :menu-item="item.children"></Item>
</el-sub-menu>
<el-menu-item v-else-if="!item.hidden&&!item.meta.isFrame" :index="handleGo(item.path)">
<template #title>
<div v-if="item.path==='/tool/swagger'">
<a class="port-link" :href="link" target="_blank">
<svg-icon :name="item.icon"/>
{{ item.title }}
</a>
</div>
<div v-else>
<el-icon>
<svg-icon :name="item.icon" class="menu-item-icon"/>
</el-icon>
<span>{{ item.title }}</span>
</div>
</template>
</el-menu-item>
</template>
</template>
<script setup>
import SvgIcon from '@/components/svgIcon/index.vue'
import Item from './MenuItem.vue'
const props = defineProps({
menuItem: {
type: Array,
required: true
}
})
const link = ref('http://gateway.feashow.cn/doc.html#/home')
const handleGo = (path) => {
if (path === "/tool/swagger") {
return ''
} else {
return path
}
}
const checkMenuItem = (item) => {
let children = item.children
let childState = false;
if (children){
for (let child of children) {
if (!child.hidden) {
childState = true
break
}
}
}
return item?.children?.length>0 && !item.hidden && childState
}
</script>
<!-- <style lang="scss" scoped>
.menu-item-icon {
width: 18px !important;
height: 18px !important;
margin-left: 0;
}
<template>
<!-- 有子菜单 -->
<template v-for="item in menuItem" :key="item.path">
<el-sub-menu v-if="checkMenuItem(item)" :index="item.path">
<template #title>
<svg-icon :name="item.icon"/>
<span>{{ item.title }}</span>
</template>
<Item :menu-item="item.children"></Item>
</el-sub-menu>
<el-menu-item v-else-if="!item.hidden&&!item.meta.isFrame" :index="handleGo(item.path)">
<template #title>
<div v-if="item.path==='/tool/swagger'">
<a class="port-link" :href="link" target="_blank">
<svg-icon :name="item.icon"/>
{{ item.title }}
</a>
</div>
<div v-else>
<el-icon>
<svg-icon :name="item.icon" class="menu-item-icon"/>
</el-icon>
<span>{{ item.title }}</span>
</div>
</template>
</el-menu-item>
</template>
</template>
<script setup>
import SvgIcon from '@/components/svgIcon/index.vue'
import Item from './MenuItem.vue'
const props = defineProps({
menuItem: {
type: Array,
required: true
}
})
const link = ref('http://gateway.feashow.cn/doc.html#/home')
const handleGo = (path) => {
if (path === "/tool/swagger") {
return ''
} else {
return path
}
}
const checkMenuItem = (item) => {
let children = item.children
let childState = false;
if (children){
for (let child of children) {
if (!child.hidden) {
childState = true
break
}
}
}
return item?.children?.length>0 && !item.hidden && childState
}
</script>
<!-- <style lang="scss" scoped>
.menu-item-icon {
width: 18px !important;
height: 18px !important;
margin-left: 0;
}
</style> -->

View File

@@ -1,54 +1,54 @@
<template>
<div class="logo" ref="logo">
<img src="../../assets/kylogo.png" alt="">
<!-- <span v-if="!siderbarStore.isCollapse">科技创新项目管理平台</span> -->
<span v-if="!siderbarStore.isCollapse"></span>
</div>
<el-scrollbar :height="`calc(100vh - ${logoHeight}px)`" style="background-color: #ffffff">
<el-menu
router
:default-active="activeMenu"
:unique-opened="true"
:collapse="siderbarStore.isCollapse"
active-text-color="#927648"
text-color="#575757"
style="border: none;"
:collapse-transition="false"
>
<MenuItem :menu-item="permisstionStore.menuList"></MenuItem>
</el-menu>
</el-scrollbar>
</template>
<script setup>
import MenuItem from './MenuItem.vue';
import {useRoute} from 'vue-router'
import {useSiderBar} from '@/stores/siderbar.js'
import {usePermisstionStroe} from '@/stores/permisstion.js'
import {nextTick} from 'vue';
const siderbarStore = useSiderBar()
const permisstionStore = usePermisstionStroe()
const route = useRoute()
const link = ref('')
const title = ref('')
const logo = ref(null)
const logoHeight = ref()
const getLogoH = () => {
logoHeight.value = logo.value.offsetHeight
}
const activeMenu = computed(() => {
const {path} = route
return path
})
onMounted(() => {
nextTick(() => {
getLogoH()
})
})
window.addEventListener('resize', () => {
getLogoH()
})
</script>
<template>
<div class="logo" ref="logo">
<img src="../../assets/kylogo.png" alt="">
<!-- <span v-if="!siderbarStore.isCollapse">科技创新项目管理平台</span> -->
<span v-if="!siderbarStore.isCollapse"></span>
</div>
<el-scrollbar :height="`calc(100vh - ${logoHeight}px)`" style="background-color: #ffffff">
<el-menu
router
:default-active="activeMenu"
:unique-opened="true"
:collapse="siderbarStore.isCollapse"
active-text-color="#927648"
text-color="#575757"
style="border: none;"
:collapse-transition="false"
>
<MenuItem :menu-item="permisstionStore.menuList"></MenuItem>
</el-menu>
</el-scrollbar>
</template>
<script setup>
import MenuItem from './MenuItem.vue';
import {useRoute} from 'vue-router'
import {useSiderBar} from '@/stores/siderbar.js'
import {usePermisstionStroe} from '@/stores/permisstion.js'
import {nextTick} from 'vue';
const siderbarStore = useSiderBar()
const permisstionStore = usePermisstionStroe()
const route = useRoute()
const link = ref('')
const title = ref('')
const logo = ref(null)
const logoHeight = ref()
const getLogoH = () => {
logoHeight.value = logo.value.offsetHeight
}
const activeMenu = computed(() => {
const {path} = route
return path
})
onMounted(() => {
nextTick(() => {
getLogoH()
})
})
window.addEventListener('resize', () => {
getLogoH()
})
</script>

View File

@@ -1,126 +1,126 @@
<template>
<div class="link-box">
<el-scrollbar noresize height="36" >
<!-- style="display: flex"-->
<div >
<router-link
v-for="item in tagsViewStore.visitedViews"
:key="item.path" :to="{ path: item.path, query: item.query }" class="tag"
:class="isActive(item) ? 'active' : ''"
@click.prevent
@contextmenu.prevent.native="openMenu(item, $event)">
<span>{{ item.meta.title }}</span>
<span @click.prevent="closeTagView(item.path)">
<svg-icon name="close" :class-name="'close-icon'"/>
</span>
</router-link>
</div>
</el-scrollbar>
<ul class="contextmenu" :style="{ 'left': left + 'px' }" v-if="visible">
<li class="el-dropdown-menu__item" @click="closeCurrentTagView">关闭当前</li>
<li class="el-dropdown-menu__item" @click="closeOtherTagView">关闭其他</li>
</ul>
</div>
</template>
<script setup>
import {useRoute} from 'vue-router';
import {useTagsView} from '@/stores/tagsview.js'
import SvgIcon from '@/components/svgIcon/index.vue'
const route = useRoute()
const tagsViewStore = useTagsView()
const visible = ref(false)
const left = ref()
const tagPath = ref()
watch(route, () => {
init()
})
onMounted(() => {
document.addEventListener('click', nullBlockClick)
})
onBeforeUnmount(() => {
document.removeEventListener('click', nullBlockClick)
})
const nullBlockClick = () => {
visible.value = false
}
const init = () => {
tagsViewStore.addVisitedViews(route)
}
const closeTagView = (path) => {
tagsViewStore.delVisitedViews(path)
}
const isActive = (tag) => {
return tag.path === route.path
}
const openMenu = (tag, e) => {
tagPath.value = tag
left.value = e.x - 230
visible.value = true
}
// 关闭当前
const closeCurrentTagView = () => {
tagsViewStore.delVisitedViews(tagPath.value.path)
visible.value = false
}
// 关闭其他
const closeOtherTagView = () => {
tagsViewStore.delOtherVisitedViews(tagPath.value)
visible.value = false
}
init()
</script>
<style lang="scss" scoped>
.link-box {
//display: flex;
position: relative;
padding: 12px 0;
line-height: 34px;
.tag {
padding: 6px;
border: 1px solid darkgray;
font-size: 13px;
margin-right: 8px;
border-radius: 6px;
background-color: #fff;
}
.active {
background-color: rgba(190,162,102,0.1);;
border: 1px solid #BEA266;
color: #333333;
> span:first-child {
color: #BEA266;
}
}
.contextmenu {
padding: 5px;
width: 88px;
//height: 82px;
position: absolute;
top: 47px;
z-index: 3000;
background: #fff;
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .3);
li {
margin: 0;
padding: 7px 10px;
cursor: pointer;
&:hover {
background: #eee;
color: #EDC49A;
}
}
}
}
</style>
<template>
<div class="link-box">
<el-scrollbar noresize height="36" >
<!-- style="display: flex"-->
<div >
<router-link
v-for="item in tagsViewStore.visitedViews"
:key="item.path" :to="{ path: item.path, query: item.query }" class="tag"
:class="isActive(item) ? 'active' : ''"
@click.prevent
@contextmenu.prevent.native="openMenu(item, $event)">
<span>{{ item.meta.title }}</span>
<span @click.prevent="closeTagView(item.path)">
<svg-icon name="close" :class-name="'close-icon'"/>
</span>
</router-link>
</div>
</el-scrollbar>
<ul class="contextmenu" :style="{ 'left': left + 'px' }" v-if="visible">
<li class="el-dropdown-menu__item" @click="closeCurrentTagView">关闭当前</li>
<li class="el-dropdown-menu__item" @click="closeOtherTagView">关闭其他</li>
</ul>
</div>
</template>
<script setup>
import {useRoute} from 'vue-router';
import {useTagsView} from '@/stores/tagsview.js'
import SvgIcon from '@/components/svgIcon/index.vue'
const route = useRoute()
const tagsViewStore = useTagsView()
const visible = ref(false)
const left = ref()
const tagPath = ref()
watch(route, () => {
init()
})
onMounted(() => {
document.addEventListener('click', nullBlockClick)
})
onBeforeUnmount(() => {
document.removeEventListener('click', nullBlockClick)
})
const nullBlockClick = () => {
visible.value = false
}
const init = () => {
tagsViewStore.addVisitedViews(route)
}
const closeTagView = (path) => {
tagsViewStore.delVisitedViews(path)
}
const isActive = (tag) => {
return tag.path === route.path
}
const openMenu = (tag, e) => {
tagPath.value = tag
left.value = e.x - 230
visible.value = true
}
// 关闭当前
const closeCurrentTagView = () => {
tagsViewStore.delVisitedViews(tagPath.value.path)
visible.value = false
}
// 关闭其他
const closeOtherTagView = () => {
tagsViewStore.delOtherVisitedViews(tagPath.value)
visible.value = false
}
init()
</script>
<style lang="scss" scoped>
.link-box {
//display: flex;
position: relative;
padding: 12px 0;
line-height: 34px;
.tag {
padding: 6px;
border: 1px solid darkgray;
font-size: 13px;
margin-right: 8px;
border-radius: 6px;
background-color: #fff;
}
.active {
background-color: rgba(190,162,102,0.1);;
border: 1px solid #BEA266;
color: #333333;
> span:first-child {
color: #BEA266;
}
}
.contextmenu {
padding: 5px;
width: 88px;
//height: 82px;
position: absolute;
top: 47px;
z-index: 3000;
background: #fff;
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .3);
li {
margin: 0;
padding: 7px 10px;
cursor: pointer;
&:hover {
background: #eee;
color: #EDC49A;
}
}
}
}
</style>