|
53 | 53 | <Icon class="mr-5px" icon="ep:plus" />
|
54 | 54 | 新增
|
55 | 55 | </el-button>
|
| 56 | + <el-button plain type="danger" @click="toggleExpandAll"> |
| 57 | + <Icon class="mr-5px" icon="ep:sort" /> |
| 58 | + 展开/折叠 |
| 59 | + </el-button> |
56 | 60 | <el-button plain @click="refreshMenu">
|
57 | 61 | <Icon class="mr-5px" icon="ep:refresh" />
|
58 | 62 | 刷新菜单缓存
|
|
63 | 67 |
|
64 | 68 | <!-- 列表 -->
|
65 | 69 | <ContentWrap>
|
66 |
| - <div style="height: 700px"> |
67 |
| - <!-- AutoResizer 自动调节大小 --> |
68 |
| - <el-auto-resizer> |
69 |
| - <template #default="{ height, width }"> |
70 |
| - <!-- Virtualized Table 虚拟化表格:高性能,解决表格在大数据量下的卡顿问题 --> |
71 |
| - <el-table-v2 |
72 |
| - v-loading="loading" |
73 |
| - :columns="columns" |
74 |
| - :data="list" |
75 |
| - :width="width" |
76 |
| - :height="height" |
77 |
| - expand-column-key="name" |
78 |
| - /> |
79 |
| - </template> |
80 |
| - </el-auto-resizer> |
81 |
| - </div> |
| 70 | + <el-auto-resizer> |
| 71 | + <template #default="{ width }"> |
| 72 | + <el-table-v2 |
| 73 | + v-model:expanded-row-keys="expandedRowKeys" |
| 74 | + :columns="columns" |
| 75 | + :data="list" |
| 76 | + :expand-column-key="columns[0].key" |
| 77 | + :height="1000" |
| 78 | + :width="width" |
| 79 | + fixed |
| 80 | + row-key="id" |
| 81 | + /> |
| 82 | + </template> |
| 83 | + </el-auto-resizer> |
82 | 84 | </ContentWrap>
|
83 | 85 |
|
84 | 86 | <!-- 表单弹窗:添加/修改 -->
|
85 | 87 | <MenuForm ref="formRef" @success="getList" />
|
86 | 88 | </template>
|
87 |
| -<script lang="ts" setup> |
| 89 | +<script lang="tsx" setup> |
88 | 90 | import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
89 | 91 | import { handleTree } from '@/utils/tree'
|
90 | 92 | import * as MenuApi from '@/api/system/menu'
|
91 | 93 | import { MenuVO } from '@/api/system/menu'
|
92 | 94 | import MenuForm from './MenuForm.vue'
|
93 |
| -import { CACHE_KEY, useCache } from '@/hooks/web/useCache' |
94 |
| -import { h } from 'vue' |
95 |
| -import { Column, ElButton } from 'element-plus' |
| 95 | +import DictTag from '@/components/DictTag/src/DictTag.vue' |
96 | 96 | import { Icon } from '@/components/Icon'
|
97 |
| -import { hasPermission } from '@/directives/permission/hasPermi' |
| 97 | +import { ElButton, TableV2FixedDir } from 'element-plus' |
| 98 | +import { checkPermi } from '@/utils/permission' |
98 | 99 | import { CommonStatusEnum } from '@/utils/constants'
|
| 100 | +import { CACHE_KEY, useCache } from '@/hooks/web/useCache' |
99 | 101 |
|
100 | 102 | defineOptions({ name: 'SystemMenu' })
|
101 | 103 |
|
102 |
| -const { wsCache } = useCache() |
103 |
| -const { t } = useI18n() // 国际化 |
104 |
| -const message = useMessage() // 消息弹窗 |
105 |
| -
|
106 |
| -// 表格的 column 字段 |
107 |
| -const columns: Column[] = [ |
| 104 | +// 虚拟列表表格 |
| 105 | +const columns = [ |
108 | 106 | {
|
109 |
| - dataKey: 'name', |
| 107 | + key: 'name', |
110 | 108 | title: '菜单名称',
|
111 |
| - width: 250 |
| 109 | + dataKey: 'name', |
| 110 | + width: 250, |
| 111 | + fixed: TableV2FixedDir.LEFT |
112 | 112 | },
|
113 | 113 | {
|
114 |
| - dataKey: 'icon', |
| 114 | + key: 'icon', |
115 | 115 | title: '图标',
|
116 |
| - width: 150, |
117 |
| - cellRenderer: ({ rowData }) => { |
118 |
| - return h(Icon, { |
119 |
| - icon: rowData.icon |
120 |
| - }) |
121 |
| - } |
| 116 | + dataKey: 'icon', |
| 117 | + width: 100, |
| 118 | + align: 'center', |
| 119 | + cellRenderer: ({ cellData: icon }) => <Icon icon={icon} /> |
122 | 120 | },
|
123 | 121 | {
|
124 |
| - dataKey: 'sort', |
| 122 | + key: 'sort', |
125 | 123 | title: '排序',
|
126 |
| - width: 100 |
| 124 | + dataKey: 'sort', |
| 125 | + width: 60 |
127 | 126 | },
|
128 | 127 | {
|
129 |
| - dataKey: 'permission', |
| 128 | + key: 'permission', |
130 | 129 | title: '权限标识',
|
131 |
| - width: 240 |
| 130 | + dataKey: 'permission', |
| 131 | + width: 300 |
132 | 132 | },
|
133 | 133 | {
|
134 |
| - dataKey: 'component', |
| 134 | + key: 'component', |
135 | 135 | title: '组件路径',
|
136 |
| - width: 240 |
| 136 | + dataKey: 'component', |
| 137 | + width: 500 |
137 | 138 | },
|
138 | 139 | {
|
139 |
| - dataKey: 'componentName', |
| 140 | + key: 'componentName', |
140 | 141 | title: '组件名称',
|
141 |
| - width: 240 |
| 142 | + dataKey: 'componentName', |
| 143 | + width: 200 |
142 | 144 | },
|
143 | 145 | {
|
144 |
| - dataKey: 'status', |
| 146 | + key: 'status', |
145 | 147 | title: '状态',
|
146 |
| - width: 160, |
| 148 | + dataKey: 'status', |
| 149 | + width: 60, |
| 150 | + fixed: TableV2FixedDir.RIGHT, |
147 | 151 | cellRenderer: ({ rowData }) => {
|
148 |
| - return h(ElSwitch, { |
149 |
| - modelValue: rowData.status, |
150 |
| - activeValue: CommonStatusEnum.ENABLE, |
151 |
| - inactiveValue: CommonStatusEnum.DISABLE, |
152 |
| - loading: menuStatusUpdating.value[rowData.id], |
153 |
| - disabled: !hasPermission(['system:menu:update']), |
154 |
| - onChange: (val) => handleStatusChanged(rowData, val as number) |
155 |
| - }) |
| 152 | + // 检查权限 |
| 153 | + if (!checkPermi(['system:menu:update'])) { |
| 154 | + return <DictTag type={DICT_TYPE.COMMON_STATUS} value={rowData.status} /> |
| 155 | + } |
| 156 | +
|
| 157 | + // 如果有权限,渲染 ElSwitch |
| 158 | + return ( |
| 159 | + <ElSwitch |
| 160 | + v-model={rowData.status} |
| 161 | + active-value={CommonStatusEnum.ENABLE} |
| 162 | + inactive-value={CommonStatusEnum.DISABLE} |
| 163 | + loading={menuStatusUpdating[rowData.id]} |
| 164 | + class="ml-4px" |
| 165 | + onChange={(val) => handleStatusChanged(rowData, val)} |
| 166 | + /> |
| 167 | + ) |
156 | 168 | }
|
157 | 169 | },
|
158 | 170 | {
|
159 |
| - dataKey: 'operation', |
| 171 | + key: 'operations', |
160 | 172 | title: '操作',
|
161 |
| - width: 200, |
| 173 | + align: 'center', |
| 174 | + width: 160, |
| 175 | + fixed: TableV2FixedDir.RIGHT, |
162 | 176 | cellRenderer: ({ rowData }) => {
|
163 |
| - return h( |
164 |
| - 'div', |
165 |
| - [ |
166 |
| - hasPermission(['system:menu:update']) && |
167 |
| - h( |
168 |
| - ElButton, |
169 |
| - { |
170 |
| - link: true, |
171 |
| - type: 'primary', |
172 |
| - onClick: () => openForm('update', rowData.id) |
173 |
| - }, |
174 |
| - '修改' |
175 |
| - ), |
176 |
| - hasPermission(['system:menu:create']) && |
177 |
| - h( |
178 |
| - ElButton, |
179 |
| - { |
180 |
| - link: true, |
181 |
| - type: 'primary', |
182 |
| - onClick: () => openForm('create', undefined, rowData.id) |
183 |
| - }, |
184 |
| - '新增' |
185 |
| - ), |
186 |
| - hasPermission(['system:menu:delete']) && |
187 |
| - h( |
188 |
| - ElButton, |
189 |
| - { |
190 |
| - link: true, |
191 |
| - type: 'danger', |
192 |
| - onClick: () => handleDelete(rowData.id) |
193 |
| - }, |
194 |
| - '删除' |
195 |
| - ) |
196 |
| - ].filter(Boolean) |
197 |
| - ) |
| 177 | + // 定义按钮列表 |
| 178 | + const buttons = [] |
| 179 | +
|
| 180 | + // 检查权限并添加按钮 |
| 181 | + if (checkPermi(['system:menu:update'])) { |
| 182 | + buttons.push( |
| 183 | + <ElButton key="edit" link type="primary" onClick={() => openForm('update', rowData.id)}> |
| 184 | + 修改 |
| 185 | + </ElButton> |
| 186 | + ) |
| 187 | + } |
| 188 | + if (checkPermi(['system:menu:create'])) { |
| 189 | + buttons.push( |
| 190 | + <ElButton |
| 191 | + key="create" |
| 192 | + link |
| 193 | + type="primary" |
| 194 | + onClick={() => openForm('create', undefined, rowData.id)} |
| 195 | + > |
| 196 | + 新增 |
| 197 | + </ElButton> |
| 198 | + ) |
| 199 | + } |
| 200 | + if (checkPermi(['system:menu:delete'])) { |
| 201 | + buttons.push( |
| 202 | + <ElButton key="delete" link type="danger" onClick={() => handleDelete(rowData.id)}> |
| 203 | + 删除 |
| 204 | + </ElButton> |
| 205 | + ) |
| 206 | + } |
| 207 | + // 如果没有权限,返回 null |
| 208 | + if (buttons.length === 0) { |
| 209 | + return null |
| 210 | + } |
| 211 | + // 渲染按钮列表 |
| 212 | + return <>{buttons}</> |
198 | 213 | }
|
199 | 214 | }
|
200 | 215 | ]
|
| 216 | +
|
| 217 | +const { wsCache } = useCache() |
| 218 | +const { t } = useI18n() // 国际化 |
| 219 | +const message = useMessage() // 消息弹窗 |
| 220 | +
|
201 | 221 | const loading = ref(true) // 列表的加载中
|
202 |
| -const list = ref<any>([]) // 列表的数据 |
| 222 | +const list = ref<any[]>([]) // 列表的数据 |
203 | 223 | const queryParams = reactive({
|
204 | 224 | name: undefined,
|
205 | 225 | status: undefined
|
206 | 226 | })
|
207 | 227 | const queryFormRef = ref() // 搜索的表单
|
| 228 | +const isExpandAll = ref(false) // 是否展开,默认全部折叠 |
| 229 | +const refreshTable = ref(true) // 重新渲染表格状态 |
| 230 | +
|
| 231 | +// 添加展开行控制 |
| 232 | +const expandedRowKeys = ref<number[]>([]) |
208 | 233 |
|
209 | 234 | /** 查询列表 */
|
210 | 235 | const getList = async () => {
|
@@ -234,6 +259,18 @@ const openForm = (type: string, id?: number, parentId?: number) => {
|
234 | 259 | formRef.value.open(type, id, parentId)
|
235 | 260 | }
|
236 | 261 |
|
| 262 | +/** 展开/折叠操作 */ |
| 263 | +const toggleExpandAll = () => { |
| 264 | + if (!isExpandAll.value) { |
| 265 | + // 展开所有 |
| 266 | + expandedRowKeys.value = list.value.map((item) => item.id) |
| 267 | + } else { |
| 268 | + // 折叠所有 |
| 269 | + expandedRowKeys.value = [] |
| 270 | + } |
| 271 | + isExpandAll.value = !isExpandAll.value |
| 272 | +} |
| 273 | +
|
237 | 274 | /** 刷新菜单缓存按钮操作 */
|
238 | 275 | const refreshMenu = async () => {
|
239 | 276 | try {
|
|
0 commit comments