From 34a73011724372504e334c91b11c6659dee4cdb6 Mon Sep 17 00:00:00 2001 From: unknown <356615479@qq.com> Date: Wed, 23 Apr 2025 13:56:22 +0800 Subject: [PATCH] 'cff' --- cff/.idea/.gitignore | 8 + cff/.idea/cff.iml | 9 + cff/.idea/misc.xml | 6 + cff/.idea/modules.xml | 8 + cff/.idea/vcs.xml | 6 + cff/echartsTheme.js | 515 ++++ cff/miniAdmin.js | 450 +++ cff/miniMenu.js | 234 ++ cff/miniPage.js | 584 ++++ cff/miniTheme.js | 576 ++++ cff/miniTongji.js | 47 + cff/treetable.js | 308 +++ cff/ztree.js | 6148 +++++++++++++++++++++++++++++++++++++++++ 13 files changed, 8899 insertions(+) create mode 100644 cff/.idea/.gitignore create mode 100644 cff/.idea/cff.iml create mode 100644 cff/.idea/misc.xml create mode 100644 cff/.idea/modules.xml create mode 100644 cff/.idea/vcs.xml create mode 100644 cff/echartsTheme.js create mode 100644 cff/miniAdmin.js create mode 100644 cff/miniMenu.js create mode 100644 cff/miniPage.js create mode 100644 cff/miniTheme.js create mode 100644 cff/miniTongji.js create mode 100644 cff/treetable.js create mode 100644 cff/ztree.js diff --git a/cff/.idea/.gitignore b/cff/.idea/.gitignore new file mode 100644 index 0000000..35410ca --- /dev/null +++ b/cff/.idea/.gitignore @@ -0,0 +1,8 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml +# 基于编辑器的 HTTP 客户端请求 +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/cff/.idea/cff.iml b/cff/.idea/cff.iml new file mode 100644 index 0000000..d6ebd48 --- /dev/null +++ b/cff/.idea/cff.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/cff/.idea/misc.xml b/cff/.idea/misc.xml new file mode 100644 index 0000000..639900d --- /dev/null +++ b/cff/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/cff/.idea/modules.xml b/cff/.idea/modules.xml new file mode 100644 index 0000000..cc04635 --- /dev/null +++ b/cff/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/cff/.idea/vcs.xml b/cff/.idea/vcs.xml new file mode 100644 index 0000000..6c0b863 --- /dev/null +++ b/cff/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/cff/echartsTheme.js b/cff/echartsTheme.js new file mode 100644 index 0000000..3254a73 --- /dev/null +++ b/cff/echartsTheme.js @@ -0,0 +1,515 @@ +layui.define(function(exports) { // 定义一个模块,使用layui的模块化机制 + exports('echartsTheme', // 导出名为'echartsTheme'的模块 + { + "color": [ // 设置图表的颜色数组 + "#3fb1e3", // 蓝色 + "#6be6c1", // 绿色 + "#626c91", // 深蓝色 + "#a0a7e6", // 浅蓝色 + "#c4ebad", // 浅绿色 + "#96dee8" // 浅蓝色 + ], + "backgroundColor": "rgba(252,252,252,0)", // 设置背景颜色为透明白色 + "textStyle": {}, // 文本样式,此处为空对象表示默认样式 + "title": { // 标题样式配置 + "textStyle": { // 主标题样式 + "color": "#666666" // 主标题颜色为灰色 + }, + "subtextStyle": { // 副标题样式 + "color": "#999999" // 副标题颜色为浅灰色 + } + }, + "line": { + // 设置线条的样式 + "itemStyle": { + // 正常状态下的样式 + "normal": { + // 边框宽度为3 + "borderWidth": "3" + } + }, + // 设置线条的样式 + "lineStyle": { + // 正常状态下的样式 + "normal": { + // 线宽为4 + "width": "4" + } + }, + // 设置符号的大小为10 + "symbolSize": "10", + // 设置符号的形状为空圆 + "symbol": "emptyCircle", + // 启用平滑曲线 + "smooth": true + }, + "radar": { + // 设置雷达图的样式 + "itemStyle": { + // 正常状态下的样式 + "normal": { + // 边框宽度为3 + "borderWidth": "3" + } + }, + // 设置线条样式 + "lineStyle": { + // 正常状态下的线条样式 + "normal": { + // 线条宽度为4 + "width": "4" + } + }, + // 设置符号大小 + "symbolSize": "10", + // 设置符号类型为空心圆 + "symbol": "emptyCircle", + // 设置曲线平滑 + "smooth": true + }, + // 设置柱状图样式 + "bar": { + // 设置项目样式 + "itemStyle": { + // 正常状态下的柱状图样式 + "normal": { + // 柱状图边框宽度为0 + "barBorderWidth": 0, + // 柱状图边框颜色为#ccc + "barBorderColor": "#ccc" + }, + // 强调状态下的柱状图样式 + "emphasis": { + // 柱状图边框宽度为0 + "barBorderWidth": 0, + // 柱状图边框颜色为#ccc + "barBorderColor": "#ccc" + } + } + } + "pie": { // 饼图配置项 + "itemStyle": { // 项目样式配置 + "normal": { // 正常状态下的样式 + "borderWidth": 0, // 边框宽度为0 + "borderColor": "#ccc" // 边框颜色为灰色(#ccc) + }, + "emphasis": { // 强调状态下的样式 + "borderWidth": 0, // 边框宽度为0 + "borderColor": "#ccc" // 边框颜色为灰色(#ccc) + } + } + }, + "scatter": { // 散点图配置项 + "itemStyle": { // 项目样式配置 + "normal": { // 正常状态下的样式 + "borderWidth": 0, // 边框宽度为0 + "borderColor": "#ccc" // 边框颜色为灰色(#ccc) + }, + "emphasis": { // 强调状态下的样式 + "borderWidth": 0, // 边框宽度为0 + "borderColor": "#ccc" // 边框颜色为灰色(#ccc) + } + } + }, + "boxplot": { // 箱线图配置项 + "itemStyle": { // 项目样式配置 + "normal": { // 正常状态下的样式 + "borderWidth": 0, // 边框宽度为0 + "borderColor": "#ccc" // 边框颜色为灰色(#ccc) + }, + "emphasis": { // 强调状态下的样式 + "borderWidth": 0, // 边框宽度为0 + "borderColor": "#ccc" // 边框颜色为灰色(#ccc) + } + } + }, + "parallel": { // 平行坐标系配置项 + "itemStyle": { // 项目样式配置 + "normal": { // 正常状态下的样式 + "borderWidth": 0, // 边框宽度为0 + "borderColor": "#ccc" // 边框颜色为灰色(#ccc) + }, + "emphasis": { // 强调状态下的样式 + "borderWidth": 0, // 边框宽度为0 + "borderColor": "#ccc" // 边框颜色为灰色(#ccc) + } + } + } + "sankey": { // 桑基图配置项 + "itemStyle": { // 项目样式配置 + "normal": { // 正常状态下的样式 + "borderWidth": 0, // 边框宽度为0 + "borderColor": "#ccc" // 边框颜色为灰色(#ccc) + }, + "emphasis": { // 强调状态下的样式 + "borderWidth": 0, // 边框宽度为0 + "borderColor": "#ccc" // 边框颜色为灰色(#ccc) + } + } + }, + "funnel": { // 漏斗图配置项 + "itemStyle": { // 项目样式配置 + "normal": { // 正常状态下的样式 + "borderWidth": 0, // 边框宽度为0 + "borderColor": "#ccc" // 边框颜色为灰色(#ccc) + }, + "emphasis": { // 强调状态下的样式 + "borderWidth": 0, // 边框宽度为0 + "borderColor": "#ccc" // 边框颜色为灰色(#ccc) + } + } + }, + "gauge": { // 仪表盘配置项 + "itemStyle": { // 项目样式配置 + "normal": { // 正常状态下的样式 + "borderWidth": 0, // 边框宽度为0 + "borderColor": "#ccc" // 边框颜色为灰色(#ccc) + }, + "emphasis": { // 强调状态下的样式 + "borderWidth": 0, // 边框宽度为0 + "borderColor": "#ccc" // 边框颜色为灰色(#ccc) + } + } + }, + "candlestick": { // 定义K线图样式 + "itemStyle": { // 项目样式配置 + "normal": { // 正常状态下的样式 + "color": "#e6a0d2", // K线图颜色为粉红色 + "color0": "transparent", // 下跌部分的颜色为透明 + "borderColor": "#e6a0d2", // 边框颜色为粉红色 + "borderColor0": "#3fb1e3", // 下跌部分的边框颜色为蓝色 + "borderWidth": "2" // 边框宽度为2 + } + } + }, + "graph": { // 定义关系图样式 + "itemStyle": { // 项目样式配置 + "normal": { // 正常状态下的样式 + "borderWidth": 0, // 边框宽度为0 + "borderColor": "#ccc" // 边框颜色为灰色 + } + }, + "lineStyle": { // 线条样式配置 + "normal": { // 正常状态下的样式 + "width": "1", // 线条宽度为1 + "color": "#cccccc" // 线条颜色为浅灰色 + } + }, + "symbolSize": "10", // 节点大小为10 + "symbol": "emptyCircle", // 节点形状为空心圆 + "smooth": true, // 是否平滑曲线 + "color": [ // 节点颜色数组 + "#3fb1e3", // 第一个颜色为蓝色 + "#6be6c1", // 第二个颜色为绿色 + "#626c91", // 第三个颜色为深蓝色 + "#a0a7e6", // 第四个颜色为淡蓝色 + "#c4ebad", // 第五个颜色为淡绿色 + "#96dee8" // 第六个颜色为浅蓝色 + ], + "label": { // 标签样式配置 + "normal": { // 正常状态下的样式 + "textStyle": { // 文本样式配置 + "color": "#ffffff" // 文本颜色为白色 + } + } + } + } + }, + "map": { // 地图配置项 + "itemStyle": { // 项目样式配置 + "normal": { // 正常状态下的样式 + "areaColor": "#eeeeee", // 区域颜色为浅灰色(#eeeeee) + "borderColor": "#aaaaaa", // 边框颜色为中灰色(#aaaaaa) + "borderWidth": 0.5 // 边框宽度为0.5 + }, + "emphasis": { // 强调状态下的样式 + "areaColor": "rgba(63,177,227,0.25)", // 区域颜色为半透明蓝色(rgba(63,177,227,0.25)) + "borderColor": "#3fb1e3", // 边框颜色为蓝色(#3fb1e3) + "borderWidth": 1 // 边框宽度为1 + } + }, + "label": { // 标签配置项 + "normal": { // 正常状态下的标签样式 + "textStyle": { // 文本样式 + "color": "#ffffff" // 文本颜色为白色(#ffffff) + } + }, + "emphasis": { // 强调状态下的标签样式 + "textStyle": { // 文本样式 + "color": "rgb(63,177,227)" // 文本颜色为蓝色(rgb(63,177,227)) + } + } + } + }, + "geo": { // 地理坐标系组件配置项 + "itemStyle": { // 项目样式配置 + "normal": { // 正常状态下的样式 + "areaColor": "#eeeeee", // 区域颜色为浅灰色(#eeeeee) + "borderColor": "#aaaaaa", // 边框颜色为中灰色(#aaaaaa) + "borderWidth": 0.5 // 边框宽度为0.5 + }, + "emphasis": { // 强调状态下的样式 + "areaColor": "rgba(63,177,227,0.25)", // 区域颜色为半透明蓝色(rgba(63,177,227,0.25)) + "borderColor": "#3fb1e3", // 边框颜色为蓝色(#3fb1e3) + "borderWidth": 1 // 边框宽度为1 + } + } + } "label": { // 标签配置项 + "normal": { // 正常状态下的样式 + "textStyle": { // 文本样式 + "color": "#ffffff" // 文本颜色为白色(#ffffff) + } + }, + "emphasis": { // 强调状态下的样式 + "textStyle": { // 文本样式 + "color": "rgb(63,177,227)" // 文本颜色为蓝色(rgb(63,177,227)) + } + } + } + }, + "categoryAxis": { // 类目轴配置项 + "axisLine": { // 轴线配置 + "show": true, // 显示轴线 + "lineStyle": { // 线条样式 + "color": "#cccccc" // 轴线颜色为浅灰色(#cccccc) + } + }, + "axisTick": { // 刻度线配置 + "show": false, // 不显示刻度线 + "lineStyle": { // 线条样式 + "color": "#333" // 刻度线颜色为深灰色(#333) + } + }, + "axisLabel": { // 轴标签配置 + "show": true, // 显示轴标签 + "textStyle": { // 文本样式 + "color": "#999999" // 轴标签颜色为浅灰色(#999999) + } + }, + "splitLine": { // 分隔线配置 + "show": true, // 显示分隔线 + "lineStyle": { // 线条样式 + "color": [ // 分隔线颜色数组 + "#eeeeee" // 分隔线颜色为浅灰色(#eeeeee) + ] + } + }, + "splitArea": { // 分隔区域配置项 + "show": false, // 是否显示分隔区域,默认为false + "areaStyle": { // 分隔区域的样式配置 + "color": [ // 分隔区域的颜色数组 + "rgba(250,250,250,0.05)", // 第一个颜色值,半透明白色 + "rgba(200,200,200,0.02)" // 第二个颜色值,半透明灰色 + ] + } + }, + "valueAxis": { // 数值轴配置项 + "axisLine": { // 轴线配置 + "show": true, // 是否显示轴线,默认为true + "lineStyle": { // 轴线的样式配置 + "color": "#cccccc" // 轴线的颜色,灰色 + } + }, + "axisTick": { // 刻度线配置 + "show": false, // 是否显示刻度线,默认为false + "lineStyle": { // 刻度线的样式配置 + "color": "#333" // 刻度线的颜色,深灰色 + } + }, + "axisLabel": { // 轴标签配置 + "show": true, // 是否显示轴标签,默认为true + "textStyle": { // 轴标签文本的样式配置 + "color": "#999999" // 轴标签文本的颜色,浅灰色 + } + }, + "splitLine": { // 分隔线配置 + "show": true, // 是否显示分隔线,默认为true + "lineStyle": { // 分隔线的样式配置 + "color": [ // 分隔线的颜色数组 + "#eeeeee" // 分隔线的颜色,浅灰色 + ] + } + }, + "splitArea": { // 配置图表的分隔区域 + "show": false, // 是否显示分隔区域,这里设置为不显示 + "areaStyle": { // 分隔区域的样式设置 + "color": [ // 分隔区域的颜色数组 + "rgba(250,250,250,0.05)", // 第一个颜色值,半透明白色 + "rgba(200,200,200,0.02)" // 第二个颜色值,半透明灰色 + ] + } + }, + "logAxis": { // 对数轴的配置 + "axisLine": { // 坐标轴线的配置 + "show": true, // 是否显示坐标轴线,这里设置为显示 + "lineStyle": { // 坐标轴线的样式设置 + "color": "#cccccc" // 坐标轴线的颜色,设置为浅灰色 + } + }, + "axisTick": { // 坐标轴刻度的配置 + "show": false, // 是否显示坐标轴刻度,这里设置为不显示 + "lineStyle": { // 坐标轴刻度的样式设置 + "color": "#333" // 坐标轴刻度的颜色,设置为深灰色 + } + }, + "axisLabel": { // 坐标轴标签的配置 + "show": true, // 是否显示坐标轴标签,这里设置为显示 + "textStyle": { // 坐标轴标签文本的样式设置 + "color": "#999999" // 坐标轴标签文本的颜色,设置为浅灰色 + } + }, + "splitLine": { // 分隔线的设置 + "show": true, // 是否显示分隔线,这里设置为显示 + "lineStyle": { // 分隔线的样式设置 + "color": [ // 分隔线的颜色数组 + "#eeeeee" // 分隔线的颜色,设置为浅灰色 + ] + } + }, + "splitArea": { // 分隔区域配置项 + "show": false, // 是否显示分隔区域,默认为false + "areaStyle": { // 分隔区域的样式配置 + "color": [ // 分隔区域的颜色数组 + "rgba(250,250,250,0.05)", // 第一个颜色值,半透明白色 + "rgba(200,200,200,0.02)" // 第二个颜色值,半透明灰色 + ] + } + }, + "timeAxis": { // 时间轴配置项 + "axisLine": { // 轴线配置 + "show": true, // 是否显示轴线,默认为true + "lineStyle": { // 轴线的样式配置 + "color": "#cccccc" // 轴线的颜色,灰色 + } + }, + "axisTick": { // 刻度线配置 + "show": false, // 是否显示刻度线,默认为false + "lineStyle": { // 刻度线的样式配置 + "color": "#333" // 刻度线的颜色,深灰色 + } + }, + "axisLabel": { // 标签配置 + "show": true, // 是否显示标签,默认为true + "textStyle": { // 标签文本的样式配置 + "color": "#999999" // 标签文本的颜色,浅灰色 + } + }, + "splitLine": { // 分隔线配置 + "show": true, // 是否显示分隔线,默认为true + "lineStyle": { // 分隔线的样式配置 + "color": [ // 分隔线的颜色数组 + "#eeeeee" // 分隔线的颜色,浅灰色 + ] + } + }, + "splitArea": { // 分隔区域配置项 + "show": false, // 是否显示分隔区域,默认为false + "areaStyle": { // 分隔区域的样式配置 + "color": [ // 分隔区域的颜色数组 + "rgba(250,250,250,0.05)", // 第一个颜色值,半透明白色 + "rgba(200,200,200,0.02)" // 第二个颜色值,半透明灰色 + ] + } + }, + "toolbox": { // 工具箱配置项 + "iconStyle": { // 图标样式配置 + "normal": { // 正常状态下的样式 + "borderColor": "#999999" // 边框颜色为深灰色(#999999) + }, + "emphasis": { // 强调状态下的样式 + "borderColor": "#666666" // 边框颜色为中灰色(#666666) + } + } + }, + "legend": { // 图例配置项 + "textStyle": { // 文本样式配置 + "color": "#999999" // 文本颜色为深灰色(#999999) + } + }, + "tooltip": { // 提示框配置项 + "axisPointer": { // 坐标轴指示器配置 + "lineStyle": { // 线条样式配置 + "color": "#cccccc", // 线条颜色为浅灰色(#cccccc) + "width": 1 // 线条宽度为1 + }, + "crossStyle": { // 十字准星样式配置 + "color": "#cccccc", // 颜色为浅灰色(#cccccc) + "width": 1 // 宽度为1 + } + } + }, + "timeline": { // 时间轴配置项 + "lineStyle": { // 轴线样式配置 + "color": "#626c91", // 轴线颜色,深蓝色 + "width": 1 // 轴线宽度为1 + }, + "itemStyle": { // 项目样式配置 + "normal": { // 正常状态下的样式 + "color": "#626c91", // 项目颜色,深蓝色 + "borderWidth": 1 // 边框宽度为1 + }, + "emphasis": { // 强调状态下的样式 + "color": "#626c91" // 强调状态下的项目颜色,深蓝色 + } + }, + "controlStyle": { // 控制按钮样式配置 + "normal": { // 正常状态下的样式 + "color": "#626c91", // 控制按钮颜色,深蓝色 + "borderColor": "#626c91", // 控制按钮边框颜色,深蓝色 + "borderWidth": 0.5 // 控制按钮边框宽度为0.5 + }, + "emphasis": { // 强调状态下的样式 + "color": "#626c91", // 强调状态下的控制按钮颜色,深蓝色 + "borderColor": "#626c91", // 强调状态下的控制按钮边框颜色,深蓝色 + "borderWidth": 0.5 // 强调状态下的控制按钮边框宽度为0.5 + } + }, + "checkpointStyle": { // 检查点样式配置 + "color": "#3fb1e3", // 检查点颜色,浅蓝色 + "borderColor": "rgba(63,177,227,0.15)" // 检查点边框颜色,半透明浅蓝色 + }, + "label": { // 标签样式配置 + "normal": { // 正常状态下的标签样式 + "textStyle": { // 文本样式配置 + "color": "#626c91" // 标签文本颜色,深蓝色 + } + }, + "emphasis": { // 强调状态下的标签样式 + "textStyle": { // 文本样式配置 + "color": "#626c91" // 强调状态下的标签文本颜色,深蓝色 + } + } + } + }, + "visualMap": { // 视觉映射组件配置项 + "color": [ // 颜色数组,用于定义视觉映射的颜色范围 + "#2a99c9", // 起始颜色,蓝色 + "#afe8ff" // 结束颜色,浅蓝色 + ] + }, + "dataZoom": { // 数据区域缩放组件配置项 + "backgroundColor": "rgba(255,255,255,0)", // 背景颜色,完全透明 + "dataBackgroundColor": "rgba(222,222,222,1)", // 数据区域的背景颜色,灰色 + "fillerColor": "rgba(114,230,212,0.25)", // 填充颜色,半透明的绿色 + "handleColor": "#cccccc", // 手柄颜色,灰色 + "handleSize": "100%", // 手柄大小,占满容器 + "textStyle": { // 文本样式配置 + "color": "#999999" // 文本颜色,深灰色 + } + }, + "markPoint": { // 标记点组件配置项 + "label": { // 标签配置 + "normal": { // 正常状态下的标签样式 + "textStyle": { // 文本样式 + "color": "#ffffff" // 文本颜色,白色 + } + }, + "emphasis": { // 强调状态下的标签样式 + "textStyle": { // 文本样式 + "color": "#ffffff" // 文本颜色,白色 + } + } + } + } + }); // 结束配置对象 + }); // 结束函数调用 \ No newline at end of file diff --git a/cff/miniAdmin.js b/cff/miniAdmin.js new file mode 100644 index 0000000..622ade4 --- /dev/null +++ b/cff/miniAdmin.js @@ -0,0 +1,450 @@ +layui.define(["jquery", "miniMenu", "element","miniPage", "miniTheme","axios","store"], function (exports) { // 定义一个模块,依赖列表包括 jQuery, miniMenu, element, miniPage, miniTheme, axios, store + var $ = layui.$, // 获取 jQuery 模块并赋值给变量 $ + element = layui.element, // 获取 element 模块并赋值给变量 element + layer = layui.layer, // 获取 layer 模块并赋值给变量 layer + miniMenu = layui.miniMenu, // 获取 miniMenu 模块并赋值给变量 miniMenu + miniTheme = layui.miniTheme, // 获取 miniTheme 模块并赋值给变量 miniTheme + store = layui.store, // 获取 store 模块并赋值给变量 store + axios = layui.axios, // 获取 axios 模块并赋值给变量 axios + miniPage = layui.miniPage; + + if (!/http(s*):\/\//.test(location.href)) { // 检查当前页面的URL是否以http或https开头,如果不是则执行以下代码块 + var tips = "请先将项目部署至web容器(Apache/Tomcat/Nginx/IIS/等),否则部分数据将无法显示"; // 定义提示信息,告知用户需要将项目部署到Web容器中 + return layer.alert(tips); // 使用layer.alert方法弹出提示框,显示上述提示信息,并终止后续代码的执行 + } + + var miniAdmin = { // 定义一个名为miniAdmin的对象,用于存放后续的代码逻辑和功能实现 + + + render: function (options) { + options.iniUrl = options.iniUrl || null; // 初始化URL,默认为null + options.logoInfo = options.logoInfo || null; // Logo信息,默认为null + options.homeInfo = options.homeInfo || null; // 首页信息,默认为null + options.clearUrl = options.clearUrl || null; // 清除URL,默认为null + options.renderPageVersion = options.renderPageVersion || false; // 是否渲染页面版本,默认为false + options.bgColorDefault = options.bgColorDefault || 0; // 默认背景颜色,默认为0 + options.multiModule = options.multiModule || false; // 是否支持多模块,默认为false + options.menuChildOpen = options.menuChildOpen || false; // 菜单子项是否默认展开,默认为false + options.loadingTime = options.loadingTime || 1; // 加载时间,默认为1秒 + options.pageAnim = options.pageAnim || false; // 页面动画效果,默认为false + } + + // 使用axios发送GET请求获取初始化数据 + axios.get(options.iniUrl).then(function (data) { + // 如果返回的数据为空,显示错误信息 + if (data == null) { + miniAdmin.error('暂无菜单信息') + } else { + // 渲染logo信息 + miniAdmin.renderLogo(options.logoInfo); + // 渲染清除按钮 + miniAdmin.renderClear(options.clearUrl); + // 渲染页面动画效果 + miniAdmin.renderAnim(options.pageAnim); + // 监听并处理首页信息和多模块信息 + miniAdmin.listen({ + // 监听homeInfo属性,并传递options对象中的homeInfo值 + homeInfo: options.homeInfo, + // 监听multiModule属性,并传递options对象中的multiModule值 + multiModule: options.multiModule, + }) + miniMenu.render({ + // 将数据列表传递给菜单渲染函数 + menuList: data.data, + // 传递是否支持多模块的选项 + multiModule: options.multiModule, + // 传递子菜单默认打开状态的选项 + menuChildOpen: options.menuChildOpen + }); + miniPage.render({ + // 设置首页信息,从options对象中获取homeInfo属性 + homeInfo: options.homeInfo, + // 设置菜单列表数据,从data对象的data属性中获取 + menuList: data.data, + // 设置是否启用多模块功能,从options对象中获取multiModule属性 + multiModule: options.multiModule, + // 设置页面渲染版本,从options对象中获取renderPageVersion属性 + renderPageVersion: options.renderPageVersion, + // 设置子菜单是否默认打开,从options对象中获取menuChildOpen属性 + menuChildOpen: options.menuChildOpen, + // 定义监听切换回调函数,当触发时调用miniAdmin的renderDevice方法 + listenSwichCallback: function () { + miniAdmin.renderDevice(); + } + }); + miniTheme.render({ + // 设置默认背景颜色 + bgColorDefault: options.bgColorDefault, + // 监听事件 + listen: true, + }); + // 删除加载器,传入加载时间参数 + miniAdmin.deleteLoader(options.loadingTime); + } + }).catch(function () { + // 捕获错误并显示错误信息 + miniAdmin.error('菜单接口有误'); + }) + }, + + /** + * 初始化logo + * @param data + */ + renderLogo: function (data) { + // 生成包含logo图片和标题的HTML字符串 + var html = 'logo

' + data.title + '

