Resssssss

Resssssss 查看完整档案

张家口编辑  |  填写毕业院校  |  填写所在公司/组织 xue-kaiyu.gitee.io/ 编辑
编辑
_ | |__ _ _ __ _ | '_ \| | | |/ _` | | |_) | |_| | (_| | |_.__/ \__,_|\__, | |___/ 个人简介什么都没有

个人动态

Resssssss 发布了文章 · 3月2日

Vue Antd Admin关于登录的一些小坑

最近在开发一个管理的前后端分离页面 前一阵子都在写静态页 今天来了登录接口 准备搞一下 把自己的思路和遇到的一些小坑告诉大家

因为antd admin给我们已经封装好了登录页面 我们只需要拿来改一改就好
首页打开scr/pages/login/Login.vue文件

<template>
  <common-layout>
    <div class="top">
      <div class="header">
        ![](@/assets/img/logo.png)
        <span class="title">{{systemName}}</span>
      </div>
      <div class="desc">Ant Design 是西湖区最具影响力的 Web 设计规范</div>
    </div>
    <div class="login">
      <a-form @submit="onSubmit" :form="form">
        <a-tabs size="large" :tabBarStyle="{textAlign: 'center'}" style="padding: 0 2px;">
          <a-tab-pane tab="账户密码登录" key="1">
            <a-alert type="error" :closable="true" v-show="error" :message="error" showIcon style="margin-bottom: 24px;" />
            <a-form-item>
              <a-input
                autocomplete="autocomplete"
                size="large"
                placeholder="admin"
                v-decorator="['name', {rules: [{ required: true, message: '请输入账户名', whitespace: true}]}]"
              >
                <a-icon slot="prefix" type="user" />
              </a-input>
            </a-form-item>
            <a-form-item>
              <a-input
                size="large"
                placeholder="888888"
                autocomplete="autocomplete"
                type="password"
                v-decorator="['password', {rules: [{ required: true, message: '请输入密码', whitespace: true}]}]"
              >
                <a-icon slot="prefix" type="lock" />
              </a-input>
            </a-form-item>
          </a-tab-pane>
          <a-tab-pane tab="手机号登录" key="2">
            <a-form-item>
              <a-input size="large" placeholder="mobile number" >
                <a-icon slot="prefix" type="mobile" />
              </a-input>
            </a-form-item>
            <a-form-item>
              <a-row :gutter="8" style="margin: 0 -4px">
                <a-col :span="16">
                  <a-input size="large" placeholder="captcha">
                    <a-icon slot="prefix" type="mail" />
                  </a-input>
                </a-col>
                <a-col :span="8" style="padding-left: 4px">
                  <a-button style="width: 100%" class="captcha-button" size="large">获取验证码</a-button>
                </a-col>
              </a-row>
            </a-form-item>
          </a-tab-pane>
        </a-tabs>
        <div>
          <a-checkbox :checked="true" >自动登录</a-checkbox>
          <a style="float: right">忘记密码</a>
        </div>
        <a-form-item>
          <a-button :loading="logging" style="width: 100%;margin-top: 24px" size="large" htmlType="submit" type="primary">登录</a-button>
        </a-form-item>
        <div>
          其他登录方式
          <a-icon class="icon" type="alipay-circle" />
          <a-icon class="icon" type="taobao-circle" />
          <a-icon class="icon" type="weibo-circle" />
          <router-link style="float: right" to="/dashboard/workplace" >注册账户</router-link>
        </div>
      </a-form>
    </div>
  </common-layout>
</template>

<script>
import CommonLayout from '@/layouts/CommonLayout'
import {login, getRoutesConfig} from '@/services/user'
import {setAuthorization} from '@/utils/request'
import {loadRoutes} from '@/utils/routerUtil'
import {mapMutations} from 'vuex'

export default {
  name: 'Login',
  components: {CommonLayout},
  data () {
    return {
      logging: false,
      error: '',
      form: this.$form.createForm(this)
    }
  },
  computed: {
    systemName () {
      return this.$store.state.setting.systemName
    }
  },
  methods: {
    ...mapMutations('account', ['setUser', 'setPermissions', 'setRoles']),
    onSubmit (e) {
      e.preventDefault()
      this.form.validateFields((err) => {
        if (!err) {
          this.logging = true
          const name = this.form.getFieldValue('name')
          const password = this.form.getFieldValue('password')
          login(name, password).then(this.afterLogin)
        }
      })
    },
    afterLogin(res) {
      this.logging = false
      const loginRes = res.data
      if (loginRes.code >= 0) {
        const {user, permissions, roles} = loginRes.data
        this.setUser(user)
        this.setPermissions(permissions)
        this.setRoles(roles)
        setAuthorization({token: loginRes.data.token, expireAt: new Date(loginRes.data.expireAt)})
        // 获取路由配置
        getRoutesConfig().then(result => {
          const routesConfig = result.data.data
          loadRoutes(routesConfig)
          this.$router.push('/dashboard/workplace')
          this.$message.success(loginRes.message, 3)
        })
      } else {
        this.error = loginRes.message
      }
    }
  }
}
</script>

这里的逻辑是点击Submit获取到表单的name和password然后调用login函数 执行完login函数后执行afterLogin函数

现在我们来看login 在src/services/user.js中

import {LOGIN, ROUTES} from '@/services/api'
import {request, METHOD, removeAuthorization} from '@/utils/request'

/**
 * 登录服务
 * @param name 账户名
 * @param password 账户密码
 * @returns {Promise<AxiosResponse<T>>}
 */
export async function login(name, password) {
  return request(LOGIN, METHOD.POST, {
    user_name: name,
    password: password
  })
}

export async function getRoutesConfig() {
  return request(ROUTES, METHOD.GET)
}

/**
 * 退出登录
 */
export function logout() {
  localStorage.removeItem(process.env.VUE_APP_ROUTES_KEY)
  localStorage.removeItem(process.env.VUE_APP_PERMISSIONS_KEY)
  localStorage.removeItem(process.env.VUE_APP_ROLES_KEY)
  removeAuthorization()
}
export default {
  login,
  logout,
  getRoutesConfig
}

这里通过返回的用户名和密码来发送请求 request这个函数是封装好的 这个相信大家都可以看懂 把参数的键值改成自己接口的就好

getRoutesConfig()这个函数是用来动态获取路由的 我这里获取的是静态的本地路由 所以暂时不需要 LOGIN这个值就是请求的地址 写在了api.js中 便于管理

//跨域代理前缀
const API_PROXY_PREFIX='/api'
const BASE_URL = process.env.NODE_ENV === 'production' ? process.env.VUE_APP_API_BASE_URL : API_PROXY_PREFIX
// const BASE_URL = process.env.VUE_APP_API_BASE_URL
module.exports = {
  LOGIN: `${BASE_URL}/Login/index`,
  ROUTES: `${BASE_URL}/routes`
}

我需要发送请求所以使用代理 把LOGIN的后缀改成你登陆的接口即可
然后到这里我们的登陆操作实际上已经完成了 但是我们还需要处理afterLogin的数据

然后让我们回到Login页面

      this.logging = false
      const loginRes = res.data
      if (loginRes.code >= 0) {
        const {user, permissions, roles} = loginRes.data
        this.setUser(user)
        this.setPermissions(permissions)
        this.setRoles(roles)
        setAuthorization({token: loginRes.data.token, expireAt: new Date(loginRes.data.expireAt)})
        // 获取路由配置
        getRoutesConfig().then(result => {
          const routesConfig = result.data.data
          loadRoutes(routesConfig)
          this.$router.push('/dashboard/workplace')
          this.$message.success(loginRes.message, 3)
        })
      } else {
        this.error = loginRes.message
      }
    }

