refactor: 同步 GiDemo 更新

This commit is contained in:
2025-04-04 19:00:18 +08:00
parent 269266d261
commit d97cf15938
14 changed files with 362 additions and 145 deletions

View File

@@ -145,4 +145,12 @@ const isDisabled: Props['cellDisabled'] = (p) => {
defineExpose({ formRef })
</script>
<style lang='scss' scoped></style>
<style lang='scss' scoped>
:deep(.arco-form-item) {
margin-bottom: 0;
.arco-form-item-message {
display: none;
}
}
</style>

View File

@@ -1,36 +0,0 @@
<template>
<div ref="BoxRef" class="gi-flexible-box" :style="style">
<slot></slot>
</div>
</template>
<script setup lang="ts">
import type { CSSProperties } from 'vue'
defineOptions({ name: 'GiFlexibleBox' })
const props = withDefaults(defineProps<Props>(), {
modelValue: false,
direction: 'right',
})
interface Props {
modelValue: boolean
direction: 'left' | 'right'
}
const BoxRef = ref<HTMLElement | null>()
const style = computed(() => {
const obj: CSSProperties = {}
obj[`margin-${props.direction}`]
= !props.modelValue && BoxRef.value && BoxRef.value.clientWidth ? `-${BoxRef.value.clientWidth}px` : 0
return obj
})
</script>
<style scoped lang="scss">
.gi-flexible-box {
transition: all 0.36s;
}
</style>

View File

@@ -0,0 +1,120 @@
<!--
@file GiIconBox 组件 - 图标容器
@description 提供可自定义的图标容器支持不同颜色尺寸和形状
-->
<template>
<div class="gi-icon-box" :class="getClass" :style="getStyle">
<slot />
</div>
</template>
<script setup lang="ts">
import { type CSSProperties, computed } from 'vue'
const props = withDefaults(defineProps<Props>(), {
color: '#165DFF',
size: 20,
shape: 'square',
})
/** 预设状态颜色 */
const STATUS_COLORS = ['primary', 'success', 'warning', 'danger'] as const
/** 形状类型 */
type ShapeType = 'square' | 'round'
/** 颜色类型 */
type ColorType = typeof STATUS_COLORS[number] | string
/** 组件属性定义 */
interface Props {
/**
* 背景颜色
* @description 支持预设状态颜色或自定义颜色值
* @default '#165DFF'
*/
color?: ColorType
/**
* 容器尺寸
* @description 单位为像素
* @default 20
*/
size?: number
/**
* 容器形状
* @description square-方形round-圆形
* @default 'square'
*/
shape?: ShapeType
}
/** 计算容器的 class */
const getClass = computed(() => {
const classes = [
STATUS_COLORS.includes(props.color as typeof STATUS_COLORS[number])
&& `gi-icon-box--${props.color}`,
props.shape && `gi-icon-box__shape--${props.shape}`,
].filter(Boolean)
return classes.join(' ')
})
/** 计算容器的样式 */
const getStyle = computed((): CSSProperties => {
const style: CSSProperties = {}
// 处理自定义颜色
if (!STATUS_COLORS.includes(props.color as typeof STATUS_COLORS[number])) {
style.backgroundColor = props.color
}
// 处理自定义尺寸
if (props.size !== 20) {
style.width = `${props.size}px`
style.height = `${props.size}px`
}
return style
})
</script>
<style lang="scss" scoped>
.gi-icon-box {
display: flex;
justify-content: center;
align-items: center;
flex-shrink: 0;
width: 20px;
height: 20px;
color: #fff;
transition: all 0.2s ease;
// 状态颜色
&--primary {
background-color: rgba(var(--primary-6));
}
&--success {
background-color: rgba(var(--success-6));
}
&--warning {
background-color: rgba(var(--warning-6));
}
&--danger {
background-color: rgba(var(--danger-6));
}
// 形状样式
&__shape {
&--square {
border-radius: 4px;
}
&--round {
border-radius: 50%;
}
}
}
</style>

View File

@@ -1,60 +0,0 @@
<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>

View File

@@ -1,32 +0,0 @@
<template>
<a-overflow-list v-if="data.length">
<a-tag v-for="(item, index) in data" :key="index" size="small">
{{ item }}
</a-tag>
<template #overflow="{ number }">
<a-popover :content-style="{ maxWidth: '300px', padding: '8px 12px' }">
<a-tag color="arcoblue" size="small">+{{ number }}</a-tag>
<template #content>
<a-space wrap>
<a-tag v-for="tag in data.filter((i, n) => n >= data.length - number)" :key="tag" size="small">
{{ tag }}
</a-tag>
</a-space>
</template>
</a-popover>
</template>
</a-overflow-list>
</template>
<script setup lang="ts">
defineOptions({ name: 'GiOverFlowTags' })
withDefaults(defineProps<Props>(), {
data: () => [],
})
interface Props {
data: string[]
}
</script>
<style scoped lang="scss"></style>

View File

@@ -0,0 +1,134 @@
<template>
<a-row align="stretch" :gutter="rowGutter" class="gi-page-layout" :class="getClass">
<a-col v-if="slots.left" v-bind="props.leftColProps" :xs="0" :sm="8" :md="7" :lg="6" :xl="5" :xxl="4" flex="260px">
<div class="gi-page-layout__left" :style="props.leftStyle">
<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">
<div v-if="slots.header" class="gi-page-layout__header" :style="props.headerStyle">
<slot name="header"></slot>
</div>
<div class="gi-page-layout__body" :style="props.bodyStyle">
<slot></slot>
</div>
</a-col>
</a-row>
</template>
<script setup lang='ts'>
import type { ColProps } from '@arco-design/web-vue'
import type { CSSProperties } from 'vue'
defineOptions({ name: 'GiPageLayout' })
const props = withDefaults(defineProps<Props>(), {
margin: true,
padding: true,
gutter: false,
leftColProps: () => ({}),
rightColProps: () => ({}),
leftStyle: () => ({}),
headerStyle: () => ({}),
bodyStyle: () => ({}),
})
/** 组件插槽定义 */
defineSlots<{
header: () => void
left: () => void
default: () => void
}>()
const getClass = computed(() => {
return {
'gi-page-layout--margin': props.margin,
'gi-page-layout--padding': props.padding,
'gi-page-layout--gutter': !!props.gutter,
}
})
const rowGutter = computed(() => {
if (typeof props.gutter === 'boolean') {
return props.gutter ? 14 : undefined
}
return props.gutter
})
/** 组件属性定义 */
interface Props {
margin?: boolean
padding?: boolean
gutter?: boolean | number
leftColProps?: ColProps
rightColProps?: ColProps
leftStyle?: CSSProperties
headerStyle?: CSSProperties
bodyStyle?: CSSProperties
}
const slots = useSlots()
</script>
<style lang='scss' scoped>
.gi-page-layout {
flex: 1;
height: 100%;
display: flex;
overflow: hidden;
box-sizing: border-box;
background-color: var(--color-bg-1);
&--margin {
margin: $margin;
}
&--padding {
.gi-page-layout__left,
.gi-page-layout__header,
.gi-page-layout__body {
padding: $padding;
}
.gi-page-layout__header {
padding-bottom: 0;
}
}
&--gutter {
.gi-page-layout__body-left {
border-right: none;
}
}
:deep(.arco-col) {
height: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
}
}
.gi-page-layout__left {
height: 100%;
border-right: 1px solid var(--color-border);
display: flex;
flex-direction: column;
overflow: hidden;
box-sizing: border-box;
}
.gi-page-layout__header {
border-bottom: 1px solid var(--color-border);
box-sizing: border-box;
}
.gi-page-layout__body {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
box-sizing: border-box;
}
</style>

View File

@@ -0,0 +1,4 @@
// 修复arco-space-item为空时仍然显示的问题
.arco-space-item:empty {
display: none;
}

View File

@@ -0,0 +1,3 @@
.arco-tabs-nav-extra:not(:empty) {
margin-right: var(--padding);
}

View File

@@ -15,6 +15,8 @@
@import './a-typography.less';
@import './a-button.less';
@import './a-divider.less';
@import './a-space.less';
@import './a-tabs.less';
@color-menu-dark-bg: var(--color-bg-1);
@card-color-bg: var(--color-bg-1);
@@ -71,4 +73,4 @@ body {
.danger {
color: rgb(var(--danger-6));
margin-right: 4px;
}
}

View File

@@ -1,4 +1,4 @@
@use './var.scss' as *;
@import './var.scss';
body {
--margin: 14px; // 通用外边距

View File

@@ -1,5 +1,5 @@
/* 全局样式 */
@use './var.scss' as *;
@import './var.scss';
.w-full {
width: 100%;
@@ -162,6 +162,26 @@
border-radius: 100px;
}
.gi_bg_1 {
background-color: var(--color-bg-1);
}
.gi_bg_2 {
background-color: var(--color-bg-2);
}
.gi_bg_3 {
background-color: var(--color-bg-3);
}
.gi_bg_4 {
background-color: var(--color-bg-4);
}
.gi_bg_5 {
background-color: var(--color-bg-5);
}
// hover按钮
.gi_hover_btn {
border: 0 !important;
@@ -211,6 +231,24 @@
}
}
// 页面卡片,右侧有返回按钮
.gi_page_card {
flex: 1;
margin: var(--margin);
box-sizing: border-box;
overflow: hidden;
display: flex;
flex-direction: column;
.arco-card-body {
flex: 1;
box-sizing: border-box;
display: flex;
flex-direction: column;
overflow: hidden;
}
}
// 卡片标题,标题左侧的伪类样式
.gi_card_title {
> .arco-card-header {
@@ -310,6 +348,26 @@
}
}
.gi_full_tabs {
flex: 1;
display: flex;
flex-direction: column;
.arco-tabs-content {
flex: 1;
padding-top: 0;
.arco-tabs-content-list {
height: 100%;
}
.arco-tabs-pane {
height: 100%;
overflow-y: auto;
}
}
}
// 编辑表格,表头的必填红色星号
.gi_column_require {
.arco-table-th-title {
@@ -321,6 +379,14 @@
}
}
.gi_full_column {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
box-sizing: border-box;
}
.gi_tabs {
display: flex;
flex-direction: column;

View File

@@ -1,17 +1,17 @@
// 基础样式
@use './base.scss' as *;
@import './base.scss';
// 全局类名样式
@use './global.scss' as *;
@import './global.scss';
// 自定义原生滚动条样式
@use './scrollbar-reset.scss' as *;
@import './scrollbar-reset.scss';
// 自定义 nprogress 插件进度条颜色
@use './nprogress.scss' as *;
@import './nprogress.scss';
// 富文本的css主题颜色变量
@use './editor.scss' as *;
@import './editor.scss';
// 动画类名
@use './animated.scss' as *;
@import './animated.scss';

View File

@@ -25,15 +25,14 @@ declare module 'vue' {
GiCodeView: typeof import('./../components/GiCodeView/index.vue')['default']
GiDot: typeof import('./../components/GiDot/index.tsx')['default']
GiEditTable: typeof import('./../components/GiEditTable/GiEditTable.vue')['default']
GiFlexibleBox: typeof import('./../components/GiFlexibleBox/index.vue')['default']
GiFooter: typeof import('./../components/GiFooter/index.vue')['default']
GiForm: typeof import('./../components/GiForm/src/GiForm.vue')['default']
GiIconBox: typeof import('./../components/GiIconBox/index.vue')['default']
GiIconSelector: typeof import('./../components/GiIconSelector/index.vue')['default']
GiIframe: typeof import('./../components/GiIframe/index.vue')['default']
GiLeftRightPane: typeof import('./../components/GiLeftRightPane/index.vue')['default']
GiOption: typeof import('./../components/GiOption/index.vue')['default']
GiOptionItem: typeof import('./../components/GiOptionItem/index.vue')['default']
GiOverFlowTags: typeof import('./../components/GiOverFlowTags/index.vue')['default']
GiPageLayout: typeof import('./../components/GiPageLayout/index.vue')['default']
GiSpace: typeof import('./../components/GiSpace/index.vue')['default']
GiSplitButton: typeof import('./../components/GiSplitButton/index.vue')['default']
GiSplitPane: typeof import('./../components/GiSplitPane/index.vue')['default']

View File

@@ -1,22 +1,31 @@
<template>
<div class="gi_table_page">
<a-tabs v-model:active-key="activeKey" type="card-gutter" size="large" @change="change">
<a-tabs v-model:active-key="activeKey" size="large" position="left" @change="change">
<a-tab-pane key="site">
<template #title><icon-apps /> 网站配置</template>
<keep-alive>
<component :is="PanMap[activeKey]" />
</keep-alive>
</a-tab-pane>
<a-tab-pane key="security">
<template #title><icon-safe /> 安全配置</template>
<keep-alive>
<component :is="PanMap[activeKey]" />
</keep-alive>
</a-tab-pane>
<a-tab-pane key="mail">
<template #title><icon-email /> 邮件配置</template>
<keep-alive>
<component :is="PanMap[activeKey]" />
</keep-alive>
</a-tab-pane>
<a-tab-pane key="login">
<template #title><icon-lock /> 登录配置</template>
<keep-alive>
<component :is="PanMap[activeKey]" />
</keep-alive>
</a-tab-pane>
</a-tabs>
<keep-alive>
<component :is="PanMap[activeKey]" />
</keep-alive>
</div>
</template>