weixin_58412143 2025-04-26 18:58 采纳率: 83.7%
浏览 13
已结题

父子组件传值下拉框回显失败

为社么根据父组件传值后初始化页面时子组件的第一个级联和最后两个下拉框没有回显?
console.log("this.expenseList[0].options", this.expenseList[0].options.userOptions);打印了三次最后一次数据正确
父组件

 <!-- 表格内容 - 收款 -->
            <div class="tableClass">
              <ReimbDetail
                ref="reimbDetailRef"
                :formData="formData"
                :isEdit="
                (!formData.isDraft || Number(formData.isUpdate) === 1) &&
                isShowWhilePrint
                "
                v-model="formData.expenseList"
              ></ReimbDetail>
            </div>

子组件代码

<template>
  <div class="detail-cont">
    <el-form :model="formData" :rules="rules" ref="formRef">
      <!-- 报销明细 -->
      <div class="title-cont">
        <h3>
          <span>收款</span>
        </h3>
        <div class="btn-cont" v-if="isEdit == '1'">
          <!-- 添加 -->
          <i class="el-icon-circle-plus-outline" @click="handleAdd"></i>
          <!-- 删除 -->
          <i class="el-icon-remove-outline" @click="handleDel" v-if="expenseList && expenseList.length > 1"></i>
        </div>
      </div>

      <el-table :data="expenseList" style="width: 100%" max-height="550px" ref="tableRef" border>
        <el-table-column type="selection" width="36" />
        <!-- 报销类型 -->
        <el-table-column prop="reimbursementType" :label="$t('travel.reimburseType')">
          <template slot-scope="scope">
            <el-form-item :prop="'expenseList.' + scope.$index + '.reimbursementType'"
              :rules="[{ required: true, trigger: 'change' }]">
              <span>{{
                selectDictLabels(
                  dict.type.oa_reimbursement_type,
                  scope.row.reimbursementType
                )
              }}</span>
            </el-form-item>
          </template>
        </el-table-column>
        <!-- 承担部门 -->
        <el-table-column prop="deptId" :label="'承担部门'">
          <template slot="header">
            <a class="label-required">*</a>承担部门
          </template>
          <template slot-scope="scope">
            <el-form-item :prop="'expenseList.' + scope.$index + '.payDeptId'"
              :rules="[{ required: true, trigger: 'change' }]">
              <el-cascader v-if="isEdit == '1'" v-model="scope.row.payDeptId" :options="deptOptions" :props="{
                value: 'id',
                label: 'label',
                children: 'children',
              }" filterable :show-all-levels="false" style="width: 100%"
                @change="(value) => handleDepBudget(scope.row, 'payDeptId', value)"></el-cascader>
              <span v-else>{{ scope.row.deptName }}</span>
            </el-form-item>
          </template>
        </el-table-column>
        <!-- 费用科目 -->
        <el-table-column prop="expenseType" :label="$t('travel.expenseItem')">
          <template slot="header">
            <a class="label-required">*</a>{{ $t("travel.expenseItem") }}
          </template>
          <template slot-scope="scope">
            <el-form-item :prop="'expenseList.' + scope.$index + '.expenseType'"
              :rules="[{ required: true, trigger: 'change' }]">
              <div v-if="isEdit == '1'" style="display: flex; align-items: center">
                <el-select v-if="isEdit == '1'" v-model="scope.row.expenseType" clearable filterable
                  :placeholder="$t('travel.pleaseSelect')" style="width: 100%"
                  @change="handleDepBudget(scope.row, 'expenseType')">
                  <el-option v-for="item in dict.type.financial_budget_type" :key="item.value" :label="item.label"
                    :value="item.value">
                  </el-option>
                </el-select>
              </div>
              <span v-else>{{
                selectDictLabels(
                  dict.type.financial_budget_type,
                  scope.row.expenseType
                )
              }}</span>
            </el-form-item>
          </template>
        </el-table-column>
        <!-- 承担部门预算 -->
        <el-table-column prop="depBudget" :label="'承担部门预算'">
          <template slot="header">
            <a class="label-required">*</a>承担部门预算
          </template>
          <template slot-scope="scope">
            <el-form-item :prop="'expenseList.' + scope.$index + '.depBudget'"
              :rules="[{ required: true, trigger: 'change' }]">
              <!-- 可用预算 -->
              <div style="color: #000000; font-weight: 600">
                {{ $t("travel.availableBudget") }}: {{ scope.row.currency }}
                {{ scope.row.budgetAmount || "0.00" }}
              </div>
              <!-- 已发生费用 -->
              <div style="color: #0000ff; font-weight: 600">
                {{ $t("travel.incurredFee") }}: {{ scope.row.currency }}
                {{ scope.row.incurredAmount || "0.00" }}
              </div>
              <!-- 审批中费用 -->
              <div style="color: #008000; font-weight: 600">
                {{ $t("travel.reviewFee") }}:{{ scope.row.currency }}
                {{ scope.row.approveAmount || "0.00" }}
              </div>
            </el-form-item>
          </template>
        </el-table-column>
        <!-- 申请金额 -->
        <el-table-column prop="consumeAmount" :label="'申请金额'">
          <template slot="header">
            <a class="label-required">*</a>申请金额
          </template>
          <template slot-scope="scope">
            <el-form-item :prop="'expenseList.' + scope.$index + '.consumeAmount'"
              :rules="[{ required: true, trigger: 'blur' }]">
              <div v-if="isEdit == '1'" style="display: flex; align-items: center">
                <el-input :placeholder="$t('travel.pleaseEnter')" style="width: 100%"
                  v-model.trim="scope.row.consumeAmount" maxlength="13"
                  oninput="value=value.replace(/^([0-9-]\d*\.?\d{0,2})?.*$/,'$1')"
                  @input="(val) => handleInputChange(val, scope.row, 'consumeAmount')" @blur="
                    handleInputBlur(
                      scope.row.consumeAmount,
                      scope.row,
                      'consumeAmount'
                    )
                    ">
                  <template slot="append">{{ scope.row.currency }}</template>
                </el-input>
              </div>
              <span v-else-if="isEdit != '1' && scope.row.consumeAmount">{{ scope.row.currency }} {{
                scope.row.consumeAmount
              }}</span>
            </el-form-item>
          </template>
        </el-table-column>
        <!-- 负责人 -->
        <el-table-column prop="userId" :label="'负责人'">
          <template slot="header">
            <a class="label-required">*</a>负责人
          </template>
          <template slot-scope="scope">
            <el-form-item :prop="'expenseList.' + scope.$index + '.userId'"
              :rules="[{ required: true, trigger: 'change' }]">
              <el-select v-if="isEdit == '1'" v-model="scope.row.userId" clearable filterable remote reserve-keyword
                :remote-method="(value) => getCostBearerList(value, scope.row.deptId)"
                @focus="(value) => getCostBearerList(value, scope.row.deptId)" :placeholder="$t('travel.pleaseSelect')"
                style="width: 100%" @change="(value) => handleSelect(value, 'user', scope)">
                <el-option v-for="item in scope.row.options.userOptions" :key="item.leaderId" :label="item.leaderName"
                  :value="item.leaderId">
                </el-option>
              </el-select>
              <span v-else>{{ scope.row.leaderName }}</span>
            </el-form-item>
          </template>
        </el-table-column>
        <!-- 高级/运营总监 -->
        <el-table-column prop="leaderId" :label="'高级/运营总监'">
          <template slot="header">
            <a class="label-required">*</a>高级/运营总监
          </template>
          <template slot-scope="scope">
            <el-form-item :prop="'expenseList.' + scope.$index + '.leaderId'"
              :rules="[{ required: true, trigger: 'change' }]">
              <el-select v-if="isEdit == '1'" v-model="scope.row.leaderId" clearable filterable remote reserve-keyword
                :remote-method="(value) => getCostBearerList2(value, scope.row.deptId)"
                @focus="(value) => getCostBearerList2(value, scope.row.deptId)" :placeholder="$t('travel.pleaseSelect')"
                style="width: 100%" @change="(value) => handleSelect(value, 'leader', scope)">
                <el-option v-for="item in scope.row.options.leaderOptions" :key="item.rosterId" :label="item.rosterName"
                  :value="item.rosterId">
                </el-option>
              </el-select>
              <span v-else>{{ scope.row.leaderName }}</span>
            </el-form-item>
          </template>
        </el-table-column>
      </el-table>
    </el-form>
  </div>
