Skip to content

权限级联组件

上次更新 2024年11月16日星期六 13:39:7 字数 0 字 时长 0 分钟

为了解决权限级联的通用处理组件

基础用法

请选择
[ "1", "1-1" ]

禁用状态

请选择
请选择
[ "1" ]

部分选择器禁用

请选择
[ "2", "2-1" ]

自定义标签

请选择城市
请选择
[ "1" ]

遵循给出默认值即进行禁用

请选择
请选择
[ "2" ]
HcCascaderDemo
HcCascaderDemo.vue
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
index.vue
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>

上次更新: