【Eelectron-vue】构建桌面应用(15)- 修改antdv输入框默认样式

作者:神秘网友 发布时间:2020-10-24 01:42:51

【Eelectron-vue】构建桌面应用(15)- 修改antdv输入框默认样式

【Eelectron-vue】构建桌面应用(15)- 修改antdv输入框默认样式及表单校验提示信息

1.前言

现在的客户端与以往的有些区别,比如登录页面的输入框,大都废弃了之前的登录框样式,而采用只存在底边的输入框。还有就是输入框的错误信息都会采用浮层的形式提示,而不是瀑布流的方式去提示。

在借鉴了其他客户端的方式,做了一些调整,这些调整都需要对antdv组件进行从新修正。因为我是用的是electron-vue+antdv的架构去构建的应用。

2.修改输入框的默认样式

先看一下效果图如下:
【Eelectron-vue】构建桌面应用(15)- 修改antdv输入框默认样式

我们要做的事情就是:

  1. 修改输入框为只有底边
  2. 当输入框获取悬浮事件时,改变border的颜色
  3. 当输入框获取焦点事件时,修改边框的颜色

2.1.1 去除输入框其他边框

antdv的处理方式和普通的输入框不一样,我们可以先设置一下输入框的border,因为是antdv提供的组件,所以我们需要F12查看输入框绑定的样式,然后去覆盖该样式的border
【Eelectron-vue】构建桌面应用(15)- 修改antdv输入框默认样式
如上图所示,我们直接去覆盖ant-inputant-input-number的样式。

.ant-input,
.ant-input-number {
  width: 100%;
  border: none !important;
  border-bottom: 1px solid #e9e3e3 !important;
}

这样就可以实现只保留底边。

2.1.2 鼠标移动时改变边框

那么我们现在来做进一步的优化,就是当我们移动到上面时去改变边框的颜色,这个也简单只要设置ant-inputhover事件就行了。

.ant-input:hover,.ant-input-number:hover {
  border-bottom: 1px solid #bebebe !important;
}

【Eelectron-vue】构建桌面应用(15)- 修改antdv输入框默认样式
鼠标悬浮时,改变边框的颜色。

2.1.3 输入框获取焦点时改变输入框的颜色

我本来以为这个也简单,当输入框获取focus时,直接改变底边的颜色即可。但是发现并非如此。

.ant-input:focus,
.ant-input-number:focus {
  border: none;
  border-bottom: 1px solid #12b7f5 !important;
}

【Eelectron-vue】构建桌面应用(15)- 修改antdv输入框默认样式
可以发现,并没有生效。看了下网上说的,蓝色边框是输入框的外部轮廓,而不是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;
}

【Eelectron-vue】构建桌面应用(15)- 修改antdv输入框默认样式
确实获取焦点时,所谓的轮廓就没有了,看来是阴影的问题。到此,修改边框功能基本上实现了。只保留底边,鼠标悬浮时会改变底边的颜色,并且获取焦点时也可以去除阴影改变底边颜色。

但是还有一个问题,当获取焦点时同时会触发悬浮的事件,导致只有鼠标离开时才会变成获取焦点时的蓝色。

这个问题是由于权重相同导致的。怎么理解呢?因为我在获取焦点时,给的是!important,而在悬浮时也是设置了!important,所以导致了两个事件的权重一样,那么在输入框获取焦点时,颜色变为蓝色。当你鼠标移出输入框,但是光标还在输入框的情况下,触发了输入框的hover事件,所以输入框底边就会变成灰色。

解决办法就是,删除悬浮时底边颜色的权重!importan,这样获取焦点事件的权重比悬浮事件的权重要高,当鼠标离开输入框,在回到输入框时,也不会去覆盖获取焦点的颜色。

我发现现在大多数客户端的校验提示信息都不是瀑布流的方式(即在输入框下面留了一大片空白,来显示错误信息),而是通过浮层的方式去处理这个问题。

