Vue js & laravel 后端编辑发票

问题描述 投票:0回答:1

编辑:

我从invoice_items中获取这些数组,产品与发票id匹配

[
    {
        "id": 27,
        "invoice_id": 14,
        "product_id": 1,
        "unit_price": 200,
        "product_quantity": 4,
        "unit_id": 7,
        "tax_rate": 0,
        "tax_type": "exclusive",
        "discount_type": "flat",
        "discount": 0,
        "created_at": "2024-01-01T12:23:34.000000Z",
        "updated_at": "2024-01-01T12:23:34.000000Z",
        "product": {
            "id": 1,
            "code": "02381402314",
            "name": "Travel Beg",
            "slug": "man-travel-beg-black-and-white",
            "product_type": "simple",
            "barcode_symbology": "CODE128",
            "stock_quantity": 6,
            "stock_alert_quantity": null,
            "purchase_price": 100,
            "sale_price": 200,
            "parent_id": null,
            "brand_id": 2,
            "category_id": 1,
            "unit_id": 7,
            "purchase_unit_id": 7,
            "sale_unit_id": 7,
            "tax_id": null,
            "tax_type": "exclusive",
            "description": null,
            "created_at": "2024-01-01T12:23:33.000000Z",
            "updated_at": "2024-01-01T12:23:33.000000Z"
        }
    },
    {
        "id": 28,
        "invoice_id": 14,
        "product_id": 3,
        "unit_price": 12000,
        "product_quantity": 1,
        "unit_id": 8,
        "tax_rate": 15,
        "tax_type": "inclusive",
        "discount_type": "flat",
        "discount": 0,
        "created_at": "2024-01-01T12:23:34.000000Z",
        "updated_at": "2024-01-01T12:23:34.000000Z",
        "product": {
            "id": 3,
            "code": "99080532114",
            "name": "Smart Phone Samsung A11 Black",
            "slug": "smart-phone-samsung-a11",
            "product_type": "single",
            "barcode_symbology": "CODE128",
            "stock_quantity": 4,
            "stock_alert_quantity": 13,
            "purchase_price": 10000,
            "sale_price": 12000,
            "parent_id": null,
            "brand_id": 9,
            "category_id": 13,
            "unit_id": 8,
            "purchase_unit_id": 8,
            "sale_unit_id": 8,
            "tax_id": 4,
            "tax_type": "inclusive",
            "description": "Latest Samsung Smart Phone.",
            "created_at": "2024-01-01T12:23:33.000000Z",
            "updated_at": "2024-01-06T15:09:00.000000Z"
        }
    }
]

使用函数 fetchInvoiceProducts

  // Fetch invoice products
  const fetchInvoiceProducts = async () => {
    try {
      const response = await axios.get(`/api/products_invoice/14`);
      selected_items.value = response.data;
      console.log(selected_items)
    } catch (error) {
      console.error('Error fetching invoice products:', error);
    }
  };

然后我将这些数组作为产品循环在我的表中,以便在我的发票中进行编辑,如下所示

<table
      class="table bg-white table-bordered my-3 p-1 table-responsive"
  >
      <thead>
          <tr class="bg-ass text-secondary">
              <th class="min150">Product</th>
              <th class="min100">Unit Price</th>
              <th class="">Stock</th>
              <th class="min100">Quantity</th>
              <th class="min100">Tax</th>
              <th class="min100">Subtotal</th>
              <th class="min100">action</th>
          </tr>
      </thead> 
      {{ selected_items }} 
      <tbody v-if="selected_items.length > 0">
          <tr v-for="p in selected_items">
              <td>{{ p.name }}</td>
              <td>{{ p.unit_price }}</td>
              <td>
                  <input
                      type="number"
                      class="max100 form-control"
                      :value="p.product_quantity"
                      disabled
                  />
              </td>
              <td>
                  <input
                      type="number"
                      class="max100 form-control"
                      min="1"
                      v-model="p.stock_quantity"
                      @input="calculateGrandTotal()"
                  />
              </td>
              <td>
                  {{
                      (
                                p.product_quantity *
                                ((((100 - p.tax_rate) *
                                    p.sale_price) /
                                    100) *
                                    (p.tax_rate / 100))
                            ).toFixed(2)
                  }}
                  $
              </td>
              <td>
                  {{
                      p.tax_type == "exclusive"
                          ? p.product_quantity *
                            (p.sale_price * (p.tax_rate / 100) +
                                p.sale_price)
                          : p.product_quantity * p.product.sale_price
                  }}
              </td>
              <td>
                  <CrossSvgIcon
                      @click="removeSelected(p.id)"
                      color="red"
                  />...
              </td>
          </tr>
      </tbody>
  </table>

