家校通小程序实战教程10部门管理前后端连接

目录

  • [1 加载后端的数据](#1 加载后端的数据)
  • [2 为什么不直接给变量赋值](#2 为什么不直接给变量赋值)
  • [3 保存部门信息](#3 保存部门信息)
  • [4 最终的效果](#4 最终的效果)
  • [5 总结](#5 总结)

现在部门管理已经完成了后端功能和前端开发,就需要在前端调用后端的数据完成界面的展示,而且在录入部门信息后需要提交到数据库里,本篇我们介绍一下前后端如何交互。

1 加载后端的数据

现在后端API已经有了,页面加载的时候需要从后端读取数据。打开应用,点击编辑JSX代码

输入如下代码

bash 复制代码
export default function DepartmentTree(props: JSXCompProps) {
  const { $w, contentSlot1, style } = props;
  const { Modal, Input, Tree, Button, Form } = antd;

  const [treeData, setTreeData] = React.useState([]);
  const [selectedNode, setSelectedNode] = React.useState(null);
  const [addChildModalVisible, setAddChildModalVisible] = React.useState(false);
  const [newDepartmentName, setNewDepartmentName] = React.useState("");
  const [contextMenuTargetId, setContextMenuTargetId] = React.useState(null);
  const [contextMenuPosition, setContextMenuPosition] = React.useState(null);
  const [form] = Form.useForm();

  // 初始化获取部门数据
  React.useEffect(() => {
    async function fetchDepartments() {
      try {
        const result = await $w.cloud.callDataSource({
          dataSourceName: "departmentManagement_adpurdw",
          methodName: "getAllDepartments",
          params: {}
        });
        if (result && result.data) {
          setTreeData(result.data);
        } else {
          Modal.warning({ title: "数据加载失败", content: "未获取到部门数据。" });
        }
      } catch (error) {
        console.error("Error fetching departments:", error);
        Modal.error({ title: "加载失败", content: "获取部门数据时出错,请稍后重试。" });
      }
    }

    fetchDepartments();
  }, [$w]);

  // 点击节点事件
  const handleNodeClick = (node) => {
    setSelectedNode(node);
    setContextMenuTargetId(null);
    form.setFieldsValue({ name: node.name });
  };

  // 切换节点展开/收起
  const toggleNode = (node) => {
    node.isOpen = !node.isOpen;
    setTreeData([...treeData]);
  };

  // 递归更新树节点
  const updateTree = (nodes, callback) => {
    return nodes.map((node) => {
      if (callback(node)) {
        return { ...node };
      }
      if (node.children) {
        return { ...node, children: updateTree(node.children, callback) };
      }
      return node;
    });
  };

  // 添加子部门逻辑
  const handleAddChild = () => {
    if (!newDepartmentName.trim()) {
      return Modal.warning({
        title: "部门名称不能为空",
        content: "请输入部门名称后重试。"
      });
    }

    const newChild = {
      id: Date.now(),
      name: newDepartmentName,
      parentId: contextMenuTargetId,
      children: [],
      isOpen: false
    };

    setTreeData((prevTreeData) =>
      updateTree(prevTreeData, (node) => {
        if (node.id === contextMenuTargetId) {
          node.children = [...(node.children || []), newChild];
          return true;
        }
        return false;
      })
    );

    setAddChildModalVisible(false);
    setNewDepartmentName("");
  };

  // 保存表单修改
  const handleSaveForm = () => {
    form
      .validateFields()
      .then((values) => {
        setTreeData((prevTreeData) =>
          updateTree(prevTreeData, (node) => {
            if (node.id === selectedNode.id) {
              node.name = values.name;
              return true;
            }
            return false;
          })
        );
        Modal.success({ title: "保存成功", content: "部门信息已更新。" });
      })
      .catch((info) => console.error("Validate Failed:", info));
  };

  // 右键点击事件
  const handleRightClick = (e, node) => {
    e.preventDefault();
    setContextMenuTargetId(node.id);
    setContextMenuPosition({ x: e.clientX, y: e.clientY });
  };

  // 渲染树节点
  const renderTree = (nodes) =>
    nodes.map((node) => (
      <Tree.TreeNode
        key={node.id}
        title={
          <span
            onClick={() => handleNodeClick(node)}
            onContextMenu={(e) => handleRightClick(e, node)}
          >
            {node.name}
          </span>
        }
      >
        {node.children && renderTree(node.children)}
      </Tree.TreeNode>
    ));

  return (
    <div style={{ display: "flex", ...style }}>
      {/* 左侧树 */}
      <div
        style={{
          flex: "1",
          borderRight: "1px solid #ddd",
          overflowY: "auto",
          padding: "10px",
          minHeight: "300px"
        }}
      >
        <Tree showLine defaultExpandAll>
          {renderTree(treeData)}
        </Tree>
      </div>

      {/* 右侧表单 */}
      <div
        style={{
          flex: "2",
          padding: "10px",
          minHeight: "300px"
        }}
      >
        {selectedNode ? (
          <Form form={form} layout="vertical">
            <h3>部门信息</h3>
            <Form.Item
              label="部门名称"
              name="name"
              rules={[{ required: true, message: "请输入部门名称" }]}
            >
              <Input />
            </Form.Item>
            <Form.Item label="部门编号">
              <Input value={selectedNode.id} disabled />
            </Form.Item>
            <div style={{ marginTop: "10px" }}>
              <Button
                type="primary"
                onClick={handleSaveForm}
                style={{ marginRight: "10px" }}
              >
                保存
              </Button>
              <Button onClick={() => form.resetFields()}>取消</Button>
            </div>
          </Form>
        ) : (
          <p>请选择左侧树节点以查看或编辑部门信息。</p>
        )}
      </div>

      {/* 添加子部门 Modal */}
      <Modal
        title="添加子部门"
        visible={addChildModalVisible}
        onOk={handleAddChild}
        onCancel={() => setAddChildModalVisible(false)}
      >
        <Input
          placeholder="请输入部门名称"
          value={newDepartmentName}
          onChange={(e) => setNewDepartmentName(e.target.value)}
        />
      </Modal>

      {/* 右键菜单 */}
      {contextMenuPosition && (
        <div
          style={{
            position: "absolute",
            top: contextMenuPosition.y,
            left: contextMenuPosition.x,
            backgroundColor: "#fff",
            boxShadow: "0 2px 8px rgba(0,0,0,0.15)",
            borderRadius: "4px",
            zIndex: 1000
          }}
          onMouseLeave={() => setContextMenuPosition(null)}
        >
          <div
            onClick={() => setAddChildModalVisible(true)}
            style={{
              padding: "8px 16px",
              cursor: "pointer",
              borderBottom: "1px solid #f0f0f0"
            }}
          >
            添加子部门
          </div>
        </div>
      )}

      {/* 插槽 */}
      <div>{contentSlot1}</div>
    </div>
  );
}

我们追加了一个代码,主要是要异步调用后端API的

bash 复制代码
  // 初始化获取部门数据
  React.useEffect(() => {
    async function fetchDepartments() {
      try {
        const result = await $w.cloud.callDataSource({
          dataSourceName: "departmentManagement_adpurdw",
          methodName: "getAllDepartments",
          params: {}
        });
        if (result && result.data) {
          setTreeData(result.data);
        } else {
          Modal.warning({ title: "数据加载失败", content: "未获取到部门数据。" });
        }
      } catch (error) {
        console.error("Error fetching departments:", error);
        Modal.error({ title: "加载失败", content: "获取部门数据时出错,请稍后重试。" });
      }
    }

    fetchDepartments();
  }, [$w]);

这里的dataSourceName和methodName是从我们的API里获取的

部门管理旁边的相当于我们的dataSourceName,而标识相当于我们的methodName

2 为什么不直接给变量赋值

我一开始认为,我直接获取数据就可以,比如这样

bash 复制代码
const treeData = $w.cloud.callDataSource({
  dataSourceName: "departmentManagement_adpurdw",
  methodName: "getAllDepartments",
  params: {}
});

但这种操作发现树是空的,并没有从后台读取数据过来。主要的原因是两方面,首先callDataSource是异步的,你这个执行完了数据其实是没返回的

两一方面,数据加载完毕并不会通知React重新渲染组件。改成useEffect的好处是,在组件首次渲染后启动数据加载任务。当数据加载完成后,通过 setTreeData 更新组件状态,React 会自动重新渲染组件并展示新数据。

尤其如果部门比较多的情况下,界面可以先出来,等数据获取完毕组装好了再次渲染树形组件,这种体验就比较好了

3 保存部门信息

数据我们现在已经可以从变量中读取了,剩下就是添加的时候将新增的部门信息保存到数据源里。这里我们调用微搭的写入方法,修改如下代码

bash 复制代码
// 添加子部门逻辑
  const handleAddChild = () => {
    if (!newDepartmentName.trim()) {
      return Modal.warning({
        title: "部门名称不能为空",
        content: "请输入部门名称后重试。",
      });
    }

    // 调用数据源写入新部门
    $w.cloud
      .callDataSource({
        dataSourceName: "bmb", // 数据源名称
        methodName: "wedaCreateV2", // 假设存在 createDepartment 方法
        params: {
          data: {
            bmmc: newDepartmentName, // 部门名称
            fbm: { _id: contextMenuTargetId }, // 父部门ID
          },
        },
      })
      .then((response) => {
        const newId = response?.id; // 从响应中获取新节点的 ID
        if (!newId) {
          throw new Error("数据源未返回有效ID");
        }

        const newChild = {
          id: newId, // 使用后端返回的 ID
          name: newDepartmentName,
          parentId: contextMenuTargetId,
          children: [],
          isOpen: false,
        };

        // 更新前端树数据
        setTreeData((prevTreeData) =>
          updateTree(prevTreeData, (node) => {
            if (node.id === contextMenuTargetId) {
              node.children = [...(node.children || []), newChild];
              return true;
            }
            return false;
          })
        );

        // 关闭模态框并清空输入
        setAddChildModalVisible(false);
        setNewDepartmentName("");
        Modal.success({
          title: "添加成功",
          content: `子部门 "${newDepartmentName}" 已成功添加。`,
        });
      })
      .catch((error) => {
        console.error("数据源写入失败", error);
        Modal.error({
          title: "操作失败",
          content: "添加子部门时出现错误,请稍后重试。",
        });
      });
  };

需要把上边给的完整代码的handleAddChild 进行替换,这里主要是调用了微搭的创建单条的API。在创建的时候需要传递对应的数据,因为我们的父部门是关联关系,新版的关联关系是对象类型,所以我们是按照对象的结构组织了数据。

4 最终的效果

现在已经可以从数据库里读取部门的信息,并且按照树形的结构进行展示

点击右键的时候可以添加部门,数据库里可以看到写入的数据

5 总结

我们本篇介绍了如何将前后端的功能连接起来,从代码上来讲还是比较复杂的,主要需要考虑react组件加载的机制,副作用的理解,以及微搭的异步加载机制。要想理解好这些概念需要你亲自做一遍,才会有深入的体会。

相关推荐
尘浮生39 分钟前
Java项目实战II基于Java+Spring Boot+MySQL的社区帮扶对象管理系统的设计与实现(开发文档+数据库+源码)
java·开发语言·数据库·spring boot·mysql·微信小程序·小程序
LS_learner2 小时前
微信小程序的文件结构
微信小程序·小程序
我是唐赢3 小时前
小程序子组件调用父组件方法、父组件调用子组件方法
前端·小程序
赖赖赖先生3 小时前
小程序自定义tab-bar,踩坑记录
小程序
zybishe4 小时前
计算机毕业设计原创定制(免费送源码):Node.JS+Express+MySQL Express 流浪动物救助系统
android·深度学习·机器学习·小程序·node.js·课程设计·express
hahaqi95276 小时前
小程序转uniapp之setData
前端·小程序·uni-app
王者鳜錸21 小时前
UNIAPP-微信小程序实现文本纠错功能
微信小程序·小程序·uni-app
狂团商城小师妹1 天前
AJAX家政系统自营+多商家家政系统服务小程序PHP+Uniapp
微信小程序·小程序·微信公众平台
V+zmm101341 天前
基于微信小程序的校园二手交易平台系统设计与开发ssm+论文源码调试讲解
java·小程序·毕业设计·mvc·ssm