让我们重点来看afterLogin这个函数 逻辑是登录后 进行信息存储
user为用户的基本信息包括昵称头像等等
permissions是用户权限信息 包括用户可以进行哪些操作
roles是用户的角色信息 可以通过角色来进行权限管理
官方的写法是登录的返回接口必须包含这些信息且数据格式也要一样 因为我们只规定了简单的角色id 所以这里需要重新处理一下
这里注意 如果不使用动态路由配置的话 getRouterConfig这个函数就可以删掉了

    afterLogin(res) {
      this.logging = false
      const loginRes = res.data
      console.log(loginRes.data.token)
      if (loginRes.code >= 0) {
        // const {user, permissions, roles} = loginRes.data

   
        let user = {
          address: '河北',
          avatar: "https://gw.alipayobjects.com/zos/rmsportal/ubnKSIfAJTxIgXOKlciN.png",
          name: 'admin',
        }
       let  permissions = [{
          id: 'form',                                    //权限ID
          operation: ['add', 'delete', 'edit', 'close']  //权限下的操作权限
        }]
        let roles = [{
          id: 'test',                                   //角色ID
          operation: ['add', 'delete', 'edit', 'close']  //角色的操作权限
        }]
        this.setUser(user)
        this.setPermissions(permissions)
        this.setRoles(roles)
        setAuthorization({token: loginRes.data.token})
        this.$router.push('/publicStore')
        this.$message.success(loginRes.message, 3)
      } else {
        this.error = loginRes.message
      }
    },

因为现在只有admin的概念 所以 我就把角色先写死了
这里的坑就是 权限和角色的类型必须是数组或者直接写一个简单的字符串比如 permission = 'form' 我一开始看文档 直接复制粘贴文档的内容 登录以后 一直报错 路由不显示 后来才发现 roles和permission实际上是数组

查看原文

赞 0 收藏 0 评论 0

Resssssss 回答了问题 · 2月22日

解决vue v-for 第一个元素少了 data-v-xxx 属性

我最近也发现这个问题了 但是我没有用cell组件 直接循环的div自己写的样式 最神奇的是 我有两个一样的组件 样式 逻辑都一模一样 就是数据不一样 另一个组件没有出现这个问题

关注 7 回答 4

Resssssss 发布了文章 · 2020-12-31

Vue中使用localStorage存储token并设置时效

相信大家平常的有的业务逻辑
就是登录之后要把token存下来
之前的项目大多数用的都是cookie
在这次的项目我用了vue-cookies保存token
保存没有问题 但是我需要再main.js中写一个全局的方法需要获取cookie中的token 这时候就发现了 无论是我用vue-cookies的方法 还是js原生的获取cookie都获取不到 百度了很多方法都没有实现
于是还是决定使用localStorage localStorage是没有时效设置的 所以需要自己写一个方法来设置时效
代码参考https://blog.csdn.net/yunchon...

首先新建一个js文件用来放封装的方法
storage.js

let myStorage=(function() {
    function setItem(params) {//存入localStorage方法
        // 存入的参数
        const obj = {
            name: "",   // 存入的名字
            value: "",   // 存入的值
            expires: "",   // 过期时间
            startTime: new Date().getTime()  //存入的时间
        }
        const options = {};// 将obj 和传进来的params 合并  放到options里面 实现js中浅拷贝
        Object.assign(options, obj, params);
        // 判断用户是否设置了过期时间
        if (options.expires) {
            //  以options.name为key,    options为值放进去
            localStorage.setItem(options.name, JSON.stringify(options));
        } else {
            // 如果 options.expires 没有设置的话, 就判断一下value的类型
            // 注意 我们 的 localStorage 只能存储字符串 像是数组和对象要转换下
            let type = Object.prototype.toString.call(options.value);
            if (type == '[object Object]' || type == '[object Array]') {
                options.value = JSON.stringify(options.value);
            }
            localStorage.setItem(options.name, options.value);
        }
    }
    // 获取数值
    function getItem(name) {
        let item = localStorage.getItem(name);
        // 判断 item 是否存在 
        if (item) {
            // 先将取到的对象 看能转换成object 对象格式,不能就说明不是json字符串形式
            try {
                item = JSON.parse(item);
            } catch (error) {
                item = item;
            }
            // 如果有expires的值,说明设置了失效时间
            if (item.expires) {
                    // 获取当前时间
                let now = new Date().getTime();
                // 当前的时间和存入时候的时间 进行相减 和过期时间进行比较
                // 大于就说明过期了 清除存储  小于或者等于 就没有过期 
                if (now - item.startTime > item.expires) {
                    localStorage.removeItem(name);
                    return false;  // 返回一个状态值
                } else {
                    //缓存未过期,返回值
                    return item.value;
                }
            } else {
                // 没有设置过期时间,直接返回值
                return item;
            }
        }else{
            return false; // 如果item 值为undefined 则说明没有存储 返回false
        }
    }

    // 移除指定的缓存
    function removeItem(name) {
        localStorage.removeItem(name);
    }
    // 移除所有的存储数据
    function clear() {
        localStorage.clear();
    }
    return {    // 返回 执行接口
        setItem,
        getItem,
        removeItem,
        clear
    }
})();
export default myStorage; //暴露方法

