简介
项目结构:html+vue+springboot
html引入VUE项目vue.min.js、jQuery的jquery-3.4.1.min.js,签名:modernizr.js、jSignature.min.js、jSignature.min.noconflict.js,手机端mui.min.js,弹窗样式:mustache.js、zeroModal.js
所用的js在这里
HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="renderer" content="webkit">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<meta name="apple-mobile-web-app-capable" content="yes">
<link rel="stylesheet" href="css/mui.min.css"/>
<link rel="stylesheet" href="css/zeroModal.css"/>
<title>签到</title>
<style>
body {
font-family: 'Helvetica Neue', Helvetica, sans-serif;
font-size: 17px;
line-height: 21px;
color: #000;
}
html,
body,
#app {
height: 100%;
margin: 0px;
padding: 0px;
/* background-color: #F5F5F5 !important; */
}
.content{
margin: 0.9375rem;
}
.title{
height: 2.5rem;
background-color: #fef6e1;
color: #dc8e35;
padding: 0.9375rem;
font-size: 0.9375rem;
display: flex;
align-items: center;
border-radius: 0.5rem;
margin-bottom: 0.9375rem;
}
.title img{
width: 0.9375rem;
height:0.9375rem;
margin-right: 0.9375rem;
}
.flex{
display: flex;
color: #333333;
border-bottom: 0.0625rem solid #efefef;
}
.left_label{
height: 1.25rem;
display: inline-block;
line-height: 1.25rem;
color: #999;
padding-top: 0.625rem;
}
.center_label{
flex: 1;
line-height: 1.25rem;
text-align: left;
}
.center_input{
flex: 1;
line-height: 1.25rem;
text-align: center;
}
.flex input{
border: 0;
margin-bottom: 0.5rem;
}
.table-view{
border-radius: 0.5rem;
border-top: 0;
border-bottom: 0;
margin-top: 0;
margin-bottom: 1rem;
padding-left: 0;
list-style: none;
background-color: #fff;
}
.table-view-cell{
padding: 0.6875rem 0.9375rem 0 0.9375rem;
overflow: hidden;
}
.qm{
padding: 0.6875rem 0.9375rem;
}
.table-view::before{
position: absolute;
right: 0;
left: 0;
height: 1px;
content: '';
-webkit-transform: scaleY(.5);
transform: scaleY(.5);
background-color: #c8c7cc;
top: -1px;
}
.table-view:after {
position: absolute;
right: 0;
bottom: 0;
left: 0;
height: 1px;
content: '';
-webkit-transform: scaleY(.5);
transform: scaleY(.5);
background-color: #c8c7cc;
}
.memo{
line-height: 1.25rem;
text-indent: 1.875rem;
}
#signature{
border: 1px solid #CCCCCC;
}
.qmcenter{
margin-top: 1rem;
}
#image{
border: 1px solid #CCCCCC;
}
</style>
</head>
<body>
<div id="app">
<div class="content">
<div class="title">
<img src="image/notice.png" />
<span>签到</span>
</div>
<form>
<ul class="table-view">
<li class="table-view-cell mui-collapse-content flex">
<div class="left_label" ><font style="color: red;">*</font>手机号:</div>
<div class="center_input">
<input type="text" placeholder="请输入手机号" v-model="form.userMobile" oninvalid="userMobile" oninput="value=value.replace(/[^\d]/g,'')">
</div>
</li>
<li class="table-view-cell mui-collapse-content flex">
<div class="left_label" ><font style="color: red;">*</font>验证码:</div>
<div class="center_input">
<input type="text" placeholder="请输入验证码" v-model="form.verificationCode"
style="width: 55%;" id="verificationCode">
<span v-show="isViewCode" style="color: #1c6fcd;" @click="sendOutVerificationCode" >获取验证码</span>
<span v-show="!isViewCode" style="color: #1c6fcd;">剩余时间{{count}}s</span>
</div>
</li>
</ul>
<ul class="table-view">
<li class="table-view-cell mui-collapse-content qm">
<div class="left_label" >
<font style="color: red;">*</font>签名:
</div>
<button type="button" class="mui-btn mui-btn-success"
style="border-radius: 5px;float: right;" @click="signatureCreate">预览签名</button>
<button type="button" class="mui-btn mui-btn-success"
style="border-radius: 5px;float: right;margin-right: 7px;" @click="signatureReset">重置签名</button>
<div class="center_input qmcenter">
<div id="signatureparent">
<div id="signature"></div>
</div>
</div>
</li>
</ul>
<button type="button" class="mui-btn mui-btn-primary mui-btn-block" style="border-radius: 8px;" @click="submitData">提交签到</button>
</form>
<div id="image" style="margin:20px;display: none;"></div>
</div>
</div>
<script type="text/javascript" src="js/jquery-3.4.1.min.js"></script>
<script type="text/javascript" src="js/modernizr.js"></script>
<script type="text/javascript" src="js/jSignature.min.js"></script>
<script type="text/javascript" src="js/jSignature.min.noconflict.js"></script>
<script type="text/javascript" src="js/mui.min.js"></script>
<script type="text/javascript" src="js/vue.min.js" charset="utf-8"></script>
<script type="text/javascript" src="js/app.js" charset="utf-8"></script>
<script type="text/javascript" src="js/mustache.js"></script>
<script type="text/javascript" src="js/zeroModal.js"></script>
<script type="text/javascript">
$(function(){
//初始化签名插件
var param= {
width: '100%',//签名区域的宽
height: '300px',//签名区域的高
signatureLine: false,//去除默认画布上那条横线
lineWidth: '2' //画笔的大小
};
$("#signature").jSignature(param);
})
var app = new Vue({
el: '#app',
data:{
form:{
userMobile:undefined,
verificationCode:undefined
},
verificationCode:undefined,
isViewCode: true,
count: '',
timer: null,
ggxxrecid:undefined,
recid:undefined,
detail:{},
},
mounted: function() {
},
created() {
//业务代码初始化
var queryStr=this.GetStrQuery();
if(queryStr!=null){
var whereStr=queryStr.wherestr;
if(whereStr!=null && whereStr!="" && whereStr!=undefined){
var whereJson = JSON.parse(whereStr);
console.log(whereJson.recid);
this.ggxxrecid = whereJson.recid;
setTimeout(function() {
app.getPxggDetail(whereJson.recid);
}, 200);
}
}
},
methods: {
//html弹窗
modaleAlert(content,contentDetail){
zeroModal.alert({
content: content,
contentDetail: contentDetail,
width: '60%',
height: '40%',
okFn: function() {
}
});
},
//是否获取允许验证码
sendOutVerificationCode(){
if(this.form.userMobile != null){
mui.ajax(serverUrl + "/getPxggTrainDetail", {
data: {
tel:this.form.userMobile,
recid:this.ggxxrecid
},
dataType: "json",
type: 'POST', //HTTP请求类型
timeout: 20000, //超时时间设置为10秒;
success: function(data) {
console.log(data);
if (data.code==200) {
app.recid = data.msg;
app.getCode();
} else {
mui.toast("您的报名信息不存在,请确认是否用此手机号报名!");
}
},
error: function(xhr, type, errorThrown) {
mui.toast('手机号检测异常');
}
});
}else {
mui.toast('请先输入手机号!');
}
},
//获取验证码
getCode:function(){
const userMobile = this.form.userMobile;
const TIME_COUNT = 120;
if (!this.timer) {
this.count = TIME_COUNT;
this.isViewCode = false;
this.timer = setInterval(() => {
if (this.count > 0 && this.count <= TIME_COUNT) {
this.count--;
} else {
this.isViewCode = true;
clearInterval(this.timer);
this.timer = null;
}
}, 1000)
}
mui.ajax(serverUrl + "/getMobileCode", {
data: {
tel:this.form.userMobile
},
dataType: "json",
type: 'POST', //HTTP请求类型
timeout: 20000, //超时时间设置为10秒;
success: function(data) {
if (data.code==200) {
app.verificationCode = data.data;
} else {
mui.toast(data.msg);
}
},
error: function(xhr, type, errorThrown) {
mui.toast('获取验证码失败');
}
});
},
//初始化获取浏览器url传参
GetStrQuery:function () {
var params = location.search.substr(1);//这一条语句获取了包括问号开始到参数的最后,不包括前面的路径,去掉问号
var pa = params.split("&");
var queryStr = new Object();
for(var i = 0; i < pa.length; i ++){
queryStr[pa[i].split("=")[0]] = unescape(pa[i].split("=")[1]);
}
if(queryStr != null) return queryStr;
return null;
},
//提交
saveData:function() {
app.imageSave();
app.submitData();
},
//提交form表单
submitData:function(){
if (this.form.userMobile == null || this.form.userMobile == undefined || this.form.userMobile == '') {
mui.toast("请填写手机号!");
return;
}
if (this.verificationCode!=this.form.verificationCode) {
mui.toast("验证码输入错误!");
return;
}
if( $("#signature").jSignature('getData', 'native').length == 0){
mui.toast("请先进行签名");
return;
}
this.form.recid = this.detail.recid;
mui.ajax(serverUrl + "/editPxggTrain", {
data: {
data:JSON.stringify(this.form)
},
dataType: "json",
type: 'POST', //HTTP请求类型
timeout: 20000, //超时时间设置为10秒;
success: function(data) {
// console.log(data);
if (data.code==200) {
if (data.data) {
mui.toast("保存成功!");
app.reset();
app.signatureReset();
}
} else {
mui.toast(data.msg);
}
},
error: function(xhr, type, errorThrown) {
mui.toast('保存失败');
}
});
},
//重置表单数据
reset:function(){
app.form.userMobile=undefined;
app.form.verificationCode=undefined;
app.verificationCode=undefined;
app.isViewCode= true;
app.count= '';
app.timer= null;
},
//重置签名画板
signatureReset:function(){
$("#signature").jSignature('reset');
$("#image").attr('src','');
document.getElementById("image").style.display="none";
},
//生成预览签名
signatureCreate:function(){
if( $("#signature").jSignature('getData', 'native').length == 0){
mui.toast("请先进行签名");
return;
}
document.getElementById("image").style.display="";
var datapair = $("#signature").jSignature("getData", "image")
console.log(datapair);
var img = new Image();
img.src = "data:" + datapair[0] + "," + datapair[1]
$(img).appendTo($("#image"))
},
//上传签名图片
imageSave:function(){
var datapair = $("#signature").jSignature("getData","image"); //将canvas里面的数据转换成base64数组
var imgBase64='data:' + datapair[0] + "," + datapair[1]; //封装成正确的base64
var file= this.base64toFile(imgBase64,'file'); //base64转换成流文件,可以打印出来看下
console.log("file====>",file);
let formData = new FormData(); //封装成formData格式
formData.append('file', file);
formData.append('businessid', app.recid);
mui.ajax(serverUrl + "/upLoadItemImage", {
contentType: false, //不可少
processData: false, //不可少
dataType: "json",
type: 'POST', //HTTP请求类型
timeout: 20000, //超时时间设置为10秒;
data : formData,
async : false,
success: function(data) {
if (data.code==200) {
if (data.data) {
mui.toast("签名图片保存成功!");
// app.reset();
}
} else {
mui.toast(data.msg);
}
},
error: function(xhr, type, errorThrown) {
mui.toast('保存失败');
}
});
},
//重点来了,该方法将Base64格式转换成流格式
base64toFile:function(dataurl, filename) {
let arr = dataurl.split(',')
let mime = arr[0].match(/:(.*?);/)[1]
let suffix = mime.split('/')[1]
let bstr = atob(arr[1])
let n = bstr.length
let u8arr = new Uint8Array(n)
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
}
return new File([u8arr], `${filename}.${suffix}`, {
type: mime
})
},
//业务代码获取详情
getPxggDetail(recid){
mui.ajax(serverUrl + "/getPxggDetail", {
data: {
recid:recid
},
dataType: "json",
type: 'POST', //HTTP请求类型
timeout: 20000, //超时时间设置为10秒;
success: function(data) {
console.log(data);
if (data.code==200) {
app.detail = data.data;
this.detail = data.data;
var newtime = this.parseTime(new Date(),'{y}-{m}-{d} {h}:{i}:{s}');
console.log(this.detail);
console.log(this.detail.signInStartTime);
console.log(this.detail.signInEndTime);
//当前时间大于报名开始时间
if (this.compareDate(newtime,this.detail.signInStartTime)) {
//在时间段内
} else {
//报名尚未开始
app.modaleAlert('签到尚未开始','');
$("input[type=text]").prop('disabled','disabled');
$("span").prop('disabled','disabled');
$(".mui-btn").prop('disabled','disabled');
}
//当前时间大于报名结束时间
if (this.compareDate(newtime,this.detail.signInEndTime)) {
//报名已结束
app.modaleAlert('签到已结束','');
$("input[type=text]").prop('disabled','disabled');
$("span").prop('disabled','disabled');
$(".mui-btn").prop('disabled','disabled');
} else {
//在时间段内
}
} else {
mui.toast(data.msg);
}
}
});
},
},
});
</script>
</body>
</html>
java服务端
@ApiOperation("手机端保存签名图片")
@RequestMapping(value = "/upLoadItemImage", method = { RequestMethod.POST }, produces = "application/json;charset=UTF-8")
public @ResponseBody AjaxResult upLoadItemImage(HttpServletRequest request, HttpServletResponse response){
try {
//这个是图片保存表的外键id
String businessid=request.getParameter("businessid");
if (StringUtils.isEmpty(businessid))
{
return AjaxResult.error("缺少必要参数");
}
String userName = "";
Date now = Date.from(Instant.now());
boolean istrue=false;
//附件表
SysAttachment attachment = new SysAttachment();
// 判断 request 是否有文件上传,即多部分请求
if (multipartResolver.isMultipart(request)) {
// 转换成多部分request
MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) multipartResolver.resolveMultipart(request);
// 取得request中的所有文件名
Iterator<String> iter = multiRequest.getFileNames();
while (iter.hasNext()) {
// 记录上传过程起始时的时间,用来计算上传时间
int pre = (int) System.currentTimeMillis();
// 取得上传文件
MultipartFile file = multiRequest.getFile(iter.next());
if (file != null) {
// 取得当前上传文件的文件名称
String fileName = file.getOriginalFilename();
// 如果名称不为“”,说明该文件存在,否则说明该文件不存在
if (fileName.trim() != "") {
//获取原始文件名、后缀和文件大小
long size = file.getSize()/ 1024;
String extension = FileUploadUtils.getExtension(file);
// 上传并返回新文件路径名称 YaWeiConfig.getUploadPath()是上传的路径比如D:/ruoyi/xxx
String pathFileName = FileUploadUtils.upload(YaWeiConfig.getUploadPath(), file);
attachment.setFileName(fileName);
//附件类型
attachment.setModule(WhythConstants.TRAIN.HANDWRITE_SIGN_IN_MUDOLE);
attachment.setBusinessid(businessid);
attachment.setPath(pathFileName);
attachment.setAttachmentSize(size);
attachment.setSuffix(extension);
attachment.setCreateBy(userName);
attachment.setCreateTime(now);
attachment.setUpdateBy(userName);
attachment.setUpdateTime(now);
istrue=iSysAttachmentService.save(attachment);
}
}
}
}
if(istrue){
return AjaxResult.success(attachment);
}else{
return AjaxResult.error("上传失败!");
}
} catch (Exception e) {
e.printStackTrace();
logger.error("上传失败:", e);
return AjaxResult.error("签名图片保存失败!失败信息:"+e.getMessage());
}
}