mirror of
https://gitee.com/xiaonuobase/snowy.git
synced 2025-12-25 23:56:20 +08:00
【前端】新增a-card一样的高度自适应组件、更新左右分离组件、自定义授权取消联动
This commit is contained in:
parent
f585b8dc6d
commit
02d1198a96
122
snowy-admin-web/src/components/XnPanel/README.md
Normal file
122
snowy-admin-web/src/components/XnPanel/README.md
Normal file
@ -0,0 +1,122 @@
|
||||
XnPanel 面板容器
|
||||
====
|
||||
|
||||
|
||||
封装说明
|
||||
----
|
||||
|
||||
> 与 Ant Design Vue 的 `a-card` 类似,用于承载页面内容的通用面板容器。
|
||||
>
|
||||
> 特性:支持系统暗黑主题、默认主体 `padding: 24px`、高度自适应父容器、内容溢出内部滚动且隐藏滚动条、可控阴影/圆角/分割线(默认右对齐的 footer),以及容器外部底部留白避免贴底;圆角可跟随系统设置自动调整。
|
||||
|
||||
|
||||
例子1
|
||||
----
|
||||
(基础使用)
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<xn-panel title="标题">
|
||||
内容区域
|
||||
</xn-panel>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import XnPanel from '@/components/XnPanel/'
|
||||
</script>
|
||||
```
|
||||
|
||||
|
||||
例子2
|
||||
----
|
||||
(标题扩展与底部操作)
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<xn-panel title="项目概览">
|
||||
<template #extra>
|
||||
<a-space>
|
||||
<a-button type="link">刷新</a-button>
|
||||
<a-button type="primary">新增</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
|
||||
主体内容...
|
||||
|
||||
<template #footer>
|
||||
<a-space>
|
||||
<a-button>取消</a-button>
|
||||
<a-button type="primary">提交</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</xn-panel>
|
||||
</template>
|
||||
```
|
||||
|
||||
|
||||
例子3
|
||||
----
|
||||
(无阴影、圆角与分割线控制、底部留白、系统圆角)
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<xn-panel
|
||||
:shadow="false"
|
||||
radius="system"
|
||||
:headerDivider="false"
|
||||
:footerDivider="true"
|
||||
:bottomGap="16"
|
||||
:padding="24"
|
||||
>
|
||||
内容较多时将内部滚动,滚动条隐藏。
|
||||
</xn-panel>
|
||||
</template>
|
||||
```
|
||||
|
||||
|
||||
内置属性
|
||||
----
|
||||
|
||||
| 属性 | 说明 | 类型 | 默认值 |
|
||||
|----------------|--------------------------------------------|------------------|-------------|
|
||||
| title | 标题文本(也可用 `title` 插槽替代) | String | '' |
|
||||
| bordered | 是否显示外边框 | Boolean | true |
|
||||
| padding | 主体内边距(Number 自动转 px,或 String) | Number/String | 24 |
|
||||
| headerPadding | 头部内边距 | Number/String | 0 |
|
||||
| footerPadding | 底部内边距 | Number/String | 10 |
|
||||
| shadow | 是否显示阴影 | Boolean | false |
|
||||
| radius | 圆角(Number/String,`system` 跟随系统) | Number/String | 'system' |
|
||||
| headerDivider | 头部分割线开关 | Boolean | true |
|
||||
| footerDivider | 底部分割线开关 | Boolean | true |
|
||||
| bottomGap | 容器外部底部留白(避免卡片贴底) | Number/String | 10 |
|
||||
|
||||
|
||||
插槽
|
||||
----
|
||||
|
||||
| 插槽名 | 说明 |
|
||||
|---------|------------------------|
|
||||
| title | 标题区域(替代 `title` 属性) |
|
||||
| extra | 标题右侧扩展区 |
|
||||
| 默认 | 主体内容 |
|
||||
| footer | 底部区域(操作/信息) |
|
||||
|
||||
|
||||
注意事项
|
||||
----
|
||||
|
||||
> 1. `bottomGap` 通过在容器外部设置 `margin-bottom` 实现,并同步减少容器高度,确保不与父容器尺寸冲突。
|
||||
>
|
||||
> 2. 若希望面板内部滚动生效,请确保父容器有明确的高度约束(例如页面布局使用 `flex` 或固定高度)。
|
||||
>
|
||||
> 3. 暗黑主题依赖项目的 CSS 变量:`--snowy-background-color`、`--component-background`、`--border-color-split`、`--card-shadow-soft`、`--heading-color` 等。
|
||||
>
|
||||
> 4. 圆角跟随系统:当系统的“圆角风格”开启时,组件圆角为 `8px`,关闭时为 `2px`;也可通过 `radius` 指定具体值进行覆盖。
|
||||
>
|
||||
> 5. footer 默认右对齐,且背景与分割线使用与整体保持一致的主题变量(背景:`--snowy-background-color`,分割线:`--border-color-split`)。
|
||||
|
||||
|
||||
更新时间
|
||||
----
|
||||
|
||||
该文档最后更新于: 2025-11-30
|
||||
154
snowy-admin-web/src/components/XnPanel/index.vue
Normal file
154
snowy-admin-web/src/components/XnPanel/index.vue
Normal file
@ -0,0 +1,154 @@
|
||||
<template>
|
||||
<div class="xn-panel" :class="{ 'xn-panel-bordered': bordered }" :style="wrapperStyle">
|
||||
<!-- 头部:支持标题与右侧扩展区 -->
|
||||
<div v-if="hasHeader" class="xn-panel-header" :style="headerStyle">
|
||||
<div class="xn-panel-title">
|
||||
<slot name="title">{{ title }}</slot>
|
||||
</div>
|
||||
<div class="xn-panel-extra">
|
||||
<slot name="extra"></slot>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 主体:自动占满剩余空间,超出时内部滚动且隐藏滚动条 -->
|
||||
<div class="xn-panel-body" :style="bodyStyle">
|
||||
<slot></slot>
|
||||
</div>
|
||||
<!-- 底部:常用于操作区或信息展示 -->
|
||||
<div v-if="hasFooter" class="xn-panel-footer" :style="footerStyle">
|
||||
<slot name="footer"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="xnPanel">
|
||||
import { computed, useSlots } from 'vue'
|
||||
import { globalStore } from '@/store'
|
||||
|
||||
// XnPanel 面板容器组件
|
||||
const props = defineProps({
|
||||
// 标题文本(也可用 title 插槽替代)
|
||||
title: { type: String, default: '' },
|
||||
// 是否显示外边框
|
||||
bordered: { type: Boolean, default: false },
|
||||
// 主体内边距(Number 自动转 px,String 直接使用)
|
||||
padding: { type: [Number, String], default: 24 },
|
||||
// 头部内边距
|
||||
headerPadding: { type: [Number, String], default: 0 },
|
||||
// 底部内边距
|
||||
footerPadding: { type: [Number, String], default: 10 },
|
||||
// 是否显示阴影(默认关闭)
|
||||
shadow: { type: Boolean, default: true },
|
||||
// 圆角(支持 Number/px)
|
||||
radius: { type: [Number, String], default: 'system' },
|
||||
// 头部分割线开关
|
||||
headerDivider: { type: Boolean, default: true },
|
||||
// 底部分割线开关
|
||||
footerDivider: { type: Boolean, default: true },
|
||||
// 容器外部底部留白(避免贴底)
|
||||
bottomGap: { type: [Number, String], default: 10 }
|
||||
})
|
||||
|
||||
const slots = useSlots()
|
||||
const store = globalStore()
|
||||
const systemRadius = computed(() => (store.roundedCornerStyleOpen ? 8 : 2))
|
||||
// 是否渲染头部:有 title 文本或存在 title/extra 插槽
|
||||
const hasHeader = computed(() => !!props.title || !!slots?.title || !!slots?.extra)
|
||||
// 是否渲染底部:存在 footer 插槽
|
||||
const hasFooter = computed(() => !!slots?.footer)
|
||||
|
||||
// 将数字统一转为 px 字符串
|
||||
const toPx = (val) => (typeof val === 'number' ? `${val}px` : val)
|
||||
|
||||
const headerStyle = computed(() => ({
|
||||
padding: toPx(props.headerPadding),
|
||||
borderBottom: props.headerDivider ? '1px solid var(--border-color-base)' : 'none'
|
||||
}))
|
||||
|
||||
const footerStyle = computed(() => ({
|
||||
padding: toPx(props.footerPadding),
|
||||
borderTop: props.footerDivider ? '1px solid var(--border-color-split)' : 'none',
|
||||
background: 'var(--snowy-background-color)'
|
||||
}))
|
||||
|
||||
const bodyStyle = computed(() => ({
|
||||
// 主体区域 padding 统一受控
|
||||
padding: toPx(props.padding)
|
||||
}))
|
||||
|
||||
// 容器样式:统一应用圆角、阴影,并在外部保留 bottomGap 间距
|
||||
const wrapperStyle = computed(() => {
|
||||
const gap = toPx(props.bottomGap)
|
||||
return {
|
||||
borderRadius: toPx(props.radius === 'system' ? systemRadius.value : props.radius),
|
||||
boxShadow: props.shadow ? 'var(--card-shadow-soft, 0 1px 6px rgba(0, 0, 0, 0.06))' : 'none',
|
||||
// 在容器外部留白,避免卡片贴到页面底部
|
||||
marginBottom: gap,
|
||||
// 同步减少容器高度,确保不超出父容器
|
||||
height: `calc(100% - ${gap})`
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.xn-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
/* 背景颜色跟随系统暗黑主题变量 */
|
||||
background: var(--snowy-background-color);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.xn-panel-bordered {
|
||||
/* 外边框颜色跟随系统变量 */
|
||||
border: 1px solid var(--border-color-split);
|
||||
}
|
||||
|
||||
.xn-panel-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
min-height: 48px;
|
||||
padding: 0;
|
||||
/* 分割线受 headerDivider 控制,默认展示 */
|
||||
border-bottom: 1px solid var(--border-color-base);
|
||||
}
|
||||
|
||||
.xn-panel-title {
|
||||
padding: 0 24px;
|
||||
color: var(--heading-color);
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.xn-panel-extra {
|
||||
padding: 0 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.xn-panel-body {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
/* 主体背景:更浅的背景层 */
|
||||
background: var(--snowy-background-color);
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
}
|
||||
|
||||
.xn-panel-body::-webkit-scrollbar {
|
||||
/* 隐藏滚动条但保留滚动行为 */
|
||||
display: none;
|
||||
}
|
||||
|
||||
.xn-panel-footer {
|
||||
/* 分割线受 footerDivider 控制,默认展示 */
|
||||
border-top: 1px solid var(--border-color-split);
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
background: var(--snowy-background-color);
|
||||
}
|
||||
</style>
|
||||
@ -1,11 +1,12 @@
|
||||
<template>
|
||||
<div class="resizable-panel" :style="{ display: 'flex', flexDirection: direction }">
|
||||
<div class="resizable-panel" :style="wrapperStyle" ref="rootRef">
|
||||
<div
|
||||
class="panel-left"
|
||||
:style="{
|
||||
[sizeProperty]: leftSize + 'px',
|
||||
minWidth: direction === 'row' ? minSize + 'px' : 'auto',
|
||||
minHeight: direction === 'column' ? minSize + 'px' : 'auto'
|
||||
minHeight: direction === 'column' ? minSize + 'px' : 'auto',
|
||||
padding: toPx(leftPadding)
|
||||
}"
|
||||
v-if="!shouldHideLeft"
|
||||
>
|
||||
@ -19,7 +20,7 @@
|
||||
>
|
||||
<div class="resizer-handle"></div>
|
||||
</div>
|
||||
<div class="panel-right" :style="{ flex: 1 }">
|
||||
<div class="panel-right" :style="{ flex: 1, padding: toPx(rightPadding) }">
|
||||
<slot name="right"></slot>
|
||||
</div>
|
||||
</div>
|
||||
@ -27,6 +28,7 @@
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
||||
import { globalStore } from '@/store'
|
||||
|
||||
const props = defineProps({
|
||||
// 初始左侧面板大小
|
||||
@ -54,20 +56,33 @@
|
||||
md: {
|
||||
type: Number,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
bottomGap: {
|
||||
type: [Number, String],
|
||||
default: 10
|
||||
},
|
||||
// 是否显示阴影(默认关闭)
|
||||
shadow: { type: Boolean, default: true },
|
||||
// 圆角(支持 Number/px)
|
||||
radius: { type: [Number, String], default: 'system' },
|
||||
// 直接隐藏左侧与分隔条
|
||||
hideLeft: { type: Boolean, default: false },
|
||||
leftPadding: { type: [Number, String], default: 24 },
|
||||
rightPadding: { type: [Number, String], default: 24 }
|
||||
})
|
||||
|
||||
const emit = defineEmits(['resize'])
|
||||
|
||||
const leftSize = ref(props.initialSize)
|
||||
const isResizing = ref(false)
|
||||
const rootRef = ref(null)
|
||||
let activeContainer = null
|
||||
|
||||
// 监听窗口宽度,用于判断是否在小屏隐藏
|
||||
const windowWidth = ref(typeof window !== 'undefined' ? window.innerWidth : 1024)
|
||||
|
||||
const shouldHideLeft = computed(() => {
|
||||
// 当 md 为 0 时,在 <768 的视口隐藏左侧与拖拽条
|
||||
return props.md === 0 && windowWidth.value < 768
|
||||
return props.hideLeft || (props.md === 0 && windowWidth.value < 768)
|
||||
})
|
||||
|
||||
// 根据方向确定使用的CSS属性
|
||||
@ -78,6 +93,7 @@
|
||||
const startResize = (e) => {
|
||||
if (shouldHideLeft.value) return
|
||||
isResizing.value = true
|
||||
activeContainer = rootRef.value || e.target.closest('.resizable-panel')
|
||||
document.addEventListener('mousemove', handleResize)
|
||||
document.addEventListener('mouseup', stopResize)
|
||||
e.preventDefault()
|
||||
@ -86,7 +102,7 @@
|
||||
const handleResize = (e) => {
|
||||
if (!isResizing.value) return
|
||||
|
||||
const container = e.currentTarget?.closest?.('.resizable-panel') || document.querySelector('.resizable-panel')
|
||||
const container = activeContainer
|
||||
if (!container) return
|
||||
|
||||
const rect = container.getBoundingClientRect()
|
||||
@ -107,6 +123,7 @@
|
||||
|
||||
const stopResize = () => {
|
||||
isResizing.value = false
|
||||
activeContainer = null
|
||||
document.removeEventListener('mousemove', handleResize)
|
||||
document.removeEventListener('mouseup', stopResize)
|
||||
}
|
||||
@ -127,6 +144,24 @@
|
||||
if (resizeHandler) window.removeEventListener('resize', resizeHandler)
|
||||
})
|
||||
|
||||
const store = globalStore()
|
||||
const systemRadius = computed(() => (store.roundedCornerStyleOpen ? 8 : 2))
|
||||
// 将数字统一转为 px 字符串
|
||||
const toPx = (val) => (typeof val === 'number' ? `${val}px` : val)
|
||||
const wrapperStyle = computed(() => {
|
||||
const gap = toPx(props.bottomGap)
|
||||
return {
|
||||
display: 'flex',
|
||||
flexDirection: props.direction,
|
||||
borderRadius: toPx(props.radius === 'system' ? systemRadius.value : props.radius),
|
||||
boxShadow: props.shadow ? 'var(--card-shadow-soft, 0 1px 6px rgba(0, 0, 0, 0.06))' : 'none',
|
||||
// 在容器外部留白,避免卡片贴到页面底部
|
||||
marginBottom: gap,
|
||||
// 同步减少容器高度,确保不超出父容器
|
||||
height: `calc(100% - ${gap})`
|
||||
}
|
||||
})
|
||||
|
||||
// 暴露方法供外部调用
|
||||
defineExpose({
|
||||
setSize: (size) => {
|
||||
|
||||
@ -2,12 +2,18 @@
|
||||
<a-modal
|
||||
v-model:open="visible"
|
||||
title="选择组织"
|
||||
:width="400"
|
||||
:width="500"
|
||||
:mask-closable="false"
|
||||
:destroy-on-close="true"
|
||||
@ok="handleOk"
|
||||
@cancel="onClose"
|
||||
>
|
||||
<div class="scopeDefineOrgActions">
|
||||
<a-space size="small">
|
||||
<a-button size="small" @click="checkAll">全选</a-button>
|
||||
<a-button size="small" @click="invertCheck">反选</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
<div class="scopeDefineOrgTreeDiv">
|
||||
<a-tree
|
||||
v-model:expandedKeys="defaultExpandedKeys"
|
||||
@ -26,12 +32,26 @@
|
||||
|
||||
<script setup="props, context" name="scopeDefineOrg">
|
||||
import roleApi from '@/api/sys/roleApi'
|
||||
import { checkOrUnCheckChildren } from '@/utils/treeHandler'
|
||||
const visible = ref(false)
|
||||
let defaultExpandedKeys = ref([])
|
||||
let checkedKeys = ref([])
|
||||
const treeData = ref([])
|
||||
|
||||
const getAllIds = (nodes) => {
|
||||
const ids = []
|
||||
const stack = [...nodes]
|
||||
while (stack.length) {
|
||||
const n = stack.pop()
|
||||
if (n && n.id) ids.push(n.id)
|
||||
if (n && n.children && n.children.length) {
|
||||
for (let i = 0; i < n.children.length; i++) {
|
||||
stack.push(n.children[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
const resultDataModel = {
|
||||
dataScopeId: '',
|
||||
defineOrgIdData: {
|
||||
@ -80,13 +100,35 @@
|
||||
checkedKeys.value.push(key)
|
||||
})
|
||||
}
|
||||
resultDataModel.defineOrgIdData.scopeDefineOrgIdList = checkedKeys.value
|
||||
}
|
||||
// 替换treeNode 中 title,key,children
|
||||
const treeFieldNames = { children: 'children', title: 'name', key: 'id' }
|
||||
|
||||
// 选中触发
|
||||
const treeCheck = (checkedKeys, { checked, node }) => {
|
||||
resultDataModel.defineOrgIdData.scopeDefineOrgIdList = checkOrUnCheckChildren(checked, node, checkedKeys)
|
||||
const treeCheck = (keysObj) => {
|
||||
checkedKeys.value = keysObj.checked
|
||||
resultDataModel.defineOrgIdData.scopeDefineOrgIdList = keysObj.checked
|
||||
}
|
||||
|
||||
const getCurrentCheckedIds = () => {
|
||||
if (Array.isArray(checkedKeys.value)) return checkedKeys.value
|
||||
if (checkedKeys.value && Array.isArray(checkedKeys.value.checked)) return checkedKeys.value.checked
|
||||
return []
|
||||
}
|
||||
|
||||
const checkAll = () => {
|
||||
const all = getAllIds(treeData.value)
|
||||
checkedKeys.value = all
|
||||
resultDataModel.defineOrgIdData.scopeDefineOrgIdList = all
|
||||
}
|
||||
|
||||
const invertCheck = () => {
|
||||
const all = getAllIds(treeData.value)
|
||||
const current = getCurrentCheckedIds()
|
||||
const set = new Set(current)
|
||||
const next = all.filter((id) => !set.has(id))
|
||||
checkedKeys.value = next
|
||||
resultDataModel.defineOrgIdData.scopeDefineOrgIdList = next
|
||||
}
|
||||
// 定义emit事件
|
||||
const emit = defineEmits({
|
||||
@ -111,4 +153,7 @@
|
||||
max-height: 450px;
|
||||
overflow: auto;
|
||||
}
|
||||
.scopeDefineOrgActions {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -8,6 +8,12 @@
|
||||
@ok="handleOk"
|
||||
@cancel="onClose"
|
||||
>
|
||||
<div class="scopeDefineOrgActions">
|
||||
<a-space size="small">
|
||||
<a-button size="small" @click="checkAll">全选</a-button>
|
||||
<a-button size="small" @click="invertCheck">反选</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
<div class="scopeDefineOrgTreeDiv">
|
||||
<a-tree
|
||||
v-model:expandedKeys="defaultExpandedKeys"
|
||||
@ -26,12 +32,26 @@
|
||||
|
||||
<script setup="props, context" name="userScopeDefineOrg">
|
||||
import userApi from '@/api/sys/userApi'
|
||||
import { checkOrUnCheckChildren } from '@/utils/treeHandler'
|
||||
const visible = ref(false)
|
||||
let defaultExpandedKeys = ref([])
|
||||
let checkedKeys = ref([])
|
||||
const treeData = ref([])
|
||||
|
||||
const getAllIds = (nodes) => {
|
||||
const ids = []
|
||||
const stack = [...nodes]
|
||||
while (stack.length) {
|
||||
const n = stack.pop()
|
||||
if (n && n.id) ids.push(n.id)
|
||||
if (n && n.children && n.children.length) {
|
||||
for (let i = 0; i < n.children.length; i++) {
|
||||
stack.push(n.children[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
const resultDataModel = {
|
||||
dataScopeId: '',
|
||||
defineOrgIdData: {
|
||||
@ -80,13 +100,35 @@
|
||||
checkedKeys.value.push(key)
|
||||
})
|
||||
}
|
||||
resultDataModel.defineOrgIdData.scopeDefineOrgIdList = checkedKeys.value
|
||||
}
|
||||
// 替换treeNode 中 title,key,children
|
||||
const treeFieldNames = { children: 'children', title: 'name', key: 'id' }
|
||||
|
||||
// 选中触发
|
||||
const treeCheck = (checkedKeys, { checked, node }) => {
|
||||
resultDataModel.defineOrgIdData.scopeDefineOrgIdList = checkOrUnCheckChildren(checked, node, checkedKeys)
|
||||
const treeCheck = (keysObj) => {
|
||||
checkedKeys.value = keysObj.checked
|
||||
resultDataModel.defineOrgIdData.scopeDefineOrgIdList = keysObj.checked
|
||||
}
|
||||
|
||||
const getCurrentCheckedIds = () => {
|
||||
if (Array.isArray(checkedKeys.value)) return checkedKeys.value
|
||||
if (checkedKeys.value && Array.isArray(checkedKeys.value.checked)) return checkedKeys.value.checked
|
||||
return []
|
||||
}
|
||||
|
||||
const checkAll = () => {
|
||||
const all = getAllIds(treeData.value)
|
||||
checkedKeys.value = all
|
||||
resultDataModel.defineOrgIdData.scopeDefineOrgIdList = all
|
||||
}
|
||||
|
||||
const invertCheck = () => {
|
||||
const all = getAllIds(treeData.value)
|
||||
const current = getCurrentCheckedIds()
|
||||
const set = new Set(current)
|
||||
const next = all.filter((id) => !set.has(id))
|
||||
checkedKeys.value = next
|
||||
resultDataModel.defineOrgIdData.scopeDefineOrgIdList = next
|
||||
}
|
||||
// 定义emit事件
|
||||
const emit = defineEmits({
|
||||
@ -111,4 +153,7 @@
|
||||
max-height: 450px;
|
||||
overflow: auto;
|
||||
}
|
||||
.scopeDefineOrgActions {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
</style>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user