权限级联组件
为了解决权限级联的通用处理组件
基础用法
请选择
[
"1",
"1-1"
]
禁用状态
请选择
请选择
[
"1"
]
部分选择器禁用
请选择
[
"2",
"2-1"
]
自定义标签
请选择城市
请选择
[
"1"
]
遵循给出默认值即进行禁用
请选择
请选择
[
"2"
]
HcCascaderDemo
vue
<!--
* @Author: yhx 2045399856@qq.com
* @Date: 2024-11-16 00:35:05
* @LastEditTime: 2024-11-16 21:37:21
* @FilePath: \noteplus\docs\component\HC\HcCascader\HcCascaderDemo.vue
* @Description:
*
-->
<script setup lang="ts">
import { ref } from 'vue';
import HcCascader from './index.vue';
// 基础用法示例数据
const basicOptions = [
{
value: '1',
label: '选项1',
children: [
{
value: '1-1',
label: '选项1-1',
children: [
{
value: '1-1-1',
label: '选项1-1-1'
}
]
},
{
value: '1-2',
label: '选项1-2',
children: [
{
value: '1-2-1',
label: '选项1-2-1'
}
]
}
]
},
{
value: '2',
label: '选项2',
children: [
{
value: '2-1',
label: '选项2-1',
children: [
{
value: '2-1-1',
label: '选项2-1-1'
},
{
value: '2-1-2',
label: '选项2-1-2'
}
]
},
{
value: '2-2',
label: '选项2-2',
children: [
{
value: '2-2-1',
label: '选项2-2-1'
}
]
}
]
}
]
const basicValue = ref(['1', '1-1'])
const basicLabels = ['一级', '二级']
// 禁用状态示例
const disabledValue = ref(['1'])
const disabledOptions = [...basicOptions]
const isDisabled = true
// 部分选择器禁用示例
const partialDisabledValue = ref(['2', '2-1'])
const disabledArray = [true, false]
// 自定义标签示例
const customValue = ref(['1'])
const customLabels = ['省份', '城市']
// 遵循给出默认值即禁用示例
const defaultValueDisabledValue = ref(['2'])
</script>
<template>
<div class="demo-container">
<!-- 基础用法 -->
<div class="demo-block">
<h3>基础用法</h3>
<HcCascader v-model="basicValue" :options="basicOptions" :labels="basicLabels" />
</div>
<div>{{ basicValue }}</div>
<!-- 禁用状态 -->
<div class="demo-block">
<h3>禁用状态</h3>
<HcCascader v-model="disabledValue" :options="disabledOptions" :disabled="isDisabled" />
</div>
<div>{{ disabledValue }}</div>
<!-- 部分选择器禁用 -->
<div class="demo-block">
<h3>部分选择器禁用</h3>
<HcCascader v-model="partialDisabledValue" :options="basicOptions" :disabled="disabledArray" />
</div>
<div>{{ partialDisabledValue }}</div>
<!-- 自定义标签 -->
<div class="demo-block">
<h3>自定义标签</h3>
<HcCascader v-model="customValue" :options="basicOptions" :labels="customLabels" />
</div>
<div>{{ customValue }}</div>
<!-- 遵循给出默认值即进行禁用 -->
<div class="demo-block">
<h3>遵循给出默认值即进行禁用</h3>
<HcCascader v-model="defaultValueDisabledValue" :options="basicOptions" :defaultValueDisabled="true" />
</div>
<div>{{ defaultValueDisabledValue }}</div>
</div>
</template>
<style scoped>
.demo-container {
padding: 20px;
}
.demo-block {
margin-bottom: 30px;
}
.demo-block h3 {
margin-bottom: 15px;
font-size: 16px;
color: #333;
}
</style>
HcCascader
vue
<script setup lang="ts">
import { ElOption, ElSelect } from 'element-plus';
import { computed, ref, watch } from 'vue';
interface Option {
value: string;
label: string;
children?: Option[];
}
interface Props {
modelValue: string[];
options: Option[];
disabled?: boolean | boolean[];
labels?: string[];
clearable?: boolean;
// 是否遵循给出默认值即不能进行禁用
defaultValueDisabled?: boolean;
}
const props = withDefaults(defineProps<Props>(), {
modelValue: () => [],
options: () => [],
disabled: false,
labels: () => [],
clearable: true,
defaultValueDisabled: false
})
const emit = defineEmits(['update:modelValue', 'change'])
// 内部值
const innerValue = ref<string[]>([])
// 计算实际的禁用状态
const disabledArray = computed(() => {
if (Array.isArray(props.disabled)) {
return props.disabled
}
return Array(getMaxDepth(props.options)).fill(props.disabled)
})
// 计算placeholder
const placeholders = computed(() => {
return props.labels.map(label => `请选择${label}`)
})
// 监听外部值变化
watch(
() => props.modelValue,
(newVal) => {
innerValue.value = [...newVal]
},
{ immediate: true }
)
// 处理选择变化
const handleChange = (index: number) => {
innerValue.value = innerValue.value.slice(0, index + 1)
emit('update:modelValue', innerValue.value)
emit('change', innerValue.value)
}
// 获取当前级别的选项
const getOptions = (level: number): Option[] => {
let currentOptions = props.options
for (let i = 0; i < level; i++) {
if (!innerValue.value[i]) return []
const selectedOption = currentOptions.find(opt => opt.value === innerValue.value[i])
if (!selectedOption || !selectedOption.children) return []
currentOptions = selectedOption.children
}
return currentOptions
}
// 获取最大深度
const getMaxDepth = (options: Option[]): number => {
let maxDepth = 1
const traverse = (opts: Option[], depth: number) => {
for (const opt of opts) {
if (opt.children) {
traverse(opt.children, depth + 1)
} else {
maxDepth = Math.max(maxDepth, depth)
}
}
}
traverse(options, 1)
return maxDepth
}
// 计算级联选择器的深度
const cascaderDepth = computed(() => getMaxDepth(props.options))
const cloneModelValue = [...props.modelValue]
// 是否遵循给出默认值即禁用
const getDefaultValueDisabled = (index: number) => {
return props.defaultValueDisabled && !!cloneModelValue[index - 1]
}
</script>
<template>
<div class="hc-cascader">
<ElSelect v-for="index in cascaderDepth" :key="index - 1" v-model="innerValue[index - 1]"
:disabled="getDefaultValueDisabled(index) || disabledArray[index - 1]"
:placeholder="placeholders[index - 1] || `请选择`" @change="handleChange(index - 1)" class="hc-cascader-select"
:clearable="clearable">
<ElOption v-for="option in getOptions(index - 1)" :key="option.value" :label="option.label"
:value="option.value" />
</ElSelect>
</div>
</template>
<style scoped>
.hc-cascader {
display: flex;
gap: 10px;
}
.hc-cascader-select {
min-width: 120px;
}
</style>