
javascript
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>级联选择器</title>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
#app {
height: 700px;
}
.cascader-container {
position: relative;
display: inline-block;
}
.cascader-input {
width: 250px;
padding: 8px;
border: 1px solid #ccc;
border-radius: 4px;
cursor: pointer;
}
.dropdown {
position: absolute;
top: 40px;
left: 0;
display: flex;
border: 1px solid #ddd;
background: white;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
.column {
width: 150px;
border-right: 1px solid #eee;
}
.column:last-child {
border-right: none;
}
ul {
list-style: none;
padding: 5px;
}
li {
display: flex;
align-items: center;
padding: 5px;
cursor: pointer;
}
li:hover {
background: #f5f5f5;
}
input[type="radio"] {
margin-right: 8px;
}
.selected {
color: #409eff;
font-weight: bold;
}
.arrow {
margin-left: auto;
}
</style>
</head>
<body>
<div id="app" @click="hideDropdown">
<div class="cascader-container" @click.stop>
<input
class="cascader-input"
:value="selectedPath"
readonly
@click="toggleDropdown"
/>
<div v-if="dropdownVisible" class="dropdown" @click.stop>
<div
v-for="(items, level) in visibleData"
:key="level"
class="column"
>
<ul>
<li
v-for="item in items"
:key="item.value"
@click.stop="toggleExpand(item, level)"
>
<input
type="radio"
name="cascader"
:value="item.value"
v-model="selectedValue"
@click.stop="selectItem(item)"
/>
<span :class="{ selected: selectedValue === item.value }"
>{{ item.label }}</span
>
<span v-if="item.children" class="arrow">></span>
</li>
</ul>
</div>
</div>
</div>
</div>
<script>
new Vue({
el: "#app",
data: {
dropdownVisible: false,
selectedValue: "",
selectedPath: "请选择",
options: [
{
value: "zhinan",
label: "指南",
children: [
{
value: "shejiyuanze",
label: "设计原则",
children: [
{
value: "yizhi",
label: "一致",
},
{
value: "fankui",
label: "反馈",
},
{
value: "xiaolv",
label: "效率",
},
{
value: "kekong",
label: "可控",
},
],
},
{
value: "daohang",
label: "导航",
children: [
{
value: "cexiangdaohang",
label: "侧向导航",
},
{
value: "dingbudaohang",
label: "顶部导航",
},
],
},
],
},
{
value: "zujian",
label: "组件",
children: [
{
value: "basic",
label: "Basic",
children: [
{
value: "layout",
label: "Layout 布局",
},
{
value: "color",
label: "Color 色彩",
},
{
value: "typography",
label: "Typography 字体",
},
{
value: "icon",
label: "Icon 图标",
},
{
value: "button",
label: "Button 按钮",
},
],
},
{
value: "form",
label: "Form",
children: [
{
value: "radio",
label: "Radio 单选框",
},
{
value: "checkbox",
label: "Checkbox 多选框",
},
{
value: "input",
label: "Input 输入框",
},
{
value: "input-number",
label: "InputNumber 计数器",
},
{
value: "select",
label: "Select 选择器",
},
{
value: "cascader",
label: "Cascader 级联选择器",
},
{
value: "switch",
label: "Switch 开关",
},
{
value: "slider",
label: "Slider 滑块",
},
{
value: "time-picker",
label: "TimePicker 时间选择器",
},
{
value: "date-picker",
label: "DatePicker 日期选择器",
},
{
value: "datetime-picker",
label: "DateTimePicker 日期时间选择器",
},
{
value: "upload",
label: "Upload 上传",
},
{
value: "rate",
label: "Rate 评分",
},
{
value: "form",
label: "Form 表单",
},
],
},
{
value: "data",
label: "Data",
children: [
{
value: "table",
label: "Table 表格",
},
{
value: "tag",
label: "Tag 标签",
},
{
value: "progress",
label: "Progress 进度条",
},
{
value: "tree",
label: "Tree 树形控件",
},
{
value: "pagination",
label: "Pagination 分页",
},
{
value: "badge",
label: "Badge 标记",
},
],
},
{
value: "notice",
label: "Notice",
children: [
{
value: "alert",
label: "Alert 警告",
},
{
value: "loading",
label: "Loading 加载",
},
{
value: "message",
label: "Message 消息提示",
},
{
value: "message-box",
label: "MessageBox 弹框",
},
{
value: "notification",
label: "Notification 通知",
},
],
},
{
value: "navigation",
label: "Navigation",
children: [
{
value: "menu",
label: "NavMenu 导航菜单",
},
{
value: "tabs",
label: "Tabs 标签页",
},
{
value: "breadcrumb",
label: "Breadcrumb 面包屑",
},
{
value: "dropdown",
label: "Dropdown 下拉菜单",
},
{
value: "steps",
label: "Steps 步骤条",
},
],
},
{
value: "others",
label: "Others",
children: [
{
value: "dialog",
label: "Dialog 对话框",
},
{
value: "tooltip",
label: "Tooltip 文字提示",
},
{
value: "popover",
label: "Popover 弹出框",
},
{
value: "card",
label: "Card 卡片",
},
{
value: "carousel",
label: "Carousel 走马灯",
},
{
value: "collapse",
label: "Collapse 折叠面板",
},
],
},
],
},
{
value: "ziyuan",
label: "资源",
children: [
{
value: "axure",
label: "Axure Components",
},
{
value: "sketch",
label: "Sketch Templates",
},
{
value: "jiaohu",
label: "组件交互文档",
},
],
},
],
expandedNodes: [],
},
computed: {
visibleData() {
let levels = [];
let currentLevel = this.options;
levels.push(currentLevel);
for (let i = 0; i < this.expandedNodes.length; i++) {
let found = currentLevel.find(
(node) => node.value === this.expandedNodes[i]
);
if (found && found.children) {
levels.push(found.children);
currentLevel = found.children;
} else {
break;
}
}
return levels;
},
},
methods: {
toggleDropdown() {
this.dropdownVisible = !this.dropdownVisible;
},
hideDropdown() {
this.dropdownVisible = false;
},
toggleExpand(item, level) {
this.expandedNodes = this.expandedNodes.slice(0, level);
if (item.children) {
this.expandedNodes.push(item.value);
}
},
selectItem(item) {
this.selectedValue = item.value;
this.selectedPath = this.getFullPath(item.value, this.options, "");
this.dropdownVisible = false;
console.log("this.selectedValue", this.selectedValue);
console.log("this.selectedPath", this.selectedPath);
},
getFullPath(value, options, path) {
for (let opt of options) {
let newPath = path ? `${path} / ${opt.label}` : opt.label;
if (opt.value === value) return newPath;
if (opt.children) {
let result = this.getFullPath(value, opt.children, newPath);
if (result) return result;
}
}
return "";
},
},
});
</script>
</body>
</html>