'; + + // 将生成的HTML字符串插入到class为layuimini-logo的元素中 + $('.layuimini-logo').html(html); + }, + + /** + * 初始化缓存地址 + * @param clearUrl + */ + renderClear: function (clearUrl) { + // 将传入的URL设置为'.layuimini-clear'元素的data-href属性值 + $('.layuimini-clear').attr('data-href', clearUrl); + }, + + /** + * 切换菜单动画 + * @param anim + */ + renderAnim: function (anim) { + // 检查传入的动画参数是否存在 + if (anim) { + // 在ID为'layuimini-bg-color'的元素之后插入一段样式代码,用于定义页面动画效果 + $('#layuimini-bg-color').after(''); + } + }, + + /** + * 进入全屏 + */ + fullScreen: function () { + // 获取文档的根元素 + var el = document.documentElement; + // 获取不同浏览器的全屏请求方法 + var rfs = el.requestFullScreen || el.webkitRequestFullScreen; + + // 如果支持标准的全屏请求方法,则调用该方法 + if (typeof rfs != "undefined" && rfs) { + rfs.call(el); + } + // 如果支持ActiveXObject(主要用于IE),则通过发送F11键实现全屏 + else if (typeof window.ActiveXObject != "undefined") { + var wscript = new ActiveXObject("WScript.Shell"); + if (wscript != null) { + wscript.SendKeys("{F11}"); + } + } + // 针对IE的msRequestFullscreen方法 + else if (el.msRequestFullscreen) { + el.msRequestFullscreen(); + } + // 针对Opera的oRequestFullscreen方法 + else if (el.oRequestFullscreen) { + el.oRequestFullscreen(); + } + // 针对Webkit内核的webkitRequestFullscreen方法 + else if (el.webkitRequestFullscreen) { + el.webkitRequestFullscreen(); + } + // 针对Firefox的mozRequestFullScreen方法 + else if (el.mozRequestFullScreen) { + el.mozRequestFullScreen(); + } + // 如果以上方法都不支持,则提示用户浏览器不支持全屏调用 + else { + miniAdmin.error('浏览器不支持全屏调用!'); + } + }, + + /** + * 退出全屏 + */ + exitFullScreen: function () { + // 获取文档对象 + var el = document; + // 定义取消全屏的方法,兼容不同浏览器 + var cfs = el.cancelFullScreen || el.webkitCancelFullScreen || el.exitFullScreen; + // 如果存在取消全屏方法并且该方法可用 + if (typeof cfs != "undefined" && cfs) { + // 调用取消全屏方法 + cfs.call(el); + } else if (typeof window.ActiveXObject != "undefined") { + // 针对IE浏览器的处理方式 + var wscript = new ActiveXObject("WScript.Shell"); + if (wscript != null) { + // 发送F11键以退出全屏模式 + wscript.SendKeys("{F11}"); + } + } else if (el.msExitFullscreen) { + // 针对IE11及以下版本的处理方式 + el.msExitFullscreen(); + } else if (el.oRequestFullscreen) { + // 针对Opera浏览器的处理方式 + el.oCancelFullScreen(); + } else if (el.mozCancelFullScreen) { + // 针对Firefox浏览器的处理方式 + el.mozCancelFullScreen(); + } else if (el.webkitCancelFullScreen) { + // 针对Webkit内核的浏览器(如Chrome和Safari)的处理方式 + el.webkitCancelFullScreen(); + } else { + // 如果以上所有方法都不可用,提示用户浏览器不支持全屏调用 + miniAdmin.error('浏览器不支持全屏调用!'); + } + }, + + /** + * 初始化设备端 + */ + renderDevice: function () { + // 如果当前是移动设备 + if (miniAdmin.checkMobile()) { + // 设置工具栏图标的属性为展开状态 + $('.layuimini-tool i').attr('data-side-fold', 1); + // 更改工具栏图标的类名为“fa fa-outdent” + $('.layuimini-tool i').attr('class', 'fa fa-outdent'); + // 移除布局主体的最小化样式 + $('.layui-layout-body').removeClass('layuimini-mini'); + // 添加布局主体的全部显示样式 + $('.layui-layout-body').addClass('layuimini-all'); + } + }, + + + /** + * 初始化加载时间 + * @param loadingTime + */ + deleteLoader: function (loadingTime) { + // 设置一个定时器,在指定的时间后执行回调函数 + setTimeout(function () { + // 使用jQuery选择器找到类名为'layuimini-loader'的元素,并使其淡出 + $('.layuimini-loader').fadeOut(); + }, loadingTime * 1000) // 将传入的loadingTime参数转换为毫秒 + }, + + /** + * 成功 + * @param title + * @returns {*} + */ + success: function (title) { + // 调用layer.msg方法显示消息提示框 + // title: 要显示的消息内容 + // icon: 1 表示成功图标 + // shade: 使用当前对象的shade属性作为遮罩层 + // scrollbar: false 表示不显示滚动条 + // time: 2000 表示消息提示框显示时间为2秒 + // shadeClose: true 表示点击遮罩层可以关闭提示框 + return layer.msg(title, {icon: 1, shade: this.shade, scrollbar: false, time: 2000, shadeClose: true}); + }, + + /** + * 失败 + * @param title + * @returns {*} + */ + error: function (title) { + // 调用layer.msg方法显示错误消息 + // title: 要显示的消息内容 + // icon: 2 表示使用错误图标 + // shade: 使用当前对象的shade属性值 + // scrollbar: false 表示不显示滚动条 + // time: 3000 表示消息显示时间为3秒 + // shadeClose: true 表示点击遮罩层可以关闭消息框 + return layer.msg(title, {icon: 2, shade: this.shade, scrollbar: false, time: 3000, shadeClose: true}); + }, + + /** + * 判断是否为手机 + * @returns {boolean} + */ + checkMobile: function () { + // 获取用户代理字符串并转换为小写 + var ua = navigator.userAgent.toLocaleLowerCase(); + // 获取平台信息并转换为小写 + var pf = navigator.platform.toLocaleLowerCase(); + // 判断是否为Android设备 + var isAndroid = (/android/i).test(ua) || ((/iPhone|iPod|iPad/i).test(ua) && (/linux/i).test(pf)) + || (/ucweb.*linux/i.test(ua)); + // 判断是否为iOS设备,排除Android设备 + var isIOS = (/iPhone|iPod|iPad/i).test(ua) && !isAndroid; + // 判断是否为Windows Phone设备 + var isWinPhone = (/Windows Phone|ZuneWP7/i).test(ua); + // 获取文档的宽度 + var clientWidth = document.documentElement.clientWidth; + // 如果既不是Android、iOS也不是Windows Phone,并且宽度大于1024,则返回false + if (!isAndroid && !isIOS && !isWinPhone && clientWidth > 1024) { + return false; + } else { + // 否则返回true + return true; + } + }, + + /** + * 监听 + * @param options + */ + listen: function (options) { + // 如果 options 对象中没有 homeInfo 属性,则初始化为一个空对象 + options.homeInfo = options.homeInfo || {}; + } + + /** + * 清理 + */ + $('body').on('click', '[data-clear]', function () { + // 显示加载动画,设置2秒后自动关闭 + var loading = layer.load(0, {shade: false, time: 2 * 1000}); + + // 清空浏览器的sessionStorage + sessionStorage.clear(); + + // 获取清理缓存的URL + var clearUrl = $(this).attr('data-href'); + + // 如果URL存在且不为空 + if (clearUrl != undefined && clearUrl != '' && clearUrl != null) { + // 发送GET请求到服务器清理缓存 + $.getJSON(clearUrl, function (data, status) { + // 关闭加载动画 + layer.close(loading); + + // 根据服务器返回的状态码判断是否成功 + if (data.code != 1) { + // 清理失败,显示错误信息 + return miniAdmin.error(data.msg); + } else { + // 清理成功,显示成功信息 + return miniAdmin.success(data.msg); + } + }).fail(function () { + // 请求失败,关闭加载动画并显示错误信息 + layer.close(loading); + return miniAdmin.error('清理缓存接口有误'); + }); + } else { + // URL不存在或为空,直接关闭加载动画并显示成功信息 + layer.close(loading); + return miniAdmin.success('清除缓存成功'); + } + }); + /** + * 刷新 + */ + $('body').on('click', '[data-refresh]', function () { + // 当点击带有 data-refresh 属性的元素时,触发此事件处理函数 + miniPage.refresh(options); + // 调用 miniPage 对象的 refresh 方法,并传入 options 参数进行页面刷新 + miniAdmin.success('刷新成功'); + // 调用 miniAdmin 对象的 success 方法,显示“刷新成功”的提示信息 + }); + + /** + * 监听提示信息 + */ + $("body").on("mouseenter", ".layui-nav-tree .menu-li", function () { + // 如果当前是移动设备,则不执行后续代码 + if (miniAdmin.checkMobile()) { + return false; + } + // 获取当前元素的class属性值 + var classInfo = $(this).attr('class'), + // 获取当前元素的HTML内容 + tips = $(this).prop("innerHTML"), + // 获取工具栏的折叠状态 + isShow = $('.layuimini-tool i').attr('data-side-fold'); + // 如果工具栏未折叠且tips存在 + if (isShow == 0 && tips) { + // 构建提示信息HTML结构 + tips = ""; + // 显示提示信息 + window.openTips = layer.tips(tips, $(this), { + tips: [2, '#2f4056'], // 设置提示信息的位置和颜色 + time: 300000, // 设置提示信息的显示时间 + skin:"popup-tips", // 设置提示信息的样式 + // 成功回调函数 + success:function (el) { + // 调整提示信息的位置 + var left = $(el).position().left - 10; + $(el).css({ left:left }); + // 重新渲染元素 + element.render(); + } + }); + } + }); + $("body").on("mouseleave", ".popup-tips", function () { + // 当鼠标离开具有类名 'popup-tips' 的元素时触发事件 + if (miniAdmin.checkMobile()) { + // 如果当前设备是移动设备,则直接返回 false,不执行后续代码 + return false; + } + // 获取具有类名 'layuimini-tool i' 的元素的 'data-side-fold' 属性值 + var isShow = $('.layuimini-tool i').attr('data-side-fold'); + if (isShow == 0) { + // 如果 'data-side-fold' 属性值为 0,表示侧边栏未展开 + try { + // 尝试关闭弹出提示框 + layer.close(window.openTips); + } catch (e) { + // 如果关闭过程中发生异常,捕获异常并打印错误信息到控制台 + console.log(e.message); + } + } + }); + + /** + * 全屏 + */ + $('body').on('click', '[data-check-screen]', function () { + // 获取当前元素的 data-check-screen 属性值 + var check = $(this).attr('data-check-screen'); + + // 如果属性值为 'full',则进入全屏模式 + if (check == 'full') { + // 调用 miniAdmin 的 fullScreen 方法进入全屏模式 + miniAdmin.fullScreen(); + // 将属性值改为 'exit' + $(this).attr('data-check-screen', 'exit'); + // 修改按钮图标为压缩图标 + $(this).html(''); + } else { + // 否则退出全屏模式 + miniAdmin.exitFullScreen(); + // 将属性值改为 'full' + $(this).attr('data-check-screen', 'full'); + // 修改按钮图标为箭头图标 + $(this).html(''); + } + }); + + /** + * 点击遮罩层 + */ + $('body').on('click', '.layuimini-make', function () { + // 调用 miniAdmin 的 renderDevice 方法重新渲染设备 + miniAdmin.renderDevice(); + }); + +} +}; + +// 导出 miniAdmin 模块 +exports("miniAdmin", miniAdmin); +}); \ No newline at end of file diff --git a/cff/miniMenu.js b/cff/miniMenu.js new file mode 100644 index 0000000..49cb1dd --- /dev/null +++ b/cff/miniMenu.js @@ -0,0 +1,234 @@ +layui.define(["element","laytpl" ,"jquery"], function (exports) { // 定义一个模块,依赖element、laytpl和jquery + var element = layui.element, // 获取layui的element模块 + $ = layui.$, // 获取layui的jQuery模块 + laytpl = layui.laytpl, // 获取layui的模板引擎模块 + layer = layui.layer; // 获取layui的弹出层模块 + + var miniMenu = { // 定义miniMenu对象 + + + render: function (options) { // 定义渲染函数 + options.menuList = options.menuList || []; // 如果未传入menuList,则默认为空数组 + options.multiModule = options.multiModule || false; // 如果未传入multiModule,则默认为false + options.menuChildOpen = options.menuChildOpen || false; // 如果未传入menuChildOpen,则默认为false + if (options.multiModule) { // 如果开启多模块 + miniMenu.renderMultiModule(options.menuList, options.menuChildOpen); // 调用多模块渲染函数 + } else { // 否则 + miniMenu.renderSingleModule(options.menuList, options.menuChildOpen); // 调用单模块渲染函数 + } + miniMenu.listen(); // 监听事件 + }, + + + renderSingleModule: function (menuList, menuChildOpen) { // 定义单模块渲染函数 + menuList = menuList || []; // 如果未传入menuList,则默认为空数组 + var leftMenuHtml = '', // 初始化左侧菜单HTML字符串 + childOpenClass = '', // 初始化子菜单打开类名 + leftMenuCheckDefault = 'layui-this'; // 设置默认选中样式类名 + var me = this ; // 保存当前上下文 + if (menuChildOpen) childOpenClass = ' layui-nav-itemed'; // 如果需要展开子菜单,添加对应的类名 + leftMenuHtml = this.renderLeftMenu(menuList,{ childOpenClass:childOpenClass }) ; // 渲染左侧菜单HTML + $('.layui-layout-body').addClass('layuimini-single-module'); // 给布局主体添加单模块标识类名 + $('.layuimini-header-menu').remove(); // 移除头部菜单 + $('.layuimini-menu-left').html(leftMenuHtml); // 将渲染好的左侧菜单HTML插入到左侧菜单容器中 + + element.init(); // 初始化layui元素 + }, + + compileMenu: function(menu,isSub){ + // 定义菜单HTML模板,使用laytpl语法进行条件渲染 + var menuHtml = '' ; + + // 如果isSub为true,则使用不同的HTML模板 + if(isSub){ + menuHtml = '' + } + + // 使用laytpl渲染菜单HTML模板并返回结果 + return laytpl(menuHtml).render(menu); + }, + + compileMenuContainer :function(menu,isSub){ + // 定义菜单容器的HTML模板,使用laytpl语法进行条件渲染 + var wrapperHtml = '' ; + + // 如果isSub为true,则使用不同的HTML模板 + if(isSub){ + wrapperHtml = '
{{d.children}}
' ; + } + + // 如果菜单没有子项,则返回空字符串 + if(!menu.children){ + return ""; + } + + // 使用laytpl渲染菜单容器的HTML模板并返回结果 + return laytpl(wrapperHtml).render(menu); + }, + + each:function(list,callback){ // 定义一个遍历函数,接收列表和回调函数作为参数 + var _list = []; // 初始化一个空数组用于存储处理后的结果 + for(var i = 0 ,length = list.length ; i/\n'; + // 根据href和menuList生成页面标题数组 + var pageTitleArray = miniPage.buildPageTitleArray(href, options.menuList); + // 如果页面标题数组不为空 + if (pageTitleArray.length > 0) { + // 遍历页面标题数组 + for (var key in pageTitleArray) { + key = parseInt(key); // 将key转换为整数类型 + // 如果不是最后一个元素,添加链接和分隔符 + if (key !== pageTitleArray.length - 1) { + pageTitleHtml += '' + pageTitleArray[key] + '/\n'; + } else { + // 如果是最后一个元素,只添加链接 + pageTitleHtml += '' + pageTitleArray[key] + '\n'; + } + } + } else { + // 如果页面标题数组为空,从sessionStorage中获取标题 + var title = sessionStorage.getItem('layuimini_page_title'); + // 如果标题为空或未定义,隐藏页面头部元素 + if (title === null || title === undefined || title === '') { + $('.layuimini-page-header').addClass('layui-hide'); + } else { + // 否则,添加标题到页面标题HTML中 + pageTitleHtml += '' + title + '\n'; + } + } + // 清空并更新页面头部的标题内容 + $('.layuimini-page-header .layuimini-page-title').empty().html(pageTitleHtml); + }, + + /** + * 初始化页面内容 + * @param options + * @param href + */ + renderPageContent: function (href, options) { + // 设置默认的 renderPageVersion 选项为 false,如果未提供该选项 + options.renderPageVersion = options.renderPageVersion || false; + + // 定义页面内容的容器选择器 + var container = '.layuimini-content-page'; + + // 如果需要渲染页面版本,则在 URL 中添加时间戳参数 + if (options.renderPageVersion) { + var v = new Date().getTime(); + href = href.indexOf("?") > -1 ? href + '&v=' + v : href + '?v=' + v; + } + + // 根据页面头部是否隐藏来调整内容容器的高度样式 + if ($(".layuimini-page-header").hasClass("layui-hide")) { + $(container).removeAttr("style"); + } else { + $(container).attr("style", "height: calc(100% - 36px)"); + } + + // 清空内容容器的 HTML 内容 + $(container).html(''); + + // 发起 AJAX 请求获取页面内容 + $.ajax({ + url: href, + type: 'get', + dataType: 'html', + success: function (data) { + // 将获取到的内容插入到内容容器中,并初始化元素 + $(container).html(data); + element.init(); + }, + error: function (xhr, textstatus, thrown) { + // 处理请求失败的情况,显示错误信息 + return layer.msg('Status:' + xhr.status + ',' + xhr.statusText + ',请稍后再试!'); + } + }); + }, + + /** + * 刷新页面内容 + * @param options + */ + refresh: function (options) { + // 获取当前URL的hash部分,并去掉开头的'#/' + var href = location.hash.replace(/^#\//, ''); + + // 如果href为空或未定义,则渲染主页内容 + if (href === null || href === undefined || href === '') { + miniPage.renderHome(options); + } else { + // 否则根据href渲染相应的页面内容 + miniPage.renderPageContent(href, options); + } + } + } + }, + + /** + * 构建页面标题数组 + * @param href + * @param menuList + */ + /** + * 构建页面标题数组 + * @param {string} href - 目标链接 + * @param {Array} menuList - 菜单列表 + * @returns {Array} - 包含页面标题的数组 + */ + buildPageTitleArray: function (href, menuList) { + // 初始化空数组,用于存储最终结果 + var array = [], + newArray = []; + // 遍历菜单列表中的每一项 + for (key in menuList) { + // 获取当前项 + var item = menuList[key]; + // 如果当前项的链接与目标链接匹配 + if (item.href === href) { + // 将当前项的标题添加到结果数组中 + array.push(item.title); + // 终止循环 + break; + } + // 如果当前项有子菜单 + if (item.child) { + // 递归调用自身,处理子菜单 + newArray = miniPage.buildPageTitleArray(href, item.child); + // 如果子菜单中有匹配项 + if (newArray.length > 0) { + // 将当前项的标题添加到子菜单结果数组的开头 + newArray.unshift(item.title); + // 合并当前项的标题和子菜单结果数组到最终结果数组中 + array = array.concat(newArray); + // 终止循环 + break; + } + } + } + // 返回最终结果数组 + return array; + }, + + /** + * 获取指定链接内容 + * @param href + * @returns {string} + */ + getHrefContent: function (href) { + // 初始化一个空字符串变量,用于存储获取的内容 + var content = ''; + // 获取当前时间的时间戳,用于防止缓存 + var v = new Date().getTime(); + // 发送AJAX请求以获取指定URL的内容 + $.ajax({ + // 根据URL是否包含查询参数来决定如何添加时间戳 + url: href.indexOf("?") > -1 ? href + '&v=' + v : href + '?v=' + v, + // 设置请求类型为GET + type: 'get', + // 期望的响应数据类型为HTML + dataType: 'html', + // 设置请求为同步,等待响应后再继续执行后续代码 + async: false, + // 请求成功时的回调函数 + success: function (data) { + // 将获取到的数据赋值给content变量 + content = data; + }, + // 请求失败时的回调函数 + error: function (xhr, textstatus, thrown) { + // 显示错误信息并返回提示消息 + return layer.msg('Status:' + xhr.status + ',' + xhr.statusText + ',请稍后再试!'); + } + }); + // 返回获取到的内容 + return content; + }, + /** + * 获取弹出层的宽高 + * @returns {jQuery[]} + */ + getOpenWidthHeight: function () { + // 获取页面内容区域的宽度 + var clienWidth = $(".layuimini-content-page").width(); + // 获取页面内容区域的高度 + var clientHeight = $(".layuimini-content-page").height(); + // 获取页面内容区域相对于文档的左偏移量 + var offsetLeft = $(".layuimini-content-page").offset().left; + // 获取页面内容区域相对于文档的上偏移量 + var offsetTop = $(".layuimini-content-page").offset().top; + // 返回包含宽度、高度、上偏移量和左偏移量的数组 + return [clienWidth, clientHeight, offsetTop, offsetLeft]; + }, + /** + * 单模块切换 + * @param tabId + */ + listenSwitchSingleModule: function (tabId) { + // 遍历所有具有layuimini-href属性的元素 + $("[layuimini-href]").each(function () { + // 如果元素的layuimini-href属性值等于传入的tabId + if ($(this).attr("layuimini-href") === tabId) { + // 定义一个函数用于自动展开菜单栏 + var addMenuClass = function ($element, type) { + if (type === 1) { + // 为当前元素添加layui-this类 + $element.addClass('layui-this'); + // 如果当前元素同时拥有layui-nav-item和layui-this类 + if ($element.hasClass('layui-nav-item') && $element.hasClass('layui-this')) { + // 将所有菜单项的类名设置为layui-nav-item + $(".layuimini-header-menu li").attr('class', 'layui-nav-item'); + } else { + // 递归调用addMenuClass,处理父级元素 + addMenuClass($element.parent().parent(), 2); + } + } else { + // 为当前元素添加layui-nav-itemed类 + $element.addClass('layui-nav-itemed'); + // 如果当前元素同时拥有layui-nav-item和layui-nav-itemed类 + if ($element.hasClass('layui-nav-item') && $element.hasClass('layui-nav-itemed')) { + // 将所有菜单项的类名设置为layui-nav-item + $(".layuimini-header-menu li").attr('class', 'layui-nav-item'); + } else { + // 递归调用addMenuClass,处理父级元素 + addMenuClass($element.parent().parent(), 2); + } + } + }; + // 调用addMenuClass函数,展开当前元素的菜单栏 + addMenuClass($(this).parent(), 1); + // 终止each循环 + return false; + } + }); + }, + + /** + * 多模块切换 + * @param tabId + */ + listenSwitchMultiModule: function (tabId) { + // 遍历所有具有 layuimini-href 属性的元素 + $("[layuimini-href]").each(function () { + // 如果元素的 layuimini-href 属性值等于传入的 tabId + if ($(this).attr("layuimini-href") === tabId) { + + // 定义一个函数,用于自动展开菜单栏 + var addMenuClass = function ($element, type) { + if (type === 1) { + // 为元素添加 'layui-this' 类 + $element.addClass('layui-this'); + // 如果元素同时具有 'layui-nav-item' 和 'layui-this' 类 + if ($element.hasClass('layui-nav-item') && $element.hasClass('layui-this')) { + // 获取父元素的 id + var moduleId = $element.parent().attr('id'); + // 设置头部菜单项的类名为 'layui-nav-item' + $(".layuimini-header-menu li").attr('class', 'layui-nav-item'); + // 为对应的头部菜单项添加 'layui-this' 类 + $("#" + moduleId + "HeaderId").addClass("layui-this"); + // 隐藏左侧菜单树 + $(".layuimini-menu-left .layui-nav.layui-nav-tree").attr('class', 'layui-nav layui-nav-tree layui-hide'); + // 显示当前模块的菜单树并添加 'layui-this' 类 + $("#" + moduleId).attr('class', 'layui-nav layui-nav-tree layui-this'); + } else { + // 递归调用 addMenuClass 函数,处理父级元素 + addMenuClass($element.parent().parent(), 2); + } + } else { + // 为元素添加 'layui-nav-itemed' 类 + $element.addClass('layui-nav-itemed'); + // 如果元素同时具有 'layui-nav-item' 和 'layui-nav-itemed' 类 + if ($element.hasClass('layui-nav-item') && $element.hasClass('layui-nav-itemed')) { + // 获取父元素的 id + var moduleId = $element.parent().attr('id'); + // 设置头部菜单项的类名为 'layui-nav-item' + $(".layuimini-header-menu li").attr('class', 'layui-nav-item'); + // 为对应的头部菜单项添加 'layui-this' 类 + $("#" + moduleId + "HeaderId").addClass("layui-this"); + // 隐藏左侧菜单树 + $(".layuimini-menu-left .layui-nav.layui-nav-tree").attr('class', 'layui-nav layui-nav-tree layui-hide'); + // 显示当前模块的菜单树并添加 'layui-this' 类 + $("#" + moduleId).attr('class', 'layui-nav layui-nav-tree layui-this'); + } else { + // 递归调用 addMenuClass 函数,处理父级元素 + addMenuClass($element.parent().parent(), 2); + } + } + }; + // 调用 addMenuClass 函数,处理当前元素的父级元素 + addMenuClass($(this).parent(), 1); + return false; // 终止 each 循环 + } + }); + }, + /** + * 修改hash地址定位 + * @param href + */ + hashChange: function (href) { + // 将传入的 href 参数拼接到当前 URL 的哈希部分,并更新浏览器地址栏 + window.location.hash = "/" + href; + }, + + /** + * 修改hash地址为主页 + */ + hashHome: function () { + // 将当前窗口的哈希值设置为根路径 "/" + window.location.hash = "/"; + }, + + /** + * 监听 + * @param options + */ + /** + * 监听函数,用于处理传入的选项参数。 + * @param {Object} options - 配置选项对象。 + */ + listen: function (options) { + // 方法体内容待补充 + /** + * 打开新窗口 + */ + $('body').on('click', '[layuimini-href]', function () { + // 显示加载动画,设置2秒后自动关闭 + var loading = layer.load(0, {shade: false, time: 2 * 1000}); + + // 获取当前点击元素的layuimini-href属性值和target属性值 + var href = $(this).attr('layuimini-href'), + target = $(this).attr('target'); + + // 如果href为空,则直接返回 + if(!href) return; + + // 保存当前点击的元素引用 + var me = this; + + // 查找左侧菜单中与当前点击元素href相同的元素 + var el = $("[layuimini-href='"+href+"']",".layuimini-menu-left"); + + // 关闭之前打开的提示框 + layer.close(window.openTips); + + // 如果找到匹配的元素 + if(el.length){ + // 移除所有layui-this类名 + $(el).closest(".layui-nav-tree").find(".layui-this").removeClass("layui-this"); + // 给父元素添加layui-this类名 + $(el).parent().addClass("layui-this"); + } + + // 如果target属性值为_blank,则在新窗口中打开链接 + if (target === '_blank') { + // 关闭加载动画 + layer.close(loading); + // 在新窗口中打开链接 + window.open(href, "_blank"); + return false; + } + + // 调用miniPage对象的hashChange方法,改变页面URL的哈希值 + miniPage.hashChange(href); + + // 设置左侧菜单的layuimini-page-add属性为yes + $('.layuimini-menu-left').attr('layuimini-page-add', 'yes'); + + // 关闭加载动画 + layer.close(loading); + }); + + /** + * 在子页面上打开新窗口 + */ + $('body').on('click', '[layuimini-content-href]', function () { + // 显示加载动画,设置2秒后自动关闭 + var loading = parent.layer.load(0, {shade: false, time: 2 * 1000}); + + // 获取点击元素的href、title和target属性值 + var href = $(this).attr('layuimini-content-href'), + title = $(this).attr('data-title'), + target = $(this).attr('target'); + + // 如果href为空,则直接返回 + if(!href) return; + + // 保存当前点击的元素引用 + var me = this; + + // 查找左侧菜单中匹配的href元素 + var el = $("[layuimini-href='"+href+"']",".layuimini-menu-left"); + + // 关闭之前打开的提示框 + layer.close(window.openTips); + + // 如果找到匹配的元素 + if(el.length){ + // 移除所有同级元素的'layui-this'类 + $(el).closest(".layui-nav-tree").find(".layui-this").removeClass("layui-this"); + // 给父元素添加'layui-this'类 + $(el).parent().addClass("layui-this"); + } + + // 如果target属性为'_blank',则在新窗口中打开链接 + if (target === '_blank') { + // 关闭加载动画 + parent.layer.close(loading); + // 在新窗口中打开链接 + window.open(href, "_blank"); + return false; + } + + // 将页面标题存储到sessionStorage中 + sessionStorage.setItem('layuimini_page_title', title); + + // 调用hashChange方法更新页面内容 + miniPage.hashChange(href); + + // 关闭加载动画 + parent.layer.close(loading); + }); + + /** + * 返回主页 + */ + $('body').on('click', '.layuimini-back-home', function () { + // 当点击事件触发时,调用 miniPage 对象的 hashHome 方法 + miniPage.hashHome(); + }); + + + }, + + + /** + * 监听hash变化 + * @returns {boolean} + */ + listenHash: function (options) { + // 初始化homeInfo选项,如果未定义则设置为空对象 + options.homeInfo = options.homeInfo || {}; + // 初始化multiModule选项,如果未定义则设置为false + options.multiModule = options.multiModule || false; + // 初始化listenSwichCallback选项,如果未定义则设置为空函数 + options.listenSwichCallback = options.listenSwichCallback || function () {}; + + // 监听hash变化事件 + window.onhashchange = function () { + // 获取当前hash值并去掉前缀'#/' + var href = location.hash.replace(/^#\//, ''); + + // 如果listenSwichCallback是函数,则调用它 + if (typeof options.listenSwichCallback === 'function') { + options.listenSwichCallback(); + } + + // 根据hash值判断要渲染的页面 + if (href === null || href === undefined || href === '') { + // 如果hash值为空,移除layui-this类并渲染首页 + $("[layuimini-href]").parent().removeClass('layui-this'); + miniPage.renderHome(options); + } else { + // 否则渲染指定页面 + miniPage.renderPage(href, options); + } + + // 检查菜单是否添加了新页面 + if ($('.layuimini-menu-left').attr('layuimini-page-add') === 'yes') { + // 如果是,则重置属性为'no' + $('.layuimini-menu-left').attr('layuimini-page-add', 'no'); + } else { + // 如果不是,重新定位菜单焦点 + $("[layuimini-href]").parent().removeClass('layui-this'); + if (options.multiModule) { + // 多模块情况下处理菜单切换 + miniPage.listenSwitchMultiModule(href); + } else { + // 单模块情况下处理菜单切换 + miniPage.listenSwitchSingleModule(href); + } + } + }; + }, \ No newline at end of file diff --git a/cff/miniTheme.js b/cff/miniTheme.js new file mode 100644 index 0000000..e4ca747 --- /dev/null +++ b/cff/miniTheme.js @@ -0,0 +1,576 @@ +layui.define(["jquery", "layer"], function (exports) { + // 获取jQuery对象 + var $ = layui.$, + // 获取Layer对象 + layer = layui.layer; + + // 定义miniTheme对象 + var miniTheme = { + + /** + * 主题配置项 + * @param bgcolorId + * @returns {{headerLogo, menuLeftHover, headerRight, menuLeft, headerRightThis, menuLeftThis}|*|*[]} + */ + config: function (bgcolorId) { + // 定义一个名为bgColorConfig的数组,用于存储背景颜色配置 + var bgColorConfig = [ + { + headerRightBg: '#ffffff', //头部右侧背景色 + headerRightBgThis: '#e4e4e4', //头部右侧选中背景色, + headerRightColor: 'rgba(107, 107, 107, 0.7)', //头部右侧字体颜色, + headerRightChildColor: 'rgba(107, 107, 107, 0.7)', //头部右侧下拉字体颜色, + headerRightColorThis: '#565656', //头部右侧鼠标选中, + headerRightNavMore: 'rgba(160, 160, 160, 0.7)', //头部右侧更多下拉颜色, + headerRightNavMoreBg: '#1E9FFF', //头部右侧更多下拉列表选中背景色, + headerRightNavMoreColor: '#ffffff', //头部右侧更多下拉列表字体色, + headerRightToolColor: '#565656', //头部缩放按钮样式, + headerLogoBg: '#192027', //logo背景颜色, + headerLogoColor: 'rgb(191, 187, 187)', //logo字体颜色, + leftMenuNavMore: 'rgb(191, 187, 187)', //左侧菜单更多下拉样式, + leftMenuBg: '#28333E', //左侧菜单背景, + leftMenuBgThis: '#1E9FFF', //左侧菜单选中背景, + leftMenuChildBg: '#0c0f13', //左侧菜单子菜单背景, + leftMenuColor: 'rgb(191, 187, 187)', //左侧菜单字体颜色, + leftMenuColorThis: '#ffffff', //左侧菜单选中字体颜色, + tabActiveColor: '#1e9fff', //tab选项卡选中颜色, + }, + { + headerRightBg: '#23262e', //头部右侧背景色 + headerRightBgThis: '#0c0c0c', //头部右侧选中背景色, + headerRightColor: 'rgba(255,255,255,.7)', //头部右侧字体颜色, + headerRightChildColor: '#676767', //头部右侧下拉字体颜色, + headerRightColorThis: '#ffffff', //头部右侧鼠标选中, + headerRightNavMore: 'rgba(255,255,255,.7)', //头部右侧更多下拉颜色, + headerRightNavMoreBg: '#1aa094', //头部右侧更多下拉列表选中背景色, + headerRightNavMoreColor: '#ffffff', //头部右侧更多下拉列表字体色, + headerRightToolColor: '#bbe3df', //头部缩放按钮样式, + headerLogoBg: '#0c0c0c', //logo背景颜色, + headerLogoColor: '#ffffff', //logo字体颜色, + leftMenuNavMore: 'rgb(191, 187, 187)', //左侧菜单更多下拉样式, + leftMenuBg: '#23262e', //左侧菜单背景, + leftMenuBgThis: '#737373', //左侧菜单选中背景, + leftMenuChildBg: 'rgba(0,0,0,.3)', //左侧菜单子菜单背景, + leftMenuColor: 'rgb(191, 187, 187)', //左侧菜单字体颜色, + leftMenuColorThis: '#ffffff', //左侧菜单选中字体颜色, + tabActiveColor: '#23262e', //tab选项卡选中颜色, + }, + { + headerRightBg: '#ffa4d1', //头部右侧背景色 + headerRightBgThis: '#bf7b9d', //头部右侧选中背景色, + headerRightColor: 'rgba(255,255,255,.7)', //头部右侧字体颜色, + headerRightChildColor: '#676767', //头部右侧下拉字体颜色, + headerRightColorThis: '#ffffff', //头部右侧鼠标选中, + headerRightNavMore: 'rgba(255,255,255,.7)', //头部右侧更多下拉颜色, + headerRightNavMoreBg: '#ffa4d1', //头部右侧更多下拉列表选中背景色, + headerRightNavMoreColor: '#ffffff', //头部右侧更多下拉列表字体色, + headerRightToolColor: '#bbe3df', //头部缩放按钮样式, + headerLogoBg: '#e694bd', //logo背景颜色, + headerLogoColor: '#ffffff', //logo字体颜色, + leftMenuNavMore: 'rgb(191, 187, 187)', //左侧菜单更多下拉样式, + leftMenuBg: '#1f1f1f', //左侧菜单背景, + leftMenuBgThis: '#737373', //左侧菜单选中背景, + leftMenuChildBg: 'rgba(0,0,0,.3)', //左侧菜单子菜单背景, + leftMenuColor: 'rgb(191, 187, 187)', //左侧菜单字体颜色, + leftMenuColorThis: '#ffffff', //左侧菜单选中字体颜色, + tabActiveColor: '#ffa4d1', //tab选项卡选中颜色, + }, + { + headerRightBg: '#1aa094', //头部右侧背景色 + headerRightBgThis: '#197971', //头部右侧选中背景色, + headerRightColor: 'rgba(255,255,255,.7)', //头部右侧字体颜色, + headerRightChildColor: '#676767', //头部右侧下拉字体颜色, + headerRightColorThis: '#ffffff', //头部右侧鼠标选中, + headerRightNavMore: 'rgba(255,255,255,.7)', //头部右侧更多下拉颜色, + headerRightNavMoreBg: '#1aa094', //头部右侧更多下拉列表选中背景色, + headerRightNavMoreColor: '#ffffff', //头部右侧更多下拉列表字体色, + headerRightToolColor: '#bbe3df', //头部缩放按钮样式, + headerLogoBg: '#0c0c0c', //logo背景颜色, + headerLogoColor: '#ffffff', //logo字体颜色, + leftMenuNavMore: 'rgb(191, 187, 187)', //左侧菜单更多下拉样式, + leftMenuBg: '#23262e', //左侧菜单背景, + leftMenuBgThis: '#1aa094', //左侧菜单选中背景, + leftMenuChildBg: 'rgba(0,0,0,.3)', //左侧菜单子菜单背景, + leftMenuColor: 'rgb(191, 187, 187)', //左侧菜单字体颜色, + leftMenuColorThis: '#ffffff', //左侧菜单选中字体颜色, + tabActiveColor: '#1aa094', //tab选项卡选中颜色, + }, + { + headerRightBg: '#1e9fff', //头部右侧背景色 + headerRightBgThis: '#0069b7', //头部右侧选中背景色, + headerRightColor: 'rgba(255,255,255,.7)', //头部右侧字体颜色, + headerRightChildColor: '#676767', //头部右侧下拉字体颜色, + headerRightColorThis: '#ffffff', //头部右侧鼠标选中, + headerRightNavMore: 'rgba(255,255,255,.7)', //头部右侧更多下拉颜色, + headerRightNavMoreBg: '#1e9fff', //头部右侧更多下拉列表选中背景色, + headerRightNavMoreColor: '#ffffff', //头部右侧更多下拉列表字体色, + headerRightToolColor: '#bbe3df', //头部缩放按钮样式, + headerLogoBg: '#0c0c0c', //logo背景颜色, + headerLogoColor: '#ffffff', //logo字体颜色, + leftMenuNavMore: 'rgb(191, 187, 187)', //左侧菜单更多下拉样式, + leftMenuBg: '#1f1f1f', //左侧菜单背景, + leftMenuBgThis: '#1e9fff', //左侧菜单选中背景, + leftMenuChildBg: 'rgba(0,0,0,.3)', //左侧菜单子菜单背景, + leftMenuColor: 'rgb(191, 187, 187)', //左侧菜单字体颜色, + leftMenuColorThis: '#ffffff', //左侧菜单选中字体颜色, + tabActiveColor: '#1e9fff', //tab选项卡选中颜色, + }, + { + headerRightBg: '#ffb800', //头部右侧背景色 + headerRightBgThis: '#d09600', //头部右侧选中背景色, + headerRightColor: 'rgba(255,255,255,.7)', //头部右侧字体颜色, + headerRightChildColor: '#676767', //头部右侧下拉字体颜色, + headerRightColorThis: '#ffffff', //头部右侧鼠标选中, + headerRightNavMore: 'rgba(255,255,255,.7)', //头部右侧更多下拉颜色, + headerRightNavMoreBg: '#d09600', //头部右侧更多下拉列表选中背景色, + headerRightNavMoreColor: '#ffffff', //头部右侧更多下拉列表字体色, + headerRightToolColor: '#bbe3df', //头部缩放按钮样式, + headerLogoBg: '#243346', //logo背景颜色, + headerLogoColor: '#ffffff', //logo字体颜色, + leftMenuNavMore: 'rgb(191, 187, 187)', //左侧菜单更多下拉样式, + leftMenuBg: '#2f4056', //左侧菜单背景, + leftMenuBgThis: '#8593a7', //左侧菜单选中背景, + leftMenuChildBg: 'rgba(0,0,0,.3)', //左侧菜单子菜单背景, + leftMenuColor: 'rgb(191, 187, 187)', //左侧菜单字体颜色, + leftMenuColorThis: '#ffffff', //左侧菜单选中字体颜色, + tabActiveColor: '#ffb800', //tab选项卡选中颜色, + }, + { + headerRightBg: '#e82121', //头部右侧背景色 + headerRightBgThis: '#ae1919', //头部右侧选中背景色, + headerRightColor: 'rgba(255,255,255,.7)', //头部右侧字体颜色, + headerRightChildColor: '#676767', //头部右侧下拉字体颜色, + headerRightColorThis: '#ffffff', //头部右侧鼠标选中, + headerRightNavMore: 'rgba(255,255,255,.7)', //头部右侧更多下拉颜色, + headerRightNavMoreBg: '#ae1919', //头部右侧更多下拉列表选中背景色, + headerRightNavMoreColor: '#ffffff', //头部右侧更多下拉列表字体色, + headerRightToolColor: '#bbe3df', //头部缩放按钮样式, + headerLogoBg: '#0c0c0c', //logo背景颜色, + headerLogoColor: '#ffffff', //logo字体颜色, + leftMenuNavMore: 'rgb(191, 187, 187)', //左侧菜单更多下拉样式, + leftMenuBg: '#1f1f1f', //左侧菜单背景, + leftMenuBgThis: '#3b3f4b', //左侧菜单选中背景, + leftMenuChildBg: 'rgba(0,0,0,.3)', //左侧菜单子菜单背景, + leftMenuColor: 'rgb(191, 187, 187)', //左侧菜单字体颜色, + leftMenuColorThis: '#ffffff', //左侧菜单选中字体颜色, + tabActiveColor: '#e82121', //tab选项卡选中颜色, + }, + { + headerRightBg: '#963885', //头部右侧背景色 + headerRightBgThis: '#772c6a', //头部右侧选中背景色, + headerRightColor: 'rgba(255,255,255,.7)', //头部右侧字体颜色, + headerRightChildColor: '#676767', //头部右侧下拉字体颜色, + headerRightColorThis: '#ffffff', //头部右侧鼠标选中, + headerRightNavMore: 'rgba(255,255,255,.7)', //头部右侧更多下拉颜色, + headerRightNavMoreBg: '#772c6a', //头部右侧更多下拉列表选中背景色, + headerRightNavMoreColor: '#ffffff', //头部右侧更多下拉列表字体色, + headerRightToolColor: '#bbe3df', //头部缩放按钮样式, + headerLogoBg: '#243346', //logo背景颜色, + headerLogoColor: '#ffffff', //logo字体颜色, + leftMenuNavMore: 'rgb(191, 187, 187)', //左侧菜单更多下拉样式, + leftMenuBg: '#2f4056', //左侧菜单背景, + leftMenuBgThis: '#586473', //左侧菜单选中背景, + leftMenuChildBg: 'rgba(0,0,0,.3)', //左侧菜单子菜单背景, + leftMenuColor: 'rgb(191, 187, 187)', //左侧菜单字体颜色, + leftMenuColorThis: '#ffffff', //左侧菜单选中字体颜色, + tabActiveColor: '#963885', //tab选项卡选中颜色, + }, + { + headerRightBg: '#2D8CF0', //头部右侧背景色 + headerRightBgThis: '#0069b7', //头部右侧选中背景色, + headerRightColor: 'rgba(255,255,255,.7)', //头部右侧字体颜色, + headerRightChildColor: '#676767', //头部右侧下拉字体颜色, + headerRightColorThis: '#ffffff', //头部右侧鼠标选中, + headerRightNavMore: 'rgba(255,255,255,.7)', //头部右侧更多下拉颜色, + headerRightNavMoreBg: '#0069b7', //头部右侧更多下拉列表选中背景色, + headerRightNavMoreColor: '#ffffff', //头部右侧更多下拉列表字体色, + headerRightToolColor: '#bbe3df', //头部缩放按钮样式, + headerLogoBg: '#0069b7', //logo背景颜色, + headerLogoColor: '#ffffff', //logo字体颜色, + leftMenuNavMore: 'rgb(191, 187, 187)', //左侧菜单更多下拉样式, + leftMenuBg: '#1f1f1f', //左侧菜单背景, + leftMenuBgThis: '#2D8CF0', //左侧菜单选中背景, + leftMenuChildBg: 'rgba(0,0,0,.3)', //左侧菜单子菜单背景, + leftMenuColor: 'rgb(191, 187, 187)', //左侧菜单字体颜色, + leftMenuColorThis: '#ffffff', //左侧菜单选中字体颜色, + tabActiveColor: '#2d8cf0', //tab选项卡选中颜色, + }, + { + headerRightBg: '#ffb800', //头部右侧背景色 + headerRightBgThis: '#d09600', //头部右侧选中背景色, + headerRightColor: 'rgba(255,255,255,.7)', //头部右侧字体颜色, + headerRightChildColor: '#676767', //头部右侧下拉字体颜色, + headerRightColorThis: '#ffffff', //头部右侧鼠标选中, + headerRightNavMore: 'rgba(255,255,255,.7)', //头部右侧更多下拉颜色, + headerRightNavMoreBg: '#d09600', //头部右侧更多下拉列表选中背景色, + headerRightNavMoreColor: '#ffffff', //头部右侧更多下拉列表字体色, + headerRightToolColor: '#bbe3df', //头部缩放按钮样式, + headerLogoBg: '#d09600', //logo背景颜色, + headerLogoColor: '#ffffff', //logo字体颜色, + leftMenuNavMore: 'rgb(191, 187, 187)', //左侧菜单更多下拉样式, + leftMenuBg: '#2f4056', //左侧菜单背景, + leftMenuBgThis: '#3b3f4b', //左侧菜单选中背景, + leftMenuChildBg: 'rgba(0,0,0,.3)', //左侧菜单子菜单背景, + leftMenuColor: 'rgb(191, 187, 187)', //左侧菜单字体颜色, + leftMenuColorThis: '#ffffff', //左侧菜单选中字体颜色, + tabActiveColor: '#ffb800', //tab选项卡选中颜色, + }, + { + headerRightBg: '#e82121', //头部右侧背景色 + headerRightBgThis: '#ae1919', //头部右侧选中背景色, + headerRightColor: 'rgba(255,255,255,.7)', //头部右侧字体颜色, + headerRightChildColor: '#676767', //头部右侧下拉字体颜色, + headerRightColorThis: '#ffffff', //头部右侧鼠标选中, + headerRightNavMore: 'rgba(255,255,255,.7)', //头部右侧更多下拉颜色, + headerRightNavMoreBg: '#ae1919', //头部右侧更多下拉列表选中背景色, + headerRightNavMoreColor: '#ffffff', //头部右侧更多下拉列表字体色, + headerRightToolColor: '#bbe3df', //头部缩放按钮样式, + headerLogoBg: '#d91f1f', //logo背景颜色, + headerLogoColor: '#ffffff', //logo字体颜色, + leftMenuNavMore: 'rgb(191, 187, 187)', //左侧菜单更多下拉样式, + leftMenuBg: '#1f1f1f', //左侧菜单背景, + leftMenuBgThis: '#3b3f4b', //左侧菜单选中背景, + leftMenuChildBg: 'rgba(0,0,0,.3)', //左侧菜单子菜单背景, + leftMenuColor: 'rgb(191, 187, 187)', //左侧菜单字体颜色, + leftMenuColorThis: '#ffffff', //左侧菜单选中字体颜色, + tabActiveColor: '#e82121', //tab选项卡选中颜色, + }, + { + headerRightBg: '#963885', //头部右侧背景色 + headerRightBgThis: '#772c6a', //头部右侧选中背景色, + headerRightColor: 'rgba(255,255,255,.7)', //头部右侧字体颜色, + headerRightChildColor: '#676767', //头部右侧下拉字体颜色, + headerRightColorThis: '#ffffff', //头部右侧鼠标选中, + headerRightNavMore: 'rgba(255,255,255,.7)', //头部右侧更多下拉颜色, + headerRightNavMoreBg: '#772c6a', //头部右侧更多下拉列表选中背景色, + headerRightNavMoreColor: '#ffffff', //头部右侧更多下拉列表字体色, + headerRightToolColor: '#bbe3df', //头部缩放按钮样式, + headerLogoBg: '#772c6a', //logo背景颜色, + headerLogoColor: '#ffffff', //logo字体颜色, + leftMenuNavMore: 'rgb(191, 187, 187)', //左侧菜单更多下拉样式, + leftMenuBg: '#2f4056', //左侧菜单背景, + leftMenuBgThis: '#626f7f', //左侧菜单选中背景, + leftMenuChildBg: 'rgba(0,0,0,.3)', //左侧菜单子菜单背景, + leftMenuColor: 'rgb(191, 187, 187)', //左侧菜单字体颜色, + leftMenuColorThis: '#ffffff', //左侧菜单选中字体颜色, + tabActiveColor: '#963885', //tab选项卡选中颜色, + } + ]; + // 检查bgcolorId是否未定义 + if (bgcolorId === undefined) { + // 如果bgcolorId未定义,返回整个背景颜色配置对象 + return bgColorConfig; + } else { + // 如果bgcolorId已定义,返回对应ID的背景颜色配置 + return bgColorConfig[bgcolorId]; + } + } + }, + + /** + * 初始化 + * @param options + */ + render: function (options) { + // 设置默认背景颜色选项,如果未定义则设为false + options.bgColorDefault = options.bgColorDefault || false; + // 设置监听选项,如果未定义则设为false + options.listen = options.listen || false; + // 从sessionStorage中获取背景颜色ID + var bgcolorId = sessionStorage.getItem('layuiminiBgcolorId'); + // 如果背景颜色ID不存在或为空字符串,则使用默认背景颜色ID + if (bgcolorId === null || bgcolorId === undefined || bgcolorId === '') { + bgcolorId = options.bgColorDefault; + } + // 构建主题CSS文件 + miniTheme.buildThemeCss(bgcolorId); + // 如果需要监听,则调用监听方法 + if (options.listen) miniTheme.listen(options); + }, + + /** + * 构建主题样式 + * @param bgcolorId + * @returns {boolean} + */ + buildThemeCss: function (bgcolorId) { + // 检查是否提供了背景颜色ID,如果没有则返回false + if (!bgcolorId) { + return false; + } + // 获取指定ID的背景颜色配置数据 + var bgcolorData = miniTheme.config(bgcolorId); + // 构建CSS样式字符串 + // 定义一个包含CSS样式的字符串,用于设置头部右侧背景色 + var styleHtml = '/*头部右侧背景色 headerRightBg */\n' + + '.layui-layout-admin .layui-header {\n' + + // 使用bgcolorData对象中的headerRightBg属性值设置背景颜色,并添加!important确保优先级 + ' background-color: ' + bgcolorData.headerRightBg + ' !important;\n' + + '}\n' + + '\n' + + '/*头部右侧选中背景色 headerRightBgThis */\n' + + // 选择器:匹配layui-layout-admin类下的layui-header内的layuimini-header-content的子元素ul中的layui-nav-item类和layui-this类,以及layuimini-tool内的i元素在悬停时 + '.layui-layout-admin .layui-header .layuimini-header-content > ul > .layui-nav-item.layui-this, .layuimini-tool i:hover {\n' + + // 设置背景颜色为bgcolorData对象中的headerRightBgThis属性值,并添加!important以覆盖其他样式 + ' background-color: ' + bgcolorData.headerRightBgThis + ' !important;\n' + + '}\n' + + '\n' + + '/*头部右侧字体颜色 headerRightColor */\n' + + // 选择器:.layui-layout-admin 下的 .layui-header 中的 .layui-nav 的 .layui-nav-item 内的 a 元素 + '.layui-layout-admin .layui-header .layui-nav .layui-nav-item a {\n' + + // 设置字体颜色为 bgcolorData 对象中的 headerRightColor 属性值 + ' color: ' + bgcolorData.headerRightColor + ';\n' + + '}\n' + + '\n' + + /**头部右侧下拉字体颜色 headerRightChildColor */ + '.layui-layout-admin .layui-header .layui-nav .layui-nav-item .layui-nav-child a {\n' + + // 设置下拉菜单中链接的字体颜色,使用 !important 确保优先级最高 + ' color: ' + bgcolorData.headerRightChildColor + '!important;\n' + + '}\n' + + '\n' + + // 定义CSS样式,当鼠标悬停在头部右侧菜单项上时,改变其颜色 + '/*头部右侧鼠标选中 headerRightColorThis */\n' + + '.layui-header .layuimini-menu-header-pc.layui-nav .layui-nav-item a:hover, .layui-header .layuimini-header-menu.layuimini-pc-show.layui-nav .layui-this {\n' + + // 设置颜色为bgcolorData对象中的headerRightColorThis属性值,并使用!important确保优先级最高 + ' color: ' + bgcolorData.headerRightColorThis + ' !important;\n' + + '}\n' + + '\n' + + // 定义头部右侧更多下拉颜色样式 + '/*头部右侧更多下拉颜色 headerRightNavMore */\n' + + '.layui-header .layui-nav .layui-nav-more {\n' + + // 设置边框顶部颜色为指定的颜色,并使用!important确保优先级最高 + ' border-top-color: ' + bgcolorData.headerRightNavMore + ' !important;\n' + + '}\n' + + '\n' + + /**头部右侧更多下拉颜色 headerRightNavMore */ + '.layui-header .layui-nav .layui-nav-mored, .layui-header .layui-nav-itemed > a .layui-nav-more {\n' + + // 设置边框颜色,上和左为透明,右和下为指定的颜色 + ' border-color: transparent transparent ' + bgcolorData.headerRightNavMore + ' !important;\n' + + '}\n' + + '\n' + + /**头部右侧更多下拉配置色 headerRightNavMoreBg headerRightNavMoreColor */ + '.layui-header .layui-nav .layui-nav-child dd.layui-this a, .layui-header .layui-nav .layui-nav-child dd.layui-this,\n' + + '.layui-layout-admin .layui-header .layui-nav .layui-nav-item .layui-nav-child .layui-this a,\n' + + '.layui-layout-admin .layui-header .layui-nav .layui-nav-item .layui-nav-child .layui-this {\n' + + // 设置背景颜色为 headerRightNavMoreBg,并使用 !important 提升优先级 + ' background-color: ' + bgcolorData.headerRightNavMoreBg + ' !important;\n' + + // 设置文字颜色为 headerRightNavMoreColor,并使用 !important 提升优先级 + ' color:' + bgcolorData.headerRightNavMoreColor + ' !important;\n' + + '}\n' + + '\n' + + // 定义头部缩放按钮样式,使用传入的headerRightToolColor颜色值 + '/*头部缩放按钮样式 headerRightToolColor */\n' + + '.layui-layout-admin .layui-header .layuimini-tool i {\n' + + // 设置头部工具图标的颜色 + ' color: ' + bgcolorData.headerRightToolColor + ';\n' + + '}\n' + + '\n' + + // 生成包含logo背景颜色的CSS样式字符串 + '/*logo背景颜色 headerLogoBg */\n' + + '.layui-layout-admin .layuimini-logo {\n' + + // 设置logo的背景颜色,使用bgcolorData对象中的headerLogoBg属性值 + ' background-color: ' + bgcolorData.headerLogoBg + ' !important;\n' + + '}\n' + + '\n' + + // 定义一个CSS样式规则,用于设置layui-layout-admin类下的layuimini-logo中的h1元素的字体颜色 + '/*logo字体颜色 headerLogoColor */\n' + + '.layui-layout-admin .layuimini-logo h1 {\n' + + // 将h1元素的颜色设置为bgcolorData对象中的headerLogoColor属性值 + ' color: ' + bgcolorData.headerLogoColor + ';\n' + + '}\n' + + '\n' + + '/*左侧菜单更多下拉样式 leftMenuNavMore */\n' + + // 选择左侧菜单中的更多按钮,并设置其顶部边框颜色 + '.layuimini-menu-left .layui-nav .layui-nav-more,.layuimini-menu-left-zoom.layui-nav .layui-nav-more {\n' + + // 使用变量bgcolorData.leftMenuNavMore的值来设置顶部边框颜色 + ' border-top-color: ' + bgcolorData.leftMenuNavMore + ';\n' + + '}\n' + + '\n' + + /** 左侧菜单更多下拉样式 leftMenuNavMore */ + '.layuimini-menu-left .layui-nav .layui-nav-mored, .layuimini-menu-left .layui-nav-itemed > a .layui-nav-more, .layuimini-menu-left-zoom.layui-nav .layui-nav-mored, .layuimini-menu-left-zoom.layui-nav .layui-nav-itemed > a .layui-nav-more {\n' + + // 设置边框颜色,上和左为透明,右和下为指定背景色 + ' border-color: transparent transparent ' + bgcolorData.leftMenuNavMore + ' !important;\n' + + '}\n' + + '\n' + + '/*左侧菜单背景 leftMenuBg */\n' + + // 设置左侧菜单的背景颜色为黑色,并应用到指定的元素上 + '.layui-side.layui-bg-black, .layui-side.layui-bg-black > .layuimini-menu-left > ul, .layuimini-menu-left-zoom > ul {\n' + + // 使用变量bgcolorData中的leftMenuBg值作为背景颜色,并添加!important确保优先级 + ' background-color: ' + bgcolorData.leftMenuBg + ' !important;\n' + + '}\n' + + '\n' + + // 左侧菜单选中背景样式定义 + '/*左侧菜单选中背景 leftMenuBgThis */\n' + + // 设置左侧菜单中当前选中项的背景颜色 + '.layuimini-menu-left .layui-nav-tree .layui-this, .layuimini-menu-left .layui-nav-tree .layui-this > a, .layuimini-menu-left .layui-nav-tree .layui-this a, .layuimini-menu-left .layui-nav-tree .layui-nav-child dd.layui-this a, .layuimini-menu-left .layui-nav-tree .layui-nav-child dd.layui-this,\n' + + // 设置左侧菜单缩放状态下当前选中项的背景颜色 + '.layuimini-menu-left-zoom.layui-nav-tree .layui-this, .layuimini-menu-left-zoom.layui-nav-tree .layui-this > a, .layuimini-menu-left-zoom.layui-nav-tree .layui-this a, .layuimini-menu-left-zoom.layui-nav-tree .layui-nav-child dd.lavueimini-tabactiveColoryt a, .layuimini-menu-left-zoom.layui-nav-tree .layui-nav-child dd.layui-this {\n' + + // 应用背景颜色,并使用!important确保优先级最高 + ' background-color: ' + bgcolorData.leftMenuBgThis + '!important\n' + + '}\n' + + '\n' + + '/*左侧菜单子菜单背景 leftMenuChildBg */\n' + + // 选择左侧菜单中已展开的父菜单项下的子菜单 + '.layuimini-menu-left .layui-nav-itemed > .layui-nav-child{\n' + + // 设置子菜单的背景颜色,并使用!important确保优先级最高 + ' background-color: ' + bgcolorData.leftMenuChildBg + ' !important;\n' + + '}\n' + + '\n' + + '/*左侧菜单字体颜色 leftMenuColor */\n' + + // 选择左侧菜单中的所有导航项链接,并设置其字体颜色为bgcolorData.leftMenuColor + '.layuimini-menu-left .layui-nav .layui-nav-item a, .layuimini-menu-left-zoom.layui-nav .layui-nav-item a {\n' + + // 使用!important确保颜色设置不会被其他CSS规则覆盖 + ' color: ' + bgcolorData.leftMenuColor + ' !important;\n' + + '}\n' + + '\n' + + // 定义左侧菜单选中字体颜色样式 + '/*左侧菜单选中字体颜色 leftMenuColorThis */\n' + + '.layuimini-menu-left .layui-nav .layui-nav-item a:hover, .layuimini-menu-left .layui-nav .layui-this a, .layuimini-menu-left-zoom.layui-nav .layui-nav-item a:hover, .layuimini-menu-left-zoom.layui-nav .layui-this a {\n' + + // 设置鼠标悬停或当前选中的菜单项字体颜色,并使用!important确保优先级最高 + ' color:' + bgcolorData.leftMenuColorThis + '!important;\n' + + '}\n' + + '\n' + + /** + * 设置tab选项卡选中颜色 + * @param {string} bgcolorData.tabActiveColor - tab选项卡选中时的背景颜色 + */ + '.layuimini-tab .layui-tab-title .layui-this .layuimini-tab-active {\n' + + // 设置背景颜色为传入的tabActiveColor值 + ' background-color: ' + bgcolorData.tabActiveColor + ';\n' + + '}\n'; + //将生成的CSS样式插入到页面中 + // 将styleHtml的内容插入到ID为'layuimini-bg-color'的元素中 + $('#layuimini-bg-color').html(styleHtml); + /** + * 构建主题选择html + * @param options + * @returns {string} + */ + /** + * 构建背景颜色HTML字符串 + * @param {Object} options - 配置选项对象 + * @param {number} options.bgColorDefault - 默认背景颜色ID + * @returns {string} 生成的背景颜色HTML字符串 + */ + buildBgColorHtml: function (options) { + // 如果未定义默认背景颜色ID,则设置为0 + options.bgColorDefault = options.bgColorDefault || 0; + + // 从sessionStorage中获取当前背景颜色ID + var bgcolorId = parseInt(sessionStorage.getItem('layuiminiBgcolorId')); + + // 如果获取到的背景颜色ID不是数字,则使用默认背景颜色ID + if (isNaN(bgcolorId)) bgcolorId = options.bgColorDefault; + + // 获取背景颜色配置 + var bgColorConfig = miniTheme.config(); + + // 初始化HTML字符串 + var html = ''; + + // 遍历背景颜色配置 + $.each(bgColorConfig, function (key, val) { + // 如果当前背景颜色ID与配置中的ID相同,添加选中样式 + if (key === bgcolorId) { + html += '
  • \n'; + } else { + html += '
  • \n'; + } + + // 构建背景颜色HTML片段 + // 开始构建HTML字符串,添加一个链接元素,并设置其属性和样式 + html += '\n' + + // 添加第一个div元素,包含两个span子元素,用于显示头部背景颜色 + '
    \n' + + // 添加第二个div元素,包含两个span子元素,用于显示左侧菜单背景颜色和右侧白色背景 + '
    \n' + + // 关闭链接元素 + '
    \n' + + // 关闭列表项元素 + '
  • '; + }); + + // 返回生成的HTML字符串 + return html; + }, + + /** + * 监听 + * @param options + */ + listen: function (options) { + // 为body元素绑定点击事件,当点击带有data-bgcolor属性的元素时触发 + $('body').on('click', '[data-bgcolor]', function () { + // 显示加载动画,设置加载时间为2秒 + var loading = layer.load(0, {shade: false, time: 2 * 1000}); + // 获取当前文档高度减去60像素的值 + var clientHeight = (document.documentElement.clientHeight) - 60; + // 调用miniTheme对象的buildBgColorHtml方法生成背景颜色HTML代码 + var bgColorHtml = miniTheme.buildBgColorHtml(options); + // 构建配色方案的HTML结构 + // 定义一个包含HTML结构的字符串变量html + var html = '
    \n' + + // 添加一个包含配色方案标题的div元素 + '
    \n' + + // 在标题div中添加一个span元素,显示文本“配色方案” + '配色方案\n' + + '
    \n' + + // 添加一个包含配色内容的div元素 + '
    \n' + + // 在内容div中添加一个ul元素,用于存放背景颜色选项 + '
      \n' + bgColorHtml + '
    \n' + + '
    \n' + + '
    ' + + ''; + // 打开一个layer弹出层,显示配色方案选择器 + layer.open({ + type: 1, // 弹出层类型为页面层 + title: false, // 不显示标题 + closeBtn: 0, // 不显示关闭按钮 + shade: 0.2, // 遮罩透明度 + anim: 2, // 弹出动画效果 + shadeClose: true, // 点击遮罩区域是否允许关闭 + id: 'layuiminiBgColor', // 弹出层ID + area: ['340px', clientHeight + 'px'], // 弹出层宽高 + offset: 'rb', // 弹出层位置,右下角 + content: html, // 弹出层内容 + success: function (index, layero) { + // 弹出层成功打开后的回调函数 + }, + end: function () { + // 弹出层关闭后的回调函数,移除选中的背景颜色样式 + $('.layuimini-select-bgcolor').removeClass('layui-this'); + } + }); + // 关闭加载动画 + layer.close(loading); + }); + } + $('body').on('click', '[data-select-bgcolor]', function () { + // 获取点击元素的 data-select-bgcolor 属性值,作为背景颜色ID + var bgcolorId = $(this).attr('data-select-bgcolor'); + + // 移除当前选中的背景颜色样式类 + $('.layuimini-color .color-content ul .layui-this').attr('class', ''); + + // 为点击的元素添加选中的样式类 + $(this).attr('class', 'layui-this'); + + // 将背景颜色ID存储到 sessionStorage 中,以便后续使用 + sessionStorage.setItem('layuiminiBgcolorId', bgcolorId); + + // 调用 miniTheme.render 方法,更新主题背景颜色 + miniTheme.render({ + // 设置默认背景颜色为选中的背景颜色ID + bgColorDefault: bgcolorId, + // 不监听变化 + listen: false, + }); + }); + } + }; + + // 导出名为 "miniTheme" 的模块,供其他文件使用 + exports("miniTheme", miniTheme); +}) +; \ No newline at end of file diff --git a/cff/miniTongji.js b/cff/miniTongji.js new file mode 100644 index 0000000..fc1b581 --- /dev/null +++ b/cff/miniTongji.js @@ -0,0 +1,47 @@ +layui.define(["jquery"], function (exports) { + // 引入jQuery库,并赋值给变量$ + var $ = layui.$; + + // 定义一个名为miniTongji的对象 + var miniTongji = { + + /** + * 初始化 + * @param options + */ + render: function (options) { + // 设置 options.specific 的默认值为 false + options.specific = options.specific || false; + // 设置 options.domains 的默认值为空数组 + options.domains = options.domains || []; + // 获取当前窗口的主机名 + var domain = window.location.hostname; + // 如果 options.specific 为 false,或者 options.specific 为 true 且域名在 options.domains 中 + if (options.specific === false || (options.specific === true && options.domains.indexOf(domain) >=0)) { + // 调用 miniTongji 对象的 listen 方法 + miniTongji.listen(); + } + }, + + /** + * 监听统计代码 + */ + listen: function () { + // 定义一个变量_hmt,如果_hmt未定义则初始化为空数组 + var _hmt = _hmt || []; + (function () { + // 创建一个新的script元素 + var hm = document.createElement("script"); + // 设置script元素的src属性为百度统计的脚本URL + hm.src = "https://hm.baidu.com/hm.js?d97abf6d61c21d773f97835defbdef4e"; + // 获取文档中第一个script标签 + var s = document.getElementsByTagName("script")[0]; + // 将新创建的script元素插入到第一个script标签之前 + s.parentNode.insertBefore(hm, s); + })(); + } + }; + + // 导出名为 "miniTongji" 的函数,供其他模块使用 + exports("miniTongji", miniTongji); +}); \ No newline at end of file diff --git a/cff/treetable.js b/cff/treetable.js new file mode 100644 index 0000000..adcfb5f --- /dev/null +++ b/cff/treetable.js @@ -0,0 +1,308 @@ +layui.define(['layer', 'table'], function (exports) { + // 获取jQuery对象 + var $ = layui.jquery; + // 获取layer模块 + var layer = layui.layer; + // 获取table模块 + var table = layui.table; + + var treetable = { + // 渲染树形表格 + render: function (param) { + // 检查参数是否合法 + if (!treetable.checkParam(param)) { + return; // 如果参数不合法,直接返回 + } + // 获取数据 + if (param.data) { + // 如果参数中包含数据,则直接初始化树形表格 + treetable.init(param, param.data); + } else { + // 如果参数中没有数据,则通过AJAX请求获取数据 + $.getJSON(param.url, param.where, function (res) { + // 使用获取到的数据初始化树形表格 + treetable.init(param, res.data); + }); + } + }, + // 渲染表格 + init: function (param, data) { + // 初始化一个空数组,用于存储处理后的数据 + var mData = []; + // 获取回调函数done + var doneCallback = param.done; + // 将传入的数据赋值给tNodes变量 + var tNodes = data; + // 遍历tNodes数组中的每一个元素 + for (var i = 0; i < tNodes.length; i++) { + // 当前遍历到的元素 + var tt = tNodes[i]; + // 如果当前元素没有id字段 + if (!tt.id) { + // 如果参数中没有treeIdName字段 + if (!param.treeIdName) { + // 弹出提示信息,告知treeIdName不能为空 + layer.msg('参数treeIdName不能为空', {icon: 5}); + // 终止函数执行 + return; + } + // 使用treeIdName对应的值作为id + tt.id = tt[param.treeIdName]; + } + // 检查tt对象是否具有pid属性 + if (!tt.pid) { + // 如果param对象没有treePidName属性,则提示错误信息并返回 + if (!param.treePidName) { + layer.msg('参数treePidName不能为空', {icon: 5}); + return; + } + // 将tt对象的pid属性设置为tt对象中对应treePidName属性的值 + tt.pid = tt[param.treePidName]; + } + } + + // 对数据进行排序 + var sort = function (s_pid, data) { + // 遍历数据数组 + for (var i = 0; i < data.length; i++) { + // 如果当前元素的父ID等于目标父ID + if (data[i].pid == s_pid) { + // 获取mData数组的长度 + var len = mData.length; + // 如果mData数组不为空且最后一个元素的ID等于目标父ID + if (len > 0 && mData[len - 1].id == s_pid) { + // 将最后一个元素的isParent属性设置为true + mData[len - 1].isParent = true; + } + // 将当前元素添加到mData数组中 + mData.push(data[i]); + // 递归调用sort函数,处理当前元素的子元素 + sort(data[i].id, data); + } + } + }; + } + }; + // 调用sort函数,对树形结构进行排序 + // 参数param.treeSpid表示树的根节点ID + // tNodes是包含所有节点信息的数组 + sort(param.treeSpid, tNodes); + + // 重写参数 + // 将param对象的url属性设置为undefined + param.url = undefined; + + // 将param对象的data属性设置为mData + param.data = mData; + + // 设置param对象的page属性,包含count和limit两个子属性 + param.page = { + // count属性表示数据的总数量,等于data数组的长度 + count: param.data.length, + // limit属性表示每页显示的数据量,等于data数组的长度 + limit: param.data.length + }; + param.cols[0][param.treeColIndex].templet = function (d) { + // 获取当前节点的ID + var mId = d.id; + // 获取当前节点的父节点ID + var mPid = d.pid; + // 判断当前节点是否为目录(是否有子节点) + var isDir = d.isParent; + // 计算当前节点前面需要填充的空白数量 + var emptyNum = treetable.getEmptyNum(mPid, mData); + // 初始化图标HTML字符串 + var iconHtml = ''; + // 根据空白数量生成相应数量的空白图标 + for (var i = 0; i < emptyNum; i++) { + iconHtml += ''; + } + if (isDir) { + // 如果是目录,添加两个图标:一个三角形和一个图层图标 + iconHtml += ' '; + } else { + // 如果是文件,添加一个文件图标 + iconHtml += ''; + } + // 添加两个空格作为间隔 + iconHtml += '  '; + // 根据是否是目录设置类型为'dir'或'file' + var ttype = isDir ? 'dir' : 'file'; + // 创建包含图标和名称的HTML字符串 + var vg = ''; + // 返回完整的HTML字符串 + return vg + iconHtml + d[param.cols[0][param.treeColIndex].field] + '' + + param.done = function (res, curr, count) { + // 在当前元素之后插入一个带有'class'为'treeTable'的元素 + $(param.elem).next().addClass('treeTable'); + + // 隐藏'treeTable'中的分页控件 + $('.treeTable .layui-table-page').css('display', 'none'); + + // 设置'treeTable'元素的'treeLinkage'属性 + $(param.elem).next().attr('treeLinkage', param.treeLinkage); + + // 注释掉的代码:绑定点击事件,用于切换行显示状态 + /*$('.treeTable .treeTable-icon').click(function () { + treetable.toggleRows($(this), param.treeLinkage); + });*/ + + // 如果设置了默认关闭树形结构,则折叠所有节点 + if (param.treeDefaultClose) { + treetable.foldAll(param.elem); + } + + // 如果存在回调函数,则执行回调函数 + if (doneCallback) { + doneCallback(res, curr, count); + } + }; + + // 渲染表格 + table.render(param); + }, + // 计算缩进的数量 + getEmptyNum: function (pid, data) { + // 初始化缩进数量为0 + var num = 0; + // 如果pid不存在,直接返回缩进数量0 + if (!pid) { + return num; + } + var tPid; + // 遍历数据数组 + for (var i = 0; i < data.length; i++) { + // 如果当前元素的id与pid匹配 + if (pid == data[i].id) { + // 增加缩进数量 + num += 1; + // 更新tPid为当前元素的父id + tPid = data[i].pid; + // 跳出循环 + break; + } + } + return num + treetable.getEmptyNum(tPid, data); + }, + // 展开/折叠行 + toggleRows: function ($dom, linkage) { + // 获取当前行的树类型 + var type = $dom.attr('lay-ttype'); + // 如果当前行是文件类型,则直接返回 + if ('file' == type) { + return; + } + // 获取当前行的ID + var mId = $dom.attr('lay-tid'); + // 判断当前行是否已经展开 + var isOpen = $dom.hasClass('open'); + // 如果已经展开,则移除展开类,否则添加展开类 + if (isOpen) { + $dom.removeClass('open'); + } else { + $dom.addClass('open'); + } + // 遍历当前行所在的tbody中的所有行 + $dom.closest('tbody').find('tr').each(function () { + // 获取当前行的图标元素 + var $ti = $(this).find('.treeTable-icon'); + // 获取当前行的父ID和类型 + var pid = $ti.attr('lay-tpid'); + var ttype = $ti.attr('lay-ttype'); + // 判断当前行的图标是否已经展开 + var tOpen = $ti.hasClass('open'); + // 如果当前行的父ID与目标ID相同 + if (mId == pid) { + // 如果目标行已经展开,则隐藏当前行 + if (isOpen) { + $(this).hide(); + // 如果当前行是目录且已经展开,则触发点击事件 + if ('dir' == ttype && tOpen == isOpen) { + $ti.trigger('click'); + } + } else { + // 如果目标行未展开,则显示当前行 + $(this).show(); + // 如果需要联动展开且当前行是目录且已经展开,则触发点击事件 + if (linkage && 'dir' == ttype && tOpen == isOpen) { + $ti.trigger('click'); + } + } + } + }); + }, + // 检查参数 + checkParam: function (param) { + // 检查参数param的treeSpid属性是否为空或未定义,如果是则弹出提示信息并返回false + if (!param.treeSpid && param.treeSpid != 0) { + layer.msg('参数treeSpid不能为空', {icon: 5}); + return false; + } + + // 检查参数param的treeColIndex属性是否为空或未定义,如果是则弹出提示信息并返回false + if (!param.treeColIndex && param.treeColIndex != 0) { + layer.msg('参数treeColIndex不能为空', {icon: 5}); + return false; + } + + // 如果所有检查都通过,则返回true + return true; + } + }, + // 展开所有 + expandAll: function (dom) { + // 获取传入的DOM元素,并找到其下一个具有'.treeTable'类的元素 + $(dom).next('.treeTable').find('.layui-table-body tbody tr').each(function () { + // 在当前行中找到具有'.treeTable-icon'类的元素 + var $ti = $(this).find('.treeTable-icon'); + // 获取该元素的'lay-ttype'属性值 + var ttype = $ti.attr('lay-ttype'); + // 检查该元素是否具有'open'类 + var tOpen = $ti.hasClass('open'); + // 如果'lay-ttype'属性值为'dir'且没有'open'类,则触发点击事件 + if ('dir' == ttype && !tOpen) { + $ti.trigger('click'); + } + }); + }, + // 折叠所有 + foldAll: function (dom) { + // 获取传入的DOM元素,并找到其下一个具有'.treeTable'类的元素 + $(dom).next('.treeTable').find('.layui-table-body tbody tr').each(function () { + // 在当前行中找到具有'.treeTable-icon'类的元素 + var $ti = $(this).find('.treeTable-icon'); + // 获取该元素的'lay-ttype'属性值 + var ttype = $ti.attr('lay-ttype'); + // 检查该元素是否具有'open'类 + var tOpen = $ti.hasClass('open'); + // 如果'lay-ttype'属性值为'dir'且该元素具有'open'类 + if ('dir' == ttype && tOpen) { + // 触发该元素的点击事件 + $ti.trigger('click'); + } + }); + } + }; + + // 加载并链接外部CSS文件,用于树形表格的样式 + layui.link(layui.cache.base + 'treetable-lay/treetable.css'); + + // 给图标列绑定事件 + $('body').on('click', '.treeTable .treeTable-icon', function () { + // 获取当前点击的图标所在的树表元素的 treeLinkage 属性值 + var treeLinkage = $(this).parents('.treeTable').attr('treeLinkage'); + + // 如果 treeLinkage 属性值为 'true',则调用 treetable.toggleRows 方法并传入 true 参数 + if ('true' == treeLinkage) { + treetable.toggleRows($(this), true); + } else { + // 否则调用 treetable.toggleRows 方法并传入 false 参数 + treetable.toggleRows($(this), false); + } + }); + }); + + // 导出名为 'treetable' 的模块,并将其赋值为 treetable 函数或对象 + exports('treetable', treetable); +}); diff --git a/cff/ztree.js b/cff/ztree.js new file mode 100644 index 0000000..59d797e --- /dev/null +++ b/cff/ztree.js @@ -0,0 +1,6148 @@ +layui.define(['jquery'],function(exports){ + // 获取layui的jQuery模块 +} + let jQuery = layui.jquery; + + (function ($) { + // 定义设置对象、根节点对象和缓存对象 + var settings = {}, roots = {}, caches = {}, + // 核心默认常量 + _consts = { + className: { + BUTTON: "button", // 按钮类名 + LEVEL: "level", // 层级类名 + ICO_LOADING: "ico_loading", // 加载图标类名 + SWITCH: "switch", // 开关类名 + NAME: 'node_name' // 节点名称类名 + }, + event: { + // 节点创建事件 + NODECREATED: "ztree_nodeCreated", + // 点击事件 + CLICK: "ztree_click", + // 展开事件 + EXPAND: "ztree_expand", + // 折叠事件 + COLLAPSE: "ztree_collapse", + // 异步加载成功事件 + ASYNC_SUCCESS: "ztree_async_success", + // 异步加载错误事件 + ASYNC_ERROR: "ztree_async_error", + // 移除事件 + REMOVE: "ztree_remove", + // 选中事件 + SELECTED: "ztree_selected", + // 取消选中事件 + UNSELECTED: "ztree_unselected" + }, + id: { + // 定义一个对象,用于存储不同元素的ID前缀 + A: "_a", // 为元素A设置ID前缀为"_a" + ICON: "_ico", // 为ICON元素设置ID前缀为"_ico" + SPAN: "_span", // 为SPAN元素设置ID前缀为"_span" + SWITCH: "_switch", // 为SWITCH元素设置ID前缀为"_switch" + UL: "_ul" // 为UL元素设置ID前缀为"_ul" + }, + line: { + // 定义一个对象,用于存储不同线条类型的名称 + ROOT: "root", // 根线条类型 + ROOTS: "roots", // 多根线条类型 + CENTER: "center", // 中心线条类型 + BOTTOM: "bottom", // 底部线条类型 + NOLINE: "noline", // 无线条类型 + LINE: "line" // 普通线条类型 + }, + folder: { + // 定义文件夹状态为打开 + OPEN: "open", + // 定义文件夹状态为关闭 + CLOSE: "close", + // 定义文件夹类型为文档 + DOCU: "docu" + }, + node: { + // 当前选中的节点 + CURSELECTED: "curSelectedNode" + } + }, + //default setting of core + _setting = { + // 树的ID,用于标识树的唯一性 + treeId: "", + // 树的对象实例,初始化为null + treeObj: null, + // 视图设置对象 + view: { + // 自定义DOM节点添加函数,默认为null + addDiyDom: null, + // 是否自动取消选中状态,默认为true + autoCancelSelected: true, + // 双击展开节点,默认为true + dblClickExpand: true, + // 展开速度,可选值为"fast"或"slow" + expandSpeed: "fast", + // 字体样式设置,默认为空对象 + fontCss: {}, + // 节点类名设置,默认为空对象 + nodeClasses: {}, + // 节点名称是否作为HTML解析,默认为false + nameIsHTML: false, + // 是否允许多选,默认为true + selectedMulti: true, + // 是否显示图标,默认为true + showIcon: true, + // 是否显示连接线,默认为true + showLine: true, + // 是否显示标题,默认为true + showTitle: true, + // 文本是否可选择,默认为false + txtSelectedEnable: false + }, + data: { + // 定义树形结构数据的键名 + key: { + // 表示节点是否有子节点的键名 + isParent: "isParent", + // 表示子节点集合的键名 + children: "children", + // 表示节点名称的键名 + name: "name", + // 表示节点标题的键名,初始为空字符串 + title: "", + // 表示节点URL的键名 + url: "url", + // 表示节点图标的键名 + icon: "icon" + }, + // 定义渲染时使用的键名 + render: { + // 渲染时使用的名称键名,初始为null + name: null, + // 渲染时使用的标题键名,初始为null + title: null, + }, + simpleData: { + // 启用或禁用简单数据模式 + enable: false, + // 节点的唯一标识键名 + idKey: "id", + // 父节点的标识键名 + pIdKey: "pId", + // 根节点的父节点标识,默认为null表示没有父节点 + rootPId: null + }, + keep: { + // 是否保留父节点信息 + parent: false, + // 是否保留叶子节点信息 + leaf: false + } + }, + async: { + // 是否启用异步请求,默认为false + enable: false, + // 设置请求的内容类型,默认为"application/x-www-form-urlencoded" + contentType: "application/x-www-form-urlencoded", + // 设置请求的类型,默认为"post" + type: "post", + // 设置响应的数据类型,默认为"text" + dataType: "text", + // 设置请求的头部信息,默认为空对象 + headers: {}, + // 设置XHR对象的额外字段,默认为空对象 + xhrFields: {}, + // 设置请求的URL地址,默认为空字符串 + url: "", + // 自动添加到请求参数中的键值对数组,默认为空数组 + autoParam: [], + // 其他手动添加的请求参数键值对数组,默认为空数组 + otherParam: [], + // 数据过滤器函数,用于处理响应数据,默认为null + dataFilter: null + }, + callback: { + // 在异步操作之前执行的回调函数,默认为null + beforeAsync: null, + // 在节点被点击之前执行的回调函数,默认为null + beforeClick: null, + // 在节点被双击之前执行的回调函数,默认为null + beforeDblClick: null, + // 在节点被右键点击之前执行的回调函数,默认为null + beforeRightClick: null, + // 在鼠标按下节点之前执行的回调函数,默认为null + beforeMouseDown: null, + // 在鼠标释放节点之前执行的回调函数,默认为null + beforeMouseUp: null, + // 在节点展开之前执行的回调函数,默认为null + beforeExpand: null, + // 在节点折叠之前执行的回调函数,默认为null + beforeCollapse: null, + // 在节点移除之前执行的回调函数,默认为null + beforeRemove: null, + + // 异步操作失败时执行的回调函数,默认为null + onAsyncError: null, + // 异步操作成功时执行的回调函数,默认为null + onAsyncSuccess: null, + // 节点创建后执行的回调函数,默认为null + onNodeCreated: null, + // 节点被点击时执行的回调函数,默认为null + onClick: null, + // 节点被双击时执行的回调函数,默认为null + onDblClick: null, + // 节点被右键点击时执行的回调函数,默认为null + onRightClick: null, + // 鼠标按下节点时执行的回调函数,默认为null + onMouseDown: null, + // 鼠标释放节点时执行的回调函数,默认为null + onMouseUp: null, + // 节点展开时执行的回调函数,默认为null + onExpand: null, + // 节点折叠时执行的回调函数,默认为null + onCollapse: null, + // 节点移除时执行的回调函数,默认为null + onRemove: null + } + }, + //default root of core + //zTree use root to save full data + /** + * 初始化根节点 + * @param {Object} setting - 配置对象 + */ + _initRoot = function (setting) { + // 获取根节点,如果不存在则创建一个新的空对象 + var r = data.getRoot(setting); + if (!r) { + r = {}; + // 将新创建的根节点设置到数据中 + data.setRoot(setting, r); + } + // 初始化根节点的子节点为空数组 + data.nodeChildren(setting, r, []); + // 初始化根节点的扩展触发标志为false + r.expandTriggerFlag = false; + // 初始化当前选中列表为空数组 + r.curSelectedList = []; + // 初始化无选择状态为true + r.noSelection = true; + // 初始化已创建节点列表为空数组 + r.createdNodes = []; + // 初始化zId为0 + r.zId = 0; + // 初始化版本号为当前时间的时间戳 + r._ver = (new Date()).getTime(); + }, + //default cache of core + _initCache = function (setting) { + // 获取缓存对象,如果不存在则初始化一个新的空对象 + var c = data.getCache(setting); + if (!c) { + // 如果缓存对象不存在,创建一个新的空对象 + c = {}; + // 将新的空对象设置到缓存中 + data.setCache(setting, c); + } + // 初始化缓存对象的nodes属性为一个空数组 + c.nodes = []; + // 初始化缓存对象的doms属性为一个空数组 + c.doms = []; + }, + //default bindEvent of core + _bindEvent = function (setting) { + // 获取树对象和事件常量 + var o = setting.treeObj, + c = consts.event; + + // 绑定节点创建事件 + o.bind(c.NODECREATED, function (event, treeId, node) { + tools.apply(setting.callback.onNodeCreated, [event, treeId, node]); + }); + + // 绑定点击事件 + o.bind(c.CLICK, function (event, srcEvent, treeId, node, clickFlag) { + tools.apply(setting.callback.onClick, [srcEvent, treeId, node, clickFlag]); + }); + + // 绑定展开事件 + o.bind(c.EXPAND, function (event, treeId, node) { + tools.apply(setting.callback.onExpand, [event, treeId, node]); + }); + + // 绑定折叠事件 + o.bind(c.COLLAPSE, function (event, treeId, node) { + tools.apply(setting.callback.onCollapse, [event, treeId, node]); + }); + + // 绑定异步加载成功事件 + o.bind(c.ASYNC_SUCCESS, function (event, treeId, node, msg) { + tools.apply(setting.callback.onAsyncSuccess, [event, treeId, node, msg]); + }); + + // 绑定异步加载错误事件 + o.bind(c.ASYNC_ERROR, function (event, treeId, node, XMLHttpRequest, textStatus, errorThrown) { + tools.apply(setting.callback.onAsyncError, [event, treeId, node, XMLHttpRequest, textStatus, errorThrown]); + }); + + // 绑定移除事件 + o.bind(c.REMOVE, function (event, treeId, treeNode) { + tools.apply(setting.callback.onRemove, [event, treeId, treeNode]); + }); + + // 绑定选中事件 + o.bind(c.SELECTED, function (event, treeId, node) { + tools.apply(setting.callback.onSelected, [treeId, node]); + }); + + // 绑定取消选中事件 + o.bind(c.UNSELECTED, function (event, treeId, node) { + tools.apply(setting.callback.onUnSelected, [treeId, node]); + }); + }, + _unbindEvent = function (setting) { + // 获取树对象 + var o = setting.treeObj, + // 获取事件常量 + c = consts.event; + // 解绑节点创建事件 + o.unbind(c.NODECREATED) + // 解绑点击事件 + .unbind(c.CLICK) + // 解绑展开事件 + .unbind(c.EXPAND) + // 解绑折叠事件 + .unbind(c.COLLAPSE) + // 解绑异步成功事件 + .unbind(c.ASYNC_SUCCESS) + // 解绑异步错误事件 + .unbind(c.ASYNC_ERROR) + // 解绑移除事件 + .unbind(c.REMOVE) + // 解绑选中事件 + .unbind(c.SELECTED) + // 解绑取消选中事件 + .unbind(c.UNSELECTED); + }, + //default event proxy of core + _eventProxy = function (event) { + // 获取事件目标元素 + var target = event.target, + // 获取树的设置信息 + // 获取指定树的设置信息 + setting = data.getSetting(event.data.treeId), + // 初始化节点ID为空字符串 + tId = "", + // 初始化节点对象为null + node = null, + // 初始化节点事件类型为空字符串 + nodeEventType = "", + // 初始化树事件类型为空字符串 + treeEventType = "", + // 初始化节点事件回调函数为null + nodeEventCallback = null, + // 初始化树事件回调函数为null + treeEventCallback = null, + // 初始化临时变量为null + tmp = null; + + // 根据事件类型判断是哪种树事件 + if (tools.eqs(event.type, "mousedown")) { + // 如果事件类型是鼠标按下,则设置树事件类型为"mousedown" + treeEventType = "mousedown"; + } else if (tools.eqs(event.type, "mouseup")) { + // 如果事件类型是鼠标抬起,则设置树事件类型为"mouseup" + treeEventType = "mouseup"; + } else if (tools.eqs(event.type, "contextmenu")) { + // 如果事件类型是右键菜单,则设置树事件类型为"contextmenu" + treeEventType = "contextmenu"; + } else if (tools.eqs(event.type, "click")) { + // 如果点击的是节点开关,则设置节点事件类型为switchNode + if (tools.eqs(target.tagName, "span") && target.getAttribute("treeNode" + consts.id.SWITCH) !== null) { + // 获取主DOM元素的ID并赋值给tId + tId = tools.getNodeMainDom(target).id; + // 设置节点事件类型为"switchNode" + nodeEventType = "switchNode"; + } else { + // 否则查找包含特定属性的DOM元素,并设置节点事件类型为clickNode + tmp = tools.getMDom(setting, target, [{tagName: "a", attrName: "treeNode" + consts.id.A}]); + if (tmp) { + // 获取主DOM元素的ID并赋值给tId + tId = tools.getNodeMainDom(tmp).id; + // 设置节点事件类型为"clickNode" + nodeEventType = "clickNode"; + } + } + } else if (tools.eqs(event.type, "dblclick")) { + // 双击事件处理,设置树事件类型为dblclick + treeEventType = "dblclick"; + // 获取目标元素中包含特定属性的子元素 + tmp = tools.getMDom(setting, target, [{tagName: "a", attrName: "treeNode" + consts.id.A}]); + if (tmp) { + // 获取节点的主DOM元素的ID + tId = tools.getNodeMainDom(tmp).id; + // 设置节点事件类型为switchNode + nodeEventType = "switchNode"; + } + } + // 检查treeEventType数组是否有元素且tId字符串是否为空 + if (treeEventType.length > 0 && tId.length == 0) { + // 调用tools.getMDom方法,获取目标元素的子元素,该子元素具有指定的标签名和属性名 + tmp = tools.getMDom(setting, target, [{tagName: "a", attrName: "treeNode" + consts.id.A}]); + // 如果找到了符合条件的元素 + if (tmp) { + // 获取该元素的主DOM节点的ID并赋值给tId + tId = tools.getNodeMainDom(tmp).id; + } + } + // event to node + if (tId.length > 0) { + // 获取节点缓存 + node = data.getNodeCache(setting, tId); + + // 根据节点事件类型进行处理 + switch (nodeEventType) { + case "switchNode" : + // 判断节点是否为父节点 + var isParent = data.nodeIsParent(setting, node); + if (!isParent) { + // 如果不是父节点,则清空事件类型 + nodeEventType = ""; + } else if (tools.eqs(event.type, "click") + || (tools.eqs(event.type, "dblclick") && tools.apply(setting.view.dblClickExpand, [setting.treeId, node], setting.view.dblClickExpand))) { + // 如果事件类型是点击或双击且满足展开条件,设置回调函数为切换节点处理函数 + nodeEventCallback = handler.onSwitchNode; + } else { + // 否则清空事件类型 + nodeEventType = ""; + } + break; + case "clickNode" : + // 如果事件类型是点击节点,设置回调函数为点击节点处理函数 + nodeEventCallback = handler.onClickNode; + break; + } + } + // event to zTree + switch (treeEventType) { + // 当事件类型为 "mousedown" 时,将 treeEventCallback 设置为 handler.onZTreeMousedown + case "mousedown" : + treeEventCallback = handler.onZTreeMousedown; + break; + // 当事件类型为 "mouseup" 时,将 treeEventCallback 设置为 handler.onZTreeMouseup + case "mouseup" : + treeEventCallback = handler.onZTreeMouseup; + break; + // 当事件类型为 "dblclick" 时,将 treeEventCallback 设置为 handler.onZTreeDblclick + case "dblclick" : + treeEventCallback = handler.onZTreeDblclick; + break; + // 当事件类型为 "contextmenu" 时,将 treeEventCallback 设置为 handler.onZTreeContextmenu + case "contextmenu" : + treeEventCallback = handler.onZTreeContextmenu; + break; + } + var proxyResult = { + // 停止标志,初始值为false + stop: false, + // 当前节点对象 + node: node, + // 节点事件类型 + nodeEventType: nodeEventType, + // 节点事件回调函数 + nodeEventCallback: nodeEventCallback, + // 树事件类型 + treeEventType: treeEventType, + // 树事件回调函数 + treeEventCallback: treeEventCallback + }; + // 返回代理结果对象 + return proxyResult + }, + //default init node of core + _initNode = function (setting, level, n, parentNode, isFirstNode, isLastNode, openFlag) { + // 如果节点不存在,直接返回 + if (!n) return; + + // 获取树的根节点 + var r = data.getRoot(setting), + // 获取当前节点的子节点 + children = data.nodeChildren(setting, n); + + // 设置当前节点的层级 + n.level = level; + + // 生成并设置当前节点的唯一标识符 + n.tId = setting.treeId + "_" + (++r.zId); + + // 设置当前节点的父节点标识符 + n.parentTId = parentNode ? parentNode.tId : null; + + // 根据节点的open属性设置是否展开 + n.open = (typeof n.open == "string") ? tools.eqs(n.open, "true") : !!n.open; + + // 判断当前节点是否是父节点 + var isParent = data.nodeIsParent(setting, n); + + // 如果当前节点有子节点 + if (tools.isArray(children)) { + // 将当前节点标记为父节点 + data.nodeIsParent(setting, n, true); + // 标记当前节点为异步加载 + n.zAsync = true; + } else { + // 更新当前节点的父节点状态 + isParent = data.nodeIsParent(setting, n, isParent); + // 根据父节点状态和异步加载设置决定是否展开当前节点 + n.open = (isParent && !setting.async.enable) ? n.open : false; + // 标记当前节点为非异步加载 + n.zAsync = !isParent; + } + n.isFirstNode = isFirstNode; + // 设置节点是否为第一个节点的函数 + n.isLastNode = isLastNode; + // 设置节点是否为最后一个节点的函数 + n.getParentNode = function () { + return data.getNodeCache(setting, n.parentTId); + }; + // 获取父节点的方法,通过缓存获取父节点信息 + n.getPreNode = function () { + return data.getPreNode(setting, n); + }; + // 获取前一个兄弟节点的方法 + n.getNextNode = function () { + return data.getNextNode(setting, n); + }; + // 获取下一个兄弟节点的方法 + n.getIndex = function () { + return data.getNodeIndex(setting, n); + }; + // 获取当前节点在同级中的索引位置 + n.getPath = function () { + return data.getNodePath(setting, n); + }; + // 获取从根节点到当前节点的路径 + n.isAjaxing = false; + // 初始化节点的Ajax请求状态为false + data.fixPIdKeyValue(setting, n); + // 修正节点的父ID键值对 + }, + _init = { + bind: [_bindEvent], + // 绑定事件的方法数组 + unbind: [_unbindEvent], + // 解绑事件的方法数组 + caches: [_initCache], + // 初始化缓存的方法数组 + nodes: [_initNode], + // 初始化节点的方法数组 + proxys: [_eventProxy], + // 事件代理的方法数组 + roots: [_initRoot], + // 初始化根节点的方法数组 + beforeA: [], + // 在操作前的回调方法数组 + afterA: [], + // 在操作后的回调方法数组 + innerBeforeA: [], + // 内部操作前的回调方法数组 + innerAfterA: [], + // 内部操作后的回调方法数组 + zTreeTools: [] + // ZTree工具方法数组 + }, + //method of operate data + data = { + // 添加节点缓存 + addNodeCache: function (setting, node) { + // 获取指定设置的缓存,并将节点添加到缓存中 + data.getCache(setting).nodes[data.getNodeCacheId(node.tId)] = node; + }, + // 获取节点缓存ID + getNodeCacheId: function (tId) { + // 返回节点ID的最后一部分作为缓存ID + return tId.substring(tId.lastIndexOf("_") + 1); + }, + // 添加afterA事件 + addAfterA: function (afterA) { + // 将afterA事件添加到初始化配置的afterA数组中 + _init.afterA.push(afterA); + }, + // 添加beforeA事件 + addBeforeA: function (beforeA) { + // 将beforeA事件添加到初始化配置的beforeA数组中 + _init.beforeA.push(beforeA); + }, + // 添加innerAfterA事件 + addInnerAfterA: function (innerAfterA) { + // 将innerAfterA事件添加到初始化配置的innerAfterA数组中 + _init.innerAfterA.push(innerAfterA); + }, + // 添加innerBeforeA事件 + addInnerBeforeA: function (innerBeforeA) { + // 将innerBeforeA事件添加到初始化配置的innerBeforeA数组中 + _init.innerBeforeA.push(innerBeforeA); + }, + // 添加初始化绑定事件 + addInitBind: function (bindEvent) { + // 将绑定事件添加到初始化配置的bind数组中 + _init.bind.push(bindEvent); + }, + // 添加初始化解绑事件 + addInitUnBind: function (unbindEvent) { + // 将解绑事件添加到初始化配置的unbind数组中 + _init.unbind.push(unbindEvent); + }, + // 添加初始化缓存 + addInitCache: function (initCache) { + // 将初始化缓存添加到初始化配置的caches数组中 + _init.caches.push(initCache); + }, + // 添加初始化节点 + addInitNode: function (initNode) { + // 将初始化节点添加到初始化配置的nodes数组中 + _init.nodes.push(initNode); + }, + // 添加初始化代理 + addInitProxy: function (initProxy, isFirst) { + if (!!isFirst) { + // 如果isFirst为真,将代理插入到proxys数组的开头 + _init.proxys.splice(0, 0, initProxy); + } else { + // 否则,将代理添加到proxys数组的末尾 + _init.proxys.push(initProxy); + } + }, + addInitRoot: function (initRoot) { + // 将初始化根节点添加到_init.roots数组中 + _init.roots.push(initRoot); + }, + addNodesData: function (setting, parentNode, index, nodes) { + // 获取父节点的子节点数据 + var children = data.nodeChildren(setting, parentNode), params; + // 如果父节点没有子节点 + if (!children) { + // 为父节点创建一个新的空子节点数组 + children = data.nodeChildren(setting, parentNode, []); + // 将索引设置为-1,表示插入位置无效 + index = -1; + } else if (index >= children.length) { + // 如果索引超出子节点数组的长度,将索引设置为-1 + index = -1; + } + + if (children.length > 0 && index === 0) { + // 如果子节点数组不为空且索引为0,则将第一个子节点的isFirstNode属性设置为false + children[0].isFirstNode = false; + // 设置第一个子节点的图标样式 + view.setNodeLineIcos(setting, children[0]); + } else if (children.length > 0 && index < 0) { + // 如果子节点数组不为空且索引小于0,则将最后一个子节点的isLastNode属性设置为false + children[children.length - 1].isLastNode = false; + // 设置最后一个子节点的图标样式 + view.setNodeLineIcos(setting, children[children.length - 1]); + } + // 设置父节点为父节点 + data.nodeIsParent(setting, parentNode, true); + + if (index < 0) { + // 如果索引小于0,则将新节点添加到子节点数组的末尾 + data.nodeChildren(setting, parentNode, children.concat(nodes)); + } else { + // 如果索引大于等于0,则在指定位置插入新节点 + params = [index, 0].concat(nodes); + children.splice.apply(children, params); + } + }, + addSelectedNode: function (setting, node) { + // 获取根节点 + var root = data.getRoot(setting); + // 如果节点未被选中,则将其添加到当前选中列表中 + if (!data.isSelectedNode(setting, node)) { + root.curSelectedList.push(node); + } + }, + addCreatedNode: function (setting, node) { + // 如果存在自定义的节点创建回调或添加自定义DOM的方法,则执行以下逻辑 + if (!!setting.callback.onNodeCreated || !!setting.view.addDiyDom) { + // 获取根节点 + var root = data.getRoot(setting); + // 将新创建的节点添加到已创建节点列表中 + root.createdNodes.push(node); + } + }, + addZTreeTools: function (zTreeTools) { + // 将传入的zTree工具对象添加到_init.zTreeTools数组中 + _init.zTreeTools.push(zTreeTools); + }, + exSetting: function (s) { + // 使用jQuery的extend方法,将传入的设置对象s合并到_setting对象中,实现深度拷贝 + $.extend(true, _setting, s); + }, + fixPIdKeyValue: function (setting, node) { + // 如果启用了简单数据模式 + if (setting.data.simpleData.enable) { + // 设置节点的父ID键值,如果节点有父节点则获取父节点的ID,否则设置为根节点的父ID + node[setting.data.simpleData.pIdKey] = node.parentTId ? node.getParentNode()[setting.data.simpleData.idKey] : setting.data.simpleData.rootPId; + } + }, + getAfterA: function (setting, node, array) { + // 遍历_init.afterA数组,并调用每个元素作为函数 + for (var i = 0, j = _init.afterA.length; i < j; i++) { + // 使用apply方法将当前上下文和参数传递给每个函数 + _init.afterA[i].apply(this, arguments); + } + }, + getBeforeA: function (setting, node, array) { + // 遍历_init.beforeA数组,并调用每个元素作为函数 + for (var i = 0, j = _init.beforeA.length; i < j; i++) { + // 使用apply方法将当前上下文和参数传递给每个函数 + _init.beforeA[i].apply(this, arguments); + } + }, + getInnerAfterA: function (setting, node, array) { + // 遍历_init.innerAfterA数组,并调用每个元素作为函数 + for (var i = 0, j = _init.innerAfterA.length; i < j; i++) { + // 使用apply方法将当前上下文和参数传递给每个函数 + _init.innerAfterA[i].apply(this, arguments); + } + }, + getInnerBeforeA: function (setting, node, array) { + // 遍历_init.innerBeforeA数组,并调用每个元素作为函数 + for (var i = 0, j = _init.innerBeforeA.length; i < j; i++) { + // 使用apply方法将当前上下文和参数传递给每个函数 + _init.innerBeforeA[i].apply(this, arguments); + } + }, + getCache: function (setting) { + // 返回缓存中对应treeId的缓存数据 + return caches[setting.treeId]; + }, + getNodeIndex: function (setting, node) { + // 如果节点为空,返回null + if (!node) return null; + // 获取父节点,如果节点没有父节点则获取根节点 + var p = node.parentTId ? node.getParentNode() : data.getRoot(setting), + // 获取父节点的所有子节点 + children = data.nodeChildren(setting, p); + // 遍历子节点数组 + for (var i = 0, l = children.length - 1; i <= l; i++) { + // 如果找到匹配的节点,返回其索引 + if (children[i] === node) { + return i; + } + } + // 如果没有找到匹配的节点,返回-1 + return -1; + }, + getNextNode: function (setting, node) { + // 如果节点为空,返回null + if (!node) return null; + // 获取父节点,如果节点没有父节点则获取根节点 + var p = node.parentTId ? node.getParentNode() : data.getRoot(setting), + // 获取父节点的所有子节点 + children = data.nodeChildren(setting, p); + // 遍历子节点数组 + for (var i = 0, l = children.length - 1; i <= l; i++) { + // 如果找到匹配的节点 + if (children[i] === node) { + // 如果是最后一个节点,返回null;否则返回下一个节点 + return (i == l ? null : children[i + 1]); + } + } + // 如果没有找到匹配的节点,返回null + return null; + }, + /** + * 根据指定的参数在节点数组中查找匹配的节点。 + * @param {Object} setting - 配置对象,用于获取子节点。 + * @param {Array} nodes - 要搜索的节点数组。 + * @param {String} key - 要匹配的键名。 + * @param {*} value - 要匹配的值。 + * @returns {Object|null} 返回匹配的节点,如果没有找到则返回null。 + */ + getNodeByParam: function (setting, nodes, key, value) { + // 如果节点数组或键名为空,直接返回null + if (!nodes || !key) return null; + // 遍历节点数组 + for (var i = 0, l = nodes.length; i < l; i++) { + // 获取当前节点 + var node = nodes[i]; + // 如果当前节点的指定键值与目标值相等,返回该节点 + if (node[key] == value) { + return nodes[i]; + } + // 获取当前节点的子节点 + var children = data.nodeChildren(setting, node); + // 递归调用自身在子节点中查找匹配的节点 + var tmp = data.getNodeByParam(setting, children, key, value); + // 如果找到匹配的节点,返回该节点 + if (tmp) return tmp; + } + // 如果未找到匹配的节点,返回null + return null; + }, + getNodeCache: function (setting, tId) { + // 如果tId不存在,返回null + if (!tId) return null; + // 从缓存中获取节点对象 + var n = caches[setting.treeId].nodes[data.getNodeCacheId(tId)]; + // 如果节点存在,返回节点;否则返回null + return n ? n : null; + }, + getNodePath: function (setting, node) { + // 如果node不存在,返回null + if (!node) return null; + + var path; + // 如果节点有父节点,递归获取父节点的路径 + if (node.parentTId) { + path = node.getParentNode().getPath(); + } else { + // 如果没有父节点,初始化路径为空数组 + path = []; + } + + // 如果路径存在,将当前节点添加到路径中 + if (path) { + path.push(node); + } + + // 返回节点路径 + return path; + }, + getNodes: function (setting) { + // 调用data.nodeChildren方法获取根节点的子节点 + return data.nodeChildren(setting, data.getRoot(setting)); + }, + getNodesByParam: function (setting, nodes, key, value) { + // 如果nodes或key为空,返回空数组 + if (!nodes || !key) return []; + var result = []; // 初始化结果数组 + // 遍历所有节点 + for (var i = 0, l = nodes.length; i < l; i++) { + var node = nodes[i]; // 当前节点 + // 如果当前节点的指定属性值等于目标值,则将该节点加入结果数组 + if (node[key] == value) { + result.push(node); + } + // 获取当前节点的子节点 + var children = data.nodeChildren(setting, node); + // 递归查找子节点中符合条件的节点并合并到结果数组中 + result = result.concat(data.getNodesByParam(setting, children, key, value)); + } + // 返回符合条件的节点数组 + return result; + }, + getNodesByParamFuzzy: function (setting, nodes, key, value) { + // 如果节点或键为空,返回空数组 + if (!nodes || !key) return []; + + // 初始化结果数组 + var result = []; + + // 将搜索值转换为小写 + value = value.toLowerCase(); + + // 遍历所有节点 + for (var i = 0, l = nodes.length; i < l; i++) { + var node = nodes[i]; + + // 如果节点的指定键是字符串且包含搜索值,则将该节点加入结果数组 + if (typeof node[key] == "string" && nodes[i][key].toLowerCase().indexOf(value) > -1) { + result.push(node); + } + + // 获取当前节点的子节点 + var children = data.nodeChildren(setting, node); + + // 递归调用函数以查找子节点中的匹配项,并将结果合并到结果数组中 + result = result.concat(data.getNodesByParamFuzzy(setting, children, key, value)); + } + + // 返回最终的结果数组 + return result; + }, + /** + * 根据过滤条件获取节点 + * @param {Object} setting - 配置对象 + * @param {Array} nodes - 节点数组 + * @param {Function} filter - 过滤函数 + * @param {Boolean} isSingle - 是否只返回一个节点 + * @param {Any} invokeParam - 传递给过滤函数的参数 + * @returns {Array|null} 符合条件的节点数组或单个节点,如果没有符合条件的节点则返回空数组或null + */ + getNodesByFilter: function (setting, nodes, filter, isSingle, invokeParam) { + // 如果节点数组为空,根据isSingle决定返回null还是空数组 + if (!nodes) return (isSingle ? null : []); + + // 初始化结果变量,根据isSingle决定是null还是空数组 + var result = isSingle ? null : []; + + // 遍历所有节点 + for (var i = 0, l = nodes.length; i < l; i++) { + var node = nodes[i]; + + // 使用过滤函数检查当前节点是否符合条件 + if (tools.apply(filter, [node, invokeParam], false)) { + // 如果只需要一个节点且找到符合条件的节点,直接返回该节点 + if (isSingle) { + return node; + } + // 否则将符合条件的节点加入结果数组 + result.push(node); + } + + // 获取当前节点的子节点 + var children = data.nodeChildren(setting, node); + + // 递归调用自身以处理子节点 + var tmpResult = data.getNodesByFilter(setting, children, filter, isSingle, invokeParam); + + // 如果只需要一个节点且在子节点中找到符合条件的节点,直接返回该节点 + if (isSingle && !!tmpResult) { + return tmpResult; + } + + // 根据isSingle决定是替换结果还是合并结果 + result = isSingle ? tmpResult : result.concat(tmpResult); + } + + // 返回最终的结果数组或单个节点 + return result; + }, + getPreNode: function (setting, node) { + // 如果节点为空,返回null + if (!node) return null; + // 获取父节点,如果当前节点没有父节点则获取根节点 + var p = node.parentTId ? node.getParentNode() : data.getRoot(setting), + // 获取父节点的子节点列表 + children = data.nodeChildren(setting, p); + // 遍历子节点列表 + for (var i = 0, l = children.length; i < l; i++) { + // 如果找到当前节点 + if (children[i] === node) { + // 返回前一个节点,如果是第一个节点则返回null + return (i == 0 ? null : children[i - 1]); + } + } + // 如果没有找到当前节点,返回null + return null; + }, + /** + * 获取树的根节点 + * @param {Object} setting - 配置对象 + * @returns {Object|null} 根节点或null + */ + getRoot: function (setting) { + // 根据配置对象的treeId获取根节点 + return setting ? roots[setting.treeId] : null; + }, + /** + * 获取所有树的根节点集合 + * @returns {Object} 根节点集合 + */ + getRoots: function () { + // 返回所有根节点的集合 + return roots; + }, + /** + * 获取指定树的配置对象 + * @param {string} treeId - 树的唯一标识符 + * @returns {Object|null} 配置对象或null + */ + getSetting: function (treeId) { + // 根据树的唯一标识符获取配置对象 + return settings[treeId]; + }, + /** + * 获取所有树的配置对象集合 + * @returns {Object} 配置对象集合 + */ + getSettings: function () { + // 返回所有配置对象的集合 + return settings; + }, + /** + * 获取指定树的工具对象 + * @param {string} treeId - 树的唯一标识符 + * @returns {Object|null} 工具对象或null + */ + getZTreeTools: function (treeId) { + // 根据树的唯一标识符获取根节点 + var r = this.getRoot(this.getSetting(treeId)); + // 返回根节点的工具对象,如果不存在则返回null + return r ? r.treeTools : null; + }, + /** + * 初始化缓存 + * @param {Object} setting - 配置对象 + */ + initCache: function (setting) { + // 遍历并应用所有缓存初始化函数 + for (var i = 0, j = _init.caches.length; i < j; i++) { + _init.caches[i].apply(this, arguments); + } + }, + /** + * 初始化节点 + * @param {Object} setting - 配置对象 + * @param {number} level - 节点层级 + * @param {Object} node - 当前节点 + * @param {Object} parentNode - 父节点 + * @param {Object} preNode - 前一个兄弟节点 + * @param {Object} nextNode - 后一个兄弟节点 + */ + initNode: function (setting, level, node, parentNode, preNode, nextNode) { + // 遍历并应用所有节点初始化函数 + for (var i = 0, j = _init.nodes.length; i < j; i++) { + _init.nodes[i].apply(this, arguments); + } + }, + initRoot: function (setting) { + // 遍历所有根节点初始化函数,并调用它们 + for (var i = 0, j = _init.roots.length; i < j; i++) { + _init.roots[i].apply(this, arguments); + } + }, + isSelectedNode: function (setting, node) { + // 获取当前设置的根节点 + var root = data.getRoot(setting); + // 遍历当前选中的节点列表,检查目标节点是否在其中 + for (var i = 0, j = root.curSelectedList.length; i < j; i++) { + if (node === root.curSelectedList[i]) return true; // 如果找到匹配的节点,返回true + } + return false; // 如果没有找到匹配的节点,返回false + }, + nodeChildren: function (setting, node, newChildren) { + if (!node) { + return null; // 如果节点不存在,返回null + } + var key = setting.data.key.children; // 获取子节点的键名 + if (typeof newChildren !== 'undefined') { + node[key] = newChildren; // 如果提供了新的子节点,更新节点的子节点 + } + return node[key]; // 返回节点的子节点 + }, + nodeIsParent: function (setting, node, newIsParent) { + if (!node) { + return false; // 如果节点不存在,返回false + } + var key = setting.data.key.isParent; + // 检查 newIsParent 是否定义 + if (typeof newIsParent !== 'undefined') { + // 如果 newIsParent 是字符串类型,则将其与 "true" 比较并转换为布尔值 + if (typeof newIsParent === "string") { + newIsParent = tools.eqs(newIsParent, "true"); + } + // 将 newIsParent 转换为布尔值并赋值给 node[key] + newIsParent = !!newIsParent; + node[key] = newIsParent; + } else if (typeof node[key] == "string"){ + // 如果 node[key] 是字符串类型,则将其与 "true" 比较并转换为布尔值 + node[key] = tools.eqs(node[key], "true"); + } else { + // 将 node[key] 转换为布尔值 + node[key] = !!node[key]; + } + // 返回 node[key] 的布尔值 + return node[key]; + }, + /** + * 设置或获取节点的名称 + * @param {Object} setting - 配置对象 + * @param {Object} node - 节点对象 + * @param {String} [newName] - 新的节点名称(可选) + * @returns {String} - 节点名称 + */ + nodeName: function (setting, node, newName) { + // 获取节点名称的键名 + var key = setting.data.key.name; + // 如果 newName 已定义,则更新节点名称 + if (typeof newName !== 'undefined') { + node[key] = newName; + } + // 将节点名称转换为字符串 + var rawName = "" + node[key]; + // 如果 render.name 是一个函数,则调用该函数并返回结果 + if(typeof setting.data.render.name === 'function') { + return setting.data.render.name.call(this,rawName,node); + } + // 否则直接返回原始名称 + return rawName; + }, + nodeTitle: function (setting, node) { + // 获取节点标题,如果标题为空则使用名称 + var t = setting.data.key.title === "" ? setting.data.key.name : setting.data.key.title; + // 将节点的标题转换为字符串 + var rawTitle = "" + node[t]; + // 如果设置了自定义渲染函数,则调用该函数渲染标题 + if(typeof setting.data.render.title === 'function') { + return setting.data.render.title.call(this,rawTitle,node); + } + // 返回原始标题 + return rawTitle; + }, + removeNodeCache: function (setting, node) { + // 获取节点的子节点 + var children = data.nodeChildren(setting, node); + // 如果存在子节点,递归删除每个子节点的缓存 + if (children) { + for (var i = 0, l = children.length; i < l; i++) { + data.removeNodeCache(setting, children[i]); + } + } + // 将当前节点的缓存设置为null + data.getCache(setting).nodes[data.getNodeCacheId(node.tId)] = null; + }, + removeSelectedNode: function (setting, node) { + // 获取树的根节点 + var root = data.getRoot(setting); + // 遍历当前选中的节点列表 + for (var i = 0, j = root.curSelectedList.length; i < j; i++) { + // 如果当前节点在选中列表中或其缓存不存在,则从选中列表中移除 + if (node === root.curSelectedList[i] || !data.getNodeCache(setting, root.curSelectedList[i].tId)) { + root.curSelectedList.splice(i, 1); + // 触发取消选中事件 + setting.treeObj.trigger(consts.event.UNSELECTED, [setting.treeId, node]); + // 调整索引以反映已移除的元素 + i--; + j--; + } + } + }, + setCache: function (setting, cache) { + // 将缓存对象存储到caches数组中,键为treeId + caches[setting.treeId] = cache; + }, + setRoot: function (setting, root) { + // 将根节点对象存储到roots数组中,键为treeId + roots[setting.treeId] = root; + }, + setZTreeTools: function (setting, zTreeTools) { + // 遍历_init.zTreeTools数组并调用每个工具函数,传递当前上下文和参数 + for (var i = 0, j = _init.zTreeTools.length; i < j; i++) { + _init.zTreeTools[i].apply(this, arguments); + } + }, + transformToArrayFormat: function (setting, nodes) { + // 如果nodes为空,返回空数组 + if (!nodes) return []; + // 初始化结果数组r + var r = []; + // 如果nodes是数组,则遍历每个节点并调用_do函数处理 + if (tools.isArray(nodes)) { + for (var i = 0, l = nodes.length; i < l; i++) { + var node = nodes[i]; + _do(node); + } + } else { + // 如果nodes不是数组,直接调用_do函数处理 + _do(nodes); + } + // 返回结果数组r + return r; + + function _do(_node) { + // 将当前节点添加到结果数组中 + r.push(_node); + + // 获取当前节点的子节点 + var children = data.nodeChildren(setting, _node); + + // 如果存在子节点,则将其转换为数组格式并合并到结果数组中 + if (children) { + r = r.concat(data.transformToArrayFormat(setting, children)); + } + } + }, + transformTozTreeFormat: function (setting, sNodes) { + // 定义变量i和l,用于循环控制 + var i, l, + // 获取节点ID的键名 + key = setting.data.simpleData.idKey, + // 获取父节点ID的键名 + parentKey = setting.data.simpleData.pIdKey; + // 如果key为空或sNodes不存在,返回空数组 + if (!key || key == "" || !sNodes) return []; + + // 如果sNodes是数组类型 + if (tools.isArray(sNodes)) { + // 初始化结果数组r + var r = []; + // 初始化临时映射对象tmpMap + var tmpMap = {}; + // 遍历sNodes数组 + for (i = 0, l = sNodes.length; i < l; i++) { + // 将每个节点按其ID存入tmpMap中 + tmpMap[sNodes[i][key]] = sNodes[i]; + } + // 遍历sNodes数组中的每一个元素 + for (i = 0, l = sNodes.length; i < l; i++) { + // 获取当前节点的父节点在tmpMap中的引用 + var p = tmpMap[sNodes[i][parentKey]]; + // 如果父节点存在且当前节点不是其父节点 + if (p && sNodes[i][key] != sNodes[i][parentKey]) { + // 获取父节点的子节点列表 + var children = data.nodeChildren(setting, p); + // 如果父节点没有子节点列表,则初始化一个空列表 + if (!children) { + children = data.nodeChildren(setting, p, []); + } + // 将当前节点添加到父节点的子节点列表中 + children.push(sNodes[i]); + } else { + // 如果父节点不存在或当前节点是其父节点,则将当前节点添加到结果数组r中 + r.push(sNodes[i]); + } + } + // 如果条件满足,返回变量r + return r; +} else { + // 如果条件不满足,返回包含sNodes的数组 + return [sNodes]; +} + } + }, + //method of event proxy + event = { + // 绑定事件的方法,接受一个设置参数 + bindEvent: function (setting) { + // 遍历_init.bind数组中的每一个元素 + for (var i = 0, j = _init.bind.length; i < j; i++) { + // 调用当前元素的apply方法,将当前上下文和传入的参数传递给它 + _init.bind[i].apply(this, arguments); + } + }, + unbindEvent: function (setting) { + // 遍历_init.unbind数组,并调用每个元素的apply方法,将当前上下文和参数传递给它 + for (var i = 0, j = _init.unbind.length; i < j; i++) { + _init.unbind[i].apply(this, arguments); + } + }, + bindTree: function (setting) { + // 创建一个包含treeId属性的对象eventParam + var eventParam = { + treeId: setting.treeId + }, + // 获取树对象 + o = setting.treeObj; + // 如果视图中不允许选择文本 + if (!setting.view.txtSelectedEnable) { + // 绑定selectstart事件处理程序,防止文本被选中,并设置CSS样式以禁用文本选择 + o.bind('selectstart', handler.onSelectStart).css({ + "-moz-user-select": "-moz-none" + }); + } + // 绑定点击事件到对象o,并使用event.proxy作为事件处理函数 + o.bind('click', eventParam, event.proxy); + // 绑定双击事件到对象o,并使用event.proxy作为事件处理函数 + o.bind('dblclick', eventParam, event.proxy); + // 绑定鼠标悬停事件到对象o,并使用event.proxy作为事件处理函数 + o.bind('mouseover', eventParam, event.proxy); + // 绑定鼠标移出事件到对象o,并使用event.proxy作为事件处理函数 + o.bind('mouseout', eventParam, event.proxy); + // 绑定鼠标按下事件到对象o,并使用event.proxy作为事件处理函数 + o.bind('mousedown', eventParam, event.proxy); + // 绑定鼠标抬起事件到对象o,并使用event.proxy作为事件处理函数 + o.bind('mouseup', eventParam, event.proxy); + // 绑定右键菜单事件到对象o,并使用event.proxy作为事件处理函数 + o.bind('contextmenu', eventParam, event.proxy); + unbindTree: function (setting) { + // 获取树形对象的引用 + var o = setting.treeObj; + // 解绑'selectstart'事件,防止文本选择 + o.unbind('selectstart', handler.onSelectStart) + // 解绑'click'事件,取消点击事件的代理处理 + .unbind('click', event.proxy) + // 解绑'dblclick'事件,取消双击事件的代理处理 + .unbind('dblclick', event.proxy) + // 解绑'mouseover'事件,取消鼠标悬停事件的代理处理 + .unbind('mouseover', event.proxy) + // 解绑'mouseout'事件,取消鼠标移出事件的代理处理 + .unbind('mouseout', event.proxy) + // 解绑'mousedown'事件,取消鼠标按下事件的代理处理 + .unbind('mousedown', event.proxy) + // 解绑'mouseup'事件,取消鼠标抬起事件的代理处理 + .unbind('mouseup', event.proxy) + // 解绑'contextmenu'事件,取消右键菜单事件的代理处理 + .unbind('contextmenu', event.proxy); + }, + doProxy: function (e) { + // 初始化一个空数组,用于存储代理函数的返回结果 + var results = []; + // 遍历所有代理函数 + for (var i = 0, j = _init.proxys.length; i < j; i++) { + // 调用当前代理函数,并将结果存储在proxyResult中 + var proxyResult = _init.proxys[i].apply(this, arguments); + // 将当前代理函数的结果添加到results数组中 + results.push(proxyResult); + // 如果当前代理函数的返回结果包含stop属性且为true,则中断循环 + if (proxyResult.stop) { + break; + } + } + // 返回所有代理函数的结果数组 + return results; + }, + proxy: function (e) { + // 获取当前树的设置信息 + var setting = data.getSetting(e.data.treeId); + + // 检查用户是否有权限执行该操作,如果没有权限则返回true + if (!tools.uCanDo(setting, e)) return true; + + // 执行代理事件并获取结果数组 + var results = event.doProxy(e), + r = true, // 初始化返回值为true + x = false; // 初始化标志位为false + + // 遍历所有代理结果 + for (var i = 0, l = results.length; i < l; i++) { + var proxyResult = results[i]; + + // 如果存在节点事件回调函数,则调用它并更新返回值和标志位 + if (proxyResult.nodeEventCallback) { + x = true; + r = proxyResult.nodeEventCallback.apply(proxyResult, [e, proxyResult.node]) && r; + } + + // 如果存在树事件回调函数,则调用它并更新返回值和标志位 + if (proxyResult.treeEventCallback) { + x = true; + r = proxyResult.treeEventCallback.apply(proxyResult, [e, proxyResult.node]) && r; + } + } + + // 返回最终的结果值 + return r; + } + }, + //method of event handler + handler = { + // 处理节点切换事件的方法 + onSwitchNode: function (event, node) { + // 获取当前树的设置信息 + var setting = data.getSetting(event.data.treeId); + + // 如果节点是展开状态 + if (node.open) { + // 调用回调函数,判断是否允许折叠节点,如果返回false则终止操作 + if (tools.apply(setting.callback.beforeCollapse, [setting.treeId, node], true) == false) return true; + + // 设置根节点的expandTriggerFlag为true,表示触发了展开操作 + data.getRoot(setting).expandTriggerFlag = true; + + // 切换节点的状态(从展开到折叠) + view.switchNode(setting, node); + } else { + // 调用回调函数,判断是否允许展开节点,如果返回false则终止操作 + if (tools.apply(setting.callback.beforeExpand, [setting.treeId, node], true) == false) return true; + + // 设置根节点的expandTriggerFlag为true,表示触发了展开操作 + data.getRoot(setting).expandTriggerFlag = true; + + // 切换节点的状态(从折叠到展开) + view.switchNode(setting, node); + } + + // 返回true表示操作成功 + return true; + }, + /** + * 处理节点点击事件的函数 + * @param {Object} event - 事件对象,包含触发事件的信息 + * @param {Object} node - 被点击的节点对象 + */ + onClickNode: function (event, node) { + // 获取当前树的设置信息 + var setting = data.getSetting(event.data.treeId), + // 根据是否按下Ctrl或Meta键以及节点是否已选中来确定点击标志 + clickFlag = ((setting.view.autoCancelSelected && (event.ctrlKey || event.metaKey)) && data.isSelectedNode(setting, node)) ? 0 : (setting.view.autoCancelSelected && (event.ctrlKey || event.metaKey) && setting.view.selectedMulti) ? 2 : 1; + // 如果beforeClick回调函数返回false,则终止执行并返回true + if (tools.apply(setting.callback.beforeClick, [setting.treeId, node, clickFlag], true) == false) return true; + // 如果点击标志为0,取消之前选中的节点 + if (clickFlag === 0) { + view.cancelPreSelectedNode(setting, node); + } else { + // 否则根据点击标志选择或取消选择节点 + view.selectNode(setting, node, clickFlag === 2); + } + // 触发CLICK事件,传递事件对象、树ID、节点和点击标志 + setting.treeObj.trigger(consts.event.CLICK, [event, setting.treeId, node, clickFlag]); + // 返回true表示事件处理完成 + return true; + }, + onZTreeMousedown: function (event, node) { + // 获取当前树的设置信息 + var setting = data.getSetting(event.data.treeId); + // 在鼠标按下事件之前执行回调函数,如果返回true则继续执行后续操作 + if (tools.apply(setting.callback.beforeMouseDown, [setting.treeId, node], true)) { + // 执行鼠标按下事件的回调函数 + tools.apply(setting.callback.onMouseDown, [event, setting.treeId, node]); + } + return true; // 始终返回true,表示事件处理完成 + }, + onZTreeMouseup: function (event, node) { + // 获取当前树的设置信息 + var setting = data.getSetting(event.data.treeId); + // 在鼠标抬起事件之前执行回调函数,如果返回true则继续执行后续操作 + if (tools.apply(setting.callback.beforeMouseUp, [setting.treeId, node], true)) { + // 执行鼠标抬起事件的回调函数 + tools.apply(setting.callback.onMouseUp, [event, setting.treeId, node]); + } + return true; // 始终返回true,表示事件处理完成 + }, + onZTreeDblclick: function (event, node) { + // 获取当前树的设置信息 + var setting = data.getSetting(event.data.treeId); + // 在双击事件之前执行回调函数,如果返回true则继续执行后续操作 + if (tools.apply(setting.callback.beforeDblClick, [setting.treeId, node], true)) { + // 执行双击事件的回调函数 + tools.apply(setting.callback.onDblClick, [event, setting.treeId, node]); + } + return true; // 始终返回true,表示事件处理完成 + }, + onZTreeContextmenu: function (event, node) { + // 获取当前树的设置信息 + var setting = data.getSetting(event.data.treeId); + // 在右键点击事件之前执行回调函数,如果返回true则继续执行后续操作 + if (tools.apply(setting.callback.beforeRightClick, [setting.treeId, node], true)) { + // 执行右键点击事件的回调函数 + tools.apply(setting.callback.onRightClick, [event, setting.treeId, node]); + } + // 检查是否有定义右键点击的回调函数,如果没有则返回false + return (typeof setting.callback.onRightClick) != "function"; + }, + onSelectStart: function (e) { + // 获取触发事件的元素节点名称并转换为小写 + var n = e.originalEvent.srcElement.nodeName.toLowerCase(); + // 如果元素是input或textarea,则允许选择文本 + return (n === "input" || n === "textarea"); + } + }, + //method of tools for zTree + tools = { + /** + * 应用函数,如果传入的参数是函数则调用该函数并返回结果,否则返回默认值。 + * @param {Function} fun - 要应用的函数 + * @param {Array} param - 函数参数数组 + * @param {*} defaultValue - 默认值 + * @returns {*} 函数执行结果或默认值 + */ + apply: function (fun, param, defaultValue) { + if ((typeof fun) == "function") { + // 如果fun是函数,则使用apply方法调用它 + return fun.apply(zt, param ? param : []); + } + // 如果fun不是函数,返回默认值 + return defaultValue; + }, + /** + * 判断节点是否可以异步加载子节点。 + * @param {Object} setting - 配置对象 + * @param {Object} node - 当前节点 + * @returns {boolean} 是否可以异步加载子节点 + */ + canAsync: function (setting, node) { + // 获取节点的子节点 + var children = data.nodeChildren(setting, node); + // 判断节点是否是父节点 + var isParent = data.nodeIsParent(setting, node); + // 判断是否启用了异步加载,并且节点存在且是父节点,同时没有异步标记且没有子节点 + return (setting.async.enable && node && isParent && !(node.zAsync || (children && children.length > 0))); + }, + /** + * 深度克隆一个对象。 + * @param {Object} obj - 要克隆的对象 + * @returns {Object} 克隆后的新对象 + */ + clone: function (obj) { + if (obj === null) return null; // 如果对象为null,直接返回null + // 根据对象类型创建新对象 + var o = tools.isArray(obj) ? [] : {}; + for (var i in obj) { + // 递归克隆对象属性 + o[i] = (obj[i] instanceof Date) ? new Date(obj[i].getTime()) : (typeof obj[i] === "object" ? tools.clone(obj[i]) : obj[i]); + } + return o; // 返回克隆后的对象 + }, + /** + * 比较两个字符串是否相等(忽略大小写)。 + * @param {string} str1 - 第一个字符串 + * @param {string} str2 - 第二个字符串 + * @returns {boolean} 字符串是否相等 + */ + eqs: function (str1, str2) { + return str1.toLowerCase() === str2.toLowerCase(); // 转换为小写后比较 + }, + /** + * 判断一个对象是否是数组。 + * @param {Object} arr - 要判断的对象 + * @returns {boolean} 是否是数组 + */ + isArray: function (arr) { + return Object.prototype.toString.apply(arr) === "[object Array]"; // 通过原型链判断是否为数组 + }, + /** + * 判断一个对象是否是HTML元素。 + * @param {Object} o - 要判断的对象 + * @returns {boolean} 是否是HTML元素 + */ + isElement: function (o) { + return ( + typeof HTMLElement === "object" ? o instanceof HTMLElement : //DOM2 + o && typeof o === "object" && o !== null && o.nodeType === 1 && typeof o.nodeName === "string" //DOM3 + ); + }, + $: function (node, exp, setting) { + // 检查exp是否存在且不是字符串类型 + if (!!exp && typeof exp != "string") { + // 如果exp存在且不是字符串,则将setting赋值为exp,并将exp置为空字符串 + setting = exp; + exp = ""; + } + // 检查node是否为字符串类型 + if (typeof node == "string") { + // 如果node是字符串,则根据setting中的treeObj获取文档对象并返回相应的jQuery对象 + return $(node, setting ? setting.treeObj.get(0).ownerDocument : null); + } else { + // 如果node不是字符串,则根据node的tId和exp构建选择器,并根据setting中的treeObj返回相应的jQuery对象 + return $("#" + node.tId + exp, setting ? setting.treeObj : null); + } + }, + getMDom: function (setting, curDom, targetExpr) { + // 如果当前DOM节点为空,返回null + if (!curDom) return null; + // 循环遍历父节点直到找到匹配的树ID或到达根节点 + while (curDom && curDom.id !== setting.treeId) { + // 遍历目标表达式数组 + for (var i = 0, l = targetExpr.length; curDom.tagName && i < l; i++) { + // 检查当前DOM节点的标签名和属性是否匹配目标表达式 + if (tools.eqs(curDom.tagName, targetExpr[i].tagName) && curDom.getAttribute(targetExpr[i].attrName) !== null) { + // 如果匹配,返回当前DOM节点 + return curDom; + } + } + // 移动到父节点继续检查 + curDom = curDom.parentNode; + } + // 如果没有找到匹配的节点,返回null + return null; + }, + + getNodeMainDom: function (target) { + // 获取目标元素的父级
  • 元素,如果不存在则获取最近的祖先
  • 元素 + return ($(target).parent("li").get(0) || $(target).parentsUntil("li").parent().get(0)); + }, + + isChildOrSelf: function (dom, parentId) { + // 判断给定的DOM节点是否是指定父节点的子节点或自身 + return ($(dom).closest("#" + parentId).length > 0); + }, + + uCanDo: function (setting, e) { + // 默认返回true,表示可以执行操作 + return true; + } + //method of operate ztree dom + view = { + // 添加节点的方法 + addNodes: function (setting, parentNode, index, newNodes, isSilent) { + // 判断父节点是否是父节点类型 + var isParent = data.nodeIsParent(setting, parentNode); + // 如果配置中要求保持叶子节点,并且父节点存在且不是父节点类型,则直接返回 + if (setting.data.keep.leaf && parentNode && !isParent) { + return; + } + // 如果新节点不是数组,则将其转换为数组 + if (!tools.isArray(newNodes)) { + newNodes = [newNodes]; + } + // 如果启用了简单数据格式,则将新节点转换为zTree格式 + if (setting.data.simpleData.enable) { + newNodes = data.transformTozTreeFormat(setting, newNodes); + } + // 如果父节点存在 + if (parentNode) { + // 获取父节点的开关对象、图标对象和子节点容器对象 + var target_switchObj = $$(parentNode, consts.id.SWITCH, setting), + target_icoObj = $$(parentNode, consts.id.ICON, setting), + target_ulObj = $$(parentNode, consts.id.UL, setting); + + // 如果父节点未打开 + if (!parentNode.open) { + // 替换父节点的开关类为关闭状态 + view.replaceSwitchClass(parentNode, target_switchObj, consts.folder.CLOSE); + // 替换父节点的图标类为关闭状态 + view.replaceIcoClass(parentNode, target_icoObj, consts.folder.CLOSE); + // 设置父节点为关闭状态 + parentNode.open = false; + // 隐藏父节点的子节点容器 + target_ulObj.css({ + "display": "none" + }); + } + + data.addNodesData(setting, parentNode, index, newNodes); + // 将新节点数据添加到父节点中 + view.createNodes(setting, parentNode.level + 1, newNodes, parentNode, index); + // 创建并显示新节点,设置其层级为父节点的层级加一 + if (!isSilent) { + view.expandCollapseParentNode(setting, parentNode, true); + } + // 如果不需要静默处理,则展开父节点以显示新添加的子节点 + } else { + data.addNodesData(setting, data.getRoot(setting), index, newNodes); + // 如果没有父节点,则将新节点数据添加到根节点中 + view.createNodes(setting, 0, newNodes, null, index); + // 创建并显示新节点,设置其层级为0(根节点) + } + }, + appendNodes: function (setting, level, nodes, parentNode, index, initFlag, openFlag) { + if (!nodes) return []; + // 如果节点列表为空,返回空数组 + var html = []; + // 初始化HTML数组,用于存储生成的HTML代码片段 + var tmpPNode = (parentNode) ? parentNode : data.getRoot(setting), + // 获取父节点,如果未指定父节点,则使用根节点 + tmpPChild = data.nodeChildren(setting, tmpPNode), + // 获取父节点的子节点列表 + isFirstNode, isLastNode; + // 定义两个变量,用于标记是否是第一个或最后一个节点 + + if (!tmpPChild || index >= tmpPChild.length - nodes.length) { + index = -1; + } + // 如果父节点没有子节点,或者索引超出范围,则将索引设置为-1,表示在末尾追加节点 + + // 遍历节点数组 + for (var i = 0, l = nodes.length; i < l; i++) { + // 获取当前节点 + var node = nodes[i]; + // 如果初始化标志为真 + if (initFlag) { + // 判断是否为第一个节点 + isFirstNode = ((index === 0 || tmpPChild.length == nodes.length) && (i == 0)); + // 判断是否为最后一个节点 + isLastNode = (index < 0 && i == (nodes.length - 1)); + // 初始化节点 + data.initNode(setting, level, node, parentNode, isFirstNode, isLastNode, openFlag); + // 将节点添加到缓存中 + data.addNodeCache(setting, node); + } + // 检查节点是否为父节点 + var isParent = data.nodeIsParent(setting, node); + + // 存储子节点的HTML内容 + var childHtml = []; + // 获取子节点 + var children = data.nodeChildren(setting, node); + // 如果存在子节点 + if (children && children.length > 0) { + // 先生成子节点的HTML,因为需要检查类型 + childHtml = view.appendNodes(setting, level + 1, children, node, -1, initFlag, openFlag && node.open); + } + // 如果打开标志为真 + if (openFlag) { + // 生成主节点前的DOM结构 + view.makeDOMNodeMainBefore(html, setting, node); + // 生成节点连接线 + view.makeDOMNodeLine(html, setting, node); + // 获取节点前的内容 + data.getBeforeA(setting, node, html); + // 生成节点名称前的DOM结构 + view.makeDOMNodeNameBefore(html, setting, node); + // 获取节点内部前的内容 + data.getInnerBeforeA(setting, node, html); + // 生成节点图标 + view.makeDOMNodeIcon(html, setting, node); + // 获取节点内部后的内容 + data.getInnerAfterA(setting, node, html); + // 生成节点名称后的DOM结构 + view.makeDOMNodeNameAfter(html, setting, node); + // 获取节点后的内容 + data.getAfterA(setting, node, html); + // 如果节点是父节点且已打开 + if (isParent && node.open) { + // 生成包含子节点的UL HTML结构 + view.makeUlHtml(setting, node, html, childHtml.join('')); + } + // 生成主节点后的DOM结构 + view.makeDOMNodeMainAfter(html, setting, node); + // 将创建的节点添加到已创建节点列表中 + data.addCreatedNode(setting, node); + } + } + return html; + }, + /** + * 将父节点的UL DOM元素追加到指定的节点上 + * @param {Object} setting - 配置对象 + * @param {Object} node - 当前节点对象 + */ + appendParentULDom: function (setting, node) { + // 初始化HTML数组 + var html = [], + // 获取当前节点对应的jQuery对象 + nObj = $$(node, setting); + // 如果当前节点不存在且有父节点ID,则递归调用自身 + if (!nObj.get(0) && !!node.parentTId) { + view.appendParentULDom(setting, node.getParentNode()); + nObj = $$(node, setting); + } + // 获取当前节点对应的UL jQuery对象 + var ulObj = $$(node, consts.id.UL, setting); + // 如果UL对象存在,则移除它 + if (ulObj.get(0)) { + ulObj.remove(); + } + // 获取子节点数据 + var children = data.nodeChildren(setting, node), + // 生成子节点的HTML内容 + childHtml = view.appendNodes(setting, node.level + 1, children, node, -1, false, true); + // 生成UL HTML内容并追加到html数组中 + view.makeUlHtml(setting, node, html, childHtml.join('')); + // 将生成的HTML内容追加到当前节点对象中 + nObj.append(html.join('')); + }, + /** + * 异步加载节点数据 + * @param {Object} setting - 配置对象 + * @param {Object} node - 当前节点对象 + * @param {Boolean} isSilent - 是否静默加载 + * @param {Function} callback - 回调函数 + */ + asyncNode: function (setting, node, isSilent, callback) { + var i, l; + // 判断当前节点是否是父节点 + var isParent = data.nodeIsParent(setting, node); + // 如果当前节点不是父节点,直接执行回调函数并返回false + if (node && !isParent) { + tools.apply(callback); + return false; + // 如果当前节点正在异步加载,直接返回false + } else if (node && node.isAjaxing) { + return false; + // 如果beforeAsync回调函数返回false,则执行回调函数并返回false + } else if (tools.apply(setting.callback.beforeAsync, [setting.treeId, node], true) == false) { + tools.apply(callback); + return false; + } + if (node) { + // 如果节点存在,设置节点的isAjaxing属性为true,表示该节点正在进行异步请求 + node.isAjaxing = true; + // 获取节点对应的图标对象 + var icoObj = $$(node, consts.id.ICON, setting); + // 更新图标对象的样式和类名,显示加载中的动画效果 + icoObj.attr({"style": "", "class": consts.className.BUTTON + " " + consts.className.ICO_LOADING}); + } + + // 初始化一个空对象,用于存储参数 + var tmpParam = {}; + // 调用工具函数生成自动参数数组 + var autoParam = tools.apply(setting.async.autoParam, [setting.treeId, node], setting.async.autoParam); + // 遍历自动参数数组 + for (i = 0, l = autoParam.length; node && i < l; i++) { + // 将每个参数按等号分割成键值对 + var pKey = autoParam[i].split("="), spKey = pKey; + if (pKey.length > 1) { + // 如果参数包含等号,则取等号后面的部分作为键 + spKey = pKey[1]; + // 取等号前面的部分作为值 + pKey = pKey[0]; + } + // 将节点的对应属性值赋给临时参数对象 + tmpParam[spKey] = node[pKey]; + } + // 调用工具函数生成其他参数数组 + var otherParam = tools.apply(setting.async.otherParam, [setting.treeId, node], setting.async.otherParam); + // 如果其他参数是数组形式 + if (tools.isArray(otherParam)) { + // 遍历数组,以键值对的形式添加到临时参数对象中 + for (i = 0, l = otherParam.length; i < l; i += 2) { + tmpParam[otherParam[i]] = otherParam[i + 1]; + } + } else { + // 如果其他参数是对象形式 + // 遍历对象,将每个属性添加到临时参数对象中 + for (var p in otherParam) { + tmpParam[p] = otherParam[p]; + } + } + + // 获取当前根节点的版本号 + var _tmpV = data.getRoot(setting)._ver; + + // 发起异步请求 + $.ajax({ + // 设置请求的内容类型 + contentType: setting.async.contentType, + // 禁用缓存 + cache: false, + // 设置请求的类型(GET、POST等) + type: setting.async.type, + // 生成请求的URL,使用工具函数处理模板字符串 + url: tools.apply(setting.async.url, [setting.treeId, node], setting.async.url), + // 根据内容类型决定发送的数据格式 + data: setting.async.contentType.indexOf('application/json') > -1 ? JSON.stringify(tmpParam) : tmpParam, + // 设置期望的响应数据类型 + dataType: setting.async.dataType, + // 设置请求头信息 + headers: setting.async.headers, + // 设置XHR字段 + xhrFields: setting.async.xhrFields, + // 请求成功时的回调函数 + success: function (msg) { + // 如果版本号发生变化,则直接返回,不进行后续处理 + if (_tmpV != data.getRoot(setting)._ver) { + return; + } + // 初始化新节点数组 + var newNodes = []; + try { + // 如果响应为空或长度为0,则新节点数组为空 + if (!msg || msg.length == 0) { + newNodes = []; + // 如果响应是字符串,则尝试解析为JSON对象 + } else if (typeof msg == "string") { + newNodes = eval("(" + msg + ")"); + // 否则直接将响应赋值给新节点数组 + } else { + newNodes = msg; + } + } catch (err) { + // 如果解析过程中发生错误,则将原始响应赋值给新节点数组 + newNodes = msg; + } + + if (node) { + // 将节点的isAjaxing属性设置为null,表示该节点不再进行异步操作 + node.isAjaxing = null; + // 设置节点的zAsync属性为true,表示该节点是异步加载的 + node.zAsync = true; + } + // 更新节点的图标和线条样式 + view.setNodeLineIcos(setting, node); + if (newNodes && newNodes !== "") { + // 应用数据过滤器处理新节点数据 + newNodes = tools.apply(setting.async.dataFilter, [setting.treeId, node, newNodes], newNodes); + // 添加新节点到视图中 + view.addNodes(setting, node, -1, !!newNodes ? tools.clone(newNodes) : [], !!isSilent); + } else { + // 如果新节点数据为空,则添加空数组作为新节点 + view.addNodes(setting, node, -1, [], !!isSilent); + } + // 触发异步成功事件 + setting.treeObj.trigger(consts.event.ASYNC_SUCCESS, [setting.treeId, node, msg]); + // 执行回调函数 + tools.apply(callback); + }, + error: function (XMLHttpRequest, textStatus, errorThrown) { + // 如果版本号不一致,直接返回 + if (_tmpV != data.getRoot(setting)._ver) { + return; + } + // 将节点的isAjaxing属性设置为null,表示该节点不再进行异步操作 + if (node) node.isAjaxing = null; + // 更新节点的图标和线条样式 + view.setNodeLineIcos(setting, node); + // 触发异步错误事件 + setting.treeObj.trigger(consts.event.ASYNC_ERROR, [setting.treeId, node, XMLHttpRequest, textStatus, errorThrown]); + } + }, + return true; + }, + cancelPreSelectedNode: function (setting, node, excludeNode) { + // 获取当前选中的节点列表 + var list = data.getRoot(setting).curSelectedList, + i, n; + // 从后向前遍历选中的节点列表 + for (i = list.length - 1; i >= 0; i--) { + n = list[i]; + // 如果当前节点是需要取消选中的节点或排除节点,则进行处理 + if (node === n || (!node && (!excludeNode || excludeNode !== n))) { + // 移除节点的选中样式 + $$(n, consts.id.A, setting).removeClass(consts.node.CURSELECTED); + if (node) { + // 从选中列表中移除该节点 + data.removeSelectedNode(setting, node); + break; + } else { + // 从选中列表中删除该节点并触发取消选中事件 + list.splice(i, 1); + setting.treeObj.trigger(consts.event.UNSELECTED, [setting.treeId, n]); + } + } + } + }, + /** + * 创建节点回调函数 + * @param {Object} setting - 配置对象,包含回调和视图设置 + */ + createNodeCallback: function (setting) { + // 检查是否有自定义的节点创建回调或添加自定义DOM的方法 + if (!!setting.callback.onNodeCreated || !!setting.view.addDiyDom) { + // 获取根节点数据 + var root = data.getRoot(setting); + // 当根节点的已创建节点列表不为空时循环处理 + while (root.createdNodes.length > 0) { + // 从已创建节点列表中移除并获取第一个节点 + var node = root.createdNodes.shift(); + // 调用视图中的添加自定义DOM方法 + tools.apply(setting.view.addDiyDom, [setting.treeId, node]); + // 如果存在节点创建回调,则触发该回调事件 + if (!!setting.callback.onNodeCreated) { + setting.treeObj.trigger(consts.event.NODECREATED, [setting.treeId, node]); + } + } + } + }, + createNodes: function (setting, level, nodes, parentNode, index) { + // 如果节点为空或长度为0,则直接返回 + if (!nodes || nodes.length == 0) return; + + // 获取根节点 + var root = data.getRoot(setting), + // 判断是否打开父节点的标志 + openFlag = !parentNode || parentNode.open || !!$$(data.nodeChildren(setting, parentNode)[0], setting).get(0); + + // 初始化创建的节点数组 + root.createdNodes = []; + + // 生成节点的HTML并追加到视图中 + var zTreeHtml = view.appendNodes(setting, level, nodes, parentNode, index, true, openFlag), + parentObj, nextObj; + + // 如果没有父节点,则将生成的HTML追加到树对象中 + if (!parentNode) { + parentObj = setting.treeObj; + //setting.treeObj.append(zTreeHtml.join('')); + } else { + // 获取父节点的UL对象 + var ulObj = $$(parentNode, consts.id.UL, setting); + if (ulObj.get(0)) { + parentObj = ulObj; + //ulObj.append(zTreeHtml.join('')); + } + } + + // 如果父对象存在 + if (parentObj) { + // 如果索引大于等于0,则获取下一个兄弟节点 + if (index >= 0) { + nextObj = parentObj.children()[index]; + } + // 如果索引大于等于0且下一个兄弟节点存在,则在该节点之前插入生成的HTML + if (index >= 0 && nextObj) { + $(nextObj).before(zTreeHtml.join('')); + } else { + // 否则直接在父对象中追加生成的HTML + parentObj.append(zTreeHtml.join('')); + } + } + + view.createNodeCallback(setting); + }, + destroy: function (setting) { + // 如果 setting 为空,直接返回 + if (!setting) return; + // 初始化缓存 + data.initCache(setting); + // 初始化根节点 + data.initRoot(setting); + // 解绑树的事件 + event.unbindTree(setting); + // 解绑其他事件 + event.unbindEvent(setting); + // 清空树对象的内容 + setting.treeObj.empty(); + // 删除 settings 中对应的树设置 + delete settings[setting.treeId]; + }, + expandCollapseNode: function (setting, node, expandFlag, animateFlag, callback) { + // 获取根节点 + var root = data.getRoot(setting); + var tmpCb, _callback; + // 如果 node 为空,执行回调并返回 + if (!node) { + tools.apply(callback, []); + return; + } + // 获取子节点 + var children = data.nodeChildren(setting, node); + // 判断是否为父节点 + var isParent = data.nodeIsParent(setting, node); + // 如果根节点的 expandTriggerFlag 为真 + if (root.expandTriggerFlag) { + _callback = callback; + tmpCb = function () { + // 如果存在回调函数,则执行回调 + if (_callback) _callback(); + // 根据节点的 open 状态触发相应的事件 + if (node.open) { + setting.treeObj.trigger(consts.event.EXPAND, [setting.treeId, node]); + } else { + setting.treeObj.trigger(consts.event.COLLAPSE, [setting.treeId, node]); + } + }; + callback = tmpCb; + // 将临时回调函数赋值给回调变量 + root.expandTriggerFlag = false; + // 重置根节点的展开触发标志 + } + if (!node.open && isParent && ((!$$(node, consts.id.UL, setting).get(0)) || (children && children.length > 0 && !$$(children[0], setting).get(0)))) { + // 如果节点未打开且是父节点,并且没有子节点或子节点的第一个元素不存在 + view.appendParentULDom(setting, node); + // 为父节点添加子节点的DOM结构 + view.createNodeCallback(setting); + // 创建节点的回调函数 + } + if (node.open == expandFlag) { + // 如果节点的打开状态与期望的展开标志相同 + tools.apply(callback, []); + // 执行回调函数 + return; + // 结束函数执行 + } + var ulObj = $$(node, consts.id.UL, setting), + // 获取节点的UL对象 + switchObj = $$(node, consts.id.SWITCH, setting), + // 获取节点的开关对象 + icoObj = $$(node, consts.id.ICON, setting); + // 获取节点的图标对象 + + if (isParent) { + // 如果节点是父节点 + node.open = !node.open; + // 切换节点的打开状态 + if (node.iconOpen && node.iconClose) { + // 如果节点有打开和关闭的图标 + icoObj.attr("style", view.makeNodeIcoStyle(setting, node)); + // 根据节点的状态设置图标样式 + } + + if (node.open) { + // 如果节点是打开状态,替换开关类为打开状态的样式 + view.replaceSwitchClass(node, switchObj, consts.folder.OPEN); + // 替换图标类为打开状态的样式 + view.replaceIcoClass(node, icoObj, consts.folder.OPEN); + + // 如果没有动画标志或展开速度为空,直接显示子节点 + if (animateFlag == false || setting.view.expandSpeed == "") { + ulObj.show(); + // 执行回调函数 + tools.apply(callback, []); + } else { + // 如果有子节点,使用滑动效果展开子节点 + if (children && children.length > 0) { + ulObj.slideDown(setting.view.expandSpeed, callback); + } else { + // 否则直接显示子节点 + ulObj.show(); + // 执行回调函数 + tools.apply(callback, []); + } + } + } else { + // 如果节点是关闭状态,替换开关类为关闭状态的样式 + view.replaceSwitchClass(node, switchObj, consts.folder.CLOSE); + // 替换图标类为关闭状态的样式 + view.replaceIcoClass(node, icoObj, consts.folder.CLOSE); + + // 如果没有动画标志、展开速度为空或没有子节点,直接隐藏子节点 + if (animateFlag == false || setting.view.expandSpeed == "" || !(children && children.length > 0)) { + ulObj.hide(); + // 执行回调函数 + tools.apply(callback, []); + } else { + // 否则使用滑动效果隐藏子节点 + ulObj.slideUp(setting.view.expandSpeed, callback); + } + } + } else { + // 如果节点不存在,直接执行回调函数 + tools.apply(callback, []); + } + expandCollapseParentNode: function (setting, node, expandFlag, animateFlag, callback) { + // 如果节点不存在,直接返回 + if (!node) return; + // 如果节点没有父节点ID,则直接展开或折叠该节点 + if (!node.parentTId) { + view.expandCollapseNode(setting, node, expandFlag, animateFlag, callback); + return; + } else { + // 否则先展开或折叠当前节点 + view.expandCollapseNode(setting, node, expandFlag, animateFlag); + } + // 如果节点有父节点ID,递归展开或折叠父节点 + if (node.parentTId) { + view.expandCollapseParentNode(setting, node.getParentNode(), expandFlag, animateFlag, callback); + } + }, + /** + * 展开或折叠子节点 + * @param {Object} setting - 配置对象 + * @param {Object} node - 当前节点 + * @param {Boolean} expandFlag - 是否展开 + * @param {Boolean} animateFlag - 是否带动画 + * @param {Function} callback - 回调函数 + */ + expandCollapseSonNode: function (setting, node, expandFlag, animateFlag, callback) { + // 获取根节点 + var root = data.getRoot(setting), + // 获取子节点列表 + treeNodes = (node) ? data.nodeChildren(setting, node) : data.nodeChildren(setting, root), + // 如果是根节点,则使用传入的动画标志,否则不使用动画 + selfAnimateSign = (node) ? false : animateFlag, + // 保存当前的展开触发标志 + expandTriggerFlag = data.getRoot(setting).expandTriggerFlag; + // 暂时禁用展开触发标志 + data.getRoot(setting).expandTriggerFlag = false; + // 如果存在子节点列表,遍历并展开或折叠每个子节点 + if (treeNodes) { + for (var i = 0, l = treeNodes.length; i < l; i++) { + if (treeNodes[i]) view.expandCollapseSonNode(setting, treeNodes[i], expandFlag, selfAnimateSign); + } + } + // 恢复原来的展开触发标志 + data.getRoot(setting).expandTriggerFlag = expandTriggerFlag; + // 最后展开或折叠当前节点 + view.expandCollapseNode(setting, node, expandFlag, animateFlag, callback); + }, + /** + * 判断节点是否被选中 + * @param {Object} setting - 配置对象 + * @param {Object} node - 当前节点 + * @returns {Boolean} - 是否被选中 + */ + isSelectedNode: function (setting, node) { + // 如果节点不存在,返回false + if (!node) { + return false; + } + // 获取当前选中的节点列表 + var list = data.getRoot(setting).curSelectedList, + i; + // 遍历选中的节点列表,检查是否存在与当前节点相同的节点 + for (i = list.length - 1; i >= 0; i--) { + if (node === list[i]) { + return true; + } + } + // 如果未找到匹配的节点,返回false + return false; + }, + makeDOMNodeIcon: function (html, setting, node) { + // 获取节点名称字符串 + var nameStr = data.nodeName(setting, node), + // 根据设置决定是否将名称作为HTML显示,否则进行转义处理 + name = setting.view.nameIsHTML ? nameStr : nameStr.replace(/&/g, '&').replace(//g, '>'); + // 生成包含图标和名称的HTML片段并推入html数组中 + html.push("", name, ""); + }, + makeDOMNodeLine: function (html, setting, node) { + // 生成节点线(用于表示树结构的连接线)的HTML片段并推入html数组中 + html.push(""); + }, + makeDOMNodeMainAfter: function (html, setting, node) { + // 在主节点的HTML片段后添加结束标签
  • + html.push(""); + }, + makeDOMNodeMainBefore: function (html, setting, node) { + // 在主节点的HTML片段前添加开始标签
  • ,并设置相关属性 + html.push("
  • "); + }, + makeDOMNodeNameAfter: function (html, setting, node) { + // 在节点名称的HTML片段后添加结束标签 + html.push(""); + }, + makeDOMNodeNameBefore: function (html, setting, node) { + // 获取节点标题、URL、字体CSS样式和节点类名 + var title = data.nodeTitle(setting, node), + url = view.makeNodeUrl(setting, node), + fontcss = view.makeNodeFontCss(setting, node), + nodeClasses = view.makeNodeClasses(setting, node), + fontStyle = []; + // 遍历字体CSS样式对象,将其转换为字符串形式并推入fontStyle数组中 + for (var f in fontcss) { + fontStyle.push(f, ":", fontcss[f], ";"); + } + html.push(" 0) ? " href='" + url + "'" : ""), " target='", view.makeNodeTarget(node), "' style='", fontStyle.join(''), + "'"); + // 如果需要显示标题并且标题存在,则添加title属性 + if (tools.apply(setting.view.showTitle, [setting.treeId, node], setting.view.showTitle) && title) { + html.push("title='", title.replace(/'/g, "'").replace(//g, '>'), "'"); + } + html.push(">"); + }, + makeNodeFontCss: function (setting, node) { + // 获取节点的字体样式 + var fontCss = tools.apply(setting.view.fontCss, [setting.treeId, node], setting.view.fontCss); + // 如果fontCss存在且不是函数,则返回fontCss,否则返回空对象 + return (fontCss && ((typeof fontCss) != "function")) ? fontCss : {}; + }, + makeNodeClasses: function (setting, node) { + // 获取节点的类名 + var classes = tools.apply(setting.view.nodeClasses, [setting.treeId, node], setting.view.nodeClasses); + // 如果classes存在且不是函数,则返回classes,否则返回默认的类名对象 + return (classes && (typeof classes !== "function")) ? classes : {add:[], remove:[]}; + }, + makeNodeIcoClass: function (setting, node) { + // 初始化图标类名数组 + var icoCss = ["ico"]; + // 如果节点没有正在加载数据 + if (!node.isAjaxing) { + // 判断节点是否是父节点 + var isParent = data.nodeIsParent(setting, node); + // 根据节点是否有iconSkin设置前缀 + icoCss[0] = (node.iconSkin ? node.iconSkin + "_" : "") + icoCss[0]; + if (isParent) { + // 如果是父节点,根据节点是否展开添加相应的图标类名 + icoCss.push(node.open ? consts.folder.OPEN : consts.folder.CLOSE); + } else { + // 如果不是父节点,添加文档图标类名 + icoCss.push(consts.folder.DOCU); + } + } + // 返回拼接后的按钮类名和图标类名 + return consts.className.BUTTON + " " + icoCss.join('_'); + }, + makeNodeIcoStyle: function (setting, node) { + // 初始化图标样式数组 + var icoStyle = []; + // 如果节点没有进行Ajax请求 + if (!node.isAjaxing) { + // 判断节点是否是父节点 + var isParent = data.nodeIsParent(setting, node); + // 根据节点状态选择相应的图标 + var icon = (isParent && node.iconOpen && node.iconClose) ? (node.open ? node.iconOpen : node.iconClose) : node[setting.data.key.icon]; + // 如果存在图标,则添加背景图片样式 + if (icon) icoStyle.push("background:url(", icon, ") 0 0 no-repeat;"); + // 如果视图设置中不显示图标或应用showIcon函数返回false,则隐藏图标 + if (setting.view.showIcon == false || !tools.apply(setting.view.showIcon, [setting.treeId, node], true)) { + icoStyle.push("display:none;"); + } + } + // 返回拼接后的图标样式字符串 + return icoStyle.join(''); + }, + makeNodeLineClass: function (setting, node) { + // 初始化线条样式数组 + var lineClass = []; + // 如果视图设置中显示线条 + if (setting.view.showLine) { + // 根据节点层级和位置添加相应的线条样式 + // 检查节点是否为根节点且是第一个和最后一个节点 + if (node.level == 0 && node.isFirstNode && node.isLastNode) { + // 将根节点的样式类添加到lineClass数组中 + lineClass.push(consts.line.ROOT); + } else if (node.level == 0 && node.isFirstNode) { + // 检查节点是否为根节点且是第一个节点 + // 将根节点的第一个样式类添加到lineClass数组中 + lineClass.push(consts.line.ROOTS); + } else if (node.isLastNode) { + // 检查节点是否是最后一个节点 + // 将底部节点的样式类添加到lineClass数组中 + lineClass.push(consts.line.BOTTOM); + } else { + // 默认情况下,将中心节点的样式类添加到lineClass数组中 + lineClass.push(consts.line.CENTER); + } + } else { + // 如果不显示线条,则添加无线条样式 + lineClass.push(consts.line.NOLINE); + } + // 根据节点是否为父节点添加文件夹样式 + if (data.nodeIsParent(setting, node)) { + lineClass.push(node.open ? consts.folder.OPEN : consts.folder.CLOSE); + } else { + lineClass.push(consts.folder.DOCU); + } + // 返回拼接后的线条样式字符串,并调用makeNodeLineClassEx方法进一步处理 + return view.makeNodeLineClassEx(node) + lineClass.join('_'); + }, + makeNodeLineClassEx: function (node) { + // 返回节点的按钮、层级和开关样式字符串 + return consts.className.BUTTON + " " + consts.className.LEVEL + node.level + " " + consts.className.SWITCH + " "; + }, + makeNodeTarget: function (node) { + // 返回节点的目标属性,如果不存在则默认为"_blank" + return (node.target || "_blank"); + }, + makeNodeUrl: function (setting, node) { + // 获取URL键值 + var urlKey = setting.data.key.url; + // 返回节点的URL,如果不存在则返回null + return node[urlKey] ? node[urlKey] : null; + }, + makeUlHtml: function (setting, node, html, content) { + // 生成UL元素的HTML代码,并根据节点状态设置显示或隐藏 + html.push("
      "); + // 插入内容到UL元素中 + html.push(content); + // 关闭UL标签 + html.push("
    "); + }, + makeUlLineClass: function (setting, node) { + // 如果视图显示线条且节点不是最后一个节点,则返回线条类名,否则返回空字符串 + return ((setting.view.showLine && !node.isLastNode) ? consts.line.LINE : ""); + }, + removeChildNodes: function (setting, node) { + // 如果节点不存在,直接返回 + if (!node) return; + // 获取节点的子节点列表 + var nodes = data.nodeChildren(setting, node); + // 如果子节点列表不存在,直接返回 + if (!nodes) return; + + // 遍历所有子节点 + for (var i = 0, l = nodes.length; i < l; i++) { + // 移除每个子节点的缓存 + data.removeNodeCache(setting, nodes[i]); + } + data.removeSelectedNode(setting); // 移除选中的节点 + delete node[setting.data.key.children]; // 删除节点的子节点属性 + + if (!setting.data.keep.parent) { // 如果设置中不保留父节点 + data.nodeIsParent(setting, node, false); // 将当前节点设置为非父节点 + node.open = false; // 关闭当前节点 + var tmp_switchObj = $$(node, consts.id.SWITCH, setting), // 获取当前节点的开关对象 + tmp_icoObj = $$(node, consts.id.ICON, setting); // 获取当前节点的图标对象 + view.replaceSwitchClass(node, tmp_switchObj, consts.folder.DOCU); // 替换开关对象的类为文档文件夹样式 + view.replaceIcoClass(node, tmp_icoObj, consts.folder.DOCU); // 替换图标对象的类为文档文件夹样式 + $$(node, consts.id.UL, setting).remove(); // 移除当前节点的子节点列表 + } else { + $$(node, consts.id.UL, setting).empty(); // 如果保留父节点,则清空子节点列表 + } + scrollIntoView: function (setting, dom) { + // 如果dom不存在,直接返回 + if (!dom) { + return; + } + // 支持IE 7 / 8 + if (typeof Element === 'undefined' || typeof HTMLElement === 'undefined') { + // 获取容器的边界矩形 + var contRect = setting.treeObj.get(0).getBoundingClientRect(), + // 获取目标元素的边界矩形 + findMeRect = dom.getBoundingClientRect(); + // 检查目标元素是否在容器视图之外 + if (findMeRect.top < contRect.top || findMeRect.bottom > contRect.bottom + || findMeRect.right > contRect.right || findMeRect.left < contRect.left) { + // 将目标元素滚动到视图中 + dom.scrollIntoView(); + } + return; + } + // CC-BY jocki84@googlemail.com, https://gist.github.com/jocki84/6ffafd003387179a988e + if (!Element.prototype.scrollIntoViewIfNeeded) { + // 如果浏览器不支持 scrollIntoViewIfNeeded 方法,则定义该方法 + Element.prototype.scrollIntoViewIfNeeded = function (centerIfNeeded) { + "use strict"; + + // 创建一个表示范围的对象,包含起始位置和长度 + function makeRange(start, length) { + return {"start": start, "length": length, "end": start + length}; + } + + // 根据是否需要居中来调整范围的覆盖方式 + function coverRange(inner, outer) { + if ( + false === centerIfNeeded || + (outer.start < inner.end && inner.start < outer.end) + ) { + // 如果不居中或内外范围有重叠,返回最大值或最小值 + return Math.max( + inner.end - outer.length, + Math.min(outer.start, inner.start) + ); + } + // 如果需要居中,返回中间值 + return (inner.start + inner.end - outer.length) / 2; + } + + // 创建一个表示点的对象,包含 x 和 y 坐标以及平移方法 + function makePoint(x, y) { + return { + "x": x, + "y": y, + "translate": function translate(dX, dY) { + // 平移点的位置 + return makePoint(x + dX, y + dY); + } + }; + } + + /** + * 计算元素在页面中的绝对位置。 + * @param {HTMLElement} elem - 需要计算位置的元素。 + * @param {Point} pt - 初始点,用于累加偏移量。 + * @returns {Point} - 返回元素的绝对位置。 + */ + function absolute(elem, pt) { + // 当元素存在时循环执行 + while (elem) { + // 将当前元素的偏移量累加到初始点上 + pt = pt.translate(elem.offsetLeft, elem.offsetTop); + // 获取当前元素的父级偏移容器 + elem = elem.offsetParent; + } + // 返回计算后的绝对位置 + return pt; + } + + var target = absolute(this, makePoint(0, 0)), // 计算目标元素的绝对位置 + extent = makePoint(this.offsetWidth, this.offsetHeight), // 获取目标元素的宽度和高度 + elem = this.parentNode, // 获取目标元素的父节点 + origin; // 定义变量origin用于存储当前元素的绝对位置 + + while (elem instanceof HTMLElement) { // 当elem是HTML元素时循环执行 + // Apply desired scroll amount. + origin = absolute(elem, makePoint(elem.clientLeft, elem.clientTop)); // 计算当前元素的绝对位置 + elem.scrollLeft = coverRange( // 设置水平滚动条的位置,使其覆盖目标范围 + makeRange(target.x - origin.x, extent.x), // 计算目标范围的左边界 + makeRange(elem.scrollLeft, elem.clientWidth) // 计算当前可视区域的范围 + ); + elem.scrollTop = coverRange( // 设置垂直滚动条的位置,使其覆盖目标范围 + makeRange(target.y - origin.y, extent.y), // 计算目标范围的上边界 + makeRange(elem.scrollTop, elem.clientHeight) // 计算当前可视区域的范围 + ); + + // Determine actual scroll amount by reading back scroll properties. + target = target.translate(-elem.scrollLeft, -elem.scrollTop); + // 将目标元素的位置平移,以考虑滚动条的偏移量 + + elem = elem.parentNode; + // 将当前元素更新为其父节点,以便继续向上遍历DOM树 + + dom.scrollIntoViewIfNeeded(); + // 如果需要,将DOM元素滚动到视图中 + + setFirstNode: function (setting, parentNode) { + // 定义一个方法,用于设置父节点的第一个子节点 + + var children = data.nodeChildren(setting, parentNode); + // 获取父节点的所有子节点 + + if (children.length > 0) { + // 如果父节点有子节点 + + children[0].isFirstNode = true; + // 将第一个子节点标记为第一个节点 + } + }, + setLastNode: function (setting, parentNode) { + // 获取父节点的所有子节点 + var children = data.nodeChildren(setting, parentNode); + // 如果子节点数组不为空,则将最后一个子节点标记为isLastNode + if (children.length > 0) { + children[children.length - 1].isLastNode = true; + } + }, + removeNode: function (setting, node) { + // 获取根节点 + var root = data.getRoot(setting), + // 获取父节点,如果当前节点有parentTId属性,则获取其父节点,否则获取根节点 + parentNode = (node.parentTId) ? node.getParentNode() : root; + + // 将当前节点的isFirstNode和isLastNode属性设为false + node.isFirstNode = false; + node.isLastNode = false; + // 定义一个方法,返回null,表示没有前一个节点 + node.getPreNode = function () { + return null; + }; + // 定义一个方法,返回null,表示没有下一个节点 + node.getNextNode = function () { + return null; + }; + + // 检查节点缓存是否存在,如果不存在则直接返回 + if (!data.getNodeCache(setting, node.tId)) { + return; + } + + // 从DOM中移除节点 + $$(node, setting).remove(); + // 从节点缓存中移除该节点 + data.removeNodeCache(setting, node); + // 从选中的节点列表中移除该节点 + data.removeSelectedNode(setting, node); + + // 获取父节点的所有子节点 + var children = data.nodeChildren(setting, parentNode); + // 遍历所有子节点 + for (var i = 0, l = children.length; i < l; i++) { + // 如果找到与要删除节点相同的节点 + if (children[i].tId == node.tId) { + // 从子节点数组中移除该节点 + children.splice(i, 1); + // 退出循环 + break; + } + } + view.setFirstNode(setting, parentNode); // 设置第一个节点 + view.setLastNode(setting, parentNode); // 设置最后一个节点 + + var tmp_ulObj, tmp_switchObj, tmp_icoObj, + childLength = children.length; // 获取子节点的长度 + + // 修复旧父节点的子节点信息 + if (!setting.data.keep.parent && childLength == 0) { + // 如果旧父节点没有子节点 + data.nodeIsParent(setting, parentNode, false); // 更新数据,标记该节点不是父节点 + parentNode.open = false; // 关闭父节点 + delete parentNode[setting.data.key.children]; // 删除父节点的子节点属性 + tmp_ulObj = $$(parentNode, consts.id.UL, setting); // 获取父节点的UL对象 + tmp_switchObj = $$(parentNode, consts.id.SWITCH, setting); // 获取父节点的SWITCH对象 + tmp_icoObj = $$(parentNode, consts.id.ICON, setting); // 获取父节点的ICON对象 + view.replaceSwitchClass(parentNode, tmp_switchObj, consts.folder.DOCU); // 替换SWITCH对象的类为文档文件夹样式 + view.replaceIcoClass(parentNode, tmp_icoObj, consts.folder.DOCU); // 替换ICON对象的类为文档文件夹样式 + tmp_ulObj.css("display", "none"); // 隐藏UL对象 + } + else if (setting.view.showLine && childLength > 0) { + // 如果设置中显示线条且子节点数量大于0 + var newLast = children[childLength - 1]; + // 获取最后一个子节点 + tmp_ulObj = $$(newLast, consts.id.UL, setting); + // 获取最后一个子节点的UL对象 + tmp_switchObj = $$(newLast, consts.id.SWITCH, setting); + // 获取最后一个子节点的SWITCH对象 + tmp_icoObj = $$(newLast, consts.id.ICON, setting); + // 获取最后一个子节点的ICON对象 + if (parentNode == root) { + // 如果父节点是根节点 + if (children.length == 1) { + // 如果只有一个子节点,即该节点原本是根节点 + view.replaceSwitchClass(newLast, tmp_switchObj, consts.line.ROOT); + // 替换为根节点样式 + } else { + var tmp_first_switchObj = $$(children[0], consts.id.SWITCH, setting); + // 获取第一个子节点的SWITCH对象 + view.replaceSwitchClass(children[0], tmp_first_switchObj, consts.line.ROOTS); + // 替换第一个子节点为多根节点样式 + view.replaceSwitchClass(newLast, tmp_switchObj, consts.line.BOTTOM); + // 替换最后一个子节点为底部节点样式 + } + } else { + view.replaceSwitchClass(newLast, tmp_switchObj, consts.line.BOTTOM); + // 如果不是根节点,则替换最后一个子节点为底部节点样式 + } + tmp_ulObj.removeClass(consts.line.LINE); + // 移除UL对象的线条样式 + } + }, + /** + * 替换节点的图标类名 + * @param {Object} node - DOM节点对象 + * @param {Object} obj - 包含class属性的对象 + * @param {string} newName - 新的类名 + */ + replaceIcoClass: function (node, obj, newName) { + // 如果obj为空或节点正在Ajax请求中,则直接返回 + if (!obj || node.isAjaxing) return; + + // 获取对象的class属性值 + var tmpName = obj.attr("class"); + + // 如果class属性值为undefined,则直接返回 + if (tmpName == undefined) return; + + // 将class属性值按"_"分割成数组 + var tmpList = tmpName.split("_"); + + // 根据newName的值进行不同的处理 + switch (newName) { + case consts.folder.OPEN: + case consts.folder.CLOSE: + case consts.folder.DOCU: + // 将数组最后一个元素替换为newName + tmpList[tmpList.length - 1] = newName; + break; + } + + // 将修改后的数组重新拼接成字符串并设置回对象的class属性 + obj.attr("class", tmpList.join("_")); + }, + /** + * 替换节点的类名 + * @param {Object} node - 需要操作的节点对象 + * @param {Object} obj - 包含类名属性的对象 + * @param {string} newName - 新的类名 + */ + replaceSwitchClass: function (node, obj, newName) { + // 如果obj为空,直接返回 + if (!obj) return; + // 获取当前对象的class属性值 + var tmpName = obj.attr("class"); + // 如果class属性值为undefined,直接返回 + if (tmpName == undefined) return; + // 将class属性值按"_"分割成数组 + var tmpList = tmpName.split("_"); + // 根据newName的值进行不同的处理 + switch (newName) { + case consts.line.ROOT: + case consts.line.ROOTS: + case consts.line.CENTER: + case consts.line.BOTTOM: + case consts.line.NOLINE: + // 更新数组的第一个元素为新的类名 + tmpList[0] = view.makeNodeLineClassEx(node) + newName; + break; + case consts.folder.OPEN: + case consts.folder.CLOSE: + case consts.folder.DOCU: + // 更新数组的第二个元素为新的类名 + tmpList[1] = newName; + break; + } + // 将更新后的数组重新拼接成字符串并设置回class属性 + obj.attr("class", tmpList.join("_")); + // 如果newName不是DOCU,移除disabled属性;否则添加disabled属性 + if (newName !== consts.folder.DOCU) { + obj.removeAttr("disabled"); + } else { + obj.attr("disabled", "disabled"); + } + }, + selectNode: function (setting, node, addFlag) { + // 如果没有添加标志,则取消先前选择的节点 + if (!addFlag) { + view.cancelPreSelectedNode(setting, null, node); + } + // 为当前节点添加选中样式 + $$(node, consts.id.A, setting).addClass(consts.node.CURSELECTED); + // 将当前节点添加到已选节点列表中 + data.addSelectedNode(setting, node); + // 触发选中事件 + setting.treeObj.trigger(consts.event.SELECTED, [setting.treeId, node]); + }, + setNodeFontCss: function (setting, treeNode) { + // 获取节点对应的DOM对象 + var aObj = $$(treeNode, consts.id.A, setting), + // 生成节点字体样式 + fontCss = view.makeNodeFontCss(setting, treeNode); + // 如果存在字体样式,则应用到节点上 + if (fontCss) { + aObj.css(fontCss); + } + }, + /** + * 设置节点的CSS类 + * @param {Object} setting - 配置对象 + * @param {Object} treeNode - 树节点对象 + */ + setNodeClasses: function (setting, treeNode) { + // 获取树节点对应的
    元素对象 + var aObj = $$(treeNode, consts.id.A, setting), + // 生成需要添加和移除的CSS类名集合 + classes = view.makeNodeClasses(setting, treeNode); + + // 如果需要添加的CSS类名存在且不为空,则添加到元素中 + if ('add' in classes && classes.add.length) { + aObj.addClass(classes.add.join(' ')); + } + + // 如果需要移除的CSS类名存在且不为空,则从元素中移除 + if ('remove' in classes && classes.remove.length) { + aObj.removeClass(classes.remove.join(' ')); + } + }, + setNodeLineIcos: function (setting, node) { + // 如果节点不存在,直接返回 + if (!node) return; + + // 获取节点的开关对象、UL对象和图标对象 + var switchObj = $$(node, consts.id.SWITCH, setting), + ulObj = $$(node, consts.id.UL, setting), + icoObj = $$(node, consts.id.ICON, setting), + + // 生成UL对象的行样式类名 + ulLine = view.makeUlLineClass(setting, node); + + // 如果行样式类名为空,移除UL对象的行样式类名 + if (ulLine.length == 0) { + ulObj.removeClass(consts.line.LINE); + } else { + // 否则,添加行样式类名到UL对象 + ulObj.addClass(ulLine); + } + + // 设置开关对象的类名 + switchObj.attr("class", view.makeNodeLineClass(setting, node)); + + // 如果节点是父节点,移除开关对象的禁用属性;否则,设置开关对象为禁用状态 + if (data.nodeIsParent(setting, node)) { + switchObj.removeAttr("disabled"); + } else { + switchObj.attr("disabled", "disabled"); + } + + // 移除图标对象的内联样式 + icoObj.removeAttr("style"); + + // 设置图标对象的内联样式和类名 + icoObj.attr("style", view.makeNodeIcoStyle(setting, node)); + icoObj.attr("class", view.makeNodeIcoClass(setting, node)); + }, + setNodeName: function (setting, node) { + // 获取节点的标题 + var title = data.nodeTitle(setting, node), + // 获取节点对应的SPAN元素对象 + nObj = $$(node, consts.id.SPAN, setting); + // 清空SPAN元素的内容 + nObj.empty(); + // 如果视图名称是HTML格式 + if (setting.view.nameIsHTML) { + // 将节点的名称作为HTML内容设置到SPAN元素中 + nObj.html(data.nodeName(setting, node)); + } else { + // 将节点的名称作为文本内容设置到SPAN元素中 + nObj.text(data.nodeName(setting, node)); + } + // 如果需要显示标题 + if (tools.apply(setting.view.showTitle, [setting.treeId, node], setting.view.showTitle)) { + // 获取节点对应的A元素对象 + var aObj = $$(node, consts.id.A, setting); + // 设置A元素的title属性为节点的标题,如果标题为空则设置为空字符串 + aObj.attr("title", !title ? "" : title); + } + }, + setNodeTarget: function (setting, node) { + // 获取节点对应的元素对象 + var aObj = $$(node, consts.id.A, setting); + // 设置元素的target属性为生成的节点目标值 + aObj.attr("target", view.makeNodeTarget(node)); + }, + setNodeUrl: function (setting, node) { + // 获取节点对应的元素对象 + var aObj = $$(node, consts.id.A, setting), + // 生成节点的URL + url = view.makeNodeUrl(setting, node); + // 如果URL为空或长度为0,则移除元素的href属性 + if (url == null || url.length == 0) { + aObj.removeAttr("href"); + } else { + // 否则,设置元素的href属性为生成的URL + aObj.attr("href", url); + } + }, + switchNode: function (setting, node) { + // 如果节点已经打开,或者节点不能异步加载,则切换节点的展开/折叠状态 + if (node.open || !tools.canAsync(setting, node)) { + // 调用视图方法切换节点的展开/折叠状态 + view.expandCollapseNode(setting, node, !node.open); + } else if (setting.async.enable) { + // 如果启用了异步加载,并且节点不能异步加载,则切换节点的展开/折叠状态 + if (!view.asyncNode(setting, node)) { + // 调用视图方法切换节点的展开/折叠状态 + view.expandCollapseNode(setting, node, !node.open); + return; // 结束函数执行 + } + } else if (node) { + // 如果节点存在,则切换节点的展开/折叠状态 + view.expandCollapseNode(setting, node, !node.open); + } + } + }; + // zTree defind + $.fn.zTree = { + // 常量对象,包含一些全局的常量值 + consts: _consts, + // 内部工具对象,包含各种工具方法 + _z: { + tools: tools, + // 视图相关的方法 + view: view, + // 事件相关的方法 + event: event, + // 数据相关的方法 + data: data + }, + /** + * 获取指定树ID的zTree对象 + * @param {string} treeId - 树的ID + * @returns {Object|null} 返回对应的zTree对象,如果不存在则返回null + */ + getZTreeObj: function (treeId) { + // 从data中获取zTree工具对象 + var o = data.getZTreeTools(treeId); + // 如果存在则返回该对象,否则返回null + return o ? o : null; + }, + /** + * 销毁指定的zTree实例或所有zTree实例 + * @param {string} [treeId] - 可选参数,树的ID。如果提供则销毁指定ID的树,否则销毁所有树 + */ + destroy: function (treeId) { + // 如果提供了treeId且其长度大于0 + if (!!treeId && treeId.length > 0) { + // 销毁指定ID的树 + view.destroy(data.getSetting(treeId)); + } else { + // 遍历所有设置并销毁每一个树 + for (var s in settings) { + view.destroy(settings[s]); + } + } + }, + init: function (obj, zSetting, zNodes) { + // 克隆默认设置对象 + var setting = tools.clone(_setting); + // 深度合并用户自定义设置到默认设置中 + $.extend(true, setting, zSetting); + // 设置树的ID为传入对象的ID + setting.treeId = obj.attr("id"); + // 将传入的对象赋值给树对象 + setting.treeObj = obj; + // 清空树对象的内容 + setting.treeObj.empty(); + // 将设置存储在全局settings对象中,以树ID为键 + settings[setting.treeId] = setting; + // 针对一些旧浏览器(例如ie6),如果不支持maxHeight样式属性,则禁用展开速度 + if (typeof document.body.style.maxHeight === "undefined") { + setting.view.expandSpeed = ""; + } + // 初始化根节点数据 + data.initRoot(setting); + // 获取根节点数据 + var root = data.getRoot(setting); + // 如果zNodes存在,则克隆并转换为数组格式;否则设置为空数组 + zNodes = zNodes ? tools.clone(tools.isArray(zNodes) ? zNodes : [zNodes]) : []; + // 如果启用了简单数据模式,则转换节点数据格式后添加到根节点 + if (setting.data.simpleData.enable) { + data.nodeChildren(setting, root, data.transformTozTreeFormat(setting, zNodes)); + } else { + // 否则直接添加节点数据到根节点 + data.nodeChildren(setting, root, zNodes); + } + + // 初始化缓存,传入设置参数 + data.initCache(setting); + + // 解绑树形结构的事件,传入设置参数 + event.unbindTree(setting); + + // 绑定树形结构的事件,传入设置参数 + event.bindTree(setting); + + // 解绑其他事件,传入设置参数 + event.unbindEvent(setting); + + // 绑定其他事件,传入设置参数 + event.bindEvent(setting); + var zTreeTools = { + // 设置对象 + setting: setting, + /** + * 添加节点函数 + * @param {Object} parentNode - 父节点对象 + * @param {number|string} index - 插入位置的索引或新节点数组 + * @param {Array|Object} newNodes - 要添加的新节点,可以是单个节点对象或节点数组 + * @param {boolean} isSilent - 是否静默添加(不触发事件) + * @returns {null|undefined} 如果无法添加节点则返回null + */ + addNodes: function (parentNode, index, newNodes, isSilent) { + // 如果未提供父节点,则默认为null + if (!parentNode) parentNode = null; + // 检查父节点是否是父节点类型 + var isParent = data.nodeIsParent(setting, parentNode); + // 如果父节点不是父节点且配置中要求保持叶子节点,则返回null + if (parentNode && !isParent && setting.data.keep.leaf) return null; + + // 将index转换为整数 + var i = parseInt(index, 10); + // 如果index不是数字,则调整参数 + if (isNaN(i)) { + isSilent = !!newNodes; // 判断newNodes是否存在,存在则isSilent为true + newNodes = index; // 将index赋值给newNodes + index = -1; // 重置index为-1 + } else { + index = i; // 否则使用转换后的整数index + } + // 如果newNodes不存在,则返回null + if (!newNodes) return null; + + // 克隆新的节点数组,确保其为数组形式 + var xNewNodes = tools.clone(tools.isArray(newNodes) ? newNodes : [newNodes]); + + // 定义添加节点的回调函数 + function addCallback() { + // 调用视图层的addNodes方法实际添加节点 + view.addNodes(setting, parentNode, index, xNewNodes, (isSilent == true)); + } + + if (tools.canAsync(setting, parentNode)) { + // 检查是否可以异步加载节点,如果可以则调用view.asyncNode方法进行异步加载 + view.asyncNode(setting, parentNode, isSilent, addCallback); + } else { + // 如果不支持异步加载,直接调用回调函数 + addCallback(); + } + // 返回新添加的节点 + return xNewNodes; + }, + cancelSelectedNode: function (node) { + // 取消选中的节点 + view.cancelPreSelectedNode(setting, node); + }, + destroy: function () { + // 销毁视图 + view.destroy(setting); + }, + expandAll: function (expandFlag) { + // 确保expandFlag为布尔值 + expandFlag = !!expandFlag; + // 展开或折叠所有子节点 + view.expandCollapseSonNode(setting, null, expandFlag, true); + // 返回最终的展开标志 + return expandFlag; + }, + expandNode: function (node, expandFlag, sonSign, focus, callbackFlag) { + // 如果节点不存在或者不是父节点,返回null + if (!node || !data.nodeIsParent(setting, node)) return null; + // 如果expandFlag既不是true也不是false,则取反当前节点的open状态 + if (expandFlag !== true && expandFlag !== false) { + expandFlag = !node.open; + } + // 确保callbackFlag为布尔值 + callbackFlag = !!callbackFlag; + + // 检查是否需要在展开或折叠节点之前执行回调函数 + if (callbackFlag && expandFlag && (tools.apply(setting.callback.beforeExpand, [setting.treeId, node], true) == false)) { + // 如果回调函数返回false,则不进行展开操作,直接返回null + return null; + } else if (callbackFlag && !expandFlag && (tools.apply(setting.callback.beforeCollapse, [setting.treeId, node], true) == false)) { + // 如果回调函数返回false,则不进行折叠操作,直接返回null + return null; + } + + // 如果需要展开节点且该节点有父节点 + if (expandFlag && node.parentTId) { + // 递归展开父节点 + view.expandCollapseParentNode(setting, node.getParentNode(), expandFlag, false); + } + + // 如果节点的展开状态与目标状态相同且没有子节点标志 + if (expandFlag === node.open && !sonSign) { + // 不需要改变节点状态,直接返回null + return null; + } + + // 设置根节点的展开触发标志为回调标志 + data.getRoot(setting).expandTriggerFlag = callbackFlag; + + // 如果当前节点不能异步加载并且有子节点 + if (!tools.canAsync(setting, node) && sonSign) { + // 展开或折叠子节点,并显示节点焦点 + view.expandCollapseSonNode(setting, node, expandFlag, true, showNodeFocus); + } else { + // 否则,根据展开标志设置节点的打开状态 + node.open = !expandFlag; + // 切换节点的状态 + view.switchNode(this.setting, node); + // 显示节点焦点 + showNodeFocus(); + } + + // 返回展开标志 + return expandFlag; + + function showNodeFocus() { + // 获取节点对象 + var a = $$(node, setting).get(0); + // 如果节点存在且焦点不为false,则将视图滚动到该节点位置 + if (a && focus !== false) { + view.scrollIntoView(setting, a); + } + } + }, + getNodes: function () { + // 返回所有节点数据 + return data.getNodes(setting); + }, + getNodeByParam: function (key, value, parentNode) { + // 如果键不存在,返回null + if (!key) return null; + // 根据参数获取节点,如果parentNode存在,则在子节点中查找,否则在所有节点中查找 + return data.getNodeByParam(setting, parentNode ? data.nodeChildren(setting, parentNode) : data.getNodes(setting), key, value); + }, + getNodeByTId: function (tId) { + // 根据节点ID获取缓存中的节点对象 + return data.getNodeCache(setting, tId); + }, + getNodesByParam: function (key, value, parentNode) { + // 如果键不存在,返回null + if (!key) return null; + // 根据参数获取节点数组,如果parentNode存在,则在子节点中查找,否则在所有节点中查找 + return data.getNodesByParam(setting, parentNode ? data.nodeChildren(setting, parentNode) : data.getNodes(setting), key, value); + }, + getNodesByParamFuzzy: function (key, value, parentNode) { + // 如果键不存在,返回null + if (!key) return null; + // 根据模糊参数获取节点数组,如果parentNode存在,则在子节点中查找,否则在所有节点中查找 + return data.getNodesByParamFuzzy(setting, parentNode ? data.nodeChildren(setting, parentNode) : data.getNodes(setting), key, value); + }, + getNodesByFilter: function (filter, isSingle, parentNode, invokeParam) { + // 将isSingle转换为布尔值 + isSingle = !!isSingle; + // 如果过滤器不存在或不是函数,返回null或空数组 + if (!filter || (typeof filter != "function")) return (isSingle ? null : []); + // 根据过滤器获取节点数组,如果parentNode存在,则在子节点中查找,否则在所有节点中查找 + return data.getNodesByFilter(setting, parentNode ? data.nodeChildren(setting, parentNode) : data.getNodes(setting), filter, isSingle, invokeParam); + }, + getNodeIndex: function (node) { + // 如果节点为空,返回null + if (!node) return null; + // 获取父节点,如果节点有parentTId属性则调用getParentNode方法,否则获取根节点 + var parentNode = (node.parentTId) ? node.getParentNode() : data.getRoot(setting); + // 获取父节点的子节点列表 + var children = data.nodeChildren(setting, parentNode); + // 遍历子节点列表,找到与目标节点匹配的索引并返回 + for (var i = 0, l = children.length; i < l; i++) { + if (children[i] == node) return i; + } + // 如果未找到匹配的节点,返回-1 + return -1; + }, + getSelectedNodes: function () { + // 初始化一个空数组用于存储选中的节点 + var r = [], list = data.getRoot(setting).curSelectedList; + // 遍历当前选中的节点列表,将每个节点添加到结果数组中 + for (var i = 0, l = list.length; i < l; i++) { + r.push(list[i]); + } + // 返回包含所有选中节点的数组 + return r; + }, + isSelectedNode: function (node) { + // 判断指定节点是否被选中,返回布尔值 + return data.isSelectedNode(setting, node); + }, + reAsyncChildNodesPromise: function (parentNode, reloadType, isSilent) { + // 创建一个新的Promise对象 + var promise = new Promise(function (resolve, reject) { + try { + // 异步重新加载子节点,并在完成后解析Promise + zTreeTools.reAsyncChildNodes(parentNode, reloadType, isSilent, function () { + resolve(parentNode); + }); + } catch (e) { + // 如果发生错误,拒绝Promise并传递错误信息 + reject(e); + } + }); + // 返回Promise对象 + return promise; + }, + /** + * 重新加载异步子节点 + * @param {Object} parentNode - 父节点对象 + * @param {string} reloadType - 重载类型,可以是 "refresh" + * @param {boolean} isSilent - 是否静默加载 + * @param {function} callback - 回调函数 + */ + reAsyncChildNodes: function (parentNode, reloadType, isSilent, callback) { + // 如果异步加载未启用,则直接返回 + if (!this.setting.async.enable) return; + + // 判断是否是根节点 + var isRoot = !parentNode; + + // 如果是根节点,获取根节点数据 + if (isRoot) { + parentNode = data.getRoot(setting); + } + + // 如果重载类型是 "refresh" + if (reloadType == "refresh") { + // 获取当前父节点的所有子节点 + var children = data.nodeChildren(setting, parentNode); + + // 遍历所有子节点并移除缓存 + for (var i = 0, l = children ? children.length : 0; i < l; i++) { + data.removeNodeCache(setting, children[i]); + } + + // 移除选中的节点 + data.removeSelectedNode(setting); + + // 清空父节点的子节点列表 + data.nodeChildren(setting, parentNode, []); + + // 如果是根节点,清空树对象 + if (isRoot) { + this.setting.treeObj.empty(); + } else { + // 否则清空父节点对应的UL元素 + var ulObj = $$(parentNode, consts.id.UL, setting); + ulObj.empty(); + } + } + + // 异步加载节点 + view.asyncNode(this.setting, isRoot ? null : parentNode, !!isSilent, callback); + }, + refresh: function () { + // 清空树对象 + this.setting.treeObj.empty(); + + // 获取根节点 + var root = data.getRoot(setting), + // 获取根节点的子节点 + nodes = data.nodeChildren(setting, root); + + // 初始化根节点 + data.initRoot(setting); + + // 将子节点重新添加到根节点 + data.nodeChildren(setting, root, nodes); + + // 初始化缓存 + data.initCache(setting); + + // 创建节点视图,参数依次为:设置、层级、子节点数组、父节点、父节点ID + view.createNodes(setting, 0, data.nodeChildren(setting, root), null, -1); + }, + removeChildNodes: function (node) { + // 如果节点为空,返回null + if (!node) return null; + // 获取指定节点的子节点 + var nodes = data.nodeChildren(setting, node); + // 从视图中移除这些子节点 + view.removeChildNodes(setting, node); + // 如果有子节点则返回它们,否则返回null + return nodes ? nodes : null; + }, + removeNode: function (node, callbackFlag) { + // 如果节点为空,直接返回 + if (!node) return; + // 确保callbackFlag是布尔值 + callbackFlag = !!callbackFlag; + // 如果callbackFlag为true且beforeRemove回调函数返回false,则不继续执行 + if (callbackFlag && tools.apply(setting.callback.beforeRemove, [setting.treeId, node], true) == false) return; + // 从视图中移除该节点 + view.removeNode(setting, node); + // 如果callbackFlag为true,触发REMOVE事件 + if (callbackFlag) { + this.setting.treeObj.trigger(consts.event.REMOVE, [setting.treeId, node]); + } + }, + selectNode: function (node, addFlag, isSilent) { + // 如果节点为空,直接返回 + if (!node) return; + + // 检查用户是否有权限进行操作 + if (tools.uCanDo(setting)) { + // 如果允许多选并且addFlag为true,则保持addFlag为true + addFlag = setting.view.selectedMulti && addFlag; + + // 如果节点有父节点 + if (node.parentTId) { + // 展开父节点并聚焦到该节点 + view.expandCollapseParentNode(setting, node.getParentNode(), true, false, showNodeFocus); + } else if (!isSilent) { + // 如果节点没有父节点且不静默处理 + try { + // 尝试让节点获取焦点并失去焦点 + $$(node, setting).focus().blur(); + } catch (e) { + // 捕获异常但不做任何处理 + } + } + // 选择节点,根据addFlag决定是否添加选中状态 + view.selectNode(setting, node, addFlag); + } + + function showNodeFocus() { + // 如果当前处于静默模式,则直接返回,不执行后续操作 + if (isSilent) { + return; + } + // 获取指定节点的第一个元素 + var a = $$(node, setting).get(0); + // 将视图滚动到指定节点的位置 + view.scrollIntoView(setting, a); + } + + /** + * 将简单节点数组转换为zTree节点格式 + * @param {Array} simpleNodes - 简单节点数组 + * @returns {Array} zTree节点格式的数组 + */ + transformTozTreeNodes: function (simpleNodes) { + return data.transformTozTreeFormat(setting, simpleNodes); + }, + + /** + * 将节点数组转换为普通数组格式 + * @param {Array} nodes - 节点数组 + * @returns {Array} 普通数组格式的数组 + */ + transformToArray: function (nodes) { + return data.transformToArrayFormat(setting, nodes); + }, + updateNode: function (node, checkTypeFlag) { + // 如果节点不存在,直接返回 + if (!node) return; + + // 获取节点对象 + var nObj = $$(node, setting); + + // 如果节点对象存在且用户有权限进行操作 + if (nObj.get(0) && tools.uCanDo(setting)) { + // 设置节点名称 + view.setNodeName(setting, node); + + // 设置节点目标 + view.setNodeTarget(setting, node); + + // 设置节点URL + view.setNodeUrl(setting, node); + + // 设置节点图标 + view.setNodeLineIcos(setting, node); + + // 设置节点字体样式 + view.setNodeFontCss(setting, node); + + // 设置节点类名 + view.setNodeClasses(setting, node); + } + } + root.treeTools = zTreeTools; // 将zTreeTools对象赋值给根节点的treeTools属性 + data.setZTreeTools(setting, zTreeTools); // 设置zTree工具到数据对象中 + var children = data.nodeChildren(setting, root); // 获取根节点的子节点 + if (children && children.length > 0) { // 如果存在子节点且子节点数量大于0 + view.createNodes(setting, 0, children, null, -1); // 创建子节点视图 + } else if (setting.async.enable && setting.async.url && setting.async.url !== '') { // 如果启用了异步加载并且URL不为空 + view.asyncNode(setting); // 异步加载节点 + } + return zTreeTools; // 返回zTree工具对象 + + var zt = $.fn.zTree, // 获取jQuery的zTree插件 + $$ = tools.$, // 从tools对象中获取$符号,通常用于简化代码书写 + consts = zt.consts; // 获取zTree插件中的常量定义 + })(jQuery); // 立即执行函数,传入jQuery对象作为参数 +/* + * JQuery zTree excheck + * v3.5.46 + * http://treejs.cn/ + * + * Copyright (c) 2010 Hunter.z + * + * Licensed same as jquery - MIT License + * http://www.opensource.org/licenses/mit-license.php + * + * Date: 2020-11-21 + */ + +(function ($) { + // 定义excheck的默认常量 + var _consts = { + event: { + // 触发检查事件的名称 + CHECK: "ztree_check" + }, + id: { + // 检查框的ID前缀 + CHECK: "_check" + }, + checkbox: { + // 复选框样式类型 + STYLE: "checkbox", + // 默认复选框类名 + DEFAULT: "chk", + // 禁用状态的复选框类名 + DISABLED: "disable", + // 未选中状态的复选框类名 + FALSE: "false", + // 选中状态的复选框类名 + TRUE: "true", + // 完全选中状态的复选框类名 + FULL: "full", + // 部分选中状态的复选框类名 + PART: "part", + // 焦点状态的复选框类名 + FOCUS: "focus" + }, + radio: { + // 单选按钮样式类型 + STYLE: "radio", + // 所有节点都参与选择的类型 + TYPE_ALL: "all", + // 同级别节点参与选择的类型 + TYPE_LEVEL: "level" + } + }, + //default setting of excheck + _setting = { + // 配置检查选项 + check: { + // 是否启用检查功能,默认为false + enable: false, + // 是否自动触发检查,默认为false + autoCheckTrigger: false, + // 设置复选框的样式,使用常量中的样式定义 + chkStyle: _consts.checkbox.STYLE, + // 是否不继承未选中状态,默认为false + nocheckInherit: false, + // 是否不继承禁用状态,默认为false + chkDisabledInherit: false, + // 设置单选按钮的类型,使用常量中定义的类型 + radioType: _consts.radio.TYPE_LEVEL, + // 设置复选框类型,"Y"和"N"都对应到"ps" + chkboxType: { + "Y": "ps", + "N": "ps" + } + }, + data: { + key: { + checked: "checked" // 定义节点是否被选中的键名 + } + }, + callback: { + beforeCheck: null, // 在节点选中前执行的回调函数 + onCheck: null // 在节点选中后执行的回调函数 + } + }, + // default root of excheck + _initRoot = function (setting) { + var r = data.getRoot(setting); // 获取树的根节点 + r.radioCheckedList = []; // 初始化根节点的单选列表为空数组 + }, + // default cache of excheck + _initCache = function (treeId) { + // 初始化缓存,当前未实现具体逻辑 + }, + // default bind event of excheck + _bindEvent = function (setting) { + var o = setting.treeObj, // 获取树对象 + c = consts.event; // 获取事件常量 + o.bind(c.CHECK, function (event, srcEvent, treeId, node) { + event.srcEvent = srcEvent; // 将源事件附加到事件对象上 + tools.apply(setting.callback.onCheck, [event, treeId, node]); // 调用选中后的回调函数 + }); + }, + _unbindEvent = function (setting) { + // 获取树对象 + var o = setting.treeObj, + // 获取事件常量 + c = consts.event; + // 解绑CHECK事件 + o.unbind(c.CHECK); + }, + //default event proxy of excheck + _eventProxy = function (e) { + // 获取事件目标元素 + var target = e.target, + // 获取设置信息 + setting = data.getSetting(e.data.treeId), + tId = "", node = null, + nodeEventType = "", treeEventType = "", + nodeEventCallback = null, treeEventCallback = null; + + // 如果事件类型是mouseover + if (tools.eqs(e.type, "mouseover")) { + // 如果启用了复选框,并且目标元素是span标签且具有CHECK属性 + if (setting.check.enable && tools.eqs(target.tagName, "span") && target.getAttribute("treeNode" + consts.id.CHECK) !== null) { + // 获取节点主DOM的ID + tId = tools.getNodeMainDom(target).id; + // 设置节点事件类型为mouseoverCheck + nodeEventType = "mouseoverCheck"; + } + } else if (tools.eqs(e.type, "mouseout")) { + // 如果事件类型是mouseout + // 如果启用了复选框,并且目标元素是span标签且具有CHECK属性 + if (setting.check.enable && tools.eqs(target.tagName, "span") && target.getAttribute("treeNode" + consts.id.CHECK) !== null) { + // 获取节点主DOM的ID + tId = tools.getNodeMainDom(target).id; + // 设置节点事件类型为mouseoutCheck + nodeEventType = "mouseoutCheck"; + } + } else if (tools.eqs(e.type, "click")) { + // 如果事件类型是click + // 如果启用了复选框,并且目标元素是span标签且具有CHECK属性 + if (setting.check.enable && tools.eqs(target.tagName, "span") && target.getAttribute("treeNode" + consts.id.CHECK) !== null) { + // 获取节点主DOM的ID + tId = tools.getNodeMainDom(target).id; + // 设置节点事件类型为checkNode + nodeEventType = "checkNode"; + } + } + // 检查tId数组的长度是否大于0 + if (tId.length > 0) { + // 从缓存中获取节点对象 + node = data.getNodeCache(setting, tId); + // 根据节点事件类型选择相应的回调函数 + switch (nodeEventType) { + // 如果事件类型是"checkNode",则使用_handler.onCheckNode作为回调函数 + case "checkNode": + nodeEventCallback = _handler.onCheckNode; + break; + // 如果事件类型是"mouseoverCheck",则使用_handler.onMouseoverCheck作为回调函数 + case "mouseoverCheck": + nodeEventCallback = _handler.onMouseoverCheck; + break; + // 如果事件类型是"mouseoutCheck",则使用_handler.onMouseoutCheck作为回调函数 + case "mouseoutCheck": + nodeEventCallback = _handler.onMouseoutCheck; + break; + } + } + var proxyResult = { + // 如果nodeEventType是"checkNode",则stop为true,否则为false + stop: nodeEventType === "checkNode", + // 当前节点对象 + node: node, + // 节点事件类型 + nodeEventType: nodeEventType, + // 节点事件回调函数 + nodeEventCallback: nodeEventCallback, + // 树事件类型 + treeEventType: treeEventType, + // 树事件回调函数 + treeEventCallback: treeEventCallback + }; + // 返回包含上述属性的对象 + return proxyResult + }, + //default init node of excheck + _initNode = function (setting, level, n, parentNode, isFirstNode, isLastNode, openFlag) { + // 如果节点不存在,直接返回 + if (!n) return; + + // 获取节点的选中状态 + var checked = data.nodeChecked(setting, n); + + // 保存节点的旧选中状态 + n.checkedOld = checked; + + // 如果节点的nocheck属性是字符串类型,将其转换为布尔值 + if (typeof n.nocheck == "string") n.nocheck = tools.eqs(n.nocheck, "true"); + + // 设置节点的nocheck属性,如果父节点存在且继承父节点的nocheck属性,则使用父节点的值 + n.nocheck = !!n.nocheck || (setting.check.nocheckInherit && parentNode && !!parentNode.nocheck); + + // 如果节点的chkDisabled属性是字符串类型,将其转换为布尔值 + if (typeof n.chkDisabled == "string") n.chkDisabled = tools.eqs(n.chkDisabled, "true"); + + // 设置节点的chkDisabled属性,如果父节点存在且继承父节点的chkDisabled属性,则使用父节点的值 + n.chkDisabled = !!n.chkDisabled || (setting.check.chkDisabledInherit && parentNode && !!parentNode.chkDisabled); + + // 如果节点的halfCheck属性是字符串类型,将其转换为布尔值 + if (typeof n.halfCheck == "string") n.halfCheck = tools.eqs(n.halfCheck, "true"); + + // 设置节点的halfCheck属性为布尔值 + n.halfCheck = !!n.halfCheck; + + // 初始化子节点的状态为-1(未定义) + n.check_Child_State = -1; + + // 初始化节点的焦点状态为false + n.check_Focus = false; + + // 定义一个方法来获取节点的选中状态 + n.getCheckStatus = function () { + return data.getCheckStatus(setting, n); + }; + } + + if (setting.check.chkStyle == consts.radio.STYLE && setting.check.radioType == consts.radio.TYPE_ALL && checked) { + // 获取根节点数据 + var r = data.getRoot(setting); + // 将当前节点添加到已选中的单选按钮列表中 + r.radioCheckedList.push(n); + } + //add dom for check + _beforeA = function (setting, node, html) { + // 检查是否启用了check功能 + if (setting.check.enable) { + // 调用makeChkFlag方法,为节点生成check标记 + data.makeChkFlag(setting, node); + // 将生成的HTML片段推入html数组中 + html.push(""); + } + }, + //update zTreeObj, add method of check + _zTreeTools = function (setting, zTreeTools) { + // 定义一个方法,用于检查节点的状态 + zTreeTools.checkNode = function (node, checked, checkTypeFlag, callbackFlag) { + // 获取当前节点的选中状态 + var nodeChecked = data.nodeChecked(setting, node); + // 如果节点被禁用,则直接返回 + if (node.chkDisabled === true) return; + // 如果checked参数不是布尔值,则取反当前节点的选中状态 + if (checked !== true && checked !== false) { + checked = !nodeChecked; + } + // 确保callbackFlag为布尔值 + callbackFlag = !!callbackFlag; + + // 如果节点的选中状态没有变化且不需要回调,则直接返回 + if (nodeChecked === checked && !checkTypeFlag) { + return; + // 如果需要回调且beforeCheck回调函数返回false,则直接返回 + } else if (callbackFlag && tools.apply(this.setting.callback.beforeCheck, [this.setting.treeId, node], true) == false) { + return; + } + // 检查用户是否有权限进行操作,并且是否启用了节点选中功能,以及节点是否没有被禁用选中 + if (tools.uCanDo(this.setting) && this.setting.check.enable && node.nocheck !== true) { + // 更新节点的选中状态 + data.nodeChecked(setting, node, checked); + // 获取节点对应的复选框对象 + var checkObj = $$(node, consts.id.CHECK, this.setting); + // 如果需要检查类型标志或节点样式为单选,则更新节点关系 + if (checkTypeFlag || this.setting.check.chkStyle === consts.radio.STYLE) view.checkNodeRelation(this.setting, node); + // 设置复选框的样式 + view.setChkClass(this.setting, checkObj, node); + // 修复父节点的复选框样式 + view.repairParentChkClassWithSelf(this.setting, node); + // 如果需要回调,则触发CHECK事件 + if (callbackFlag) { + this.setting.treeObj.trigger(consts.event.CHECK, [null, this.setting.treeId, node]); + } + } + } + + zTreeTools.checkAllNodes = function (checked) { + // 调用view对象的repairAllChk方法,修复所有节点的选中状态 + view.repairAllChk(this.setting, !!checked); + } + + zTreeTools.getCheckedNodes = function (checked) { + // 如果checked参数不为false,则将其转换为布尔值true + checked = (checked !== false); + // 获取根节点的所有子节点 + var children = data.nodeChildren(setting, data.getRoot(this.setting)); + // 返回树中所有被选中的节点 + return data.getTreeCheckedNodes(this.setting, children, checked); + } + + zTreeTools.getChangeCheckedNodes = function () { + // 获取根节点的所有子节点 + var children = data.nodeChildren(setting, data.getRoot(this.setting)); + // 返回树中所有改变选中状态的节点 + return data.getTreeChangeCheckedNodes(this.setting, children); + } + + zTreeTools.setChkDisabled = function (node, disabled, inheritParent, inheritChildren) { + // 将disabled参数转换为布尔值 + disabled = !!disabled; + // 将inheritParent参数转换为布尔值 + inheritParent = !!inheritParent; + // 将inheritChildren参数转换为布尔值 + inheritChildren = !!inheritChildren; + // 修复子节点的禁用状态 + view.repairSonChkDisabled(this.setting, node, disabled, inheritChildren); + // 修复父节点的禁用状态 + view.repairParentChkDisabled(this.setting, node.getParentNode(), disabled, inheritParent); + } + + var _updateNode = zTreeTools.updateNode; + zTreeTools.updateNode = function (node, checkTypeFlag) { + // 如果存在原始的updateNode方法,则调用它 + if (_updateNode) _updateNode.apply(zTreeTools, arguments); + // 如果节点不存在或检查功能未启用,则直接返回 + if (!node || !this.setting.check.enable) return; + // 获取节点对应的jQuery对象 + var nObj = $$(node, this.setting); + // 如果节点存在且用户有权限操作 + if (nObj.get(0) && tools.uCanDo(this.setting)) { + // 获取节点的复选框对象 + var checkObj = $$(node, consts.id.CHECK, this.setting); + // 如果checkTypeFlag为true或复选框样式为单选按钮 + if (checkTypeFlag == true || this.setting.check.chkStyle === consts.radio.STYLE) + // 修复节点的选中关系 + view.checkNodeRelation(this.setting, node); + // 设置复选框的CSS类 + view.setChkClass(this.setting, checkObj, node); + // 修复父节点的CSS类 + view.repairParentChkClassWithSelf(this.setting, node); + } + } + }, + //method of operate data + _data = { + // 获取选中的单选框列表 + getRadioCheckedList: function (setting) { + // 获取根节点的单选框选中列表 + var checkedList = data.getRoot(setting).radioCheckedList; + // 遍历选中列表,检查每个节点是否在缓存中存在 + for (var i = 0, j = checkedList.length; i < j; i++) { + // 如果节点不在缓存中,则从选中列表中移除 + if (!data.getNodeCache(setting, checkedList[i].tId)) { + checkedList.splice(i, 1); + i--; + j--; + } + } + // 返回处理后的选中列表 + return checkedList; + }, + + // 获取节点的选中状态 + getCheckStatus: function (setting, node) { + // 如果未启用选中功能或节点不可选中,则返回null + if (!setting.check.enable || node.nocheck || node.chkDisabled) return null; + // 获取节点是否被选中的状态 + var checked = data.nodeChecked(setting, node), + r = { + checked: checked, + // 根据不同的选中样式和子节点状态计算半选状态 + half: node.halfCheck ? node.halfCheck : (setting.check.chkStyle == consts.radio.STYLE ? (node.check_Child_State === 2) : (checked ? (node.check_Child_State > -1 && node.check_Child_State < 2) : (node.check_Child_State > 0))) + }; + // 返回选中状态对象 + return r; + }, + + // 获取树形结构中所有选中的节点 + getTreeCheckedNodes: function (setting, nodes, checked, results) { + // 如果节点列表为空,则返回空数组 + if (!nodes) return []; + // 判断是否只允许选择一个节点(单选模式) + var onlyOne = (checked && setting.check.chkStyle == consts.radio.STYLE && setting.check.radioType == consts.radio.TYPE_ALL); + // 初始化结果数组 + results = !results ? [] : results; + // 遍历节点列表 + for (var i = 0, l = nodes.length; i < l; i++) { + var node = nodes[i]; + // 获取当前节点的子节点 + var children = data.nodeChildren(setting, node); + // 获取当前节点是否被选中 + var nodeChecked = data.nodeChecked(setting, node); + // 如果节点可选中且未禁用,并且其选中状态与目标状态一致,则将其加入结果数组 + if (node.nocheck !== true && node.chkDisabled !== true && nodeChecked == checked) { + results.push(node); + // 如果只允许选择一个节点,则找到后立即退出循环 + if (onlyOne) { + break; + } + } + // 递归处理子节点 + data.getTreeCheckedNodes(setting, children, checked, results); + // 如果只允许选择一个节点,且已找到符合条件的节点,则退出循环 + if (onlyOne && results.length > 0) { + break; + } + } + // 返回结果数组 + return results; + }, + getTreeChangeCheckedNodes: function (setting, nodes, results) { + // 如果节点为空,返回空数组 + if (!nodes) return []; + // 如果结果未定义,初始化为空数组 + results = !results ? [] : results; + // 遍历所有节点 + for (var i = 0, l = nodes.length; i < l; i++) { + // 获取当前节点 + var node = nodes[i]; + // 获取当前节点的子节点 + var children = data.nodeChildren(setting, node); + // 检查当前节点是否被选中 + var nodeChecked = data.nodeChecked(setting, node); + // 如果节点没有被禁用且选中状态发生变化,则将节点加入结果数组 + if (node.nocheck !== true && node.chkDisabled !== true && nodeChecked != node.checkedOld) { + results.push(node); + } + // 递归处理子节点 + data.getTreeChangeCheckedNodes(setting, children, results); + } + // 返回结果数组 + return results; + }, + /** + * 生成节点的检查标志。 + * @param {Object} setting - 配置对象,包含树形结构的相关设置。 + * @param {Object} node - 当前处理的节点对象。 + */ + makeChkFlag: function (setting, node) { + // 如果节点不存在,直接返回 + if (!node) return; + + // 初始化检查标志为-1 + var chkFlag = -1; + + // 获取节点的子节点列表 + var children = data.nodeChildren(setting, node); + + // 如果存在子节点 + if (children) { + // 遍历所有子节点 + for (var i = 0, l = children.length; i < l; i++) { + var cNode = children[i]; // 当前子节点 + var nodeChecked = data.nodeChecked(setting, cNode); // 判断子节点是否被选中 + var tmp = -1; // 临时变量,用于存储子节点的状态 + + // 如果检查样式是单选框 + if (setting.check.chkStyle == consts.radio.STYLE) { + // 如果子节点不可检查或禁用,则使用其子状态 + if (cNode.nocheck === true || cNode.chkDisabled === true) { + tmp = cNode.check_Child_State; + } else if (cNode.halfCheck === true) { + // 如果子节点半选,则状态为2 + tmp = 2; + } else if (nodeChecked) { + // 如果子节点被选中,则状态为2 + tmp = 2; + } else { + // 根据子节点的子状态决定状态 + tmp = cNode.check_Child_State > 0 ? 2 : 0; + } + // 如果状态为2,更新检查标志并退出循环 + if (tmp == 2) { + chkFlag = 2; + break; + } else if (tmp == 0) { + // 如果状态为0,更新检查标志为0 + chkFlag = 0; + } + } else if (setting.check.chkStyle == consts.checkbox.STYLE) { + // 如果检查样式是复选框 + if (cNode.nocheck === true || cNode.chkDisabled === true) { + tmp = cNode.check_Child_State; + } else if (cNode.halfCheck === true) { + // 如果子节点半选,则状态为1 + tmp = 1; + } else if (nodeChecked) { + // 根据子节点的子状态决定状态 + tmp = (cNode.check_Child_State === -1 || cNode.check_Child_State === 2) ? 2 : 1; + } else { + // 根据子节点的子状态决定状态 + tmp = (cNode.check_Child_State > 0) ? 1 : 0; + } + // 如果状态为1,更新检查标志并退出循环 + if (tmp === 1) { + chkFlag = 1; + break; + } else if (tmp === 2 && chkFlag > -1 && i > 0 && tmp !== chkFlag) { + // 如果状态为2且之前有其他状态,更新检查标志为1并退出循环 + chkFlag = 1; + break; + } else if (chkFlag === 2 && tmp > -1 && tmp < 2) { + // 如果之前状态为2且当前状态在0和2之间,更新检查标志为1并退出循环 + chkFlag = 1; + break; + } else if (tmp > -1) { + // 更新检查标志为当前状态 + chkFlag = tmp; + } + } + } + } + // 将计算得到的检查标志赋值给节点的check_Child_State属性 + node.check_Child_State = chkFlag; + } + //method of event proxy + _event = {}, + // 事件处理程序的方法集合 + _handler = { + /** + * 处理节点选中事件的函数 + * @param {Object} event - 事件对象 + * @param {Object} node - 被操作的节点对象 + * @returns {boolean} - 返回是否成功处理事件 + */ + onCheckNode: function (event, node) { + // 如果节点被禁用,则直接返回false + if (node.chkDisabled === true) return false; + // 获取当前树的设置信息 + var setting = data.getSetting(event.data.treeId); + // 调用beforeCheck回调函数,如果返回false,则终止操作 + if (tools.apply(setting.callback.beforeCheck, [setting.treeId, node], true) == false) return true; + // 获取节点当前的选中状态 + var nodeChecked = data.nodeChecked(setting, node); + // 切换节点的选中状态 + data.nodeChecked(setting, node, !nodeChecked); + // 更新节点关系 + view.checkNodeRelation(setting, node); + // 获取节点对应的复选框元素 + var checkObj = $$(node, consts.id.CHECK, setting); + // 设置复选框的样式 + view.setChkClass(setting, checkObj, node); + // 修复父节点的复选框样式 + view.repairParentChkClassWithSelf(setting, node); + // 触发CHECK事件 + setting.treeObj.trigger(consts.event.CHECK, [event, setting.treeId, node]); + return true; + }, + /** + * 处理鼠标悬停在复选框上的事件 + * @param {Object} event - 事件对象 + * @param {Object} node - 被操作的节点对象 + * @returns {boolean} - 返回是否成功处理事件 + */ + onMouseoverCheck: function (event, node) { + // 如果节点被禁用,则直接返回false + if (node.chkDisabled === true) return false; + // 获取当前树的设置信息和复选框元素 + var setting = data.getSetting(event.data.treeId), + checkObj = $$(node, consts.id.CHECK, setting); + // 设置节点的check_Focus属性为true + node.check_Focus = true; + // 设置复选框的样式 + view.setChkClass(setting, checkObj, node); + return true; + }, + /** + * 处理鼠标移出复选框的事件 + * @param {Object} event - 事件对象 + * @param {Object} node - 被操作的节点对象 + * @returns {boolean} - 返回是否成功处理事件 + */ + onMouseoutCheck: function (event, node) { + // 如果节点被禁用,则直接返回false + if (node.chkDisabled === true) return false; + // 获取当前树的设置信息和复选框元素 + var setting = data.getSetting(event.data.treeId), + checkObj = $$(node, consts.id.CHECK, setting); + // 设置节点的check_Focus属性为false + node.check_Focus = false; + // 设置复选框的样式 + view.setChkClass(setting, checkObj, node); + return true; + } + }, + //method of tools for zTree + _tools = {}, + // 操作ztree DOM的方法 + _view = { + /** + * 检查节点关系,确保单选模式下只有一个节点被选中 + * @param {Object} setting - ztree的配置对象 + * @param {Object} node - 当前操作的节点对象 + */ + checkNodeRelation: function (setting, node) { + var pNode, i, l, + r = consts.radio; // 获取常量中的单选模式配置 + var nodeChecked = data.nodeChecked(setting, node); // 判断当前节点是否被选中 + if (setting.check.chkStyle == r.STYLE) { // 如果配置为单选模式 + var checkedList = data.getRadioCheckedList(setting); // 获取已选中的单选节点列表 + if (nodeChecked) { // 如果当前节点被选中 + if (setting.check.radioType == r.TYPE_ALL) { // 如果单选类型为全选 + for (i = checkedList.length - 1; i >= 0; i--) { // 遍历已选中的单选节点列表 + pNode = checkedList[i]; // 获取已选中的节点 + var pNodeChecked = data.nodeChecked(setting, pNode); // 判断已选中的节点是否被选中 + if (pNodeChecked && pNode != node) { // 如果已选中的节点被选中且不是当前节点 + data.nodeChecked(setting, pNode, false); // 取消已选中节点的选中状态 + checkedList.splice(i, 1); // 从已选中列表中移除该节点 + + view.setChkClass(setting, $$(pNode, consts.id.CHECK, setting), pNode); // 更新节点的样式 + if (pNode.parentTId != node.parentTId) { // 如果父节点不同 + view.repairParentChkClassWithSelf(setting, pNode); // 修复父节点的样式 + } + } + } + // 将节点添加到已选中列表中 + checkedList.push(node); + } else { + // 获取父节点,如果当前节点有父节点则获取其父节点,否则获取根节点 + var parentNode = (node.parentTId) ? node.getParentNode() : data.getRoot(setting); + // 获取父节点的所有子节点 + var children = data.nodeChildren(setting, parentNode); + // 遍历所有子节点 + for (i = 0, l = children.length; i < l; i++) { + pNode = children[i]; + // 检查子节点是否被选中 + var pNodeChecked = data.nodeChecked(setting, pNode); + // 如果子节点被选中且不是当前节点,则取消其选中状态并更新视图 + if (pNodeChecked && pNode != node) { + data.nodeChecked(setting, pNode, false); + view.setChkClass(setting, $$(pNode, consts.id.CHECK, setting), pNode); + } + } + } else if (setting.check.radioType == r.TYPE_ALL) { + // 如果设置为单选类型,则从已选中列表中移除当前节点 + for (i = 0, l = checkedList.length; i < l; i++) { + if (node == checkedList[i]) { + checkedList.splice(i, 1); + break; + } + } + } + + } else { + // 获取当前节点的子节点 + var children = data.nodeChildren(setting, node); + + // 如果当前节点被选中,并且没有子节点或子节点为空,或者设置中允许部分选中状态 + if (nodeChecked && (!children || children.length == 0 || setting.check.chkboxType.Y.indexOf("s") > -1)) { + // 设置子节点的复选框为选中状态 + view.setSonNodeCheckBox(setting, node, true); + } + + // 如果当前节点未被选中,并且没有子节点或子节点为空,或者设置中允许部分未选中状态 + if (!nodeChecked && (!children || children.length == 0 || setting.check.chkboxType.N.indexOf("s") > -1)) { + // 设置子节点的复选框为未选中状态 + view.setSonNodeCheckBox(setting, node, false); + } + + // 如果当前节点被选中,并且设置中允许父节点选中状态 + if (nodeChecked && setting.check.chkboxType.Y.indexOf("p") > -1) { + // 设置父节点的复选框为选中状态 + view.setParentNodeCheckBox(setting, node, true); + } + + // 如果当前节点未被选中,并且设置中允许父节点未选中状态 + if (!nodeChecked && setting.check.chkboxType.N.indexOf("p") > -1) { + // 设置父节点的复选框为未选中状态 + view.setParentNodeCheckBox(setting, node, false); + } + } + }, + makeChkClass: function (setting, node) { + // 获取复选框和单选按钮的常量 + var c = consts.checkbox, r = consts.radio, + fullStyle = ""; + // 检查节点是否被选中 + var nodeChecked = data.nodeChecked(setting, node); + // 如果节点被禁用,设置样式为禁用状态 + if (node.chkDisabled === true) { + fullStyle = c.DISABLED; + // 如果节点处于半选状态,设置样式为部分选中 + } else if (node.halfCheck) { + fullStyle = c.PART; + // 如果设置为单选按钮样式,根据子节点状态设置样式 + } else if (setting.check.chkStyle == r.STYLE) { + fullStyle = (node.check_Child_State < 1) ? c.FULL : c.PART; + // 根据节点是否选中和子节点状态设置样式 + } else { + fullStyle = nodeChecked ? ((node.check_Child_State === 2 || node.check_Child_State === -1) ? c.FULL : c.PART) : ((node.check_Child_State < 1) ? c.FULL : c.PART); + } + // 生成复选框的类名 + var chkName = setting.check.chkStyle + "_" + (nodeChecked ? c.TRUE : c.FALSE) + "_" + fullStyle; + // 如果节点有焦点且未被禁用,添加焦点样式 + chkName = (node.check_Focus && node.chkDisabled !== true) ? chkName + "_" + c.FOCUS : chkName; + // 返回完整的类名字符串 + return consts.className.BUTTON + " " + c.DEFAULT + " " + chkName; + }, + /** + * 修复所有复选框的状态 + * @param {Object} setting - 配置对象 + * @param {Boolean} checked - 是否选中 + */ + repairAllChk: function (setting, checked) { + // 如果启用了复选框并且样式为复选框样式 + if (setting.check.enable && setting.check.chkStyle === consts.checkbox.STYLE) { + // 获取根节点 + var root = data.getRoot(setting); + // 获取根节点的所有子节点 + var children = data.nodeChildren(setting, root); + // 遍历所有子节点 + for (var i = 0, l = children.length; i < l; i++) { + var node = children[i]; + // 如果节点没有被禁用且没有禁止选中 + if (node.nocheck !== true && node.chkDisabled !== true) { + // 设置节点的选中状态 + data.nodeChecked(setting, node, checked); + } + // 更新子节点的复选框状态 + view.setSonNodeCheckBox(setting, node, checked); + } + } + }, + /** + * 修复单个节点的复选框样式 + * @param {Object} setting - 配置对象 + * @param {Object} node - 节点对象 + */ + repairChkClass: function (setting, node) { + // 如果节点不存在,直接返回 + if (!node) return; + // 标记节点的复选框状态 + data.makeChkFlag(setting, node); + // 如果节点没有被禁止选中 + if (node.nocheck !== true) { + // 获取节点的复选框对象 + var checkObj = $$(node, consts.id.CHECK, setting); + // 设置复选框的样式 + view.setChkClass(setting, checkObj, node); + } + }, + repairParentChkClass: function (setting, node) { + // 如果节点不存在或没有父节点ID,则直接返回 + if (!node || !node.parentTId) return; + // 获取父节点 + var pNode = node.getParentNode(); + // 修复父节点的复选框样式 + view.repairChkClass(setting, pNode); + // 递归修复父节点的父节点的复选框样式 + view.repairParentChkClass(setting, pNode); + }, + + repairParentChkClassWithSelf: function (setting, node) { + // 如果节点不存在,则直接返回 + if (!node) return; + // 获取子节点列表 + var children = data.nodeChildren(setting, node); + // 如果存在子节点,则修复第一个子节点的父节点复选框样式 + if (children && children.length > 0) { + view.repairParentChkClass(setting, children[0]); + } else { + // 否则修复当前节点的父节点复选框样式 + view.repairParentChkClass(setting, node); + } + }, + + repairSonChkDisabled: function (setting, node, chkDisabled, inherit) { + // 如果节点不存在,则直接返回 + if (!node) return; + // 如果节点的chkDisabled属性与传入值不同,则更新该属性 + if (node.chkDisabled != chkDisabled) { + node.chkDisabled = chkDisabled; + } + // 修复当前节点的复选框样式 + view.repairChkClass(setting, node); + // 获取子节点列表 + var children = data.nodeChildren(setting, node); + // 如果存在子节点且需要继承,则递归修复所有子节点的chkDisabled属性和复选框样式 + if (children && inherit) { + for (var i = 0, l = children.length; i < l; i++) { + var sNode = children[i]; + view.repairSonChkDisabled(setting, sNode, chkDisabled, inherit); + } + } + }, + + repairParentChkDisabled: function (setting, node, chkDisabled, inherit) { + // 如果节点不存在,则直接返回 + if (!node) return; + // 如果节点的chkDisabled属性与传入值不同且需要继承,则更新该属性 + if (node.chkDisabled != chkDisabled && inherit) { + node.chkDisabled = chkDisabled; + } + // 修复当前节点的复选框样式 + view.repairChkClass(setting, node); + // 递归修复父节点的chkDisabled属性和复选框样式 + view.repairParentChkDisabled(setting, node.getParentNode(), chkDisabled, inherit); + }, + setChkClass: function (setting, obj, node) { + // 如果对象不存在,直接返回 + if (!obj) return; + // 如果节点的nocheck属性为true,隐藏复选框 + if (node.nocheck === true) { + obj.hide(); + } else { + // 否则显示复选框 + obj.show(); + } + // 设置复选框的class属性 + obj.attr('class', view.makeChkClass(setting, node)); + }, + setParentNodeCheckBox: function (setting, node, value, srcNode) { + // 获取当前节点的复选框对象 + var checkObj = $$(node, consts.id.CHECK, setting); + // 如果没有传入srcNode,则将当前节点赋值给srcNode + if (!srcNode) srcNode = node; + // 更新节点的检查状态标志 + data.makeChkFlag(setting, node); + // 如果节点没有禁用检查且未设置为不检查 + if (node.nocheck !== true && node.chkDisabled !== true) { + // 设置节点的选中状态 + data.nodeChecked(setting, node, value); + // 更新复选框的样式和类名 + view.setChkClass(setting, checkObj, node); + // 如果自动检查触发开启且当前节点不是源节点,触发CHECK事件 + if (setting.check.autoCheckTrigger && node != srcNode) { + setting.treeObj.trigger(consts.event.CHECK, [null, setting.treeId, node]); + } + } + // 如果节点有父节点 + if (node.parentTId) { + var pSign = true; + // 如果值为false + if (!value) { + // 获取父节点的所有子节点 + var pNodes = data.nodeChildren(setting, node.getParentNode()); + // 遍历所有子节点 + for (var i = 0, l = pNodes.length; i < l; i++) { + var pNode = pNodes[i]; + // 获取子节点的选中状态 + var nodeChecked = data.nodeChecked(setting, pNode); + // 如果子节点未被禁用且选中,或者子节点被禁用但有子节点被选中,则pSign设为false并跳出循环 + if ((pNode.nocheck !== true && pNode.chkDisabled !== true && nodeChecked) + || ((pNode.nocheck === true || pNode.chkDisabled === true) && pNode.check_Child_State > 0)) { + pSign = false; + break; + } + } + } + if (pSign) { + // 如果 pSign 为真,设置父节点的复选框状态 + view.setParentNodeCheckBox(setting, node.getParentNode(), value, srcNode); + } + }, + setSonNodeCheckBox: function (setting, node, value, srcNode) { + // 如果节点不存在,直接返回 + if (!node) return; + // 获取当前节点的复选框对象 + var checkObj = $$(node, consts.id.CHECK, setting); + // 如果源节点未定义,则将当前节点设为源节点 + if (!srcNode) srcNode = node; + + // 初始化 hasDisable 标志位,用于记录子节点是否有禁用的复选框 + var hasDisable = false; + // 获取当前节点的子节点列表 + var children = data.nodeChildren(setting, node); + if (children) { + // 遍历所有子节点 + for (var i = 0, l = children.length; i < l; i++) { + var sNode = children[i]; + // 递归设置子节点的复选框状态 + view.setSonNodeCheckBox(setting, sNode, value, srcNode); + // 如果子节点被禁用,则设置 hasDisable 为 true + if (sNode.chkDisabled === true) hasDisable = true; + } + } + + // 如果当前节点不是根节点且未被禁用 + if (node != data.getRoot(setting) && node.chkDisabled !== true) { + // 如果存在禁用的子节点且当前节点未被禁用,更新当前节点的复选框状态 + if (hasDisable && node.nocheck !== true) { + data.makeChkFlag(setting, node); + } + // 如果当前节点未被禁用且未设置为不检查 + if (node.nocheck !== true && node.chkDisabled !== true) { + // 设置当前节点的选中状态 + data.nodeChecked(setting, node, value); + // 根据子节点的状态更新当前节点的子节点状态 + if (!hasDisable) node.check_Child_State = (children && children.length > 0) ? (value ? 2 : 0) : -1; + } else { + // 如果当前节点被禁用或设置为不检查,则将其子节点状态设为 -1 + node.check_Child_State = -1; + } + // 更新当前节点的复选框样式 + view.setChkClass(setting, checkObj, node); + // 如果自动触发检查事件,并且当前节点不是源节点且未被禁用或设置为不检查,则触发检查事件 + if (setting.check.autoCheckTrigger && node != srcNode && node.nocheck !== true && node.chkDisabled !== true) { + setting.treeObj.trigger(consts.event.CHECK, [null, setting.treeId, node]); + } + } + + } + }, + + _z = { + tools: _tools, + view: _view, + event: _event, + data: _data + }; + // 将常量对象扩展到jQuery的zTree插件中 + $.extend(true, $.fn.zTree.consts, _consts); + // 将工具对象扩展到jQuery的zTree插件中 + $.extend(true, $.fn.zTree._z, _z); + + var zt = $.fn.zTree, + tools = zt._z.tools, + consts = zt.consts, + view = zt._z.view, + data = zt._z.data, + event = zt._z.event, + $$ = tools.$; + + /** + * 设置或获取节点的选中状态 + * @param {Object} setting - 配置对象 + * @param {Object} node - 节点对象 + * @param {boolean|string} newChecked - 新的选中状态,可以是布尔值或字符串 + * @returns {boolean} - 返回节点的最终选中状态 + */ + data.nodeChecked = function (setting, node, newChecked) { + // 如果节点不存在,返回false + if (!node) { + return false; + } + var key = setting.data.key.checked; + // 如果newChecked被定义了 + if (typeof newChecked !== 'undefined') { + // 如果newChecked是字符串类型,将其转换为布尔值 + if (typeof newChecked === "string") { + newChecked = tools.eqs(newChecked, "true"); + } + // 将newChecked转换为布尔值并赋值给节点的key属性 + newChecked = !!newChecked; + node[key] = newChecked; + } else if (typeof node[key] == "string"){ + // 如果节点的key属性是字符串类型,将其转换为布尔值 + node[key] = tools.eqs(node[key], "true"); + } else { + // 否则,将节点的key属性转换为布尔值 + node[key] = !!node[key]; + } + // 返回节点的最终选中状态 + return node[key]; + }; + + // 调用exSetting方法,传入_setting参数进行设置 + data.exSetting(_setting); + + // 添加初始化绑定事件,传入_bindEvent作为回调函数 + data.addInitBind(_bindEvent); + + // 添加初始化解绑事件,传入_unbindEvent作为回调函数 + data.addInitUnBind(_unbindEvent); + + // 添加初始化缓存,传入_initCache作为回调函数 + data.addInitCache(_initCache); + + // 添加初始化节点,传入_initNode作为回调函数 + data.addInitNode(_initNode); + + // 添加初始化代理,传入_eventProxy作为回调函数,并设置第二个参数为true + data.addInitProxy(_eventProxy, true); + + // 添加初始化根节点,传入_initRoot作为回调函数 + data.addInitRoot(_initRoot); + + // 添加在A操作之前的回调函数,传入_beforeA作为回调函数 + data.addBeforeA(_beforeA); + + // 添加ZTree工具,传入_zTreeTools作为回调函数 + data.addZTreeTools(_zTreeTools); + var _createNodes = view.createNodes; + view.createNodes = function (setting, level, nodes, parentNode, index) { + // 如果存在原始的 createNodes 方法,则调用它 + if (_createNodes) _createNodes.apply(view, arguments); + // 如果没有节点需要创建,直接返回 + if (!nodes) return; + // 修复父节点的选中状态类 + view.repairParentChkClassWithSelf(setting, parentNode); + } + + var _removeNode = view.removeNode; + view.removeNode = function (setting, node) { + // 获取要删除节点的父节点 + var parentNode = node.getParentNode(); + // 如果存在原始的 removeNode 方法,则调用它 + if (_removeNode) _removeNode.apply(view, arguments); + // 如果节点或其父节点不存在,直接返回 + if (!node || !parentNode) return; + // 修复父节点的选中状态类 + view.repairChkClass(setting, parentNode); + // 修复父节点的选中状态类(与上一个修复方法可能不同) + view.repairParentChkClass(setting, parentNode); + } + + var _appendNodes = view.appendNodes; // 保存原始的appendNodes方法 + view.appendNodes = function (setting, level, nodes, parentNode, index, initFlag, openFlag) { + // 定义新的appendNodes方法,接收多个参数 + var html = ""; // 初始化html变量为空字符串 + if (_appendNodes) { + // 如果存在原始的appendNodes方法,则调用它并传递所有参数 + html = _appendNodes.apply(view, arguments); + } + if (parentNode) { + // 如果parentNode存在,则调用makeChkFlag方法 + data.makeChkFlag(setting, parentNode); + } + return html; // 返回生成的html内容 + } + })(jQuery); // 立即执行函数,传入jQuery对象 +/* + * JQuery zTree exedit + * v3.5.46 + * http://treejs.cn/ + * + * Copyright (c) 2010 Hunter.z + * + * Licensed same as jquery - MIT License + * http://www.opensource.org/licenses/mit-license.php + * + * Date: 2020-11-21 + */ + +(function ($) { + // 定义exedit的默认常量 + var _consts = { + event: { + // 拖拽事件 + DRAG: "ztree_drag", + // 放置事件 + DROP: "ztree_drop", + // 重命名事件 + RENAME: "ztree_rename", + // 拖动移动事件 + DRAGMOVE: "ztree_dragmove" + }, + id: { + // 编辑状态ID + EDIT: "_edit", + // 输入框ID + INPUT: "_input", + // 删除按钮ID + REMOVE: "_remove" + }, + move: { + // 内部移动类型 + TYPE_INNER: "inner", + // 上一个节点移动类型 + TYPE_PREV: "prev", + // 下一个节点移动类型 + TYPE_NEXT: "next" + }, + node: { + // 当前选中节点编辑状态 + CURSELECTED_EDIT: "curSelectedNode_Edit", + // 临时目标树ID + TMPTARGET_TREE: "tmpTargetzTree", + // 临时目标节点ID + TMPTARGET_NODE: "tmpTargetNode" + } + }, + // 定义exedit的默认设置 + _setting = { + edit: { + // 是否启用编辑功能 + enable: false, + // 是否在编辑时全选节点名称 + editNameSelectAll: false, + // 是否显示删除按钮 + showRemoveBtn: true, + // 是否显示重命名按钮 + showRenameBtn: true, + // 删除按钮的标题 + removeTitle: "remove", + // 重命名按钮的标题 + renameTitle: "rename", + drag: { + // 自动展开触发器 + autoExpandTrigger: false, + // 是否允许复制操作 + isCopy: true, + // 是否允许移动操作 + isMove: true, + // 是否允许移动到前一个节点 + prev: true, + // 是否允许移动到下一个节点 + next: true, + // 是否允许移动到内部节点 + inner: true, + // 最小移动尺寸 + minMoveSize: 5, + // 最大边界值 + borderMax: 10, + // 最小边界值 + borderMin: -5, + // 最大显示节点数 + maxShowNodeNum: 5, + // 自动打开时间(毫秒) + autoOpenTime: 500 + } + }, + view: { + // 添加悬停DOM元素的回调函数,初始值为null + addHoverDom: null, + // 移除悬停DOM元素的回调函数,初始值为null + removeHoverDom: null + }, + callback: { + // 拖动节点前的回调函数,初始值为null + beforeDrag: null, + // 打开拖动节点前的回调函数,初始值为null + beforeDragOpen: null, + // 放置节点前的回调函数,初始值为null + beforeDrop: null, + // 编辑节点名称前的回调函数,初始值为null + beforeEditName: null, + // 重命名节点前的回调函数,初始值为null + beforeRename: null, + // 拖动节点时的回调函数,初始值为null + onDrag: null, + // 拖动节点移动时的回调函数,初始值为null + onDragMove: null, + // 放置节点后的回调函数,初始值为null + onDrop: null, + // 重命名节点后的回调函数,初始值为null + onRename: null + } + }, + //default root of exedit + _initRoot = function (setting) { + // 获取根节点对象 + var r = data.getRoot(setting), rs = data.getRoots(); + // 当前编辑的节点设为空 + r.curEditNode = null; + // 当前编辑的输入框设为空 + r.curEditInput = null; + // 当前悬停的节点设为空 + r.curHoverNode = null; + // 拖拽标志设为0 + r.dragFlag = 0; + // 初始化拖拽显示前的节点数组 + r.dragNodeShowBefore = []; + // 初始化拖拽遮罩列表 + r.dragMaskList = new Array(); + // 设置是否显示悬停的DOM元素 + rs.showHoverDom = true; + }, + //default cache of exedit + _initCache = function (treeId) { + // 初始化缓存,暂未实现具体逻辑 + }, + //default bind event of exedit + _bindEvent = function (setting) { + // 获取树对象 + var o = setting.treeObj; + // 获取事件常量 + var c = consts.event; + o.bind(c.RENAME, function (event, treeId, treeNode, isCancel) { + // 绑定重命名事件,调用回调函数 + tools.apply(setting.callback.onRename, [event, treeId, treeNode, isCancel]); + }); + + o.bind(c.DRAG, function (event, srcEvent, treeId, treeNodes) { + // 绑定拖拽事件,调用回调函数 + tools.apply(setting.callback.onDrag, [srcEvent, treeId, treeNodes]); + }); + + o.bind(c.DRAGMOVE, function (event, srcEvent, treeId, treeNodes) { + // 绑定拖拽移动事件,调用回调函数 + tools.apply(setting.callback.onDragMove, [srcEvent, treeId, treeNodes]); + }); + + o.bind(c.DROP, function (event, srcEvent, treeId, treeNodes, targetNode, moveType, isCopy) { + // 调用设置中的回调函数,处理节点拖放事件 + tools.apply(setting.callback.onDrop, [srcEvent, treeId, treeNodes, targetNode, moveType, isCopy]); + }); + }, + _unbindEvent = function (setting) { + // 获取树对象和常量 + var o = setting.treeObj; + var c = consts.event; + // 解绑重命名事件 + o.unbind(c.RENAME); + // 解绑拖拽开始事件 + o.unbind(c.DRAG); + // 解绑拖拽移动事件 + o.unbind(c.DRAGMOVE); + // 解绑拖拽放下事件 + o.unbind(c.DROP); + }, + //default event proxy of exedit + _eventProxy = function (e) { + // 获取事件目标、相关目标、设置等变量 + var target = e.target, + setting = data.getSetting(e.data.treeId), + relatedTarget = e.relatedTarget, + tId = "", node = null, + nodeEventType = "", treeEventType = "", + nodeEventCallback = null, treeEventCallback = null, + tmp = null; + + if (tools.eqs(e.type, "mouseover")) { + // 处理鼠标悬停事件,获取目标节点的ID和事件类型 + tmp = tools.getMDom(setting, target, [{tagName: "a", attrName: "treeNode" + consts.id.A}]); + if (tmp) { + tId = tools.getNodeMainDom(tmp).id; + nodeEventType = "hoverOverNode"; + } + } else if (tools.eqs(e.type, "mouseout")) { + // 处理鼠标移出事件,判断是否移出了节点范围 + tmp = tools.getMDom(setting, relatedTarget, [{tagName: "a", attrName: "treeNode" + consts.id.A}]); + if (!tmp) { + tId = "remove"; + nodeEventType = "hoverOutNode"; + } + } else if (tools.eqs(e.type, "mousedown")) { + // 处理鼠标按下事件,获取目标节点的ID和事件类型 + tmp = tools.getMDom(setting, target, [{tagName: "a", attrName: "treeNode" + consts.id.A}]); + if (tmp) { + tId = tools.getNodeMainDom(tmp).id; + nodeEventType = "mousedownNode"; + } + } + // 检查 tId 数组是否非空 + if (tId.length > 0) { + // 从缓存中获取节点对象 + node = data.getNodeCache(setting, tId); + // 根据节点事件类型选择相应的回调函数 + switch (nodeEventType) { + // 如果事件类型是鼠标按下节点 + case "mousedownNode" : + // 设置回调函数为鼠标按下节点的处理函数 + nodeEventCallback = _handler.onMousedownNode; + break; + // 如果事件类型是鼠标悬停到节点上 + case "hoverOverNode" : + // 设置回调函数为鼠标悬停到节点上的处理函数 + nodeEventCallback = _handler.onHoverOverNode; + break; + // 如果事件类型是鼠标移出节点 + case "hoverOutNode" : + // 设置回调函数为鼠标移出节点的处理函数 + nodeEventCallback = _handler.onHoverOutNode; + break; + } + } + var proxyResult = { + stop: false, // 初始化停止标志为false + node: node, // 当前节点 + nodeEventType: nodeEventType, // 节点事件类型 + nodeEventCallback: nodeEventCallback, // 节点事件回调函数 + treeEventType: treeEventType, // 树事件类型 + treeEventCallback: treeEventCallback // 树事件回调函数 + }; + return proxyResult // 返回代理结果对象 + }, + //default init node of exedit + _initNode = function (setting, level, n, parentNode, isFirstNode, isLastNode, openFlag) { + if (!n) return; // 如果节点不存在,直接返回 + n.isHover = false; // 初始化节点的悬停状态为false + n.editNameFlag = false; // 初始化节点的编辑名称标志为false + }, + //update zTreeObj, add method of edit + _zTreeTools = function (setting, zTreeTools) { + zTreeTools.cancelEditName = function (newName) { + var root = data.getRoot(this.setting); // 获取根节点 + if (!root.curEditNode) return; // 如果没有正在编辑的节点,直接返回 + view.cancelCurEditNode(this.setting, newName ? newName : null, true); // 取消当前编辑的节点 + } + zTreeTools.copyNode = function (targetNode, node, moveType, isSilent) { + if (!node) return null; // 如果节点不存在,返回null + var isParent = data.nodeIsParent(setting, targetNode); // 判断目标节点是否是父节点 + if (targetNode && !isParent && this.setting.data.keep.leaf && moveType === consts.move.TYPE_INNER) return null; // 如果目标节点不是父节点且不允许移动到叶子节点,返回null + var _this = this, + newNode = tools.clone(node); // 克隆新节点 + if (!targetNode) { + targetNode = null; // 如果目标节点不存在,设置为null + moveType = consts.move.TYPE_INNER; // 设置移动类型为内部移动 + } + if (moveType == consts.move.TYPE_INNER) { + function copyCallback() { + view.addNodes(_this.setting, targetNode, -1, [newNode], isSilent); // 添加新节点到目标节点 + } + + if (tools.canAsync(this.setting, targetNode)) { + view.asyncNode(this.setting, targetNode, isSilent, copyCallback); // 如果目标节点支持异步加载,则异步加载后执行回调 + } else { + copyCallback(); // 否则直接执行回调 + } + } else { + view.addNodes(this.setting, targetNode.parentNode, -1, [newNode], isSilent); // 添加新节点到目标节点的父节点 + view.moveNode(this.setting, targetNode, newNode, moveType, false, isSilent); // 移动目标节点到新节点位置 + } + return newNode; // 返回新节点 + } + zTreeTools.editName = function (node) { + if (!node || !node.tId || node !== data.getNodeCache(this.setting, node.tId)) return; // 如果节点不存在或节点ID无效或节点不在缓存中,直接返回 + if (node.parentTId) view.expandCollapseParentNode(this.setting, node.getParentNode(), true); // 如果节点有父节点,展开父节点 + view.editNode(this.setting, node) // 编辑节点名称 + } + zTreeTools.moveNode = function (targetNode, node, moveType, isSilent) { + if (!node) return node; // 如果节点不存在,直接返回节点 + var isParent = data.nodeIsParent(setting, targetNode); // 判断目标节点是否是父节点 + if (targetNode && !isParent && this.setting.data.keep.leaf && moveType === consts.move.TYPE_INNER) { + return null; // 如果目标节点不是父节点且不允许移动到叶子节点,返回null + } else if (targetNode && ((node.parentTId == targetNode.tId && moveType == consts.move.TYPE_INNER) || $$(node, this.setting).find("#" + targetNode.tId).length > 0)) { + return null; // 如果目标节点是当前节点的父节点且移动类型为内部移动,或者目标节点包含当前节点,返回null + } else if (!targetNode) { + targetNode = null; // 如果目标节点不存在,设置为null + var _this = this; + + function moveCallback() { + // 调用view对象的moveNode方法,执行节点移动操作 + view.moveNode(_this.setting, targetNode, node, moveType, false, isSilent); + } + + // 判断是否可以异步处理节点移动,并且移动类型为内部移动 + if (tools.canAsync(this.setting, targetNode) && moveType === consts.move.TYPE_INNER) { + // 如果可以异步处理,则调用view对象的asyncNode方法进行异步节点移动 + view.asyncNode(this.setting, targetNode, isSilent, moveCallback); + } else { + // 否则直接调用moveCallback方法进行同步节点移动 + moveCallback(); + } + // 返回当前节点对象 + return node; + } + + zTreeTools.setEditable = function (editable) { + // 设置树的编辑状态 + this.setting.edit.enable = editable; + // 刷新树以应用新的编辑状态 + return this.refresh(); + } + }, + //method of operate data + _data = { + /** + * 设置子节点的层级 + * @param {Object} setting - 配置对象 + * @param {Object} parentNode - 父节点对象 + * @param {Object} node - 当前节点对象 + */ + setSonNodeLevel: function (setting, parentNode, node) { + // 如果当前节点不存在,直接返回 + if (!node) return; + // 获取当前节点的子节点列表 + var children = data.nodeChildren(setting, node); + // 设置当前节点的层级,如果存在父节点则层级加1,否则为0 + node.level = (parentNode) ? parentNode.level + 1 : 0; + // 如果子节点列表不存在,直接返回 + if (!children) return; + // 遍历所有子节点,递归设置每个子节点的层级 + for (var i = 0, l = children.length; i < l; i++) { + if (children[i]) data.setSonNodeLevel(setting, node, children[i]); + } + } + }, + //method of event proxy + _event = {}, + // 事件处理程序的方法 + _handler = { + /** + * 当鼠标悬停在节点上时触发的事件处理函数 + * @param {Object} event - 事件对象,包含事件相关的数据 + * @param {Object} node - 当前悬停的节点对象 + */ + onHoverOverNode: function (event, node) { + // 获取树的设置信息 + var setting = data.getSetting(event.data.treeId), + // 获取树的根节点 + root = data.getRoot(setting); + // 如果当前悬停的节点不是目标节点 + if (root.curHoverNode != node) { + // 调用onHoverOutNode方法处理之前的悬停节点 + _handler.onHoverOutNode(event); + } + // 更新当前悬停的节点为目标节点 + root.curHoverNode = node; + // 在视图中添加悬停效果的DOM元素 + view.addHoverDom(setting, node); + }, + /** + * 当鼠标移出节点时触发的事件处理函数 + * @param {Object} event - 事件对象,包含事件相关的数据 + * @param {Object} node - 当前移出的节点对象(可选) + */ + onHoverOutNode: function (event, node) { + // 获取树的设置信息 + var setting = data.getSetting(event.data.treeId), + // 获取树的根节点 + root = data.getRoot(setting); + // 如果当前有悬停的节点且该节点未被选中 + if (root.curHoverNode && !data.isSelectedNode(setting, root.curHoverNode)) { + // 从视图中移除悬停效果的DOM元素 + view.removeTreeDom(setting, root.curHoverNode); + // 清空当前悬停的节点 + root.curHoverNode = null; + } + }, + onMousedownNode: function (eventMouseDown, _node) { + var i, l, + setting = data.getSetting(eventMouseDown.data.treeId), + root = data.getRoot(setting), roots = data.getRoots(); + //right click can't drag & drop + if (eventMouseDown.button == 2 || !setting.edit.enable || (!setting.edit.drag.isCopy && !setting.edit.drag.isMove)) return true; + + //input of edit node name can't drag & drop + var target = eventMouseDown.target, + _nodes = data.getRoot(setting).curSelectedList, + nodes = []; + if (!data.isSelectedNode(setting, _node)) { + nodes = [_node]; + } else { + for (i = 0, l = _nodes.length; i < l; i++) { + if (_nodes[i].editNameFlag && tools.eqs(target.tagName, "input") && target.getAttribute("treeNode" + consts.id.INPUT) !== null) { + return true; + } + nodes.push(_nodes[i]); + if (nodes[0].parentTId !== _nodes[i].parentTId) { + nodes = [_node]; + break; + } + } + } + + view.editNodeBlur = true; // 设置编辑节点失去焦点标志为true + view.cancelCurEditNode(setting); // 取消当前正在编辑的节点 + + // 获取文档对象和body对象 + var doc = $(setting.treeObj.get(0).ownerDocument), + body = $(setting.treeObj.get(0).ownerDocument.body), curNode, tmpArrow, tmpTarget, + isOtherTree = false, // 标记是否为其他树 + targetSetting = setting, // 目标设置 + sourceSetting = setting, // 源设置 + preNode, nextNode, // 前一个节点和后一个节点 + preTmpTargetNodeId = null, // 临时目标节点ID + preTmpMoveType = null, // 临时移动类型 + tmpTargetNodeId = null, // 临时目标节点ID + moveType = consts.move.TYPE_INNER, // 移动类型,默认为内部移动 + mouseDownX = eventMouseDown.clientX, // 鼠标按下时的X坐标 + mouseDownY = eventMouseDown.clientY, // 鼠标按下时的Y坐标 + startTime = (new Date()).getTime(); // 记录开始时间 + + if (tools.uCanDo(setting)) { // 如果可以执行操作 + doc.bind("mousemove", _docMouseMove); // 绑定鼠标移动事件到文档上 + } + + function _docMouseMove(event) { + // 避免在点击节点后开始拖动 + if (root.dragFlag == 0 && Math.abs(mouseDownX - event.clientX) < setting.edit.drag.minMoveSize + && Math.abs(mouseDownY - event.clientY) < setting.edit.drag.minMoveSize) { + return true; // 如果鼠标移动距离小于最小拖动距离,则不进行拖动操作 + } + var i, l, tmpNode, tmpDom, tmpNodes; + body.css("cursor", "pointer"); // 将鼠标指针样式设置为指针 + + if (root.dragFlag == 0) { + // 检查是否允许拖动操作 + if (tools.apply(setting.callback.beforeDrag, [setting.treeId, nodes], true) == false) { + _docMouseUp(event); // 如果不允许拖动,则调用_docMouseUp方法结束拖动 + return true; // 返回true表示处理完成 + } + + // 遍历所有节点 + for (i = 0, l = nodes.length; i < l; i++) { + // 如果是第一个节点,初始化dragNodeShowBefore数组 + if (i == 0) { + root.dragNodeShowBefore = []; + } + // 获取当前节点 + tmpNode = nodes[i]; + // 如果当前节点是父节点且处于展开状态 + if (data.nodeIsParent(setting, tmpNode) && tmpNode.open) { + // 折叠该节点 + view.expandCollapseNode(setting, tmpNode, !tmpNode.open); + // 记录该节点在拖拽前的状态为展开 + root.dragNodeShowBefore[tmpNode.tId] = true; + } else { + // 记录该节点在拖拽前的状态为折叠 + root.dragNodeShowBefore[tmpNode.tId] = false; + } + } + + // 设置拖拽标志位为1 + root.dragFlag = 1; + // 隐藏悬停的DOM元素 + roots.showHoverDom = false; + // 显示iframe遮罩层 + tools.showIfameMask(setting, true); + + // 排序逻辑 + var isOrder = true, lastIndex = -1; + if (nodes.length > 1) { + // 获取父节点或根节点的子节点列表 + var pNodes = nodes[0].parentTId ? data.nodeChildren(setting, nodes[0].getParentNode()) : data.getNodes(setting); + tmpNodes = []; + // 遍历父节点或根节点的子节点列表 + for (i = 0, l = pNodes.length; i < l; i++) { + // 如果当前节点在dragNodeShowBefore中有记录 + if (root.dragNodeShowBefore[pNodes[i].tId] !== undefined) { + // 检查顺序是否被打乱 + if (isOrder && lastIndex > -1 && (lastIndex + 1) !== i) { + isOrder = false; + } + // 将当前节点添加到临时节点数组中 + tmpNodes.push(pNodes[i]); + // 更新最后一个索引值 + lastIndex = i; + } + // 如果临时节点数组的长度与原节点数组长度相同,则替换原节点数组并退出循环 + if (nodes.length === tmpNodes.length) { + nodes = tmpNodes; + break; + } + } + } + if (isOrder) { + // 获取第一个节点的前一个节点 + preNode = nodes[0].getPreNode(); + // 获取最后一个节点的下一个节点 + nextNode = nodes[nodes.length - 1].getNextNode(); + } + + // 创建一个新的