</template>
<script>
import { getBdDeptAllList, depBudgetS } from "@/api/agentFollow/travel";
import { rosteruser } from "@/api/user.js";
import { printLeaderList } from "@/api/financial/commiProcess";
import { getDate } from "@/utils";
import { message } from '../../config_file/config_file';
import {
  getDeptreeChild,
  approval,
} from "@/api/agentFollow/process.js";
export default {
  name: "ReimbDetail",
  components: {},
  dicts: ["oa_reimbursement_type", "financial_budget_type"],
  props: {
    defaultExpenseList: {
      type: Array,
      default() {
        return [];
      },
    },
    dataState: {
      type: Number,
    },
    applyDate: {
      type: String,
    },
    formData: {
      type: Object,
      default() {
        return {};
      },
    },
  },
  data() {
    return {
      isEdit: this.$route.query.isEdit || "1", // 0 详情  1编辑
      validateFlag: false,
      rules: {
        'expenseList.0.reimbursementType': [{ required: true, trigger: 'change' }],
        'expenseList.0.payDeptId': [{ required: true, trigger: 'change' }],
        'expenseList.0.expenseType': [{ required: true, trigger: 'change' }],
        'expenseList.0.depBudget': [{ required: true, trigger: 'change' }],
        'expenseList.0.consumeAmount': [{ required: true, trigger: 'blur' }],
        'expenseList.0.userId': [{ required: true, trigger: 'change' }],
        'expenseList.0.leaderId': [{ required: true, trigger: 'change' }]
      },
      expenseList: [
        {
          reimbursementType: "1", // 费用类型
          payDeptId: this.formData.deptId, // 承担部门id
          payDeptName: this.formData.deptId, // 承担部门名称
          expenseType: null, // 费用科目
          // 承担部门预算
          currency: null, // 币种
          approveAmount: null, // 审批中费用
          budgetAmount: null, // 可用预算
          incurredAmount: null, // 已发生费用
          consumeAmount: null, // 申请金额
          userId: this.formData.leaderId, // 负责人id
          userName: this.formData.leaderName, // 负责人名称
          leaderId: this.formData.leaderId, // 高级/运营总监id
          leaderName: this.formData.leaderName, // 高级/运营总监名称
          deptOptions: [], // 承担部门列表
          // 下拉选项
          options: {
            divisionOption: [], // 报销承担分部列表
            userOptions: [], // 负责人列表
            leaderOptions: [], // 高级/运营总列表
          },
        }
      ],
      arr: {},
      deptOptions: [],
    };
  },
  watch: {
    expenseList: {
      handler(newVal) {
        this.$emit('input', newVal);
      },
      deep: true
    }
  },
  mounted() {
    this.onDictReady();
  },
  methods: {
    // 初始化
    async onDictReady() {
      this.arr = {
        reimbursementType: "1", // 费用类型
        payDeptId: this.formData.deptId, // 承担部门id
        payDeptName: this.formData.deptName, // 承担部门名称
        userId: this.formData.leaderId, // 负责人id
        userName: this.formData.leaderName, // 负责人名称
        leaderId: this.formData.leaderId, // 高级/运营总监id
        leaderName: this.formData.leaderName, // 高级/运营总监名称
        options: {
          divisionOption: [], // 报销承担分部列表
          userOptions: [], // 负责人列表
          leaderOptions: [], // 高级/运营总列表
        },
      }
      
      if (this.formData.expenseList && this.formData.expenseList.length > 0) {
        this.expenseList = this.formData.expenseList;
      } else {
        this.expenseList = []
        this.expenseList.push(this.arr)
      }

      try {
        // 获取所有需要的数据
        const [divisionOption, userOptions, leaderOptions] = await Promise.all([
          this.getDivisionList(""),
          this.getCostBearerList("", this.formData.deptId),
          this.getCostBearerList2("", this.formData.deptId)
        ]);

        // 更新每个行的options
        this.expenseList.forEach((item, index) => {
          this.$set(this.expenseList, index, { ...item, options }); // 替换整个对象以触发响应式更新
        });
        console.log("this.expenseList[0].options", this.expenseList[0].options.userOptions);
        // 强制更新视图
        this.$nextTick(() => {
          this.expenseList = [...this.expenseList];
        });
      } catch (error) {
        console.error('初始化数据失败:', error);
      }
    },
    // 金额/汇率处理
    handleInputBlur(item, row, str) {
      // 保留两位小数
      if (item !== "" && item != undefined) {
        row[str] = (item / 1).toFixed(2);
      } else {
        row.consumeAmount = 0;
      }
      if (row.consumeAmount && row.budgetAmount) {
        if (parseFloat(row.consumeAmount) > parseFloat(row.budgetAmount)) {
          this.$modal.confirm(this.$t("travel.amountCompareTips")); // 报销金额不能大于可用预算
          row.consumeAmount = "";
          return false;
        }
      }
    },
    // 输入框内容变化处理
    handleInputChange(val, row, str) {
      if (val === '') {
        row[str] = '';
        // 触发校验
        this.$refs.formRef.validateField(`expenseList.${this.expenseList.indexOf(row)}.${str}`);
      }
    },
    // 计算承担主体预算(部门)
    async handleDepBudget(row, type, value) {
      console.log("handleDepBudget--row", row.expenseType, row.payDeptId);

      if (row.expenseType || row.payDeptId) {
        row.deptId = row.payDeptId.length > 0 ? row.payDeptId[row.payDeptId.length - 1] : "";
        console.log("2222222--row", row.expenseType, row.deptId);

        // 获取负责人列表
        const userOptions = await this.getCostBearerList("", row.deptId);
        row.options.userOptions = userOptions;

        // 获取高级/运营总监列表
        const leaderOptions = await this.getCostBearerList2("", row.deptId);
        row.options.leaderOptions = leaderOptions;

        // 获取预算信息
        depBudgetS({
          deptId: row.deptId,
          reimburseDate: this.applyDate,
          subjectType: row.expenseType,
        }).then((res) => {
          this.$set(row, "approveAmount", res.data.approveAmount);
          this.$set(row, "budgetAmount", res.data.budgetAmount);
          this.$set(row, "incurredAmount", res.data.incurredAmount);
          this.$set(row, "currency", res.data.currency);
        });
      }

      if (type == "payDeptId") {
        // 清空费用承担人
        row.userId = "";
        row.userName = "";
        row.leaderId = "";
        row.leaderName = "";
        row.expenseType = "";

        // 触发校验
        this.$nextTick(() => {
          this.$refs.formRef.validateField([
            `expenseList.${this.expenseList.indexOf(row)}.userId`,
            `expenseList.${this.expenseList.indexOf(row)}.leaderId`,
            `expenseList.${this.expenseList.indexOf(row)}.expenseType`
          ]);
        });
      }
    },
    // 远程检索 -- 报销承担分部
    async getDivisionList() {
      try {
        let res = await getDeptreeChild();
        this.deptOptions = res.data;
      } catch (error) { }
    },
    // 远程检索 -- 负责人
    async getCostBearerList(val, deptId) {
      const params = {
        deptId: deptId || "",
        userName: typeof val === "string" ? val : "",
      };

      try {
        const res = await printLeaderList(params);
        if (res.code === "000") {
          return res.data;
        } else {
          return [];
        }
      } catch (error) {
        return [];
      }
    },
    // 远程检索 -- 高级/运营总监
    async getCostBearerList2(val, deptId) {
      const params = {
        deptId: deptId || "",
        userName: typeof val === "string" ? val : "",
      };

      try {
        const res = await rosteruser(params);
        if (res.code === "000") {
          return res.data;
        } else {
          return [];
        }
      } catch (error) {
        return [];
      }
    },
    // 负责人&高级/运营总监 选择
    handleSelect(value, type, scope) {
      const target = scope.row.options[type + "Options"].find((item) => {
        if (type === 'user') {
          return item.leaderId === value;
        } else {
          return item.rosterId === value;
        }
      }) || null;

      if (type === 'user') {
        scope.row[type + "Id"] = target?.leaderId;
        scope.row[type + "Name"] = target?.leaderName;
      } else {
        scope.row[type + "Id"] = target?.rosterId;
        scope.row[type + "Name"] = target?.rosterName;
      }

      // 强制更新视图
      this.$nextTick(() => {
        this.expenseList = [...this.expenseList];
      });
    },
    // 添加
    async handleAdd() {
      try {
        // 获取所有需要的数据
        const [divisionOption, userOptions, leaderOptions] = await Promise.all([
          this.getDivisionList(""),
          this.getCostBearerList("", this.formData.deptId),
          this.getCostBearerList2("", this.formData.deptId)
        ]);

        // 创建新行数据
        const newRow = {
          ...this.arr,
          options: {
            divisionOption: divisionOption || [],
            userOptions: userOptions || [],
            leaderOptions: leaderOptions || []
          }
        };
        console.log("ADD111111111--options.userOption", userOptions)
        // 添加到列表
        this.expenseList.push(newRow);

        // 强制更新视图
        this.$nextTick(() => {
          this.expenseList = [...this.expenseList];
        });
      } catch (error) {
        console.error('添加行数据失败:', error);
        this.$message.error('添加行数据失败');
      }
    },
   
   
  },
};
</script>


  • 写回答

