横向树列表

运行效果

基于树形动态数据集,使用 antdv 原生 Table 组件实现数据在表格中横向展示

案例需求

将平台中树形结构动态数据集数据,使用表格进行横向展示,支持节点单步加载、全部数据加载及数据级联选择

知识点

  • 使用平台树形动态数据集进行数据保存及“父 id 、全路径 id 、层级、是否叶子节点”字段平台自动维护
  • 使用 antdv 下原生 Table 组件实现树形数据在表中横向展示

开发过程

新增树形动态数据集

  • 在应用中“数据”新增动态数据集,其中需新增字段“父id、全路径id、层级、是否叶子节点”,用于后续设置数据为树形结果数据,并对这些字段进行自动维护

  • 勾选树形数据,设置父列属性

  • 设置“父id、全路径id、层级、是否叶子节点”字段

  • 保存数据集配置并新增数据

工具条

{% raw %}
<antdv:ToolBar class="x-toolbar" id="toolBar0">
    <antdv:Button id="button2" on:onClick="onButton2Click" text="刷新">
    </antdv:Button>
    <antdv:Button id="loadAllBtn" on:onClick="onLoadAllBtnClick" text="加载全部数据">
    </antdv:Button>
    <antdv:Button id="button0" on:onClick="onSaveBtnClick" text="保存选择数据" type="primary" xmlns:antdv="$UI/comp/antdv/components">
    </antdv:Button>
    <antdv:Div id="div0">
        <antdv:Typography.Text content="开启级联选择:" id="typographyText0">
        </antdv:Typography.Text>
        <antdv:Switch bind:ref="pageData.current.cascade" checkedChildren="是" id="switch0" unCheckedChildren="否">
        </antdv:Switch>
    </antdv:Div>
</antdv:ToolBar>
{% endraw%}

添加数据集

  • 功能菜单数据:用于表格数据展示,取消树形数据设置及设置分页为-1,全部加载,并设置刷新前事件,用于控制数据是全部加载还是parent过滤加载。

{% raw %}
//菜单数据加载前过滤设置,用于控制数据是全部加载还是parent过滤加载
onMainDataBeforeRefresh = (event) => {
    let mainData = event.source;
    //设置数据全部加载
    if (this.loadAll) {
        mainData.setFilter("parentFilter", []);
        this.loadAll = false;
        return;
    }
    //设置数据单步加载
    let parentId = this.comp("pageData").getValue("parentId");
    let filter = [];
    if (parentId) {
        filter.push({ name: "parentId", value: parentId, op: "eq" });
    } else {
        filter.push({ name: "parentId", value: null, op: "isNull" });
    }
    mainData.setFilter("parentFilter", filter);
}
{% endraw %}
  • 用户功能菜单:用于保存选择的菜单数据(用户根据需要自行新建,当前案例中表仅包含用户 id 及菜单 id)

  • 树形功能菜单数据:与功能菜单数据为同一个动态数据集,用于绑定树形选择组件,需要设置加载所有数据

使用原生 antdv Table 进行渲染

.w

//调用js中定义的方法
{tableRender?.()}

.js

//引入
import {Table} from "ant-design-vue";
//渲染表格
let tableRender = () => {
    let columns = getColumns();
    //渲染字段    
    return (
        <>
            <Table
                key={new Date().getTime()}
                class={"tableBox"}
                columns={columns}
                dataSource={state.treeData}
                rowKey="fid"
                pagination={false}
                bordered={true}
                rowHoverable={false}
            />
        </>
    );
}

表格数据刷新及图标相关代码

{% raw %}
//选择
let onChange = (event, column, text, record, index) => {
    let cascade = pageData.getValue("cascade");
    let treeData = [...state.treeData] || [];
    treeData.filter(item => cascade ? (item.fullId?.includes(record.fullId)) : (item.fid == record.fid)).forEach(item => item.checked = event.target.checked);
    state.treeData = treeData;
}

//展开子节点数据
let onExpand = (record) => {
    pageData.setValue("parentId", record.fid);
    mainData.refreshData();
}

//编辑菜单数据
let onEdit = async (record, isNew = false) => {
    if (isNew) {
        await mainData.newData();
        mainData.setValue("parentId", record.fid);
    } else {
        mainData.to(record.fid);
    }
    $page.comp("menuModal").show();
}

let confirm = (content, ok) => {
    Modal.confirm({
        title: '提示',
        content,
        getContainer: () => document.querySelector('#pcx-page-root'),
        okText: "确定",
        cancelText: "取消",
        onOk() {
            if (ok) {
                ok();
            }
        }
    });
}

