一套后台管理系统的入门级的增删改查(vue3组合式api+elemment-plus)

一、页面示意:

图一

图二

二、组件结构

  • 列表组件 :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"
    }
  ],
}                         
相关推荐
来恩100338 分钟前
jQuery选择器
前端·javascript·jquery
前端繁华如梦40 分钟前
树上挂苹果还是挂玻璃球?Three.js 程序化果实的完整实现指南
前端·javascript
CDwenhuohuo1 小时前
优惠券组件直接用 uview plus
前端·javascript·vue.js
川冰ICE2 小时前
TypeScript装饰器与元编程实战
前端·javascript·typescript
AI砖家2 小时前
Vue3组件传参大全,各种传参方式的对比
前端·javascript·vue.js
希望永不加班2 小时前
var局部变量类型推断的利弊
java·服务器·前端·javascript·html
threelab2 小时前
Three.js 3D 地图可视化 | 三维可视化 / AI 提示词
前端·javascript·人工智能·3d·着色器
爱怪笑的小杰杰2 小时前
Leaflet 高性能大数据量图圆:彻底解决缩放/拖拽偏移问题
大数据·前端·vue.js·贴图
失眠的咕噜3 小时前
PDA 安卓设备上传多张图片
android·前端·javascript
掰头战士3 小时前
深入了解JS原型及原型继承链机制
javascript