springboot+vite 商品管理

SpringBoot + Vue3 +MySql5.7 +JDK8

一、 SpringBoot项目搭建

1 SpringBoot概述

1.1 SpringBoot 概念

SpringBoot提供了一种快速使用Spring的方式,基于约定优于配置的思想,可以让开发人员不必在配置与逻 辑业务之间进行思维的切换,全身心的投入到逻辑业务的代码编写中,从而大大提高了开发的效率,一定程度 上缩短了项目周期。2014 年4 月,Spring Boot 1.0.0 发布。Spring的顶级项目之一(https://spring.io)。

image-20240908153434481

1.2 Spring缺点

1.2.1 配置繁琐

虽然Spring的组件代码是轻量级的,但它的配置却是重量级的。一开始,Spring用XML配置,而且是很多 XML配置。Spring 2.5引入了基于注解的组件扫描,这消除了大量针对应用程序自身组件的显式XML配置。 Spring 3.0引入了基于Java的配置,这是一种类型安全的可重构配置方式,可以代替XML。 所有这些配置都代表了开发时的损耗。因为在思考Spring特性配置和解决业务问题之间需要进行思维切换,所 以编写配置挤占了编写应用程序逻辑的时间。和所有框架一样,Spring实用,但它要求的回报也不少。

1.2.2 依赖繁琐

项目的依赖管理也是一件耗时耗力的事情。在环境搭建时,需要分析要导入哪些库的坐标,而且还需要分析导 入与之有依赖关系的其他库的坐标,一旦选错了依赖的版本,随之而来的不兼容问题就会严重阻碍项目的开发进度。

1.3 SpringBoot 功能

1.3.1 自动配置

Spring Boot的自动配置是一个运行时(更准确地说,是应用程序启动时)的过程,考虑了众多因素,才决定 Spring配置应该用哪个,不该用哪个。该过程是SpringBoot自动完成的。

1.3.2 起步依赖

起步依赖本质上是一个Maven项目对象模型(Project Object Model,POM),定义了对其他库的传递依赖 ,这些东西加在一起即支持某项功能。 简单的说,起步依赖就是将具备某种功能的坐标打包到一起,并提供一些默认的功能。

1.3.3 辅助功能

提供了一些大型项目中常见的非功能性特性,如嵌入式服务器、安全、指标,健康检测、外部配置等。Spring Boot 并不是对 Spring 功能上的增强,而是提供了一种快速使用Spring 的方式。

2 Springboot快速入门

2.1 案例需求

搭建SpringBoot工程,定义HelloWorldController.hello()方法,返回”Hello SpringBoot!”。

2.2 案例实现步骤

1)创建项目

2)定义Controller

3)启动测试

2.2.1 创建Maven项目

image-20240908160314091

·

配置URL

https://start.aliyun.com

image-20240908160601168

image-20240908160718809

配置Maven仓库

image-20240908161537711

2.2.2 新建HelloController测试

image-20240908161348382

HelloController.java

@RestController
public class HelloController {
    @RequestMapping("/hello")
    public String hello(){
        return "Hello SpringBoot";
    }
}

启动测试

image-20240908162045690

浏览器测试

image-20240908162154560

3 数据库

3.1 新建数据库

image-20240908164037098

3.2 新建表

use hddata;

CREATE TABLE user (
    id INT AUTO_INCREMENT PRIMARY KEY COMMENT '用户ID,自增长',
    username VARCHAR(50) NOT NULL COMMENT '用户名',
    password VARCHAR(255) NOT NULL COMMENT '用户密码',
    name VARCHAR(100) NOT NULL COMMENT '姓名'
) COMMENT='用户信息表';

CREATE TABLE product (
    id INT AUTO_INCREMENT PRIMARY KEY COMMENT '商品ID,自增长',
    number VARCHAR(50) NOT NULL COMMENT '商品编号',
    name VARCHAR(100) NOT NULL COMMENT '商品名称',
    brand INT NOT NULL COMMENT '商品品牌'
) COMMENT='商品信息表';

4 项目实战

4.1 引入相关依赖

<!-- 引入MyBatis plus依赖 -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.5</version>
</dependency>

<!--mysql驱动依赖-->
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <version>8.0.33</version>
</dependency>

<!--lombok-->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.28</version>
</dependency>

<!--hutool工具类-->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.16</version>
</dependency>

<!--validation依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
    <version>3.1.0</version>
</dependency>

<!--pageHelper 分页插件-->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>1.4.7</version>
</dependency>

4.2 分层建包

image-20240908170839452

User类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    @TableId(type = IdType.AUTO)
    private int id; // 用户ID,自增长
    private String username; // 用户名
    private String password; // 用户密码
    private String name; // 姓名
}

4.3 数据库配置

#数据库配置
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/hddata?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC
    username: root
    password: fp

#mybatis-plus配置
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  mapper-locations: classpath*:/mybatis/*.xml
  type-aliases-package: com.hwadee.userproject.pojo

4.4 创建用户数据层

image-20240908193833037

UserMapper

public interface UserMapper extends BaseMapper<User> {
}

配置mapper包扫描

@SpringBootApplication
@MapperScan("com.hwadee.userproject.mapper")
public class UserProjectApplication {

    public static void main(String[] args) {
        SpringApplication.run(UserProjectApplication.class, args);
    }

}

mybatis-plus测试

@SpringBootTest
class UserProjectApplicationTests {

    @Autowired
    UserMapper userMapper;

    //新增用户
    @Test
    void contextLoads() {
        User user = new User();
        user.setUsername("admin");
        user.setPassword(DigestUtil.md5Hex("123456"));
        user.setName("管理员");
        userMapper.insert(user);
    }

    //查询所有用户
    @Test
    void contextLoads1() {
        List<User> users = userMapper.selectList(null);
        System.out.println("====用户数据====");
        users.forEach(user -> System.out.println(user));
    }

}

4.5 创建前后端交互对象

DataResult

public class DataResult<T> implements Serializable {
    private static final long serialVersionUID = 1L;
    private boolean success = true;
    private String message = "操作成功!";
    private Integer statusCode = 200;
    private T data;
    private long timestamp = System.currentTimeMillis();

    public DataResult() {
    }

    public DataResult(Boolean success, Integer code, String msg, T data) {
        this.success = success;
        this.statusCode = code;
        this.message = msg;
        this.data = data;
    }

    //返回成功
    public static DataResult success() {
        return new DataResult(true,200, "操作成功!", null);
    }

    //返回失败 500
    public static DataResult error(String message) {
        return new DataResult(false,HttpStatus.INTERNAL_SERVER_ERROR.value(), message, null);
    }

    //返回成功并携带数据
    public static <T> DataResult<T> success(T data) {
        return new DataResult<T>(true,200, "操作成功!", data);
    }

    public boolean getSuccess() {
        return this.success;
    }

    public void setSuccess(boolean success) {
        this.success = success;
    }

    public String getMessage() {
        return this.message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public Integer getStatusCode() {
        return this.statusCode;
    }

    public void setStatusCode(Integer statusCode) {
        this.statusCode = statusCode;
    }

    public T getData() {
        return this.data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public long getTimestamp() {
        return this.timestamp;
    }

    public void setTimestamp(long timestamp) {
        this.timestamp = timestamp;
    }
}

4.6 数据校验

User

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    @TableId(type = IdType.AUTO)
    private int id; // 用户ID,自增长

    @NotNull(message = "用户名不能为空")
    @Pattern(regexp = "^[\\x00-\\x7F]+$", message = "用户名不能包含汉字")
    @Size(min = 3, message = "用户名长度必须大于2个字符")
    private String username; // 用户名

    @NotNull(message = "密码不能为空")
    @Pattern(regexp = "^[\\x00-\\x7F]+$", message = "密码不能包含汉字")
    @Size(min = 3, message = "密码长度必须大于2个字符")
    private String password; // 用户密码
    
    private String name; // 姓名
}

4.7 全局控制异常

image-20240908215505236

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(Exception.class)
    public DataResult handleException(Exception e){
        e.printStackTrace();
        return DataResult.error(StringUtils.hasLength(e.getMessage()) ? e.getMessage() : "操作失败");
    }
}

4.8 用户注册

开启事务

@SpringBootApplication
@MapperScan("com.hwadee.userproject.mapper")
@EnableTransactionManagement//开启事务管理
public class UserProjectApplication {

    public static void main(String[] args) {
        SpringApplication.run(UserProjectApplication.class, args);
    }

}
@RestController
@RequestMapping("/user")
public class UserController {

    @Resource
    private UserMapper userMapper;

    @RequestMapping("/register")
    @Transactional(rollbackFor = Exception.class)  //事务控制
    public DataResult register(@Validated @RequestBody User user){
        if(!StringUtils.hasLength(user.getName())){
            return DataResult.error("姓名不能为空");
        }
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username",user.getUsername());
        User user1 = userMapper.selectOne(queryWrapper);
        if (Objects.isNull(user1)){
            user.setPassword(DigestUtil.md5Hex(user.getPassword()));
            userMapper.insert(user);
            //int i = 1/0; //测试事务
            return DataResult.success("注册成功");
        }else {
            return DataResult.error("用户名已存在");
        }
    }
}

PostMan测试

image-20240908221350942

4.9 用户登录

    /**
     * 用户登录
     * @param user
     * @return
     */
    @PostMapping("/login")
    public DataResult login(@Validated @RequestBody User user) {
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("username", user.getUsername());
        //查询用户
        User user1 = userMapper.selectOne(wrapper);
        if (Objects.nonNull(user1)) {
            //用户存在,校验密码
            if (user1.getPassword().equals(DigestUtil.md5Hex(user.getPassword()))) {
                return DataResult.success("登录成功");
            }
            return DataResult.error("密码错误");
        } else {
            return DataResult.error("用户名错误");
        }
    }

测试

image-20240908223743275

4.9 配置跨域

image-20240908221717890

CorsConfig

@Configuration
public class CorsConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**") // 所有接口
                .allowCredentials(true) // 是否发送 Cookie
                .allowedOriginPatterns("*") // 允许的源(Spring 5.3 及以上版本支持通配符)
                .allowedMethods("GET", "POST", "PUT", "DELETE") // 支持的方法
                .allowedHeaders("*")  // 允许的头部信息
                .exposedHeaders("Authorization", "Content-Type"); // 暴露的头部信息
    }
}

二、Vue项目搭建

必须先安装node

软件安装:nodejs16

https://nodejs.org/download/release/v16.20.0/

1 创建项目

1.1 安装vite环境

cmd进去项目目录,依次执行以下命令

#配置新淘宝镜像(防止出现下载缓慢)
npm config set registry https://registry.npmmirror.com
#查看当前的 npm 镜像地址
npm config get registry
#安装vite
npm init vite@latest
#Package name 就是项目的包名,它用于唯一标识你的项目,在项目的配置文件 package.json 中使用。

image-20240908225750119

image-20240908225810494

image-20240908225916859

1.2 启动项目

# 安装依赖
npm i
#启动项目
npm run dev

image-20240908230143683

image-20240908230322296

image-20240908230248904

1.3 项目结构介绍

image-20240908231752816

node_modules:

通过 npm install 下载安装的项目依赖包

public:

存放静态资源公共资源,不会被压缩合并

—3DModel:存放.glb3D模型

—favicon.ico:网站图标

—index.html:首页入口.html文件

src:

项目开发主要文件夹

—api

—assets:静态文件,存放图片

—components:存放组件

—store:与vuex相关

—styles:存放样式

—utils:工具类

—views:界面组件

—App.vue:根组件

—main.js:入口文件

—router.js:存放路由,实现界面跳转

.gitignore:

用来配置那些文件不归git管理

package.json:

项目配置和包管理文件(项目依赖和技术)

package-lock.json :

锁定依赖的版本信息,确保不同的开发者在不同的时间或环境中使用相同的版本。

README.md:

说明文档,主要看项目运行的命令

vue.config.js:

项目配置信息:跨域proxy代理

2 引入相关配置

配置@全局路径设置

vite.config.js

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'


//记得引入
import { fileURLToPath, URL } from 'node:url'

export default defineConfig({
  plugins: [vue()],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url))
    }
  }
})

2.1 引入静态文件

image-20240908234012770

main.js

//注释默认样式
//import './style.css'

//引入公共样式
import './assets/base.css'

2.2 引入Element-plus

#安装
npm install element-plus --save

main.js

//引入ElementPlus
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'

//在 mount 之前调用 use 方法
createApp(App).use(ElementPlus).mount('#app')

在Hellowold.vue中测试是否引入成功

<template>
  <div style="margin-top: 20px">
    <el-button>Default</el-button>
    <el-button type="primary">Primary</el-button>
    <el-button type="success">Success</el-button>
    <el-button type="info">Info</el-button>
    <el-button type="warning">Warning</el-button>
    <el-button type="danger">Danger</el-button>
  </div>
</template>

<script setup lang='js'>

</script>

<style lang='scss' scoped>

</style>

image-20240908232649035

2.3 引入router

#安装
npm install vue-router@latest

创建views/HomeView.vue

<template>
  <div style="margin-top: 20px">
     Home
  </div>
</template>

<script setup lang='js'>

</script>

<style lang='scss' scoped>

</style>

创建router/index.js

index.js

import { createRouter,createWebHistory } from 'vue-router'

//导入组件
import Home from '@/views/home/HomeView.vue'

//定义路由关系
const routes = [
  {
    path: '/home',
    name: 'home',
    component: Home
  }
]

//创建路由器
const router = createRouter({
  history: createWebHistory(),
  routes:routes
})

//导出路由
export default router

main.js

// @/router 如果不直接写后面的路径,默认找index.js
import router from '@/router/index.js' 

//在 mount 之前调用 use 方法
createApp(App).use(router).use(ElementPlus).mount('#app')

App.vue

<template>
   <!-- 全局配置组件 -->
  <el-config-provider :locale="zhCn">
    <RouterView />
  </el-config-provider>
</template>
<script setup>

import {ElConfigProvider} from "element-plus";
import {zhCn} from "element-plus/es/locale/index";
</script>

测试路由

image-20240908235823703

2.4 安装axios

npm install axios -S

创建utils/request.js

import axios from 'axios';
const URL =  "http://localhost:8080";
// create an axios instance
const service = axios.create({
    baseURL: URL, // url = base url + request url
    timeout: 10000, // request timeout
    withCredentials: true,
    crossDomain: true
})

export default service

创建后端api/getData.js

import request from "@/utils/request";

/**
 * 用户登录的api
 * @param data
 */
export  const doLogin=(data)=>{
    return request({
        url:'user/login', //请求url
        method :'post', //请求方式
        data //请求参数
    })
}

3 登录页面

新建/views/login/LoginView.vue

<template>
   <div class="login-container">
     <el-card class="login-card">
       <div class="login-header">
         <h3 class="login-title">欢迎登录</h3>
         <p class="login-subtitle">校园管理系统</p>
       </div>
       <el-form
           ref="loginFormRule"
           :model="loginForm"
           :rules="rules"
           label-width="auto"
           class="loin-form" >
         <el-form-item label="用户名" prop="username">
           <el-input v-model="loginForm.username"  autocomplete="off" placeholder="请输入用户名" />
         </el-form-item>
         <el-form-item label="密码" prop="password">
           <el-input v-model="loginForm.password" type="password" autocomplete="off" placeholder="请输入密码"/>
         </el-form-item>
         <el-form-item>
           <el-button class="login-button" type="primary" @click="submitForm()">
             登录
           </el-button>

         </el-form-item>
       </el-form>

     </el-card>

   </div>

</template>

<script  setup>
   import {ref} from "vue";
   import {ElMessage} from "element-plus";
   import {doLogin} from "@/api/getData.js";
   import router from "@/router/index.js";

   /**
    * 表单绑定的数据
    * @type {Ref<UnwrapRef<{password: string, username: string}>>}
    */
   const loginForm = ref({
     username:'',
     password:'',
   })

   const loginFormRule = ref(null);
   /**
    * 提交
    */
   const  submitForm =()=>{
     //提交前验证
     loginFormRule.value.validate((valid)=>{
       if(valid){
         login();
       }else{
         ElMessage.error("请输入用户名或密码")
       }

     })

     //跳转到首页
   }
   /**
    * 登录方法
    */
   const login = ()=>{
     
     doLogin(loginForm.value).then(({data})=>{
          if(data.statusCode == 200){
            router.push("/home")
          }else{
            ElMessage.error(data.message)
          }
        })

   }

   /**
    * 验证表单规则
    * @type {Ref<any>}
    */
    const rules = ref({
      username: [
        { required: true, message: '请输入用户名', trigger: 'blur' },
        { min: 2, message: '用户名长度最小为2个字符', trigger: 'blur' },
        { 
          pattern: /^[^\u4e00-\u9fa5]+$/, 
          message: '用户名不能包含汉字', 
          trigger: 'blur' 
        }
      ],
      password: [
        { required: true, message: '请输入密码', trigger: 'blur' },
        { min: 2, message: '密码长度最小为2个字符', trigger: 'blur' },
        { 
          pattern: /^[^\u4e00-\u9fa5]+$/, 
          message: '密码不能包含汉字', 
          trigger: 'blur' 
        }
      ]
});


</script>

