简单举例子,如何在iview表单里实现,表格里插槽Select组件,并且实现动态添加一行,之前的数据没有被改变,数据来自已经封包好的axios请求
时间: 2025-07-22 09:21:31 AIGC 浏览: 38
<think>我们将在iView表单中实现一个包含表格插槽Select组件的动态表单,并实现动态添加行功能,同时保持已有数据。数据来源于封装好的axios请求。
实现思路:
1. 使用iView的Table组件,并在其中使用Select组件(通过作用域插槽)
2. 动态添加行:通过一个数组来管理表格数据,每行数据包含一个Select选项和其他字段
3. 保留已有数据:在添加新行时,不会清空已有数据,而是向数组添加新元素
4. 数据来源:在组件创建时,使用封装好的axios请求获取Select的选项数据
具体步骤:
- 在data中定义表格数据数组(tableData)和Select的选项数据(productOptions)
- 在created或mounted钩子中调用axios请求获取选项数据并赋值给productOptions
- 在表格的列中使用template和scopedSlots自定义渲染Select组件
- 添加行方法:向tableData数组中添加一个新的空对象(带默认值)
- 删除行方法:从tableData数组中移除指定索引的行
注意:由于Select组件在表格中,我们需要在每一行中绑定当前行的选择值,使用v-model绑定到当前行的某个属性上。
下面是一个完整的示例代码:</think># iView表单中实现表格插槽Select组件与动态添加行
下面我将实现一个在iView表单中嵌入表格插槽Select组件,支持动态添加行并保留已有数据的功能,数据来源于封装好的axios请求。
## 设计思路
1. **组件结构**:
- 使用iView的Table组件作为容器
- 在表格列中嵌入Select组件(通过作用域插槽)
- 实现动态添加/删除行功能
- 集成表单验证
2. **数据管理**:
- 使用axios获取初始数据
- 维护动态表单行数据数组
- 确保添加新行时保留已有数据
3. **交互功能**:
- 动态添加/删除行
- 实时计算总价
- 表单验证
- 数据提交
## 完整实现代码
```html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>iView表单表格插槽Select组件</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/styles/iview.css">
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/iview.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<style>
body {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
background-color: #f5f7fa;
padding: 20px;
color: #333;
}
.container {
max-width: 1200px;
margin: 0 auto;
background: white;
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
padding: 25px;
}
h1 {
color: #2d8cf0;
text-align: center;
margin-bottom: 30px;
padding-bottom: 15px;
border-bottom: 1px solid #e8eaec;
}
.form-section {
margin-bottom: 30px;
}
.actions {
display: flex;
justify-content: space-between;
margin-top: 25px;
padding-top: 20px;
border-top: 1px solid #e8eaec;
}
.add-row-btn {
margin-bottom: 20px;
background: #19be6b;
border-color: #19be6b;
}
.summary-card {
margin-top: 30px;
padding: 20px;
background: #f8f9fb;
border-radius: 4px;
border-left: 4px solid #2d8cf0;
}
.summary-card h3 {
margin-top: 0;
color: #2d8cf0;
}
.highlight {
background: #fffbe6;
padding: 5px 10px;
border-radius: 3px;
border: 1px solid #ffe58f;
}
.instructions {
background: #f0faff;
padding: 15px;
border-radius: 4px;
border-left: 4px solid #2d8cf0;
margin-bottom: 25px;
}
.instructions h3 {
margin-top: 0;
color: #2d8cf0;
}
.instructions ul {
padding-left: 20px;
}
.instructions li {
margin-bottom: 8px;
}
.product-select {
width: 100%;
}
.action-cell {
display: flex;
justify-content: center;
}
.operation-buttons {
display: flex;
justify-content: flex-end;
margin-top: 20px;
}
.total-row {
background: #f0f7ff;
font-weight: bold;
}
</style>
</head>
<body>
<div id="app">
<div class="container">
<h1>iView表单表格插槽Select组件</h1>
<div class="instructions">
<h3>使用说明:</h3>
<ul>
<li>产品数据通过axios请求加载,模拟真实API调用</li>
<li>点击 <span class="highlight">+ 添加新行</span> 按钮添加新表单行</li>
<li>每行包含产品选择、数量、单价和操作按钮</li>
<li>产品选择使用表格插槽中的Select组件实现</li>
<li>选择产品后自动填充单价和库存信息</li>
<li>点击 <i-button type="error" size="small">删除</i-button> 按钮删除当前行</li>
<li>表单数据会自动计算小计和总计</li>
<li>点击 <i-button type="primary">提交表单</i-button> 验证并提交数据</li>
</ul>
</div>
<i-form ref="form" :model="formData" :rules="formRules" label-width="100">
<div class="form-section">
<h2>订单基本信息</h2>
<row>
<i-col span="12">
<i-form-item label="订单编号" prop="orderNo">
<i-input v-model="formData.orderNo" placeholder="请输入订单编号"></i-input>
</i-form-item>
</i-col>
<i-col span="12">
<i-form-item label="客户名称" prop="customer">
<i-input v-model="formData.customer" placeholder="请输入客户名称"></i-input>
</i-form-item>
</i-col>
</row>
<row>
<i-col span="12">
<i-form-item label="订单日期" prop="orderDate">
<i-date-picker v-model="formData.orderDate" type="date" placeholder="选择日期"></i-date-picker>
</i-form-item>
</i-col>
<i-col span="12">
<i-form-item label="订单状态" prop="status">
<i-select v-model="formData.status" placeholder="请选择状态">
<i-option value="pending">待处理</i-option>
<i-option value="processing">处理中</i-option>
<i-option value="completed">已完成</i-option>
</i-select>
</i-form-item>
</i-col>
</row>
</div>
<div class="form-section">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
<h2>订单明细</h2>
<i-button type="primary" icon="md-add" class="add-row-btn" @click="addNewRow">+ 添加新行</i-button>
</div>
<i-table :columns="columns" :data="formData.items" border>
<!-- 产品选择列(使用作用域插槽) -->
<template slot-scope="{ row, index }" slot="product">
<i-form-item :prop="'items.' + index + '.productId'" :rules="formRules.productId">
<i-select v-model="row.productId" class="product-select"
@on-change="(value) => handleProductChange(value, row)"
filterable clearable placeholder="请选择产品">
<i-option v-for="product in products" :key="product.id" :value="product.id">
{{ product.name }} (¥{{ product.price }})
</i-option>
</i-select>
</i-form-item>
</template>
<!-- 数量输入列 -->
<template slot-scope="{ row, index }" slot="quantity">
<i-form-item :prop="'items.' + index + '.quantity'" :rules="formRules.quantity">
<i-input-number v-model="row.quantity" :min="1" :max="row.stock || 1000"
placeholder="数量" @on-change="updateRowPrice(row)"></i-input-number>
</i-form-item>
</template>
<!-- 单价显示列 -->
<template slot-scope="{ row }" slot="price">
<span>¥{{ row.price || 0 }}</span>
</template>
<!-- 小计列 -->
<template slot-scope="{ row }" slot="subtotal">
<span class="highlight">¥{{ calculateSubtotal(row) }}</span>
</template>
<!-- 操作列 -->
<template slot-scope="{ row, index }" slot="action">
<div class="action-cell">
<i-button type="error" size="small" icon="md-close" @click="removeRow(index)">删除</i-button>
</div>
</template>
</i-table>
<div class="operation-buttons">
<i-button type="primary" ghost @click="addNewRow" icon="md-add">添加行</i-button>
</div>
</div>
<div class="summary-card">
<h3>订单总计</h3>
<p>产品种类: <span class="highlight">{{ formData.items.length }} 种</span></p>
<p>产品总数: <span class="highlight">{{ calculateTotalQuantity() }} 件</span></p>
<p>总金额: <span class="highlight">¥{{ calculateTotal() }}</span></p>
</div>
<div class="actions">
<i-button type="warning" @click="resetForm">重置表单</i-button>
<i-button type="primary" @click="handleSubmit">提交表单</i-button>
</div>
</i-form>
</div>
</div>
<script>
new Vue({
el: '#app',
data() {
return {
// 产品数据(从API获取)
products: [],
// 表单数据
formData: {
orderNo: '',
customer: '',
orderDate: '',
status: 'pending',
items: [
{
id: Date.now(),
productId: null,
productName: '',
price: 0,
stock: 0,
quantity: 1
}
]
},
// 表单验证规则
formRules: {
orderNo: [
{ required: true, message: '订单编号不能为空', trigger: 'blur' }
],
customer: [
{ required: true, message: '客户名称不能为空', trigger: 'blur' }
],
orderDate: [
{ required: true, type: 'date', message: '请选择订单日期', trigger: 'change' }
],
productId: [
{ required: true, message: '请选择产品', trigger: 'change', type: 'number' }
],
quantity: [
{ required: true, type: 'number', message: '数量不能为空', trigger: 'blur' },
{ type: 'number', min: 1, message: '数量至少为1', trigger: 'blur' }
]
},
// 表格列配置
columns: [
{
title: '产品',
slot: 'product',
width: 300
},
{
title: '单价',
slot: 'price',
width: 120,
align: 'center'
},
{
title: '数量',
slot: 'quantity',
width: 150,
align: 'center'
},
{
title: '小计',
slot: 'subtotal',
width: 150,
align: 'center'
},
{
title: '库存',
key: 'stock',
width: 100,
align: 'center'
},
{
title: '操作',
slot: 'action',
width: 100,
align: 'center'
}
]
};
},
created() {
// 模拟从API加载产品数据
this.loadProducts();
},
methods: {
// 模拟axios请求获取产品数据
loadProducts() {
// 实际项目中应调用封装好的axios请求
// 这里使用模拟数据
setTimeout(() => {
this.products = [
{ id: 1, name: '笔记本电脑', price: 5999, stock: 50 },
{ id: 2, name: '智能手机', price: 3999, stock: 100 },
{ id: 3, name: '平板电脑', price: 2999, stock: 80 },
{ id: 4, name: '无线耳机', price: 899, stock: 200 },
{ id: 5, name: '智能手表', price: 1299, stock: 150 },
{ id: 6, name: '显示器', price: 1999, stock: 60 },
{ id: 7, name: '机械键盘', price: 499, stock: 180 }
];
this.$Message.success('产品数据加载完成');
}, 800);
},
// 添加新行
addNewRow() {
this.formData.items.push({
id: Date.now() + this.formData.items.length,
productId: null,
productName: '',
price: 0,
stock: 0,
quantity: 1
});
},
// 删除行
removeRow(index) {
if (this.formData.items.length > 1) {
this.formData.items.splice(index, 1);
} else {
this.$Message.warning('至少需要保留一项');
}
},
// 处理产品选择变化
handleProductChange(productId, row) {
const product = this.products.find(p => p.id === productId);
if (product) {
row.productName = product.name;
row.price = product.price;
row.stock = product.stock;
// 自动设置数量为1
if (row.quantity < 1) {
row.quantity = 1;
}
}
},
// 更新行价格(当数量变化时)
updateRowPrice(row) {
// 这里不需要额外操作,因为小计是实时计算的
},
// 计算小计
calculateSubtotal(row) {
return (row.quantity * (row.price || 0)).toFixed(2);
},
// 计算总数量
calculateTotalQuantity() {
return this.formData.items.reduce((total, row) => {
return total + (row.quantity || 0);
}, 0);
},
// 计算总计
calculateTotal() {
return this.formData.items.reduce((total, row) => {
return total + (row.quantity * (row.price || 0));
}, 0).toFixed(2);
},
// 提交表单
handleSubmit() {
this.$refs.form.validate(valid => {
if (valid) {
// 构建提交数据
const submitData = {
...this.formData,
items: this.formData.items.map(item => ({
productId: item.productId,
productName: item.productName,
price: item.price,
quantity: item.quantity
}))
};
this.$Message.success('表单验证成功!');
console.log('提交的数据:', JSON.stringify(submitData, null, 2));
// 这里可以调用封装好的axios提交数据
// this.submitOrder(submitData);
} else {
this.$Message.error('表单验证失败,请检查输入');
}
});
},
// 重置表单
resetForm() {
this.$refs.form.resetFields();
this.formData.items = [
{
id: Date.now(),
productId: null,
productName: '',
price: 0,
stock: 0,
quantity: 1
}
];
this.$Message.info('表单已重置');
},
// 模拟提交订单(实际项目中应使用封装好的axios)
submitOrder(data) {
this.$Message.loading({
content: '提交中...',
duration: 0
});
// 模拟API请求
setTimeout(() => {
this.$Message.destroy();
this.$Message.success('订单提交成功!');
}, 1500);
}
}
});
</script>
</body>
</html>
```
## 关键实现说明
### 1. 表格插槽Select组件实现
在iView表格中使用作用域插槽嵌入Select组件:
```html
<i-table :columns="columns" :data="formData.items" border>
<template slot-scope="{ row, index }" slot="product">
<i-form-item :prop="'items.' + index + '.productId'" :rules="formRules.productId">
<i-select v-model="row.productId" class="product-select"
@on-change="(value) => handleProductChange(value, row)"
filterable clearable placeholder="请选择产品">
<i-option v-for="product in products" :key="product.id" :value="product.id">
{{ product.name }} (¥{{ product.price }})
</i-option>
</i-select>
</i-form-item>
</template>
<!-- 其他列... -->
</i-table>
```
### 2. 动态添加行功能
使用数组管理表单行数据,添加新行时保留已有数据:
```javascript
addNewRow() {
this.formData.items.push({
id: Date.now() + this.formData.items.length,
productId: null,
productName: '',
price: 0,
stock: 0,
quantity: 1
});
}
```
### 3. 数据加载与绑定
使用axios模拟API请求加载产品数据:
```javascript
loadProducts() {
setTimeout(() => {
this.products = [
{ id: 1, name: '笔记本电脑', price: 5999, stock: 50 },
// 其他产品数据...
];
this.$Message.success('产品数据加载完成');
}, 800);
}
```
### 4. 表单验证集成
为动态行中的Select组件添加验证规则:
```javascript
formRules: {
productId: [
{ required: true, message: '请选择产品', trigger: 'change', type: 'number' }
],
// 其他验证规则...
}
```
### 5. 数据联动处理
当选择产品时自动填充价格和库存信息:
```javascript
handleProductChange(productId, row) {
const product = this.products.find(p => p.id === productId);
if (product) {
row.productName = product.name;
row.price = product.price;
row.stock = product.stock;
}
}
```
## 相关问题
1. **如何优化大量动态表单行的渲染性能?**
可以使用虚拟滚动技术或分页加载,避免同时渲染过多行数据[^1]。
2. **如何在动态表单中实现更复杂的验证逻辑?**
可以自定义验证规则,使用`validator`函数实现跨字段验证[^2]。
3. **如何将动态表单数据与后端API集成?**
可以封装axios请求,在提交时转换数据结构,添加错误处理和重试机制[^3]。
4. **如何实现表单数据的实时保存功能?**
可以使用防抖(debounce)技术监听表单变化,定期将数据保存到本地存储或后端。
5. **如何处理大量产品数据的搜索性能问题?**
可以在后端实现搜索接口,或者使用前端虚拟滚动技术优化大型列表渲染。
6. **如何实现表单行的拖拽排序功能?**
可以使用iView的拖拽组件或第三方库如Sortable.js实现行拖拽功能。
阅读全文
相关推荐
















