You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React对接Laravel API,实现商机与账户数据关联显示账户名称

解决商机与账户关联显示账户名称的问题

你的核心问题是直接访问数组accounts的nome属性,这是错误的——accounts是数组,需要根据商机的account_id匹配对应的账户对象,才能拿到账户名称。下面是具体的修复方案:

基础修复方案(直接匹配)

在渲染每一条商机时,使用数组的find方法,根据account_id从accounts数组中找到对应的账户,再提取名称。同时处理找不到账户的异常情况(比如账户已删除但商机未清理):

修改表格tbody中对应<td>的代码:

<tbody>
  {opportunities.map((opportunity) => {
    // 根据account_id找到对应账户
    const matchedAccount = accounts.find(acc => acc.id === opportunity.account_id);
    return (
      <tr key={opportunity.id}>
        <th>{opportunity.id}</th>
        <td>{opportunity.nome}</td>
        <td>{opportunity.account_id}</td>
        {/* 显示账户名称,找不到则显示默认文本 */}
        <td>{matchedAccount ? matchedAccount.nome : "未关联账户"}</td>
        <td>{opportunity.estagioNome}</td>
        <td>{opportunity.dataApresentacao}</td>
        <td className="text-center">
          <Link
            to={`edit/${opportunity.id}`}
            className="btn btn-outline-warning"
          >
            Editar
          </Link>
          <button
            onClick={() => deleteOpportunity(opportunity.id)}
            className="btn btn-outline-danger m-1"
          >
            Deletar
          </button>
        </td>
      </tr>
    );
  })}
</tbody>

性能优化方案(大数据量适用)

如果账户数据较多,find方法每次渲染都会遍历数组,性能会受影响。可以用useMemo将账户数组转换成ID-账户对象的映射,这样查找账户的时间复杂度从O(n)降到O(1):

  1. 导入useMemo:
import { useState, useEffect, useMemo } from "react";
  1. 在组件内部生成账户映射:
const OpportunityShow = () => {
  const [opportunities, setOpportunities] = useState([]);
  const [accounts, setAccounts] = useState([]);

  // 生成ID到账户的映射,仅在accounts变化时重新计算
  const accountMap = useMemo(() => {
    return accounts.reduce((map, account) => {
      map[account.id] = account;
      return map;
    }, {});
  }, [accounts]);

  // ... 其余代码不变
  1. 渲染时直接通过映射获取账户:
<td>{accountMap[opportunity.account_id]?.nome || "未关联账户"}</td>

额外优化建议

  • 合并两个useEffect,减少重复渲染:
useEffect(() => {
  const fetchData = async () => {
    await Promise.all([getAllOpportunities(), getAllAccounts()]);
  };
  fetchData();
}, []);

这样可以同时发起两个API请求,避免两次独立的渲染触发。

  • 移除无用的value属性:原代码中<td value={opportunity.account_id}>的value属性对表格单元格无效,可以直接删掉。

修改后的完整组件代码

import Table from "react-bootstrap/Table";
import axios from "axios";
import { Link } from "react-router-dom";
import { useState, useEffect, useMemo } from "react";
import Form from "react-bootstrap/Form";
import Button from "react-bootstrap/Button";

const endpoint = "http://localhost:8000/api";
const endpointAccount = "http://localhost:8000/api";

const OpportunityShow = () => {
  const [opportunities, setOpportunities] = useState([]);
  const [accounts, setAccounts] = useState([]);

  // 生成账户ID映射表
  const accountMap = useMemo(() => {
    return accounts.reduce((map, account) => {
      map[account.id] = account;
      return map;
    }, {});
  }, [accounts]);

  useEffect(() => {
    const fetchData = async () => {
      await Promise.all([getAllOpportunities(), getAllAccounts()]);
    };
    fetchData();
  }, []);

  const getAllAccounts = async () => {
    const response = await axios.get(`${endpointAccount}/accounts`);
    setAccounts(response.data);
  };

  const getAllOpportunities = async () => {
    const response = await axios.get(`${endpoint}/opportunities`);
    setOpportunities(response.data);
  };

  const deleteOpportunity = async (id) => {
    await axios.delete(`${endpoint}/opportunity/${id}`);
    getAllOpportunities();
  };

  return (
    <div className="d-grid gap-2">
      <div className="row">
        <div className="col-8">
          <Form className="d-flex m-1">
            <Form.Control
              type="search"
              placeholder="Filtro"
              className="me-2"
              aria-label="Search"
            />
            <Button variant="outline-secondary">Search</Button>
          </Form>
        </div>
        <div className="col-4">
          <Link
            to="/opportunity/create"
            className="col-11 btn btn-outline-primary m-1 "
          >
            Create
          </Link>
        </div>
      </div>
      <Table hover className="">
        <thead>
          <tr>
            <th scope="col">#</th>
            <th scope="col">Nome</th>
            <th scope="col">Conta id</th>
            <th scope="col">Conta nome</th>
            <th scope="col">Fase</th>
            <th scope="col">Data Fechamento</th>
            <th className="text-center" scope="col">
              Ações
            </th>
          </tr>
        </thead>
        <tbody>
          {opportunities.map((opportunity) => (
            <tr key={opportunity.id}>
              <th>{opportunity.id}</th>
              <td>{opportunity.nome}</td>
              <td>{opportunity.account_id}</td>
              <td>{accountMap[opportunity.account_id]?.nome || "未关联账户"}</td>
              <td>{opportunity.estagioNome}</td>
              <td>{opportunity.dataApresentacao}</td>
              <td className="text-center">
                <Link
                  to={`edit/${opportunity.id}`}
                  className="btn btn-outline-warning"
                >
                  Editar
                </Link>
                <button
                  onClick={() => deleteOpportunity(opportunity.id)}
                  className="btn btn-outline-danger m-1"
                >
                  Deletar
                </button>
              </td>
            </tr>
          ))}
        </tbody>
      </Table>
    </div>
  );
};

export default OpportunityShow;

内容的提问来源于stack exchange,提问作者Coimbra

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 04:20:33