一、页面示意:
图一
图二
二、组件结构
- 列表组件 :index.vue,对应图一
- 添加组件:add.vue,对应图二,用抽屉效果
- 编辑组件:edit.vue,和添加组件的效果一个。
三、代码
1、列表组件: index.vue
<template>
<h1>增删改查案例</h1>
<div>
<!--查询条件 -->
<el-form :model="searchData" label-width="40px" :inline="true">
<el-form-item label="书号">
<el-input v-model="searchData.id" />
</el-form-item>
<el-form-item label="书名">
<el-input v-model="searchData.name" />
</el-form-item>
<el-form-item label="作者">
<el-input v-model="searchData.author" />
</el-form-item>
<el-form-item label="价格">
<el-input v-model="searchData.minprice" />
</el-form-item>
<el-form-item label="-->">
<el-input v-model="searchData.maxprice" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="searchFn">查询</el-button>
<el-button @click="showAdd">添加</el-button>
</el-form-item>
</el-form>
</div>
<div>
<!-- 书籍列表 -->
<el-table :data="bookData" stripe style="width: 100%">
<el-table-column prop="id" label="书号" width="180" />
<el-table-column prop="name" label="书名" width="180" />
<el-table-column prop="author" label="作者" />
<el-table-column prop="price" label="价格" />
<el-table-column label="类型" >
<template #default="scope">
<span>{{typeObj[scope.row.type]}}</span>
</template>
</el-table-column>
<el-table-column label="图片">
<template #default="scope">
<img :src="scope.row.img" style="width: 100px;height: 100px" />
</template>
</el-table-column>
<el-table-column fixed="right" label="操作" width="120">
<template #default="scope">
<el-button link type="primary" size="small" @click="showEdit(scope.row.id)">
编辑
</el-button>
<el-button link type="primary" size="small" @click.prevent="deleteBook(scope.row.id)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<el-drawer v-model="drawer" title="添加书籍" direction="rtl" :before-close="handleClose">
<AddBook @close="closeFn" @ok="okFn"></AddBook>
</el-drawer>
<el-drawer v-model="drawerEdit" title="修改书籍" direction="rtl" >
<EditBook :id="editId" @ok="editOkFn" @close="editCloseFn" ></EditBook>
</el-drawer>
</div>
</template>
<script setup>
import { reactive, onMounted,ref } from "vue";
import { getBooksApi, getBooksByCondationApi, deleteBookApi } from "@/api/crud";
import AddBook from "./Add.vue";
import EditBook from "./edit.vue";
const typeObj = {
"new":"最新",
"hot":"热卖"
}
// 书籍数据
let bookData = reactive([]);
//一、 获取书籍信息
const getBooks = () => {
getBooksApi()
.then(res => {
if (res.data) {
console.log("bookData前", bookData);
// bookData = res.data;
bookData.length = 0;
bookData.push(...res.data);
console.log("bookData后", bookData);
}
})
.catch(err => {
console.log("获取数据出错", err);
})
}
onMounted(() => {
getBooks();
})
// 二、查询书籍
const searchData = reactive({
"id": "",
"name": "",
"author": "",
"minprice": "",
"maxprice": ""
})
const searchFn = () => {
const temp = {};
for (const key in searchData) {
if (searchData[key] !== "") {
temp[key] = searchData[key];
}
}
if (temp["minprice"]) {
delete temp["minprice"];
temp["price_gte"] = searchData.minprice;
}
if (temp["maxprice"]) {
delete temp["maxprice"];
temp["price_lte"] = searchData.maxprice;
}
getBooksByCondationApi(temp)
.then(res => {
if (res.data) {
bookData.length = 0;
bookData.push(...res.data);
}
})
}
// 三、删除书籍
const deleteBook = (id) => {
deleteBookApi(id)
.then(res => {
console.log("删除的返回res", res);
// 删除成功后,重新获取数据
getBooks();
})
}
// 四、添加
const drawer = ref(false)
const handleClose = (done) => {
ElMessageBox.confirm('Are you sure you want to close this?')
.then(() => {
done()
})
.catch(() => {
// catch error
})
}
// 显示添加页面
const showAdd=()=>{
drawer.value=true;
}
const closeFn=()=>{
drawer.value = false;
}
const okFn=()=>{
closeFn();
getBooks();
}
// 五、修改
const drawerEdit = ref(false);
const editId = ref("");//修改的书籍的id
// 显示修改页面
const showEdit=(id)=>{
editId.value=id;
drawerEdit.value=true;
}
const editCloseFn=()=>{
editId.value ="";
drawerEdit.value = false;
}
const editOkFn=()=>{
editCloseFn();
getBooks();
}
</script>
<style lang="css" scoped></style>
2、添加组件:add.vue
<template>
<el-form ref="formRef" :model="newBook" label-width="120px" :rules="rules">
<el-form-item label="书号" prop="id">
<el-input v-model="newBook.id" />
</el-form-item>
<el-form-item label="书名" prop="name">
<el-input v-model="newBook.name" />
</el-form-item>
<el-form-item label="作者" prop="author">
<el-input v-model="newBook.author" />
</el-form-item>
<el-form-item label="价格" prop="price">
<el-input v-model="newBook.price" />
</el-form-item>
<el-form-item label="图片" prop="img">
<el-input v-model="newBook.img" />
</el-form-item>
<el-form-item label="类型" prop="type">
<!-- <el-input v-model="newBook.type" /> -->
<el-select v-model="newBook.type" class="m-2" placeholder="请选择书籍类型" size="large">
<!-- <el-option v-for="item in booktypes" :key="item.id" :label="item.name" :value="item.value" /> -->
<el-option label="最新" value="new" />
<el-option label="热卖" value="hot" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="addBookFn(formRef)">添加</el-button>
<el-button @click="cancelFn">取消</el-button>
</el-form-item>
</el-form>
</template>
<script setup>
import { reactive, ref } from 'vue';
import { addBookApi,getBookTypeApi } from "@/api/crud"
const emit = defineEmits(['close', "ok"]);
const booktypes = reactive([]);
const getBookType = () => {
getBookTypeApi()
.then(res=>{
if(res.data){
booktypes.length=0;
booktypes.push(...res.data);
}
})
}
getBookType();
const formRef = ref();
// 定义添加的书籍
const newBook = reactive({
"id": "",
"name": "",
"author": "",
"price": 0,
"img": "",
"type": ""
})
// 表单验证的规则
const rules = reactive({
"id": [
{ required: true, message: '请输入书号', trigger: 'blur' }
],
"name": [
{ required: true, message: '请输入书名', trigger: 'blur' }
],
"author": [
{ required: true, message: '请输入作者', trigger: 'blur' }
],
"price": [
{ required: true, message: '请输入价格', trigger: 'blur' }
],
"img": [
{ required: true, message: '请输入图片地址', trigger: 'blur' }
],
"type": [
{ required: true, message: '请输入类型', trigger: 'blur' }
],
})
const addBookFn = (formEl) => {
if (!formEl) {
return;
}
console.log("newBook",newBook);
formEl.validate((isOk) => {
if (isOk) {
addBookApi(newBook)
.then(res => {
console.log(res);
if (res.data) {
// 添加成功
alert("添加成功!");
// 关闭弹窗
emit("ok");
}
})
.catch(err => {
})
}
})
}
const cancelFn = () => {
emit('close');
}
</script>
3、编辑组件:edit.vue
<template>
<el-form ref="formRef" :model="editBook" label-width="120px" :rules="rules">
<el-form-item label="书号" prop="id">
<el-input v-model="editBook.id" />
</el-form-item>
<el-form-item label="书名" prop="name">
<el-input v-model="editBook.name" />
</el-form-item>
<el-form-item label="作者" prop="author">
<el-input v-model="editBook.author" />
</el-form-item>
<el-form-item label="价格" prop="price">
<el-input v-model="editBook.price" />
</el-form-item>
<el-form-item label="图片" prop="img">
<el-input v-model="editBook.img" />
</el-form-item>
<el-form-item label="类型" prop="type">
<el-input v-model="editBook.type" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="editBookFn(formRef)">修改</el-button>
<el-button @click="cancelFn">取消</el-button>
</el-form-item>
</el-form>
</template>
<script setup>
import { reactive, ref,watch } from 'vue';
import { getBookDetailApi,editBookApi } from '@/api/crud';
const props = defineProps(["id"]);
const formRef = ref();
// 定义修改的书籍
const editBook = reactive({
"id": "",
"name": "",
"author": "",
"price": 0,
"img": "",
"type": ""
})
// 获取当前修改的书籍信息
const getBookDetail = async () => {
try {
const res = await getBookDetailApi(props.id);
// editBook = res.data;
for(let key in editBook){
editBook[key] = res.data[key];
}
} catch (err) {
console.log("服务器出错",err);
}
}
console.log("props",props);
watch(props,()=>{
console.log("props.id变了");
getBookDetail();
},{deep:true,immediate:true})
const emit = defineEmits(['close', "ok"]);
// 表单验证的规则
const rules = reactive({
"id": [
{ required: true, message: '请输入书号', trigger: 'blur' }
],
"name": [
{ required: true, message: '请输入书名', trigger: 'blur' }
],
"author": [
{ required: true, message: '请输入作者', trigger: 'blur' }
],
"price": [
{ required: true, message: '请输入价格', trigger: 'blur' }
],
"img": [
{ required: true, message: '请输入图片地址', trigger: 'blur' }
],
"type": [
{ required: true, message: '请输入类型', trigger: 'blur' }
],
})
const editBookFn = (formEl) => {
if (!formEl) {
return;
}
formEl.validate((isOk) => {
if (isOk) {
editBookApi(props.id,editBook)
.then(res => {
console.log(res);
if (res.data) {
alert("修改成功!");
// 关闭弹窗
emit("ok");
}
})
.catch(err => {
})
}
})
}
const cancelFn = () => {
emit('close');
}
</script>
4、补充【api和axios封装】:api/crud.js;utils/serviceMock.js
api/crud.js
import service from "@/utils/serviceMock.js";
// 获取所有书籍
export const getBooksApi = ()=>service.get('/books');
// 根据编号获取书籍详情
export const getBookDetailApi = (id)=>service.get('/books/'+id);
// 查询书籍
export const getBooksByCondationApi = (params)=>service.get('/books',{params});
// 删除书籍
export const deleteBookApi = (id)=>service.delete('/books/'+id);
// 添加书籍
export const addBookApi = (data)=>service.post('/books',data);
// 修改书籍
export const editBookApi = (id,data)=>service.put('/books/'+id,data);
// 获取书籍类型
export const getBookTypeApi = ()=>service.get('/bookType');
utils/serviceMock.js
import axios from 'axios';
import store from '@/store';
const service = axios.create({
baseURL:"http://localhost:3000"
})
// 请求拦截器:所有请求的公共业务
service.interceptors.request.use(config=>{
store.commit("showLoading");
return config;
})
// 响应拦截器
service.interceptors.response.use((res)=>{
store.commit("hideLoading");
// loading.close();
return res;
})
export default service;
5、mock数据,用json-server
{
"books": [
{
"id": "878912",
"name": "水浒1",
"author": "施耐庵",
"price": 51.5,
"img": "/src/assets/imgs/01.jpg",
"type": "hot"
},
{
"id": "878913",
"name": "红楼梦",
"author": "曹雪芹2",
"price": 51.8,
"img": "/src/assets/imgs/01.jpg",
"type": "hot"
},
{
"id": "878917",
"name": "论语1",
"author": "王锐1",
"price": "5.38",
"img": "/imgs/img3.jpg",
"type": "new"
},
{
"id": "878918",
"name": "老子",
"author": "李家恒",
"price": 54.8,
"img": "/imgs/img4.jpg",
"type": "new"
},
{
"id": "878919",
"name": "孟子2",
"author": "李家恒",
"price": 54.8,
"img": "/images/img4.jpg",
"type": "new"
},
{
"id": "878920",
"name": "孟子3",
"author": "李家恒",
"price": 54.8,
"img": "/images/img4.jpg",
"type": "new"
},
{
"id": "878921",
"name": "孟子4",
"author": "李家恒",
"price": 54.8,
"img": "/images/img4.jpg",
"type": "new"
},
{
"id": "878922",
"name": "孟子5",
"author": "李家恒",
"price": 54.8,
"img": "/images/img4.jpg",
"type": "new"
},
{
"id": "878923",
"name": "孟子6",
"author": "李家恒",
"price": 54.8,
"img": "/images/img4.jpg",
"type": "new"
},
{
"id": "01008",
"name": "霸道总裁爱上我",
"author": "曹宇",
"price": "9.9",
"img": "/src/imgs/01.jpg",
"type": "hot"
},
{
"id": "01009",
"name": "西厢记",
"author": "赵蕊",
"price": "10.9",
"img": "/src/assets/imgs/01.jpg",
"type": "hot"
},
{
"id": "01010",
"name": "钢铁是怎样炼成的",
"author": "奥斯特洛夫斯基",
"price": "11.9",
"img": "/src/assets/imgs/02.jpg",
"type": "hot"
},
{
"id": "01011",
"name": "12",
"author": "22",
"price": 0,
"img": "1",
"type": "hot"
},
{
"id": "01012",
"name": "假如我是亿万富翁",
"author": "金莉",
"price": "1000",
"img": "/src/assets/imgs/01.jpg",
"type": "new"
}
],
}