作者介绍:计算机专业研究生,现企业打工人,从事Java全栈开发
主要内容:技术学习笔记、Java实战项目、项目问题解决记录、AI、简历模板、简历指导、技术交流、论文交流(SCI论文两篇)
上点关注下点赞 生活越过越灿烂
文末点击 按需交流
前言
一直想做一款家政管理系统,看了很多优秀的开源项目但是发现没有合适的。于是利用空闲休息时间开始自己写了一套管理系统。
功能介绍
平台采用B/S结构,后端采用主流的Springboot框架进行开发,前端采用主流的Vue.js进行开发。
整个平台包括前台和后台两个部分。
系统分为后台和前台两部分。
后台的主要功能:
家政管理:管理系统可以录入、修改和查询家政的基本信息,如家政姓名、特长、备注等。
类型管理:系统可以管理家政的类型信息,包括类型的名称等。
标签管理:管理标签录入、修改和查询标签的信息。
评论管理:管理和浏览整个网站的评论信息。
统计分析:系统可以根据家政的活动数据和会员参与度进行统计和分析,帮助管理员了解整个系统的状况。
消息管理:家政管理员可以在系统上发布消息,整个网站的用户都能收到。
系统信息:管理员可以查看系统的基本信息,包括系统名称、服务器信息、内存信息、cpu信息、软件信息等。
前台的主要功能:
注册登录:用户通过注册和登录后,才能使用网站。
门户浏览:用户进入首页后,可以浏览家政列表信息,包括最新、最热、推荐。
智能推荐:详情页右侧的热门推荐。
用户中心:包括用户基本资料修改、用户邮箱推送、消息。
我的预约:包括我预约的家政的信息。
模糊搜索:顶部搜索功能,支持模糊搜索家政信息。
家政评论:详情页下侧用户可以评论家政。
前端技术栈 ES6、vue、vuex、vue-router、vue-cli、axios、antd
后端技术栈 SpringBoot、MyBatis、Maven
适合人群
大学生、系统设计人员、课程作业、毕业设计
开发过程
无论是家政管理、用户管理、标签管理、分类管理、评价管理、日志管理、消息管理等功能都是基于springboot+vue框架开发的,开发流程是:
第一步:编写实体类
第二步:编写mapper数据库映射
第三步:编写service层
第四步:编写controller层
第五步:编写界面和API
下面用家政管理功能来演绎这个流程,其它的管理功能都是这个流程。
第一步:编写实体类
在server的entity文件夹中,新建Thing.java文件。并写入如下代码:
@Data
@TableName("b_thing")
public class Thing implements Serializable {
@TableId(value = "id",type = IdType.AUTO)
public Long id;
@TableField
public String title;
@TableField
public String cover;
@TableField
public String description;
@TableField
public String price;
@TableField
public String status;
@TableField
public String createTime;
@TableField
public String mobile;
@TableField
public String email;
@TableField
public String location;
@TableField
public String pv;
@TableField
public String recommendCount;
@TableField
public String wishCount;
@TableField
public String collectCount;
@TableField
public Long classificationId;
@TableField(exist = false)
public List<Long> tags; // 标签
@TableField(exist = false)
public MultipartFile imageFile;
@TableField
public String userId;
}
第二步:编写mapper数据库映射
在server的mapper文件夹下,新建ThingMapper.java文件,并写入代码:
// Mapper是mybatis里面的技术,用于操作mysql中的数据
@Mapper
public interface ThingMapper extends BaseMapper<Thing> {
}
第三步:编写service层
在server的service文件夹中,新建ThingService.java代码,并写入代码:
public interface ThingService {
List getThingList(String keyword, String sort, String c, String tag); // 查
void createThing(Thing thing); // 增
void deleteThing(String id); // 删
void updateThing(Thing thing); // 改
Thing getThingById(String id);
void addWishCount(String thingId);
void addCollectCount(String thingId);
List getUserThing(String userId);
}
并在impl中编写它的实现类ThingServiceImpl.java
第四步: 编写controller层
在server的controller文件夹新建ThingController.java文件,实现增删改查接口,并编写代码:
@RestController
@RequestMapping("/thing")
public class ThingController {
private final static Logger logger = LoggerFactory.getLogger(ThingController.class);
@Autowired
ThingService service;
@Value("${File.uploadPath}")
private String uploadPath;
@RequestMapping(value = "/list", method = RequestMethod.GET)
public APIResponse list(String keyword, String sort, String c, String tag){
List<Thing> list = service.getThingList(keyword, sort, c, tag);
return new APIResponse(ResponeCode.SUCCESS, "查询成功", list);
}
@RequestMapping(value = "/detail", method = RequestMethod.GET)
public APIResponse detail(String id){
Thing thing = service.getThingById(id);
return new APIResponse(ResponeCode.SUCCESS, "查询成功", thing);
}
@Access(level = AccessLevel.ADMIN)
@RequestMapping(value = "/create", method = RequestMethod.POST)
@Transactional
public APIResponse create(Thing thing) throws IOException {
String url = saveThing(thing);
if(!StringUtils.isEmpty(url)) {
thing.cover = url;
}
service.createThing(thing);
return new APIResponse(ResponeCode.SUCCESS, "创建成功");
}
@Access(level = AccessLevel.ADMIN)
@RequestMapping(value = "/delete", method = RequestMethod.POST)
public APIResponse delete(String ids){
System.out.println("ids===" + ids);
// 批量删除
String[] arr = ids.split(",");
for (String id : arr) {
service.deleteThing(id);
}
return new APIResponse(ResponeCode.SUCCESS, "删除成功");
}
@Access(level = AccessLevel.ADMIN)
@RequestMapping(value = "/update", method = RequestMethod.POST)
@Transactional
public APIResponse update(Thing thing) throws IOException {
System.out.println(thing);
String url = saveThing(thing);
if(!StringUtils.isEmpty(url)) {
thing.cover = url;
}
service.updateThing(thing);
return new APIResponse(ResponeCode.SUCCESS, "更新成功");
}
public String saveThing(Thing thing) throws IOException {
MultipartFile file = thing.getImageFile();
String newFileName = null;
if(file !=null && !file.isEmpty()) {
// 存文件
String oldFileName = file.getOriginalFilename();
String randomStr = UUID.randomUUID().toString();
newFileName = randomStr + oldFileName.substring(oldFileName.lastIndexOf("."));
String filePath = uploadPath + File.separator + "image" + File.separator + newFileName;
File destFile = new File(filePath);
if(!destFile.getParentFile().exists()){
destFile.getParentFile().mkdirs();
}
file.transferTo(destFile);
}
if(!StringUtils.isEmpty(newFileName)) {
thing.cover = newFileName;
}
return newFileName;
}
@RequestMapping(value = "/listUserThing", method = RequestMethod.GET)
public APIResponse listUserThing(String userId){
List<Thing> list = service.getUserThing(userId);
return new APIResponse(ResponeCode.SUCCESS, "查询成功", list);
}
}
第五步:编写界面和API
打开前端web工程,在views文件夹下新建thing.vue文件,并编写代码:
<template>
<div>
<!--页面区域-->
<div class="page-view">
<div class="table-operations">
<a-space>
<a-button type="primary" @click="handleAdd">新增</a-button>
<a-button @click="handleBatchDelete">批量删除</a-button>
<a-input-search addon-before="名称" enter-button @search="onSearch" @change="onSearchChange" />
</a-space>
</div>
<a-table
size="middle"
rowKey="id"
:loading="data.loading"
:columns="columns"
:data-source="data.dataList"
:scroll="{ x: 'max-content' }"
:row-selection="rowSelection"
:pagination="{
size: 'default',
current: data.page,
pageSize: data.pageSize,
onChange: (current) => (data.page = current),
showSizeChanger: false,
showTotal: (total) => `共${total}条数据`,
}"
>
<template #bodyCell="{ text, record, index, column }">
<template v-if="column.key === 'operation'">
<span>
<a @click="handleEdit(record)">编辑</a>
<a-divider type="vertical" />
<a-popconfirm title="确定删除?" ok-text="是" cancel-text="否" @confirm="confirmDelete(record)">
<a href="#">删除</a>
</a-popconfirm>
</span>
</template>
</template>
</a-table>
</div>
<!--弹窗区域-->
<div>
<a-modal
:visible="modal.visile"
:forceRender="true"
:title="modal.title"
width="880px"
ok-text="确认"
cancel-text="取消"
@cancel="handleCancel"
@ok="handleOk"
>
<div>
<a-form ref="myform" :label-col="{ style: { width: '80px' } }" :model="modal.form" :rules="modal.rules">
<a-row :gutter="24">
<a-col span="24">
<a-form-item label="家政姓名" name="title">
<a-input placeholder="请输入" v-model:value="modal.form.title" />
</a-form-item>
</a-col>
<a-col span="12">
<a-form-item label="分类" name="classificationId">
<a-select
placeholder="请选择"
allowClear
:options="modal.cData"
:field-names="{ label: 'title', value: 'id' }"
v-model:value="modal.form.classificationId"
/>
</a-form-item>
</a-col>
<a-col span="12">
<a-form-item label="标签">
<a-select mode="multiple" placeholder="请选择" allowClear v-model:value="modal.form.tags">
<template v-for="item in modal.tagData">
<a-select-option :value="item.id">{{ item.title }}</a-select-option>
</template>
</a-select>
</a-form-item>
</a-col>
<a-col span="24">
<a-form-item label="封面">
<a-upload-dragger
name="file"
accept="image/*"
:multiple="false"
:before-upload="beforeUpload"
v-model:file-list="fileList"
>
<p class="ant-upload-drag-icon">
<template v-if="modal.form.coverUrl">
<img :src="modal.form.coverUrl" style="width: 60px; height: 80px" />
</template>
<template v-else>
<file-image-outlined />
</template>
</p>
<p class="ant-upload-text"> 请选择要上传的封面图片 </p>
</a-upload-dragger>
</a-form-item>
</a-col>
<a-col span="24">
<a-form-item label="家政简介">
<a-textarea placeholder="请输入" v-model:value="modal.form.description" />
</a-form-item>
</a-col>
<a-col span="12">
<a-form-item label="小时价格" name="price">
<a-input-number placeholder="请输入" :min="0" v-model:value="modal.form.price" style="width: 100%" />
</a-form-item>
</a-col>
<a-col span="12">
<a-form-item label="手机号">
<a-input-number placeholder="请输入" :min="0" v-model:value="modal.form.mobile" style="width: 100%" />
</a-form-item>
</a-col>
<a-col span="12">
<a-form-item label="年龄">
<a-input-number placeholder="请输入" :min="0" v-model:value="modal.form.age" style="width: 100%" />
</a-form-item>
</a-col>
<a-col span="12">
<a-form-item label="性别">
<a-input placeholder="请输入" v-model:value="modal.form.sex" style="width: 100%" />
</a-form-item>
</a-col>
<a-col span="12">
<a-form-item label="所在地区">
<a-input placeholder="请输入" v-model:value="modal.form.location" style="width: 100%" />
</a-form-item>
</a-col>
<a-col span="12">
<a-form-item label="状态" name="status">
<a-select placeholder="请选择" allowClear v-model:value="modal.form.status">
<a-select-option key="0" value="0">上架</a-select-option>
<a-select-option key="1" value="1">下架</a-select-option>
</a-select>
</a-form-item>
</a-col>
</a-row>
</a-form>
</div>
</a-modal>
</div>
</div>
</template>
页面展示
后台
点击与我交流