核心的代码就是这些
然后引入这个js

import setStorage from 'yourpath/storage.js'

接下来就可以使用了

setStorage.setItem({value:yourtoken,name:'token',expires:604800000})//存储token  过期时间是毫秒   我这里是一周

setStorage.getItem('token')//获取token
查看原文

赞 0 收藏 0 评论 0

Resssssss 发布了文章 · 2020-11-21

vue+vant 实现的获取验证码倒计时按钮

一个非常简单也是非常常用的逻辑
就是登录时获取手机验证码的并且倒计时的按钮逻辑
我用的是移动端的ui插件vant

引入vant的方式就不再赘述了详情请看vant官网
https://vant-contrib.gitee.io...

直接上代码

<van-field v-model="phone" clearable  maxlength="15" label="手机号" left-icon="phone-circle-o" placeholder="请输入手机号">
    <van-button v-if='phone.length >= 6 && showCountdown == true' ref="smsCode"  slot="button" size="mini"><van-count-down :time="time" style="color:#777" @finish='countDownFinish' format=" ss 秒后重试" /></van-button>
    <van-button v-else-if="phone.length >=6"  @click="getSmsCode"  slot="button" size="mini">获取验证码</van-button>
    <van-button v-else     disabled    slot="button" size="mini">获取验证码</van-button>
</van-field>

其实逻辑不是很难
我这里用了三个按钮逻辑分别是
最开始手机输入框的长度如果不够6位的话获取验证码的按钮是disabled
长度大于等于6位的话获取验证码可点击 点击后显示倒计时countdown按钮

在data中

data(){
    return{
        phone: '',//手机号
        time: 60 * 1000,//倒计时的时间  毫秒级 60秒要*10000
        showCountdown: false//是否显示倒计时
    }
}

methods中

methods:{
    getSmsCode(){
        this.showCountdown = true//这里只是简单的显示倒计时逻辑获取验证码需要具体的接口
    },
    countDownFinish(){//倒计时结束后的方法
        this.showCountdown = false //隐藏倒计时
    }
}
查看原文

赞 0 收藏 0 评论 0

Resssssss 发布了文章 · 2020-08-28

支付宝小程序与微信小程序input的事件onInput在某些业务场景的区别

长话短说
这个业务场景就是我要控制input的输入,
比如我要限制某个场景下input只能输入两位小数 超过长度截取三位以后的数
这个区别就是
微信小程序onInput: 可以正常控制数字并且给input的value赋值
支付宝小程序onInput: 可以正常控制数字(绑定的数字和data的数值已经修改) 但是还是可能输出并显示 这里就需要使用onBlur或者onConfirm来实现逻辑了 体验上并没有微信小程序的input好 这里我用了一个定时器来实现微信同样的效果

————————————————————————————————————————————————————————
更新一下
发现支付宝的input是有个属性叫做controlled
否是为受控组件。为 true 时,value 内容会完全受 setData 控制
把它设置成true
就可以用setData更新数据了

查看原文

赞 0 收藏 0 评论 0

Resssssss 发布了文章 · 2020-08-07

支付宝小程序父组件调用子组件方法

