hy-virtual-tree
v1.1.57
Published
```vue <template> <div style="width: 100%; height: 500px;"> <div> <button @click="updateData">更新数据</button> <button @click="setExpandedKeys('node-1')">设置展开节点</button> </div> <div ref="treeRef" style="width: 100%; height: 400px;">
Maintainers
Readme
一、使用案例
1、基础使用
<template>
<div style="width: 100%; height: 500px;">
<div>
<button @click="updateData">更新数据</button>
<button @click="setExpandedKeys('node-1')">设置展开节点</button>
</div>
<div ref="treeRef" style="width: 100%; height: 400px;"></div>
</div>
</template>
<script>
import { VirtualTree } from 'hy-virtual-tree'
import 'hy-virtual-tree/style'
export default {
data() {
return {
data: [
{
"id": "node-1",
"label": "node-1",
"isDevice": false,
"children": [
{
"id": "node-1-1",
"label": "node-1-1",
"status": 1,
"isDevice": true
},
]
},
{
"id": "node-2",
"label": "node-2",
"isDevice": false,
"children": [
{
"id": "node-2-1",
"label": "node-2-1",
"status": 1,
"isDevice": true
},
{
"id": "node-2-2",
"label": "node-2-2",
"status": 1,
"isDevice": true
},
]
}
],
type: 'checkbox',
}
},
mounted() {
this.virtualTree = new VirtualTree({
// 指定容器
container: this.$refs.treeRef,
// 数据
data: this.data,
// 每一项的高度
itemHeight: 30,
// 可视区域外的缓冲项数量
bufferSize: 10,
// 可视区域外的缓冲项数量
indent: 16,
props: {
// 设置value值
value: 'id',
// 设置label值
label: 'label',
// 设置children值
children: 'children',
// 自定义每一项class值
class(data, node) {
if (node.level > 1) return 'children'
return 'parent'
},
},
// 初始化时处理数据,想回显必须return值,若想要过滤,可不return
handleMethod: (data) => {
console.log('handleMethod', data)
return data
},
// 自定义点击事件
onNodeClick: (data, node, e) => {
console.log("onNodeClicks??", data, node, e)
},
// 节点展开回调
onNodeExpand: (data, node) => {
console.log('onNodeExpand', node, data)
},
// 节点收起回调
onNodeCollapse: (data, node) => {
console.log('onNodeCollapse', node, data)
},
})
},
methods: {
// 全量更新数据
setData() {
this.virtualTree.setData([])
},
// 更新当前已有的数据(有新增删除项请使用 setData 方法)
updateData() {
this.virtualTree.updateData([{
"id": "node-1-1", // value值不可以变
"label": "node-1-1.1",
"status": 1,
"isDevice": true
}])
// 或者
// this.virtualTree.updateData([{
// "id": "node-1", // value值不可以变
// "label": "node-1.2",
// "isDevice": false,
// "children": [
// {
// "id": "node-1-1", // value值不可以变
// "label": "node-1-1.2",
// "status": 2,
// "isDevice": true
// },
// ]
// }])
},
// 自定义展开的节点
setExpandedKeys(key) {
this.virtualTree.setChecked(key)
},
}
}
</script>2、设置选择框
<template>
<div style="width: 100%; height: 500px;">
<div>
<button @click="setChecked">设置单个节点选中状态</button>
<button @click="setCheckedKeys">设置全部选中状态</button>
<button @click="getCheckedInfo">获取当前选中的keys和节点</button>
</div>
<div ref="treeRef" style="width: 100%; height: 400px;"></div>
</div>
</template>
<script>
import { VirtualTree } from 'hy-virtual-tree'
import 'hy-virtual-tree/style'
export default {
data() {
return {
data: [
{
"id": "node-1",
"label": "node-1",
"status": 2,
"isDevice": false,
"children": [
{
"id": "node-1-1",
"label": "node-1-1",
"status": 1,
"isDevice": true
},
]
},
{
"id": "node-2",
"label": "node-2",
"status": 1,
"isDevice": false,
"children": [
{
"id": "node-2-1",
"label": "node-2-1",
"status": 1,
"isDevice": true
},
{
"id": "node-2-2",
"label": "node-2-2",
"status": 1,
"isDevice": true
},
]
}
],
type: 'checkbox',
}
},
mounted() {
this.virtualTree = new VirtualTree({
// 指定容器
container: this.$refs.treeRef,
// 数据
data: this.data,
// 每一项的高度
itemHeight: 30,
// 可视区域外的缓冲项数量
bufferSize: 10,
// 可视区域外的缓冲项数量
indent: 16,
props: {
// 设置value值
value: 'id',
// 设置label值
label: 'label',
// 设置children值
children: 'children',
// 自定义每一项class值
class: (data, node) => {
if (node.level > 1) return 'children'
return 'parent'
},
// 自定义每一项要显示统计模块
showCount: (data, node) => {
return !data.isDevice
},
// 自定义要统计的模块(只统计最后一个子节点)
countFilter: (data) => {
return data.isDevice && data.status === 1
},
// 自定义要统计的模块(只统计最后一个子节点)
totalFilter: (data) => {
return data.isDevice
}
},
// 点击节点时,触发展开/收起
expandOnClickNode: true,
// 点击节点时,触发选中/取消选中
checkOnClickNode: false,
// 选中项配置
rowSelection: {
// 选中类型,checkbox/radio
type: this.type,
// 自定义每一项是否显示选中
showSelect: (data, node) => {
if (this.type === 'checkbox') return true
return this.type === 'radio' && data.isDevice
},
// 点击选中/取消选中触发回调
onCheckChange(data, info, checked) {
console.log("onCheckChange", data, info, checked)
}
},
// 自定义点击事件
onNodeClick: (data, node, e) => {
console.log("onNodeClicks??", data, node, e)
},
// 节点展开回调
onNodeExpand: (data, node) => {
console.log('onNodeExpand', node, data)
},
// 节点收起回调
onNodeCollapse: (data, node) => {
console.log('onNodeCollapse', node, data)
},
})
},
methods: {
/** 设置一个选择框的选中状态 */
setChecked() {
const keys = this.virtualTree.getCheckedKeys()
this.virtualTree.setChecked('node-1-1', !keys.includes('node-1-1'))
},
/** 设置全部选中状态 */
setCheckedKeys() {
this.virtualTree.setChecked(['node-1', 'node-1-1'])
},
// 获取当前选中的keys和节点
getCheckedInfo() {
const keys = this.virtualTree.getCheckedKeys()
const list = this.virtualTree.getCheckedNodes()
console.log('list', keys, list)
},
}
}
</script>3、设置统计和状态
<template>
<div style="width: 100%; height: 500px;">
<div>
<button @click="updateData">更新节点状态</button>
</div>
<div ref="treeRef" style="width: 100%; height: 400px;"></div>
</div>
</template>
<script>
import { VirtualTree } from 'hy-virtual-tree'
import 'hy-virtual-tree/style'
export default {
data() {
return {
data: [
{
"id": "node-1",
"label": "node-1",
"status": 2,
"isDevice": false,
"children": [
{
"id": "node-1-1",
"label": "node-1-1",
"status": 1,
"isDevice": true
},
]
},
{
"id": "node-2",
"label": "node-2",
"status": 1,
"isDevice": false,
"children": [
{
"id": "node-2-1",
"label": "node-2-1",
"status": 1,
"isDevice": true
},
{
"id": "node-2-2",
"label": "node-2-2",
"status": 1,
"isDevice": true
},
]
}
],
type: 'checkbox',
}
},
mounted() {
this.virtualTree = new VirtualTree({
// 指定容器
container: this.$refs.treeRef,
// 数据
data: this.data,
// 每一项的高度
itemHeight: 30,
// 可视区域外的缓冲项数量
bufferSize: 10,
// 可视区域外的缓冲项数量
indent: 16,
props: {
// 设置value值
value: 'id',
// 设置label值
label: 'label',
// 设置children值
children: 'children',
// 自定义每一项class值
class(data, node) {
if (data.isDevice > 1) {
return 'device ' + data.status === 1 ? 'online' : 'offline'
}
return 'dept'
},
// 自定义显示统计模块
showCount: (data, node) => {
return !data.isDevice
},
// 自定义要统计的模块(只统计最后一个子节点)
countFilter: (data) => {
return data.isDevice && data.status === 1
},
// 自定义要统计的模块(只统计最后一个子节点)
totalFilter: (data) => {
return data.isDevice
}
},
// 自定义渲染状态模块
renderStatus: (data, node) => {
const el = document.createElement('div')
if (data.children?.length) return;
el.innerHTML = data.status === 1 ? '在线' : '离线'
return el
},
})
},
methods: {
// 更新 node-1-1 状态
updateData() {
const node = this.virtualTree.getNode("node-1-1")
this.virtualTree.updateData([{
...node.data
status: node.data.status === 1 ? 2 : 1
}])
},
}
}
</script>
<style>
.device.online {
color: #fff;
}
.device.offline {
color: red;
}
</style>4、过滤节点
<template>
<div style="width: 100%; height: 500px;">
<div>
<input v-model="searchValue" >
<button @click="filter">过滤</button>
</div>
<div ref="treeRef" style="width: 100%; height: 400px;"></div>
</div>
</template>
<script>
import { VirtualTree } from 'hy-virtual-tree'
import 'hy-virtual-tree/style'
export default {
data() {
return {
searchValue: '',
data: [
{
"id": "node-1",
"label": "node-1",
"status": 2,
"isDevice": false,
"children": [
{
"id": "node-1-1",
"label": "node-1-1",
"status": 1,
"isDevice": true
},
]
},
{
"id": "node-2",
"label": "node-2",
"status": 1,
"isDevice": false,
"children": [
{
"id": "node-2-1",
"label": "node-2-1",
"status": 1,
"isDevice": true
},
{
"id": "node-2-2",
"label": "node-2-2",
"status": 1,
"isDevice": true
},
]
}
],
type: 'checkbox',
}
},
mounted() {
this.virtualTree = new VirtualTree({
// 指定容器
container: this.$refs.treeRef,
// 数据
data: this.data,
// 每一项的高度
itemHeight: 30,
// 可视区域外的缓冲项数量
bufferSize: 10,
// 可视区域外的缓冲项数量
indent: 16,
props: {
// 设置value值
value: 'id',
// 设置label值
label: 'label',
// 设置children值
children: 'children',
},
filterMethod: (params, data, node) => {
if (!params) return true;
return data.label.includes(params)
},
})
},
methods: {
// 获取当前选中的选项
filter() {
this.virtualTree.getCheckedKeys(this.searchValue)
}
}
}
</script>二、传参
| 选项 | 类型 | 描述 | 默认值 | | --- | --- | --- | --- | | container | string、HTMLElement | 容器 | - | | data | Array<Record<string, any>> | 展示数据 | [] | | business (1.1) | 'device' | 'groupDevice' | 业务类型 | | | props | Props | 配置选项,具体看下表 | | | itemHeight (1.1.0) | number | 每一行高度 | 30 | | itemGap (1.1.0) | number | 每一行间隔 | 10 | | color (1.1.0) | string | 字体颜色 | '#fff' | | fontSize (1.1.0) | number | 字体大小 | 14 | | bgColor (1.1.0) | string | 背景颜色 | | | nodeBgColor (1.1.0) | string | 节点背景颜色 | '#1c334a' | | bufferSize | number | 可视区域外的缓冲项数量 | 10 | | indent | number | 相邻级节点间的水平缩进,单位为像素 | 16 | | rowSelection | RowSelection | 是否可选择 | | | expandOnClickNode | boolean | 是否在点击节点的时候展开或者收缩节点 | true | | checkOnClickNode | boolean | 是否在点击节点的时候选中节点 | false | | checkOnClickLeaf | boolean | 点击叶节点(最后一个子节点)时是否检查或取消节点 | true | | checkOnDblclickParentNode (1.1.0) | boolean | 双击父节点会触发选中功能 | true | | handleMethod | (data: TreeNodeData) => TreeNodeData | 初始化自定义处理数据 | | | filterMethod | (params: any, data: TreeNodeData, node: TreeNode) => boolean | 自定义过滤方法 | | | renderItem (1.1.2) | (data: TreeNodeData, node: TreeNode) => HTMLElement | string | 自定义整个节点内容,设置后 renderIcon、renderContent、renderStatus、renderRight 无效 | | | renderIcon | (data: TreeNodeData, node: TreeNode) => HTMLElement | string | 自定义渲染图标 | | | renderContent (1.1.2) | (data: TreeNodeData, node: TreeNode) => HTMLElement | string | 自定义渲染内容 | | | renderStatus | (data: TreeNodeData, node: TreeNode) => HTMLElement | string | 自定义状态模块 | | | renderRight (1.1.2) | (data: TreeNodeData, node: TreeNode) => HTMLElement | string | 自定义右侧内容 | | | onNodeClick | (data: TreeNodeData, node: TreeNode, e: MouseEvent) => void | 当节点被鼠标左键点击的时候触发 | | | onNodeContextmenu | (data: TreeNodeData, node: TreeNode, e: MouseEvent) => void | 当节点被鼠标右键点击的时候触发 | | | onLoad (1.1.0) | Function | 完成初始化回调函数 | | | deviceConfig (1.1.0) | { deviceStatusMap } | 设备树业务配置,仅在 business 设置为 'device' | 'groupDevice' 时生效 | { deviceStatusMap: { 0: '离线', 1: '在线', 2: '监控中', 3: '通话中', 4: '录像中', 5: '录音中', 6: '广播中', 7: 'SOS' } } |
三、Props
| 选项 | 类型 | 描述 | 默认值 | | --- | --- | --- | --- | | value | string | 每个树节点用来作为唯一标识的属性,在整棵树中应该是唯一的 | value | | label | string | 指定节点标签为节点对象的某个属性值 | label | | children | string | 指定子树为节点对象的某个属性值 | children | | disabled (1.0.1) | string | ((data: TreeNodeData, node: TreeNode) => boolean) | 指定节点选择框是否禁用为节点对象的某个属性值 | disabled | | class | string/Function | 自定义节点类名 | - | | showCount | boolean | ((data: TreeNodeData, node: TreeNode) => boolean) | 是否显示统计模块 | | | countFilter | (data: TreeNodeData) => boolean | 自定义过滤个数统计逻辑,优先级高于count | | | totalFilter | (data: TreeNodeData) => boolean | 自定义过滤总数统计逻辑,优先级高于total | | | count | string | 指定统计个数属性名称 | count | | total | string | 指定统计总数属性名称 | total |
四、RowSelection
| 选项 | 类型 | 描述 | 默认值 | | --- | --- | --- | --- | | type | 'checkbox' | 'radio' | 选项类型 | checkbox | | checkStrictly | boolean | 在type='checkbox'有效,是否严格的遵循父子不互相关联的做法 | false | | showSelect | boolean | ((data: TreeNodeData, node: TreeNode) => boolean) | 是否显示选项 | false | | onCheckChange (1.1.0) | (data: TreeNodeData, info: {checkedKeys: TreeKey[], checkedNodes: TreeNodeData[], checkedBusinessKeys?: TreeKey[]; checkedBusinessNodes?: TreeNodeData[]; halfCheckedKeys: TreeKey[], halfCheckedNodes: TreeNodeData[]}, checked: boolean) => void | 选项变化回调事件,(1.1.0) 新增 checkedBusinessKeys 和 checkedBusinessNodes,只有在设置了 business 才生效 | - |
五、方法
| 选项 | 描述 | 参数 | | --- | --- | --- | | setData (1.1.0) | 全量更新数据,(1.1.0)新增设置完成 callback 回调函数 | (data: TreeData, callback: Function) | | updateData (1.1.0) | 局部更新数据,仅可更新已有的数据,(1.1.0)新增设置完成 callback 回调函数 | (data: TreeData, callback: Function) | | getNode | 获取指定节点数据 | (data: TreeKey | TreeNodeData) => TreeNode | | scrollToIndex | 滚动到指定下标位置 | (index: number) | | scrollToNode | 滚动到指定key的位置 | (key: TreeKey) | | filter (1.1.0) | 手动触发filterMethod函数,params 为过滤参数, (1.1.0) 新增filterAll 为是否过滤全部(filterAll 为 false 时,父节点满足条件时就会停止下钻,并返回全部子节点),filterAll 默认为 true | (params: any, filterAll: boolean) | | getChecked (1.1.0) | 若节点可被选择(即 showCheckbox 为 true),则返回目前被选中的节点所组成的数组,leafOnly 是否只返回根节点,checkedBusinessKeys 和 checkedBusinessNodes 只有在设置了 business 才生效 | (leafOnly: boolean) => {checkedKeys: TreeKey[]; checkedNodes: TreeNodeData[]; checkedBusinessKeys: TreeKey[]; checkedBusinessNodes: TreeNodeData[]; } | | getCheckedKeys | 若节点可被选择(即 showCheckbox 为 true),则返回目前被选中的节点所组成的数组,leafOnly 是否只返回根节点 | (leafOnly: boolean) => TreeKey[] | | getCheckedNodes | 若节点可被选择(即 showCheckbox 为 true),则返回目前被选中的节点所组成的数组,leafOnly 是否只返回根节点 | (leafOnly: boolean) => TreeNodeData[] | | setChecked (1.1.2) | 通过 key 设置某个节点的勾选状态,(1.1.2) 新增 isBusiness 参数,可用业务id设置选中值,仅设置了business生效,默认为false | (key: TreeKey, checked: boolean, isBusiness: boolean) | | setCheckedKeys (1.1.2) | 通过 keys 设置目前勾选的节点,(1.1.2) 新增 isBusiness 参数,可用业务id设置选中值,仅设置了business生效,默认为false | (keys: TreeKey[], isBusiness: boolean) | | setExpandedKeys | 设置展开的节点 | (keys: TreeKey[]) | | setRowSelection | 设置 rowSelection 配置,设置时可选填传参,没传的参数会拿初始化时传的值 | (rowSelection: RowSelection) | | refresh | 刷新视图 | | | destroy | 销毁组件 | |
六、声明类型
export type TreeKey = string | number
export type TreeNodeData = Record<string, any>
export type TreeData = Array<TreeNodeData>
export interface TreeNode {
key: TreeKey
level: number
parent?: TreeNode
children?: TreeNode[]
data: TreeNodeData
label?: string
isLeaf?: boolean
expanded?: boolean
count?: number
total?: number
checkedMark?: boolean // 勾选标记,作用于内部逻辑
}
export interface Tree {
treeNodeMap: Map<TreeKey, TreeNode>
deviceMap?: Map<TreeKey, TreeNode[]>
levelTreeNodeMap: Map<number, TreeNode[]>
treeNodes: TreeNode[]
maxLevel: number
}
export interface Props {
value: string
label: string
children: string
disabled: string | ((data: TreeNodeData, node: TreeNode) => boolean)
class: string | ((data: TreeNodeData, node: TreeNode) => string)
// total、count 和 countFilter、totalFilter 只能二选一
showCount: boolean | ((data: TreeNodeData, node: TreeNode) => boolean) // 是否显示统计模块
total: string // 总数字段
count: string // 统计字段
countFilter?: (data: TreeNodeData) => boolean // 自定义过滤个数统计,优先级高于count
totalFilter?: (data: TreeNodeData) => boolean // 自定义过滤总数统计,优先级高于total、count
}
export interface RowSelection {
type: 'checkbox' | 'radio'
checkStrictly?: boolean // 在type='checkbox'有效,是否严格的遵循父子不互相关联的做法
showSelect?: boolean | ((data: TreeNodeData, node: TreeNode) => boolean)
onCheckChange?: (
data: TreeNodeData,
info: {
checkedKeys: TreeKey[]
checkedNodes: TreeNodeData[]
checkedBusinessKeys?: TreeKey[] // 业务选中key
checkedBusinessNodes?: TreeNodeData[] // 业务选中节点
halfCheckedKeys: TreeKey[]
halfCheckedNodes: TreeNodeData[]
},
checked: boolean
) => void
}
// 自定义渲染元素
export type CustomRenderFn = (
data: TreeNodeData,
node: TreeNode
) => Element | string | any
// 业务类型 device-设备 groupDevice-群组设备
type Business = 'device' | 'groupDevice'
export interface VirtualTreeProps {
container: string | HTMLElement
data: TreeData
emptyText?: string // 内容为空的时候展示的文本
props?: Partial<Props>
business?: Business
itemHeight?: number
itemGap?: number
color?: string // 字体颜色
fontSize?: number // 字体大小
bgColor?: string // 背景颜色
nodeBgColor?: string // 节点背景颜色
bufferSize?: number
showSelected?: boolean
indent?: number
expandOnClickNode?: boolean // 是否在点击节点的时候展开或者收缩节点
checkOnClickNode?: boolean // 是否在点击节点的时候选中节点
checkOnClickLeaf?: boolean // 点击叶节点(最后一个子节点)时是否检查或取消节点
checkOnDblclickParentNode?: boolean // 双击父节点会触发选中功能
rowSelection?: RowSelection
handleMethod?: (data: TreeNodeData) => TreeNodeData // 初始化自定义处理数据
filterMethod?: (params: any, data: TreeNodeData, node: TreeNode) => boolean // 自定义过滤方法
renderIcon?: CustomRenderFn // 自定义渲染图标
renderItem?: CustomRenderFn // 自定义渲染内容
renderStatus?: CustomRenderFn // 自定义状态文本
onNodeClick?: (data: TreeNodeData, node: TreeNode, e: MouseEvent) => void // 当节点被鼠标左键点击的时候触发
onNodeContextmenu?: (
data: TreeNodeData,
node: TreeNode,
e: MouseEvent
) => void // 当节点被鼠标右键点击的时候触发
onNodeExpand?: (data: TreeNodeData, node: TreeNode) => void // 节点展开回调事件
onNodeCollapse?: (data: TreeNodeData, node: TreeNode) => void // 节点收起回调事件
onLoad?: () => void // 完成初始化回调函数
// 设备业务配置
deviceConfig?: {
deviceStatusMap: {} // 状态文案
}
}
// 业务类型声明
// 设备状态(data.deviceStatus) 0-离线 1-在线 2-监控中 3-通话中 4-录像中 5-录音中 6-广播中 7-SOS
export type DeviceStatus = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7
// 业务数据类型(data.dataType) 1-部门 3-设备 4-群组 5-用户 6-考勤 7-水源点 8-采集站
export type DataType = 1 | 3 | 4 | 5 | 6 | 7 | 8
七、自定义样式
.hy-tree {
--hy-color-primary: #2df2ff;
}
.hy-checkbox {
--hy-checkbox-height: 26px; /* 容器高度 */
--hy-checkbox-font-size: 14px; /* 字体大小 */
--hy-checkbox-font-weight: 500; /* 字体粗细 */
--hy-checkbox-text-color: #fff; /* 字体颜色 */
--hy-checkbox-input-width: 14px; /* 选中框宽度 */
--hy-checkbox-input-height: 14px; /* 选中框高度 */
--hy-checkbox-input-border: 1px solid #dcdfe6; /* 默认边框样式 */
--hy-checkbox-checked-icon-color: #fff; /* 选中图标颜色 */
--hy-checkbox-checked-text-color: var(--hy-color-primary); /* 选中文字颜色 */
--hy-checkbox-checked-input-border-color: var(--hy-color-primary); /* 选中边框颜色 */
--hy-checkbox-checked-bg-color: rgba(45, 242, 255, .5); /* 选中背景颜色 */
--hy-checkbox-input-border-color-hover: var(--hy-color-primary); /* 移入边框颜色 */
--hy-checkbox-disabled-input-fill: #f5f7fa; /* 禁用选择框背景颜色 */
--hy-checkbox-disabled-border-color: #dcdfe6; /* 禁用边框颜色 */
--hy-checkbox-border-radius: 2px; /* 边框角弯度 */
}
.hy-radio {
--hy-radio-height: 26px; /* 容器高度 */
--hy-radio-font-size: 14px; /* 字体大小 */
--hy-radio-font-weight: 500; /* 字体粗细 */
--hy-radio-text-color: #fff; /* 字体颜色 */
--hy-radio-input-height: 14px; /* 选中框宽度 */
--hy-radio-input-width: 14px; /* 选中框高度 */
--hy-radio-input-border: 1px solid #dcdfe6; /* 默认边框样式 */
--hy-radio-checked-icon-color: #fff; /* 选中图标颜色 */
--hy-radio-checked-text-color: var(--hy-color-primary); /* 选中文字颜色 */
--hy-radio-checked-input-border-color: var(--hy-color-primary); /* 选中边框颜色 */
--hy-radio-checked-bg-color: transparent; /* 选中背景颜色 */
--hy-radio-input-border-color-hover: var(--hy-color-primary); /* 移入边框颜色 */
--hy-radio-disabled-input-fill: #f5f7fa; /* 禁用选择框背景颜色 */
--hy-radio-disabled-border-color: #dcdfe6; /* 禁用边框颜色 */
--hy-radio-border-radius: 100%; /* 边框角弯度 */
}