Bladeren bron

build:验证码

snow 2 jaren geleden
bovenliggende
commit
c171d635b0

File diff suppressed because it is too large
+ 0 - 0
dist/static/js/0.js


File diff suppressed because it is too large
+ 0 - 0
dist/static/js/app.js


File diff suppressed because it is too large
+ 0 - 0
dist/static/js/chunk-libs.js


+ 1 - 0
package.json

@@ -41,6 +41,7 @@
     "element-ui": "^2.13.2",
     "file-saver": "2.0.1",
     "fuse.js": "3.4.4",
+    "identify": "^0.1.5",
     "js-cookie": "2.2.0",
     "js-md5": "^0.7.3",
     "jsonlint": "1.6.3",

+ 3 - 1
src/App.vue

@@ -40,7 +40,9 @@ export default {
             await this.logout()
           }
         })
-        .catch(async(err) => await this.logout())
+        .catch(async(err) => {
+          await this.logout()
+        })
     },
     async logout() {
       if (this.$route.path !== '/login' && this.$route.path !== '/accept') {

+ 0 - 1
src/apis/user/index.js

@@ -106,7 +106,6 @@ export function requsetSupertubeCompany(data) {
 export function requsetUserBindingCompany(data) {
   const _data = {
     ...data,
-    size: 10,
     noRelation: true
   }
 

+ 133 - 0
src/components/identify/index.vue

@@ -0,0 +1,133 @@
+<template>
+  <div class="s-canvas">
+    <canvas id="s-canvas" :width="contentWidth" :height="contentHeight" />
+  </div>
+</template>
+<script>
+export default {
+  name: 'SIdentify',
+  props: {
+    identifyCode: {
+      type: String,
+      default: '1234'
+    },
+    fontSizeMin: {
+      type: Number,
+      default: 18
+    },
+    fontSizeMax: {
+      type: Number,
+      default: 40
+    },
+    backgroundColorMin: {
+      type: Number,
+      default: 180
+    },
+    backgroundColorMax: {
+      type: Number,
+      default: 240
+    },
+    colorMin: {
+      type: Number,
+      default: 50
+    },
+    colorMax: {
+      type: Number,
+      default: 160
+    },
+    lineColorMin: {
+      type: Number,
+      default: 40
+    },
+    lineColorMax: {
+      type: Number,
+      default: 180
+    },
+    dotColorMin: {
+      type: Number,
+      default: 0
+    },
+    dotColorMax: {
+      type: Number,
+      default: 255
+    },
+    contentWidth: {
+      type: Number,
+      default: 111
+    },
+    contentHeight: {
+      type: Number,
+      default: 38
+    }
+  },
+  watch: {
+    identifyCode() {
+      this.drawPic()
+    }
+  },
+  mounted() {
+    this.drawPic()
+  },
+  methods: {
+    // 生成一个随机数
+    randomNum(min, max) {
+      return Math.floor(Math.random() * (max - min) + min)
+    },
+    // 生成一个随机的颜色
+    randomColor(min, max) {
+      const r = this.randomNum(min, max)
+      const g = this.randomNum(min, max)
+      const b = this.randomNum(min, max)
+      return 'rgb(' + r + ',' + g + ',' + b + ')'
+    },
+    drawPic() {
+      const canvas = document.getElementById('s-canvas')
+      const ctx = canvas.getContext('2d')
+      ctx.textBaseline = 'bottom'
+      // 绘制背景
+      ctx.fillStyle = this.randomColor(this.backgroundColorMin, this.backgroundColorMax)
+      ctx.fillRect(0, 0, this.contentWidth, this.contentHeight)
+      // 绘制文字
+      for (let i = 0; i < this.identifyCode.length; i++) {
+        this.drawText(ctx, this.identifyCode[i], i)
+      }
+      // this.drawLine(ctx) // 绘制干扰线
+      // this.drawDot(ctx) // 绘制干扰点
+    },
+    // 绘制文本
+    drawText(ctx, txt, i) {
+      ctx.fillStyle = this.randomColor(this.colorMin, this.colorMax)
+      ctx.font = this.randomNum(this.fontSizeMin, this.fontSizeMax) + 'px SimHei'
+      const x = (i + 1) * (this.contentWidth / (this.identifyCode.length + 1))
+      const y = this.randomNum(this.fontSizeMax, this.contentHeight - 5)
+      var deg = this.randomNum(-30, 30) // 字符旋转角度(不超过45度比较好)
+      // 修改坐标原点和旋转角度
+      ctx.translate(x, y)
+      ctx.rotate(deg * Math.PI / 180)
+      ctx.fillText(txt, 0, 0)
+      // 恢复坐标原点和旋转角度
+      ctx.rotate(-deg * Math.PI / 180)
+      ctx.translate(-x, -y)
+    },
+    drawLine(ctx) {
+      // 绘制干扰线
+      for (let i = 0; i < 8; i++) {
+        ctx.strokeStyle = this.randomColor(this.lineColorMin, this.lineColorMax)
+        ctx.beginPath()
+        ctx.moveTo(this.randomNum(0, this.contentWidth), this.randomNum(0, this.contentHeight))
+        ctx.lineTo(this.randomNum(0, this.contentWidth), this.randomNum(0, this.contentHeight))
+        ctx.stroke()
+      }
+    },
+    drawDot(ctx) {
+      // 绘制干扰点
+      for (let i = 0; i < 100; i++) {
+        ctx.fillStyle = this.randomColor(0, 255)
+        ctx.beginPath()
+        ctx.arc(this.randomNum(0, this.contentWidth), this.randomNum(0, this.contentHeight), 1, 0, 2 * Math.PI)
+        ctx.fill()
+      }
+    }
+  }
+}
+</script>

+ 1 - 0
src/layout/components/Navbar.vue

@@ -368,6 +368,7 @@ export default {
     async onSwitchCompanyMode() {
       const level = this.currentLevel === accountLevels.supplier ? accountLevels.super : accountLevels.supplier
       this.$refs.companySelect && this.$refs.companySelect.selectAllCompany()
+      this.$store.commit(userStoreActions.setCompanyList, [])
       this.$store.commit(userStoreActions.setCurrentLevel, level)
       await this.$store.dispatch(userStoreActions.reloadMenu, level)
 

+ 332 - 0
src/layout/components/company-select-origin/index.vue

@@ -0,0 +1,332 @@
+<template>
+  <el-select
+    v-loading.fullscreen="companyChangeLoading"
+    :element-loading-text="'正在切换至 ' + getUserCompany().name + ' ......'"
+    element-loading-background="rgba(0, 0, 0, 0.8)"
+    element-loading-spinner="el-icon-loading"
+    :value="!global ? value : currentCompany"
+    :remote-method="handleRemoteSearch"
+    :placeholder="placeholder"
+    style="width:380px"
+    :disabled="disabled"
+    reserve-keyword
+    :size="size"
+    filterable
+    remote
+    @change="setCurrentCompany"
+  >
+    <!-- 当selectAll为true 且为超管账号允许选择所有公司 -->
+    <el-option v-if="selectAll && isSupertube" :label="allCompanyLabel" value="" />
+
+    <el-option
+      v-for="(company, index) in companylist"
+      :key="index"
+      class="custom-company"
+      :label="genCompanyLabel(company)"
+      :value="company.code"
+    />
+
+    <p v-if="state.loading" class="company-loading">
+      <i class="el-icon-loading" />
+      加载中...
+    </p>
+
+    <p v-if="state.noMore" class="company-more">没有更多数据了...</p>
+  </el-select>
+</template>
+
+<script>
+import { requsetSupertubeCompany, requsetUserBindingCompany } from '@/apis/user'
+import { isBusinessCompany, isOnlyBusinessCompanyPath } from './utils'
+import { accountLevels } from '@/assets/js/statusList'
+import { userStoreActions } from '@/store/modules/user'
+import { getUserCompany } from '@/utils/auth'
+import { convertCompanylist } from '@/utils'
+import { mapGetters, mapState } from 'vuex'
+
+export default {
+  /**
+   * @param placeholder
+   * @param size 选择器尺寸
+   * @param disabled 是否禁用
+   * @param selectAll 超管账号是否可以选择所有公司
+   * @param global 是否设置全局选中公司(全局使用/组件独立使用)
+   */
+  props: {
+    selectAll: {
+      type: Boolean,
+      default: false
+    },
+    size: {
+      type: String,
+      default: 'small'
+    },
+    placeholder: {
+      type: String,
+      default: ''
+    },
+    disabled: {
+      type: Boolean,
+      default: false
+    },
+    global: {
+      type: Boolean,
+      default: false
+    },
+    value: {
+      type: String,
+      default: ''
+    },
+    isSupplier: {
+      type: Boolean,
+      default: false
+    }
+  },
+  computed: {
+    allCompanyLabel() { return this.currentLevel === accountLevels.supplier ? '所有供应商' : '所有业务公司' },
+    ...mapGetters(['currentLevel']),
+    ...mapState({
+      currentCompany: state => state.user.currentCompany,
+      companylist: state => state.user.companylist,
+      isSupertube: state => state.user.isSupertube
+    })
+  },
+  data() {
+    return {
+      ScrollWrapper: null,
+      initialization: true,
+      companyChangeLoading: false,
+      key: 1,
+      state: {
+        loading: false,
+        noMore: false
+      },
+      params: {
+        name: '',
+        page: 2,
+        size: 10
+      }
+    }
+  },
+  watch: {
+    currentLevel: {
+      handler() {
+        if (!this.isSupertube) return
+        this.params = {
+          name: '',
+          page: 1,
+          size: 10
+        }
+        this.state = {
+          loading: false,
+          noMore: false
+        }
+        this.initalData()
+      }
+    }
+  },
+  async mounted() {
+    await this.initalData()
+    this.ScrollWrapper = await this.getScrollWrapper()
+    this.ScrollWrapper && this.bindEvent(this.ScrollWrapper)
+  },
+  beforeDestroy() {
+    this.ScrollWrapper && this.removeEvent(this.ScrollWrapper)
+  },
+  methods: {
+    getUserCompany,
+    genCompanyLabel(company) {
+      const { name, code, type } = company
+      return `${code}/${name}${type === '1' ? '(已升级为业务公司)' : ''}`
+    },
+    async initalData(reset = false) {
+      // 区分是否超管用户请求不同接口
+      this.state.loading = true
+      const api = this.isSupertube || this.isSupplier ? requsetSupertubeCompany : requsetUserBindingCompany
+      const isSupplier = this.currentLevel === accountLevels.supplier
+      const { page, size, name } = this.params
+      const params = {
+        ...(this.isSupertube ? { type: isSupplier ? '3' : '1' } : {}),
+        [this.isSupertube ? 'name' : 'companyName']: name,
+        page,
+        size
+      }
+
+      const { code, data } = await api(params)
+
+      if (code === 0) {
+        if (data.list.length < 10) this.state.noMore = true
+        this.state.loading = false
+        this.params.page++
+        const _list = this.isSupertube ? data.list : convertCompanylist(data.list)
+        this.$store.commit('user/setCompanylist', reset ? [...this.companylist, ..._list] : _list)
+        return this.isSupertube ? data.list : convertCompanylist(data.list)
+      }
+    },
+
+    setCurrentCompany(currentCompany) {
+      this.$emit('update:value', currentCompany)
+      this.$emit('change', currentCompany)
+      // 作为通用组件使用不设置全局选中公司
+      if (!this.global) return null
+      // 只能选择业务公司的页面
+      const { path } = this.$route
+      if (isOnlyBusinessCompanyPath(path) && !isBusinessCompany(currentCompany)) {
+        this.$message.warning('该页面只能选择业务公司')
+        return
+      }
+      this.$store.commit(userStoreActions.setCurrentCompany, currentCompany)
+      if (this.isSupertube) return null // 超管用户不刷新路由
+      this.changeRouterWithCompany()
+    },
+
+    async changeRouterWithCompany() {
+      const { path, query } = this.$route
+      query.redirect = path
+      const keys = Object.keys(query).filter(key => key && key !== 'error')
+
+      // 1.保存透传参数
+      // 1.2 是否详情页
+      const isDetailPath = path.indexOf('Detail') >= 0
+      let queryString = ''
+      const chunk = (qs, key, index) => {
+        const next = `${key}=${query[key]}${index === keys.length ? '' : '&'}`
+        return qs + next
+      }
+
+      // 2.重新获取用户信息和菜单
+      this.companyChangeLoading = true
+      const result = await this.$store.dispatch(userStoreActions.reloadMenu)
+      this.companyChangeLoading = false
+      switch (result) {
+        // 登出
+        case 'noToken':
+          await this.$store.dispatch('user/logout')
+          await this.$router.push(`/login`)
+          break
+
+          // 角色异常或该公司禁用清空菜单返回首页
+        case 'disabled':
+          await this.$store.dispatch('user/disabledCompany')
+          break
+
+          // 切换路由
+        default:
+          queryString = isDetailPath ? this.parsePathDetail2List(path) : keys.reduce(chunk, '?')
+          await this.$router.replace('/reload' + queryString)
+          break
+      }
+    },
+
+    parsePathDetail2List(path) {
+      const chunks = path.split('Detail')
+      return `?redirect=${chunks[0]}`
+    },
+
+    getScrollWrapper() {
+      return new Promise(resolve => {
+        setTimeout(() => {
+          let optionEl = document.querySelector('.custom-company')
+          if (optionEl && (optionEl = optionEl.parentElement)) {
+            resolve(optionEl.parentElement)
+            return
+          }
+          resolve(null)
+        }, 500)
+      })
+    },
+
+    async handleScroll() {
+      const ScrollWrapper = this.ScrollWrapper
+      const height = ScrollWrapper.clientHeight
+      const scrollTop = ScrollWrapper.scrollTop
+      const scrollHeight = ScrollWrapper.scrollHeight
+      const { loading, noMore } = this.state
+
+      // 是否允许下拉加载
+      const isAllowLoad = !loading && !noMore
+      if (height + scrollTop >= scrollHeight && isAllowLoad) {
+        const list = await this.loadMore()
+        const _list = this.isSupertube ? list : convertCompanylist(list)
+        // 保存到全局
+        this.$store.commit(userStoreActions.setCompanyList, [...this.companylist, ..._list])
+      }
+    },
+
+    async loadMore() {
+      // 区分超管用户和普通用户
+      const api = this.isSupertube ? requsetSupertubeCompany : requsetUserBindingCompany
+      const isSupplier = this.currentLevel === accountLevels.supplier
+      this.state.loading = true
+      const { page, size, name } = this.params
+      const nameProp = this.isSupertube ? 'name' : 'companyName'
+
+      const params = {
+        [nameProp]: name,
+        page,
+        size
+      }
+
+      const { data } = await api({
+        ...params,
+        ...(this.isSupertube ? { type: isSupplier ? '3' : '1' } : {})
+      })
+      this.state.loading = false
+      const isTransborder = this.companylist.length >= Number(data.count) // 是否越界
+
+      if (isTransborder) {
+        this.state.noMore = true
+        this.state.loading = false
+        return []
+      }
+      this.params.page++
+      return data.list
+    },
+
+    async handleRemoteSearch(name) {
+      // 清空全局业务公司列表
+      this.$store.commit(userStoreActions.setCompanyList, [])
+      this.params.name = name
+      this.params.page = 1
+      this.state.noMore = false
+      this.state.loading = false
+      // 获取列表数据
+      const result = await this.initalData()
+      if (result) this.$store.commit(userStoreActions.setCompanyList, result)
+    },
+
+    bindEvent(ScrollWrapper) {
+      ScrollWrapper.addEventListener('scroll', this.handleScroll)
+    },
+
+    removeEvent(ScrollWrapper) {
+      ScrollWrapper.removeEventListener('scroll', this.handleScroll)
+    },
+
+    selectAllCompany() {
+      this.value = ''
+      this.$nextTick(() => (this.key = this.key + 1))
+      this.global && this.$store.commit(userStoreActions.setCurrentCompany, '')
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.company-more {
+  text-align: center;
+  color: #999;
+  font-size: 14px;
+  line-height: 38px;
+}
+
+.company-loading {
+  display: flex;
+  font-size: 14px;
+  justify-content: center;
+  align-items: center;
+  color: #999;
+  gap: 10px;
+  line-height: 38px;
+}
+</style>

+ 17 - 0
src/layout/components/company-select-origin/utils.js

@@ -0,0 +1,17 @@
+// 配置选择公司必须为业务公司的路由
+export const onlyBusinessCompanyPath = [
+  // '/goodStore/grossProfit',
+  // '/goodStore/platformGrossProfit',
+  // '/sellOut/projectDetail', // 项目管理详情
+  // '/sellOut/project', // 项目管理
+  // '/sellOut/zixunOrderDetail', // 竟价单管理详情
+  // '/sellOut/zixunOrder', // 竟价单管理
+  // '/goodStore/goodsCostDetail', // 商品成本详情
+  // '/goodStore/goodsCost' // 商品成本
+]
+
+// 路由是否必须选择业务公司
+export const isOnlyBusinessCompanyPath = path => onlyBusinessCompanyPath.includes(path)
+
+// 公司是否为业务公司
+export const isBusinessCompany = code => code.indexOf('GS') >= 0

+ 6 - 7
src/layout/components/company-select/index.vue

@@ -5,7 +5,7 @@
     element-loading-background="rgba(0, 0, 0, 0.8)"
     element-loading-spinner="el-icon-loading"
     :value="!global ? value : currentCompany"
-    :remote-method="handleRemoteSearch"
+    :remote-method="isSupertube ? handleRemoteSearch : undefined"
     :placeholder="placeholder"
     style="width:380px"
     :disabled="disabled"
@@ -139,7 +139,8 @@ export default {
       const { name, code, type } = company
       return `${code}/${name}${type === '1' ? '(已升级为业务公司)' : ''}`
     },
-    async initalData(reset = false) {
+    async initalData() {
+      if (!this.isSupertube) return
       // 区分是否超管用户请求不同接口
       this.state.loading = true
       const api = this.isSupertube || this.isSupplier ? requsetSupertubeCompany : requsetUserBindingCompany
@@ -151,15 +152,13 @@ export default {
         page,
         size
       }
-
       const { code, data } = await api(params)
-
       if (code === 0) {
         if (data.list.length < 10) this.state.noMore = true
         this.state.loading = false
         this.params.page++
         const _list = this.isSupertube ? data.list : convertCompanylist(data.list)
-        this.$store.commit('user/setCompanylist', reset ? [...this.companylist, ..._list] : _list)
+        this.$store.commit('user/setCompanylist', [...this.companylist, ..._list])
         return this.isSupertube ? data.list : convertCompanylist(data.list)
       }
     },
@@ -184,7 +183,6 @@ export default {
       const { path, query } = this.$route
       query.redirect = path
       const keys = Object.keys(query).filter(key => key && key !== 'error')
-
       // 1.保存透传参数
       // 1.2 是否详情页
       const isDetailPath = path.indexOf('Detail') >= 0
@@ -254,6 +252,7 @@ export default {
     },
 
     async loadMore() {
+      if (!this.isSupertube) return
       // 区分超管用户和普通用户
       const api = this.isSupertube ? requsetSupertubeCompany : requsetUserBindingCompany
       const isSupplier = this.currentLevel === accountLevels.supplier
@@ -296,7 +295,7 @@ export default {
     },
 
     bindEvent(ScrollWrapper) {
-      ScrollWrapper.addEventListener('scroll', this.handleScroll)
+      this.isSupertube && ScrollWrapper.addEventListener('scroll', this.handleScroll)
     },
 
     removeEvent(ScrollWrapper) {

+ 27 - 5
src/store/modules/user.js

@@ -334,13 +334,13 @@ const actions = {
           commit('setCurrentLevel', level)
           //获取公司列表(区分超管和普通账户api)
           const api = isSupertube ? requsetSupertubeCompany : requsetUserBindingCompany
+          
           //获取缓存里的业务公司 => 公司名称搜索 => 存在设置为默认公司
-          const company = getUserCompany();
           const { data: companyData, code: companyCode } = await api({
-            // code | companyCode
-            ...({[isSupertube ? 'code' : 'companyCode'] :company ? company.code : ""}),
-            ...({token: getToken()})
+            ...(!isSupertube ? ({size:10000}) : {}),
+            ...({token: getToken()}),
           })
+
            //保存公司列表 普通用户和超管使用两个列表需要转换
            let _companylist = []
            if(companyCode === 0) {
@@ -352,11 +352,12 @@ const actions = {
              resolve("noToken")
              return
            }
+           
           //非超管用户获取保存 流程权限 & 关键字字段 & 设置默认业务公司
           let defaultCompany = ''
           if(!isSupertube){
             //查找默认的业务公司 设置到全局的currentCompany
-            defaultCompany =  _companylist[0];
+            defaultCompany =  _companylist.find(({is_main}) => is_main === '1');
             const result = dispatch('getRoleInfo', defaultCompany)
             if(result === 'noToken') {
               resolve('noToken')
@@ -464,12 +465,29 @@ async function getRoleProcess(roleid) {
   };
 }
 
+// function isHasRoute(routes){
+//   let isHas = false
+//   const currentPath = location.hash.split('#')[1]
+//   console.log(currentPath)
+
+//   const dfs = (routes) => {
+//     routes.forEach(route => {
+//       if(route.path === currentPath) isHas = true;
+//       if(route.children && route.children.length > 0) dfs(route.children)
+//     })
+//   }
+
+//   dfs(routes)
+//   return isHas
+// }
+
 // 区分供应商和业务公司获取不同的菜单列表
 async function getRoleMenuList(level = "") {
   const { code, data, message } = await menuList({
     ...(level ? {level} : {})
   })
 
+
   //menu接受账户异常:禁用 并返回
   if(message === '账户已禁用' || message === '没有该公司的默认角色'){
     return {
@@ -522,6 +540,10 @@ async function getRoleMenuList(level = "") {
       arrag.push(pItem);
     });
   }
+
+  // const isHas = isHasRoute(arrag)
+  // if(!isHas) router.push('/welcome')
+  
   return {
     mcode: code,
     mdata: {

+ 15 - 4
src/views/interest/account/addEditDepart.vue

@@ -11,7 +11,7 @@
     element-loading-text="拼命加载中"
     element-loading-spinner="el-icon-loading"
     element-loading-background="rgba(0, 0, 0, 0.8)"
-    @close="showModelThis = false;companyNo=''"
+    @close="showModelThis = false;companyNo='';itemlist=[]"
   >
     <el-card style="margin-top: -20px">
       <el-row :gutter="10">
@@ -145,11 +145,13 @@ export default {
       companyList: [],
       is_mainoptions: [],
       isIndeterminate: false,
+      inital: true,
       departData: [],
+      itemlist: [],
       ruleForm: {
         nickname: '', // 真实姓名
         mobile: '', // 手机号
-        itemid: '', // 角色id
+        itemid: [], // 角色id
         company_relaton: []
       },
       platformoptions: [],
@@ -238,6 +240,11 @@ export default {
 
       dfs(data[0].child)
       this.departData = data[0].child
+
+      if (this.inital) {
+        this.inital = false
+        this.ruleForm.itemid = this.itemlist ? this.itemlist.map(({ id }) => id) : []
+      }
     },
 
     async getClist() {
@@ -307,18 +314,23 @@ export default {
         if (this.$refs.ruleForm) {
           this.$refs.ruleForm.resetFields()
           this.$refs.ruleForm.clearValidate()
+
           if (sitem) {
             const {
+              item_companyNo,
+              item_list,
               nickname,
               mobile,
               id
             } = sitem
 
+            this.itemlist = item_list
+
             this.companyList = sitem.company_relaton
+            this.companyNo = item_companyNo
 
             this.ruleForm = {
               id,
-              itemid: '',
               mobile: mobile || '',
               nickname: nickname || ''
             }
@@ -351,7 +363,6 @@ export default {
             }
 
             const res = await asyncRequest.update(model)
-
             this.loading = false
             if (res && res.code === 0) {
               const title = this.id === 'add' ? '添加成功' : '修改成功'

+ 4 - 2
src/views/interest/action/index.vue

@@ -394,12 +394,14 @@ export default {
         const model = {
           roleid: this.roleActive.id,
           role_name: this.ruleForm.role_name,
-          level: this.ruleForm.level,
+          level: "2",
           action: this.action,
           action_data: this.action_data,
           private_data: arr,
           private_field: this.private_field.split(','),
-          is_allow_see_price: this.ruleForm.is_allow_see_price
+          is_allow_see_price: this.ruleForm.is_allow_see_price,
+          relaComNo: this.currentCompany,
+          needRela:true
         };
 
         const loadding = this.$loading();

+ 65 - 5
src/views/login/index.vue

@@ -67,8 +67,27 @@
           </el-form-item>
         </el-tooltip>
 
+        <el-form-item prop="code">
+          <div style="display:flex">
+
+            <span class="svg-container" style="margin-left:10px">
+              <i class="el-icon-key" />
+            </span>
+            <el-input v-model="loginForm.code" placeholder="验证码" style="line-height:54px" />
+
+            <div @click="refreshCode">
+              <Identify
+                content-height="52"
+                :identify-code="identifyCode"
+                style="cursor:pointer"
+              />
+            </div>
+          </div>
+        </el-form-item>
+
         <el-button
-          :loading="loading"
+          :loading="
+            loading"
           :disabled="loading"
           type="primary"
           style="width: 100%; margin-top: 5px"
@@ -93,8 +112,7 @@
             </div>
           </router-link> -->
         </div>
-      </el-form>
-    </div>
+      </el-form></div>
 
     <div v-if="loginBeian !== ''" class="beian">
       <span>{{ loginTitle }}</span>
@@ -109,8 +127,10 @@ import asyncRequest from '@/apis/service/user'
 import resToken from '@/mixins/resToken'
 import { isnumber, isAlphanumeric, validAlphabets, isMobile } from '@/utils/validate'
 import urlConfig from '@/apis/url-config'
+import Identify from '@/components/identify'
 export default {
   name: 'Login',
+  components: { Identify },
   mixins: [resToken],
   data() {
     const validateUsername = (rule, value, callback) => {
@@ -141,6 +161,19 @@ export default {
         }
       }
     }
+
+    const validateCode = (rule, value, callback) => {
+      if (value === '') {
+        callback(new Error('验证码不能为空!'))
+      } else {
+        if (value !== this.identifyCode) {
+          callback(new Error('请输入正确的验证码'))
+        } else {
+          callback()
+        }
+      }
+    }
+
     return {
       ver: '',
       show: false,
@@ -149,12 +182,16 @@ export default {
       loginBeianUrl: urlConfig.loginBeianUrl,
       loginForm: {
         username: '',
-        password: ''
+        password: '',
+        code: ''
       },
       loginRules: {
         username: [{ required: true, trigger: 'blur', validator: validateUsername }],
-        password: [{ required: true, trigger: 'blur', validator: validatePassword }]
+        password: [{ required: true, trigger: 'blur', validator: validatePassword }],
+        code: [{ required: true, trigger: 'blur', validator: validateCode }]
       },
+      identifyCode: '',
+      identifyCodes: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd'],
       passwordType: 'password',
       capsTooltip: false,
       loading: false,
@@ -183,6 +220,8 @@ export default {
     //     password: "mlp123",
     //   };
     // }
+
+    this.refreshCode()
     // this.getversion();
     if (this.loginForm.username === '') {
       this.$refs.username.focus()
@@ -224,6 +263,22 @@ export default {
         this.$refs.password.focus()
       })
     },
+    // 生成随机数
+    randomNum(min, max) {
+      max = max + 1
+      return Math.floor(Math.random() * (max - min) + min)
+    },
+    // 更新验证码
+    refreshCode() {
+      this.identifyCode = ''
+      this.makeCode(this.identifyCodes, 4)
+      console.log('当前验证码:', this.identifyCode)
+    },
+    makeCode(data, len) {
+      for (let i = 0; i < len; i++) {
+        this.identifyCode += this.identifyCodes[this.randomNum(0, this.identifyCodes.length - 1)]
+      }
+    },
     handleLogin() {
       this.$refs.loginForm.validate((valid) => {
         if (valid) {
@@ -484,4 +539,9 @@ $light_gray: #f2f2f2;
     }
   }
 }
+
+.s-canvas{
+  margin:0px;
+  height: 38px;
+}
 </style>

Some files were not shown because too many files changed in this diff