修改el-form制作自定义元素登录框

最近看到react 和vuetify 的输入框聚焦文字会变小上移,觉得它们框架有的效果,element你也应该有,于是乎,就尝试修改,试了很久发现 el-form-item 中的label和input根本连不上关系,就想到了一个解决办法,那就是el-input中有个自定义的slot属性,借此属性得以实现效果。

下面就是文档的属性。
在这里插入图片描述
这是最终效果图。截图的颜色不好看,有条件的自己修改一下样式即可。
在这里插入图片描述

代码有点套娃,有条件的可以使用slot去进行二次封装。
  <template>
  <div class="login">
    <el-form ref="form" :model="form" :rules="rules" class="login-form" auto-complete="off">

      <el-form-item prop="username">
        <el-input v-model="form.username" type="text" @keyup.enter.native="handleLogin" required clearable>
          <span slot="prefix" class="wrap">
            <i class="el-input__icon el-icon-user-solid" />
            <span>名称</span>
          </span>
        </el-input>
      </el-form-item>

      <el-form-item prop="password">
        <el-input v-model="form.password" type="password" @keyup.enter.native="handleLogin" required clearable>
          <span slot="prefix" class="wrap">
            <i class="el-input__icon el-icon-lock" />
            <span>密码</span>
          </span>
        </el-input>
      </el-form-item>

      <el-form-item prop="captcha">

        <div class="flex justify-around">

          <el-input class="w--55%" v-model="form.captcha" placeholder="验证码" @keyup.enter.native="submit">
            <svg-icon slot="prefix" icon-class="validCode" class="el-input__icon input-icon" />
          </el-input>

          <div class="login-code w--40%">
            <canvas ref="captcha" width="100%" height="38px" @click="captcha('captcha')" />
          </div>

        </div>

      </el-form-item>

      <el-checkbox v-model="form.rememberMe" style="margin: 0px 0px 25px 0px">记住密码</el-checkbox>
      <el-form-item style="width: 100%">
        <el-button :loading="loading" size="medium" type="primary" style="width: 100%" @click.native.prevent="submit">
          {{ loading ? "登 录 中..." : "登 录" }}
        </el-button>

        <div style="float: right" v-if="register">
          <router-link class="link-type" :to="'/register'">立即注册</router-link>
        </div>

      </el-form-item>
    </el-form>
    <!--  底部  -->
    <div class="el-login-footer">
      <span>Copyright © 2018-2022 ruoyi.vip All Rights Reserved.</span>
    </div>

  </div>
</template>

<script>
import Cookies from "js-cookie";
import { encrypt, decrypt } from "@/utils/jsencrypt";
import { mapActions } from 'pinia';
import { user } from '@/pinia';
export default {
  name: "Login",
  data() {
    const validator = (rule, value, callback) => {
      if (value !== this.captchaCode) return callback(new Error(rule.message))
      callback();
    };
    return {
      form: {
        username: "admin",
        password: "123456",
        captcha: "",
        rememberMe: false,
        uuid: "ccf0a46bfcf9409b95e9aea8700c9ed7",
      },
      rules: {
        username: [{ required: true, trigger: "blur", message: "请输入账号" }],
        password: [{ required: true, trigger: "blur", message: "请输入密码" }],
        captcha: [{ required: true, trigger: 'blur', message: "请输入验证码" }, { validator, trigger: 'blur', message: "验证码错误" }],
      },
      captchaCode: '',
      loading: false,
      register: false, // 注册开关
      redirect: undefined,
    };
  },
  mounted() {
    this.getCookie();
    this.captcha('captcha');
  },
  methods: {
    ...mapActions(user, ['login']),
    getCookie() {
      const username = Cookies.get("username");
      const password = Cookies.get("password");
      const rememberMe = Cookies.get("rememberMe");
      this.form = {
        username: !username ? this.form.username : username,
        password: !password ? this.form.password : decrypt(password),
        rememberMe: !rememberMe ? false : Boolean(rememberMe),
      };
    },
    submit() {
      this.$refs.form.validate(async (valid) => {
        if (!valid) return;
        this.loading = true;
        if (this.form.rememberMe) {
          Cookies.set("username", this.form.username, { expires: 30 });
          Cookies.set("password", encrypt(this.form.password), { expires: 30 });
          Cookies.set("rememberMe", this.form.rememberMe, { expires: 30 });
        } else {
          Cookies.remove("username");
          Cookies.remove("password");
          Cookies.remove("rememberMe");
        }
        try {
          await this.login(this.form);
          this.$router.push({ path: this.redirect || "/" }).catch(() => { });
        } catch (error) {
          console.error(error);
          this.loading = false;
          this.captcha('captcha');
        }
      });
      this.loading = false;
    },
    // 获取生成验证码的canvas
    captcha(el) {
      const container = {
        scope: (max, min) => parseInt(Math.random() * (max - min) + min),
        color: (max, min, o = 1) => `rgba(${container.scope(max, min)},${container.scope(max, min)},${container.scope(max, min)},${o})`,
        num: (n) => Math.floor(Math.random() * n),
        isnum: (n) => typeof n === 'number' && !isNaN(n),
        operator: ['+', '-', '*'],
        rect: [0, 0, 100, 38],
        calculate: [0, '+-*/', 0, '=?']
      }
      const { scope, color, num, isnum, operator, rect, calculate: calcu } = container;
      const ctx = this.$refs[el].getContext("2d");
      const [x1, y1, x2, y2] = rect;

      ctx.clearRect(x1, y1, x2, y2);
      ctx.fillStyle = color(180, 230);
      ctx.save()
      ctx.fillRect(x1, y1, x2, y2);

      Array.from({ length: 4 }, (_, index) => {
        ctx.beginPath();
        ctx.strokeStyle = color(80, 180, .33);
        ctx.save();
        ctx.moveTo(scope(0, x2), scope(0, y2));
        ctx.moveTo(scope(0, x2), scope(0, y2));
        ctx.lineTo(scope(0, x2), scope(0, y2));
        ctx.stroke();
        ctx.beginPath();
        ctx.fillStyle = color(0, 255, .33);
        ctx.save();
        ctx.arc(scope(0, x2), scope(0, y2), 1, 0, 2 * Math.PI);
        ctx.fill();
        ctx.font = `${scope(18, 30)}px Simhei`;
        ctx.fillStyle = color(80, 150);
        ctx.save();
        let item = calcu[index];
        isnum(item) && (item = num(10));
        item === '+-*/' && (item = operator[num(3)]);
        index === 2 && calcu[1] === '-' && calcu[0] <= item && (item = calcu[0] ? calcu[0] : 0);
        calcu[index] = item;
        ctx.translate(22 * index + -12, 10);
        ctx.rotate((scope(-12, 12) * Math.PI) / 180);
        ctx.fillText(item, y2 / 2, y2 / 2);
        ctx.restore();
      });
      const result = eval(calcu.slice(0, calcu.length - 1).join(""));
      this.captchaCode = result.toString();
      return result.toString();
    },
  },
  watch: {
    $route: {
      handler(route) {
        this.redirect = route.query && route.query.redirect;
      },
      immediate: true,
    },
  },
};
</script>

<style rel="stylesheet/scss" lang="scss" scoped>
	.login {
	  display: flex;
	  justify-content: center;
	  align-items: center;
	  height: 100%;
	  background-image: linear-gradient(
	    to right,
	    #00c6fb,
	    #005bea,
	    #00c6fb,
	    #005bea
	  );
	  background-size: cover;
	  .title {
	    margin: 0px auto 30px auto;
	    text-align: center;
	    color: #707070;
	  }
	  .el-form {
	    position: absolute;
	    top: 50%;
	    left: 75%;
	    width: 400px;
	    padding: 40px 40px 40px 40px;
	    transform: translate(-50%, -50%);
	    background: rgba(0, 0, 0, 0.2);
	    box-sizing: border-box;
	    box-shadow: 0 15px 25px rgba(0, 0, 0, 0.6);
	    border-radius: 10px;
	    .el-form-item {
	      margin-bottom: 30px;
	      h2 {
	        text-align: center;
	        color: #c0c4cc;
	      }
	      /deep/ {
	        .el-input__inner {
	          border: none;
	          border-radius: 0;
	          border-bottom: 1px solid #fff;
	          background-color: transparent !important;
	          color: #c0c4cc;
	          // 伪元素的修改 line
	          & + .el-input__prefix::before {
	            content: "";
	            position: absolute;
	            display: block;
	            bottom: 0%;
	            left: 50%;
	            width: 0;
	            height: 2px;
	            background-color: #409eff;
	            transition: all 0.8s;
	          }
	          &:focus + .el-input__prefix::before {
	            left: 0;
	            width: 100%;
	          }
	          // label的修改 文本存在时的显示css
	          &:-webkit-autofill ~ .el-input__prefix > .wrap,
	          &:focus ~ .el-input__prefix > .wrap,
	          &:valid ~ .el-input__prefix > .wrap {
	            color: #fff;
	            display: inline-block;
	            font-size: 14px;
	            transform: perspective(1000px) translate3d(-10px, -24px, -100px);
	            transition: all 0.4s;
	          }
	        }
	        .el-input__prefix {
	          width: 100%;
	          text-align: left;
	          color: #c0c4cc;
	          font-size: 18px;
	          left: 0px;
	          pointer-events: none;
	          transform-style: preserve-3d;
	          & .wrap {
	            transform: perspective(1000px) translate3d(0, 0, 0);
	            transition: all 0.4s;
	          }
	        }
	      }
	    }
	  }
	
	  .el-login-footer {
	    height: 40px;
	    line-height: 40px;
	    position: fixed;
	    bottom: 0;
	    width: 100%;
	    text-align: center;
	    color: #fff;
	    font-family: Arial;
	    font-size: 12px;
	    letter-spacing: 1px;
	  }
	}