官方文档描述父组件不能调用子组件的方法 其实是可以的记录下以便日后使用

//子组件.axml
<text>我只是一个子组件</text>



//子组件.js
methods:{
    childMethod(){
        console.log("调用到了子组件的方法")
    }
}

//父组件.json

{
    child-compoent: '../componets/子组件路径'
}


//父组件.axml
<child-compoent ref="childMethods" />
//用ref来指向子组件


//父组件.js
{
    childMethods(ref){//注册子组件实例来调用子组件方法
        this.childCompoentMethod = ref
    },
    callChildCompoentMethods(){
        this.childCompoentMethod.childMethod()
    }
}

这样就可以调用到子组件的方法啦

查看原文

赞 1 收藏 1 评论 0

Resssssss 发布了文章 · 2020-08-07

支付宝小程序子组件调用父组件的方法

其实是一个很简单需求 使用自定义组件时有时候需要子组件调用父组件的方法来进行一些操作 比如更新数据这种
但是官方文档写的不是很清楚自己记录一下方便下次使用的时候查看

//子组件.axml
<button onTap="callFatherCompoentMethod">我是一个子组件</button>
//子组件.js
methods:{
    callFatherCompoentMethod(){
        this.props.onCallFatherMethod()
        //子组件调用写在methods的方法中  这里注意  调用的函数名一定要是on开头
    }
}

//父组件.json
{
    'child-compoent': '../compoents/你的子组件路径'
}
//父组件.axml
<child-compoent onCallFatherMethod="fatherMethod" />
//再次强调  调用函数一定是on靠头
//父组件.js
fatherMethod(){
console.log('调用到了父组件的方法')
}

ok 就是这么简单 其实跟vue的自定义组件差不多

查看原文

赞 1 收藏 1 评论 0

Resssssss 提出了问题 · 2020-02-14

vants使用babel-plugin-import按需加载组件 Icon组件不显示

按照官方文档的babel-plugin-import按需加载了插件
配置了.babelrc文件

然后在main.js中

import {Icon, Button} from 'vant'
Vue.use(Icon);
Vue.use(Button)

button组件没问题
但是icon组件不显示

按照官网的说法使用了babel-plugin-import
是自动加载样式的
但是icon组件引入使用是不显示

然后我引入了css样式 还是不显示 不知道是什么情况

关注 2 回答 0

Resssssss 发布了文章 · 2019-12-25

一次有关于vue keep-alive组件缓存的业务需求

我的业务需求是有订单列表A订单详情页B以及其他页面C

简单来说就是从订单详情页B进入订单列表页A需要将订单列表页缓存下来(因为有筛选条件) 其他页面进入的话不缓存
简单,vue不是有keep-alive吗 判断一下

最开始是这样的

在App.vue中

  <keep-alive >
      <router-view v-if="this.$route.meta.keepAlive" ></router-view>
    </keep-alive>
  <router-view v-if="!this.$route.meta.keepAlive"></router-view> -->

然后在路由页面的order_list设置

meta:{
    keepAlive:false
}

最后呢在orderDetail中写一个路由守卫

beforeRouteLeave(to,from,next){
    if(to.name == 'order_list'){
        to.meta.keepAlive = true
    }
}

SoEazy!!!
但是事情远远没有这么简单

测试发现 第一次进去组件不缓存 第二次才行 什么鬼哦

百度一下吧
https://segmentfault.com/a/1190000020217946
看到了这篇帖子 老哥说的好像很有道理
开整

我的项目是3.0的 并且我创建项目的时候已经选择了vuex
如果没有的话在views同级建一个文件夹store在下面新建一个index.js写vuex的存储
我的vuex是这样的

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    catchList:[]
  },
  mutations: {
    keepAlive(state, component) {
      !state.catchList.includes(component) && state.catchList.push(component)
    },
    noKeepAlive(state) {
      state.catchList = []
    } 
  },
  actions: {
  },
  modules: {
  }
})

如果你是建项目选择了vuex的话无须在main.js引入了
如果没有的话
在main.js中

