|
|
@@ -1,35 +1,23 @@
|
|
|
-<!-- RegionNode.vue -->
|
|
|
<template>
|
|
|
<li class="region-node">
|
|
|
- <div class="region-header relative"> <!-- 新增 relative 类 -->
|
|
|
- <!-- 复选框使用绝对定位,固定位置 -->
|
|
|
- <div class="checkbox-wrapper absolute">
|
|
|
- <input
|
|
|
- type="checkbox"
|
|
|
- :id="region.regionId"
|
|
|
- :checked="region.checked"
|
|
|
- :indeterminate="region.indeterminate"
|
|
|
- @change="toggleRegion"
|
|
|
- :disabled="disabled"
|
|
|
- />
|
|
|
- </div>
|
|
|
- <!-- 文字使用 padding 留出左侧空间 -->
|
|
|
- <label
|
|
|
- :style="{
|
|
|
- color: disabled ? (region.checked ? '#a8abb2' : '#a8abb2') : (region.checked ? '#FF0066' : '#606266'),
|
|
|
- cursor: disabled ? 'not-allowed' : 'pointer'
|
|
|
- }"
|
|
|
- :for="region.id"
|
|
|
+ <div class="region-header">
|
|
|
+ <!-- 使用Element Plus默认样式的el-checkbox -->
|
|
|
+ <el-checkbox
|
|
|
class="ml-checkbox"
|
|
|
+ :id="region.regionId"
|
|
|
+ v-model="region.checked"
|
|
|
+ :indeterminate="region.indeterminate"
|
|
|
+ @change="handleCheckboxChange"
|
|
|
+ :disabled="disabled"
|
|
|
>
|
|
|
{{ region.regionName }}
|
|
|
- </label>
|
|
|
+ </el-checkbox>
|
|
|
</div>
|
|
|
|
|
|
<ul v-if="region.children && region.children.length > 0" class="child-nodes">
|
|
|
<RegionNode
|
|
|
v-for="child in region.children"
|
|
|
- :key="child.id"
|
|
|
+ :key="child.regionId"
|
|
|
:region="child"
|
|
|
@check-change="handleChildCheckChange"
|
|
|
:disabled="disabled"
|
|
|
@@ -39,12 +27,13 @@
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
-import {defineEmits, defineProps} from 'vue';
|
|
|
+import { defineEmits, defineProps } from 'vue';
|
|
|
|
|
|
const props = defineProps({
|
|
|
region: {
|
|
|
type: Object,
|
|
|
- required: true
|
|
|
+ required: true,
|
|
|
+ default: () => ({})
|
|
|
},
|
|
|
disabled: {
|
|
|
type: Boolean,
|
|
|
@@ -56,21 +45,23 @@ const emits = defineEmits(['check-change']);
|
|
|
|
|
|
// 初始化节点状态
|
|
|
const initNodeState = (node) => {
|
|
|
+ if (!node) return;
|
|
|
+ // 确保节点有必要的属性
|
|
|
+ if (node.checked === undefined) node.checked = false;
|
|
|
+ if (node.indeterminate === undefined) node.indeterminate = false;
|
|
|
+
|
|
|
if (node.children && node.children.length > 0) {
|
|
|
- node.children.forEach(child => {
|
|
|
- initNodeState(child);
|
|
|
- });
|
|
|
+ node.children.forEach(child => initNodeState(child));
|
|
|
}
|
|
|
};
|
|
|
|
|
|
// 初始化当前节点
|
|
|
initNodeState(props.region);
|
|
|
|
|
|
-// 切换节点选中状态
|
|
|
-const toggleRegion = () => {
|
|
|
- const newChecked = !props.region.checked;
|
|
|
- updateNodeChecked(props.region, newChecked);
|
|
|
- emits('check-change', props.region.regionId, newChecked);
|
|
|
+// 处理复选框变更
|
|
|
+const handleCheckboxChange = (checked) => {
|
|
|
+ updateNodeChecked(props.region, checked);
|
|
|
+ emits('check-change', props.region.regionId, checked);
|
|
|
};
|
|
|
|
|
|
// 处理子节点选中状态变更
|
|
|
@@ -79,9 +70,10 @@ const handleChildCheckChange = (childId, isChecked) => {
|
|
|
updateParentState(props.region);
|
|
|
};
|
|
|
|
|
|
-
|
|
|
// 更新节点及其子节点的选中状态
|
|
|
const updateNodeChecked = (node, isChecked) => {
|
|
|
+ if (!node) return;
|
|
|
+
|
|
|
node.checked = isChecked;
|
|
|
node.indeterminate = false;
|
|
|
|
|
|
@@ -93,112 +85,47 @@ const updateNodeChecked = (node, isChecked) => {
|
|
|
};
|
|
|
|
|
|
// 更新父节点状态
|
|
|
-// const updateParentState = (parentNode) => {
|
|
|
-// if (!parentNode.children || parentNode.children.length === 0) return;
|
|
|
-//
|
|
|
-// const allChecked = parentNode.children.every(child => child.checked);
|
|
|
-// const noneChecked = parentNode.children.every(child => !child.checked);
|
|
|
-//
|
|
|
-// parentNode.checked = allChecked;
|
|
|
-// parentNode.indeterminate = !allChecked && !noneChecked;
|
|
|
-// };
|
|
|
const updateParentState = (parentNode) => {
|
|
|
- if (!parentNode.children || parentNode.children.length === 0) return;
|
|
|
+ if (!parentNode || !parentNode.children || parentNode.children.length === 0) return;
|
|
|
|
|
|
- // 子ノードが選択されている限り、親ノードはchecked:trueとマークされます。
|
|
|
parentNode.checked = parentNode.children.some(child => child.checked);
|
|
|
- // indeterminateを必要とせずにfalseにする
|
|
|
parentNode.indeterminate = false;
|
|
|
};
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
|
.region-node {
|
|
|
- margin: 4px 0;
|
|
|
+ margin: 1px 0;
|
|
|
+ list-style: none;
|
|
|
}
|
|
|
|
|
|
.region-header {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
- cursor: pointer;
|
|
|
-}
|
|
|
-.region-header label {
|
|
|
- font-size: 14px;
|
|
|
- font-weight: 400;
|
|
|
+ margin-bottom: 0;
|
|
|
}
|
|
|
|
|
|
.child-nodes {
|
|
|
- margin-left: 20px;
|
|
|
+ margin-left: 15px;
|
|
|
padding-left: 0;
|
|
|
- list-style: none;
|
|
|
display: flex;
|
|
|
flex-wrap: wrap;
|
|
|
gap: 29px;
|
|
|
row-gap: 3px;
|
|
|
- }
|
|
|
+}
|
|
|
|
|
|
.child-nodes .region-node {
|
|
|
width: calc(100% / 7 - 26px);
|
|
|
box-sizing: border-box;
|
|
|
}
|
|
|
|
|
|
-input[type="checkbox"] {
|
|
|
- -webkit-appearance: none;
|
|
|
- -moz-appearance: none;
|
|
|
- appearance: none;
|
|
|
- width: 14px;
|
|
|
- height: 14px;
|
|
|
- border: 1px solid #dcdfe6;
|
|
|
- border-radius: 2px;
|
|
|
- background-color: white;
|
|
|
- position: relative;
|
|
|
- cursor: pointer;
|
|
|
- vertical-align: middle;
|
|
|
- margin-right: 1px;
|
|
|
- margin-top: -4px;
|
|
|
-}
|
|
|
-input[type="checkbox"]:checked {
|
|
|
- background-color: #FF0066;
|
|
|
- border-color: #FF0066;
|
|
|
-}
|
|
|
-
|
|
|
-input[type="checkbox"]:checked::after {
|
|
|
- content: "";
|
|
|
- position: absolute;
|
|
|
- left: 3.85px;
|
|
|
- top: 0.7px;
|
|
|
- width: 4.14px;
|
|
|
- height: 7.88px;
|
|
|
- border: 1.53px solid white;
|
|
|
- border-top: none;
|
|
|
- border-left: none;
|
|
|
- transform: rotate(45deg);
|
|
|
-}
|
|
|
-.relative {
|
|
|
- position: relative;
|
|
|
-}
|
|
|
-
|
|
|
-.checkbox-wrapper {
|
|
|
- position: absolute;
|
|
|
- top: 0;
|
|
|
- left: 0;
|
|
|
- width: 16px;
|
|
|
-}
|
|
|
-
|
|
|
.ml-checkbox {
|
|
|
- padding-left: 22px;
|
|
|
+ padding-left: 3px;
|
|
|
display: block;
|
|
|
- margin-top: 1px;
|
|
|
-}
|
|
|
-input[type="checkbox"]:disabled {
|
|
|
- background-color: #f5f7fa;
|
|
|
- border-color: #e4e7ed;
|
|
|
- cursor: not-allowed;
|
|
|
}
|
|
|
-input[type="checkbox"]:disabled:checked {
|
|
|
- background-color: #f2f6fc;
|
|
|
-}
|
|
|
-input[type="checkbox"]:disabled:checked::after {
|
|
|
- border-color: #a8abb2;
|
|
|
+@media (max-width: 767px) {
|
|
|
+ .child-nodes .region-node {
|
|
|
+ width: calc(100% / 4 - 12px);
|
|
|
+ }
|
|
|
}
|
|
|
</style>
|