编辑:
我从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
我认为问题是我用两个函数得到的数据类型不一样!!!
我尝试了很多方法都没有成功
你不能只是将两个数据结构推向一致并希望它能起作用;您需要确认数据。
您的第一步是确定您需要从两个对象中获取哪些数据,并确保您从每个产品的 API 调用中获取所需的所有数据。
据我所知,您需要一个具有以下功能的对象:
因此,您选择的项目应始终符合此要求,以便您的表格可以正确填充。对于这样的项目,您应该使用 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>
您应该考虑重构此组件以使其更具可读性,并对其进行分解,使其不再是一个功能遍布各处的大型组件。理想情况下,表格行应该是一个子组件,并且税收和小计计算属性应该位于该子组件内。