然后是上面代码的结果,

-----------------------------------------------------------------
Product      | Unit Price   | Stock | Quantity  | Tax    | Subtotal
-----------------------------------------------------------------
99080532114  | 200        | 6     | 2         | ...  | ...
02381402314  | 100        | 6     | 2         | ...  | ...

第二方,客户使用名称和仓库 ID 搜索要从产品中添加的产品

从 PRODUCT 中搜索产品的数组 2 个参数:仓库 = ${selected_warehouse.value} AND 名称 = ${name}


[
    {
        "id": 2,
        "name": "Casual Shoe for Man",
        "sale_price": 1000,
        "stock_quantity": 3,
        "tax_rate": 2.55,
        "tax_id": 2,
        "tax_type": "exclusive",
        "sale_unit_id": 8,
        "short_name": "box"
    },
    {
        "id": 3,
        "name": "Smart Phone Samsung A11 Black",
        "sale_price": 12000,
        "stock_quantity": 3,
        "tax_rate": 20,
        "tax_id": 4,
        "tax_type": "inclusive",
        "sale_unit_id": 8,
        "short_name": "box"
    }
]

通过函数获取

  async function fetchProducts(name = product_q.value) {
    if (name.length < 1) {
        clearProducts();
        return;
    }
    try {
        const response = await axios.get(`/api/warehouse-products/${selected_warehouse.value}/${name}`);
        items.value = response.data;
    } catch (error) {
        console.error('Error fetching products:', error);
    }
}

这是我完整的 Vue 组件代码。

<template>
    <div>
      <!-- Search product input :disabled="!selected_warehouse"-->
      <div class="p-1 dropdown-search-select-box">
        <label class="my-1">Search product</label>
        <input
          
          type="text"
          class="form-control form-control-sm"
          placeholder="Search items..."
          v-model="product_q"
          @keyup="fetchProducts(product_q)"
        />
        <ul class="list-group dropdown-search-list" v-if="items.length > 0">
          <li
            @click="onSelectProduct(p)"
            :key="p.id"
            class="list-group-item cursor-pointer"
            v-for="p in items"
          >
            {{ p.name }}
          </li>
        </ul>
      </div>
  
      <table
      class="table bg-white table-bordered my-3 p-1 table-responsive"
  >
      <thead>
          <tr class="bg-ass text-secondary">
              <th class="min150">Product</th>
              <th class="min100">Unit Price</th>
              <th class="">Stock</th>
              <th class="min100">Quantity</th>
              <th class="min100">Tax</th>
              <th class="min100">Subtotal</th>
              <th class="min100">action</th>
          </tr>
      </thead> 
      {{ selected_items }} 
      <tbody v-if="selected_items.length > 0">
          <tr v-for="p in selected_items">
              <td>{{ p.name }}</td>
              <td>{{ p.unit_price }}</td>
              <td>
                  <input
                      type="number"
                      class="max100 form-control"
                      :value="p.product_quantity"
                      disabled
                  />
              </td>
              <td>
                  <input
                      type="number"
                      class="max100 form-control"
                      min="1"
                      v-model="p.stock_quantity"
                      @input="calculateGrandTotal()"
                  />
              </td>
              <td>
                  {{
                      (
                                p.product_quantity *
                                ((((100 - p.tax_rate) *
                                    p.sale_price) /
                                    100) *
                                    (p.tax_rate / 100))
                            ).toFixed(2)
                  }}
                  $
              </td>
              <td>
                  {{
                      p.tax_type == "exclusive"
                          ? p.product_quantity *
                            (p.sale_price * (p.tax_rate / 100) +
                                p.sale_price)
                          : p.product_quantity * p.product.sale_price
                  }}
              </td>
              <td>
                  <CrossSvgIcon
                      @click="removeSelected(p.id)"
                      color="red"
                  />...
              </td>
          </tr>
      </tbody>
  </table>
    </div>{{ product_q }}
  </template>
  
  <script setup>
  import { ref, onMounted } from 'vue';
  import axios from 'axios';
  
  const product_q = ref('');
  const selected_warehouse = ref(1);
  const items = ref([]);
  const selected_items = ref([]);
  
  async function fetchProducts(name = product_q.value) {
    if (name.length < 1) {
        clearProducts();
        return;
    }
    try {
        const response = await axios.get(`/api/warehouse-products/${selected_warehouse.value}/${name}`);
        items.value = response.data;
    } catch (error) {
        console.error('Error fetching products:', error);
    }
}
  // Fetch invoice products
  const fetchInvoiceProducts = async () => {
    try {
      const response = await axios.get(`/api/products_invoice/14`);
      selected_items.value = response.data;
      console.log(selected_items)
    } catch (error) {
      console.error('Error fetching invoice products:', error);
    }
  };
  
  // Add a new product to the selected items
  function onSelectProduct(product) {
    const existingProduct = selected_items.value.find(
        (item) => item.id === product.id
    );
  
    if (existingProduct) {
      existingProduct.quantity += 1;
    } else {
      product.quantity = 1;
      selected_items.value.push(product);
    }
  
    clearProducts();
    product_q.value = '';
    calculateGrandTotal();
  };
  
  // Clear products in the dropdown
  const clearProducts = () => {
    items.value = [];
  };
  
  // Calculate grand total (add your logic here)
  const calculateGrandTotal = () => {
    // Add your logic to calculate the grand total
  };
  
  onMounted(() => {
    // Fetch invoice products when the component is mounted
    fetchInvoiceProducts();
    fetchProducts();
  });
  </script>
  

