Commit 8149c6ca by wanglizhen

前端组件上传

parent f71a3023
...@@ -122,7 +122,70 @@ aside { ...@@ -122,7 +122,70 @@ aside {
//main-container全局样式 //main-container全局样式
.app-container { .app-container {
padding: 20px; $boxShadow: 2px 2px 4px rgba(30, 92, 235, 0.1);
padding: var(--container-pd);
padding-top: 0;
&.scroller {
display: flex;
flex-direction: column;
height: var(--app-height);
.app-container {
&__body,
&__wrap {
height: calc(100% - var(--navbar-height) - var(--container-pd));
}
}
}
&.full {
display: flex;
flex-direction: column;
min-height: var(--app-height);
.app-container__body {
flex: 1;
}
.app-container__wrap {
flex: 1;
}
}
&__card {
padding: var(--container-pd-inner);
background-color: #fff;
border-radius: var(--el-border-radius-base);
box-shadow: $boxShadow;
}
/* 左侧 树等组件容器 */
&__side {
flex: 1;
overflow: hidden;
display: flex !important;
flex-direction: column;
padding: var(--container-pd-inner);
height: 100%;
background-color: #fff;
border-radius: var(--el-border-radius-base);
box-shadow: $boxShadow;
&-tree {
flex: 1;
overflow: hidden;
}
}
&__body {
display: flex;
flex-direction: column;
padding: var(--container-pd-inner);
height: 100%;
background-color: #fff;
border-radius: var(--el-border-radius-base);
box-shadow: $boxShadow;
}
&__wrap {
display: flex;
flex-direction: column;
}
&__card,
&__body {
padding-bottom: var(--container-pd);
}
} }
.components-container { .components-container {
......
// $--color-primary: #409EFF;
$--color-primary: #356cec;
$--color-success: #67C23A;
$--color-warning: #E6A23C;
$--color-danger: #F56C6C;
$--color-info: #909399;
// base color // base color
$blue: #324157; $blue: #324157;
$light-blue: #333c46; $light-blue: #3A71A8;
$red: #C03639; $red: #C03639;
$pink: #E65D6E; $pink: #E65D6E;
$green: #30B08F; $green: #30B08F;
...@@ -8,214 +15,94 @@ $tiffany: #4AB7BD; ...@@ -8,214 +15,94 @@ $tiffany: #4AB7BD;
$yellow: #FEC171; $yellow: #FEC171;
$panGreen: #30B08F; $panGreen: #30B08F;
// 默认主题变量 // 默认菜单主题风格
$menuText: #bfcbd9;
$menuActiveText: #409eff;
$menuBg: #304156;
$menuHover: #263445;
// 浅色主题theme-light
$menuLightBg: #ffffff;
$menuLightHover: #f0f1f5;
$menuLightText: #303133;
$menuLightActiveText: #409EFF;
// 基础变量
$base-sidebar-width: 200px;
$sideBarWidth: 200px;
// 菜单暗色变量
$base-menu-color: #bfcbd9; $base-menu-color: #bfcbd9;
$base-menu-color-active: #f4f4f5; // $base-menu-color-active: #f4f4f5;
$base-menu-background: #304156; $base-menu-color-active: #fff;
$base-sub-menu-background: #1f2d3d; $base-menu-background: var(--el-color-primary);
$base-sub-menu-hover: #001528; $base-logo-title-color: #ffffff;
// 组件变量
$--color-primary: #409EFF;
$--color-success: #67C23A;
$--color-warning: #E6A23C;
$--color-danger: #F56C6C;
$--color-info: #909399;
:export {
menuText: $menuText;
menuActiveText: $menuActiveText;
menuBg: $menuBg;
menuHover: $menuHover;
menuLightBg: $menuLightBg;
menuLightHover: $menuLightHover;
menuLightText: $menuLightText;
menuLightActiveText: $menuLightActiveText;
sideBarWidth: $sideBarWidth;
// 导出基础颜色
blue: $blue;
lightBlue: $light-blue;
red: $red;
pink: $pink;
green: $green;
tiffany: $tiffany;
yellow: $yellow;
panGreen: $panGreen;
// 导出组件颜色
colorPrimary: $--color-primary;
colorSuccess: $--color-success;
colorWarning: $--color-warning;
colorDanger: $--color-danger;
colorInfo: $--color-info;
}
// CSS变量定义
:root {
/* 亮色模式变量 */
--sidebar-bg: #{$menuBg};
--sidebar-text: #{$menuText};
--menu-hover: #{$menuHover};
--navbar-bg: #ffffff;
--navbar-text: #303133;
/* splitpanes default-theme 变量 */
--splitpanes-default-bg: #ffffff;
}
// 暗黑模式变量
html.dark {
/* 默认通用 */
--el-bg-color: #141414;
--el-bg-color-overlay: #1d1e1f;
--el-text-color-primary: #ffffff;
--el-text-color-regular: #d0d0d0;
--el-border-color: #434343;
--el-border-color-light: #434343;
/* 侧边栏 */
--sidebar-bg: #141414;
--sidebar-text: #ffffff;
--menu-hover: #2d2d2d;
--menu-active-text: #{$menuActiveText};
/* 顶部导航栏 */ $base-menu-light-color: rgba(0, 0, 0, 0.7);
--navbar-bg: #141414; $base-menu-light-background: #ffffff;
--navbar-text: #ffffff; $base-logo-light-title-color: #001529;
--navbar-hover: #141414;
/* 标签栏 */ $base-sub-menu-background: var(--el-color-primary);
--tags-bg: #141414; $base-sub-menu-hover: #001528;
--tags-item-bg: #1d1e1f;
--tags-item-border: #303030;
--tags-item-text: #d0d0d0;
--tags-item-hover: #2d2d2d;
--tags-close-hover: #64666a;
/* splitpanes 组件暗黑模式变量 */
--splitpanes-bg: #141414;
--splitpanes-border: #303030;
--splitpanes-splitter-bg: #1d1e1f;
--splitpanes-splitter-hover-bg: #2d2d2d;
/* blockquote 暗黑模式变量 */
--blockquote-bg: #1d1e1f;
--blockquote-border: #303030;
--blockquote-text: #d0d0d0;
/* Cron 时间表达式 模式变量 */
--cron-border: #303030;
/* splitpanes default-theme 暗黑模式变量 */
--splitpanes-default-bg: #141414;
/* 侧边栏菜单覆盖 */
.sidebar-container {
.el-menu-item, .menu-title {
color: var(--el-text-color-regular);
}
& .theme-dark .nest-menu .el-sub-menu>.el-sub-menu__title,
& .theme-dark .el-sub-menu .el-menu-item {
background-color: var(--el-bg-color) !important;
}
}
/* 顶部栏栏菜单覆盖 */
.el-menu--horizontal {
.el-menu-item {
&:not(.is-disabled) {
&:hover,
&:focus {
background-color: var(--navbar-hover) !important;
}
}
}
}
/* 分割窗格覆盖 */
.splitpanes {
background-color: var(--splitpanes-bg);
.splitpanes__pane {
background-color: var(--splitpanes-bg);
border-color: var(--splitpanes-border);
}
.splitpanes__splitter { // 自定义暗色菜单风格
background-color: var(--splitpanes-splitter-bg); /**
border-color: var(--splitpanes-border); $base-menu-color:hsla(0,0%,100%,.65);
$base-menu-color-active:#fff;
$base-menu-background:#001529;
$base-logo-title-color: #ffffff;
&:hover { $base-menu-light-color:rgba(0,0,0,.70);
background-color: var(--splitpanes-splitter-hover-bg); $base-menu-light-background:#ffffff;
} $base-logo-light-title-color: #001529;
&:before, $base-sub-menu-background:#000c17;
&:after { $base-sub-menu-hover:#001528;
background-color: var(--splitpanes-border); */
}
}
}
/* 表格样式覆盖 */
.el-table {
--el-table-header-bg-color: var(--el-bg-color-overlay) !important;
--el-table-header-text-color: var(--el-text-color-regular) !important;
--el-table-border-color: var(--el-border-color-light) !important;
--el-table-row-hover-bg-color: var(--el-bg-color-overlay) !important;
.el-table__header-wrapper, .el-table__fixed-header-wrapper { $base-sidebar-width: 220px;
th { $base-sidebar-width-container: 54px;
background-color: var(--el-bg-color-overlay, #f8f8f9) !important;
color: var(--el-text-color-regular, #515a6e);
}
}
}
/* 树组件高亮样式覆盖 */ :root {
.el-tree { --font-family: Arial, PingFang SC, SourceHanSansCN, 微软雅黑, Microsoft YaHei, Helvetica Neue, Helvetica, Hiragino Sans GB, sans-serif;
.el-tree-node.is-current > .el-tree-node__content { --body-bg: #F3F5FB;
background-color: var(--el-bg-color-overlay) !important; --el-menu-horizontal-height: 50px;
color: var(--el-color-primary); --header-height: 50px;
} --container-pd: 10px;
--container-pd-inner: 20px;
.el-tree-node__content:hover { --navbar-height: 48px;
background-color: var(--el-bg-color-overlay); --inner-height: 100vh;
} --inner-height: 100dvh;
}
--highlight-bg-left: rgba(255,255,255,0.15); // 侧边栏-高亮渐变-左
/* 下拉菜单样式覆盖 */ --highlight-bg-right: rgba(255,255,255,0.05); // 侧边栏-高亮渐变-右
.el-dropdown-menu__item:not(.is-disabled):focus, .el-dropdown-menu__item:not(.is-disabled):hover{ }
background-color: var(--navbar-hover) !important; :root {
} --base-sidebar-width: 220px;
--base-sidebar-width-container: 64px;
--base-sidebar-width-mini: 64px;
// 默认菜单主题风格
--base-menu-color: #bfcbd9;
--base-menu-color-active: #fff;
--base-menu-background: var(--el-color-primary);
--base-logo-title-color: #ffffff;
--base-menu-light-color: rgba(0, 0, 0, 0.7);
--base-menu-light-background: #ffffff;
--base-logo-light-title-color: #001529;
--base-sub-menu-background: var(--el-color-primary);
--base-sub-menu-hover: #001528;
--el-transition: all .3s cubic-bezier(.645, .045, .355, 1), border 0s, color .1s, font-size 0s, transform 0s;
}
/* blockquote样式覆盖 */ .app-wrapper {
blockquote { &.column {
background-color: var(--blockquote-bg) !important; --base-sidebar-width: 266px;
border-left-color: var(--blockquote-border) !important;
color: var(--blockquote-text) !important;
}
/* 时间表达式标题样式覆盖 */
.popup-result .title {
background: var(--cron-border);
} }
} }
// the :export directive is the magic sauce for webpack
// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass
:export {
menuColor: var(--base-menu-color);
menuLightColor: var(--base-menu-light-color);
menuColorActive: var(--base-menu-color-active);
menuBackground: var(--base-menu-background);
menuLightBackground: var(--base-menu-light-background);
subMenuBackground: var(--base-sub-menu-background);
subMenuHover: var(--base-sub-menu-hover);
sideBarWidth: var(--base-sidebar-width);
logoTitleColor: var(--base-logo-title-color);
logoLightTitleColor: var(--base-logo-light-title-color);
primaryColor: var(--el-color-primary);
successColor: $--color-success;
dangerColor: $--color-danger;
infoColor: $--color-info;
warningColor: $--color-warning;
}
<script setup lang="ts" name="PageTitle">
/**
* 用法说明
<PageTitle showBackBtn @handlePageBack="handlePageBack">
<span>新增拟入职申请</span>
<template #btnList>
<el-button type="primary">提 交</el-button>
</template>
</PageTitle>
*/
import { computed, onMounted } from 'vue'
import { useRoute } from 'vue-router'
import useSettingsStore from '@/store/modules/settings'
import useAppStore from '@/store/modules/app'
import pageTitleBack from '@/assets/images/pageTitleBack.png'
const route = useRoute()
const settingsStore = useSettingsStore()
const appStore = useAppStore()
const emit = defineEmits<{
back: []
}>()
const props = withDefaults(defineProps<{
back?: boolean
title?: string
}>(), {
back: false
})
const layout = computed(() => settingsStore.layout)
const titleStr = computed<string>(() => props.title ?? String(route?.meta?.title ?? ''))
const tagsView = computed(() => settingsStore.tagsView)
// 是否存在 侧边栏展开收起按钮
const showHamburger = computed(() => appStore.sidebar.showHamburger)
// 是否存在 侧边栏展开收起按钮 和 header 和 tagsview
const space = computed(() => showHamburger.value && layout.value !== 'comprehensive' && !tagsView.value)
function handlePageBack():void {
emit('back')
}
</script>
<template>
<div
class="page-title"
:class="{
'tags-view': !tagsView && layout !== 'comprehensive'
}">
<div
class="page-title-container"
:class="{
'hamburger': space
}">
<div class="left-part">
<!-- <img
v-if="props.back"
class="page-back-logo"
:src="pageTitleBack"
alt=""
@click="handlePageBack"> -->
<el-button
v-if="props.back"
icon="Back"
class="page-back-logo"
link
size=large
@click="handlePageBack"/>
<div class="title">
<slot name="title">{{ titleStr }}</slot>
</div>
</div>
<div class="right-part">
<slot name="buttons"/>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.page-title {
margin-bottom: var(--container-pd);
// padding-top: var(--container-pd);
/* !tagsView 隐藏标签 */
&.tags-view {
position: sticky;
top: 0;
// padding-top: 0;
height: var(--navbar-height);
z-index: 5;
.page-title-container {
margin-left: calc(0px - var(--container-pd));
margin-right: calc(0px - var(--container-pd));
padding-left: var(--container-pd-inner);
}
}
&-container {
height: var(--navbar-height);
border-radius: 4px;
background: rgba(255, 255, 255, 1);
box-shadow: 2px 2px 4px rgba(30, 92, 235, 0.1);
padding: 0 13px 0 20px;
transition: var(--el-transition);
display: flex;
align-items: center;
justify-content: space-between;
/* 显示侧边栏收起展开按钮时 */
&.hamburger {
padding-left: calc(var(--navbar-height) + 12px) !important;
}
.left-part {
display: flex;
align-items: center;
.page-back-logo {
margin-right: 10px;
width: 12px;
height: 10px;
cursor: pointer;
color: #000;
}
.title {
font-size: 16px;
color: rgba(46, 46, 46, 1);
}
}
.right-part {
display: flex;
align-items: center;
}
}
}
</style>
import { h } from 'vue'
import {
ElButton
} from 'element-plus'
import button from './button.module.scss'
export default function ButtonLink(props, context) {
return h(
ElButton,
{
link: true,
type: props?.type
},
() => [
h(
'i',
{
class: [
'icon-sicmp',
props.icon,
button['icon-sicmp']
]
}
),
h(
'span',
{
class: button['button-text']
},
context.slots.default ? context.slots.default() : props.text || ''
)
]
)
}
\ No newline at end of file
<script lang="ts" name="PageTopButtons">
import { defineComponent, ref, h } from 'vue'
import { useRouter } from 'vue-router'
import { ElSpace } from 'element-plus'
import ButtonLink from './ButtonLink'
export default defineComponent({
emit: ['submit', 'preview', 'save'],
props: {
actions: {
type: [Array, Boolean],
default: true
}
},
setup(props, { attrs, slots, emit, expose }) {
// 路由
const router = useRouter()
// 统一点击事件
function onClick(type: string) {
// 如点击返回,判断父组件是否监听back事件,如无则默认返回上一页
if (type === 'back') {
if (typeof attrs.onBack === 'function') {
attrs.onBack()
return
}
router.back()
return
}
// 除back事件,其它事件需定义在父组件
emit(type)
}
return () => h(
'div',
{
class: 'page-top-wrap'
},
[
// 左侧按钮
h(
'div',
props.actions === true || (Array.isArray(props.actions) && props.actions.includes('back')) ? h(
ButtonLink,
{
icon: 'icon-sicmp-fanhui',
text: '返回',
onClick: () => onClick('back')
}
) : ''
),
// 右侧按钮
h(
'div',
h(
ElSpace,
{
size: 'large',
class: 'buttons-space'
},
() => [
props.actions === true || (Array.isArray(props.actions) && props.actions.includes('submit')) ? h(
ButtonLink,
{
icon: 'icon-sicmp-tijiao',
text: '提交',
onClick: () => onClick('submit')
}
) : null,
props.actions === true || (Array.isArray(props.actions) && props.actions.includes('preview')) ? h(
ButtonLink,
{
icon: 'icon-sicmp-yulanyonghu',
text: '预览',
onClick: () => onClick('preview')
}
) : null,
props.actions === true || (Array.isArray(props.actions) && props.actions.includes('validate')) ? h(
ButtonLink,
{
icon: 'icon-sicmp-jiancha',
text: '检查',
onClick: () => onClick('validate')
}
) : null,
props.actions === true || (Array.isArray(props.actions) && props.actions.includes('save')) ? h(
ButtonLink,
{
icon: 'icon-sicmp-baocun',
text: '保存',
onClick: () => onClick('save')
}
) : null,
props.actions === true || (Array.isArray(props.actions) && props.actions.includes('approve')) ? h(
ButtonLink,
{
icon: 'icon-sicmp-shenhetongguo',
text: '通过审批',
type: 'success',
onClick: () => onClick('approve')
}
) : null,
props.actions === true || (Array.isArray(props.actions) && props.actions.includes('turnBack')) ? h(
ButtonLink,
{
icon: 'icon-sicmp-tuihui',
text: '退回修改',
type: 'warning',
onClick: () => onClick('turnBack')
}
) : null,
props.actions === true || (Array.isArray(props.actions) && props.actions.includes('refuse')) ? h(
ButtonLink,
{
icon: 'icon-sicmp-shenhejujue',
text: '审批拒绝',
type: 'danger',
onClick: () => onClick('refuse')
}
) : null,
props.actions === true || (Array.isArray(props.actions) && props.actions.includes('download')) ? h(
ButtonLink,
{
icon: 'icon-sicmp-xiazai',
text: '下载PDF',
type: 'primary',
onClick: () => onClick('download')
}
) : null
]
)
)
]
)
}
})
</script>
<style lang="scss" scoped>
.page-top-wrap {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
:deep(.icon-sicmp) {
font-size: 20px;
}
:deep(.button-text) {
margin-left: 6px;
}
:deep(.el-space) {
.el-space__item {
&:last-child {
margin-right: 0 !important;
}
}
}
</style>
\ No newline at end of file
<script lang="ts" name="PageWrapperSearch">
import {
ref,
computed,
nextTick,
defineComponent,
h,
isVNode,
useSlots,
withModifiers,
onMounted,
onBeforeUnmount
} from 'vue'
import {
ElRow,
ElCol,
ElForm,
ElIcon,
ElButton,
ElSpace
} from 'element-plus'
import type {
FormInstance,
FormItemProp,
FormRules,
FormValidateCallback
} from 'element-plus'
import {
ArrowDown,
ArrowUp,
Search,
Refresh
} from '@element-plus/icons-vue'
const XS = 0
const SM = 768
const MD = 992
const LG = 1200
const XL = 1920
export default defineComponent({
emits: ['update:advanced', 'search', 'reset'],
components: {
ElRow,
ElCol,
ElForm
},
props: {
xs: {
type: Number,
default: 24
},
sm: {
type: Number,
default: 24
},
md: {
type: Number,
default: 12
},
lg: {
type: Number,
default: 8
},
xl: {
type: Number,
default: 6
},
labelWidth: [Number, String]
},
setup(props, { emit, attrs, slots }) {
let observer
const ElFormRef = ref<FormInstance>()
const max = ref(0)
const isAdvanced = ref(true)
const isFold = ref(true)
const labelWidthValue = ref('')
const inputEvents = ref([])
const gutter = computed(() => (24 / max.value % 24) || 0)
onMounted(() => {
observer = new ResizeObserver((entries) => {
if (entries[0].contentRect.width > 0) {
const rect = ElFormRef.value?.$el.getBoundingClientRect()
initSize()
// console.log('ResizeObserver', entries[0])
// console.log(rect)
}
})
observer.observe(ElFormRef.value?.$el)
initSize()
})
onBeforeUnmount(() => {
observer.disconnect()
observer.unobserve(ElFormRef.value?.$el)
})
function initSize() {
const docW = document.documentElement.clientWidth
// const rect = ElFormRef.value?.$el.getBoundingClientRect()
// const docW = rect.width
// console.log('initSize', docW)
max.value = 24 / (docW >= XL ? props.xl
: docW >= LG ? props.lg
: docW >= MD ? props.md
: docW >= SM ? props.sm
: props.xs)
resetLabelWidth()
}
// 重置表单label宽度
function resetLabelWidth() {
labelWidthValue.value = ''
nextTick().then(() => {
const formItemLabels = ElFormRef.value?.$el ? ElFormRef.value.$el.querySelectorAll('.el-form-item__label') : []
const arr: number[] = []
formItemLabels.forEach(item => {
arr.push(item.offsetWidth)
})
const max = Math.max.apply(null, arr)
labelWidthValue.value = String(max + 16)
})
}
function toggleFold() {
isFold.value = !isFold.value
labelWidthValue.value = ''
resetLabelWidth()
}
return () => {
// 搜索 重置
const buttons = h(
ElCol,
{
class: 'ElColButtons',
xs: 24,
sm: props.sm,
md: props.md,
lg: props.lg,
xl: props.xl
},
() => slots.buttons ? slots.buttons() : h(
ElSpace,
{
class: 'buttons-space'
},
() => [
h(
ElButton,
{
round: false,
plain: true,
onClick: () => {
emit('reset', ElFormRef.value)
}
},
() => '重置'
),
h(
ElButton,
{
type: 'primary',
round: false,
onClick: () => {
emit('search')
}
},
() => '查询'
),
cols.length > max.value ? h(
'div',
{
class: 'advanced',
onClick: toggleFold
},
[
h(
'span',
isFold.value ? '展开' : '收起'
),
h(
ElIcon,
{
class: 'advanced-icon'
},
() => h(isFold.value ? ArrowDown : ArrowUp)
)
]
) : null
]
)
)
// 默认插槽包裹el-col
const SLOTS_DEFAULT = slots.default ? slots?.default() : []
SLOTS_DEFAULT.push({
type: {},
props: null,
key: null,
ref: null,
scopeId: null,
children: null,
component: null,
dirs: null,
transition: null,
el: null,
anchor: null,
target: null,
targetAnchor: null,
suspense: null,
shapeFlag: 0,
patchFlag: 0,
appContext: null
})
const cols = SLOTS_DEFAULT
.filter(item => typeof item.type === 'object')
.map((item, index, arr) => {
if (index === arr.length - 1) {
return [h(
ElCol,
{
xs: 24,
sm: props.sm,
md: props.md,
lg: props.lg,
xl: props.xl,
style: 'height:50px'
}
), buttons]
}
// item.children.default()[0].props.onKeyup = function (e) {
// console.log(e)
// console.log('keyupEnter')
// }
// console.log(item.children.default()[0])
return h(
ElCol,
{
class: { 'hidden': isAdvanced.value && index + 1 >= max.value && isFold.value },
key: `formitem${index}`,
xs: 24,
sm: props.sm,
md: props.md,
lg: props.lg,
xl: props.xl
},
() => h(item)
)
})
return h(
ElForm,
{
...attrs,
labelWidth: props.labelWidth || labelWidthValue.value,
labelPosition: 'right',
ref: ElFormRef
},
() => h(
ElRow,
{
gutter: gutter.value
},
() => cols
)
)
}
}
})
</script>
<style lang="scss" scoped>
:deep(.el-form-item__content) {
> div {
width: 100%;
}
}
:deep(.el-space) {
.el-space__item {
&:last-child {
margin-right: 0 !important;
}
}
}
.advanced {
color: rgba(53, 108, 236, 1);
font-size: 12px;
white-space: nowrap;
cursor: pointer;
&-icon {
margin: 0 4px;
}
}
.buttons {
&-space {
justify-content: flex-end;
width: 100%;
// position: absolute;
// right: 6px;
// bottom: 0;
// padding-bottom: 19px;
}
}
.ElColButtons{
position: absolute;
right: 0;
bottom: 0;
padding-bottom: 18px;
}
.hidden {
// visibility: hidden;
display: none !important;
padding: 0 !important;
width: 0;
max-width: 0;
}
</style>
.icon-sicmp {
font-size: 20px;
}
.button-text {
margin-left: 6px;
}
<template> <template>
<section class="app-main"> <section
<router-view v-slot="{ Component, route }"> class="app-main"
<transition name="fade-transform" mode="out-in"> :class="{
<keep-alive :include="tagsViewStore.cachedViews"> 'has-tags-view': hasTagsView,
<component v-if="!route.meta.link" :is="Component" :key="route.path"/> 'has-header': hasHeader,
</keep-alive> [layout]: true
</transition> }">
</router-view> <el-scrollbar @scroll="onScroll">
<iframe-toggle /> <section class="app-main-container">
<copyright /> <router-view v-slot="{ Component, route }">
<transition name="fade-transform" mode="out-in">
<keep-alive :include="tagsViewStore.cachedViews">
<component
v-if="!route.meta.link"
:is="Component"
:key="route.path"/>
</keep-alive>
</transition>
</router-view>
</section>
</el-scrollbar>
</section> </section>
</template> </template>
<script setup> <script setup lang="ts" name="AppMain">
import copyright from "./Copyright/index" import { computed } from 'vue'
import iframeToggle from "./IframeToggle/index"
import useTagsViewStore from '@/store/modules/tagsView' import useTagsViewStore from '@/store/modules/tagsView'
import useAppStore from '@/store/modules/app'
import settingsStore from '@/store/modules/settings'
const route = useRoute() const props = defineProps({
const tagsViewStore = useTagsViewStore() needTagsView: Boolean
onMounted(() => {
addIframe()
}) })
defineOptions({
watchEffect(() => { inheritAttrs: false
addIframe() // ... 更多自定义属性
}) })
const appStore = useAppStore()
const tagsViewStore = useTagsViewStore()
const settings = settingsStore()
function addIframe() { const hasHeader = computed(() => ['comprehensive'].includes(layout.value))
if (route.meta.link) { const hasTagsView = computed(() => settings.tagsView)
useTagsViewStore().addIframeView(route) const layout = computed(() => settings.layout)
}
function onScroll(val) {
appStore.updateScrollTop(val.scrollTop)
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.app-main { .app-main {
/* 50= navbar 50 */ --app-height: var(--inner-height);
min-height: calc(100vh - 50px); --pd: var(--container-pd);
width: 100%;
position: relative; position: relative;
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden; overflow: hidden;
} border-radius: 0 0 var(--el-border-radius-base) var(--el-border-radius-base);
// transition: padding .3s ease, height .3s ease;
.app-main:has(.copyright) { &.has-header {
padding-bottom: 36px; padding-top: var(--pd);
} --app-height: calc(var(--inner-height) - var(--el-menu-horizontal-height) - var(--pd));
// --app-height: calc(var(--inner-height) - var(--el-menu-horizontal-height) - var(--navbar-height));
.fixed-header + .app-main {
padding-top: 50px;
}
.hasTagsView {
.app-main {
/* 84 = navbar + tags-view = 50 + 34 */
min-height: calc(100vh - 84px);
} }
&.has-tags-view {
.fixed-header + .app-main { padding-top: 0;
padding-top: 84px; --app-height: calc(var(--inner-height) - var(--navbar-height) - var(--pd));
} &.has-header {
} --app-height: calc(var(--inner-height) - var(--el-menu-horizontal-height) - var(--navbar-height) - var(--pd));
</style> }
<style lang="scss">
// fix css style bug in open el-dialog
.el-popup-parent--hidden {
.fixed-header {
padding-right: 6px;
} }
} }
::-webkit-scrollbar {
width: 6px;
height: 6px;
}
::-webkit-scrollbar-track {
background-color: #f1f1f1;
}
::-webkit-scrollbar-thumb {
background-color: #c0c0c0;
border-radius: 3px;
}
</style> </style>
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
<navbar @setLayout="setLayout" /> <navbar @setLayout="setLayout" />
<tags-view v-if="needTagsView" /> <tags-view v-if="needTagsView" />
</div> </div>
<app-main /> <app-main :need-tags-view="needTagsView" />
<settings ref="settingRef" /> <settings ref="settingRef" />
</div> </div>
</div> </div>
......
...@@ -43,6 +43,8 @@ import ImageUpload from "@/components/ImageUpload" ...@@ -43,6 +43,8 @@ import ImageUpload from "@/components/ImageUpload"
import ImagePreview from "@/components/ImagePreview" import ImagePreview from "@/components/ImagePreview"
// 字典标签组件 // 字典标签组件
import DictTag from '@/components/DictTag' import DictTag from '@/components/DictTag'
// PageTitle组件
import PageTitle from './components/PageTitle/index.vue'
const app = createApp(App) const app = createApp(App)
...@@ -65,6 +67,7 @@ app.component('ImageUpload', ImageUpload) ...@@ -65,6 +67,7 @@ app.component('ImageUpload', ImageUpload)
app.component('ImagePreview', ImagePreview) app.component('ImagePreview', ImagePreview)
app.component('RightToolbar', RightToolbar) app.component('RightToolbar', RightToolbar)
app.component('Editor', Editor) app.component('Editor', Editor)
app.component('PageTitle', PageTitle)
app.use(router) app.use(router)
app.use(store) app.use(store)
......
...@@ -11,7 +11,7 @@ import usePermissionStore from '@/store/modules/permission' ...@@ -11,7 +11,7 @@ import usePermissionStore from '@/store/modules/permission'
NProgress.configure({ showSpinner: false }) NProgress.configure({ showSpinner: false })
const whiteList = ['/login', '/register'] const whiteList = ['/login', '/register', '/assetLibrary']
const isWhiteList = (path) => { const isWhiteList = (path) => {
return whiteList.some(pattern => isPathMatch(pattern, path)) return whiteList.some(pattern => isPathMatch(pattern, path))
......
...@@ -58,6 +58,11 @@ export const constantRoutes = [ ...@@ -58,6 +58,11 @@ export const constantRoutes = [
hidden: true hidden: true
}, },
{ {
path: '/assetLibrary',
component: () => import('@/views/assetLibrary/index'),
hidden: true
},
{
path: '', path: '',
component: Layout, component: Layout,
redirect: '/index', redirect: '/index',
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论