import store from './store'
new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

ok
接下来就来实现我的逻辑了
其实我的逻辑是完全按照那个老哥的方法来的 但是有改变
为了更清晰 我就将完整过程记录下来

在App.vue页面需要这样

<template>
  <div id="app">
  <keep-alive :include="this.state_catchList">
    <router-view></router-view>
</keep-alive>

  </div>
</template>
<script>
import { mapState } from 'vuex';
export default {

  name: 'app',
  mounted(){
    console.log(this.state_catchList)
  },
  computed:{
    ...mapState({
      state_catchList: state => state.catchList
    })
  }
}
</script>

include的含义是包含此name的组件会被缓存(这里注意组件要写name,不是路由的name,是export default下面的那个name)
对应的是exclude
我这里绑定的就是vuex中的catchList了

首先在你的全局路由守卫中加入,记住是全局的,我是写在main.js中

  if(to.name === "order_list"){
    store.commit('keepAlive','order_list')
    next()
  }

这段代码应该不难理解 如果我进入了order_list页面
就将keepAlive跟名称提交到vuex的数组相当于将组件添加到了缓存
但是业务需求是我只有在从订单详情页进入订单列表才缓存 其他页面进入不缓存

因为router本身就带有路由守卫 我就写在这里就行了

import store from '../store/index.js'//引入vuex
  {
    path: '/order_list',
    name: 'order_list',
    component: orderList,
    meta:{
      keepAlive: false
    },
    beforeEnter:(to,from,next)=>{
      if(from.name != 'orderDetail'){
        console.log(from.name)
        store.commit("noKeepAlive")
        next()
      }else{
        next()
      }
    }
  },

这里的逻辑是在离开orderList前判断如果不是来自订单详情页
就将catchList清空了相当于不缓存

ok到这里基本上就ok了
测试一下
好的第一次进入缓存了
但是出现了新的问题
我的这几个页面底下有一个tabbar 就是相当于footer
在我从其他页面进入订单列表页再进入订单详情页中然后再退回到订单详情页时,发现tabbar选中的选项是我从上个页面的选项 比如我是从首页进入的订单列表 从订单详细页面退回的话 tabbar显示的就是首页

这让我很难受
最开始想的是一个笨办法
我给每一个进入order_list 添加一个路由守卫并提交'noKeepAlive'

但是试过了 不生效

试过了销毁重载组件等方法

最后想到了被缓存的组件也是有钩子的
这里注意被缓存的是不走正常的created这一套钩子的
但是他有activated和deactivated这两个钩子
那就在activated中操作一哈

  activated(){
    console.log('我被缓存了 但是我还是想干点啥')
    this.active = 1
  }

这里需要注意的是这个是写在单独的tabbar组件中的
active是tabbar绑定的一个下标 我直接让他写死为订单列表的下标了

查看原文

赞 0 收藏 0 评论 0

Resssssss 提出了问题 · 2019-12-25

关于vue keep-alive第一次不生效的问题

我的业务需求是
在一个列表页 这个列表页有分类的标签 有筛选条件 当用户选择后进入详情页需要保留这些数据 退回是筛选条件还在 当时想的肯定是用keep-alive缓存了 但是我发现了一个问题 就是第一次从详情页退回不会缓存 第二次才会

我的代码如下

   <keep-alive >
      <router-view v-if="this.$route.meta.keepAlive" ></router-view>
    </keep-alive>
  <router-view v-if="!this.$route.meta.keepAlive"></router-view>

在keep-alive中添加缓存条件

在订单详细页面

  beforeRouteLeave(to,from,next){
    console.log(to)
    if(to.name == 'order_list'){
      console.log(to.name)
      to.meta.keepAlive = true
        next()
    }else{
      next()
    }
  },

剩下的就是将其他页面进入orderList页面的keepAlive设置为false

关注 4 回答 3

认证与成就

  • 获得 7 次点赞
  • 获得 10 枚徽章 获得 0 枚金徽章, 获得 1 枚银徽章, 获得 9 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2018-09-12
个人主页被 1.1k 人浏览