<!-- * Ag Grid 表格树 文件浏览器例子 * https://www.ag-grid.com/vue-data-grid/tree-data/#file-browser-example --> <script setup lang="ts"> import { ref, reactive, onMounted } from 'vue'; import { date } from 'quasar'; import { AgGridVue } from 'ag-grid-vue3'; import { useMessage } from 'src/common/hooks'; import AG_GRID_LOCALE from 'src/config/ag-grid-locale'; import { getData2 } from '../config'; const defaultColDef = { flex: 1, filter: true, sortable: true, resizable: true, suppressMenu: true, }; const { warn } = useMessage(); const gridApi = ref<any>(null); const gridColumnApi = ref<any>(null); const state = reactive({ columnDefs: [ { field: 'dateModified', headerName: '修改时间', minWidth: 250, comparator: (d1: any, d2: any) => { return new Date(d1).getTime() < new Date(d2).getTime() ? -1 : 1; }, valueFormatter: (params: any) => { const timeStamp = new Date(params.value); return date.formatDate(timeStamp, 'YYYY-MM-DD HH:mm:ss'); }, }, { field: 'size', headerName: '大小', aggFunc: 'sum', valueFormatter: (params: any) => { return params.value ? Math.round(params.value * 10) / 10 + ' MB' : '0 MB'; }, }, ], rowData: [] as any, }); const autoGroupColumnDef = reactive({ headerName: '文件', minWidth: 330, cellRendererParams: { checkbox: true, suppressCount: true, innerRenderer: getFileCellRenderer(), }, }); onMounted(() => { setTimeout(() => { state.rowData = getData2(); console.log('rowData', state.rowData); }, 1000); }); function onGridReady(params: any) { gridApi.value = params.api; gridColumnApi.value = params.columnApi; } function getDataPath(data: any) { return data.filePath; } function getRowId(params: any) { return params.data.id; } function addNewGroup() { const newGroupData = [ { id: getNextId(), filePath: ['Music', 'wav', 'hit_' + new Date().getTime() + '.wav'], dateModified: new Date(), size: 58.9, }, ]; gridApi.value.applyTransaction({ add: newGroupData }); } function moveSelectedNodeToTarget(targetRowId: any) { console.log('moveSelectedNodeToTarget', targetRowId); let selectedNode = gridApi.value.getSelectedNodes()[0]; // single selection if (!selectedNode) { warn('没有选择节点!'); return; } let targetNode = gridApi.value.getRowNode(targetRowId); if (!targetNode) { warn('目标文件不存在!'); return; } let invalidMove = selectedNode.key === targetNode.key || isSelectionParentOfTarget(selectedNode, targetNode); if (invalidMove) { warn('无效选择 - 不能是父级或与目标相同!'); return; } let rowsToUpdate = getRowsToUpdate(selectedNode, targetNode.data.filePath); gridApi.value.applyTransaction({ update: rowsToUpdate }); } function removeSelected() { let selectedNode = gridApi.value.getSelectedNodes()[0]; // single selection if (!selectedNode) { warn('没有选择节点!'); return; } gridApi.value.applyTransaction({ remove: getRowsToRemove(selectedNode) }); } function getFileCellRenderer() { class FileCellRenderer { init(params: any) { let tempDiv = document.createElement('div'); let value = params.value; let icon = getFileIcon(params.value); tempDiv.innerHTML = icon ? '<span><i class="' + icon + '" style="font-size: 16px"></i>' + ' ' + '<span class="filename"></span>' + value + '</span>' : value; const that: any = this; that.eGui = tempDiv.firstChild; } getGui() { const that: any = this; return that.eGui; } refresh() { return false; } } return FileCellRenderer; } function getFileIcon(name: any) { return endsWith(name, '.mp3') || endsWith(name, '.wav') ? 'far fa-file-audio' : endsWith(name, '.xls') ? 'far fa-file-excel' : endsWith(name, '.txt') ? 'far fa-file' : endsWith(name, '.pdf') ? 'far fa-file-pdf' : 'far fa-folder'; } function endsWith(str: any, match: any) { let len; if (str == null || !str.length || match == null || !match.length) { return false; } len = str.length; return str.substring(len - match.length, len) === match; } function getNextId() { const _window: any = window; if (!_window.nextId) { _window.nextId = 15; } else { _window.nextId++; } return _window.nextId; } function isSelectionParentOfTarget(selectedNode: any, targetNode: any) { let children = selectedNode.childrenAfterGroup || []; for (let i = 0; i < children.length; i++) { if (targetNode && children[i].key === targetNode.key) return true; isSelectionParentOfTarget(children[i], targetNode); } return false; } function getRowsToUpdate(node: any, parentPath: any) { let res: any = []; let newPath = parentPath.concat([node.key]); if (node.data) { // groups without data, i.e. 'filler groups' don't need path updated // 没有数据的组,即“填充组”不需要更新路径 node.data.filePath = newPath; } let children = node.childrenAfterGroup || []; for (let i = 0; i < children.length; i++) { let updatedChildRowData = getRowsToUpdate(children[i], newPath); res = res.concat(updatedChildRowData); } // ignore nodes that have no data, i.e. 'filler groups' // 忽略没有数据的节点,即“填充组” return node.data ? res.concat([node.data]) : res; } function getRowsToRemove(node: any) { let res: any = []; const children = node.childrenAfterGroup || []; for (let i = 0; i < children.length; i++) { res = res.concat(getRowsToRemove(children[i])); } // ignore nodes that have no data, i.e. 'filler groups' // 忽略没有数据的节点,即“填充组” return node.data ? res.concat([node.data]) : res; } </script> <template> <div class="box"> <div class="btns"> <q-btn color="primary" label="在Music下添加一个新组" no-caps @click="addNewGroup" /> <q-btn color="primary" label="移动到stuff文件夹中" no-caps @click="moveSelectedNodeToTarget('9')" /> <q-btn color="primary" label="删除所选组和它的子组" no-caps @click="removeSelected" /> </div> <!-- rowSelection="multiple":多选,按住control键多选;【control+shift+鼠标点击】可选中多行 animateRows=true:启用行动画 :masterDetail="true" 启用展开的细节网格行 :isRowMaster="isRowMaster" 确定哪一个行该展开 --> <div class="ag-table"> <ag-grid-vue style="height: 500px" class="ag-theme-alpine" :localeText="AG_GRID_LOCALE" :columnDefs="state.columnDefs" :rowData="state.rowData" :defaultColDef="defaultColDef" :animateRows="true" :pagination="true" :paginationPageSize="5" @grid-ready="onGridReady" :treeData="true" :getDataPath="getDataPath" :getRowId="getRowId" :autoGroupColumnDef="autoGroupColumnDef" :groupDefaultExpanded="-1" > </ag-grid-vue> </div> </div> </template> <style lang="scss" scoped> .btns { padding: 10px; display: flex; flex-direction: row; justify-content: flex-start; align-items: center; column-gap: 10px; } .ag-table { padding: 0 10px 10px 10px; } </style>