Commit 9a7eb4b2 authored by caixingbing's avatar caixingbing

顶部标签

parent ccf4127b
......@@ -41,6 +41,7 @@
"clipboard": "2.0.8",
"core-js": "3.25.3",
"echarts": "5.4.0",
"element-resize-detector": "^1.2.4",
"element-ui": "2.15.12",
"file-saver": "2.0.5",
"fuse.js": "6.4.3",
......
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1684392363345" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4078" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16"><path d="M300.8 202.666667l-12.8 64h-42.666667v640h533.333334v-640h-42.666667l-12.8-64h119.466667v768h-661.333334v-768h119.466667zM554.666667 640v64H320v-64h234.666667z m149.333333-170.666667v64H320v-64h384zM512 53.333333A96 96 0 0 1 608 149.333333v10.666667h58.24l34.133333 170.666667H323.626667l34.133333-170.666667h58.24V149.333333A96 96 0 0 1 512 53.333333z m0 64A32 32 0 0 0 480 149.333333v74.666667h-69.76l-8.533333 42.666667h220.586666l-8.533333-42.666667H544V149.333333A32 32 0 0 0 512 117.333333z" fill="#979797" p-id="4079"></path></svg>
\ No newline at end of file
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1684392363345" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4078" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16"><path d="M300.8 202.666667l-12.8 64h-42.666667v640h533.333334v-640h-42.666667l-12.8-64h119.466667v768h-661.333334v-768h119.466667zM554.666667 640v64H320v-64h234.666667z m149.333333-170.666667v64H320v-64h384zM512 53.333333A96 96 0 0 1 608 149.333333v10.666667h58.24l34.133333 170.666667H323.626667l34.133333-170.666667h58.24V149.333333A96 96 0 0 1 512 53.333333z m0 64A32 32 0 0 0 480 149.333333v74.666667h-69.76l-8.533333 42.666667h220.586666l-8.533333-42.666667H544V149.333333A32 32 0 0 0 512 117.333333z" fill="#58637B" p-id="4079"></path></svg>
\ No newline at end of file
<template>
<div class="navbar">
<hamburger id="hamburger-container" :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
<breadcrumb id="breadcrumb-container" class="breadcrumb-container" v-if="!topNav"/>
<top-nav id="topmenu-container" class="topmenu-container" v-if="topNav"/>
<div class="right-menu">
<template v-if="device!=='mobile'">
<search id="header-search" class="right-menu-item" />
<el-tooltip content="源码地址" effect="dark" placement="bottom">
<ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" />
</el-tooltip>
<el-tooltip content="文档地址" effect="dark" placement="bottom">
<ruo-yi-doc id="ruoyi-doc" class="right-menu-item hover-effect" />
</el-tooltip>
<screenfull id="screenfull" class="right-menu-item hover-effect" />
<el-tooltip content="布局大小" effect="dark" placement="bottom">
<size-select id="size-select" class="right-menu-item hover-effect" />
</el-tooltip>
</template>
<el-dropdown class="avatar-container right-menu-item hover-effect" trigger="click">
<div class="avatar-wrapper">
<img :src="avatar" class="user-avatar">
<i class="el-icon-caret-bottom" />
<div id="navBar" class="navbar">
<div class="left-menu" :style="{width: scrollerWidth}">
<tags-view />
</div>
<div ref="rightMenu" class="flex-box right-menu">
<div class="menu-bells"><img src="@/assets/images/message.png"><i /></div>
<i class="menu-line" />
<el-dropdown class="avatar-container" trigger="hover">
<div class="flex-box avatar-wrapper">
<img v-if="avatar" class="pic-avatar" src="@/assets/images/avatar.png">
<span v-else class="user-avatar">{{ name&&name.slice(0, 1) }}</span>
{{ name }}
</div>
<el-dropdown-menu slot="dropdown">
<el-dropdown-menu slot="dropdown" class="user-dropdown">
<router-link to="/user/profile">
<el-dropdown-item>个人中心</el-dropdown-item>
<el-dropdown-item>用户中心</el-dropdown-item>
</router-link>
<el-dropdown-item @click.native="setting = true">
<span>布局设置</span>
</el-dropdown-item>
<el-dropdown-item divided @click.native="logout">
<span>退出登录</span>
<span>退出</span>
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
......@@ -48,53 +27,36 @@
<script>
import { mapGetters } from 'vuex'
import Breadcrumb from '@/components/Breadcrumb'
import TopNav from '@/components/TopNav'
import Hamburger from '@/components/Hamburger'
import Screenfull from '@/components/Screenfull'
import SizeSelect from '@/components/SizeSelect'
import Search from '@/components/HeaderSearch'
import RuoYiGit from '@/components/RuoYi/Git'
import RuoYiDoc from '@/components/RuoYi/Doc'
import elementResizeDetectorMaker from "element-resize-detector"
import TagsView from './TagsView'
export default {
components: {
Breadcrumb,
TopNav,
Hamburger,
Screenfull,
SizeSelect,
Search,
RuoYiGit,
RuoYiDoc
TagsView
},
data() {
return {
scrollerWidth: null
}
},
computed: {
...mapGetters([
'sidebar',
'avatar',
'device'
]),
setting: {
get() {
return this.$store.state.settings.showSettings
'name'
])
},
set(val) {
this.$store.dispatch('settings/changeSetting', {
key: 'showSettings',
value: val
mounted() {
const _this = this, erd = elementResizeDetectorMaker(), navBar = document.getElementById("navBar")
erd.listenTo(navBar, element => {
_this.$nextTick(() => {
const nvWidth = navBar.offsetWidth
const rtWidth = _this.$refs.rightMenu.offsetWidth
this.scrollerWidth = (nvWidth - rtWidth - 100)+'px' || '800px'
})
})
}
},
topNav: {
get() {
return this.$store.state.settings.topNav
}
}
},
methods: {
toggleSideBar() {
this.$store.dispatch('app/toggleSideBar')
},
async logout() {
this.$confirm('确定注销并退出系统吗?', '提示', {
confirmButtonText: '确定',
......@@ -112,89 +74,94 @@ export default {
<style lang="scss" scoped>
.navbar {
height: 50px;
overflow: hidden;
height: 56px;
overflow: inherit;
position: relative;
background: #fff;
box-shadow: 0 1px 4px rgba(0,21,41,.08);
.hamburger-container {
line-height: 46px;
height: 100%;
float: left;
cursor: pointer;
transition: background .3s;
-webkit-tap-highlight-color:transparent;
&:hover {
background: rgba(0, 0, 0, .025)
}
}
.breadcrumb-container {
.left-menu{
float: left;
}
.topmenu-container {
position: absolute;
left: 50px;
}
.errLog-container {
display: inline-block;
vertical-align: top;
}
.right-menu {
float: right;
height: 100%;
line-height: 50px;
&:focus {
outline: none;
}
.right-menu-item {
display: inline-block;
padding: 0 8px;
height: 100%;
font-size: 18px;
color: #5a5e66;
vertical-align: text-bottom;
&.hover-effect {
.menu-bells{
position: relative;
margin-right: 16px;
cursor: pointer;
transition: background .3s;
&:hover {
background: rgba(0, 0, 0, .025)
img{
width: 24px;
height: 24px;
}
i{
width: 6px;
height: 6px;
background: #FF4545;
border-radius: 50%;
position: absolute;
right: 0;
top: 0;
}
}
.menu-line{
display: inline-block;
width: 1px;
height: 24px;
background: #EEEEEE;
margin-right: 16px;
}
.avatar-container {
margin-right: 30px;
margin-right: 24px;
margin-top: -5px;
.avatar-wrapper {
margin-top: 5px;
position: relative;
.user-avatar {
font-size: 12px;
color: #232323;
line-height: 20px;
cursor: pointer;
width: 40px;
height: 40px;
border-radius: 10px;
.pic-avatar{
width: 20px;
height: 20px;
border-radius: 50%;
margin-right: 4px;
overflow: hidden;
}
.el-icon-caret-bottom {
.user-avatar {
display: inline-block;
cursor: pointer;
position: absolute;
right: -20px;
top: 25px;
font-size: 12px;
width: 20px;
height: 20px;
line-height: 20px;
text-align: center;
background: #E3EEF9;
color: #0081FF;
border-radius: 50%;
margin-right: 4px;
}
}
}
}
}
.user-dropdown {
.el-dropdown-menu__item{
font-size: 12px;
color: #232323;
line-height: 24px;
border-top: 0;
margin-top: 0;
padding: 0 12px;
&:focus, &:not(.is-disabled):hover{
background: #E3EEF9;
}
&:before{
height: 0;
margin: 0;
}
}
}
</style>
......@@ -87,7 +87,17 @@ export default {
bottom: 0px;
}
.el-scrollbar__wrap {
height: 39px;
height: 100%;
padding-top: 24px;
margin-bottom: 0 !important;
}
.is-horizontal{
width: 0;
height: 0;
}
.is-vertical{
width: 0;
height: 0;
}
}
}
......
......@@ -2,19 +2,22 @@
<div id="tags-view-container" class="tags-view-container">
<scroll-pane ref="scrollPane" class="tags-view-wrapper" @scroll="handleScroll">
<router-link
v-for="tag in visitedViews"
v-for="(tag, index) in visitedViews"
ref="tag"
:key="tag.path"
:class="isActive(tag)?'active':''"
:to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"
tag="span"
class="tags-view-item"
:style="activeStyle(tag)"
@click.middle.native="!isAffix(tag)?closeSelectedTag(tag):''"
@contextmenu.prevent.native="openMenu(tag,$event)"
>
<svg :class="isActive(tag)?'tags-icon tags-icon-active':'tags-icon'" aria-hidden="true">
<use :xlink:href="iconName(tag)" />
</svg>
{{ tag.title }}
<span v-if="!isAffix(tag)" class="el-icon-close" @click.prevent.stop="closeSelectedTag(tag)" />
<i :class="index!=visitedViews.length-1 && index != isActiveIndex() && index != isActiveIndex()-1?'tags-item-line':'tags-item-line item-color'" />
</router-link>
</scroll-pane>
<ul v-show="visible" :style="{left:left+'px',top:top+'px'}" class="contextmenu">
......@@ -47,6 +50,19 @@ export default {
visitedViews() {
return this.$store.state.tagsView.visitedViews
},
iconName() {
return function(val) {
let tagIcon = val.meta.tagIcon || 'defaultTag', tagIcons = val.meta.tagIcons || 'defaultTags'
let icon = this.isActive(val) ? tagIcons : tagIcon
if (!icon) {
const index = val.path.indexOf('/', val.path.indexOf('/') + 1)
const parentPath = val.path.slice(0, index)
const currentRoute = this.$router.options.routes.find(item => item.path === parentPath)
icon = this.isActive(val) ? currentRoute.meta.tagIcons : currentRoute.meta.tagIcon
}
return `#icon-${icon}`
}
},
routes() {
return this.$store.state.permission.routes
},
......@@ -75,16 +91,13 @@ export default {
isActive(route) {
return route.path === this.$route.path
},
activeStyle(tag) {
if (!this.isActive(tag)) return {};
return {
"background-color": this.theme,
"border-color": this.theme
};
},
isAffix(tag) {
return tag.meta && tag.meta.affix
},
isActiveIndex() {
const idx = this.visitedViews.findIndex(item => item.path === this.$route.path) || 0
return idx
},
isFirstView() {
try {
return this.selectedTag.fullPath === '/index' || this.selectedTag.fullPath === this.visitedViews[1].fullPath
......@@ -239,44 +252,63 @@ export default {
<style lang="scss" scoped>
.tags-view-container {
height: 34px;
height: 56px;
width: 100%;
background: #fff;
border-bottom: 1px solid #d8dce5;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .12), 0 0 3px 0 rgba(0, 0, 0, .04);
.tags-view-wrapper {
.tags-view-item {
display: inline-block;
position: relative;
cursor: pointer;
height: 26px;
line-height: 26px;
border: 1px solid #d8dce5;
min-width: 128px;
height: 32px;
line-height: 32px;
color: #495060;
background: #fff;
padding: 0 8px;
padding: 0 20px 0 8px;
font-size: 12px;
margin-left: 5px;
margin-top: 4px;
.tags-icon{
width: 16px;
height: 16px;
fill: currentColor;
color: #5a5e66;
margin: 8px 4px -3px 4px;
&.tags-icon-active{
color: pink;
}
}
.tags-item-line{
display: block;
width: 1px;
height: 24px;
background: #D0D1D9;
position: absolute;
right: 0;
top: 4px;
z-index: 2;
&.item-color{
background: #FFFFFF;
}
}
&:first-of-type {
margin-left: 15px;
margin-left: 24px;
}
&:hover {
.el-icon-close{
background-color: rgba(153,153,153,0.3);
color: #999999;
}
&:last-of-type {
margin-right: 15px;
}
&.active {
background-color: #42b983;
color: #fff;
border-color: #42b983;
&::before {
content: '';
background: #fff;
display: inline-block;
width: 8px;
height: 8px;
border-radius: 50%;
position: relative;
margin-right: 2px;
background-color: #F5F5F5;
color: #232323;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
&:hover {
.el-icon-close{
background-color: initial;
color: initial;
}
}
}
}
......@@ -310,6 +342,9 @@ export default {
.tags-view-wrapper {
.tags-view-item {
.el-icon-close {
position: absolute;
right: 5px;
top: 8px;
width: 16px;
height: 16px;
vertical-align: 2px;
......@@ -322,10 +357,6 @@ export default {
display: inline-block;
vertical-align: -3px;
}
&:hover {
background-color: #b4bccc;
color: #fff;
}
}
}
}
......
......@@ -2,10 +2,9 @@
<div :class="classObj" class="app-wrapper" :style="{'--current-color': theme}">
<div v-if="device==='mobile'&&sidebar.opened" class="drawer-bg" @click="handleClickOutside"/>
<sidebar v-if="!sidebar.hide" class="sidebar-container"/>
<div :class="{hasTagsView:needTagsView,sidebarHide:sidebar.hide}" class="main-container">
<div :class="{sidebarHide:sidebar.hide}" class="main-container">
<div :class="{'fixed-header':fixedHeader}">
<navbar/>
<tags-view v-if="needTagsView"/>
</div>
<app-main/>
<right-panel>
......@@ -39,7 +38,6 @@ export default {
sideTheme: state => state.settings.sideTheme,
sidebar: state => state.app.sidebar,
device: state => state.app.device,
needTagsView: state => state.settings.tagsView,
fixedHeader: state => state.settings.fixedHeader
}),
classObj() {
......
......@@ -70,7 +70,7 @@ export const constantRoutes = [
path: 'index',
component: () => import('@/views/index'),
name: 'Index',
meta: { title: '首页', icon: 'dashboard', affix: true }
meta: { title: '首页', icon: 'index', icons: 'indexs', tagIcon: 'indexTag', tagIcons: 'indexTags', affix: true }
}
]
},
......@@ -87,6 +87,26 @@ export const constantRoutes = [
meta: { title: '个人中心', icon: 'user' }
}
]
},
{
path: '/party',
component: Layout,
hidden: true,
redirect: 'noredirect',
children: [
{
path: 'party-a',
component: () => import('@/views/detail/party-a/index'),
name: 'PartyA',
meta: { title: '甲方详情', icon: 'custom', icons: 'customs', tagIcon: 'customTag', tagIcons: 'customTags' }
},
{
path: 'party-b',
component: () => import('@/views/detail/party-b/index'),
name: 'PartyB',
meta: { title: '已方详情', icon: 'users' }
}
]
}
]
......
<template>
<div class="app-container">
甲方详情
</div>
</template>
<script>
export default {
name: 'PartyA',
data() {
return {
}
},
created() {
},
methods: {
}
}
</script>
<style lang="scss" scoped>
</style>
<template>
<div v-loading="loading" class="app-container">
<iframe ref="companyIframe" marginwidth="0" marginheight="0" frameborder="0" scrolling="no" width="100%" :height="iframeHight" :src="src" />
</div>
</template>
<script>
export default {
name: 'EnterpriseData',
components: {
},
data() {
return {
loading: false, // 是否加载中
companyId: null, // 企业ID
iframeHight: window.innerHeight, // iframe高度
scrollTop: 0, // 滚动条距离内部页面顶部距离
token: this.$store.getters.token // 需要携带的token
}
},
created() {
if (this.$route.query.companyId) { // 获取companyId
this.loading = true
this.companyId = this.encodeStr(window.atob(this.$route.query.companyId))
this.src = `https://pre-plug.jiansheku.com/enterprise/${this.companyId}?token=${this.token}`
}
// 示例传参:?companyId=window.btoa('12306')
},
mounted() {
this.getInframeHight() // 实时控制iframe高度
window.addEventListener('scroll', this.scrolling) // 监听页面滚动事件
},
beforeDestroy() {
window.removeEventListener('scroll', this.scrolling) // 销毁页面滚动事件
},
methods: {
getInframeHight() {
const _this = this
window.addEventListener('message', function(e) {
const data = e.data
const sc = document && document.documentElement.scrollTop || document && document.body.scrollTop
if (data && typeof data === 'object') {
// 动态设置iFrame高度
if (data.height) {
_this.iframeHight = data.height
_this.loading = false
}
// 点击栏目名及子标签动态设置滚动高度
if (data.scrollHeight) {
window.scrollTo(sc, parseInt(data.scrollHeight) + 83)
}
// 点击下拉子标签动态设置滚动高度
if (data.clientHeight) {
window.scrollTo(sc, sc - 30)
}
}
})
},
scrolling() {
// 滚动条距文档顶部的距离
const scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop
this.scrollTop = scrollTop
// 准备传值
this.$nextTick(() => {
const ifa = this.$refs.companyIframe
ifa.contentWindow.postMessage({ 'scrollTop': this.scrollTop }, '*')
})
},
// companyId加密
encodeStr(str, secondary) {
const table = 'VyB2Kz79QWYjpiD5lRCIMwJEhqFSx0GN1cveZfU4gs6rk8dPbLtAomOnT3'
const ss = [2, 7, 5, 1, 4, 8, 3, 0, 6]
const xor = 177451812
const add = 8728348608
const mp2 = new Map()
let content = secondary ? str : ' '
let result = '' // 最终加密id
if (!secondary) {
let s = parseInt(str)
s = (s ^ xor) + add
for (let i = 0; i < table.length; i++) {
const s1 = table.substring(i, i + 1)
mp2.set(i, s1)
}
for (let i = 0; i < 9; i++) {
const r = mp2.get(parseInt(s / this.power(58, i) % 58))
content = this.changeStr(content, ss[i], r)
}
}
// 二次加密
const idArr = content.split('')
for (var i = 0; i < idArr.length; i++) {
const hex = idArr[i].charCodeAt().toString(16)
result = result + hex
}
return result
},
power(a, b) {
let power = 1
for (let c = 0; c < b; c++) {
power *= a
}
return power
},
changeStr(str, index, changeStr) {
return str.substr(0, index) + changeStr + str.substr(index + changeStr.length)
}
}
}
</script>
<style lang="scss" scoped>
</style>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment