前端vue+后端ssm项目
下载地址:
前端:https://download.csdn.net/download/2401_83418369/90649449
后端: https://download.csdn.net/download/2401_83418369/90649441
一、项目基础环境搭建
1、新建Maven项目
2、创建目录,结构如下:
3、配置pom.xml文件引入依赖:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.study</groupId><artifactId>ssm</artifactId><packaging>war</packaging><version>1.0-SNAPSHOT</version><name>ssm Maven Webapp</name><url>http://maven.apache.org</url><dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>3.8.1</version><scope>test</scope></dependency><!-- 引入Springmvc框架--><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.3.8</version></dependency><!-- 引入Spring-jdbc,支持事务相关--><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.3.8</version></dependency><!-- 引入Spring aspect 切面编程需要的库--><dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>5.3.8</version></dependency><!-- 引入Mybatis库--><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.7</version></dependency><!-- 引入druid数据库连接池--><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.6</version></dependency><!-- 引入MySQL驱动,这里根据自己的MySQL版本,有5.0也有8.0的,具体情况具体分析--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.33</version></dependency></dependencies><build><finalName>ssm</finalName></build>
</project>
4、配置Tomcat环境:
具体的步骤看:tomcat的安装与配置(包含在idea中配置tomcat)_2024.1.4idea配置tomcat-CSDN博客
启动Tomcat测试一下:
5、项目全局配置web.xml文件
<!DOCTYPE web-app PUBLIC"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN""http://java.sun.com/dtd/web-app_2_3.dtd" ><web-app><display-name>Archetype Created Web Application</display-name><context-param><param-name>contextConfigLocation</param-name><param-value>classpath:applicationContext.xml</param-value></context-param>
<!--
1、ContextLoaderListener监听器作用是启动web容器时,自动装配ApplicationContext的配置信息
2、它实现了ServletContextListener接口,在web.xml配置该监听器,启动容器时,会默认执行它实现的方法--><listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener><!-- 使用Springmvc的处理中文乱码的过滤器--><filter><filter-name>CharacterEncodingFilter</filter-name><filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class><init-param><!--初始化值encoding,设置编码方式--><param-name>encoding</param-name><param-value>utf-8</param-value></init-param><init-param><param-name>forceRequestEncoding</param-name><param-value>true</param-value></init-param><init-param><param-name>forceResponseEncoding</param-name><param-value>true</param-value></init-param></filter><filter-mapping><filter-name>CharacterEncodingFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping><!-- 配置HiddenHttpMethodFilter过滤器,用于将post请求转化为put或delete请求--><filter><filter-name>hiddenHttpMethodFilter</filter-name><filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class></filter><filter-mapping><filter-name>hiddenHttpMethodFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping><!-- 配置springmvc分配器DispatcherServlet--><servlet><servlet-name>springDispatcherServlet</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!-- 配置要解析的Springmvc配置文件:这里的细节:如果配置文件的名字是分配器的名字后面加上 -servlet 并且把这个文件放在web.xml文件一样的位置,那么web.xml文件就不需要再配置这个contextConfigLocation 参数--><!-- <init-param>--><!-- <param-name>contextConfigLocation</param-name>--><!-- <param-value>classpath:WEB-INF/springDispatcherServlet-servlet.xml</param-value>--><!-- </init-param>--><!-- 设置Servlet的优先级,数字越小优先级越高--><load-on-startup>1</load-on-startup></servlet><!-- 配置URL映射地址--><servlet-mapping><servlet-name>springDispatcherServlet</servlet-name><url-pattern>/</url-pattern></servlet-mapping></web-app>
新建Spring配置文件
如果有报错,将配置文件的提示改为语法检查
包的结构 :
6、Springmvc配置文件
如果Springmvc配置文件的名字是分配器的名字后面加上 -servlet 并且把这个文件放在web.xml文件一样的位置,那么web.xml文件就不需要再配置这个contextConfigLocation 参数
springDispatcherServlet-servlet.xml配置文件的信息:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:mvc="http://www.springframework.org/schema/mvc"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd"><!--use-default-filters="false" 禁用默认过滤器规则Springmvc只是接管控制层,对于其他的dao,Service层不管context:include-filter 只扫描控制器--><context:component-scan base-package="com.hspedu.furn"use-default-filters="false"><context:include-filter type="annotation"expression="org.springframework.stereotype.Controller"/></context:component-scan><!-- 配置视图解析器--><bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"><!-- 配置前缀和后缀--><property name="prefix" value="/WEB-INF/views/"/><property name="suffix" value=".html"/></bean><!-- 能支持 SpringMVC 高级功能,比如 JSR303 校验,映射动态请求 --><mvc:annotation-driven></mvc:annotation-driven><!-- 将 SpringMVC 不能处理的请求交给 Tomcat, 比如请求 css,js 等 --><mvc:default-servlet-handler/></beans>
测试一下:创建一个ControllerTest类
package com.hspedu.furn.controller;import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;/*** 这是一个测试类,测试浏览器的请求是否能正确被解析和响应*/
@Controller
public class ControllerTest {@RequestMapping("/hi")public String hi(){System.out.println("hi方法被调用。。。");return "hi";}
}
在创建一个返回页面: (注意这个页面是根据自己的视图解析器的前后缀匹配来决定的)
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>hi</title>
</head>
<body>
<h1>hi,成功!</h1>
</body>
</html>
启动Tomcat后访问得到:
7、配置Spring和Mybatis并完成整合
先在pom.xml文件中引入Mybatis整合Spring的适配包
<!-- 引入Mybatis整合Spring的适配包--><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>2.0.6</version></dependency>
新建一个jdbc.properties文件
配置jdbc.propertis文件 (注意MySQL用户名和密码必须是自己的,还有数据库目前还没有创建,后面创建好后再修改)
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.user=root
jdbc.pwd=root
jdbc.url=jdbc:mysql://localhost:3306/XXX?useSSL=true&useUnicode=true&characterEncoding=UTF-8
在applicationContext.xml文件中进行配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- Spring的配置文件:主要配置和业务逻辑有关的,比如数据源,事务控制等-->
<!-- 配置扫描包不包含Controller层,因为Controller层由Springmvc扫描--><context:component-scan base-package="com.hspedu.furn"><context:exclude-filter type="annotation"expression="org.springframework.stereotype.Controller"/></context:component-scan><!-- 配置数据库连接池,引入外部的jdbc.properties文件--><context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 配置数据源对象--><bean class="com.alibaba.druid.pool.DruidDataSource" id="pooledDataSource"><property name="username" value="${jdbc.user}"/><property name="password" value="${jdbc.pwd}"/><property name="driverClassName" value="${jdbc.driver}"/><property name="url" value="${jdbc.url}"/></bean><!-- 配置Mybatis和Spring整合1、在项目中引入Mybatis整合Spring的适配包
--><bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactory">
<!-- 指定Mybatis全局配置文件--><property name="configLocation" value="classpath:mybatis-config.xml"/>
<!-- 指定数据源--><property name="dataSource" ref="pooledDataSource"/>
<!-- 指定Mybatis的Mapper文件,在开发中通常放在类路径中,这里没有创建对应的xml文件会爆红--><property name="mapperLocations" value="classpath:mapper/*.xml"/></bean><!--配置扫描器,将Mybatis接口的实现加入到ioc容器-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 1、扫描所有的dao接口的实现,加入到ioc容器-->
<!-- 2、这里dao接口就是Mapper接口--><property name="basePackage" value="com.hspedu.furn.dao"/>
</bean><!--配置事务管理器对象
要配置数据源属性,这样指定该事务管理器 是对哪个数据源进行事务控制-->
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager"><property name="dataSource" ref="pooledDataSource"/>
</bean><!--
之前启动事务基于注解声明式事务管理,现在采用是下面一种xml配置+切入点表达式<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>--><aop:config>
<!-- 切入点表达式--><aop:pointcut id="txPoint" expression="execution(* com.hspedu.furn.service..*(..))"/><!-- 配置事务增强,使用txAdvice指定规则对txPoint进行切入--><aop:advisor advice-ref="txAdvice" pointcut-ref="txPoint"/></aop:config><!-- 配置事务增强(指定事务规则),也就是指定事务如何切入-->
<tx:advice id="txAdvice"><tx:attributes>
<!-- 代表所有方法都是事务方法--><tx:method name="*"/>
<!-- 以get开始的所有方法,我们认为是只读,进行调优--><tx:method name="get*" read-only="true"/></tx:attributes>
</tx:advice></beans>
创建mybatis-config.xml文件 和一个mapper目录
mybatis-config.xml文件内容如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration></configuration>
配置完成后进行测试
新建Test的目录
package com.hspedu.test;import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class t1 {@Testpublic void test(){ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");Object bean = ioc.getBean("sqlSessionFactory");System.out.println(bean);Object bean1 = ioc.getBean("transactionManager");System.out.println(bean1);Object bean2 = ioc.getBean("pooledDataSource");System.out.println(bean2);}
}
如果没有报错,那么就是配置成功的
8、创建表,使用逆向工程生成Bean、XxxMapper和XxxMappe.xml
添加一个数据库和数据表
create database furn_ssm;
use furn_ssm;
create table furn(
id int(11) primary key auto_increment,
name varchar(64) not null,
maker varchar(64) not null,
price decimal(11,2) not null,
sales int(11) not null,
stock int(11) not null,
img_path varchar(256) not null
);INSERT INTO furn(`id` , `name` , `maker` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , '北欧风格小桌子' , '熊猫家居' , 180 , 666 , 7 , 'assets/images/product-image/6.jpg');INSERT INTO furn(`id` , `name` , `maker` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , '简约风格小椅子' , '熊猫家居' , 180 , 666 , 7 , 'assets/images/product-image/4.jpg');INSERT INTO furn(`id` , `name` , `maker` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , '典雅风格小台灯' , '蚂蚁家居' , 180 , 666 , 7 , 'assets/images/product-image/14.jpg');INSERT INTO furn(`id` , `name` , `maker` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , '温馨风格盆景架' , '蚂蚁家居' , 180 , 666 , 7 , 'assets/images/product-image/16.jpg');
创建后 可以修改对应的jdbc.properties配置信息:主要是将数据库的名称改为furn_ssm
在mybatis-config.xml配置文件配置javaBean的别名
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 配置别名-->
<typeAliases>
<!-- 如果一个包下面有很多类名,那么可以通过配置package来配置别名,他会将包下面的类名作为别名--><package name="com.hspedu.furn.bean"/>
</typeAliases>
</configuration>
在pom.xml文件中引入逆向工程依赖包 (一定要记得刷新Maven)
<!-- 引入mybatis逆向工程依赖包--><dependency><groupId>org.mybatis.generator</groupId><artifactId>mybatis-generator-core</artifactId><version>1.4.0</version></dependency>
逆向工程配置文件看下面 :
MyBatis Generator Core – MyBatis Generator XML Configuration File Reference
直接在项目下面新建一个xml文件用于放置逆向工程的配置信息
配置信息如下:(注意一些MySQL的名字和密码必须是自己的,还有一些工程名是自定义的)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfigurationPUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN""http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd"><generatorConfiguration><context id="DB2Tables" targetRuntime="MyBatis3"><!-- 生成没有注释的Bean--><commentGenerator><property name="suppressAllComments" value="true"/></commentGenerator><!-- 配置数据库连接信息--><jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"connectionURL="jdbc:mysql://localhost:3306/furn_ssm?characterEncoding=UTF-8"userId="root"password="root"></jdbcConnection><javaTypeResolver ><property name="forceBigDecimals" value="false" /></javaTypeResolver><!-- 指定javaBean生成的位置--><javaModelGenerator targetPackage="com.hspedu.furn.bean" targetProject=".\src\main\java"><property name="enableSubPackages" value="true" /><property name="trimStrings" value="true" /></javaModelGenerator><!-- 指定Sql映射文件的生成位置--><sqlMapGenerator targetPackage="mapper" targetProject=".\src\main\resources"><property name="enableSubPackages" value="true" /></sqlMapGenerator><!-- 指定dao接口生成的位置,也就是mapper接口--><javaClientGenerator type="XMLMAPPER" targetPackage="com.hspedu.furn.dao" targetProject=".\src\main\java"><property name="enableSubPackages" value="true" /></javaClientGenerator><!-- 指定要逆向生成的表和生成策略 --><table tableName="furn" domainObjectName="Furn"></table></context>
</generatorConfiguration>
测试逆向工程代码参考文档:
MyBatis Generator Core – Running MyBatis Generator With Java
创建逆向工程的测试类 :
package com.hspedu.furn;
import org.junit.Test;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.internal.DefaultShellCallback;
import java.io.File;
import java.util.ArrayList;
import java.util.List;public class MBGTest {@Testpublic void mbgTest() throws Exception {List<String> warnings = new ArrayList<String>();boolean overwrite = true;File configFile = new File("mbg.xml");ConfigurationParser cp = new ConfigurationParser(warnings);Configuration config = cp.parseConfiguration(configFile);DefaultShellCallback callback = new DefaultShellCallback(overwrite);MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);myBatisGenerator.generate(null);}
}
测试结果:(没有输出结果就是最好的结果)
逆向工程自动创建好了 Bean、XxxMapper和XxxMappe.xml
将之前注销的部分取消注销
测试furnMapper接口的方法
@Testpublic void insertTest(){ApplicationContext ioc =new ClassPathXmlApplicationContext("applicationContext.xml");System.out.println(ioc);}
当获取ioc容器时输出的结果抛出了异常
我的MySQL驱动是8.0,这里的异常是说已存在了result Maps,我查看了FurnMapper.xml文件发现了有重复的部分(基本上有一半重复)
解决该bug:(查了很多资料)
原因:多库/多Schema导致重复生成,当数据库存在多个 Schema 或 Catalog 时,MyBatis Generator 可能无法区分具体表来源,导致重复生成代码。
总结:MyBatis Generator 生成 Mapper.xml 重复代码的解决方案
方法1:JDBC 连接添加 nullCatalogMeansCurrent
属性:强制 MyBatis Generator 将未指定 Catalog 的表视为当前连接的默认库
<property name="nullCatalogMeansCurrent" value="true" />
方法2:在 <table> 标签中指定 catalog 或 schema:明确指定表所属的数据库名称,避免扫描其他库的同名表
<table tableName="furn" domainObjectName="Furn" catalog="furn_ssm">
catalog 对应 MySQL 的数据库名,schema 适用于其他数据库
下面的官方解释建议采用第一种方法:
MyBatis Generator Core – MySql Usage Notes
继续完成测试:
先在Furn类中添加有参和无参构造器
package com.hspedu.test;import com.hspedu.furn.bean.Furn;
import com.hspedu.furn.dao.FurnMapper;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;import java.math.BigDecimal;public class FurnMapperTest {@Testpublic void insertTest(){ApplicationContext ioc =new ClassPathXmlApplicationContext("applicationContext.xml");FurnMapper bean = ioc.getBean(FurnMapper.class);Furn furn = new Furn(null, "北欧", "家具", new BigDecimal(111), 6666, 55, "img");int insert = bean.insert(furn);System.out.println(insert);}
}
结果正常输出
二、搭建Vue前端工程
1、安装node.js(14.17.3)和插件cli
删除之前的node.js版本,因为之前我用的是vue2,现在用vue3,需要下载14.17.3
node.js(14.17.3) 下载地址(适用Windows64位)
https://nodejs.org/dist/v14.17.3/node-v14.17.3-x64.msi
下载完成后,新开一个命令行输入node -v检验 node.js版本
全局安装vue插件cli:
在命令行输入以下命令
npm install -g @vue/cli
创建vue项目:这个位置随意
进入该文件并输入cmd回车
输入命令(ssm_vue这个是项目名称)
vue create ssm_vue
下面是选择步骤
手动选择
选择圈出的三个选项 ,enter
版本选3.x ,enter
输入y,enter
选择下面的方式,enter
下面的步骤是保存当前配置,下次配置时可以直接使用,下面的名字是自定义的
这样就成功了
根据他的提示启动服务
2、使用idea 打开ssm_vue项目,并配置项目启动
将自己创建的项目拖拽到idea即可
打开新窗口
找到自己安装node.js的位置
点击运用并确定
退出之前启动的服务:Ctrl+C 输入y
点击启动即可
点击local的网址就可以访问
3、配置vue服务端口
在vue.config.js文件中配置服务端口
再次启动服务后
4、安装element-plus
直接在cmd中输入下面的命令即可
npm install element-plus --save
三、创建项目基础界面
1、删除HomeView.vue的内容
2、 将原来的组件删除,新创建一个组件 Header
内容如下:
<script>export default {name:"Header"}
</script><template><div style="height: 50px;line-height: 50px;border-bottom: 1px solid #ccc;display: flex"><div style="width: 200px;padding-left: 30px;font-weight: bold;color:dodgerblue">后台管理</div><div style="flex: 1"></div><div style="width: 100px">下拉框</div></div>
</template><style scoped></style>
3、将App.vue文件内容删除,增加下面的代码
<template><Header/>
</template>
<script>
//@表示src
//即使你导入了 Header 组件,仍需在 components 选项中显式注册,
// 才能在当前组件的模板 <template> 中使用 <Header/> 标签。//import 的作用:仅将组件代码引入当前文件,但 Vue 并不知道这个组件是否要在模板中使用
import Header from "@/components/Header.vue";
//Vue 组件必须通过 export default 导出其配置对象(如 data、methods、components 等)
// ,否则父组件或根实例无法识别和使用它//导入相当于“采购食材”,注册相当于“将食材加入菜单”。即使采购了食材(import Header),
// 如果不把菜品加入菜单(注册到 components),顾客(模板)就无法点餐(使用 <Header/>)
export default {name:"Layout",//components 的作用:显式声明当前组件依赖的子组件(注册)// ,将导入的组件与模板中的标签名关联起来,让 Vue 在编译模板时能正确解析components:{Header}
}
</script><style></style>
解释:为什么要导入和导出?
import 的作用:仅将组件代码引入当前文件,但 Vue 并不知道这个组件是否要在模板中使用。
components 的作用:显式声明当前组件依赖的子组件,将导入的组件与模板中的标签名关联起来,让 Vue 在编译模板时能正确解析(注册)
再次启动项目,就可以看到如下的效果:
配置全局css文件
*{margin: 0;padding: 0;box-sizing: border-box;
}
引入css配置文件
import '@/assets/css/global.css'
引入 Element-plus,这里也是在main.js文件写入
快速开始 | Element Plus (element-plus.org)
//引入Element-plus
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
createApp(App).use(store).use(router).use(ElementPlus).mount('#app')
如何提示说没有安装就点击进行安装
在App.vue文件中使用了一个基础按钮
重启项目后
添加下拉列表框:(copy)
Dropdown 下拉菜单 | Element Plus (element-plus.org)
<script setup></script><template>
<div><el-menudefault-active="2"class="el-menu-vertical-demo"@open="handleOpen"@close="handleClose"><el-sub-menu index="1"><template #title><el-icon><location /></el-icon><span>Navigator One</span></template><el-menu-item-group title="Group One"><el-menu-item index="1-1">item one</el-menu-item><el-menu-item index="1-2">item two</el-menu-item></el-menu-item-group><el-menu-item-group title="Group Two"><el-menu-item index="1-3">item three</el-menu-item></el-menu-item-group><el-sub-menu index="1-4"><template #title>item four</template><el-menu-item index="1-4-1">item one</el-menu-item></el-sub-menu></el-sub-menu><el-menu-item index="2"><el-icon><icon-menu /></el-icon><span>Navigator Two</span></el-menu-item><el-menu-item index="3" disabled><el-icon><document /></el-icon><span>Navigator Three</span></el-menu-item><el-menu-item index="4"><el-icon><setting /></el-icon><span>Navigator Four</span></el-menu-item></el-menu>
</div>
</template><style scoped></style>
在APP.vue文件中导入Aside组件并注入到components
找到网址的默认路径
再默认的网页中添加一个按钮
网页布局分为三个部分
进一步调整得到如下的结果:
HomeView.vue的页面如下所示:
<template>
<!--这个主要是用来路由到的页面,默认访问的页面-->
<div>
<!-- 添加按钮和查询框--><div style="margin: 10px 5px;display:inline-block"><el-button type="primary">新增</el-button><el-button>其他</el-button></div><div style="display: inline-block"><el-input v-model="input" style="width: 150px" placeholder="请输入关键字" /><el-button type="success">提交</el-button></div>
<!-- 表格--><el-table :data="tableData" stripe style="width: 100%"><el-table-column sortable prop="date" label="日期" /><el-table-column prop="name" label="姓名" /><el-table-column prop="address" label="地址" /><el-table-column fixed="right" label="操作" min-width="120"><template #default><el-button link type="primary" @click="handleClick">删除</el-button><el-button link type="primary">编辑</el-button></template></el-table-column></el-table>
</div>
</template><script>
// @ is an alias to /src
// import HelloWorld from '@/components/HelloWorld.vue'export default {name: 'HomeView',components: {},//数据池的方法data(){return{tableData:[{date: '2016-05-03',name: 'Tom',address: 'No. 189, Grove St, Los Angeles',},{date: '2016-05-02',name: 'Tom',address: 'No. 189, Grove St, Los Angeles',},{date: '2016-05-04',name: 'Tom',address: 'No. 189, Grove St, Los Angeles',},{date: '2016-05-01',name: 'Tom',address: 'No. 189, Grove St, Los Angeles',},]}}
}
</script>
四、添加家居信息
创建FurnService接口的方法
package com.hspedu.furn.service;import com.hspedu.furn.bean.Furn;public interface FurnService {//添加方法public void save(Furn furn);
}
FurnServiceImpl实现类的方法
package com.hspedu.furn.service.Impl;import com.hspedu.furn.bean.Furn;
import com.hspedu.furn.dao.FurnMapper;
import com.hspedu.furn.service.FurnService;
import org.springframework.stereotype.Service;import javax.annotation.Resource;@Service
public class FurnServiceImpl implements FurnService {//实现自动依赖注入@Resourceprivate FurnMapper furnMapper;@Overridepublic void save(Furn furn) {furnMapper.insertSelective(furn);}
}
新建一个测试类进行测试
package com.hspedu.service;import com.hspedu.furn.bean.Furn;
import com.hspedu.furn.service.FurnService;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;import java.math.BigDecimal;public class FurnServiceTest {private ClassPathXmlApplicationContext ioc;//FurnServiceImpl被Spring AOP代理(如使用了事务、切面等),// 导致实际注入的是代理对象,其类型为接口FurnServicepublic FurnService furnService;@Beforepublic void init(){ioc = new ClassPathXmlApplicationContext("applicationContext.xml");furnService = ioc.getBean( FurnService.class);}@Testpublic void save(){Furn furn = new Furn(null, "北欧", "家具", new BigDecimal(111), 6666, 55, "img");furnService.save(furn);}
}
给img_path属性设置默认值
添加一个Message对象作为响应封装对象
package com.hspedu.furn.bean;import java.util.HashMap;
import java.util.Map;public class Message {//状态码,200成功,400失败private int code;//信息private String message;//返回浏览器的数据private Map<String,Object> extend=new HashMap<>();//返回成功的信息public static Message success(){Message message = new Message();message.setCode(200);message.setMessage("success");return message;}//返回失败的信息public static Message fail(){Message message = new Message();message.setCode(400);message.setMessage("fail");return message;}//返回传输的信息//这里return this表示直接返回该对象不用创建新的对象,// 支持链式调用,避免了不必要的对象复制开销。public Message add(String key,Object value){extend.put(key,value);return this;}public int getCode() {return code;}public void setCode(int code) {this.code = code;}public String getMessage() {return message;}public void setMessage(String message) {this.message = message;}public Map<String, Object> getExtend() {return extend;}public void setExtend(Map<String, Object> extend) {this.extend = extend;}public Message() {}public Message(int code, String message, Map<String, Object> extend) {this.code = code;this.message = message;this.extend = extend;}
}
在Controller层增加FurnController对象
package com.hspedu.furn.controller;import com.hspedu.furn.bean.Furn;
import com.hspedu.furn.bean.Message;
import com.hspedu.furn.service.FurnService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;import javax.annotation.Resource;//作为组件被注入到Springmvc
@Controller
public class FurnController {//自动依赖注入,注入接口类型(动态代理对象)@Resourceprivate FurnService furnService;/*** @ResponseBody将相应的对象转成json格式的字符串* @RequestBody将前端发送的json格式的字符串封装成对象* @PostMapping("/save")规定使用post方式发送请求,访问的路径是/save,* 斜杆默认转成ip:端口工程路径* @param furn 封装的对象* @return*/@ResponseBody@PostMapping("/save")public Message save(@RequestBody Furn furn){furnService.save(furn);Message success = Message.success();return success;}
}
在pom.xml文件中引入Jackson用于处理json数据
<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.12.4</version></dependency>
一定一定一定要刷新Maven,否则不能真正的导入依赖 !!!!必须要看见导入的依赖才算真正的导入成功!!!
使用Postman进行测试:
将header请求头的Content-Type设置为 application/json
请求体的数据:
{"name":"小台灯","maker":"中国制造","price":100,"sales":1000,"stock":999
}
响应的数据如下:
在HomeView.vue页面完善添加功能,当点击添加按钮可以弹出对话框
<template>
<!--这个主要是用来路由到的页面,默认访问的页面-->
<div>
<!-- 添加按钮和查询框--><div style="margin: 10px 5px;display:inline-block"><el-button type="primary" @click="add">新增</el-button><el-button>其他</el-button></div><div style="display: inline-block"><el-input v-model="input" style="width: 150px" placeholder="请输入关键字" /><el-button type="success">提交</el-button></div>
<!-- 表格--><el-table :data="tableData" stripe style="width: 100%"><el-table-column sortable prop="date" label="日期" /><el-table-column prop="name" label="姓名" /><el-table-column prop="address" label="地址" /><el-table-column fixed="right" label="操作" min-width="120"><template #default><el-button link type="primary" @click="handleClick">删除</el-button><el-button link type="primary">编辑</el-button></template></el-table-column></el-table><!-- 下面是对话框和表单
<el-input v-model="form.name" style="width: 80%"></el-input>
这里的form.name 表示form对象的属性name
必须和后端的对象字段一样因为要将这些字段信息生成json格式打包给后端,
这里的属性名可以动态生成,不需要在数据池里面编写--><el-dialogtitle="提示"v-model="dialogVisible"width="30%"><el-form :model="form" label-width="120px"><!-- 家居名 --><el-form-item label="家居名"><el-input v-model="form.name" style="width: 80%"></el-input></el-form-item><!-- 厂商 --><el-form-item label="厂商"><el-input v-model="form.maker" style="width: 80%"></el-input></el-form-item><!-- 价格 --><el-form-item label="价格"><el-input v-model="form.price" style="width: 80%"></el-input></el-form-item><!-- 销量 --><el-form-item label="销量"><el-input v-model="form.sales" style="width: 80%"></el-input></el-form-item><!-- 库存 --><el-form-item label="库存"><el-input v-model="form.stock" style="width: 80%"></el-input></el-form-item></el-form><template #footer><span class="dialog-footer">
<!-- 直接写dialogVisible = false,因为只用一句,所以可以直接写在属性上--><el-button @click="dialogVisible = false">取消</el-button><el-button type="primary" @click="save">确定</el-button></span></template></el-dialog>
</div>
</template><script>
// @ is an alias to /src
// import HelloWorld from '@/components/HelloWorld.vue'
export default {name: 'HomeView',components: {},//数据池的方法data(){return{//这里默认表单是不显示的dialogVisible:false,form:{},tableData:[{date: '2016-05-03',name: 'Tom',address: 'No. 189, Grove St, Los Angeles',},{date: '2016-05-02',name: 'Tom',address: 'No. 189, Grove St, Los Angeles',},{date: '2016-05-04',name: 'Tom',address: 'No. 189, Grove St, Los Angeles',},{date: '2016-05-01',name: 'Tom',address: 'No. 189, Grove St, Los Angeles',},]}},methods:{add(){this.dialogVisible=true;//但调用该方法后,将form对象的信息进行清除,// 防止下次点击后出现上一次填写的数据this.form={}}}
}
</script>
在idea的终端安装Axios (注意要在该vue项目的路径下安装)
npm i axios -S
创建一个utils包,在包里创建一个request.js文件
这个request文件用来设置request请求
//导入Axios包
import axios from "axios";
//通过Axios创建对象Request 发送请求到后端
const request = axios.create({timeout:5000
})
//Request拦截器加上统一的处理
//比如Content-Type
request.interceptors.request.use(config=>{config.headers['Content-Type'] = 'application/json;charset=utf-8'return config
},error=>{return Promise.reject(error)
})
//导出Request对象,在其他文件就可以使用
export default request
当测试save方法时就会报这个跨域请求的问题
这里解决这个问题采用的方法是代理服务器(Proxy)
原理:利用同源策略仅限制浏览器的特性,通过中间服务器转发请求,隐藏真实后端地址
实现方式:
- 开发环境代理:在Vue/React中配置vue.config.js或webpack-dev-server,将/api路径代理到目标服务器
- 生产环境Nginx:通过反向代理配置,将请求路径映射到后端服务,并添加CORS头
适用场景:开发调试阶段常用,生产环境需搭配Nginx
解决:在 vue.config.js配置文件中添加proxy代理对象
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({transpileDependencies: true
})module.exports={devServer:{port:10000 ,//启动端口// 设置代理proxy:{'/api':{ //设置拦截器,拦截器的格式 斜杆+名字 拦截规则:匹配所有以/api开头的请求路径target:'http://localhost:8080/ssm', //目标服务器地址:将匹配的请求转发到http://localhost:8080/ssmchangeOrigin:true, //设置同源pathRewrite:{ //路径重写'/api':'' //api被替换为空,前端统一使用代理前缀(如 /api),但后端接口路径不含该前缀//前端请求:/api/user/list//实际转发:http://backend.com/user/list// /api 这部分被 target的路径代替}}}}
}
在HomeView.vue文件中完善save方法
<template>
<!--这个主要是用来路由到的页面,默认访问的页面-->
<div>
<!-- 添加按钮和查询框--><div style="margin: 10px 5px;display:inline-block"><el-button type="primary" @click="add">新增</el-button><el-button>其他</el-button></div><div style="display: inline-block"><el-input v-model="input" style="width: 150px" placeholder="请输入关键字" /><el-button type="success">提交</el-button></div>
<!-- 表格--><el-table :data="tableData" stripe style="width: 100%"><el-table-column sortable prop="date" label="日期" /><el-table-column prop="name" label="姓名" /><el-table-column prop="address" label="地址" /><el-table-column fixed="right" label="操作" min-width="120"><template #default><el-button link type="primary" @click="handleClick">删除</el-button><el-button link type="primary">编辑</el-button></template></el-table-column></el-table><!-- 下面是对话框和表单
<el-input v-model="form.name" style="width: 80%"></el-input>
这里的form.name 表示form对象的属性name
必须和后端的对象字段一样因为要将这些字段信息生成json格式打包给后端,
这里的属性名可以动态生成,不需要在数据池里面编写--><el-dialogtitle="提示"v-model="dialogVisible"width="30%"><el-form :model="form" label-width="120px"><!-- 家居名 --><el-form-item label="家居名"><el-input v-model="form.name" style="width: 80%"></el-input></el-form-item><!-- 厂商 --><el-form-item label="厂商"><el-input v-model="form.maker" style="width: 80%"></el-input></el-form-item><!-- 价格 --><el-form-item label="价格"><el-input v-model="form.price" style="width: 80%"></el-input></el-form-item><!-- 销量 --><el-form-item label="销量"><el-input v-model="form.sales" style="width: 80%"></el-input></el-form-item><!-- 库存 --><el-form-item label="库存"><el-input v-model="form.stock" style="width: 80%"></el-input></el-form-item></el-form><template #footer><span class="dialog-footer">
<!-- 直接写dialogVisible = false,因为只用一句,所以可以直接写在属性上--><el-button @click="dialogVisible = false">取消</el-button><el-button type="primary" @click="save">确定</el-button></span></template></el-dialog>
</div>
</template><script>
//导入组件
import request from "@/utils/request";
export default {name: 'HomeView',components: {},//数据池的方法data(){return{//这里默认表单是不显示的dialogVisible:false,form:{},tableData:[{date: '2016-05-03',name: 'Tom',address: 'No. 189, Grove St, Los Angeles',},{date: '2016-05-02',name: 'Tom',address: 'No. 189, Grove St, Los Angeles',},{date: '2016-05-04',name: 'Tom',address: 'No. 189, Grove St, Los Angeles',},{date: '2016-05-01',name: 'Tom',address: 'No. 189, Grove St, Los Angeles',},]}},methods:{add(){this.dialogVisible=true;//但调用该方法后,将form对象的信息进行清除,// 防止下次点击后出现上一次填写的数据this.form={}},save(){request.post("/api/save",this.form).then(response=>{console.log("response",response)this.dialogVisible=false})}}
}
</script>
五、显示家居信息
因为dao层FurnMapper接口通过逆向工程生成,所以不用再编写,直接在Service层添加方法即可
在furnService接口中添加方法:
在furnServiceImpl对象 中实现接口中的方法:
在测试类中进行测试
在Controller层增加方法
在HomeView.vue页面中修该表格的字段信息
通过Axios的request对象发送get请求拿到响应数据
将响应对象进行统一的处理, 通过Response拦截器
request.js文件
//导入Axios包
import axios from "axios";
//通过Axios创建对象Request 发送请求到后端
const request = axios.create({timeout:5000
})
//Request拦截器加上统一的处理
//比如Content-Type
request.interceptors.request.use(config=>{config.headers['Content-Type'] = 'application/json;charset=utf-8'return config
},error=>{return Promise.reject(error)
})//response拦截器拦截响应对象,统一处理返回的结果
request.interceptors.response.use(response=>{let res = response.data//如果返回的是文件,就返回if (response.config.responseType==='blob'){return res}//如果是String,就转成json对象if (typeof res==='string'){res = res ? JSON.parse(res):res}return res
},error=>{return Promise.reject(error)
})//导出Request对象,在其他文件就可以使用
export default request
HomeView.vue文件
<template>
<!--这个主要是用来路由到的页面,默认访问的页面-->
<div>
<!-- 添加按钮和查询框--><div style="margin: 10px 5px;display:inline-block"><el-button type="primary" @click="add">新增</el-button><el-button>其他</el-button></div><div style="display: inline-block"><el-input v-model="input" style="width: 150px" placeholder="请输入关键字" /><el-button type="success">提交</el-button></div>
<!-- 表格
:data="tableData" 单向渲染数据,从数据池的data数据池的tableData字段里获取数据,
<=> v-bind:data --><el-table :data="tableData" stripe style="width: 100%"><el-table-column sortable prop="id" label="ID" /><el-table-column prop="name" label="家居名" /><el-table-column prop="maker" label="厂家" /><el-table-column prop="price" label="价格" /><el-table-column prop="sales" label="销量" /><el-table-column prop="stock" label="库存" /><el-table-column fixed="right" label="操作" min-width="120"><template #default><el-button link type="primary" @click="handleClick">删除</el-button><el-button link type="primary">编辑</el-button></template></el-table-column></el-table><!-- 下面是对话框和表单
<el-input v-model="form.name" style="width: 80%"></el-input>
这里的form.name 表示form对象的属性name
必须和后端的对象字段一样因为要将这些字段信息生成json格式打包给后端,
这里的属性名可以动态生成,不需要在数据池里面编写--><el-dialogtitle="提示"v-model="dialogVisible"width="30%"><el-form :model="form" label-width="120px"><!-- 家居名 --><el-form-item label="家居名"><el-input v-model="form.name" style="width: 80%"></el-input></el-form-item><!-- 厂商 --><el-form-item label="厂商"><el-input v-model="form.maker" style="width: 80%"></el-input></el-form-item><!-- 价格 --><el-form-item label="价格"><el-input v-model="form.price" style="width: 80%"></el-input></el-form-item><!-- 销量 --><el-form-item label="销量"><el-input v-model="form.sales" style="width: 80%"></el-input></el-form-item><!-- 库存 --><el-form-item label="库存"><el-input v-model="form.stock" style="width: 80%"></el-input></el-form-item></el-form><template #footer><span class="dialog-footer">
<!-- 直接写dialogVisible = false,因为只用一句,所以可以直接写在属性上--><el-button @click="dialogVisible = false">取消</el-button><el-button type="primary" @click="save">确定</el-button></span></template></el-dialog>
</div>
</template><script>
//导入组件
import request from "@/utils/request";
export default {name: 'HomeView',components: {},//数据池的方法data(){return{//这里默认表单是不显示的dialogVisible:false,form:{},tableData:[]}},//钩子函数, created()函数调用后//数据池和方法池的数据都进行了初始化created() {//调用list方法展示数据this.list()},methods:{add(){this.dialogVisible=true;//但调用该方法后,将form对象的信息进行清除,// 防止下次点击后出现上一次填写的数据this.form={}},save(){request.post("/api/save",this.form).then(response=>{console.log("response",response)this.dialogVisible=false//增加家居后调用查询this.list()})},list(){request.get("/api/list").then(response=>{this.tableData=response.extend.furnList})}}
}
</script>
六、修改家居信息
在furnService接口中添加方法
在实现类中实现方法
在Controller层增加相应的业务代码
使用Postman 进行测试
回显数据并修改
点击按钮时,将 scope.row
(当前行数据)作为参数传递给 handle
方法
得到的结果是一个代理对象
将代理对象的数据绑定到form对象中
还有一种回显数据的方法是将代理对象的id取出,然后将id发送给后端,后端根据id查询对应的Furn对象,然后再将对象进行回显
因为新增和编辑都是调用同一个对话框并且点击确认时是调用同一个方法,区别是有无id
<template>
<!--这个主要是用来路由到的页面,默认访问的页面-->
<div>
<!-- 添加按钮和查询框--><div style="margin: 10px 5px;display:inline-block"><el-button type="primary" @click="add">新增</el-button><el-button>其他</el-button></div><div style="display: inline-block"><el-input v-model="input" style="width: 150px" placeholder="请输入关键字" /><el-button type="success">提交</el-button></div>
<!-- 表格
:data="tableData" 单向渲染数据,从数据池的data数据池的tableData字段里获取数据,
<=> v-bind:data --><el-table :data="tableData" stripe style="width: 100%"><el-table-column sortable prop="id" label="ID" /><el-table-column prop="name" label="家居名" /><el-table-column prop="maker" label="厂家" /><el-table-column prop="price" label="价格" /><el-table-column prop="sales" label="销量" /><el-table-column prop="stock" label="库存" /><el-table-column fixed="right" label="操作" min-width="120"><template #default="scope"><el-button link type="primary" @click="handleClick">删除</el-button>
<!-- 点击按钮时,将 scope.row(当前行数据)作为参数传递给 handle 方法--><el-button link type="primary" @click="handle(scope.row)">编辑</el-button></template></el-table-column></el-table><!-- 下面是对话框和表单
<el-input v-model="form.name" style="width: 80%"></el-input>
这里的form.name 表示form对象的属性name
必须和后端的对象字段一样因为要将这些字段信息生成json格式打包给后端,
这里的属性名可以动态生成,不需要在数据池里面编写--><el-dialogtitle="提示"v-model="dialogVisible"width="30%"><el-form :model="form" label-width="120px"><!-- 家居名 --><el-form-item label="家居名"><el-input v-model="form.name" style="width: 80%"></el-input></el-form-item><!-- 厂商 --><el-form-item label="厂商"><el-input v-model="form.maker" style="width: 80%"></el-input></el-form-item><!-- 价格 --><el-form-item label="价格"><el-input v-model="form.price" style="width: 80%"></el-input></el-form-item><!-- 销量 --><el-form-item label="销量"><el-input v-model="form.sales" style="width: 80%"></el-input></el-form-item><!-- 库存 --><el-form-item label="库存"><el-input v-model="form.stock" style="width: 80%"></el-input></el-form-item></el-form><template #footer><span class="dialog-footer">
<!-- 直接写dialogVisible = false,因为只用一句,所以可以直接写在属性上--><el-button @click="dialogVisible = false">取消</el-button><el-button type="primary" @click="save">确定</el-button></span></template></el-dialog>
</div>
</template><script>
//导入组件
import request from "@/utils/request";
import {ElMessage} from "element-plus";
export default {name: 'HomeView',components: {},//数据池的方法data(){return{//这里默认表单是不显示的dialogVisible:false,//对话框的数据form:{},tableData:[]}},//钩子函数, created()函数调用后//数据池和方法池的数据都进行了初始化created() {//调用list方法展示数据this.list()},methods:{//点击新增按钮会调用该方法add(){this.dialogVisible=true;//但调用该方法后,将form对象的信息进行清除,// 防止下次点击后出现上一次填写的数据this.form={}},//点击确认按钮会调用该方法save(){//因为新增和编辑都是调用同一个对话框并且点击确认时是同一个方法,不同的是有无id//所以根据id来请求后端的方法if (this.form.id){//如果有id,那么就是修改数据request.put("/api/update",this.form).then(response=>{//根据不同的状态码响应不同的信息if (response.code===200){ElMessage({message: '修改成功',type: 'success',})}else {ElMessage.error('修改失败')}//关闭对话框并更新数据this.dialogVisible=falsethis.list()})}//如果没有id,说明是新增数据else {request.post("/api/save",this.form).then(response=>{// console.log("response",response)this.dialogVisible=false//增加家居后调用查询this.list()})}},//启用项目时由生命周期函数进行调用list(){request.get("/api/list").then(response=>{this.tableData=response.extend.furnList})},//点击编辑按钮时调用该方法进行回显数据handle(row){// console.log(row)//先将代理对象转成json格式的字符串,再将json格式的字符串转成json对象// 再将json对象放入数据池的form对象中this.form = JSON.parse(JSON.stringify(row))//显示对话框this.dialogVisible=true}}
}
</script>
这部分代码不能放到最后进行统一的调用,是因为异步请求的执行顺序 问题,request.put()
和 request.post()
都是基于 Promise 的异步操作,其回调函数(.then()
)会在请求完成后才执行,此时下面这段代码已经被执行,后面才调用回调函数,此时不能真正的显示最新的数据
this.dialogVisible=false//增加家居后调用查询this.list()
使用另一种方式回显数据:(从数据库中查询回显)
在furnService接口中添加方法:
在实现类中实现方法
在Controller层增加相应的业务代码
使用Postman进行测试
七、删除家居信息
在furnService接口中添加方法
在furnServiceImpl对象 中实现接口中的方法:
在Controller层增加相应的业务代码
使用Postman进行测试
在删除按钮绑定确认事件并调用相应的方法实现Ajax请求
<template>
<!--这个主要是用来路由到的页面,默认访问的页面-->
<div>
<!-- 添加按钮和查询框--><div style="margin: 10px 5px;display:inline-block"><el-button type="primary" @click="add">新增</el-button><el-button>其他</el-button></div><div style="display: inline-block"><el-input v-model="input" style="width: 150px" placeholder="请输入关键字" /><el-button type="success">提交</el-button></div>
<!-- 表格
:data="tableData" 单向渲染数据,从数据池的data数据池的tableData字段里获取数据,
<=> v-bind:data --><el-table :data="tableData" stripe style="width: 100%"><el-table-column sortable prop="id" label="ID" /><el-table-column prop="name" label="家居名" /><el-table-column prop="maker" label="厂家" /><el-table-column prop="price" label="价格" /><el-table-column prop="sales" label="销量" /><el-table-column prop="stock" label="库存" /><el-table-column fixed="right" label="操作" min-width="120"><template #default="scope">
<!-- 删除按钮 --><el-popconfirm title="确认删除吗?" @confirm="handleDel(scope.row.id)"><template #reference><el-button size="small" type="danger">删除</el-button></template></el-popconfirm>
<!-- 点击按钮时,将 scope.row(当前行数据)作为参数传递给 handle 方法--><el-button link type="primary" @click="handle(scope.row)">编辑</el-button></template></el-table-column></el-table><!-- 下面是对话框和表单
<el-input v-model="form.name" style="width: 80%"></el-input>
这里的form.name 表示form对象的属性name
必须和后端的对象字段一样因为要将这些字段信息生成json格式打包给后端,
这里的属性名可以动态生成,不需要在数据池里面编写--><el-dialogtitle="提示"v-model="dialogVisible"width="30%"><el-form :model="form" label-width="120px"><!-- 家居名 --><el-form-item label="家居名"><el-input v-model="form.name" style="width: 80%"></el-input></el-form-item><!-- 厂商 --><el-form-item label="厂商"><el-input v-model="form.maker" style="width: 80%"></el-input></el-form-item><!-- 价格 --><el-form-item label="价格"><el-input v-model="form.price" style="width: 80%"></el-input></el-form-item><!-- 销量 --><el-form-item label="销量"><el-input v-model="form.sales" style="width: 80%"></el-input></el-form-item><!-- 库存 --><el-form-item label="库存"><el-input v-model="form.stock" style="width: 80%"></el-input></el-form-item></el-form><template #footer><span class="dialog-footer">
<!-- 直接写dialogVisible = false,因为只用一句,所以可以直接写在属性上--><el-button @click="dialogVisible = false">取消</el-button><el-button type="primary" @click="save">确定</el-button></span></template></el-dialog>
</div>
</template><script>
//导入组件
import request from "@/utils/request";
import {ElMessage} from "element-plus";
export default {name: 'HomeView',components: {},//数据池的方法data(){return{//这里默认表单是不显示的dialogVisible:false,//对话框的数据form:{},tableData:[]}},//钩子函数, created()函数调用后//数据池和方法池的数据都进行了初始化created() {//调用list方法展示数据this.list()},methods:{//点击新增按钮会调用该方法add(){this.dialogVisible=true;//但调用该方法后,将form对象的信息进行清除,// 防止下次点击后出现上一次填写的数据this.form={}},//点击确认按钮会调用该方法save(){//因为新增和编辑都是调用同一个对话框并且点击确认时是同一个方法,不同的是有无id//所以根据id来请求后端的方法if (this.form.id){//如果有id,那么就是修改数据request.put("/api/update",this.form).then(response=>{//根据不同的状态码响应不同的信息if (response.code===200){ElMessage({message: '修改成功',type: 'success',})}else {ElMessage.error('修改失败')}//关闭对话框并更新数据this.dialogVisible=falsethis.list()})}//如果没有id,说明是新增数据else {request.post("/api/save",this.form).then(response=>{// console.log("response",response)this.dialogVisible=false//增加家居后调用查询this.list()})}},//启用项目时由生命周期函数进行调用list(){request.get("/api/list").then(response=>{this.tableData=response.extend.furnList})},//点击编辑按钮时调用该方法进行回显数据handle(row){// console.log(row)//先将代理对象转成json格式的字符串,再将json格式的字符串转成json对象// 再将json对象放入数据池的form对象中this.form = JSON.parse(JSON.stringify(row))//显示对话框this.dialogVisible=true},handleDel(id){request.delete("/api/delete/"+id).then(response=>{if (response.code===200){//根据响应的状态码进行弹窗显示ElMessage({message: '删除成功',type: 'success',})}else {ElMessage.error('删除失败')}//刷新数据this.list()})}}
}
</script>
八、分页显示列表
引入Mybatis pagehelper分页插件
<dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper</artifactId><version>5.2.1</version></dependency>
一定要记得刷新Maven!!!
在Mybatis主配置文件中配置分页拦截器
启用合理化(reasonable=true)
• 自动修正页码:
- 若页码 小于 1,自动修正为 第 1 页
- 若页码 超过总页数,自动修正为 最后一页
<plugins><plugin interceptor="com.github.pagehelper.PageInterceptor"><property name="reasonable" value="true"/></plugin>
</plugins>
直接在Controller层添加接口
使用Postman进行测试
添加相关导航栏的代码
<div style="margin:10px 0"><el-pagination@size-change="handlePageSizeChange"@current-change="handleCurrentChange":current-page="currentPage":page-sizes="[5, 10]":page-size="pageSize"layout="total, sizes, prev, pager, next, jumper":total="total"></el-pagination></div>
<template>
<!--这个主要是用来路由到的页面,默认访问的页面-->
<div>
<!-- 添加按钮和查询框--><div style="margin: 10px 5px;display:inline-block"><el-button type="primary" @click="add">新增</el-button><el-button>其他</el-button></div><div style="display: inline-block"><el-input v-model="input" style="width: 150px" placeholder="请输入关键字" /><el-button type="success">提交</el-button></div>
<!-- 表格
:data="tableData" 单向渲染数据,从数据池的data数据池的tableData字段里获取数据,
<=> v-bind:data --><el-table :data="tableData" stripe style="width: 100%"><el-table-column sortable prop="id" label="ID" /><el-table-column prop="name" label="家居名" /><el-table-column prop="maker" label="厂家" /><el-table-column prop="price" label="价格" /><el-table-column prop="sales" label="销量" /><el-table-column prop="stock" label="库存" /><el-table-column fixed="right" label="操作" min-width="120"><template #default="scope">
<!-- 删除按钮 --><el-popconfirm title="确认删除吗?" @confirm="handleDel(scope.row.id)"><template #reference><el-button size="small" type="danger">删除</el-button></template></el-popconfirm>
<!-- 点击按钮时,将 scope.row(当前行数据)作为参数传递给 handle 方法--><el-button link type="primary" @click="handle(scope.row)">编辑</el-button>
<!-- 方式2:从数据查询数据-->
<!-- <el-button link type="primary" @click="edit(scope.row.id)">编辑</el-button>--></template></el-table-column></el-table><!-- 下面是对话框和表单
<el-input v-model="form.name" style="width: 80%"></el-input>
这里的form.name 表示form对象的属性name
必须和后端的对象字段一样因为要将这些字段信息生成json格式打包给后端,
这里的属性名可以动态生成,不需要在数据池里面编写--><el-dialogtitle="提示"v-model="dialogVisible"width="30%"><el-form :model="form" label-width="120px"><!-- 家居名 --><el-form-item label="家居名"><el-input v-model="form.name" style="width: 80%"></el-input></el-form-item><!-- 厂商 --><el-form-item label="厂商"><el-input v-model="form.maker" style="width: 80%"></el-input></el-form-item><!-- 价格 --><el-form-item label="价格"><el-input v-model="form.price" style="width: 80%"></el-input></el-form-item><!-- 销量 --><el-form-item label="销量"><el-input v-model="form.sales" style="width: 80%"></el-input></el-form-item><!-- 库存 --><el-form-item label="库存"><el-input v-model="form.stock" style="width: 80%"></el-input></el-form-item></el-form><template #footer><span class="dialog-footer">
<!-- 直接写dialogVisible = false,因为只用一句,所以可以直接写在属性上--><el-button @click="dialogVisible = false">取消</el-button><el-button type="primary" @click="save">确定</el-button></span></template></el-dialog><!-- 分页导航栏
@size-change="handlePageSizeChange"每页显示的数据改变后就会调用handlePageSizeChange方法,
这里会将pageSize传给handlePageSizeChange方法@current-change="handleCurrentChange"点击其他页会调用handleCurrentChange方法,
会将pageNum(显示第几页)传给该方法
--><div style="margin:10px 0"><el-pagination@size-change="handlePageSizeChange"@current-change="handleCurrentChange":current-page="currentPage":page-sizes="[5,10]":page-size="pageSize"layout="total, sizes, prev, pager, next, jumper":total="total"></el-pagination></div>
</div>
</template><script>
//导入组件
import request from "@/utils/request";
import {ElMessage} from "element-plus";
export default {name: 'HomeView',components: {},//数据池的方法data(){return{//分页数据绑定currentPage:1,pageSize:5,total:10,//这里默认表单是不显示的dialogVisible:false,//对话框的数据form:{},tableData:[]}},//钩子函数, created()函数调用后//数据池和方法池的数据都进行了初始化created() {//调用list方法展示数据this.list()},methods:{//点击新增按钮会调用该方法add(){this.dialogVisible=true;//但调用该方法后,将form对象的信息进行清除,// 防止下次点击后出现上一次填写的数据this.form={}},//点击确认按钮会调用该方法save(){//因为新增和编辑都是调用同一个对话框并且点击确认时是同一个方法,不同的是有无id//所以根据id来请求后端的方法if (this.form.id){//如果有id,那么就是修改数据request.put("/api/update",this.form).then(response=>{//根据不同的状态码响应不同的信息if (response.code===200){ElMessage({message: '修改成功',type: 'success',})}else {ElMessage.error('修改失败')}//关闭对话框并更新数据this.dialogVisible=falsethis.list()})}//如果没有id,说明是新增数据else {request.post("/api/save",this.form).then(response=>{// console.log("response",response)this.dialogVisible=false//增加家居后调用查询this.list()})}},//启用项目时由生命周期函数进行调用list(){// request.get("/api/list").then(response=>{// this.tableData=response.extend.furnList// })//分页查询request.get("/api/page",{params:{//传参给后端pageNum:this.currentPage,//第几页pageSize:this.pageSize//每页的记录数}}).then(response=>{//将数据绑定到数据池的tableData对象this.tableData=response.extend.pageInfo.listthis.total=response.extend.pageInfo.total})},//点击编辑按钮时调用该方法进行回显数据handle(row){// console.log(row)//先将代理对象转成json格式的字符串,再将json格式的字符串转成json对象// 再将json对象放入数据池的form对象中this.form = JSON.parse(JSON.stringify(row))//显示对话框this.dialogVisible=true},//删除数据时调用该方法handleDel(id){request.delete("/api/delete/"+id).then(response=>{if (response.code===200){//根据响应的状态码进行弹窗显示ElMessage({message: '删除成功',type: 'success',})}else {ElMessage.error('删除失败')}//刷新数据this.list()})},handleCurrentChange(pageNum){this.currentPage=pageNumthis.list()},handlePageSizeChange(pageSize){this.pageSize=pageSizethis.list()}//方式2:从数据库中查询数据// edit(id){// request.get("/api/find/"+id).then(response=>{// // console.log(response)// this.form=response.extend.furn// this.dialogVisible=true// })// }}
}
</script>
九、带条件查询分页显示列表
在furnService接口中添加方法
在furnServiceImpl对象 中实现接口中的方法:
在Controller层增加相应的业务代码
在分页查询方法的基础上进行修改
使用Postman进行测试
完善前端的查询功能
十、添加家居表单完善前端校验
在数据池中添加校验规则
<template>
<!--这个主要是用来路由到的页面,默认访问的页面-->
<div>
<!-- 添加按钮和查询框--><div style="margin: 10px 5px;display:inline-block"><el-button type="primary" @click="add">新增</el-button><el-button>其他</el-button></div><div style="display: inline-block"><el-input v-model="input" style="width: 150px" placeholder="请输入关键字" /><el-button type="success" @click="list">查询</el-button></div>
<!-- 表格
:data="tableData" 单向渲染数据,从数据池的data数据池的tableData字段里获取数据,
<=> v-bind:data --><el-table :data="tableData" stripe style="width: 100%"><el-table-column sortable prop="id" label="ID" /><el-table-column prop="name" label="家居名" /><el-table-column prop="maker" label="厂家" /><el-table-column prop="price" label="价格" /><el-table-column prop="sales" label="销量" /><el-table-column prop="stock" label="库存" /><el-table-column fixed="right" label="操作" min-width="120"><template #default="scope">
<!-- 删除按钮 --><el-popconfirm title="确认删除吗?" @confirm="handleDel(scope.row.id)"><template #reference><el-button size="small" type="danger">删除</el-button></template></el-popconfirm>
<!-- 点击按钮时,将 scope.row(当前行数据)作为参数传递给 handle 方法--><el-button link type="primary" @click="handle(scope.row)">编辑</el-button>
<!-- 方式2:从数据查询数据-->
<!-- <el-button link type="primary" @click="edit(scope.row.id)">编辑</el-button>--></template></el-table-column></el-table><!-- 下面是对话框和表单
<el-input v-model="form.name" style="width: 80%"></el-input>
这里的form.name 表示form对象的属性name
必须和后端的对象字段一样因为要将这些字段信息生成json格式打包给后端,
这里的属性名可以动态生成,不需要在数据池里面编写--><el-dialogtitle="提示"v-model="dialogVisible"width="30%"><el-form :model="form" :rules="rules" ref="form" label-width="120px"><!-- 家居名 --><el-form-item label="家居名" prop="name"><el-input v-model="form.name" style="width: 80%"></el-input></el-form-item><!-- 厂商 --><el-form-item label="厂商" prop="maker"><el-input v-model="form.maker" style="width: 80%"></el-input></el-form-item><!-- 价格 --><el-form-item label="价格" prop="price"><el-input v-model="form.price" style="width: 80%"></el-input></el-form-item><!-- 销量 --><el-form-item label="销量" prop="sales"><el-input v-model="form.sales" style="width: 80%"></el-input></el-form-item><!-- 库存 --><el-form-item label="库存" prop="stock"><el-input v-model="form.stock" style="width: 80%"></el-input></el-form-item></el-form><template #footer><span class="dialog-footer">
<!-- 直接写dialogVisible = false,因为只用一句,所以可以直接写在属性上--><el-button @click="dialogVisible = false">取消</el-button><el-button type="primary" @click="save">确定</el-button></span></template></el-dialog><!-- 分页导航栏
@size-change="handlePageSizeChange"每页显示的数据改变后就会调用handlePageSizeChange方法,
这里会将pageSize传给handlePageSizeChange方法@current-change="handleCurrentChange"点击其他页会调用handleCurrentChange方法,
会将pageNum(显示第几页)传给该方法
--><div style="margin:10px 0"><el-pagination@size-change="handlePageSizeChange"@current-change="handleCurrentChange":current-page="currentPage":page-sizes="[5,10]":page-size="pageSize"layout="total, sizes, prev, pager, next, jumper":total="total"></el-pagination></div>
</div>
</template><script>
//导入组件
import request from "@/utils/request";
import {ElMessage} from "element-plus";
export default {name: 'HomeView',components: {},//数据池的方法data(){return{input:'',//分页数据绑定currentPage:1,pageSize:5,total:10,//这里默认表单是不显示的dialogVisible:false,//对话框的数据form:{},tableData:[],//校验规则rules:{name:[{required:true,message:"请输入家居名",trigger:"blur"}],maker:[{required:true,message:"请输入厂家名",trigger:"blur"}],price:[{required:true,message:"请输入价格",trigger:"blur"},{pattern:/^([1-9]\d*|0)(\.\d+)?$/ ,message: "请输入数字",trigger:"blur"}],sales:[{required:true,message:"请输入销量",trigger:"blur"},{pattern:/^([1-9]\d*|0)$/ ,message: "请输入数字",trigger:"blur"}],stock:[{required:true,message:"请输入库存",trigger:"blur"},{pattern:/^([1-9]\d*|0)$/ ,message: "请输入数字",trigger:"blur"}]}}},//钩子函数, created()函数调用后//数据池和方法池的数据都进行了初始化created() {//调用list方法展示数据this.list()},methods:{//点击新增按钮会调用该方法add(){this.dialogVisible=true;//但调用该方法后,将form对象的信息进行清除,// 防止下次点击后出现上一次填写的数据this.form={}//清空上一次的校验this.$nextTick(() => {this.$refs['form'].resetFields();});},//点击确认按钮会调用该方法save(){//因为新增和编辑都是调用同一个对话框并且点击确认时是同一个方法,不同的是有无id//所以根据id来请求后端的方法if (this.form.id){//如果有id,那么就是修改数据request.put("/api/update",this.form).then(response=>{//根据不同的状态码响应不同的信息if (response.code===200){ElMessage({message: '修改成功',type: 'success',})}else {ElMessage.error('修改失败')}//关闭对话框并更新数据this.dialogVisible=falsethis.list()})}//如果没有id,说明是新增数据else {//添加校验规则this.$refs['form'].validate((valid)=>{if (valid){//校验通过后才发送请求到后端request.post("/api/save",this.form).then(response=>{// console.log("response",response)this.dialogVisible=false//增加家居后调用查询this.list()//添加成功的窗口ElMessage({message: '添加成功',type: 'success',})})}else {//验证没有通过ElMessage.error('添加失败')return false}})}},//启用项目时由生命周期函数进行调用list(){// request.get("/api/list").then(response=>{// this.tableData=response.extend.furnList// })//分页查询request.get("/api/selectByName",{params:{//传参给后端pageNum:this.currentPage,//第几页pageSize:this.pageSize,//每页的记录数name:this.input}}).then(response=>{//将数据绑定到数据池的tableData对象this.tableData=response.extend.pageInfo.listthis.total=response.extend.pageInfo.total})},//点击编辑按钮时调用该方法进行回显数据handle(row){// console.log(row)//先将代理对象转成json格式的字符串,再将json格式的字符串转成json对象// 再将json对象放入数据池的form对象中this.form = JSON.parse(JSON.stringify(row))//显示对话框this.dialogVisible=true},//删除数据时调用该方法handleDel(id){request.delete("/api/delete/"+id).then(response=>{if (response.code===200){//根据响应的状态码进行弹窗显示ElMessage({message: '删除成功',type: 'success',})}else {ElMessage.error('删除失败')}//刷新数据this.list()})},handleCurrentChange(pageNum){this.currentPage=pageNumthis.list()},handlePageSizeChange(pageSize){this.pageSize=pageSizethis.list()}//方式2:从数据库中查询数据// edit(id){// request.get("/api/find/"+id).then(response=>{// // console.log(response)// this.form=response.extend.furn// this.dialogVisible=true// })// }}
}
</script>
十一、添加家居表单完善后端校验
引入JSR303数据校验依赖
<!-- 引入jsr303数据校验支持--><dependency><groupId>org.hibernate</groupId><artifactId>hibernate-validator</artifactId><version>6.1.0.Final</version></dependency>
一定要刷新Maven!!!
对Furn对象的字段进行校验
对保存方法进行修改
使用Postman进行测试
这里就不在前端进行展示了,如果要展示,就是在数据池里面添加一个属性然后将数据存储到数据池里面,用插值列表框显示
更多功能持续完善....
相关文章:
前端vue+后端ssm项目
下载地址: 前端:https://download.csdn.net/download/2401_83418369/90649449 后端: https://download.csdn.net/download/2401_83418369/90649441 一、项目基础环境搭建 1、新建Maven项目 2、创建目录,结构如下: …...
Python实例题:Python获取阴阳师壁纸
目录 Python实例题 题目 实现思路 代码实现 代码解释 get_wallpaper_links 函数: download_wallpapers 函数: 主程序: 运行思路 注意事项 Python实例题 题目 Python获取阴阳师壁纸 实现思路 发送请求获取网页内容:使…...
考研408操作系统文件管理——4.2目录系统详解
考研408操作系统文件管理——目录系统详解 一、目录管理基本概念 1.1 目录的核心功能 目录是文件系统的核心管理组件,主要实现: 按名存取:通过文件名快速定位物理地址路径解析:将逻辑路径转换为物理块地址共享控制:支持多用户共享同一文件命名空间管理:维护全局唯一的…...
国产SMT贴片机自主技术突破解析
内容概要 随着电子信息产业对精密制造需求的持续升级,国产SMT贴片机的技术突破已成为装备自主化进程的关键节点。本文聚焦设备研发的三大核心领域:高动态运动控制系统通过线性电机与数字信号处理技术的融合,将重复定位精度提升至5μm级别&am…...
Ai Agent 在生活领域的深度应用与使用指南
在科技不断革新的时代,Ai Agent 正以前所未有的态势融入生活的各个角落,成为提升生活品质与效率的得力助手。它凭借强大的智能处理能力,解决了传统生活模式中的诸多痛点,在家庭、出行、健康管理等多个场景中展现出巨大的应用价值…...
CPU与GPU之间的交互
命令队列和命令列表 每个GPU都维护着一个命令队列,本质上是一个环形缓冲区,等待着cpu提交到gpu的命令,同时执行命令 在Direct3D中命令队列被抽象为ID3D12CommandQueue接口来表示。通过下面的方式创建命令队列。 ComPtr<ID3D12CommandQue…...
MySQL运维三部曲初级篇:从零开始打造稳定高效的数据库环境
文章目录 一、服务器选型——给数据库一个舒适的家二、系统调优——打造高性能跑道三、MySQL配置——让数据库火力全开四、监控体系——数据库的体检中心五、备份恢复——数据安全的最后防线六、主从复制——数据同步的艺术七、安全加固——守护数据长城 引言:从小白…...
Python制作简易PDF查看工具PDFViewerV1.0查找功能优化
原文说明 为不破坏原文结构,因此功能优化不在原文中维护了。关于这款工具原文请通过下面链接访问。Python制作简易PDF查看工具PDFViewerV1.0 这款小工具基本功能已经可以作为一款文档浏览器使用,但还有一些美中不足的地方,本文将介绍对文本查…...
MOPSO实现无人机多目标路径规划(Matlab完整源码和数据)
一、MOPSO算法核心原理 MOPSO(多目标粒子群优化算法)通过模拟鸟群觅食行为,在搜索空间中寻找满足多个冲突目标的Pareto最优解集。其核心流程包括: 粒子初始化:随机生成粒子群,每个粒子代表一条候选路径&a…...
Python:使用web框架Flask搭建网站
Date: 2025.04.19 20:30:43 author: lijianzhan Flask 是一个轻量级的 Python Web 开发框架,以简洁灵活著称,适合快速构建中小型 Web 应用或 API 服务。以下是 Flask 的核心概念、使用方法和实践指南 Flask 的核心特点: 轻量级 核心代码仅约…...
芝法酱躺平攻略(21)——kafka安装和使用
本节内容比较初级,故接着躺平攻略写 一、官网的下载 1.1 下载解压 首先,去官网下载jar包,放进linux中,解压到对应位置。 我的位置放在/WORK/MIDDLEWARE/kafka/4.0 1.2 常见配置 # 每个topic默认的分片数 num.properties4 # 数…...
C语言知识复习资料
## 第一章 C语言基本知识 ### 【考点1】C程序 - 用C语言编写的程序称为C语言源程序,源程序文件后缀名为".c" - 源程序经编译后生成后缀名为".obj"的目标文件 - 再把目标文件与各种库函数连接起来,生成后缀名为".exe"可执行文件 - C语言有三…...
CMFA在自动驾驶中的应用案例
CMFA在自动驾驶中的典型应用案例 CMFA(Cross-Modal Feature Alignment)方法在自动驾驶领域有多个成功的应用场景,以下是几个典型案例: 1. 多模态3D目标检测 应用场景:车辆、行人、骑行者等交通参与者的精确检测 …...
进程控制(下)【Linux操作系统】
文章目录 进程程序替换进程替换有关函数和指令函数:execl函数:execv函数:execlp函数:execvp函数:execvpe 进程替换的原理为什么进程替换时,原进程的环境变量不会被覆盖? 进程替换具体会造成什么…...
【后端】【python】Python 爬虫常用的框架解析
一、总结 Python 爬虫常用的框架主要分为 三类: 轻量级请求库:如 requests、httpx,用于快速发请求。解析与处理库:如 BeautifulSoup、lxml、pyquery。爬虫框架系统:如 Scrapy、pyspider、Selenium、Playwright 等&am…...
JDBC 数据库连接全解析:从驱动配置到工具类封装
目录 一. 将MySQL对应版本的jar包放入Java项目中 1. 准备工作 2. 复制到Java项目 二. 获取数据库连接 1. 连接Mysql数据库的URL 2. 连接数据库的用户名 3. 连接数据库的密码 4. 通过反射实例化 三. Properties文件用法 1. properties文件介绍 2. Properties工具类 a.…...
【图片识别分类】如何快速识别照片中的水印文字,对图片进行关键字分类,快速整理水印相机拍摄图片,基于WPF和腾讯OCR的技术实现
项目背景 在施工现场,施工人员通常会使用水印相机拍摄照片,这些照片带有时间、地点、施工阶段等水印信息。为了便于管理和归档,需要快速识别照片中的水印文字,并根据关键字对照片进行分类和整理。 界面设计 界面设计简洁直观&…...
第32讲:卫星遥感与深度学习融合 —— 让地球“读懂”算法的语言
目录 🔍 一、讲讲“遥感+深度学习”到底是干啥的? ✅ 能解决什么问题? 🧠 二、基础原理串讲:深度学习如何“看懂”遥感图? 🛰 遥感图像数据类型: 🧠 CNN的基本思路: 🧪 三、实战案例:用CNN对遥感图像做地类分类 📦 所需R包: 🗂️ 步骤一:构建训…...
Java 静态变量、静态方法及工具类介绍
目录 一、静态变量(Static Variables) 1. 基本概念 2. 核心特性 (1)类级别共享 (2)生命周期 (3)内存分配 3. 使用方法 (1)访问方式 (2)初始化时机 4. 典型应用场景 (1)共享常量 (2)计数器功能 (3)配置信息 二、静态方法(Static …...
【win 1】win 右键菜单添加 idea pycharm vscode trae 打开文件夹
编程时经常需要通过 程序 打开文件夹,有时安装时没注意选上添加到右键菜单,又不想重新安装,有什么方法? 之前教程都是改注册表有点繁琐,这里利用开源的 windows 右键管理软件,可以快捷简单的添加。 右键菜…...
XSS跨站脚本攻击漏洞
目录 一、基本概念 二、XSS分类 1、反射型XSS 2、存储型XSS 3、DOM型XSS 三、手工测试 1、反射型XSS漏洞 (1)安全等级low (2)DOM的XSS (3)安全等级medium (4)安全等级hig…...
TensorFlow 实现 Mixture Density Network (MDN) 的完整说明
本文档详细解释了一段使用 TensorFlow 构建和训练混合密度网络(Mixture Density Network, MDN)的代码,涵盖数据生成、模型构建、自定义损失函数与预测可视化等各个环节。 1. 导入库与设置超参数 import numpy as np import tensorflow as t…...
servlet-服务器内部转发和客户端重定向
服务器内部转发以及客户端重定向 服务器内部转发以及客户端重定向1)服务器内部转发:request.getRequestDispatcher("...").forward(request,response);--- 一次请求响应的过程,对于客户端而言,内部转发多少次ÿ…...
手动实现LinkedList
前言 大家好,我是Maybe。最近在学习数据结构中的链表,自己手动实现了一个LinkedList。我想与大家分享一下。 思维导图 代码部分 package Constant;public class constant {public static final String INDEX_IS_WRONG"输入的下标不合法"; }p…...
实现AWS Step Function安全地请求企业内部API返回数据
需要编写一个Step Function在AWS云上运行,它需要访问企业内部的API获取JSON格式的数据,企业有网关和防火墙,API有公司的okta身份认证,通过公司的域账号来授权访问,现在需要创建一个专用的域账号,让Step Fun…...
掌握 MySQL:从命令行操作到数据类型与字段管理
掌握 MySQL:从命令行操作到数据类型与字段管理 MySQL 作为全球最流行的开源关系型数据库管理系统,广泛应用于 Web 开发、数据分析和企业级应用中。无论是初学者还是资深开发者,掌握 MySQL 的基本命令行操作、了解其数据库类型、数据类型、字…...
基于大语言模型的自动化单元测试生成系统及测试套件评估方法
A System for Automated Unit Test Generation Using Large Language Models and Assessment of Generated Test Suites 翻译于上述论文 基于大语言模型的自动化单元测试生成系统及测试套件评估方法 摘要 单元测试是软件测试生命周期中最基础的测试层级,对确保软…...
使用vue2技术写了一个纯前端的静态网站商城-鲜花销售商城
先给大家看一下网站的整体效果截图: 这个前端静态网站项目主要实现了以下功能: 商城首页、商品分类页、登录注册页、个人中心页、我的收藏页、我的订单页、商品详情页等功能。 最近不是在学习前端开发嘛,肯定要做一些项目来练习以下自己学…...
PyTorch深度学习框架60天进阶学习计划 - 第46天:自动化模型设计(一)
PyTorch深度学习框架60天进阶学习计划 - 第46天:自动化模型设计(一) 第一部分:使用ENAS算法生成图像分类网络 大家好!欢迎来到我们PyTorch深度学习框架60天进阶学习计划的第46天。今天我们要深入探讨一个话题——使用…...
【上海大学计算机系统结构实验报告】多机环境下MPI并行编程
实验目的 学习编制多进程并行程序实现如下功能: 创建多进程,输出进程号和进程数。运行多进程并行例子程序。编程实现大规模矩阵的并行计算。 实验过程及结果分析 实验环境 操作系统:Ubuntu 20.04开发工具:GCC 9.3.0、OpenMPI…...
实用电脑工具,轻松实现定时操作
软件介绍 如果你的电脑有时候需要像个听话的小助手一样,按照你的指令在特定时间做些事情,比如到了点就关机、开机,或者自动打开某个软件,那你可得了解下这个小帮手啦! 小巧功能却不少 程序定时器是一款超实用的电脑…...
jQuery — 动画和事件
介绍 jQuery动画与事件是提升网页交互的核心工具。动画方面,jQuery通过简洁API实现平滑过渡效果,提供预设方法如slideUp(),支持.animate()自定义CSS属性动画,并内置队列系统实现动画链式执行。开发者可精准控制动画速度、回调时机…...
Kubernetes相关的名词解释kube-proxy插件(3)
什么是kube-proxy? kube-proxy 是一个网络代理组件,运行在每个节点(Node)上,是 Kubernetes 服务(Service)功能的核心实现之一。它的主要职责是通过维护网络规则,实现集群内服务&…...
第3章 垃圾收集器与内存分配策略《深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)》
第3章 垃圾收集器与内存分配策略 3.2 对象已死 Java世界中的所有对象实例,垃圾收集器进行回收前就是确定对象哪些是活着的,哪些已经死去。 3.2.1 引用计数算法 常见的回答是:给对象中添加一个引用计数器,有地方引用࿰…...
MCP是什么?为什么突然那么火?
什么是MCP? MCP全称为Model Context Protocol(模型上下文协议),是由Anthropic公司在2024年11月推出的一个开源协议。Anthropic是一家以其开发的Claude大语言模型而闻名的公司。MCP旨在提供一个通用的开放标准,以简化大型语言模型…...
与终端同居日记:Linux指令の进阶撩拨手册
前情提要: 当你和终端的关系从「早安打卡」进阶到「深夜代码同居」,那些曾经高冷的指令开始展露致命の反差萌—— man 是那个永远在线的钢铁直男说明书,只会说:"想懂我?自己看文档!"(…...
STM32单片机入门学习——第42节: [12-2] BKP备份寄存器RTC实时时钟
写这个文章是用来学习的,记录一下我的学习过程。希望我能一直坚持下去,我只是一个小白,只是想好好学习,我知道这会很难,但我还是想去做! 本文写于:2025.04.19 STM32开发板学习——第42节: [12-2] BKP备份寄存器&RTC实时时钟 前言开发板说…...
AI 驱动抗生素发现:从靶点到化合物测试
AI 驱动抗生素发现:从靶点到化合物测试 目录 基于 AI 驱动的研发流程发现抗生素,整合靶点选择和深度学习分子生成,显著提升了候选药物发现效率。结合数据平衡技术,机器学习和 AutoML 能有效提升不平衡数据集分类性能。RibbonFold 是一种新的 AI 模型,可以准确预测淀粉样蛋…...
群晖威联通飞牛等nas如何把宿主机硬盘挂接到可道云docker容器中
可道云系统是用户常用的一款面向个人用户的轻量级私有云存储工具,以高效管理和安全存储为核心,打造便捷的数字化办公体验。但是用户希望把原有其他磁盘中文件挂接到这个新系统中有很大的难度,主要是对linux文件系统理解有很大的误区,认为目录结构是固定的…...
用 R 语言打造交互式叙事地图:讲述黄河源区生态变化的故事
目录 🌟 项目背景:黄河源头的生态变迁 🧰 技术栈介绍 🗺️ 最终效果预览 💻 项目构建步骤 1️⃣ 数据准备 2️⃣ 构建 Leaflet 地图 3️⃣ 使用 scrollama 实现滚动触发事件 4️⃣ 使用 R Markdown / Quarto 打包发布 🎬 效果展示截图 📦 完整代码仓库 …...
opencv(双线性插值原理)
双线性插值是一种图像缩放、旋转或平移时进行像素值估计的插值方法。当需要对图像进行变换时,特别是尺寸变化时,原始图像的某些像素坐标可能不再是新图像中的整数位置,这时就需要使用插值算法来确定这些非整数坐标的像素值。 双线性插值的工…...
Flutter 弹窗队列管理:实现一个线程安全的通用弹窗队列系统
在开发复杂的 Flutter 应用时,弹窗的管理往往是一个令人头疼的问题。尤其是在多个弹窗需要按顺序显示,或者弹窗的显示需要满足特定条件时,手动管理弹窗的显示和隐藏不仅繁琐,还容易出错。为了解决这个问题,我们可以实现…...
Linux压缩与解压命令完全指南:tar.gz、zip等格式详解
Linux压缩与解压命令完全指南:tar.gz、zip等格式详解 在Linux系统中,文件压缩和解压是日常操作中不可或缺的一部分。本文将全面介绍Linux下常用的压缩和解压命令,包括tar.gz、tar、zip等格式的区别和使用方法,帮助你高效管理文件…...
doris/clickhouse常用sql
一、doris常用SQL 1、doris统计数据库的总大小(单位:MB) SELECT table_schema AS database_name,ROUND(SUM(data_length) / 1024 / 1024, 2) AS database_size_MB FROM information_schema.tables WHERE table_schema NOT IN (information…...
实现AWS Lambda函数安全地请求企业内部API返回数据
需要编写一个Lambda函数在AWS云上运行,它需要访问企业内部的API获取JSON格式的数据,企业有网关和防火墙,API有公司的okta身份认证,通过公司的域账号来授权访问,现在需要创建一个专用的域账号,让Lambda函数访…...
【Easylive】Interact与Web服务调用实例及网关安全拦截机制解析
【Easylive】项目常见问题解答(自用&持续更新中…) 汇总版 easylive-cloud-interacteasylive-cloud-web 1. 不同服务(web和interact)之间的调用方式 调用流程 • 角色分工: • easylive-cloud-web:作…...
【HDFS】EC重构过程中的校验功能:DecodingValidator
一、动机 DecodingValidator是在HDFS-15759中引入的一个用于校验EC数据重构正确性的组件。 先说下引入DecodingValidator的动机,据很多已知的ISSUE(如HDFS-14768, HDFS-15186, HDFS-15240,这些目前都已经fix了)反馈, EC在重构的时候可能会有各种各样的问题,导致数据错误…...
Chromium 134 编译指南 macOS篇:编译优化技巧(六)
1. 引言 在Chromium 134的开发过程中,优化编译速度是提升开发效率的关键因素。本文将重点介绍如何使用ccache工具来加速C/C代码的编译过程,特别是在频繁切换分支和修改代码时。通过合理配置和使用这些工具,您将能够显著减少编译时间…...
FPGA——基于DE2_115实现DDS信号发生器
FPGA——基于DE2_115实现DDS信号发生器 文章目录 FPGA——基于DE2_115实现DDS信号发生器一、实验要求二、实现过程(1)新建工程 二、波形存储器ROM(1)方波模块(2)正弦波形存储器(3)锁…...
PHP中的ReflectionClass讲解【详细版】
快餐: ReflectionClass精简版 在PHP中,ReflectionClass是一个功能强大的反射类,它就像是一个类的“X光透视镜”,能让我们在程序运行时深入了解类的内部结构和各种细节。 一、反射类的基本概念和重要性 反射是指在程序运行期间获…...