//删除功能菜单
let onDelMenu = (record) => {
    confirm((record.isLeaf ? "" : "当前数据下还有子菜单数据,") + "确定删除?", async () => {
        state.loading = true;
        try {
            let { data: { result, message: msg } } = await $page.request({ url: `/vue/main/dbrest/menus?fullId=like.${record.fullId}*`, method: "DELETE" });
            if (result > 0) {
                message.info("删除成功");

                //删除当前数据及子数据
                let treeData = [...state.treeData] || [];
                treeData = treeData.filter(item => !item.fullId?.includes(record.fid));
                state.treeData = treeData;
                //重新加载父数据
                // onExpand({ fid: record.parentId });
                $page.loadAll = true;
                resetData = true;
                mainData.refreshData()
            } else {
                message.info("删除失败:" + msg);
            }
        } catch (error) {
            message.info("删除失败");
        } finally {
            state.loading = false;
        }
    })
}

//获取表格列
let getColumns = () => {
    let columns = [];
    for (let { key, value, rowSpan } of columnsData) {
        let column = {
            title: value,
            dataIndex: key,
            ellipsis: true,
            customRender: ({ text, record, index, column }) => onRender(text, record, index, key),
            customCell: (record, rowIndex, column) => {
                return {
                    rowSpan: record[`${key}_rowSpan`] || 0
                }
            }

        }
        columns.push(column)
    }
    return columns;
}

//多选框渲染
let onRender = (text, record = {}, index, column) => {
    if (!text) return;
    let divBox = [];
    divBox.push(<Checkbox checked={record.checked || false} onChange={(e) => { return onChange(e, column, text, record, index); }}>{text || '空'}</Checkbox>);

    let optDiv = [];
    if (!record.isLeaf) {
        optDiv.push(<FolderOutlined title="展开" onClick={() => onExpand(record)} />);
    }
    optDiv.push(<PlusOutlined onClick={() => onEdit(record, true)} />);
    optDiv.push(<DeleteOutlined onClick={() => onDelMenu(record)} />);
    optDiv.push(<EditOutlined onClick={() => onEdit(record, false)} />);
    divBox.push(<Space class={"optDiv"} style={{ color: "#1677FF", display: "none" }}>{optDiv}</Space>);
    return <Space>{divBox}</Space>;
};

//渲染表格
let tableRender = () => {
    let columns = getColumns();
    //渲染字段    
    return (
        <>
            <Table
                key={new Date().getTime()}
                class={"tableBox"}
                columns={columns}
                dataSource={state.treeData}
                rowKey="fid"
                pagination={false}
                bordered={true}
                rowHoverable={false}
            />
        </>
    );
}
{% endraw %}

工具栏中数据加载及保存等方法

{% raw %}
//数据初始
let onPageInitState = async (event) => {
    //默认异步加载
    await onButton2Click();
}

//保存用户选择数据
let onSaveBtnClick = async (event) => {
    state.loading = true;
    try {
        //获取选择数据
        let menuIds = [];
        state.treeData.filter(item => item.checked).forEach(item => menuIds.push(item.fid));
        //获取用户已保存的数据 
        let userInfo = userData.getCurrentRow();
        if (!userInfo) {
            await userData.newData();
        }
        userData.setValue("menuIds", menuIds.join());
        userData.saveData();
        message.info("保存成功")
    } catch (error) {
        message.error("保存失败");
    } finally {
        state.loading = false;
    }
}

//刷新
let onButton2Click = async (event) => {
    resetData = true;
    $page.loadAll = false;
    await userData.refreshData();
    await mainData.refreshData();
}

//加载全部数据
let onLoadAllBtnClick = async (event) => {
    resetData = true;
    $page.loadAll = true;
    await userData.refreshData();
    await mainData.refreshData();
}

//保存菜单数据
let onMenuModalOk = async (event) => {
    state.loading = true;
    try {
        //校验数据集必填
        mainData.enabledCheck.set(true);//没有接管保存时需要设置
        let checkInfo = mainData.check();
        if (!checkInfo.valid) {
            message.error(checkInfo.msg.join());
            return;
        }
        let currentRow = mainData.getCurrentRow().toJson({ ui: true });
        await mainData.saveData();
        message.info("保存成功")
        //设置当前节点父数据叶子节点为否
        let treeData = [...state.treeData] || [];
        treeData.forEach(item => {
            if (item.fid == currentRow.parentId) {
                item.isLeaf = 0;
            }
        })

        state.treeData = treeData
        //加载子节点数据
        onExpand({ fid: currentRow.parentId });
        restData1.refreshData();
    } catch (error) {
        message.error("保存失败");
    } finally {
        state.loading = false;
    }
}
{% endraw %}

案例位置

桌面-页面-列表表单组件-横向树列表.w

使用html的input(file)

使用html的input做文件选择具体实现如下:

js定义渲染方法返回input

let inputRender = () => {
    return <input id="input0" type="file" />
}

在页面源码中需要显示input的地方通过 {inputRender()}引入,即可使用html的input做文件选择

1727688782183

运行效果如下:

1727688805423

results matching ""

    No results matching ""