我收到错误

Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'sale_price') same with all other variables in my components  

当我从搜索输入添加新产品时,无法识别 p.sale_price 和 p.name

我认为问题是我用两个函数得到的数据类型不一样!!!

我尝试了很多方法都没有成功

arrays laravel vuejs3
1个回答
0
投票

你不能只是将两个数据结构推向一致并希望它能起作用;您需要确认数据。

您的第一步是确定您需要从两个对象中获取哪些数据,并确保您从每个产品的 API 调用中获取所需的所有数据。

据我所知,您需要一个具有以下功能的对象:

  1. 身份证
  2. 姓名
  3. 单价
  4. 库存剩余
  5. 订购数量
  6. 税率
  7. 税种

因此,您选择的项目应始终符合此要求,以便您的表格可以正确填充。对于这样的项目,您应该使用 Typescript 来帮助您。但既然你不是,你需要对数据创建一些转换。

创建两个函数来帮助强制数据采用首选结构(我做了我能做的最好的映射;您需要相应地更新)

  function productToProductRow(product){
    return {
      id: product.id,
      name: product.name,
      unit_price: product.sale_price,
      stock_left: product.stock_quantity,
      order_quantity: 1,
      tax_rate: product.tax_rate,
      tax_type: product.tax_type
    }
  }

  function invoiceToProductRow(invoice){
    return {
      id: invoice.product.id,
      name: invoice.product.name,
      unit_price: invoice.product.sale_price,
      stock_left: invoice.product.stock_quantity,
      order_quantity: invoice.product_quantity,
      tax_rate: invoice.product.tax_rate,
      tax_type: invoice.product.tax_type
    }

  }

现在,在分配数据时更新。只需使用每个项目上的功能即可。

selected_items.value = response.data

应该是:

selected_items.value = response.data.map((invoice) => invoiceToProductRow(invoice));

并且

items.value = response.data;

应该是:

items.value = response.data.map((product) => productToProductRow(product));

您的

<tbody>
也需要更新:

<tbody v-if="selected_items.length > 0">
  <tr v-for="p in selected_items">
    <td>{{ p.name }}</td>
    <td>{{ p.unit_price }}</td>
    <td>
      <input
        type="number"
        class="max100 form-control"
        :value="p.stock_left"
        disabled
      />
    </td>
    <td>
      <input
        type="number"
        class="max100 form-control"
        min="1"
        v-model="p.order_quantity"
        @input="calculateGrandTotal()"
      />
    </td>
    <td>
      {{
        (
          p.order_quantity *
          ((((100 - p.tax_rate) *
            p.unit_price) /
            100) *
            (p.tax_rate / 100))
          ).toFixed(2)
      }}
      $
    </td>
    <td>
      {{
        p.tax_type == "exclusive"
          ? p.order_quantity *
            (p.unit_price * (p.tax_rate / 100) +
                p.unit_price)
          : p.order_quantity * p.unit_price
      }}
    </td>
    <td>
      <CrossSvgIcon
        @click="removeSelected(p.id)"
        color="red"
      />...
    </td>
  </tr>
</tbody>

您应该考虑重构此组件以使其更具可读性,并对其进行分解,使其不再是一个功能遍布各处的大型组件。理想情况下,表格行应该是一个子组件,并且税收和小计计算属性应该位于该子组件内。

© www.soinside.com 2019 - 2024. All rights reserved.