业务一览画面:<template>
<el-table :data="tableData" style="width: 100%" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" />
<el-table-column label="产品名称" width="220">
<template #default="scope">
<div class="product-name-wrapper">
<el-link type="primary" :underline="false" @click="goToProductDetail(scope.row.id)">
<span>{{ scope.row.name }}</span>
</el-link>
</div>
</template>
</el-table-column>
<el-table-column property="categoryName" label="产品分类" width="120" />
<el-table-column property="price" label="产品单价" width="120" />
<el-table-column property="createAt" label="生产日期" width="250" />
<el-table-column property="price" label="单价" width="120" />
<el-table-column property="stock" label="库存" width="120" />
<el-table-column label="购买数量" width="180">
<template #default="scope">
<div>
<el-input-number v-model="scope.row.number" :min="0" :max="scope.row.stock" size="small"
controls-position="right" />
</div>
</template>
</el-table-column>
<el-table-column label="合计" width="120">
<template #default="scope">
¥{{ (scope.row.price * scope.row.number) }}
</template>
</el-table-column>
<el-table-column label="操作" width="180">
<template #default="scope">
<el-button size="small" @click="handleEdit(scope.$index, scope.row)">
编辑
</el-button>
<el-button size="small" type="danger" @click="handleDelete(scope.$index, scope.row)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<div class="footer">
<el-button type="primary" round @click="addToCart">加入购物车</el-button>
</div>
<div class="summary">
<span>总金额:</span>
<span>¥{{ (totalAmount) }}</span>
</div>
</template>
<script setup>
const router = useRouter();
const user = "aaa"
import productApi from '@/api/products'
import { ref, computed } from 'vue';
import { ElMessageBox, ElMessage } from 'element-plus'
const tableData = ref([]);
const goToProductDetail = (productId) => {
router.push({
path: `/home/test/${productId}`,
query: { type: 'view' }
})
}
// 编辑详情页
const handleEdit = (index, row) => {
router.push({
path: `/home/test/${row.id}`,
// 添加查询参数表示编辑模式
query: { type: 'edit' }
})
}
// 删除操作
const handleDelete = (index, row) => {
ElMessageBox.confirm(
'确定要删除该商品吗?',
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
).then(() => {
// 调用 API 删除商品
productApi.delete(row.id).then(res => {
// 删除成功后从前端移除
tableData.value.splice(index, 1)
ElMessage.success('删除成功')
}).catch(err => {
console.error('删除失败:', err)
ElMessage.error('删除失败,请重试')
})
}).catch(() => {
// 用户点击取消
ElMessage.info('已取消删除')
})
}
// 获取商品数据
function getProduct() {
productApi.getAll(user).then(res => {
tableData.value = res.data.map(item => ({
...item,
number: item.number || 0,
}));
});
}
// 创建一个响应式数组,用于存储选中的项
const allcheckbox = ref([])
function handleSelectionChange(selection) {
// 当选择变化时更新存储的选中项
allcheckbox.value = selection
}
// 计算总金额
const totalAmount = computed(() => {
return allcheckbox.value.reduce((sum, row) => {
const price = parseFloat(row.price) || 0;
const number = parseInt(row.number) || 0;
return sum + (price * number);
}, 0);
});
// 加入购物车
const addToCart = () => {
const selectedItems = allcheckbox.value;
if (!selectedItems.length) {
ElMessage.warning('请先选择要加入购物车的商品');
return;
}
selectedItems.forEach(item => {
const payload = {
name: item.name,
price: item.price,
category: item.category,
quantity: item.number,
user: user
};
productApi.addCarItem(payload)
.then(res => {
ElMessage.success(`${item.name} 加入购物车成功`);
})
.catch(error => {
console.error(`添加 ${item.name} 到购物车失败`, error);
ElMessage.error(`添加 ${item.name} 失败`);
});
});
getProduct();
// 刷新商品列表
allcheckbox.value = [];
};
onMounted(() => {
getProduct();
});
</script>
<style scoped>
.summary {
float: right;
margin-top: 55px;
margin-right: 50px;
}
.footer {
float: right;
margin-top: 50px;
margin-right: 280px;
}
</style> 商品详情画面:<template>
<el-form ref="ruleFormRef" style="margin-top: 20px;max-width: 600px" :model="ruleForm" :rules="rules"
label-width="auto" :disabled="pageMode === 'view'">
<el-form-item label="产品名称:" prop="name">
<el-input v-model="ruleForm.name" />
</el-form-item>
<el-form-item label="产品描述:" prop="description">
<el-input v-model="ruleForm.description" type="textarea"/>
</el-form-item>
<el-form-item label="产品分类:" prop="category" style="width: 50%;">
<el-select v-model="ruleForm.category" placeholder="请选择分类">
<el-option v-for="item in categorys" :label="item.name" :value="item.id" :key="item.id" />
</el-select>
</el-form-item>
<el-form-item label="产品单价:" prop="price">
<el-input v-model.number="ruleForm.price" type="number" style="width: 240px" />
</el-form-item>
<el-form-item label="产品库存:" prop="stock">
<el-input v-model.number="ruleForm.stock" type="number" style="width: 240px" />
</el-form-item>
<el-form-item label="生产厂家:" prop="manufacturer">
<el-input v-model="ruleForm.manufacturer" />
</el-form-item>
<el-form-item label="产地:" required>
<el-col :span="12">
<el-form-item prop="originProvince">
<el-select v-model="ruleForm.originProvince" placeholder="省" style="flex: 1; margin-right: 10px">
<el-option v-for="item in originProvinces" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item prop="originCity">
<el-select v-model="ruleForm.originCity" placeholder="市" style="flex: 1 "
:disabled="!ruleForm.originProvince">
<el-option v-for="item in originCitys" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
</el-col>
</el-form-item>
<el-form-item label="发货地:" required>
<el-col :span="12">
<el-form-item prop="shippingProvince">
<el-select v-model="ruleForm.shippingProvince" placeholder="省" style="flex: 1; margin-right: 10px">
<el-option v-for="item in originProvinces" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item prop="shippingCity">
<el-select v-model="ruleForm.shippingCity" placeholder="市" style="flex: 1"
:disabled="!ruleForm.shippingProvince">
<el-option v-for="item in shippingCities" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
</el-col>
</el-form-item>
<el-form-item label="生产日期" required>
<el-col :span="11">
<el-form-item prop="createAt">
<el-date-picker v-model="ruleForm.createAt" type="date" aria-label="Pick a date" placeholder="Pick a date"
style="width: 100%" />
</el-form-item>
</el-col>
</el-form-item>
<el-form-item>
<!-- 登录按钮:仅在默认模式下显示 -->
<el-button v-if="pageMode === 'default'" style="margin-left: 18px;" type="primary" @click="submitForm(ruleFormRef, ruleForm)">
登录
</el-button>
<!-- 更新按钮:仅在编辑模式下显示 -->
<el-button v-if="pageMode === 'edit'" style="margin-left: 30px;" type="primary" @click="updateForm(ruleFormRef)">
更新
</el-button>
<!-- 重置按钮:在编辑或默认模式下都显示 -->
<el-button v-if="pageMode === 'edit' || pageMode === 'default'" style="margin-left: 30px;" type="primary" @click="resetForm(ruleFormRef)">
重置
</el-button>
</el-form-item>
</el-form>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue';
import productApi from '@/api/products'
import placeApi from '@/api/place'
import { useRouter } from 'vue-router'
import { ElLoading, ElMessage } from 'element-plus'
const pageMode = ref('') // 可选值: 'view', 'edit', 'default'
const router = useRouter();
const ruleFormRef = ref()
const categorys = ref([])// 产品分类
const originProvinces = ref([]) // 省份数据
const originCitys = ref([]) // 城市数据
const shippingCities = ref([]) // 发货地城市数据
const ruleForm = reactive({
name: '',
description: '',
category: '',
price: '',
stock: '',
manufacturer: '',
originProvince: '',
originCity: '',
shippingProvince: '',
shippingCity: '',
createAt: '',
user: 'aaa',
delivery: false,
type: [],
})
const rules = reactive({
name: [
{ required: true, message: '请输入产品名称', trigger: 'blur' },
{ max: 10, message: '长度超过10位', trigger: 'blur' },
],
description: [
{ required: true, message: '请输入产品描述', trigger: 'blur' },
{ max: 200, message: '长度超过200位', trigger: 'blur' },
],
category: [
{
required: true, message: '请选择产品分类', trigger: 'change', },
],
price: [
{
required: true,
message: '请输入产品单价',
trigger: ['blur', 'change']
},
{
validator: (rule, value, callback) => {
if (value === null || value === '') {
callback(new Error('请输入产品单价'))
} else if (value <= 0) {
callback(new Error('单价必须大于 0'))
} else if (value > 10000) {
callback(new Error('单价不能大于 10000'))
} else {
callback()
}
},
trigger: ['blur', 'change']
}
],
stock: [
{
required: true,
message: '请输入产品库存',
trigger: ['blur', 'change']
},
{
validator: (rule, value, callback) => {
if (value === null || value === '') {
callback(new Error('请输入商品单价'))
} else if (value <= 0) {
callback(new Error('单价必须大于 0'))
} else if (value > 10000) {
callback(new Error('单价不能大于 10000'))
} else {
callback()
}
},
trigger: ['blur', 'change']
}
],
manufacturer: [
{ required: true, message: '请输入生产厂家', trigger: 'blur' },
],
originProvince: {
validator: (rule, value, callback) => {
if (!value) {
callback(new Error('请选择产地省份'))
} else {
callback()
}
},
trigger: 'change'
},
originCity:{
validator: (rule, value, callback) => {
if (!value) {
callback(new Error('请选择产地城市'))
} else {
callback()
}
},
trigger: 'change'
},
shippingProvince: {
validator: (rule, value, callback) => {
if (!value) {
callback(new Error('请选择发货省份'))
} else {
callback()
}
},
trigger: 'change'
},
shippingCity:{
validator: (rule, value, callback) => {
if (!value) {
callback(new Error('请选择发货城市'))
} else {
callback()
}
},
trigger: 'change'
},
createAt: [
{
type: 'date',
required: true,
message: '请输入日期',
trigger: 'change',
},
],
})
// 重置表单操作
const resetForm = (formEl) => {
if (!formEl) return
formEl.resetFields()
}
//产品分类
function getCategory() {
productApi.getCategory().then(res => {
categorys.value = res.data;
})
}
// 获取商品详情
function getProductDetail(productId) {
productApi.getById(productId).then(res => {
const data = res.data
// 先赋值整个对象
Object.assign(ruleForm, data)
}).catch(error => {
console.error('获取商品详情失败:', error);
});
}
// 获取省份
function getProvinces() {
placeApi.getProvince().then(res => {
originProvinces.value = res.data
}).catch(error => {
console.error('获取省份失败:', error)
})
}
// 监听产地省份变化,加载城市
watch(
() => ruleForm.originProvince,
(newVal) => {
if (newVal) {
placeApi.getCity(newVal).then(res => {
originCitys.value = res.data
}).catch(() => {
originCitys.value = []
})
} else {
// 如果省份为空,则清空城市列表和当前选中的城市字段
originCitys.value = []
ruleForm.originCity = ''
}
}
)
// 监听发货地省份变化,加载城市
watch(
() => ruleForm.shippingProvince,
(newVal) => {
if (newVal) {
placeApi.getCity(newVal).then(res => {
shippingCities.value = res.data
}).catch(() => {
shippingCities.value = []
})
} else {
// 如果省份为空,则清空城市列表和当前选中的城市字段
shippingCities.value = []
ruleForm.shippingCity = ''
}
}
)
// 更新操作
const updateForm = async (formEl) => {
if (!formEl) return
const loading = ElLoading.service({ fullscreen: true })
await formEl.validate((valid, fields) => {
if (valid) {
console.log('开始提交...')
// 调用 API 更新商品信息
productApi.update(ruleForm).then(res => {
ElMessage.success('更新成功')
router.push('/home/test1') // 可选跳转回列表页
}).catch(error => {
console.error('更新失败:', error)
ElMessage.error('更新失败,请重试')
}).finally(() => {
loading.close()
})
} else {
console.log('表单验证失败:', fields)
ElMessage.error('请检查输入内容')
loading.close()
return false
}
})
}
// 登录操作
const submitForm = async (formEl, ruleForm) => {
if (!formEl) return
await formEl.validate((valid, fields) => {
if (valid) {
console.log('开始提交商品...')
const loading = ElLoading.service({ fullscreen: true })
productApi.addProduct(ruleForm).then(res => {
ElMessage.success('登录成功')
router.push('/home/test1')
}).catch(error => {
console.error('登录失败:', error)
ElMessage.error('登录失败,请重试')
}).finally(() => {
loading.close()
})
} else {
console.log('表单验证失败:', fields)
ElMessage.error('请检查输入内容')
loading.close()
return false
}
})
}
onMounted(() => {
getCategory()
getProvinces()
// 获取当前路由信息
const route = router.currentRoute.value
// 判断页面是查看、编辑还是新增模式
const type = route.query.type
const productId = route.params.id || route.query.id
if (type === 'view') {
pageMode.value = 'view' // 查看模式:不显示按钮
} else if (type === 'edit') {
pageMode.value = 'edit' // 编辑模式:显示“更新”、“重置”
} else {
pageMode.value = 'default' // 默认模式:显示“登录”、“重置”
}
// 如果存在商品 ID,则调用接口获取商品详情并填充表单
if (productId) {
getProductDetail(productId)
}
})
</script>
<style>
.location-selectors {
display: flex;
justify-content: space-between;
width: 100%;
}
</style> 购物车画面:<template>
<el-table :data="tableData" style="width: 100%">
<el-table-column property="name" label="产品名称" width="220"/>
<el-table-column property="categoryName" label="产品分类" width="120" />
<el-table-column property="price" label="产品单价" width="120" />
<el-table-column property="createAt" label="生产日期" width="250" />
<el-table-column property="price" label="单价" width="120" />
<el-table-column property="quantity" label="购买数量" width="180">
<template #default="scope">
<div>
<el-input-number v-model="scope.row.number" :min="0" :max="scope.row.stock" size="small"
controls-position="right" />
</div>
</template>
</el-table-column>
<el-table-column label="合计" width="120">
<template #default="scope">
¥{{ (scope.row.price * scope.row.number) }}
</template>
</el-table-column>
<el-table-column label="操作" width="180">
<template #default="scope">
<el-button size="small" type="danger" @click="handleDelete(scope.$index, scope.row)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<div class="footer">
<el-button type="primary" round>结算</el-button>
</div>
<div class="summary">
<span>总金额:</span>
<span>¥{{ (totalAmount) }}</span>
</div>
</template>
<script setup>
const router = useRouter();
const user = "aaa"
import productApi from '@/api/products'
import { ref, computed } from 'vue';
import { ElMessageBox, ElMessage } from 'element-plus'
const tableData = ref([]);
// 删除操作
const handleDelete = (index, row) => {
ElMessageBox.confirm(
'确定要删除该商品吗?',
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
).then(() => {
// 调用 API 删除商品
productApi.deleteCar(row.id).then(res => {
// 删除成功后从前端移除
tableData.value.splice(index, 1)
ElMessage.success('删除成功')
}).catch(err => {
console.error('删除失败:', err)
ElMessage.error('删除失败,请重试')
})
}).catch(() => {
// 用户点击取消
ElMessage.info('已取消删除')
})
}
function getProduct() {
productApi.getCartAll(user).then(res => {
tableData.value = res.data.map(item => ({
...item,
number: item.quantity || 0
}));
});
}
// 计算总金额
const totalAmount = computed(() => {
return tableData.value.reduce((sum, row) => {
const price = parseFloat(row.price) || 0;
const number = parseInt(row.number) || 0;
return sum + (price * number);
}, 0);
});
onMounted(() => {
getProduct();
});
</script>
<style scoped>
.summary {
float: right;
margin-top: 55px;
margin-right: 50px;
}
.footer {
float: right;
margin-top: 50px;
margin-right: 510px;
}
</style> 现在我想将商品详情画面拆成像之前的4层组件结构,而购物车和商品一览画面保持不变,只将商品详情画面的form表单拆成自定义组件功能保持不变
最新发布