1、首先需要安装fastdfs,请自行参考
装好后启动,因为服务器比较low,所以我这里只把后端和mysql放到了服务器上,前端放在了本地
2、后端
(1)、配置application.yml
server:
port: 8080
spring:
devtools:
restart:
enabled: false
datasource:
url: jdbc:mysql://39.106.193.224:3306/Good?useSSL=false&serverTimezone=GMT%2B8
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: update
show-sql: true
itcast-fastdfs:
#文件上传临时目录
upload_location: ${user.dir}/multipartUpload
(2)、fastfds配置文件
## fastdfs-client.properties
fastdfs.connect_timeout_in_seconds = 5
fastdfs.network_timeout_in_seconds = 30
fastdfs.charset = UTF-8
fastdfs.http_anti_steal_token = false
fastdfs.http_secret_key = FastDFS1234567890
fastdfs.http_tracker_http_port = 80
fastdfs.tracker_servers = 39.106.193.224:22122
(3)、pojo
因为是两表,所以这里用了双向一对一
@Entity
@Table(name = "goods")
public class Goods {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "goodsid")
private int goodsId;
@Column(name = "goodsname")
private String goodsName;
@Column(name = "goodsprice")
private int goodsPrice;
@Column(name = "imageurl",length = 255)
private String imageUrl;
@Column(name = "status")
private int status;
@Column(name = "goodssp")
private String goodsSp;
@OneToOne(mappedBy = "goods",cascade = CascadeType.ALL)
private GoodsDetail goodsDetail;
public int getGoodsId() {
return goodsId;
}
public void setGoodsId(int goodsId) {
this.goodsId = goodsId;
}
public String getGoodsName() {
return goodsName;
}
public void setGoodsName(String goodsName) {
this.goodsName = goodsName;
}
public int getGoodsPrice() {
return goodsPrice;
}
public void setGoodsPrice(int goodsPrice) {
this.goodsPrice = goodsPrice;
}
public String getImageUrl() {
return imageUrl;
}
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public String getGoodsSp() {
return goodsSp;
}
public void setGoodsSp(String goodsSp) {
this.goodsSp = goodsSp;
}
public GoodsDetail getGoodsDetail() {
return goodsDetail;
}
public void setGoodsDetail(GoodsDetail goodsDetail) {
this.goodsDetail = goodsDetail;
}
}
@Entity
@Table(name = "goodsdetails")
public class GoodsDetail {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "goodsdetailid")
private int goodsdetailId;
@Column(name = "goodtye")
private String goodTye;
// @Column(name = "goodsid")
// private int goodsId;
@JsonIgnore
@OneToOne(fetch = FetchType.EAGER,optional = false)
@JoinColumn(name="goodsid", referencedColumnName = "goodsid",unique = true )
private Goods goods;
public int getGoodsdetailId() {
return goodsdetailId;
}
public void setGoodsdetailId(int goodsdetailId) {
this.goodsdetailId = goodsdetailId;
}
public String getGoodTye() {
return goodTye;
}
public void setGoodTye(String goodTye) {
this.goodTye = goodTye;
}
public Goods getGoods() {
return goods;
}
public void setGoods(Goods goods) {
this.goods = goods;
}
}
(4)、service
dao层省略,没什么东西
@Service
@Transactional
public class GoodsService {
@Resource
private GoodsDao goodsDao;
public List<Goods> findAll() {
List<Goods> goodsList = goodsDao.findAll();
return goodsList;
}
public void delGoods(int id){
goodsDao.deleteById(id);
}
public Goods saveGoods(Goods goods){
goods.getGoodsDetail().setGoods(goods);
return goodsDao.save(goods);
}
public void updGoods(Goods goods){
goods.getGoodsDetail().setGoods(goods);
goodsDao.saveAndFlush(goods);
}
public void upGood(int id){
goodsDao.updGoodsStatus(id);
}
public void downGood(int id){
goodsDao.updGoodsStatus2(id);
}
}
@Service
@Transactional
public class GoodsDetailService {
@Resource
private GoodsDatailDao goodsDatailDao;
public List<GoodsDetail> findAll(){
return goodsDatailDao.findAll();
}
public void delGoods(int id){
goodsDatailDao.deleteById(id);
}
public void saveGoods(GoodsDetail goodsDetail){
goodsDatailDao.save(goodsDetail);
}
public void updGoods(GoodsDetail goodsDetail){
goodsDatailDao.saveAndFlush(goodsDetail);
}
}
(5)、controller
ResponseResult 是一个工具类,不用也没关系
@Controller
public class GoodsController {
@Resource
private GoodsService goodsService;
@GetMapping("findGoods")
@ResponseBody
public List<Goods> findGoods(){
return goodsService.findAll();
}
@DeleteMapping("delGoods/{goodsId}")
@ResponseBody
public ResponseResult delGoods(@PathVariable("goodsId") int id){
goodsService.delGoods(id);
ResponseResult result = new ResponseResult();
return result;
}
@PostMapping("saveGoods")
@ResponseBody
public ResponseResult saveGoods(@RequestBody Goods goods) {
Goods goods1 = goodsService.saveGoods(goods);
ResponseResult result = new ResponseResult();
return result;
}
@PutMapping("modifyGoods")
@ResponseBody
public ResponseResult modifyGoods(@RequestBody Goods goods) {
goodsService.updGoods(goods);
ResponseResult result = new ResponseResult();
return result;
}
@PutMapping("upGoods/{goodsId}")
@ResponseBody
public ResponseResult upGoods(@PathVariable("goodsId") int id){
goodsService.upGood(id);
ResponseResult result = new ResponseResult();
return result;
}
@PutMapping("downGoods/{goodsId}")
@ResponseBody
public ResponseResult downGoods(@PathVariable("goodsId") int id){
goodsService.downGood(id);
ResponseResult result = new ResponseResult();
return result;
}
}
文件上传controller
@RestController
@RequestMapping("/filesystem")
public class FileServerController {
@Value("${itcast-fastdfs.upload_location}")
private String upload_location;
@PostMapping("/upload")
public FileSystem upload(@RequestParam("file") MultipartFile file) throws IOException {
//将文件先存储在web服务器上(本机),再调用fastDFS的client将文件上传到 fastDSF服务器
FileSystem fileSystem = new FileSystem();
//得到 文件的原始名称
String originalFilename = file.getOriginalFilename();
//扩展名
String extention = originalFilename.substring(originalFilename.lastIndexOf("."));
String fileNameNew = UUID.randomUUID()+extention;
//定义file,使用file存储上传的文件
File file1 = new File(upload_location+fileNameNew);
file.transferTo(file1);
//获取新上传文件的物理路径
String newFilePath = file1.getAbsolutePath();
try {
//加载fastDFS客户端的配置 文件
ClientGlobal.initByProperties("config/application-fastdfs.properties");
System.out.println("network_timeout=" + ClientGlobal.g_network_timeout + "ms");
System.out.println("charset=" + ClientGlobal.g_charset);
//创建tracker的客户端
TrackerClient tracker = new TrackerClient();
TrackerServer trackerServer = tracker.getConnection();
StorageServer storageServer = null;
//定义storage的客户端
StorageClient1 client = new StorageClient1(trackerServer, storageServer);
//文件元信息
NameValuePair[] metaList = new NameValuePair[1];
metaList[0] = new NameValuePair("fileName", originalFilename);
//执行上传,将上传成功的存放在web服务器(本机)上的文件上传到 fastDFS
String fileId = client.upload_file1(newFilePath,null, metaList);
System.out.println("upload success. file id is: " + fileId);
fileSystem.setFileId(fileId);
fileSystem.setFilePath(fileId);
fileSystem.setFileName(originalFilename);
//通过调用service及dao将文件的路径存储到数据库中
//...
//关闭trackerServer的连接
trackerServer.close();
} catch (Exception ex) {
ex.printStackTrace();
}
return fileSystem;
}
}
public class FileSystem {
private String fileId;
private String filePath;
private long fileSize;
private String fileName;
private String fileType;
public String getFileId() {
return fileId;
}
public String getFilePath() {
return filePath;
}
public long getFileSize() {
return fileSize;
}
public String getFileName() {
return fileName;
}
public String getFileType() {
return fileType;
}
public void setFileId(String fileId) {
this.fileId = fileId;
}
public void setFilePath(String filePath) {
this.filePath = filePath;
}
public void setFileSize(long fileSize) {
this.fileSize = fileSize;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public void setFileType(String fileType) {
this.fileType = fileType;
}
}
后台到这就差不多了,这时候还获取不到图片路径,在能被项目加载到的配置类中加入以下代码,我放在了跨域的类中
@Bean
MultipartConfigElement multipartConfigElement() {
MultipartConfigFactory factory = new MultipartConfigFactory();
factory.setLocation("/app/tmp");
return factory.createMultipartConfig();
}
3、前端
<template>
<div>
<div class="bookstable">
<el-row class="bookssavebtn">
<el-button type="primary" @click="handleSave()" icon="el-icon-plus">新增</el-button>
</el-row>
<el-table :data="tableData" style="width: 100%" max-height="600px">
<el-table-column v-show="showId" fixed prop="goodsId" label="编号" width="150"></el-table-column>
<el-table-column prop="imageUrl" label="商品图片" width="250">
<!-- 图片的显示 -->
<template slot-scope="scope">
<img :src="scope.row.imageUrl" min-width="70" height="90" />
</template>
</el-table-column>
<el-table-column prop="goodsName" label="商品名称" width="150"></el-table-column>
<el-table-column prop="goodsDetail.goodTye" label="型号" width="120"></el-table-column>
<el-table-column prop="goodsPrice" label="价格" width="180"></el-table-column>
<el-table-column prop="goodsSp" label="商铺" width="120"></el-table-column>
<el-table-column label="是否上下架" prop="status" :formatter="stateFormat"></el-table-column>
<el-table-column fixed="right" label="操作" width="150">
<template slot-scope="scope">
<el-button @click="handleStatusUp(scope.row.goodsId)" type="text" size="small" v-if="scope.row.status==2">
上架
</el-button>
<el-button @click="handleStatusDown(scope.row.goodsId)" type="text" size="small" v-if="scope.row.status==1">
下架
</el-button>
<el-button @click="handleEdit(scope.row)" type="text" size="small">编辑</el-button>
<!-- scope.row表示选中当前行的数据 -->
<el-button type="text" @click="handleDelete(scope.row.goodsId)" size="small">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 编辑模块 dialog 组件 @open="dialogOpened"-->
<el-dialog title="编辑信息" :visible.sync="dialogFormVisible" :before-close="closeDialog" center>
<el-radio-group v-model="labelPosition" size="small">
<!-- ref="books"绑定数据的显示集合 -->
<el-radio-button label="left">左对齐</el-radio-button>
<el-radio-button label="right">右对齐</el-radio-button>
<el-radio-button label="top">顶部对齐</el-radio-button>
</el-radio-group>
<div style="margin: 20px;"></div>
<el-form :label-position="labelPosition" label-width="80px" :model="goods" :rules="rules" ref="goods">
<!-- 上面的:model绑定的是你的实体对象,:rules绑定的是你在data中设定的非空验证条件, ref="books"也是实体对象 -->
<!-- 这里的prop绑定的是你实体对象里的属性,不需要实体对象点出来,直接写属性 -->
<!-- 如果是数字类型的数据,使用v-model.number绑定,有利于文本类型判断 -->
<el-form-item label="编号" prop="goodsId" v-show="showId">
<el-input v-model="goods.goodsId" readonly></el-input>
</el-form-item>
<el-form-item label="商品图片" prop="imageUrl">
<el-upload
class="upload-demo"
action="http://39.106.193.224:8080/filesystem/upload"
:on-preview="handlePreview"
:on-remove="handleRemove"
:file-list="fileList"
:on-success="getImageUrl"
list-type="picture">
<el-button size="small" type="primary" >点击上传</el-button>
<div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过500kb</div>
</el-upload>
<!--<!– 图片的显示 –>-->
<!--<template slot-scope="scope">-->
<!--<img :src="goods.imageUrl" min-width="70" height="70" />-->
<!--</template>-->
</el-form-item>
<el-form-item label="型号" prop="goodTye">
<el-input v-model="goods.goodsDetail.goodTye"></el-input>
</el-form-item>
<el-form-item label="商品名称" prop="goodsName">
<el-input v-model="goods.goodsName"></el-input>
</el-form-item>
<el-form-item label="价格" prop="goodsPrice">
<el-input v-model.number="goods.goodsPrice"></el-input>
</el-form-item>
<el-form-item label="商铺" prop="goodsSp">
<el-input v-model="goods.goodsSp"></el-input>
</el-form-item>
<el-form-item label="是否上下架" prop="status">
<el-select v-model="goods.status" placeholder="请选择">
<el-option
v-for="item in status"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<!-- 这里onSubmit('books'),books是你定义的实体对象 -->
<el-button @click="onCancel('goods')">取 消</el-button>
<el-button type="primary" @click="onSubmit('goods')">确 定</el-button>
</div>
</el-dialog>
</div>
</div>
</template>
<script>
const axios = require('axios')
export default {
name: 'Goods',
data () {
return {
fileList: [],
tableData: [],//页面显示绑定对象
dialogFormVisible: false,//编辑框显示隐藏
labelPosition: 'right',//编辑框对齐方式
showId: false,//设置编号隐藏或显示
readonly: true,//设置编号只读
status: [{
value: 1,
label: '上架'
}, {
value: 2,
label: '下架'
}],
goods: {//编辑框实体对象
goodsId: '',
goodsName: '',
goodsPrice: '',
imageUrl: '',
status: '',
goodsSp: '',
goodsDetail: {
goodTye: '',
goodsdetailId: ''
}
},
// 表单验证规则
rules: {
goodsName: [{
required: true,
message: '请输入商品名称',
trigger: 'blur'
},
{
min: 0,
max: 100,
message: '长度大于0个字符',
trigger: 'blur'
}
],
goodsSp: [{
required: true,
message: '请输入商铺',
trigger: 'blur'
}, {
min: 0,
max: 100,
message: '长度大于0个字符',
trigger: 'blur'
}],
status: [{
required: true,
message: '请选择状态',
trigger: 'blur'
}],
goodsPrice: [{
required: true,
message: '请输入商品价格',
trigger: 'blur'
}]
},
}
},
created () {//创建加载事件
var app = this
console.log('init')
this.init()
},
watch: {//监控一个值的变换
}, methods: {//提供存放方法的地方
handleRemove (file, fileList) {
console.log(file, fileList)
},
handlePreview (file) {
console.log(file)
},
//新增按钮事件
handleSave () {
this.showId = false//隐藏图书编号
this.dialogFormVisible = true//显示编辑框
this.goods = {//先将编辑框置空
goodsId: '',
goodsName: '',
goodsPrice: '',
imageUrl: '',
status: '',
goodsSp: '',
goodsDetail: {
goodTye: '',
goodsdetailId: ''
}
}
},
//修改按钮事件
handleEdit (row) {
this.showId = true
this.dialogFormVisible = true
this.goods = row
},
handleStatusUp (id) {
var vm = this
this.$confirm('确认上架吗?').then(_ => {
axios.put('/upGoods/' + id)
.then(function (response) {
vm.$message.success('上架成功')
vm.init(vm) //刷新数据
})
.catch(function (error) {
})
})
.catch(_ => {
this.$notify({
message: '上架操作已取消',
type: 'info',
duration: 3000
})
})
},
getImageUrl(file){
this.goods.imageUrl="http://39.106.193.224:8888/"+file.fileId;
console.log(this.goods.imageUrl)
},
stateFormat (row, column) {
if (row.status === 1) {
return '上架'
} else if (row.status === 2) {
return '下架'
}
},
handleStatusDown (id) {
var vm = this
this.$confirm('确认下架吗?').then(_ => {
axios.put('/downGoods/' + id)
.then(function (response) {
vm.$message.success('下架成功')
vm.init(vm) //刷新数据
})
.catch(function (error) {
})
})
.catch(_ => {
console.log(_)
this.$notify({
message: '下架操作已取消',
type: 'info',
duration: 3000
})
})
}
,
//编辑提交事件
onSubmit (name) {
var id = this.goods.goodsId
var vm = this
this.$refs[name].validate(valid => {//用于表单验证
if (valid) {//验证通过
if (id == '') {
console.log('-------新增-------')
axios.post('/saveGoods/', this.goods)
.then(function (response) {
vm.$notify({
title: '提示',
message: '添加成功',
duration: 3000
})
// 重新加载数据;
vm.init(vm)
})
.catch(function (error) {
console.log(error)
})
} else {
console.log('-------修改-------')
axios
.put('/modifyGoods/', this.goods)
.then(function (response) {
vm.$notify({
message: '编辑成功',
type: 'success',
duration: 3000
})
this.tableData = response.data
})
.catch(function (error) {
console.log(error)
})
}
vm.$message.success('提交成功!')
this.dialogFormVisible = false
} else {
vm.$message.error('请完成必填项')
this.$notify({
message: '操作失败',
type: 'error',
duration: 3000
})
}
})
},
//取消编辑框
onCancel (formName) {
this.$refs[formName].resetFields()//使关闭前的非空验证,错误提示清空
this.dialogFormVisible = false
this.$notify({
message: '编辑框已关闭',
type: 'info',
duration: 3000
})
},
closeDialog (done) {//编辑框右上角x,由于非空判断,清空问题而写
done()//关闭窗口
this.$refs.goods.clearValidate()
this.$notify({
message: '编辑框已关闭',
type: 'info',
duration: 3000
})
},
init (h) {
var app = this
var a = h == undefined ? app : h
console.log('init')
axios.get('/findGoods',)
.then(response => {
// handle success
console.log('============', response)
a.tableData = response.data
console.log('response', response)
})
.catch(function (error) {
// handle error
console.log(error)
})
},
handleDelete (i) {//i为当前行的id
var vm = this
this.$confirm('确认删除吗?').then(_ => {
axios.delete('/delGoods/' + i)
.then(function (response) {
vm.$message.success('删除成功')
vm.init(vm) //刷新数据
})
.catch(function (error) {
})
})
.catch(_ => {
this.$notify({
message: '删除操作已取消',
type: 'info',
duration: 3000
})
})
},
}
,
}
</script>
<style>
.bookstable {
margin: 0 auto;
width: 60%;
position: relative;
}
.bookssavebtn {
height: 50px;
float: left;
}
</style>
建好数据库,把那些端口改成自己的一个应该就能运行了
把项目打包到服务器上,然后java -jar jar包就OK了