【Eelectron-vue】构建桌面应用(15)- 修改antdv输入框默认样式
【Eelectron-vue】构建桌面应用(15)- 修改antdv输入框默认样式
【Eelectron-vue】构建桌面应用(15)- 修改antdv输入框默认样式及表单校验提示信息1.前言
现在的客户端与以往的有些区别,比如登录页面的输入框,大都废弃了之前的登录框样式,而采用只存在底边的输入框。还有就是输入框的错误信息都会采用浮层的形式提示,而不是瀑布流的方式去提示。
在借鉴了其他客户端的方式,做了一些调整,这些调整都需要对antdv
组件进行从新修正。因为我是用的是electron-vue
+antdv
的架构去构建的应用。
2.修改输入框的默认样式
先看一下效果图如下:
我们要做的事情就是:
- 修改输入框为只有底边
- 当输入框获取悬浮事件时,改变border的颜色
- 当输入框获取焦点事件时,修改边框的颜色
2.1.1 去除输入框其他边框
antdv
的处理方式和普通的输入框不一样,我们可以先设置一下输入框的border
,因为是antdv
提供的组件,所以我们需要F12查看输入框绑定的样式,然后去覆盖该样式的border
。
如上图所示,我们直接去覆盖ant-input
和ant-input-number
的样式。
.ant-input, .ant-input-number { width: 100%; border: none !important; border-bottom: 1px solid #e9e3e3 !important; }
这样就可以实现只保留底边。
2.1.2 鼠标移动时改变边框
那么我们现在来做进一步的优化,就是当我们移动到上面时去改变边框的颜色,这个也简单只要设置ant-input
的hover
事件就行了。
.ant-input:hover,.ant-input-number:hover { border-bottom: 1px solid #bebebe !important; }
鼠标悬浮时,改变边框的颜色。
2.1.3 输入框获取焦点时改变输入框的颜色
我本来以为这个也简单,当输入框获取focus
时,直接改变底边的颜色即可。但是发现并非如此。
.ant-input:focus, .ant-input-number:focus { border: none; border-bottom: 1px solid #12b7f5 !important; }
可以发现,并没有生效。看了下网上说的,蓝色边框是输入框的外部轮廓,而不是border的问题。于是就设置输入框的outline:none
。并没有生效还是有蓝色边框。
.ant-input:focus, .ant-input-number:focus { border: none; border-bottom: 1px solid #12b7f5 !important; outline: none; }
如果不是蓝色边框的慌,又不是轮廓的问题,那么只有一种可能了,那就是box-shadow
的问题,于是尝试了修改
.ant-input:focus, .ant-input-number:focus { border: none; border-bottom: 1px solid #12b7f5 !important; box-shadow: none; }
确实获取焦点时,所谓的轮廓就没有了,看来是阴影的问题。到此,修改边框功能基本上实现了。只保留底边,鼠标悬浮时会改变底边的颜色,并且获取焦点时也可以去除阴影改变底边颜色。
但是还有一个问题,当获取焦点时同时会触发悬浮的事件,导致只有鼠标离开时才会变成获取焦点时的蓝色。
这个问题是由于权重相同导致的。怎么理解呢?因为我在获取焦点时,给的是!important
,而在悬浮时也是设置了!important
,所以导致了两个事件的权重一样,那么在输入框获取焦点时,颜色变为蓝色。当你鼠标移出输入框,但是光标还在输入框的情况下,触发了输入框的hover
事件,所以输入框底边就会变成灰色。
解决办法就是,删除悬浮时底边颜色的权重!importan
,这样获取焦点事件的权重比悬浮事件的权重要高,当鼠标离开输入框,在回到输入框时,也不会去覆盖获取焦点的颜色。
我发现现在大多数客户端的校验提示信息都不是瀑布流的方式(即在输入框下面留了一大片空白,来显示错误信息),而是通过浮层的方式去处理这个问题。
传统的方式:
这种在客户端尤其是只有底边的的情况下,会有很大的空白,页面显得大而宽泛。需要给登陆页面设置较大的宽和高。而正常的登陆基本上就是用户名密码登陆按钮这些,不需要太多留白。在antdv
中默认的留白是32px
吧,我记得是。所以为每一个表单项多留出32px
的空白,就显得这个页面不紧凑。所以我们需要重新去修改一下这种提示信息的位置。
而我们要实现的效果就是这种浮层的形式提示表单的错误信息:
于是研究了一下antdv
的表单,看看有没有关于表单校验信息的位置修改方法,研究之后,无果。又尝试能不能使用tooltip
来实现,发现并没有触发事件去触发这个tooltip
的显示。
于是决定自己写一个仿tooltip
校验的信息,写的话,我们需要先理一下思路。
- 首先这个提示信息应该是浮层,就是说脱离文档流,并且
z-index
高于其他元素- 创建一个div,里面包含两个小的div,一个是小三角,一个是提示信息
- 该div应该和input在同一父元素下
- 三角的实现应该是利用border去设置
- 小三角应该偏离一段距离,能对到我们的输入框,输入信息的起始位置
- 整个div应该可以显示隐藏,所以我们需要定义一个属性去控制显示隐藏,以及定义一个msg去显示错误的提示信息
好了这个思路我们理清了,那么操作起来就相对简单多了,把复杂的问题拆分成一个个小的问题,人后逐个攻破,就会容易的多了。
首先的第一个难点就是怎么创建一个小三角。如果你做过,当然就不难,如果没做过还是有一点难度的,当然现在网上有很多关于这方面的资料,感兴趣的可以搜一下。
我处理问题的习惯就是,一个新的东西,我会先脱离当前的项目,重新搞一个简单页面或者脚手架测试一下可行性。
<div style="width:300px;height: 300px;background-color: #bfa;"> </div> <div style="position: absolute;top:20px"> <div style="width: 100px; height: 100; background-color: black;"> </div> <!-- <div style="width: 200px;height: 20px;background-color: black;border-radius: 2px;"> </div> --> </div>
然后我们将黑色div的都设为零,并且设置其border为一定的大小
<div style="width:300px;height: 300px;background-color: #bfa;"> </div> <div style="position: absolute;top:20px"> <div style="width: 0; height: 0; border: 100px solid; border-color: red black;margin-left: 10px;"> </div> <!-- <div style="width: 200px;height: 20px;background-color: black;border-radius: 2px;"> </div> --> </div>
然后将,这样我们只要把其他的边框设置透明就能让它只显示一块了
<div style="width:300px;height: 300px;background-color: #bfa;"> </div> <div style="position: absolute;top:20px"> <div style="width: 0; height: 0; border: 100px solid; border-color: transparent transparent black;margin-left: 10px;"> </div> <!-- <div style="width: 200px;height: 20px;background-color: black;border-radius: 2px;"> </div> --> </div>
接着,把border的大小改变一下
<div style="width:300px;height: 300px;background-color: #bfa;"> </div> <div style="position: absolute;top:20px"> <div style="width: 0; height: 0; border: 5px solid; border-color: transparent transparent black;margin-left: 10px;"> </div> <!-- <div style="width: 200px;height: 20px;background-color: black;border-radius: 2px;"> </div> --> </div>
这么一个小三角形就出来了,然后打开下面的那个div
<div style="width:300px;height: 300px;background-color: #bfa;"> </div> <div style="position: absolute;top:20px"> <div style="width: 0; height: 0; border: 5px solid; border-color: transparent transparent black;margin-left: 10px;"> </div> <div style="width: 200px;height: 20px;background-color: black;border-radius: 2px;color:white;font-size: 12px;line-height: 20px;padding-left: 5px;"> 请输入正确的用户名 </div> </div>
是不是就有内个味了。
好了回到我们的项目中,需要在我们的表单输入框的父节点下,加上我们刚才的代码,并且添加控制显示影藏的属性
<a-form-model-item :label-col="formItemLayout.labelCol" :wrapper-col="formItemLayout.wrapperCol" prop="username" > <a-input v-model="loginVo.username" placeholder="账号" @change="changeUsername" > <a-icon slot="prefix" type="user" /> </a-input> <div class="custom-tooltip" v-show="showUsername"> <div class="custom-srrow"></div> <div class="custom-info">{{ usernameMsg }}</div> </div> </a-form-model-item>
样式定义如下:
.custom-tooltip { position: absolute; top: 20px; z-index: 999; .custom-srrow { width: 0; height: 0; border: 5px solid; border-color: transparent transparent black; margin-left: 10px; } .custom-info { width: 120px; height: 25px; background-color: black; border-radius: 2px; color: white; line-height: 25px; padding-left: 10px; } }
这样的话基本就实现了,这种提示信息的展示。但是还有点美中不足的就是,这么写每一个输入框都需要写一份,那最好的办法就是封装成组件,这个我先不打算做,因为还没考虑好,是单独做个组件还是和输入框一起做一个组件,然后引入到form表单中。如果感兴趣大家可以自己尝试一下。
这里不再赘述,并附上所有的代码
<template> <div class="login-container"> <div class="login-top"> <div class="left">xxxxxxx</div> <div class="right"> <span class="window-min" @click="windowMin"> <a-icon type="minus" /> </span> <span class="window-close" @click="windowClose"> <a-icon type="close" /> </span> </div> <img src="[email protected]/assets/logo123.png" class="logo" /> </div> <div class="login-form"> <a-form-model ref="loginForm" :model="loginVo" :rules="rules"> <a-form-model-item :label-col="formItemLayout.labelCol" :wrapper-col="formItemLayout.wrapperCol" > <a-form-model-item prop="address" :style="{ display: 'inline-block', width: 'calc(60%)' }" > <a-input v-model="loginVo.address" placeholder="IP地址" @change="changeAddress" > <a-icon slot="prefix" type="global" /> </a-input> <div class="custom-tooltip" v-show="showAddress"> <div class="custom-srrow"></div> <div class="custom-info">{{ addressMsg }}</div> </div> </a-form-model-item> <a-form-model-item prop="port" :style="{ display: 'inline-block', width: 'calc(40%)' }" > <a-input-number @change="changePort" v-model="loginVo.port" placeholder="端口" :max="65535" :min="1" /> <div class="custom-tooltip" v-show="showPort"> <div class="custom-srrow"></div> <div class="custom-info">{{ portMsg }}</div> </div> </a-form-model-item> </a-form-model-item> <a-form-model-item :label-col="formItemLayout.labelCol" :wrapper-col="formItemLayout.wrapperCol" prop="username" > <a-input v-model="loginVo.username" placeholder="账号" @change="changeUsername" > <a-icon slot="prefix" type="user" /> </a-input> <div class="custom-tooltip" v-show="showUsername"> <div class="custom-srrow"></div> <div class="custom-info">{{ usernameMsg }}</div> </div> </a-form-model-item> <a-form-model-item :label-col="formItemLayout.labelCol" :wrapper-col="formItemLayout.wrapperCol" prop="password" > <a-input @change="changePassword" v-model="loginVo.password" type="password" placeholder="密码" > <a-icon slot="prefix" type="lock" /> </a-input> <div class="custom-tooltip" v-show="showPassword"> <div class="custom-srrow"></div> <div class="custom-info">{{ passwordMsg }}</div> </div> </a-form-model-item> <a-form-model-item :label-col="formItemLayout.labelCol" :wrapper-col="formItemLayout.wrapperCol" > <a-checkbox @change="rememberPassword" v-model="rememberMe" class="record-password" > 记住密码 </a-checkbox> </a-form-model-item> <a-form-model-item :label-col="formItemLayout.labelCol" :wrapper-col="formItemLayout.wrapperCol" > <a-button type="primary" @click="onSubmit" class="login-button" >登陆</a-button > </a-form-model-item> </a-form-model> </div> </div> </template> <script> import ipRules from "@/utils/ipRules"; import { formItemLayout, formTailLayout } from "@/utils/const"; import { message } from "ant-design-vue"; const { session } = require("electron"); export default { name: "login-page", data() { return { loginVo: { username: "", password: "", address: localStorage.getItem("address"), port: localStorage.getItem("port"), type: "login", }, showUsername: false, showPassword: false, showAddress: false, showPort: false, rememberMe: false, addressMsg: "请输入你的IP地址", usernameMsg: "请输入你的账户", passwordMsg: "请输入你的密码", portMsg: "请输入你的服务端口", formItemLayout, formTailLayout, rules: {}, }; }, created() { console.log("localStorage", localStorage); }, methods: { windowMin() { this.$electron.ipcRenderer.send("window-min"); }, windowClose() { this.$electron.ipcRenderer.send("window-close"); }, onSubmit() { if (this.loginVo.address === "" || this.loginVo.address === null) { this.showAddress = true; return; } if (this.loginVo.port === "" || this.loginVo.port === null) { this.showPort = true; return; } if (this.loginVo.username === "" || this.loginVo.username === null) { this.showUsername = true; return; } if (this.loginVo.password === "" || this.loginVo.password === null) { this.showPassword = true; return; } this.$electron.ipcRenderer.send("login", this.loginVo); this.$electron.ipcRenderer.once("login-message", (event, arg) => { let result = JSON.parse(arg); console.log("登陆结果:", result); if (result.type === "0") { localStorage.setItem("address", this.loginVo.address); localStorage.setItem("port", this.loginVo.port); localStorage.setItem("user", JSON.stringify(result)); if (rememberMe) { localStorage.setItem("username", this.loginVo.username); localStorage.setItem("password", this.loginVo.password); } this.$router.push("/home"); } else { this.$message.error(result.msg); } }); }, rememberPassword() {}, changeAddress() { if (ipRules.test(this.loginVo.address)) { this.showAddress = false; } else { this.showAddress = true; this.addressMsg = "你输入的IP地址规范"; } }, changePort() { if (this.loginVo.port === "" || this.loginVo.port === null) { this.showPort = true; } else { this.showPort = false; } }, changeUsername() { if (this.loginVo.username === "" || this.loginVo.username === null) { this.showUsername = true; } else { this.showUsername = false; } }, changePassword() { if (this.loginVo.password === "" || this.loginVo.password === null) { this.showPassword = true; } else { this.showPassword = false; } }, }, }; </script> <style lang="scss"> .login-container { width: 100vw; height: 100vh; -webkit-app-region: drag; //无边框下设置窗口可拖拽 font-size: 14px; .login-top { position: relative; width: 100%; height: 70px; background-color: #f3eff1; color: gray; .left { float: left; height: 30px; line-height: 30px; padding-left: 10px; } .right { float: right; .window-min, .window-close { width: 30px; height: 30px; line-height: 30px; display: inline-block; text-align: center; -webkit-app-region: no-drag; //事件处可以禁用拖拽区域 } .window-min:hover { background-color: rgb(209, 207, 207); } .window-close:hover { background-color: red; } } .logo { position: absolute; top: 40px; left: 160px; width: 70px; } } .login-form { width: 70vw; height: 150px; position: absolute; left: 15vw; top: 120px; // 去除input 默认的蓝色边框 .ant-input:focus, .ant-input-number:focus { border: none; border-bottom: 1px solid #12b7f5 !important; box-shadow: none; } .ant-row { margin-bottom: 0px; font-size: 12px; -webkit-app-region: no-drag; //事件处可以禁用拖拽区域 } .ant-input, .ant-input-number { width: 100%; border: none !important; border-bottom: 1px solid #e9e3e3 !important; border-radius: 0px; font-size: 12px; } .ant-input-number { height: 31px; } .ant-input-prefix { left: 0px; } .ant-input:hover,.ant-input-number:hover { border-bottom: 1px solid #bebebe ; } .login-button { border: none; display: block; width: 100%; background-color: #c22064; color: white; } .record-password { font-size: 12px; color: #b8b7b7; } } .custom-tooltip { position: absolute; top: 20px; z-index: 999; .custom-srrow { width: 0; height: 0; border: 5px solid; border-color: transparent transparent black; margin-left: 10px; } .custom-info { width: 120px; height: 25px; background-color: black; border-radius: 2px; color: white; line-height: 25px; padding-left: 10px; } } } </style>
好了,基本上就这么多了,如果有问题,欢迎留言!
【Eelectron-vue】构建桌面应用(15)- 修改antdv输入框默认样式相关教程
-
【转载】Hexo+GitHub构建个人博客
【转载】Hexo+GitHub构建个人博客 原文地址:https://www.namidame.tech/Hexo+GitHub_build_blog.html 本篇教程讲述如何用Hexo和GitHub搭建一个静态博客。 一、安装Node.js 到Node.js官网下载最新版本安装。本教程使用6.3.0版本。 安装完毕后打开命令行,输入
-
3W1H分析法,全面构建数据分析思维!
3W1H分析法,全面构建数据分析思维! 作者:陈新涛 转自:中国统计网 「Why-What-How」在讲解概念和执行上是个不错的思维模型,本文依例按此框架来拆分「数据分析」。相信很多朋友已经有了较丰富的分析经验,这里权且从个人的角度进行梳理,以资参考。为了帮
-
《Centos7——FileBeat+Redis+ELK构建企业级日志分析平台》
《Centos7——FileBeat+Redis+ELK构建企业级日志分析平台》 目录 ES-1部署(192.168.194.130) 1. 时间同步 2. 关闭防火墙 3. 安装JDK 4. 安装Elasticsearch 5. 查看ES集群是否同步 6. 安装logstash 7. 安装kibana 8. 部署redis 9. 测试redis ES2操作(192.168.1
-
获取windows当前桌面背景图 - 来自360浏览器的壁纸
获取windows当前桌面背景图 - 来自360浏览器的壁纸 本文仅限于获取 来自360浏览器的壁纸的windows系统。 打开计算机,进入“C:\Users\Administrator\AppData\Roaming\360browser\bkinfo” 看得到一大堆没有后缀名的文件,全部复制到 D:\tmp\ 使用快捷键“Win+
-
VMware安装Centos7构建本地测试的环境
VMware安装Centos7构建本地测试的环境 1安装好相关的虚拟机 软件:推荐使用VMwear,我用的是VMwear 12 镜像:CentOS7 ,如果没有镜像可以在官网下载 :http://isoredirect.centos.org/centos/7/isos/x86_64/CentOS-7-x86_64-DVD-1804.iso 硬件:因为是在宿主机
-
java-实现桌面壁纸自动切换(有界面,可还以自己设置时间的那种
java-实现桌面壁纸自动切换(有界面,可还以自己设置时间的那种哦) 前言 界面以及功能实现 本篇文章主要讲java的GUI技术搭建页面和java的时间驱动来监听操作,jsoup分析网页数据,以及用到fastjson来进行json数据交互。 项目结构如下: 效果图: 文章很长,
-
详解构建可运行的JavaScript规范的方法
编程不仅仅是给计算机下达如何完成一项任务的指令,它还包括以一种精确的方式与他人交流思想,甚至是与未来的自己。这样的交流可以有多个目标,也许是为了共享信息,或者只是为了更容易地修改—如果你不理解或不记得很久以前做过什么,那么就很难修改。 当我
-
python自动构建Markdown博客列表
python自动构建Markdown博客列表 python 构建博客列表 https://blog.csdn.net/nameofcsdn/article/details/104988119 这篇博客中我给出了一个python程序代码,用来输出所有博客的标题和url 因为CSDN恶心的限制,一篇文章不能超过64000字,所以我不得不用Markd