forked from liweijie/education
1 line
27 KiB
JSON
1 line
27 KiB
JSON
{"remainingRequest":"C:\\Users\\Admin\\Desktop\\education\\frontend\\node_modules\\vue-loader\\lib\\index.js??vue-loader-options!C:\\Users\\Admin\\Desktop\\education\\frontend\\src\\components\\TreePanel\\index.vue?vue&type=style&index=0&id=2b4aa809&lang=scss&scoped=true","dependencies":[{"path":"C:\\Users\\Admin\\Desktop\\education\\frontend\\src\\components\\TreePanel\\index.vue","mtime":1787567871522},{"path":"C:\\Users\\Admin\\Desktop\\education\\frontend\\node_modules\\css-loader\\dist\\cjs.js","mtime":1787309792478},{"path":"C:\\Users\\Admin\\Desktop\\education\\frontend\\node_modules\\vue-loader\\lib\\loaders\\stylePostLoader.js","mtime":1787309794547},{"path":"C:\\Users\\Admin\\Desktop\\education\\frontend\\node_modules\\postcss-loader\\src\\index.js","mtime":1787309793084},{"path":"C:\\Users\\Admin\\Desktop\\education\\frontend\\node_modules\\sass-loader\\dist\\cjs.js","mtime":1787309791955},{"path":"C:\\Users\\Admin\\Desktop\\education\\frontend\\node_modules\\cache-loader\\dist\\cjs.js","mtime":1787309791955},{"path":"C:\\Users\\Admin\\Desktop\\education\\frontend\\node_modules\\vue-loader\\lib\\index.js","mtime":1787309793790}],"contextDependencies":[],"result":[{"type":"Buffer","data":"base64:CgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKDQoudHJlZS1zaWRlYmFyIHsNCiAgZmxleC1zaHJpbms6IDA7DQogIHdpZHRoOiAyMjBweDsNCiAgYmFja2dyb3VuZDogI2ZmZjsNCiAgYm9yZGVyLXJpZ2h0OiAxcHggc29saWQgI2U4ZWFlZDsNCiAgZGlzcGxheTogZmxleDsNCiAgZmxleC1kaXJlY3Rpb246IGNvbHVtbjsNCiAgb3ZlcmZsb3c6IGhpZGRlbjsNCiAgcG9zaXRpb246IHJlbGF0aXZlOw0KICB0cmFuc2l0aW9uOiB3aWR0aCAwLjI1cyBlYXNlOw0KICANCiAgJi5jb2xsYXBzZWQgew0KICAgIHdpZHRoOiA0MnB4Ow0KICB9DQogIA0KICAmLnJlc2l6aW5nIHsNCiAgICB0cmFuc2l0aW9uOiBub25lOw0KICAgIHdpbGwtY2hhbmdlOiB3aWR0aDsNCiAgICANCiAgICAqIHsNCiAgICAgIHBvaW50ZXItZXZlbnRzOiBub25lICFpbXBvcnRhbnQ7DQogICAgfQ0KICB9DQogIA0KICAmLm5vLWluaXRpYWwtdHJhbnNpdGlvbiB7DQogICAgdHJhbnNpdGlvbjogbm9uZTsNCiAgfQ0KfQ0KDQoucmVzaXplLWhhbmRsZSB7DQogIHBvc2l0aW9uOiBhYnNvbHV0ZTsNCiAgdG9wOiAwOw0KICByaWdodDogMDsNCiAgd2lkdGg6IDZweDsNCiAgaGVpZ2h0OiAxMDAlOw0KICBjdXJzb3I6IGNvbC1yZXNpemU7DQogIHotaW5kZXg6IDIwOw0KICBiYWNrZ3JvdW5kOiB0cmFuc3BhcmVudDsNCiAgdHJhbnNpdGlvbjogYmFja2dyb3VuZCAwLjJzOw0KICANCiAgJjpob3ZlciB7DQogICAgYmFja2dyb3VuZDogcmdiYSg2NCwgMTU4LCAyNTUsIDAuMyk7DQogIH0NCiAgDQogICYuYWN0aXZlIHsNCiAgICBiYWNrZ3JvdW5kOiByZ2JhKDY0LCAxNTgsIDI1NSwgMC41KTsNCiAgfQ0KfQ0KDQouY29sbGFwc2UtYnV0dG9uLWNvbnRhaW5lciB7DQogIHBvc2l0aW9uOiBhYnNvbHV0ZTsNCiAgdG9wOiA1MCU7DQogIHJpZ2h0OiAwOw0KICB0cmFuc2Zvcm06IHRyYW5zbGF0ZVkoLTUwJSk7DQogIHotaW5kZXg6IDEwMDsNCiAgZGlzcGxheTogZmxleDsNCiAgYWxpZ24taXRlbXM6IGNlbnRlcjsNCiAganVzdGlmeS1jb250ZW50OiBjZW50ZXI7DQogIHdpZHRoOiAxNXB4Ow0KICBoZWlnaHQ6IDIwcHg7DQogIGJhY2tncm91bmQ6ICNmZmY7DQogIGJvcmRlci1yYWRpdXM6IDAgNHB4IDRweCAwOw0KICBib3gtc2hhZG93OiAwIDFweCAzcHggcmdiYSgwLCAwLCAwLCAwLjEpOw0KICB0cmFuc2l0aW9uOiBhbGwgMC4ycyBlYXNlOw0KICANCiAgLnRyZWUtc2lkZWJhci5jb2xsYXBzZWQgJiB7DQogICAgcmlnaHQ6IDA7DQogICAgYmFja2dyb3VuZDogI2Y3ZjhmYTsNCiAgICBib3JkZXItcmFkaXVzOiAwIDRweCA0cHggMDsNCiAgfQ0KICANCiAgLnRyZWUtc2lkZWJhci5yZXNpemluZyAmIHsNCiAgICBwb2ludGVyLWV2ZW50czogbm9uZTsNCiAgfQ0KfQ0KDQouY29sbGFwc2UtYnV0dG9uIHsNCiAgZm9udC1zaXplOiAxNHB4Ow0KICBjb2xvcjogIzkwOTM5OTsNCiAgY3Vyc29yOiBwb2ludGVyOw0KICBwYWRkaW5nOiA0cHg7DQogIGJvcmRlci1yYWRpdXM6IDRweDsNCiAgdHJhbnNpdGlvbjogYWxsIDAuMnM7DQogIA0KICAmOmhvdmVyIHsNCiAgICBjb2xvcjogIzAwODc1QTsNCiAgICBiYWNrZ3JvdW5kOiAjRTZGNEVBOw0KICB9DQp9DQoNCi50cmVlLWhlYWRlciB7DQogIGRpc3BsYXk6IGZsZXg7DQogIGFsaWduLWl0ZW1zOiBjZW50ZXI7DQogIGp1c3RpZnktY29udGVudDogc3BhY2UtYmV0d2VlbjsNCiAgcGFkZGluZzogMCAxMHB4Ow0KICBoZWlnaHQ6IDQwcHg7DQogIGJvcmRlci1ib3R0b206IDFweCBzb2xpZCAjZThlYWVkOw0KICBiYWNrZ3JvdW5kOiAjZjdmOGZhOw0KICBmbGV4LXNocmluazogMDsNCg0KICAudHJlZS10aXRsZSB7DQogICAgZm9udC1zaXplOiAxM3B4Ow0KICAgIGZvbnQtd2VpZ2h0OiA2MDA7DQogICAgY29sb3I6ICMzMDMxMzM7DQogICAgd2hpdGUtc3BhY2U6IG5vd3JhcDsNCiAgICBvdmVyZmxvdzogaGlkZGVuOw0KICAgIGRpc3BsYXk6IGZsZXg7DQogICAgYWxpZ24taXRlbXM6IGNlbnRlcjsNCiAgICBnYXA6IDVweDsNCg0KICAgIGkgew0KICAgICAgY29sb3I6ICMwMDg3NUE7DQogICAgICBmb250LXNpemU6IDE0cHg7DQogICAgfQ0KICB9DQoNCiAgLnRyZWUtYWN0aW9ucyB7DQogICAgZGlzcGxheTogZmxleDsNCiAgICBhbGlnbi1pdGVtczogY2VudGVyOw0KICAgIGdhcDogNHB4Ow0KICAgIGZsZXgtc2hyaW5rOiAwOw0KICB9DQp9DQoNCi50cmVlLWFjdGlvbi1pY29uIHsNCiAgZm9udC1zaXplOiAxNHB4Ow0KICBjb2xvcjogIzkwOTM5OTsNCiAgY3Vyc29yOiBwb2ludGVyOw0KICBwYWRkaW5nOiA0cHg7DQogIGJvcmRlci1yYWRpdXM6IDRweDsNCiAgdHJhbnNpdGlvbjogYWxsIDAuMnM7DQoNCiAgJjpob3ZlciB7DQogICAgY29sb3I6ICMwMDg3NUE7DQogICAgYmFja2dyb3VuZDogI0U2RjRFQTsNCiAgfQ0KfQ0KDQoudHJlZS1zZWFyY2ggew0KICBwYWRkaW5nOiAxMHB4IDEwcHggNHB4Ow0KICBmbGV4LXNocmluazogMDsNCn0NCg0KLnRyZWUtd3JhcCB7DQogIGZsZXg6IDE7DQogIG92ZXJmbG93LXk6IGF1dG87DQogIHBhZGRpbmc6IDZweCA2cHggMTJweDsNCiAgDQogIC50cmVlLXNpZGViYXIucmVzaXppbmcgJiB7DQogICAgb3ZlcmZsb3c6IGhpZGRlbjsNCiAgfQ0KDQogICY6Oi13ZWJraXQtc2Nyb2xsYmFyIHsNCiAgICB3aWR0aDogNHB4Ow0KICB9DQoNCiAgJjo6LXdlYmtpdC1zY3JvbGxiYXItdGh1bWIgew0KICAgIGJhY2tncm91bmQ6ICNkY2RmZTY7DQogICAgYm9yZGVyLXJhZGl1czogNHB4Ow0KICAgIA0KICAgICY6aG92ZXIgew0KICAgICAgYmFja2dyb3VuZDogI2MwYzRjYzsNCiAgICB9DQogIH0NCg0KICA6OnYtZGVlcCAuZWwtdHJlZS1ub2RlX19jb250ZW50IHsNCiAgICBoZWlnaHQ6IDMycHg7DQogICAgYm9yZGVyLXJhZGl1czogNHB4Ow0KICAgIG1hcmdpbi1ib3R0b206IDFweDsNCg0KICAgICY6aG92ZXIgew0KICAgICAgYmFja2dyb3VuZDogI2YwZjdmZjsNCiAgICB9DQogIH0NCg0KICA6OnYtZGVlcCAuZWwtdHJlZS1ub2RlLmlzLWN1cnJlbnQgPiAuZWwtdHJlZS1ub2RlX19jb250ZW50IHsNCiAgICBiYWNrZ3JvdW5kOiAjRTNGMkU5Ow0KICAgIGNvbG9yOiAjMDA4NzVBOw0KICAgIGZvbnQtd2VpZ2h0OiA2MDA7DQoNCiAgICAubm9kZS1pY29uIHsNCiAgICAgIGNvbG9yOiAjMDA4NzVBICFpbXBvcnRhbnQ7DQogICAgfQ0KICB9DQp9DQoNCi50cmVlLW5vZGUgew0KICBkaXNwbGF5OiBmbGV4Ow0KICBhbGlnbi1pdGVtczogY2VudGVyOw0KICBnYXA6IDVweDsNCiAgZm9udC1zaXplOiAxM3B4Ow0KICBvdmVyZmxvdzogaGlkZGVuOw0KDQogIC5ub2RlLWljb24gew0KICAgIGZvbnQtc2l6ZTogMTRweDsNCiAgICBjb2xvcjogI2Y1YTYyMzsNCiAgICBmbGV4LXNocmluazogMDsNCiAgfQ0KDQogIC5ub2RlLWxhYmVsIHsNCiAgICBvdmVyZmxvdzogaGlkZGVuOw0KICAgIHRleHQtb3ZlcmZsb3c6IGVsbGlwc2lzOw0KICAgIHdoaXRlLXNwYWNlOiBub3dyYXA7DQogIH0NCn0NCg0KOjp2LWRlZXAgLmVsLWljb24tZG9jdW1lbnQubm9kZS1pY29uIHsNCiAgY29sb3I6ICM5MDkzOTkgIWltcG9ydGFudDsNCn0NCg=="},{"version":3,"sources":["index.vue"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmfA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA","file":"index.vue","sourceRoot":"src/components/TreePanel","sourcesContent":["<template>\r\n <div class=\"tree-sidebar\" :class=\"{ collapsed: collapsed, resizing: isResizing, 'no-initial-transition': isLoadingFromStorage}\" :style=\"{ width: sidebarWidth + 'px' }\">\r\n <!-- 右侧拖动条 -->\r\n <div v-if=\"!collapsed\" class=\"resize-handle\" @mousedown=\"startResize\" @touchstart=\"startResize\" :class=\"{ active: isResizing }\" />\r\n <div class=\"tree-header\">\r\n <span class=\"tree-title\" v-show=\"!collapsed\">\r\n <i :class=\"titleIconClass\"></i> {{ title }}\r\n </span>\r\n <div class=\"tree-actions\" v-show=\"!collapsed\">\r\n <el-tooltip :content=\"isExpandedAll ? '收起全部' : '展开全部'\" placement=\"right\">\r\n <i class=\"tree-action-icon\" :class=\"isExpandedAll ? 'el-icon-arrow-down' : 'el-icon-arrow-up'\" @click=\"toggleExpandAll\" />\r\n </el-tooltip>\r\n <el-tooltip content=\"刷新\" placement=\"right\">\r\n <i class=\"tree-action-icon el-icon-refresh\" @click=\"handleRefresh\" />\r\n </el-tooltip>\r\n <slot name=\"actions\"></slot>\r\n </div>\r\n </div>\r\n \r\n <!-- 侧边栏展开/收起按钮 -->\r\n <div class=\"collapse-button-container\">\r\n <el-tooltip :content=\"collapsed ? '展开' : '收起'\" placement=\"right\">\r\n <i class=\"collapse-button\" :class=\"collapsed ? 'el-icon-d-arrow-right' : 'el-icon-d-arrow-left'\" @click=\"toggleCollapsed\" />\r\n </el-tooltip>\r\n </div>\r\n\r\n <div class=\"tree-search\" v-show=\"!collapsed\" v-if=\"showSearch\">\r\n <el-input v-model=\"searchKeyword\" :placeholder=\"searchPlaceholder\" clearable size=\"small\" prefix-icon=\"el-icon-search\" @input=\"onSearch\" />\r\n </div>\r\n\r\n <div class=\"tree-wrap\" v-show=\"!collapsed\">\r\n <el-tree \r\n ref=\"treeRef\" \r\n :data=\"treeData\" \r\n :props=\"treeProps\" \r\n :expand-on-click-node=\"expandOnClickNode\"\r\n :filter-node-method=\"filterNodeMethod\"\r\n :default-expand-all=\"defaultExpandAll\"\r\n :default-expanded-keys=\"defaultExpandedKeys\"\r\n :node-key=\"nodeKey\"\r\n :check-strictly=\"checkStrictly\"\r\n :show-checkbox=\"showCheckbox\"\r\n @node-click=\"onNodeClick\"\r\n @check=\"onCheck\"\r\n @node-expand=\"onNodeExpand\"\r\n @node-collapse=\"onNodeCollapse\"\r\n >\r\n <span class=\"tree-node\" slot-scope=\"{ node, data }\">\r\n <slot name=\"node\" :node=\"node\" :data=\"data\">\r\n <i :class=\"data.children && data.children.length ? 'el-icon-folder' : 'el-icon-document'\" class=\"node-icon\" />\r\n <span class=\"node-label\" :title=\"node.label\">{{ node.label }}</span>\r\n </slot>\r\n </span>\r\n </el-tree>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script>\r\nexport default {\r\n name: \"TreeSidebar\",\r\n props: {\r\n // 树形数据\r\n treeData: {\r\n type: Array,\r\n default: () => []\r\n },\r\n // 标题\r\n title: {\r\n type: String,\r\n default: '树形结构'\r\n },\r\n // 标题图标类名\r\n titleIconClass: {\r\n type: String,\r\n default: 'el-icon-office-building'\r\n },\r\n // 是否显示搜索框\r\n showSearch: {\r\n type: Boolean,\r\n default: true\r\n },\r\n // 搜索框占位符\r\n searchPlaceholder: {\r\n type: String,\r\n default: '请输入名称'\r\n },\r\n // 是否默认收起侧边栏\r\n defaultCollapsed: {\r\n type: Boolean,\r\n default: false\r\n },\r\n // 树配置项\r\n treeProps: {\r\n type: Object,\r\n default: () => ({\r\n children: \"children\",\r\n label: \"label\"\r\n })\r\n },\r\n // 节点唯一标识字段\r\n nodeKey: {\r\n type: String,\r\n default: 'id'\r\n },\r\n // 是否在点击节点时展开或收起\r\n expandOnClickNode: {\r\n type: Boolean,\r\n default: false\r\n },\r\n // 是否显示复选框\r\n showCheckbox: {\r\n type: Boolean,\r\n default: false\r\n },\r\n // 是否严格的遵循父子不互相关联\r\n checkStrictly: {\r\n type: Boolean,\r\n default: false\r\n },\r\n // 是否默认展开所有节点\r\n defaultExpandAll: {\r\n type: Boolean,\r\n default: false\r\n },\r\n // 默认展开的节点的key数组\r\n defaultExpandedKeys: {\r\n type: Array,\r\n default: () => []\r\n },\r\n // 默认宽度\r\n defaultWidth: {\r\n type: Number,\r\n default: 220\r\n },\r\n // 收起时的宽度\r\n collapsedWidth: {\r\n type: Number,\r\n default: 20\r\n },\r\n // 最小宽度\r\n minWidth: {\r\n type: Number,\r\n default: 180\r\n },\r\n // 最大宽度\r\n maxWidth: {\r\n type: Number,\r\n default: 400\r\n },\r\n // 本地存储的宽度key\r\n storageKey: {\r\n type: String,\r\n default: 'tree-sidebar-width'\r\n },\r\n // 是否启用本地存储宽度\r\n enableStorage: {\r\n type: Boolean,\r\n default: true\r\n },\r\n // 自定义过滤方法\r\n filterMethod: {\r\n type: Function,\r\n default: null\r\n }\r\n },\r\n data() {\r\n return {\r\n searchKeyword: \"\",\r\n collapsed: this.defaultCollapsed,\r\n sidebarWidth: this.defaultCollapsed ? this.collapsedWidth : this.defaultWidth,\r\n isResizing: false,\r\n startX: 0,\r\n startWidth: 0,\r\n saveWidthTimer: null,\r\n rafId: null,\r\n isLoadingFromStorage: false,\r\n expandedAll: this.defaultExpandAll\r\n };\r\n },\r\n computed: {\r\n // 计算当前是否全部展开\r\n isExpandedAll: {\r\n get() {\r\n return this.expandedAll;\r\n },\r\n set(val) {\r\n this.expandedAll = val;\r\n }\r\n }\r\n },\r\n watch: {\r\n collapsed(newVal, oldVal) {\r\n if (newVal !== oldVal) {\r\n this.handleCollapseChange(newVal);\r\n this.$emit(\"collapsed-change\", newVal);\r\n }\r\n },\r\n // 监听内部展开状态变化,触发实际树的展开/收起\r\n expandedAll(newVal) {\r\n this.$nextTick(() => {\r\n if (newVal) {\r\n this.expandAllNodes();\r\n } else {\r\n this.collapseAllNodes();\r\n }\r\n });\r\n this.$emit(\"expanded-all-change\", newVal);\r\n },\r\n // 监听搜索关键词\r\n searchKeyword(val) {\r\n if (this.$refs.treeRef) {\r\n this.$refs.treeRef.filter(val);\r\n this.$emit(\"search\", val);\r\n }\r\n }\r\n },\r\n mounted() {\r\n this.isLoadingFromStorage = true\r\n if (!this.collapsed && this.enableStorage) {\r\n const savedWidth = this.getSavedWidth();\r\n if (savedWidth !== null) {\r\n this.sidebarWidth = savedWidth;\r\n }\r\n }\r\n this.$nextTick(() => {\r\n this.isLoadingFromStorage = false\r\n })\r\n if (this.expandedAll) {\r\n this.$nextTick(() => {\r\n this.expandAllNodes();\r\n });\r\n }\r\n },\r\n beforeDestroy() {\r\n this.cleanup();\r\n },\r\n methods: {\r\n // 节点过滤方法\r\n filterNodeMethod(value, data) {\r\n if (this.filterMethod) {\r\n return this.filterMethod(value, data);\r\n }\r\n if (!value) return true;\r\n return data.label && data.label.indexOf(value) !== -1;\r\n },\r\n // 清理定时器和动画帧\r\n cleanup() {\r\n if (this.rafId) {\r\n cancelAnimationFrame(this.rafId);\r\n this.rafId = null;\r\n }\r\n if (this.saveWidthTimer) {\r\n clearTimeout(this.saveWidthTimer);\r\n this.saveWidthTimer = null;\r\n }\r\n },\r\n // 处理收起/展开状态变化\r\n handleCollapseChange(isCollapsed) {\r\n if (isCollapsed) {\r\n this.saveWidthToStorage();\r\n this.sidebarWidth = this.collapsedWidth;\r\n } else {\r\n const savedWidth = this.getSavedWidth();\r\n this.sidebarWidth = savedWidth !== null ? savedWidth : this.defaultWidth;\r\n }\r\n },\r\n // 获取保存的宽度\r\n getSavedWidth() {\r\n if (!this.enableStorage) {\r\n return null;\r\n }\r\n try {\r\n const savedWidth = localStorage.getItem(this.storageKey);\r\n if (savedWidth) {\r\n const width = parseInt(savedWidth, 10);\r\n if (!isNaN(width) && width >= this.minWidth && width <= this.maxWidth) {\r\n return width;\r\n }\r\n }\r\n } catch (error) {\r\n console.warn(`Failed to load sidebar width from storage with key ${this.storageKey}:`, error);\r\n }\r\n return null;\r\n },\r\n // 保存宽度到本地存储\r\n saveWidthToStorage() {\r\n if (this.collapsed || !this.enableStorage) return;\r\n try {\r\n localStorage.setItem(this.storageKey, this.sidebarWidth.toString());\r\n } catch (error) {\r\n console.warn(`Failed to save sidebar width to storage with key ${this.storageKey}:`, error);\r\n }\r\n },\r\n // 切换侧边栏收起/展开状态\r\n toggleCollapsed() {\r\n this.collapsed = !this.collapsed;\r\n },\r\n // 切换展开/折叠所有节点\r\n toggleExpandAll() {\r\n this.isExpandedAll = !this.isExpandedAll;\r\n },\r\n // 展开所有节点\r\n expandAllNodes() {\r\n if (!this.$refs.treeRef) return;\r\n const allNodes = this.getAllNodes(this.$refs.treeRef.root);\r\n allNodes.forEach(node => {\r\n if (node.expanded !== undefined && !node.expanded) {\r\n node.expanded = true;\r\n }\r\n });\r\n },\r\n // 获取所有节点\r\n getAllNodes(rootNode) {\r\n const nodes = [];\r\n const traverse = (node) => {\r\n if (!node) return;\r\n nodes.push(node);\r\n if (node.childNodes && node.childNodes.length) {\r\n node.childNodes.forEach(child => traverse(child));\r\n }\r\n };\r\n traverse(rootNode);\r\n return nodes;\r\n },\r\n // 收起所有节点\r\n collapseAllNodes() {\r\n if (!this.$refs.treeRef) return;\r\n const allNodes = this.getAllNodes(this.$refs.treeRef.root);\r\n allNodes.forEach(node => {\r\n if (node.expanded !== undefined && node.expanded) {\r\n node.expanded = false;\r\n }\r\n });\r\n },\r\n // 处理刷新操作\r\n handleRefresh() {\r\n this.$emit(\"refresh\");\r\n },\r\n // 节点点击事件\r\n onNodeClick(data, node, e) {\r\n this.$emit(\"node-click\", data, node, e);\r\n },\r\n // 复选框选中事件\r\n onCheck(data, checkedInfo) {\r\n this.$emit(\"check\", data, checkedInfo);\r\n },\r\n // 节点展开事件\r\n onNodeExpand(data, node, e) {\r\n this.$emit(\"node-expand\", data, node, e);\r\n },\r\n // 节点折叠事件\r\n onNodeCollapse(data, node, e) {\r\n this.$emit(\"node-collapse\", data, node, e);\r\n },\r\n // 搜索处理\r\n onSearch() {\r\n // 搜索逻辑已在 watch 中处理\r\n },\r\n // 设置当前选中的节点\r\n setCurrentKey(key) {\r\n if (this.$refs.treeRef) {\r\n this.$refs.treeRef.setCurrentKey(key);\r\n }\r\n },\r\n // 获取当前选中的节点\r\n getCurrentNode() {\r\n if (this.$refs.treeRef) {\r\n return this.$refs.treeRef.getCurrentNode();\r\n }\r\n return null;\r\n },\r\n // 获取当前选中的节点的key\r\n getCurrentKey() {\r\n if (this.$refs.treeRef) {\r\n return this.$refs.treeRef.getCurrentKey();\r\n }\r\n return null;\r\n },\r\n // 设置选中的节点keys(复选框)\r\n setCheckedKeys(keys) {\r\n if (this.$refs.treeRef && this.showCheckbox) {\r\n this.$refs.treeRef.setCheckedKeys(keys);\r\n }\r\n },\r\n // 获取选中的节点keys(复选框)\r\n getCheckedKeys() {\r\n if (this.$refs.treeRef && this.showCheckbox) {\r\n return this.$refs.treeRef.getCheckedKeys();\r\n }\r\n return [];\r\n },\r\n // 获取选中的节点(复选框)\r\n getCheckedNodes() {\r\n if (this.$refs.treeRef && this.showCheckbox) {\r\n return this.$refs.treeRef.getCheckedNodes();\r\n }\r\n return [];\r\n },\r\n // 清空搜索\r\n clearSearch() {\r\n this.searchKeyword = \"\";\r\n if (this.$refs.treeRef) {\r\n this.$refs.treeRef.filter(\"\");\r\n }\r\n },\r\n // 过滤树\r\n filter(value) {\r\n this.searchKeyword = value;\r\n },\r\n // 开始调整大小\r\n startResize(e) {\r\n e.preventDefault();\r\n e.stopPropagation();\r\n this.isResizing = true;\r\n this.startX = e.type === 'mousedown' ? e.clientX : e.touches[0].clientX;\r\n this.startWidth = this.sidebarWidth;\r\n \r\n if (e.type === 'mousedown') {\r\n document.addEventListener('mousemove', this.handleResizeMove);\r\n document.addEventListener('mouseup', this.stopResize);\r\n } else {\r\n document.addEventListener('touchmove', this.handleResizeMove, { passive: false });\r\n document.addEventListener('touchend', this.stopResize);\r\n }\r\n this.disableUserSelect();\r\n },\r\n // 处理调整大小移动\r\n handleResizeMove(e) {\r\n if (!this.isResizing) return;\r\n if (this.rafId) {\r\n cancelAnimationFrame(this.rafId);\r\n }\r\n this.rafId = requestAnimationFrame(() => {\r\n e.preventDefault();\r\n e.stopPropagation();\r\n const clientX = e.type === 'mousemove' ? e.clientX : e.touches[0].clientX;\r\n const deltaX = clientX - this.startX;\r\n const newWidth = this.startWidth + deltaX;\r\n const clampedWidth = Math.max(this.minWidth, Math.min(this.maxWidth, newWidth));\r\n if (Math.abs(clampedWidth - this.sidebarWidth) >= 1) {\r\n this.sidebarWidth = clampedWidth;\r\n }\r\n });\r\n },\r\n // 停止调整大小\r\n stopResize() {\r\n if (!this.isResizing) return;\r\n this.isResizing = false;\r\n if (this.rafId) {\r\n cancelAnimationFrame(this.rafId);\r\n this.rafId = null;\r\n }\r\n this.startX = 0;\r\n this.startWidth = 0;\r\n document.removeEventListener('mousemove', this.handleResizeMove);\r\n document.removeEventListener('mouseup', this.stopResize);\r\n document.removeEventListener('touchmove', this.handleResizeMove);\r\n document.removeEventListener('touchend', this.stopResize);\r\n this.enableUserSelect();\r\n this.saveWidthToStorage();\r\n },\r\n // 禁用用户选择\r\n disableUserSelect() {\r\n document.body.style.userSelect = 'none';\r\n document.body.style.webkitUserSelect = 'none';\r\n document.body.style.mozUserSelect = 'none';\r\n document.body.style.msUserSelect = 'none';\r\n },\r\n // 启用用户选择\r\n enableUserSelect() {\r\n document.body.style.userSelect = '';\r\n document.body.style.webkitUserSelect = '';\r\n document.body.style.mozUserSelect = '';\r\n document.body.style.msUserSelect = '';\r\n },\r\n // 重置宽度到默认值\r\n resetWidth() {\r\n this.sidebarWidth = this.defaultWidth;\r\n this.saveWidthToStorage();\r\n },\r\n // 获取当前宽度\r\n getCurrentWidth() {\r\n return this.sidebarWidth;\r\n },\r\n // 设置宽度\r\n setWidth(width) {\r\n if (typeof width === 'number' && width >= this.minWidth && width <= this.maxWidth) {\r\n this.sidebarWidth = width;\r\n if (!this.collapsed) {\r\n this.saveWidthToStorage();\r\n }\r\n }\r\n }\r\n }\r\n};\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n.tree-sidebar {\r\n flex-shrink: 0;\r\n width: 220px;\r\n background: #fff;\r\n border-right: 1px solid #e8eaed;\r\n display: flex;\r\n flex-direction: column;\r\n overflow: hidden;\r\n position: relative;\r\n transition: width 0.25s ease;\r\n \r\n &.collapsed {\r\n width: 42px;\r\n }\r\n \r\n &.resizing {\r\n transition: none;\r\n will-change: width;\r\n \r\n * {\r\n pointer-events: none !important;\r\n }\r\n }\r\n \r\n &.no-initial-transition {\r\n transition: none;\r\n }\r\n}\r\n\r\n.resize-handle {\r\n position: absolute;\r\n top: 0;\r\n right: 0;\r\n width: 6px;\r\n height: 100%;\r\n cursor: col-resize;\r\n z-index: 20;\r\n background: transparent;\r\n transition: background 0.2s;\r\n \r\n &:hover {\r\n background: rgba(64, 158, 255, 0.3);\r\n }\r\n \r\n &.active {\r\n background: rgba(64, 158, 255, 0.5);\r\n }\r\n}\r\n\r\n.collapse-button-container {\r\n position: absolute;\r\n top: 50%;\r\n right: 0;\r\n transform: translateY(-50%);\r\n z-index: 100;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 15px;\r\n height: 20px;\r\n background: #fff;\r\n border-radius: 0 4px 4px 0;\r\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\r\n transition: all 0.2s ease;\r\n \r\n .tree-sidebar.collapsed & {\r\n right: 0;\r\n background: #f7f8fa;\r\n border-radius: 0 4px 4px 0;\r\n }\r\n \r\n .tree-sidebar.resizing & {\r\n pointer-events: none;\r\n }\r\n}\r\n\r\n.collapse-button {\r\n font-size: 14px;\r\n color: #909399;\r\n cursor: pointer;\r\n padding: 4px;\r\n border-radius: 4px;\r\n transition: all 0.2s;\r\n \r\n &:hover {\r\n color: #00875A;\r\n background: #E6F4EA;\r\n }\r\n}\r\n\r\n.tree-header {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n padding: 0 10px;\r\n height: 40px;\r\n border-bottom: 1px solid #e8eaed;\r\n background: #f7f8fa;\r\n flex-shrink: 0;\r\n\r\n .tree-title {\r\n font-size: 13px;\r\n font-weight: 600;\r\n color: #303133;\r\n white-space: nowrap;\r\n overflow: hidden;\r\n display: flex;\r\n align-items: center;\r\n gap: 5px;\r\n\r\n i {\r\n color: #00875A;\r\n font-size: 14px;\r\n }\r\n }\r\n\r\n .tree-actions {\r\n display: flex;\r\n align-items: center;\r\n gap: 4px;\r\n flex-shrink: 0;\r\n }\r\n}\r\n\r\n.tree-action-icon {\r\n font-size: 14px;\r\n color: #909399;\r\n cursor: pointer;\r\n padding: 4px;\r\n border-radius: 4px;\r\n transition: all 0.2s;\r\n\r\n &:hover {\r\n color: #00875A;\r\n background: #E6F4EA;\r\n }\r\n}\r\n\r\n.tree-search {\r\n padding: 10px 10px 4px;\r\n flex-shrink: 0;\r\n}\r\n\r\n.tree-wrap {\r\n flex: 1;\r\n overflow-y: auto;\r\n padding: 6px 6px 12px;\r\n \r\n .tree-sidebar.resizing & {\r\n overflow: hidden;\r\n }\r\n\r\n &::-webkit-scrollbar {\r\n width: 4px;\r\n }\r\n\r\n &::-webkit-scrollbar-thumb {\r\n background: #dcdfe6;\r\n border-radius: 4px;\r\n \r\n &:hover {\r\n background: #c0c4cc;\r\n }\r\n }\r\n\r\n ::v-deep .el-tree-node__content {\r\n height: 32px;\r\n border-radius: 4px;\r\n margin-bottom: 1px;\r\n\r\n &:hover {\r\n background: #f0f7ff;\r\n }\r\n }\r\n\r\n ::v-deep .el-tree-node.is-current > .el-tree-node__content {\r\n background: #E3F2E9;\r\n color: #00875A;\r\n font-weight: 600;\r\n\r\n .node-icon {\r\n color: #00875A !important;\r\n }\r\n }\r\n}\r\n\r\n.tree-node {\r\n display: flex;\r\n align-items: center;\r\n gap: 5px;\r\n font-size: 13px;\r\n overflow: hidden;\r\n\r\n .node-icon {\r\n font-size: 14px;\r\n color: #f5a623;\r\n flex-shrink: 0;\r\n }\r\n\r\n .node-label {\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n }\r\n}\r\n\r\n::v-deep .el-icon-document.node-icon {\r\n color: #909399 !important;\r\n}\r\n</style>"]}]} |