最终效果如图
此内容仅仅完成此部分功能,后续逐步介绍整体的前端后台
实现查询效果的界面分页效果
实现图片上传到对象服务器(对于图片,文件等资源需要有专门的服务器,占用空间巨大)
对于上次mapper实现的增删改查进行界面编写
云服务器内的资源文件如下
项目序列-2:https://github.com/Jonekaka/javaweb-qingcheng-2-77
掌握ElementUI常用组件的使用
掌握ES6常用语法的使用
完成品牌管理前端代码
掌握图片上传代码的编写
掌握阿里云OSS的使用
Element,“饿了么”出品的一套为开发者、设计师和产品经理准备的基于 Vue 2.0 的桌面端组件库。
比如表格,表单,下拉列表样式等等,因此可以直接使用方便,后台开发可以较快完成实践
提供了现成的组件模板
需要什么组件,选择然后查看提供的源码,复制调用即可,有样式,有属性介绍
详见elementUI官网 http://element-cn.eleme.io/2.0/#/zh-CN
编程语言JavaScript是ECMAScript的实现和扩展 。ecma本身是一套规范,javascript实现了这套规范
ECMAScript是由ECMA(一个类似W3C的标准组织)参与进行标准化的语法规范。
ECMAScript定义了:
语言语法 – 语法解析规则、关键字、语句、声明、运算符等。
类型 – 布尔型、数字、字符串、对象等。
原型和继承
内建对象和函数的标准库 – JSON、Math、数组方法、对象自省方法等。
没有4版本,因为有很多新特性导致备受争议,被搁置了
ECMAScript标准不定义HTML或CSS的相关功能,也不定义类似DOM(文档对象模型)
的Web API,这些都在独立的标准中进行定义。ECMAScript涵盖了各种环境中JS的使用
场景,无论是浏览器环境还是类似node.js的非浏览器环境。
ECMAScript是一门充满活力的语言,并在不断进化中。
未来版本的规范中将持续进行重要的技术改进
ECMAScript 6.0(以下简称ES6)是JavaScript语言的下一代标准,2015年6月正式发
布。它的目标,是使得JavaScript语言可以用来编写复杂的大型应用程序,成为企业级开
发语言。
在ES6以前,var关键字声明变量。无论声明在何处,都会被视为声明在函数的最顶部(不在函数内即在全局作用域的最顶部)。这就是函数变量提升,例如
function aa() {
if(true) {
var test = 'hello man'
}
alert(test)
}
test不应该被检测到的,但是后面仍然可以使用
以上的代码实际上是:
function aa() {
var test;
if(true) {
test = 'hello man'
}
alert(test)
}
这其实并不好,因为声明一个变量,肯定是希望作用范围局限在声明的范围,而不是自由扩展,否则会导致语法混乱
接下来ES6
通常用let和const来声明,let表示变量、const表示常量。let和const都是块级作用
域。怎么理解这个块级作用域?在一个函数内部 ,在一个代码块内部。
相当于收缩了变量作用范围
function aa() {
if(bool) {
let test = 'hello man'
} else {
//test 在此处访问不到
console.log(test)
}
}
const 用于声明常量,看以下代码
const name = 'lux'
name = 'joe' //再次赋值此时会报错
es6模板字符解决了ES5在字符串功能上的痛点。
比如拼接url地址,变量等等,需要考虑其中取值问题,现在自动解决了,输入变量名自动取值拼接
第一个用途,基本的字符串格式化。将表达式嵌入字符串中进行拼接。用${}来界定。
注意使用反引号,就是英文~键下的小点
//es5
var name = 'lux'
console.log('hello' + name)
//es6
const name = 'lux'
console.log(`hello ${
name}`) //hello lux
第二个用途,在ES5时我们通过反斜杠()来做多行字符串或者字符串一行行拼接。ES6反
引号(``)直接搞定。
假如字符很长,传统要使用斜杠保持适当的长度,现在使用反引号包着就行
// es5
var msg = "Hi \
man!"
// es6
const template = `<div>
<span>hello world</span>
</div>`
ES6有意思的一部分就是函数的快捷写法。也就是箭头函数。
箭头函数最直观的三个特点。
1不需要function关键字来创建函数
2省略return关键字
3继承当前上下文的 this 关键字
看下面代码(ES6)
(response,message) => {
.......
}
相当于ES5代码
function(response,message){
......
}
ui有了,如何书写有了,功能函数也有了,开始写界面吧
引入ui组件文件
css,字体文件
基于vue.js。内部js,异步js
使用的还是之前的增删改查代码,此为其添加界面
需求:如下图所示,使用表格控件显示全部的品牌数据,分页与条件查询下面依次完善。
对于图片也应该显示
代码实现:
<!--引入样式表-->
<!--引入js-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF‐8">
<title>品牌管理</title>
<link rel="stylesheet" href="../css/elementui.css">
</head>
<body>
<script src="../js/vue.js"></script>
<script src="../js/elementui.js"></script>
<script src="../js/axios.js"></script>
</body>
</html>
(2)编写页面js:
/*定义作用域,约定为上的div app*/
/*后端异步请求*/
/*箭头函数省略,e6新特性*/
<script>
new Vue({
el: '#app',
data() {
return {
tableData: []
}
},
created() {
axios.get('/brand/findAll.do').then(response=> {
this.tableData = response.data;
});
}
})
</script>
(3)使用表格控件展现数据
直接调控件即可
<!--取数据中的哪个属性-->
<!--表头,标题-->
<!--图片模板列-->
<!--scop行对象-->
<el‐table :data="tableData" border style="width: 100%">
<el‐table‐column
prop="id"
label="ID"
width="180">
</el‐table‐column>
<el‐table‐column
prop="name"
label="名称"
width="180">
</el‐table‐column>
<el‐table‐column
prop="letter"
label="首字母">
</el‐table‐column>
<el‐table‐column
label="图片"
width="180">
<template slot‐scope="scope">
<img :src="scope.row.image">
</template>
</el‐table‐column>
</el‐table>
结果全部展示并不现实就像百度搜索中的百万条结果,你只需要看到前10条,需要分页
(1)页面的表格下方添加分页组件
<!--这里是分页-->
<!--执行的方法-->
<!--sync修饰符,不加表示从变量取值后,变量更改不会改变,就错误的固定了,就是点击没有反应,
不会重新加载数据-->
<!--从下面vue中取值-->
<!--约定显示的控件有哪些-->
<el-pagination>
@size-change="fetchData"
@current-change="fetchData"
:current-page.sync="currentPage"
:page-sizes="[10, 20, 30, 40]"
:page-size="size"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination>
size-change 页大小更改时触发的事件
current-change 当前页更改时触发的事件
current-page 当前页变量
page-sizes 每页记录数选项列表
page-size 当前选择的每页记录数
layout布局列表 total 总记录数 sizes 每页记录数选择框 prev 上一页 next下一页
pager 页码 jumper 为页码跳转
total 为总记录数
(2)修改js脚本
new Vue({
el: '#app',
data(){
return {
tableData: [],
currentPage: 1,
total: 10,
size: 10
}
},
created(){
this.fetchData();
},
methods:{
fetchData (){
axios.get(
`/brand/findPage.do?page=${
this.currentPage}&size=${
this.size}`).then(
response=> {
this.tableData = response.data.rows;
this.total = response.data.total;
});
}
}
})
查找ui控件,
实现思路: 表格上方添加表单,表单内的文本框等控件绑定查询对象,点击查询按钮触发方法,将查询对象传递给后端进行查询。
代码实现:
(1)在表格上方添加表单
<!‐‐查询表单‐‐>
<!--placeholder是文本框默认的文字提示,占位文本-->
<el‐form :inline="true" >
<el‐form‐item label="品牌名称">
<el‐input v‐model="searchMap.name" placeholder="品牌名称">
</el‐input>
</el‐form‐item>
<el‐form‐item label="品牌的首字母">
<el‐input v‐model="searchMap.letter" placeholder="品牌的首字母"></el‐input>
</el‐form‐item>
<el‐button type="primary" @click="fetchData()">查询</el‐button>
</el‐form>
(2)修改 js代码 ,data新增searchMap属性,既分页,又有条件
searchMap: {
}
更改axios调用的代码,改为post提交,传递searchMap对象
axios.post(`/brand/findPage.do?
page=${
this.currentPage}&size=${
this.size}`,this.searchMap)
需求:页面上添加“新增”按钮,点击新增按钮弹出窗口,窗口中包含表单和保存、关闭按
钮。填写数据后点击保存按钮关闭窗口,刷新列表数据。
(1)窗口的弹出与关闭:data添加属性,用于控制窗口显示
formVisible: false
添加弹出窗口
<!‐‐弹出窗口‐‐>
<el‐dialog title="编辑" :visible.sync="formVisible" >
<el‐button @click="formVisible = false" >关闭</el‐button>
</el‐dialog>
添加按钮,控制窗口的弹出
(2)data添加属性pojo 用于保存实体
pojo: {
}
在窗口添加表单
<!--标签宽度,不指定默认占一行-->
<el‐form label‐width="80px">
<el‐form‐item label="品牌名称">
<el‐input placeholder="品牌名称" v‐model="pojo.name">
</el‐input>
</el‐form‐item>
<el‐form‐item label="品牌首字母">
<el‐input placeholder="品牌首字母" v‐model="pojo.letter">
</el‐input>
</el‐form‐item>
<el‐form‐item label="品牌图片">
<el‐input placeholder="品牌图片" v‐model="pojo.image">
</el‐input>
</el‐form‐item>
<el‐form‐item label="排序">
<el‐input placeholder="排序" v‐model="pojo.seq"></el‐
input>
</el‐form‐item>
<el‐form‐item>
<el‐button @click="save()">保存</el‐button>
<el‐button @click="formVisible = false">关闭</el‐button>
</el‐form‐item>
</el‐form>
(3)添加save方法
save (){
axios.post('/brand/add.do',this.pojo).then(response => {
this.fetchData ();//刷新列表
this.formVisible = false ;//关闭窗口
});
}
eui选择按钮组件
需求:在表格中添加操作列,操作列中有修改按钮,点击修改按钮弹出窗口加载数据。
用户修改数据后点击保存,保存后关闭窗口刷新列表。
共用新增的弹出编辑窗口
既然是新增,修改和新增的不同在于id是否存在,因此可以通过判断微调路径实现统一,判断即可
本质就是新增UI按钮,编写按钮触发的函数,函数调用后台函数
(1)在表格中新增模板列
<el‐table‐column
label="操作" >
<template slot‐scope="scope">
<el‐button @click="edit(scope.row.id)" type="text" size="small">
修改</el‐button>
</template>
</el‐table‐column>
(2)增加edit方法,用于加载数据
edit (id){
this.formVisible = true // 打开窗口
// 调用查询
axios.get(`/brand/findById.do?id=${
id}`).then(response => {
this.pojo = response.data;
})
}
(3)修改save方法
新增id不存在,修改id存在
save (){
axios.post(`/brand/${
this.pojo.id==null?'add':'update'}.do`,this.pojo).th
en(response => {
this.fetchData (); //刷新列表
this.formVisible = false ;//关闭窗口
});
}
(4)编辑拉取当前id的数据是应该的,但是新增不行,要清空
<!--这里希望数据新增时,之前的pojo数据清空,否则上一次操作的如新增,编辑数据还在弹出框中-->
<el-button type="primary" @click="pojo={},formVisible = true">新增</el-button>
需求: 表格操作列增加“删除”按钮,点击删除,弹出提示,确定后删除数据刷新列表。
(1)操作列中新增“删除”按钮
<el‐button @click="dele(scope.row.id)" type="text" size="small">删除</el‐
button>
(2)新增dele方法
/*删除后重新刷新数据*/
/*对于删除应该有提示窗,否则点击就删除完蛋*/
dele (id){
this.$confirm('确定要删除此记录吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then( () => {
axios.get(`/brand/delete.do?id=${
id}`).then(response => {
if(response.data.code==0){
this.fetchData (); //刷新列表
}else{
this.$alert(response.data.message)
}
})
})
}
(1)在弹出窗口中放置图片上传组件
/* 上传之后会触发钩子函数*/
/* 上传之前会触发钩子函数*/
<el‐upload
class="avatar‐uploader"
action="/upload/native.do"
:show‐file‐list="false"
:on‐success="handleAvatarSuccess"
:before‐upload="beforeAvatarUpload">
<img v‐if="imageUrl" :src="imageUrl" class="avatar">
<i v‐else class="el‐icon‐plus avatar‐uploader‐icon"></i>
</el‐upload>
(2)修改脚本,data 增加属性
url地址默认为空
imageUrl: ''
增加方法
/*上传文件,返回的具体地址*/
/*上传之前对图片格式检测*/
handleAvatarSuccess(res, file) {
this.imageUrl = file.response;
},
beforeAvatarUpload(file) {
const isJPG = file.type === 'image/jpeg';
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isJPG) {
this.$message.error('上传头像图片只能是 JPG 格式!');
}
if (!isLt2M) {
this.$message.error('上传头像图片大小不能超过 2MB!');
}
return isJPG && isLt2M;
}
(3)修改edit方法和save方法
//save方法第一句添加,将url上传
this.pojo.image= this.imageUrl;
//edit方法回调时添加,上传后显示
this.imageUrl=this.pojo.image //显示图片
需要注意的是商品图片上传大小不一,应该用统一格式显示
<style>
.avatar-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.avatar-uploader .el-upload:hover {
border-color: #409EFF;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
line-height: 178px;
text-align: center;
}
.avatar {
width: 178px;
height: 178px;
display: block;
}
</style>
SpringMVC 中,文件的上传,是通过 MultipartResolver 实现的。 所以,如果要实现文
件的上传,只要在 spring-mvc.xml 中注册相应的 MultipartResolver 即可。
MultipartResolver 的实现类有两个:
<!‐‐ 多部分文件上传 ‐‐>
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver
">
<property name="maxUploadSize" value="104857600" />
<property name="defaultEncoding" value="UTF‐8"></property>
</bean>
maxUploadSize: 设置允许上传的最大文件大小,以字节为单位计算。当设为-1时表示
无限制,默认是-1。
defaultEncoding:表示用来解析request请求的默认编码格式,当没有指定的时候根据
Servlet规范会使用默认值ISO-8859-1。当request自己指明了它的编码格式的时候就会忽
略这里指定的defaultEncoding。
(2)qingcheng_web_manager工程新建controller
@RestController
@RequestMapping("/upload")
public class UploadController {
@Autowired
private HttpServletRequest request;
/*本地文件上传,取参数file*/
@PostMapping("/native")
public String nativeUpload(@RequestParam("file") MultipartFile file)
{
/*确定用户上传的路径,写入的路径,获得img的绝对路径*/
String path=request.getSession().getServletContext().getRealPath("img");
/*存储路径,获得文件的原始文件名*/
String filePath = path +"/"+ file.getOriginalFilename();
File desFile = new File(filePath);
/*确保目录一定存在,不存在就创建*/
if(!desFile.getParentFile().exists()){
desFile.mkdirs();
}
try {
/*写文件的细节,已经封装好*/
file.transferTo(desFile);
} catch (Exception e) {
e.printStackTrace();
}
/*最终返回上传后的文件名*/
return "http://localhost:9101/img/"+file.getOriginalFilename();
}
}
MultipartFile是spring类型,代表HTML中form data方式上传的文件,包含二进制数据
+文件名称
但是
通常文件图片不会放到本地,否则本地服务器将承担巨大的存储压力,而是单独开辟一个云空间存储,因为其空间需求增长迅速
对于一些小公司自建文件存储仓库并非最佳方案,可以使用阿里云存储
阿里云对象存储服务(Object Storage Service,简称OSS)提供基于网络的数据存取服务。使用OSS,可以通过网络随时存储和调用包括文本、图片、音频和视频等在内的各种非结构化数据文件。
阿里云OSS将数据文件以对象(object)的形式上传到存储空间(bucket)中。
您可以进行以下操作:
创建一个或者多个存储空间,向每个存储空间中添加一个或多个文件。(项目对应着一个空间,方便管理)
通过获取已上传文件的地址进行文件的分享和下载。
通过修改存储空间或文件的属性或元信息来设置相应的访问权限。
在阿里云管理控制台执行基本和高级OSS任务。
使用阿里云开发工具包或直接在应用程序中进行RESTful API调用执行基本和高级OSS任务
(1)打开https://www.aliyun.com/ ,申请阿里云账号并完成实名认证。
(2)找到控制台
(3)开通OSS:
进入控制台
(4)创建存储空间
新建Bucket,命名为qcshangchenglilia(我瞎编的,名字是不可以重复的,自己注册一个去) ,
读写权限设置为公共读,任何人都有读权限
私有,必须拥有高级权限,(太麻烦)
公共读写,任何人都具备读写权限(不安全)
会提供访问域名,所有图片等等会存储在其下
根据流量,访问次数收费
创建好后可见
然后为项目创建访问账号密码
有子账号与母账号区别
子账号可以为每个项目都独立访问,母账号则一个key访问所有,一旦泄露,所有项目都不安全
当然为每一个项目创建一个访问的独立子账号
需要注意的是,如果开通编程访问,除了创建时可见秘钥,其他不可见,因此应该及时保存信息
子账号如此
母账号想要得到秘钥还需要手机号验证
看官方oss文档即可,引入javajar包
(1)创建测试工程,引入依赖
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun‐sdk‐oss</artifactId>
<version>2.8.2</version>
</dependency>
(2)新建类和main方法
//输入自己选择的服务器的域名,控制台窗口查看
String endpoint = "http://oss‐cn‐beijing.aliyuncs.com";
// 云账号AccessKey有所有API访问权限,建议遵循阿里云安全最佳实践,创建
并使用RAM子账号进行API访问或日常运维,请登录
String accessKeyId = "LTAI5tMGz1h9CtwUTo39169A";
String accessKeySecret = "自己注册不香吗?)";
//bucket创建时写的名字
String bucketName = "qcshangchenglilia";
// 创建OSSClient实例
OSSClient ossClient = new OSSClient(endpoint, accessKeyId,
accessKeySecret);
// 上传文件流
InputStream inputStream = null;
try {
inputStream = new FileInputStream("d:/a.jpg");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
ossClient.putObject(bucketName, "test.html", inputStream);
// 关闭client
ossClient.shutdown();
上传后,在仓库-文件管理可见
并且每个文件都有了域名,拥有权限后即可查看,成为互联网共用资源
总是如此连接不现实,应该配置成bean文件
(1)qingcheng_common_web添加依赖
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun‐sdk‐oss</artifactId>
<version>2.8.2</version>
</dependency>
(2)qingcheng_common_web添加配置
<!‐‐阿里云OSS‐‐>
<bean id="ossClient" class="com.aliyun.oss.OSSClient">
<constructor‐arg index="0" value="oss‐cn‐beijing.aliyuncs.com">
</constructor‐arg>
//id,秘钥,自己注册不香吗?
<constructor‐arg index="1" value="accessKeyId">
</constructor‐arg>
<constructor‐arg index="2"
value="accessKeySecret"></constructor‐arg>
</bean>
(3)UploadController新增方法
@Autowired
private OSSClient ossClient;
@PostMapping("/oss")
public String ossUpload(@RequestParam("file") MultipartFile
file,String folder){
//bucket名字
String bucketName="qcshangchenglilia";
String fileName= folder+ "/"+ UUID.randomUUID()+
file.getOriginalFilename();
try {
ossClient.putObject(bucketName,fileName,file.getInputStream());
} catch (IOException e) {
e.printStackTrace();
}
return "https://"+bucketName +".oss‐cn‐
beijing.aliyuncs.com/"+fileName;
}
(4)修改brand.html页面的上传组件
这样点击上传时,就将图片文件上传到阿里云oss对象服务器了
<el‐upload
......
action="/upload/oss.do?folder=brand"
......
>
</el‐upload>