mirror of
https://github.com/continew-org/continew-admin-ui.git
synced 2025-11-11 12:57:10 +08:00
feat: 新增左树右表布局组件GiLeftRightPane封装,分割面板组件GiSplitPaneButton封装,以及代码优化(同步 GiDemo 更新)
This commit is contained in:
60
src/components/GiLeftRightPane/index.vue
Normal file
60
src/components/GiLeftRightPane/index.vue
Normal file
@@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<a-row align="stretch" :gutter="14" class="gi-left-right-pane">
|
||||
<a-col
|
||||
:xs="0" :sm="8" :md="7" :lg="6" :xl="5" :xxl="4" flex="260px" v-bind="props.leftColProps"
|
||||
class="h-full overflow-hidden"
|
||||
>
|
||||
<div class="gi-left-right-pane__left">
|
||||
<slot name="left"></slot>
|
||||
</div>
|
||||
</a-col>
|
||||
|
||||
<a-col
|
||||
:xs="24" :sm="16" :md="17" :lg="18" :xl="19" :xxl="20" flex="1" v-bind="props.rightColProps"
|
||||
class="h-full overflow-hidden"
|
||||
>
|
||||
<div class="gi-left-right-pane__right">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</template>
|
||||
|
||||
<script lang='ts' setup>
|
||||
import type { ColProps } from '@arco-design/web-vue'
|
||||
|
||||
interface Props {
|
||||
leftColProps?: ColProps
|
||||
rightColProps?: ColProps
|
||||
}
|
||||
|
||||
defineOptions({ name: 'GiLeftRightPane' })
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
leftColProps: () => ({}),
|
||||
rightColProps: () => ({}),
|
||||
})
|
||||
|
||||
// 一般用于左树右表结构页面
|
||||
|
||||
defineSlots<{
|
||||
left: () => void
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
.gi-left-right-pane {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
padding: $margin;
|
||||
box-sizing: border-box;
|
||||
|
||||
&__left,
|
||||
&__right {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
83
src/components/GiSpace/index.vue
Normal file
83
src/components/GiSpace/index.vue
Normal file
@@ -0,0 +1,83 @@
|
||||
<template>
|
||||
<div class="gi-space" :class="getClass" :style="getStyle">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { CSSProperties } from 'vue'
|
||||
|
||||
interface Props {
|
||||
size?: number | [number, number] | 'mini' | 'small' | 'medium' | 'large'
|
||||
direction?: 'horizontal' | 'vertical'
|
||||
justify?: 'start' | 'center' | 'end' | 'space-between' | 'space-around'
|
||||
align?: 'start' | 'center' | 'end' | 'baseline' | 'stretch'
|
||||
wrap?: boolean
|
||||
fill?: boolean // 充满整行
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
size: 'small',
|
||||
direction: 'horizontal',
|
||||
justify: 'start',
|
||||
align: 'center',
|
||||
wrap: true,
|
||||
fill: false,
|
||||
})
|
||||
|
||||
const getClass = computed(() => {
|
||||
const arr: string[] = []
|
||||
if (props.wrap) {
|
||||
arr.push('gi-space-wrap')
|
||||
}
|
||||
if (props.direction) {
|
||||
arr.push(`gi-space-direction-${props.direction}`)
|
||||
}
|
||||
if (props.fill) {
|
||||
arr.push('gi-space-fill')
|
||||
}
|
||||
return arr.join(' ')
|
||||
})
|
||||
|
||||
const getStyle = computed(() => {
|
||||
const obj: CSSProperties = {}
|
||||
if (Array.isArray(props.size)) {
|
||||
const [colGap = 0, rowGap = 0] = props.size
|
||||
obj.gap = props.wrap ? `${rowGap}px ${colGap}px` : `0 ${colGap}px`
|
||||
} else {
|
||||
const sizeMap: Record<'mini' | 'small' | 'medium' | 'large', number> = {
|
||||
mini: 4,
|
||||
small: 8,
|
||||
medium: 16,
|
||||
large: 24,
|
||||
}
|
||||
const size = sizeMap[props.size] || props.size
|
||||
obj.gap = props.wrap ? `${size}px` : `0 ${size}px`
|
||||
}
|
||||
obj['justify-content'] = props.justify
|
||||
obj['align-items'] = props.align
|
||||
return obj
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.gi-space {
|
||||
display: inline-flex;
|
||||
|
||||
&.gi-space-wrap {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
&.gi-space-fill {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
&.gi-space-direction-horizontal {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
&.gi-space-direction-vertical {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
58
src/components/GiSplitButton/index.vue
Normal file
58
src/components/GiSplitButton/index.vue
Normal file
@@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<div class="gi-split-button" :class="getClass" @click="emit('click')">
|
||||
<icon-right v-if="props.collapsed" />
|
||||
<icon-left v-else />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang='ts' setup>
|
||||
interface Props {
|
||||
collapsed: boolean
|
||||
type?: 'default' | 'circle'
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
collapsed: false,
|
||||
type: 'circle',
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'click'): void
|
||||
}>()
|
||||
|
||||
const getClass = computed(() => {
|
||||
return `gi-split-button--${props.type}`
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
.gi-split-button {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 999;
|
||||
border: 1px solid var(--color-border-2);
|
||||
background-color: var(--color-bg-1);
|
||||
cursor: pointer;
|
||||
|
||||
&--default {
|
||||
width: 18px;
|
||||
height: 40px;
|
||||
left: 0;
|
||||
box-shadow: 2px 0 6px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
&--circle {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%;
|
||||
left: -12px;
|
||||
z-index: 999;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<div ref="boxRef" class="gi-flexible-box" :style="getStyle">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { type CSSProperties, computed, ref } from 'vue'
|
||||
import { useBreakpoint } from '@/hooks'
|
||||
|
||||
interface Props {
|
||||
visible: boolean
|
||||
direction?: 'left' | 'right'
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
visible: false,
|
||||
direction: 'left',
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:visible', value: boolean): void
|
||||
}>()
|
||||
|
||||
const boxRef = ref<HTMLElement | null>()
|
||||
const { breakpoint } = useBreakpoint()
|
||||
watch(() => breakpoint.value, (val) => {
|
||||
if (['xs'].includes(val)) {
|
||||
emit('update:visible', false)
|
||||
} else {
|
||||
emit('update:visible', true)
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
const getStyle = computed(() => {
|
||||
const obj: CSSProperties = {}
|
||||
obj[`margin-${props.direction}`]
|
||||
= !props.visible && boxRef.value && boxRef.value.clientWidth ? `-${boxRef.value.clientWidth}px` : 0
|
||||
return obj
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.gi-flexible-box {
|
||||
transition: all 0.36s;
|
||||
}
|
||||
</style>
|
||||
62
src/components/GiSplitPane/index.vue
Normal file
62
src/components/GiSplitPane/index.vue
Normal file
@@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<div class="gi-split-pane">
|
||||
<GiSplitPaneFlexibleBox v-model:visible="visible">
|
||||
<div class="gi-split-pane__left">
|
||||
<slot name="left"></slot>
|
||||
</div>
|
||||
</GiSplitPaneFlexibleBox>
|
||||
<div class="gi-split-pane__line">
|
||||
<GiSplitButton :collapsed="!visible" @click="handleClick"></GiSplitButton>
|
||||
</div>
|
||||
<div class="gi-split-pane__right">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang='ts'>
|
||||
defineSlots<{
|
||||
left: () => void
|
||||
}>()
|
||||
|
||||
const visible = ref(true)
|
||||
|
||||
const handleClick = () => {
|
||||
visible.value = !visible.value
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
.gi-split-pane {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
overflow: hidden;
|
||||
|
||||
&__left,
|
||||
&__right {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
&__left {
|
||||
padding-right: $padding;
|
||||
}
|
||||
|
||||
&__right {
|
||||
flex: 1;
|
||||
padding-left: $padding;
|
||||
}
|
||||
|
||||
&__line {
|
||||
width: 1px;
|
||||
height: auto;
|
||||
background-color: var(--color-border-2);
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user