|
| 1 | +<script setup lang="ts"> |
| 2 | +import { computed, reactive, ref } from 'vue' |
| 3 | +import { SectionItem, SectionLayout, SectionMain, useSelection } from '@/shared' |
| 4 | +
|
| 5 | +interface User { |
| 6 | + id: number |
| 7 | + name: string |
| 8 | + role: string |
| 9 | + disabled?: boolean |
| 10 | +} |
| 11 | +
|
| 12 | +const config = reactive({ |
| 13 | + singleSelect: false, |
| 14 | + ignoreSelectable: false, |
| 15 | +}) |
| 16 | +
|
| 17 | +const users = ref<User[]>([ |
| 18 | + { id: 1, name: '张三', role: 'Admin' }, |
| 19 | + { id: 2, name: '李四', role: 'Editor' }, |
| 20 | + { id: 3, name: '王五', role: 'User', disabled: true }, |
| 21 | + { id: 4, name: '赵六', role: 'User' }, |
| 22 | + { id: 5, name: '孙七', role: 'Editor', disabled: true }, |
| 23 | + { id: 6, name: '周八', role: 'Admin' }, |
| 24 | +]) |
| 25 | +
|
| 26 | +const selection = useSelection<User, number>({ |
| 27 | + singleSelect: computed(() => config.singleSelect), |
| 28 | + toKey: (row) => row.id, |
| 29 | + selectable: (row) => !row.disabled, |
| 30 | +}) |
| 31 | +
|
| 32 | +const selectedNames = computed(() => |
| 33 | + selection.selection.value.map((r) => r.name), |
| 34 | +) |
| 35 | +
|
| 36 | +const selectableRows = computed(() => |
| 37 | + users.value.filter( |
| 38 | + (r) => config.ignoreSelectable || selection.isSelectable(r), |
| 39 | + ), |
| 40 | +) |
| 41 | +const selectedSelectableCount = computed( |
| 42 | + () => selectableRows.value.filter((r) => selection.isSelected(r)).length, |
| 43 | +) |
| 44 | +const allSelected = computed( |
| 45 | + () => |
| 46 | + selectableRows.value.length > 0 && |
| 47 | + selectedSelectableCount.value === selectableRows.value.length, |
| 48 | +) |
| 49 | +const isIndeterminate = computed( |
| 50 | + () => |
| 51 | + selectedSelectableCount.value > 0 && |
| 52 | + selectedSelectableCount.value < selectableRows.value.length, |
| 53 | +) |
| 54 | +
|
| 55 | +function handleToggleAll(val: any) { |
| 56 | + const checked = !!val |
| 57 | + selectableRows.value.forEach((row) => |
| 58 | + selection.toggleSelection(row, checked, config.ignoreSelectable), |
| 59 | + ) |
| 60 | +} |
| 61 | +
|
| 62 | +function selectAllowed() { |
| 63 | + if (config.singleSelect) { |
| 64 | + const row = users.value.find((r) => selection.isSelectable(r)) |
| 65 | + if (row) { |
| 66 | + selection.toggleSelection(row, true) |
| 67 | + } |
| 68 | + } |
| 69 | + else { |
| 70 | + users.value.forEach((row) => { |
| 71 | + if (selection.isSelectable(row)) { |
| 72 | + selection.toggleSelection(row, true) |
| 73 | + } |
| 74 | + }) |
| 75 | + } |
| 76 | +} |
| 77 | +
|
| 78 | +function toggleThird() { |
| 79 | + const row = users.value[2] |
| 80 | + if (row) { |
| 81 | + selection.toggleSelection(row, undefined, config.ignoreSelectable) |
| 82 | + } |
| 83 | +} |
| 84 | +</script> |
| 85 | + |
| 86 | +<template> |
| 87 | + <SectionLayout height="100%"> |
| 88 | + <SectionItem card> |
| 89 | + <div style="padding: 12px"> |
| 90 | + <ElSpace |
| 91 | + wrap |
| 92 | + :size="12" |
| 93 | + alignment="center" |
| 94 | + > |
| 95 | + <ElSwitch |
| 96 | + v-model="config.singleSelect" |
| 97 | + active-text="单选模式" |
| 98 | + @change="() => selection.clearSelection()" |
| 99 | + /> |
| 100 | + <ElSwitch |
| 101 | + v-model="config.ignoreSelectable" |
| 102 | + active-text="忽略 selectable(编程操作)" |
| 103 | + /> |
| 104 | + <ElDivider direction="vertical" /> |
| 105 | + <ElButton @click="selectAllowed"> |
| 106 | + 选择可选项 |
| 107 | + </ElButton> |
| 108 | + <ElButton @click="toggleThird"> |
| 109 | + 切换第 3 行 |
| 110 | + </ElButton> |
| 111 | + <ElButton |
| 112 | + type="danger" |
| 113 | + @click="selection.clearSelection()" |
| 114 | + > |
| 115 | + 清空选择 |
| 116 | + </ElButton> |
| 117 | + </ElSpace> |
| 118 | + </div> |
| 119 | + </SectionItem> |
| 120 | + |
| 121 | + <SectionMain card> |
| 122 | + <div style="padding: 16px"> |
| 123 | + <ElSpace |
| 124 | + direction="vertical" |
| 125 | + fill |
| 126 | + style="width: 100%" |
| 127 | + > |
| 128 | + <ElText tag="b"> |
| 129 | + 1. 基础用法 |
| 130 | + </ElText> |
| 131 | + |
| 132 | + <ElTable |
| 133 | + :data="users" |
| 134 | + border |
| 135 | + style="width: 100%" |
| 136 | + > |
| 137 | + <ElTableColumn |
| 138 | + label="选择" |
| 139 | + width="80" |
| 140 | + align="center" |
| 141 | + > |
| 142 | + <template #header> |
| 143 | + <ElCheckbox |
| 144 | + v-if="!config.singleSelect" |
| 145 | + :model-value="allSelected" |
| 146 | + :indeterminate="isIndeterminate" |
| 147 | + @change="handleToggleAll" |
| 148 | + /> |
| 149 | + <ElText v-else> |
| 150 | + 单选 |
| 151 | + </ElText> |
| 152 | + </template> |
| 153 | + <template #default="{ row }"> |
| 154 | + <ElCheckbox |
| 155 | + :model-value="selection.isSelected(row)" |
| 156 | + :disabled="!selection.isSelectable(row)" |
| 157 | + @change="(val) => selection.toggleSelection(row, !!val)" |
| 158 | + /> |
| 159 | + </template> |
| 160 | + </ElTableColumn> |
| 161 | + <ElTableColumn |
| 162 | + prop="id" |
| 163 | + label="ID" |
| 164 | + width="80" |
| 165 | + align="center" |
| 166 | + /> |
| 167 | + <ElTableColumn |
| 168 | + prop="name" |
| 169 | + label="姓名" |
| 170 | + /> |
| 171 | + <ElTableColumn |
| 172 | + prop="role" |
| 173 | + label="角色" |
| 174 | + width="120" |
| 175 | + align="center" |
| 176 | + /> |
| 177 | + <ElTableColumn |
| 178 | + label="可选" |
| 179 | + width="100" |
| 180 | + align="center" |
| 181 | + > |
| 182 | + <template #default="{ row }"> |
| 183 | + <ElTag :type="row.disabled ? 'danger' : 'success'"> |
| 184 | + {{ row.disabled ? '不可选' : '可选' }} |
| 185 | + </ElTag> |
| 186 | + </template> |
| 187 | + </ElTableColumn> |
| 188 | + </ElTable> |
| 189 | + |
| 190 | + <ElAlert |
| 191 | + type="info" |
| 192 | + :closable="false" |
| 193 | + > |
| 194 | + <template #title> |
| 195 | + 当前选择 |
| 196 | + </template> |
| 197 | + <div> |
| 198 | + <div> |
| 199 | + 选中键(ids): |
| 200 | + {{ selection.selectedKeys.value.join(', ') || '无' }} |
| 201 | + </div> |
| 202 | + <div>选中项: {{ selectedNames.join(', ') || '无' }}</div> |
| 203 | + </div> |
| 204 | + </ElAlert> |
| 205 | + |
| 206 | + <ElAlert |
| 207 | + type="success" |
| 208 | + :closable="false" |
| 209 | + > |
| 210 | + <template #title> |
| 211 | + 说明 |
| 212 | + </template> |
| 213 | + <ul style="margin: 4px 0; padding-left: 18px"> |
| 214 | + <li>singleSelect 为真时,每次选择前会清空已有选择</li> |
| 215 | + <li> |
| 216 | + selectable 控制行是否可选;示例中标记为“不可选”的行将禁用勾选 |
| 217 | + </li> |
| 218 | + <li> |
| 219 | + toggleSelection(row, selected?, ignoreSelectable?) |
| 220 | + 支持忽略可选逻辑进行编程式切换 |
| 221 | + </li> |
| 222 | + </ul> |
| 223 | + </ElAlert> |
| 224 | + </ElSpace> |
| 225 | + </div> |
| 226 | + </SectionMain> |
| 227 | + </SectionLayout> |
| 228 | +</template> |
0 commit comments