在使用html写的UI图的基础上将数据变为动态的,原计划是新建vue项目然后将html兼容进去,但发现有些样式不兼容(ui图使用了框架,里面包含大量样子需要做剥离与兼容)同时涉及到页面跳转的问题所以考虑在html中引入vue.js实现数据的双向绑定,方便做数据展示。已下为遇到的问题和坑:
一、html页面引入vue.js
可直接参考 在原生HTML文件中引入Vue并使用
我是下载后将js文件放入本地引用的
在创建 Vue 实例并挂载到 html元素中有些不一样
<template>
<div id="app"> // vue挂载的元素 id="app" 可以不写 会默认挂载到当前页面
</div>
</template>
<script>
export default {
name: "index",//当前页面的名称
data() {
//数据定义
return {}
},
created: {//html加载完成之前
},
mounted: {//html加载完成后执行
},
methods: {//组件方法
},
watch: {
// watch监听方法,擅长处理的场景:一个数据影响多个数据
},
}
</script>
js引入vue.js的版本
<html>
<head>
</head>
<div id="app"> // vue挂载的元素 id="app" 必须写 可以不用写在顶级div上 写在那个div上vue就挂载在那个div的作用域中
...
</div>
<script>
new Vue({
el: '#app', // Vue 实例挂载的元素 名字要与 id="app" 对上 可以换其他名字
data: { //数据定义 与vue版的不一样,需要先挂载html元素
return {
*****
};
},
methods: {
****
}
* * * * * * *
// 其他的方法就一样了
})
</script>
</html>
可以参考 在原生HTML文件中引入Vue并使用
二、图形验证码
一般登录、注册使用的验证码是后端返回的,但由于时间比较紧加上不想重复写相关逻辑(系统有原本后台登录相关的验证码)就考虑直接由前端生成,然后验证。
1、效果图展示
点击图片既可以切换
2.代码
代码是参考js创建随机验证码 其中做了一些修改,原代码为原始的js
/**
* @param {number} size 随机字符数
* @return {str[]} 验证码列表
*/
genRandomStr(size = 4) {
const range = [
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
];
const strList = [];
for (let i = 0; i < size; i++) {
const randomStr = range[Math.floor(Math.random() * range.length)];
strList.push(randomStr);
}
return strList;
},
/**
* @param {number} width 验证码高度
* @param {number} height 验证码宽度
* @return {imgSrc:string,value:string} 返回图片链接,验证码值
*/
useCaptcha(width = 100, height = 50) {
// 生成随机内容
const canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext("2d");
const captcha = this.genRandomStr();
for (let i in captcha) {
const str = captcha[i];
const transLen = (width * 0.8) / captcha.length;// 偏移量
const sizeRatio = 0.3 + 0.5 * Math.random();// 随机文字大小比例
const degRatio = (Math.random() - 0.5) * (width / height); // 设置为长宽比不会旋转太过 // 随机旋转比例
ctx.font = height * sizeRatio + "px serif";// 设置字体大小
// ctx.rotate(degRatio);
ctx.fillStyle = this.randomColor();
ctx.fillText(str, width * 0.1, height * 0.8, width * 0.8);
ctx.translate(transLen, 0); // 偏移
// ctx.rotate(-degRatio); // 恢复旋转
}
ctx.translate(-width * 0.8, 0);
for (var i = 0; i <= 50; i++) {
ctx.beginPath(); // 新建画图路径
ctx.strokeStyle = this.randomColor();
ctx.moveTo(Math.random() * width, Math.random() * height);
ctx.lineTo(Math.random() * width, Math.random() * height);
ctx.stroke();
}
const result = {
imgSrc: canvas.toDataURL(),
value: captcha.join(""),
};
return result;
},
setImg() {
const imgDom = document.getElementById("idcode");
const captchaObj = this.useCaptcha(300, 150);
imgDom.src = captchaObj.imgSrc;
console.log("验证码值:", captchaObj.value);
// document.getElementById("yzm").value = captchaObj.value;
this.captCode = captchaObj.value;
},
randomColor() {
const randR = Math.floor(Math.random() * 256);
const randG = Math.floor(Math.random() * 256);
const randB = Math.floor(Math.random() * 256);
return `rgb(${randR},${randG},${randB})`;
}
三、fom表单的操作
1、表单的点击事件
需要使用v-on 用来绑定各种事件,直接使用onclick
会不生效
参考链接 vue常用事件v-on:click详解事件对象,事件冒泡,事件默认行为 vue基本语法
2、form表单中的button按钮如何阻止提交
在from表单标签开始的地方加上onsubmit="return false"
在button上使用return false可以阻止提交
<button type="submit" class="submit_button" onClick=“submitAction();return false;”>提交</button>
但是这样虽然会阻止button按钮提交,但会导致表单内的单选框无法使用,可见下文
3、获取表单的数据同时校验数据
使用vue项目时可用使用element ui框架来做表单,同时加验证之类的。但现在是使用的html页面,虽然单独引入element的css也可以实现功能,但是样式之类的就不能保证和给出的ui图一致,所以在对ui图代码不做样式改动的情况先完成表单数据获取与验证的功能。(也可以使用原生form的数据获取与校验,但是原生js有点麻烦,既然引入了vue就是想简单一点,同时用v-model 也可以实现就是验证麻烦一点,看具体业务验证复杂程度,我这就就判断必填)
(1)html改造
在表单中添加id,同时在字段上也添加id与name,同时在输入框前面的提示字使用label
标签包裹添加for属性
<form class="form clearfix" onClick="return false;" id="gr-form">
<div class="row">
<div class="col-lg-12 col-xs-12 col-md-12 col-sm-12">
<div class="form-item">
<div class="ipt-tit"><span style="color: red;padding-right: 3px;">*</span>
<label for="nickName" style="font-weight: normal;">姓名</label></div>
<div class="ipt-area">
<input class="form-control" id="nickName" name="nickName" placeholder="" style="width: 40%;">
<span>必须填写真实姓名,否则不能通过审核</span>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12 col-xs-12 col-md-12 col-sm-12">
<div class="form-item">
<div class="ipt-tit"><span style="color: red;padding-right: 3px;">*</span>
<label for="papersType" style="font-weight: normal;">证件类型</label></div>
<div class="ipt-area">
<select class="form-select" style="width: 40%;" id="papersType" name="papersType">
<option value="0">居民身份证</option>
<option value="1">士官证</option>
<option value="2">临时身份证</option>
<option value="3">护照</option>
<option value="4">通行证</option>
<option value="5">外国人居留证</option>
<option value="6">其他证件</option>
</select>
<span>请选择证件类型</span>
</div>
</div>
</div>
</div>
<div class="pwdOper">
<span class="remember">
<label >
<input type="checkbox" id="protocol" name="protocol" value="yes">
<span class="checkbox"></span> 我已经阅读并同意<a href="javascript:void(0)" onClick="btn()">《农村产权交易管理信息平台会员注册协议》</a>
</label>
</span>
</div>
</form>
(2)js获取数据
使用serializeArray() 方法通过序列化表单值来创建对象数组(名称和值)
数据通过"对象.属性名"添加属性和方法
(已下方法是写在vue的methods中的)
grsubmitAction() {
let serializeArray = $("#gr-form").serializeArray();//获取数据 并序列化
if(this.parameterVerification(serializeArray)) {//校验
//业务代码
}
},
parameterVerification(serializeArray){
var data = {}
const verifyStatus=true;
serializeArray.forEach(function (input) {
if(!verifyStatus){return;}
let name = input.name;
let value = input.value;
if (value.trim() === "") {
let label = document.querySelector('label[for="' + name + '"]');
if (label !== null){
layer.msg(label.textContent + '为必填项', function () {
});
verifyStatus=false
return;
}
} else {
data[name] = value
}
})
if(data.protocol!==null && data.protocol!=='yes'){
layer.msg('请先阅读并同意《用户协议》', function () {
});
verifyStatus=false
}
if (data.phoneNumber !==null && data.phoneNumber!=='') {
if(!/^1[3456789]\d{9}$/.test(data.phoneNumber)){
layer.msg('请输入正确的手机号', function () {
})
verifyStatus=false
}
}
this.form = data
return verifyStatus;
},
其中 $("#gr-form").serializeArray()
是获取去id为gr-form
的表单数据,在parameterVerification方法进行判断是否必填
是否必填看的是 label标签 let label = document.querySelector('label[for="' + name + '"]');
如果有值的话就说明这个对应的输入框是必填的,同时这里的for
属性与输入框的id和name是一致的,这样就可以与输入框一一对应,这行代码通过label.textContent
方法可用获取到 label 标签中的文字作为提示的内容。
layer.msg()这是layui的弹出层,可用单独引用,这里换成自己框架的弹出层提示
js获取表单数据与验证可用查考:JS获取表单数据 原生js实现from表单验证 多种方式给JS对象添加属性和方法 列化表单数据 serialize()、serializeArray()及使用
4、from表单中的单选或多线按钮无法使用
在上文中提到可用使用 在button上使用return false可以阻止提交,这样会导致在表单中的单选或多选无法使用,这个无论是加在 表单上或是按钮上都会有影响,最开始以为是单选框的问题 参考 HTML 单选按钮无法正常工作 文章的问题二中第2条,去掉return false 同时将按钮改为div 绑定点击事件后正常。
四、给页面添加遮罩
由于我这个是html应用vue.js,是在页面出现后才去创建vue实例,然后再去渲染页面上的{{XXX}} 与vue的一些指令,这样就会导致 {{XXX}}这类的代码会以文本的情况先出现在页面上,等vue创建后才会再去渲染,页面会有1 秒左右的延迟,之后闪动一下出现我们想要的,类似与这种
所以,我们在页面显示的时候先给加遮罩,等vue创建完成后再把遮罩取消掉,这样就不会显得很突兀。
//在最外层的div后面加上下面这段代码,默认页面打开的时候就加了遮罩
<mask id="maskdiv" style="position: absolute; top: 0px; left: 0px; background-color: rgba(128, 128, 128, 0.5); width: 1536px; height: 2510px; z-index: 10000;">
<div id="loadingContainer" style="position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background-color: rgb(255, 255, 255); padding: 20px; border-radius: 10px; box-shadow: rgba(0, 0, 0, 0.5) 0px 0px 10px; text-align: center;">
<img src="https://i.miji.bid/2023/11/28/d3172e065f40eefc779ec2f90d2dba7c.gif" style="width: 50px; height: 50px;">
<div style="margin-top: 10px; color: black;">加载中,请稍候...</div>
</div>
</mask>
<script>
function loadingHide() {
document.body.removeChild(document.getElementById('maskdiv'));
}
var vm = new Vue({
el: '#app',
data() {
return {
};
},
created(){
},
mounted(){//vue的 html加载完成后执行
loadingHide() //关闭遮罩
},
})
</script>
参考原文: JS的loading遮罩层,拿来可用 不过原文是使用js来绘制的这个遮罩,我直接将转换成代码的遮罩复制到页面上页面显示。
五、发送短信按钮添加间隔禁用
// 使用 button
<button class="margin-left-20 padding-left-5 padding-right-5" id="hqyzm" v-on:click="fsyzm()">获取验证码</button>
//不使用 button
<div class="login-btn "v-on:click="verificationCode()" id="hqyzm" style="display: flex;justify-content: center;
align-items: center;width: 100px; margin-top: 0px; margin-left: 5px;">发送验证码</div>
fsyzm(){
comm.getAjaxNoToken(mineCenterApi.verificationCode + this.form.phoneNumber, {}, function(result) {
if (result.code === 200) {
layer.msg('验证码已发送')
}
});
var btn=document.getElementById('hqyzm') //获取按钮
var time=60
btn.disabled=true //禁用
var timer=setInterval(function () {
if (time<0){
clearInterval(timer)
btn.disabled=false
btn.innerHTML='获取验证码'
time=60
}else {
btn.innerHTML = '还剩' + time + '秒'
time -= 1
}
},1000)
},
verificationCode(){
if(vm.sffsyzm){ // div无法设置禁用 在vue的date中设置一个变量 然后进行判断
comm.getAjaxNoToken(mineCenterApi.verificationCode + vm.form.phoneNumber, {}, function(result) {
if (result.code === 200) {
layer.msg('验证码已发送')
vm.sffsyzm=false // 验证码发送了就设置false 不再发送验证码
var btn=document.getElementById('hqyzm')
var time=60
var timer=setInterval(function () {
if (time<0){
clearInterval(timer)
vm.sffsyzm=true
btn.innerHTML='发送验证码'
time=60
}else {
btn.innerHTML = '还剩' + time + '秒'
time -= 1
}
},1000)
}
});
}else {
layer.msg('请稍后再试')
}
},