</style>

<style lang="scss">
	input:-webkit-autofill,
	textarea:-webkit-autofill,
	select:-webkit-autofill {
	  -webkit-text-fill-color: #ededed !important; //字体颜色
	  background-color: transparent;
	  background-image: none;
	  transition: background-color 50000s ease-in-out 0s; //背景色透明  生效时长  过渡效果  启用时延迟的时间
	}
</style>

<style lang="scss">
$num: 0; // padding、margin
@while $num <= 60 {
  .p-#{$num} {
    padding: #{$num}px !important;
  }
  .pt-#{$num} {
    padding-top: #{$num}px !important;
  }
  .pb-#{$num} {
    padding-bottom: #{$num}px !important;
  }
  .pl-#{$num} {
    padding-left: #{$num}px !important;
  }
  .pr-#{$num} {
    padding-right: #{$num}px !important;
  }
  .px-#{$num} {
    padding-left: #{$num}px !important;
    padding-right: #{$num}px !important;
  }
  .py-#{$num} {
    padding-top: #{$num}px !important;
    padding-bottom: #{$num}px !important;
  }
  .m-#{$num} {
    margin: #{$num}px !important;
  }
  .mt-#{$num} {
    margin-top: #{$num}px !important;
  }
  .mb-#{$num} {
    margin-bottom: #{$num}px !important;
  }
  .ml-#{$num} {
    margin-left: #{$num}px !important;
  }
  .mr-#{$num} {
    margin-right: #{$num}px !important;
  }
  .mx-#{$num} {
    margin-left: #{$num}px !important;
    margin-right: #{$num}px !important;
  }
  .my-#{$num} {
    margin-top: #{$num}px !important;
    margin-bottom: #{$num}px !important;
  }
  $num: $num + 5;
}
$pos: -20; // top、left、right、bottom
@while $pos <= 20 {
  .top-#{$pos} {
    top: #{$pos}px !important;
  }
  .bottom-#{$pos} {
    bottom: #{$pos}px !important;
  }
  .left-#{$pos} {
    left: #{$pos}px !important;
  }
  .right-#{$pos} {
    right: #{$pos}px !important;
  }
  $pos: $pos + 5;
}
$spacing: 0; // width-px、height-px
@while $spacing <= 100 {
  .w--#{$spacing} {
    width: #{$spacing}px !important;
  }
  .h--#{$spacing} {
    height: #{$spacing}px !important;
  }
  $spacing: $spacing + 5;
}
$percentage: 0; // width-%、height-%
@while $percentage <= 100 {
  .w--#{$percentage}\% {
    width: #{$percentage + "%"} !important;
  }
  .h--#{$percentage}\% {
    height: #{$percentage + "%"} !important;
  }
  $percentage: $percentage + 5;
}
.w--unset {
  width: unset !important;
}
.font-style {
  font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif;
}
html,
body {
  height: 100% !important;
}
.fr {
  float: right !important;
  &::after {
    content: "";
    display: block;
    clear: both;
  }
}
.fl {
  float: right !important;
  &::after {
    content: "";
    display: block;
    clear: both;
  }
}
.text-xs {
  font-size: 12px;
}
.text-sm {
  font-size: 14px;
}
.text-base {
  font-size: 16px;
}
.text-lg {
  font-size: 18px;
}
.text-xl {
  font-size: 20px;
}
.truncate {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.line-clamp-1 {
  overflow: hidden;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 1;
}
.line-clamp-2 {
  overflow: hidden;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
}
.line-clamp-3 {
  overflow: hidden;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
}
.line-clamp-4 {
  overflow: hidden;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 4;
}
.select-none {
  user-select: none !important;
}
.w-full {
  width: 100% !important;
}
.h-full {
  height: 100% !important;
}
.relative {
  position: relative !important;
}
.absolute {
  position: absolute !important;
}
.fixed {
  position: fixed !important;
}
.flex {
  display: flex !important;
}
.inline-block {
  display: inline-block !important;
}
.hidden {
  display: none !important;
}
.text-center {
  text-align: center !important;
}
.text-left {
  text-align: left !important;
}
.text-right {
  text-align: right !important;
}
.cursor-pointer {
  cursor: pointer !important;
}
.flex-wrap {
  flex-wrap: wrap !important;
}
.flex-nowrap {
  flex-wrap: nowrap !important;
}
.flex-col {
  flex-direction: column !important;
}
.flex-row {
  flex-direction: row !important;
}
.justify-center {
  justify-content: center !important;
}
.self-center {
  align-self: center !important;
}
.justify-between {
  justify-content: space-between !important;
}
.justify-around {
  justify-content: space-around !important;
}
.justify-evenly {
  justify-content: space-evenly !important;
}
.items-baseline {
  align-items: baseline !important;
}
.items-center {
  align-items: center !important;
}
.items-stretch {
  align-items: stretch !important;
}
.align-top {
  vertical-align: top !important;
}
.align-middle {
  vertical-align: middle !important;
}
.align-bottom {
  vertical-align: bottom !important;
}
.overflow-hidden {
  overflow: hidden !important;
}
.overflow-y {
  overflow-y: scroll !important;
}
.overflow-x {
  overflow-x: scroll !important;
}
.rounded-none {
  border-radius: 0 !important;
}
.rounded-sm {
  border-radius: 2px !important;
}
.rounded {
  border-radius: 4px !important;
}
.rounded-md {
  border-radius: 6px !important;
}
.rounded-lg {
  border-radius: 8px !important;
}
.rounded-xl {
  border-radius: 12px !important;
}
.rounded-2xl {
  border-radius: 16px !important;
}
.rounded-3xl {
  border-radius: 24px !important;
}
.rounded-full {
  border-radius: 99999px !important;
}
.shadow-sm {
  box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
}
.shadow {
  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1);
}
.shadow-md {
  box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1);
}
.shadow-lg {
  box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1);
}
.shadow-xl {
  box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1);
}
.shadow-2xl {
  box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
}
.shadow-inner {
  box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.05);
}
.shadow-none {
  box-shadow: 0 0 #0000;
}
.grid {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  @at-root .gap {
    gap: 4px !important;
  }
  @at-root .gap-2 {
    gap: 8px !important;
  }
  @at-root .gap-3 {
    gap: 12px !important;
  }
  .grid-item-1 {
    grid-area: auto / span 1 !important;
  }
  .grid-item-2 {
    grid-area: auto / span 2 !important;
  }
  .grid-item-3 {
    grid-area: auto / span 3 !important;
  }
  .grid-item-4 {
    grid-area: auto / span 4 !important;
  }
  .grid-item-5 {
    grid-area: auto / span 5 !important;
  }
  .grid-item-6 {
    grid-area: auto / span 6 !important;
  }
  .grid-item-7 {
    grid-area: auto / span 7 !important;
  }
  .grid-item-8 {
    grid-area: auto / span 8 !important;
  }
  .grid-item-9 {
    grid-area: auto / span 9 !important;
  }
  .grid-item-10 {
    grid-area: auto / span 10 !important;
  }
  .grid-item-11 {
    grid-area: auto / span 11 !important;
  }
  .grid-item-12 {
    grid-area: auto / span 12 !important;
  }
}

<style>
请勿转载
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值