<style scoped>
.login-container{
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
  background: linear-gradient(to right, #00b2ff, #00b2ff, #1550a8);
}
.login-card{
  width: 600px;
  padding: 40px;
  text-align: center;
  border-radius: 16px;
  box-shadow: 0 4px 8px rgb(0,0,0,0.1);
}
.login-header{
  margin-bottom: 40px;
}
.login-title{
  font-size: 30px;
  font-weight: bold;
  color: #333333;
  margin-bottom: 10px;
  letter-spacing: 10px;
}
.login-subtitle{
  font-size: 25px;
  color: #666666;
  letter-spacing: 20px;

}
.loin-form{
  width: 100%;

}
.login-button{
  width: 100%;
  font-size: 16px;
}

</style>

三、商品的增删改查

1 新建表

-- 使用自己的数据库
use hddata;

CREATE TABLE product (
    id INT AUTO_INCREMENT PRIMARY KEY COMMENT '商品ID,自增长',
    number VARCHAR(50) NOT NULL COMMENT '商品编号',
    name VARCHAR(100) NOT NULL COMMENT '商品名称',
    brand VARCHAR(50) NOT NULL COMMENT '商品品牌'
) COMMENT='商品信息表';

2 后端代码

2.1 新建实体类

image-20240909180024477

Product

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Product {
    @TableId(type = IdType.AUTO)
    private int id;

    @NotEmpty(message = "编号不能为空")
    private String number;

    @NotEmpty(message = "名称不能为空")
    private String name;

    @NotEmpty(message = "品牌不能为空")
    private String brand;

}

2.2 新建数据层接口

image-20240909174346270

ProductMapper

public interface ProductMapper extends BaseMapper<Product> {
}

2.3 新建控制层

image-20240909175308746

商品查询

ProductController

@RestController
@RequestMapping("/product")
public class ProductController {

    @Resource
    private ProductMapper productMapper;

    /**
     * 查询所有商品
     */
    @RequestMapping("/list")
    public DataResult list(@RequestParam(required = false) String name) {
        QueryWrapper<Product> queryWrapper = new QueryWrapper<>();
        if (StringUtils.hasLength(name)) {
            queryWrapper.like("name", name);
        }
        List<Product> brands = productMapper.selectList(queryWrapper);
        return DataResult.success(brands);
    }
}

浏览器测试

image-20240909174839992

商品添加

image-20240909175813368

    /**
     * 添加商品
     */
    @RequestMapping("/add")
    @Transactional
    public DataResult add(@Validated @RequestBody Product product) {
        productMapper.insert(product);
        return DataResult.success();
    }

测试

image-20240909175736133

编辑商品

先通过id回显数据

image-20240909213023239

/**
 * 通过主键查找
 * @param id
 * @return
 */
@RequestMapping("/getById")
public DataResult getById(@RequestParam String id){
    Product product = productMapper.selectById(id);
    return DataResult.success(product);
}

测试

image-20240909213355414

编辑商品

image-20240909213915752

    /**
     * 编辑商品
     */
    @RequestMapping("/edit")
    @Transactional
    public DataResult edit(@Validated @RequestBody Product product) {
        if(product.getId() == 0){
            return DataResult.error("id不能为空");
        }
        productMapper.updateById(product);
        return DataResult.success();
    }

测试

image-20240909214043812

删除商品

image-20240909214505301

/**
 * 删除商品
 * @param id
 * @return
 */
@RequestMapping("/delete")
@Transactional
public DataResult delete(@RequestParam String id){
    if(!StringUtils.hasLength(id)){
        return DataResult.error("id不能为空");
    }
    productMapper.deleteById(id);
    return DataResult.success();
}

image-20240909214448091

3 前端代码

3.1 商品列表

新建/views/product/ProductView.vue

image-20240909215641185

配置路由跳转

image-20240909215845738

import Product from '@/views/product/ProductView.vue'
//定义路由关系
const routes = [
  {
    path: '/',
    name: 'login',
    component: Login
  },
  {
    path: '/home',
    name: 'home',
    component: Home
  },
  {
    path: '/product',
    name: 'product',
    component: Product
  }
]

修改登录后路由跳转到商品界面

image-20240909220106976

点击登录测试

image-20240909220225558

ProductView.vue

<template>
  <el-card >
    <template #header>
      <div class="card-header">
        <span>商品管理</span>
        <el-button type="primary"  @click="visibleDrawer=true ;drawerTile='新增商品'">新增商品</el-button>
      </div>
    </template>
<!--    搜索表单-->
    <el-form :inline="true" :model="searchForm" class="demo-form-inline">
      <el-form-item label="名称">
        <el-input v-model="searchProductName" placeholder="请输入商品名称" clearable />
      </el-form-item>

      <el-form-item>
        <el-button type="primary" @click="productList">搜索</el-button>
      </el-form-item>
    </el-form>

<!--    商品显示列表-->
    <el-table :data="products" style="width: 100%">
      <el-table-column prop="number" label="编号" width="200" />
      <el-table-column prop="name" label="名称" width="200" />
      <el-table-column prop="brand" label="品牌" width="200" />

      <!-- <el-table-column fixed="right" label="操作" min-width="200">
        <template #default="{row}">
          <el-button  type="primary" size="small" @click="handleEdit(row)">
            编辑
          </el-button>
          <el-button  type="danger" size="small" @click="handleDelete(row)">
           删除</el-button>
        </template>
      </el-table-column> -->
    </el-table>
  </el-card>
</template>

<script setup lang='js'>
import {ref} from "vue";
import {getProductByNameList} from "@/api/getData.js";
import {ElMessage, ElMessageBox} from "element-plus";

/**
 * 定义搜索表单数据
 * @type {Ref<UnwrapRef<{}>>}
 */

const searchForm = ref({})
/**
 * 定义搜索字段
 * @type {Ref<any>}
 */
const searchProductName = ref('');


//列表数据
const products = ref([])

/**
 * 定义查询商品的方法
 */
 const productList = ()=>{
  getProductByNameList(searchProductName.value).then(({data})=>{
    if(data.statusCode==200){
      products.value = data.data
    }
  })
}

//查询数据
productList()


</script>

<style scoped>

  .card-header{
    display: flex;
    justify-content: space-between;
    align-items: center;
  }
  
  </style>

添加后端接口

image-20240909225722263

getData.js

/**
 * 查询商品列表的api
 * @param data
 */
export const getProductByNameList=(name)=>{

    return request({
        url:'product/list?name='+ name, //请求url
        method :'post' //请求方式
    })
}

测试

image-20240909225851645

3.2 新增商品

image-20240909233513514

  <!--    抽屉样式的编辑框-->

  <el-drawer
    v-model="visibleDrawer"
    :title="drawerTile"
    :direction="rt1"
    size="30%"
  >
    <el-form :model="productModel" label-width="auto" style="max-width: 600px">
      <el-form-item label="编号">
        <el-input v-model="productModel.number" placeholder="请输入编号" />
      </el-form-item>
      <el-form-item label="名称">
        <el-input v-model="productModel.name" placeholder="请输入名称" />
      </el-form-item>
      <el-form-item label="品牌">
        <el-input v-model="productModel.brand" placeholder="请输入品牌" />
      </el-form-item>

      <el-form-item>
        <el-button
          type="primary"
          @click="drawerTile == '新增商品' ? doAddProduct() : doUpdateProduct()">提交</el-button>
      </el-form-item>
    </el-form>
  </el-drawer>

定义相关数据变量

image-20240909233633054

//编辑框数据
const visibleDrawer = ref(false) //显示或者隐藏

const drawerTile = ref('') //新增或者编辑

const productModel = ref({
  id: 0,
  number: '',
  name: '',
  brand: ''
})

const clearData = ()=>{
  productModel.value = {
    id: 0,
    number: '',
    name: '',
    brand: ''
  }
}

//新增商品
const doAddProduct= ()=>{
    addProduct(productModel.value).then(({data})=>{
      if(data.statusCode==200){
        ElMessage.success("添加成功")
      }
      clearData()
      visibleDrawer.value=false;
      productList();
  })

}

//编辑商品
const doUpdateProduct = () => {

}

image-20240909233754293

<el-button
          type="primary"
          @click=" visibleDrawer = true;drawerTile = '新增商品';">新增商品</el-button>

添加后端接口

image-20240909233953961

image-20240909234029963

/**
 * 添加商品
 * @param data
 */
export const addProduct=(data)=>{
    return request({
        url:'product/add' ,//请求url
        method :'post', //请求方式
        data
    })
}

测试代码

image-20240909231736026

3.2 编辑商品

image-20240909234237699

<el-table-column fixed="right" label="操作" min-width="200">
        <template #default="{row}">
          <el-button  type="primary" size="small" @click="handleEdit(row)">
            编辑
          </el-button>
          <el-button  type="danger" size="small" @click="handleDelete(row)">
           删除</el-button>
        </template>
 </el-table-column>

添加编辑事件

image-20240909234712437

//编辑商品
const doUpdateProduct = () => {
  updateProduct(productModel.value).then((data)=>{
    if(data.status==200){
      ElMessage.success("编辑成功")
    }
    clearData()
    visibleDrawer.value=false;
    productList();
  })
}

/**
 * 打开编辑
 */
 const handleEdit = (row)=>{
  visibleDrawer.value=true
  drawerTile.value="编辑商品"
  // 数据赋值给 model
  productModel.value = row;
  productModel.value.id= row.id
}

添加后端接口

image-20240909234836476

image-20240909234948601

/**
 * 编辑商品
 */
export  const updateProduct=(data)=>{
    return request({
        url:'product/edit' ,//请求url
        method :'post', //请求方式
        data
    })
}

测试

image-20240910000305340

3.3 删除商品

image-20240910001155501

/**
 * 删除方法
 */

 const handleDelete = (row)=>{

    ElMessageBox.confirm(
        '您确定删除这条数据么',
        '提示',
        {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning',
        }
      ) 
        .then(() => {
          deleteProduct(row.id).then(({data})=>{
            if(data.statusCode==200){
              productList();
              ElMessage({
                type: 'success',
                message: '删除成功',
              })
            }
          })
        })
        .catch(() => {
          ElMessage({
            type: 'info',
            message: '取消删除',
          })
        })
}

添加后端接口

image-20240910000606866

image-20240910000847214

/**
 * 删除商品
 */
export  const deleteProduct=(id)=>{

    return request({
        url:'product/delete?id='+id, //请求url
        method :'post', //请求方式
    })
}

测试

image-20240910000925096

3.4 ProductView.vue 完整代码

<template>
  <el-card>
    <template #header>
      <div class="card-header">
        <span>商品管理</span>
        <el-button
          type="primary"
          @click=" visibleDrawer = true;drawerTile = '新增商品';">新增商品</el-button>
      </div>
    </template>
    <!--    搜索表单-->
    <el-form :inline="true" :model="searchForm" class="demo-form-inline">
      <el-form-item label="名称">
        <el-input
          v-model="searchProductName"
          placeholder="请输入商品名称"
          clearable
        />
      </el-form-item>

      <el-form-item>
        <el-button type="primary" @click="productList">搜索</el-button>
      </el-form-item>
    </el-form>

    <!--    商品显示列表-->
    <el-table :data="products" style="width: 100%">
      <el-table-column prop="number" label="编号" width="200" />
      <el-table-column prop="name" label="名称" width="200" />
      <el-table-column prop="brand" label="品牌" width="200" />

      <el-table-column fixed="right" label="操作" min-width="200">
        <template #default="{row}">
          <el-button  type="primary" size="small" @click="handleEdit(row)">
            编辑
          </el-button>
          <el-button  type="danger" size="small" @click="handleDelete(row)">
           删除</el-button>
        </template>
      </el-table-column>
    </el-table>
  </el-card>

  <!--    抽屉样式的编辑框-->

  <el-drawer
    v-model="visibleDrawer"
    :title="drawerTile"
    :direction="rt1"
    size="30%"
  >
    <el-form :model="productModel" label-width="auto" style="max-width: 600px">
      <el-form-item label="编号">
        <el-input v-model="productModel.number" placeholder="请输入编号" />
      </el-form-item>
      <el-form-item label="名称">
        <el-input v-model="productModel.name" placeholder="请输入名称" />
      </el-form-item>
      <el-form-item label="品牌">
        <el-input v-model="productModel.brand" placeholder="请输入品牌" />
      </el-form-item>

      <el-form-item>
        <el-button
          type="primary"
          @click="drawerTile == '新增商品' ? doAddProduct() : doUpdateProduct()">提交</el-button>
      </el-form-item>
    </el-form>
  </el-drawer>
</template>

<script setup lang="js">
import {ref} from "vue";
import {getProductByNameList,addProduct,updateProduct,deleteProduct} from "@/api/getData.js";
import {ElMessage, ElMessageBox} from "element-plus";

/**
 * 定义搜索表单数据
 * @type {Ref<UnwrapRef<{}>>}
 */

const searchForm = ref({})
/**
 * 定义搜索字段
 * @type {Ref<any>}
 */
const searchProductName = ref('');


//列表数据
const products = ref([])

/**
 * 定义查询商品的方法
 */
 const productList = ()=>{
  getProductByNameList(searchProductName.value).then(({data})=>{
    if(data.statusCode==200){
      products.value = data.data
    }
  })
}

//查询数据
productList()

//编辑框数据
const visibleDrawer = ref(false) //显示或者隐藏

const drawerTile = ref('') //新增或者编辑

const productModel = ref({
  id: 0,
  number: '',
  name: '',
  brand: ''
})

const clearData = ()=>{
  productModel.value = {
    id: 0,
    number: '',
    name: '',
    brand: ''
  }
}

//新增商品
const doAddProduct= ()=>{
    addProduct(productModel.value).then(({data})=>{
      if(data.statusCode==200){
        ElMessage.success("添加成功")
      }
      clearData()
      visibleDrawer.value=false;
      productList();
  })

}

//编辑商品
const doUpdateProduct = () => {
  updateProduct(productModel.value).then((data)=>{
    if(data.status==200){
      ElMessage.success("编辑成功")
    }
    clearData()
    visibleDrawer.value=false;
    productList();
  })
}

/**
 * 打开编辑
 */
 const handleEdit = (row)=>{
  visibleDrawer.value=true
  drawerTile.value="编辑商品"
  // 数据赋值给 model
  productModel.value = row;
  productModel.value.id= row.id
}

/**
 * 删除方法
 */

 const handleDelete = (row)=>{

    ElMessageBox.confirm(
        '您确定删除这条数据么',
        '提示',
        {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning',
        }
      ) 
        .then(() => {
          deleteProduct(row.id).then(({data})=>{
            if(data.statusCode==200){
              productList();
              ElMessage({
                type: 'success',
                message: '删除成功',
              })
            }
          })
        })
        .catch(() => {
          ElMessage({
            type: 'info',
            message: '取消删除',
          })
        })
}

</script>

<style scoped>
.card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
</style>

posted @ 2024-09-22 11:07  千夜ん  阅读(15)  评论(0编辑  收藏  举报