传统的方式:
【Eelectron-vue】构建桌面应用(15)- 修改antdv输入框默认样式
这种在客户端尤其是只有底边的的情况下,会有很大的空白,页面显得大而宽泛。需要给登陆页面设置较大的宽和高。而正常的登陆基本上就是用户名密码登陆按钮这些,不需要太多留白。在antdv中默认的留白是32px吧,我记得是。所以为每一个表单项多留出32px的空白,就显得这个页面不紧凑。所以我们需要重新去修改一下这种提示信息的位置。

而我们要实现的效果就是这种浮层的形式提示表单的错误信息:
【Eelectron-vue】构建桌面应用(15)- 修改antdv输入框默认样式
于是研究了一下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>

【Eelectron-vue】构建桌面应用(15)- 修改antdv输入框默认样式
然后我们将黑色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>

【Eelectron-vue】构建桌面应用(15)- 修改antdv输入框默认样式
然后将,这样我们只要把其他的边框设置透明就能让它只显示一块了

<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>

【Eelectron-vue】构建桌面应用(15)- 修改antdv输入框默认样式
接着,把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>

【Eelectron-vue】构建桌面应用(15)- 修改antdv输入框默认样式
这么一个小三角形就出来了,然后打开下面的那个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>

【Eelectron-vue】构建桌面应用(15)- 修改antdv输入框默认样式
是不是就有内个味了。

好了回到我们的项目中,需要在我们的表单输入框的父节点下,加上我们刚才的代码,并且添加控制显示影藏的属性

<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输入框默认样式相关教程

  1. 【转载】Hexo+GitHub构建个人博客

    【转载】Hexo+GitHub构建个人博客 原文地址:https://www.namidame.tech/Hexo+GitHub_build_blog.html 本篇教程讲述如何用Hexo和GitHub搭建一个静态博客。 一、安装Node.js 到Node.js官网下载最新版本安装。本教程使用6.3.0版本。 安装完毕后打开命令行,输入

  2. 3W1H分析法,全面构建数据分析思维!

    3W1H分析法,全面构建数据分析思维! 作者:陈新涛 转自:中国统计网 「Why-What-How」在讲解概念和执行上是个不错的思维模型,本文依例按此框架来拆分「数据分析」。相信很多朋友已经有了较丰富的分析经验,这里权且从个人的角度进行梳理,以资参考。为了帮

  3. 《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

  4. 获取windows当前桌面背景图 - 来自360浏览器的壁纸

    获取windows当前桌面背景图 - 来自360浏览器的壁纸 本文仅限于获取 来自360浏览器的壁纸的windows系统。 打开计算机,进入“C:\Users\Administrator\AppData\Roaming\360browser\bkinfo” 看得到一大堆没有后缀名的文件,全部复制到 D:\tmp\ 使用快捷键“Win+

  5. 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 硬件:因为是在宿主机

  6. java-实现桌面壁纸自动切换(有界面,可还以自己设置时间的那种

    java-实现桌面壁纸自动切换(有界面,可还以自己设置时间的那种哦) 前言 界面以及功能实现 本篇文章主要讲java的GUI技术搭建页面和java的时间驱动来监听操作,jsoup分析网页数据,以及用到fastjson来进行json数据交互。 项目结构如下: 效果图: 文章很长,

  7. 详解构建可运行的JavaScript规范的方法

    编程不仅仅是给计算机下达如何完成一项任务的指令,它还包括以一种精确的方式与他人交流思想,甚至是与未来的自己。这样的交流可以有多个目标,也许是为了共享信息,或者只是为了更容易地修改—如果你不理解或不记得很久以前做过什么,那么就很难修改。 当我

  8. python自动构建Markdown博客列表

    python自动构建Markdown博客列表 python 构建博客列表 https://blog.csdn.net/nameofcsdn/article/details/104988119 这篇博客中我给出了一个python程序代码,用来输出所有博客的标题和url 因为CSDN恶心的限制,一篇文章不能超过64000字,所以我不得不用Markd