3条回答 默认 最新

  • 紫雾凌寒 新星创作者: 人工智能技术领域 2025-04-26 19:02
    关注

    让 “紫雾凌寒” 来帮你回答,本回答结合 DeepSeek

    如果还有问题,请评论或点击头像私信,如果有帮助到您, 请关注、采纳、点赞

    1. 关键点分析
      - 从问题描述来看,主要问题是父子组件传值后,子组件部分下拉框没有回显。
      - 原因可能有多种,比如数据传递不完整、子组件对传入数据的处理逻辑有误、下拉框的初始化逻辑依赖数据的加载顺序等。
    2. 解决方案
      - 检查数据传递
      • 首先确保父组件传递给子组件的数据是完整且正确的。可以在父组件传递数据时打印传递的数据,确认其格式和内容是否符合子组件的预期。
      • 例如,假设父组件有如下数据传递给子组件:
    // 父组件
    import React, { useState } from'react';
    import ChildComponent from './ChildComponent';
    
    const ParentComponent = () => {
        const [expenseList, setExpenseList] = useState([
            { id: 1, options: ['option1', 'option2'] },
            { id: 2, options: ['option3', 'option4'] }
        ]);
        return <ChildComponent expenseList={expenseList} />;
    };
    
    export default ParentComponent;
    
    • 子组件接收和处理数据
      • 在子组件中,正确接收父组件传递的数据,并按照下拉框的初始化逻辑进行处理。
      • 例如子组件代码:
    // 子组件
    import React, { useEffect } from'react';
    
    const ChildComponent = ({ expenseList }) => {
        useEffect(() => {
            // 这里假设下拉框是通过某个状态控制显示选项
            const handleInitOptions = () => {
                // 假设下拉框的状态是selectedOption
                if (expenseList && expenseList.length > 0) {
                    // 第一个级联下拉框回显逻辑
                    const firstDropdownOptions = expenseList[0].options;
                    // 这里假设你有一个设置第一个下拉框选项的函数setFirstDropdownOptions
                    setFirstDropdownOptions(firstDropdownOptions);
    
                    // 最后两个下拉框回显逻辑,假设最后两个下拉框的数据获取逻辑类似
                    const lastTwoDropdownOptions = expenseList.slice(-2).map(item => item.options);
                    // 假设你有一个设置最后两个下拉框选项的函数setLastTwoDropdownOptions
                    setLastTwoDropdownOptions(lastTwoDropdownOptions);
                }
            };
            handleInitOptions();
        }, [expenseList]);
    
        return (
            <div>
                {/* 这里假设第一个下拉框 */}
                <select>
                    {/* 这里选项会根据上述逻辑动态填充 */}
                </select>
                {/* 最后两个下拉框 */}
                <select>
                    {/* 这里选项会根据上述逻辑动态填充 */}
                </select>
                <select>
                    {/* 这里选项会根据上述逻辑动态填充 */}
                </select>
            </div>
        );
    };
    
    export default ChildComponent;
    
    • 检查下拉框初始化顺序
      • 确认下拉框的初始化逻辑是否正确,特别是那些没有正确回显的下拉框。
      • 确保数据加载完成后再进行下拉框的初始化操作,比如可以使用useEffect钩子来确保在数据准备好后执行初始化代码。
        3. 多种解决方案的优缺点
    • 优点
      • 检查数据传递:这种方法直接从数据源头入手,如果数据传递有误,能快速定位问题。
      • 子组件接收和处理数据:全面检查子组件对数据的处理过程,确保数据能正确地应用到下拉框的初始化中。
      • 检查下拉框初始化顺序:能有效避免因数据加载顺序问题导致的下拉框回显异常。
    • 缺点
      • 检查数据传递:可能需要在多个地方打印和检查数据,比较繁琐。
      • 子组件接收和处理数据:如果子组件逻辑复杂,查找问题可能需要花费较多时间。
      • 检查下拉框初始化顺序:需要对下拉框的整个初始化流程有清晰的了解,否则可能遗漏问题点。
        4. 总结
    • 父子组件传值后下拉框回显失败,通常是数据传递或子组件处理逻辑的问题。通过仔细检查数据传递、子组件对数据的处理以及下拉框的初始化顺序,可以逐步定位和解决问题。在解决过程中,利用console.log打印关键数据和状态,有助于快速发现问题所在。

    请注意,上述代码示例基于React框架,实际应用中可能需要根据具体的技术栈进行调整。

    希望以上解答对您有所帮助。如果您有任何疑问,欢迎在评论区提出。

    评论

报告相同问题?

问题事件

  • 已结题 (查看结题原因) 8月25日
  • 创建了问题 4月26日