Browse Source

1、admin登录 需要验证码 2、操作日志

刘琳琳 2 days ago
parent
commit
48b02baa12

+ 37 - 0
src/api/operationLog.js

@@ -0,0 +1,37 @@
+import request from '@/utils/request'
+
+/**
+ * 黑名单任务列表(分页)
+ * @param data
+ * @returns {*}
+ */
+export function getList(data) {
+  return request({
+    url: '/api/oper/page',
+    method: 'post',
+    headers: { 'content-type': 'application/json' },
+    data
+  })
+}
+
+/**
+ * 所属模块枚举列表
+ * @returns {*}
+ */
+export function getOperationTagList() {
+  return request({
+    url: '/api/oper/tag/list',
+    method: 'post'
+  })
+}
+
+/**
+ * 业务类型枚举列表
+ * @returns {*}
+ */
+export function getOperationBusinessTagList() {
+  return request({
+    url: '/api/oper/Business/type/list',
+    method: 'post'
+  })
+}

+ 8 - 0
src/api/user.js

@@ -8,6 +8,14 @@ export function login(data) {
   })
 }
 
+export function getCode(data) {
+  return request({
+    url: '/pc/getCode',
+    method: 'post',
+    data
+  })
+}
+
 export function getInfo(token) {
   return request({
     url: '/vue-admin-template/user/info',

+ 1 - 1
src/components/Breadcrumb/index.vue

@@ -3,7 +3,7 @@
     <transition-group name="breadcrumb">
       <el-breadcrumb-item v-for="(item,index) in levelList" :key="item.path">
         <template v-if="item.redirect==='noRedirect'||index==levelList.length-1">
-          <div v-if="['/HHFaceDeviceAuth/index', '/HHAromeDeviceAuth/index'].includes(item.path)">
+          <div v-if="['/HHFaceDeviceAuth/index', '/HHAromeDeviceAuth/index', '/operationLog/index'].includes(item.path)">
             <span class="no-redirect">
               {{ item.meta.title }}
             </span>

+ 1 - 1
src/icons/svg/eye.svg

@@ -1 +1 @@
-<svg width="128" height="64" xmlns="http://www.w3.org/2000/svg"><path d="M127.072 7.994c1.37-2.208.914-5.152-.914-6.87-2.056-1.717-4.797-1.226-6.396.982-.229.245-25.586 32.382-55.74 32.382-29.24 0-55.74-32.382-55.968-32.627-1.6-1.963-4.57-2.208-6.397-.49C-.17 3.086-.399 6.275 1.2 8.238c.457.736 5.94 7.36 14.62 14.72L4.17 35.96c-1.828 1.963-1.6 5.152.228 6.87.457.98 1.6 1.471 2.742 1.471s2.284-.49 3.198-1.472l12.564-13.983c5.94 4.416 13.021 8.587 20.788 11.53l-4.797 17.418c-.685 2.699.686 5.397 3.198 6.133h1.37c2.057 0 3.884-1.472 4.341-3.68L52.6 42.83c3.655.736 7.538 1.227 11.422 1.227 3.883 0 7.767-.49 11.422-1.227l4.797 17.173c.457 2.208 2.513 3.68 4.34 3.68.457 0 .914 0 1.143-.246 2.513-.736 3.883-3.434 3.198-6.133l-4.797-17.172c7.767-2.944 14.848-7.114 20.788-11.53l12.336 13.738c.913.981 2.056 1.472 3.198 1.472s2.284-.49 3.198-1.472c1.828-1.963 1.828-4.906.228-6.87l-11.65-13.001c9.366-7.36 14.849-14.474 14.849-14.474z"/></svg>
+<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M127.072 7.994c1.37-2.208.914-5.152-.914-6.87-2.056-1.717-4.797-1.226-6.396.982-.229.245-25.586 32.382-55.74 32.382-29.24 0-55.74-32.382-55.968-32.627-1.6-1.963-4.57-2.208-6.397-.49C-.17 3.086-.399 6.275 1.2 8.238c.457.736 5.94 7.36 14.62 14.72L4.17 35.96c-1.828 1.963-1.6 5.152.228 6.87.457.98 1.6 1.471 2.742 1.471s2.284-.49 3.198-1.472l12.564-13.983c5.94 4.416 13.021 8.587 20.788 11.53l-4.797 17.418c-.685 2.699.686 5.397 3.198 6.133h1.37c2.057 0 3.884-1.472 4.341-3.68L52.6 42.83c3.655.736 7.538 1.227 11.422 1.227 3.883 0 7.767-.49 11.422-1.227l4.797 17.173c.457 2.208 2.513 3.68 4.34 3.68.457 0 .914 0 1.143-.246 2.513-.736 3.883-3.434 3.198-6.133l-4.797-17.172c7.767-2.944 14.848-7.114 20.788-11.53l12.336 13.738c.913.981 2.056 1.472 3.198 1.472s2.284-.49 3.198-1.472c1.828-1.963 1.828-4.906.228-6.87l-11.65-13.001c9.366-7.36 14.849-14.474 14.849-14.474z"/></svg>

+ 3 - 1
src/icons/svg/password.svg

@@ -1 +1,3 @@
-<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M108.8 44.322H89.6v-5.36c0-9.04-3.308-24.163-25.6-24.163-23.145 0-25.6 16.881-25.6 24.162v5.361H19.2v-5.36C19.2 15.281 36.798 0 64 0c27.202 0 44.8 15.281 44.8 38.961v5.361zm-32 39.356c0-5.44-5.763-9.832-12.8-9.832-7.037 0-12.8 4.392-12.8 9.832 0 3.682 2.567 6.808 6.407 8.477v11.205c0 2.718 2.875 4.962 6.4 4.962 3.524 0 6.4-2.244 6.4-4.962V92.155c3.833-1.669 6.393-4.795 6.393-8.477zM128 64v49.201c0 8.158-8.645 14.799-19.2 14.799H19.2C8.651 128 0 121.359 0 113.201V64c0-8.153 8.645-14.799 19.2-14.799h89.6c10.555 0 19.2 6.646 19.2 14.799z"/></svg>
+<svg width="11" height="12" viewBox="0 0 11 12" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M7.00482 4.85928V3.43663C7.00482 2.33349 6.11133 1.44 5.00819 1.44C3.90506 1.44 3.01157 2.33494 3.01157 3.43807V4.86072H7.00482V4.85928ZM1.46892 4.87518L1.46747 4.83036V3.26747C1.46747 2.40145 1.81157 1.57012 2.42458 0.957108C3.03759 0.344096 3.86747 0 4.73494 0H5.28289C7.08723 0 8.54892 1.46313 8.54892 3.26602V4.87518C9.39036 4.99229 10.0164 5.71229 10.0164 6.56241V10.2969C10.0164 11.2381 9.25446 12 8.31325 12H1.70313C0.761928 12 0 11.2381 0 10.2969V6.56386C0 5.71374 0.626024 4.99373 1.46892 4.87518ZM4.61494 8.72096V9.83422C4.61494 10.0511 4.79133 10.2275 5.00819 10.2275C5.22506 10.2275 5.40145 10.0511 5.40145 9.83422V8.72096C5.88578 8.53157 6.16771 8.0241 6.07084 7.51373C5.97398 7.00193 5.52868 6.63181 5.00819 6.63181C4.48771 6.63181 4.04096 7.00193 3.94554 7.51373C3.84867 8.0241 4.1306 8.53157 4.61494 8.72096Z" fill="#889AA4"/>
+</svg>

+ 3 - 1
src/icons/svg/user.svg

@@ -1 +1,3 @@
-<svg width="130" height="130" xmlns="http://www.w3.org/2000/svg"><path d="M63.444 64.996c20.633 0 37.359-14.308 37.359-31.953 0-17.649-16.726-31.952-37.359-31.952-20.631 0-37.36 14.303-37.358 31.952 0 17.645 16.727 31.953 37.359 31.953zM80.57 75.65H49.434c-26.652 0-48.26 18.477-48.26 41.27v2.664c0 9.316 21.608 9.325 48.26 9.325H80.57c26.649 0 48.256-.344 48.256-9.325v-2.663c0-22.794-21.605-41.271-48.256-41.271z" stroke="#979797"/></svg>
+<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M6.44462 5.98117C7.81966 5.82931 8.93276 4.71579 9.08462 3.34055C9.28407 1.53 7.87304 0 6.10324 0C4.44683 0 3.10345 1.34193 3.10345 2.99917V3.00062C3.10345 4.77083 4.63448 6.18062 6.44462 5.98117ZM6.00062 6.82759C3.99807 6.82759 0.500048 7.98248 0.500048 10.2763V11.5694C0.500048 11.808 0.692461 12 0.928117 12H11.0718C11.1282 11.9999 11.184 11.9886 11.236 11.9669C11.2881 11.9452 11.3353 11.9134 11.3751 11.8735C11.4149 11.8335 11.4464 11.786 11.4678 11.7339C11.4893 11.6817 11.5002 11.6258 11.5 11.5694V10.2763C11.5015 7.98393 8.00338 6.82759 6.00062 6.82759Z" fill="#889AA4"/>
+</svg>

+ 14 - 0
src/router/index.js

@@ -505,6 +505,20 @@ export const constantRoutes = [
       }
     ]
   },
+  {
+    path: '/operationLog',
+    component: Layout,
+    name: 'OperationLog',
+    meta: { title: '操作日志', icon: 'el-icon-user-solid', accountType: [0, 1, 2], authType: ['HHFace', 'HHArome'] },
+    children: [
+      {
+        path: 'index',
+        name: 'index',
+        component: () => import('@/views/operationLog/index'),
+        meta: { title: '操作日志', icon: 'el-icon-user-solid', accountType: [0, 1, 2], authType: ['HHFace', 'HHArome'] }
+      }
+    ]
+  },
   // 404 page must be placed at the end !!!
   { path: '*', redirect: '/404', hidden: true }
 ]

+ 7 - 2
src/store/modules/user.js

@@ -7,6 +7,7 @@ const getDefaultState = () => {
   return {
     token: getToken(),
     name: '',
+    phone: '',
     avatar: '',
     authAccountInfo: null
   }
@@ -24,6 +25,9 @@ const mutations = {
   SET_NAME: (state, name) => {
     state.name = name
   },
+  SET_PHONE: (state, phone) => {
+    state.phone = phone
+  },
   SET_AVATAR: (state, avatar) => {
     state.avatar = avatar
   },
@@ -35,11 +39,12 @@ const mutations = {
 const actions = {
   // user login
   login({ commit, dispatch }, userInfo) {
-    const { username, password } = userInfo
+    const { username, password, phone, code } = userInfo
     return new Promise((resolve, reject) => {
-      login({ username: username.trim(), password: password }).then(async response => {
+      login({ username: username.trim(), password: password, phone, code }).then(async response => {
         const { data } = response
         commit('SET_NAME', username.trim())
+        commit('SET_PHONE', phone?.trim())
         commit('SET_TOKEN', data)
         setToken(data)
         resolve()

+ 39 - 0
src/styles/iconfont/iconfont.css

@@ -0,0 +1,39 @@
+@font-face {
+  font-family: "iconfont"; /* Project id 4803281 */
+  src: url('iconfont.woff2?t=1736127768797') format('woff2'),
+       url('iconfont.woff?t=1736127768797') format('woff'),
+       url('iconfont.ttf?t=1736127768797') format('truetype');
+}
+
+.iconfont {
+  font-family: "iconfont" !important;
+  font-size: 16px;
+  font-style: normal;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+.icon-eye:before {
+  content: "\e62a";
+}
+
+.icon-eye-open:before {
+  content: "\e62b";
+}
+
+.icon-password:before {
+  content: "\e626";
+}
+
+.icon-user:before {
+  content: "\e627";
+}
+
+.icon-phone:before {
+  content: "\e628";
+}
+
+.icon-captcha:before {
+  content: "\e629";
+}
+

File diff suppressed because it is too large
+ 0 - 0
src/styles/iconfont/iconfont.js


+ 51 - 0
src/styles/iconfont/iconfont.json

@@ -0,0 +1,51 @@
+{
+  "id": "4803281",
+  "name": "alipay-auth-platform-admin-icon",
+  "font_family": "iconfont",
+  "css_prefix_text": "icon-",
+  "description": "alipay-auth-platform-admin(航汇-支付宝授权平台)",
+  "glyphs": [
+    {
+      "icon_id": "43044158",
+      "name": "eye",
+      "font_class": "eye",
+      "unicode": "e62a",
+      "unicode_decimal": 58922
+    },
+    {
+      "icon_id": "43044159",
+      "name": "eye-open",
+      "font_class": "eye-open",
+      "unicode": "e62b",
+      "unicode_decimal": 58923
+    },
+    {
+      "icon_id": "43044016",
+      "name": "password",
+      "font_class": "password",
+      "unicode": "e626",
+      "unicode_decimal": 58918
+    },
+    {
+      "icon_id": "43044017",
+      "name": "user",
+      "font_class": "user",
+      "unicode": "e627",
+      "unicode_decimal": 58919
+    },
+    {
+      "icon_id": "43044018",
+      "name": "phone",
+      "font_class": "phone",
+      "unicode": "e628",
+      "unicode_decimal": 58920
+    },
+    {
+      "icon_id": "43044019",
+      "name": "captcha",
+      "font_class": "captcha",
+      "unicode": "e629",
+      "unicode_decimal": 58921
+    }
+  ]
+}

BIN
src/styles/iconfont/iconfont.ttf


BIN
src/styles/iconfont/iconfont.woff


BIN
src/styles/iconfont/iconfont.woff2


+ 1 - 0
src/styles/index.scss

@@ -3,6 +3,7 @@
 @import './transition.scss';
 @import './element-ui.scss';
 @import './sidebar.scss';
+@import "./iconfont/iconfont.css";
 
 body {
   height: 100%;

+ 144 - 22
src/views/login/index.vue

@@ -7,9 +7,7 @@
       </div>
 
       <el-form-item prop="username">
-        <span class="svg-container">
-          <svg-icon icon-class="user" />
-        </span>
+        <i class="iconfont icon-user form-left-container" />
         <el-input
           ref="username"
           v-model="loginForm.username"
@@ -22,9 +20,7 @@
       </el-form-item>
 
       <el-form-item prop="password">
-        <span class="svg-container">
-          <svg-icon icon-class="password" />
-        </span>
+        <i class="iconfont icon-password form-left-container" />
         <el-input
           :key="passwordType"
           ref="password"
@@ -36,10 +32,40 @@
           auto-complete="on"
           @keyup.enter.native="handleLogin"
         />
-        <span class="show-pwd" @click="showPwd">
-          <svg-icon :icon-class="passwordType === 'password' ? 'eye' : 'eye-open'" />
-        </span>
+        <i :class="`iconfont icon-${passwordType === 'password' ? 'eye' : 'eye-open'} form-right-container`" @click="showPwd" />
       </el-form-item>
+      <template v-if="isAdmin">
+        <el-form-item prop="phone">
+          <i class="iconfont icon-phone form-left-container" />
+          <el-input
+            ref="phone"
+            v-model="loginForm.phone"
+            placeholder="手机号"
+            name="phone"
+            type="text"
+            tabindex="1"
+            auto-complete="on"
+          />
+        </el-form-item>
+
+        <div style="display: flex;align-items: center">
+          <el-form-item prop="code" style="flex: 1">
+            <i class="iconfont icon-captcha form-left-container" />
+            <el-input
+              ref="code"
+              v-model="loginForm.code"
+              placeholder="验证码"
+              name="code"
+              type="text"
+              tabindex="1"
+              auto-complete="on"
+            />
+          </el-form-item>
+          <el-button class="code_btn" type="primary" :disabled="isSending" :loading="submitLoading" @click="sendCode">
+            {{ isSending ? `${countdown}s后重试` : "发送验证码" }}
+          </el-button>
+        </div>
+      </template>
 
       <el-button :loading="loading" type="primary" style="width:100%;margin-bottom:30px;" @click.native.prevent="handleLogin">登 录</el-button>
     </el-form>
@@ -49,28 +75,33 @@
 <script>
 import { validUsername } from '@/utils/validate'
 import store from '@/store'
+import { getCode } from '@/api/user'
 
 export default {
   name: 'Login',
   data() {
-    const validateUsername = (rule, value, callback) => {
-      if (!validUsername(value)) {
-        callback(new Error('请输入你的账号'))
+    const validatePassword = (rule, value, callback) => {
+      if (!value || value.length < 6) {
+        callback(new Error('密码不能少于6位'))
       } else {
         callback()
       }
     }
-    const validatePassword = (rule, value, callback) => {
-      if (!value || value.length < 6) {
-        callback(new Error('密码不能少于6位'))
+    const validatePhone = (rule, value, callback) => {
+      const phoneReg = /^1[3-9]\d{9}$/
+      if (!value || !phoneReg.test(value)) {
+        callback(new Error('请输入正确的手机号码'))
       } else {
         callback()
       }
     }
+
     return {
       loginForm: {
         username: null,
-        password: null
+        password: null,
+        phone: null,
+        code: ''
       },
       loginRules: {
         username: [{
@@ -78,11 +109,29 @@ export default {
           whitespace: true,
           message: '请输入你的账号',
           trigger: ['change', 'blur'] }],
-        password: [{ required: true, trigger: 'blur', validator: validatePassword }]
+        password: [{ required: true, trigger: ['change', 'blur'], validator: validatePassword }],
+        phone: [{ required: true, trigger: ['change', 'blur'], validator: validatePhone }],
+        code: [{
+          required: true,
+          whitespace: true,
+          message: '请输入验证码',
+          trigger: ['change', 'blur'] }]
       },
       loading: false,
       passwordType: 'password',
-      redirect: undefined
+      redirect: undefined,
+
+      /** 验证码开始 **/
+      isSending: false,
+      submitLoading: false,
+      countdown: 60,
+      timer: null
+      /** 验证码结束 **/
+    }
+  },
+  computed: {
+    isAdmin() {
+      return this.loginForm.username == 'admin'
     }
   },
   watch: {
@@ -109,6 +158,10 @@ export default {
       this.$refs.loginForm.validate(valid => {
         if (valid) {
           this.loading = true
+          if (!this.isAdmin) {
+            this.loginForm.phone = ''
+            this.loginForm.code = ''
+          }
           this.$store.dispatch('user/login', this.loginForm).then(async() => {
             await this.$store.dispatch('user/getAuthAccountQuery')
             this.$router.push({ path: this.redirect || '/' })
@@ -121,6 +174,43 @@ export default {
           return false
         }
       })
+    },
+    // 发送验证码
+    sendCode() {
+      this.$refs.loginForm.validateField(['phone'], (valid) => {
+        console.log('发送验证码校验', valid)
+        // 字段校验成功则返回空串
+        if (!valid) {
+          const _this = this
+          _this.submitLoading = true
+          getCode({
+            username: _this.loginForm.username,
+            phone: _this.loginForm.phone
+          }).then(res => {
+            this.$message({
+              type: 'success',
+              message: '发送验证码成功!'
+            })
+            _this.isSending = true
+            _this.countdown = 60
+            // 模拟请求后端发送验证码
+            console.log('验证码已发送至手机号:', _this.loginForm.phone)
+
+            _this.timer = setInterval(() => {
+              if (_this.countdown > 0) {
+                _this.countdown -= 1
+              } else {
+                clearInterval(_this.timer)
+                _this.isSending = false
+              }
+            }, 1000)
+          }).finally(() => {
+            _this.submitLoading = false
+          })
+        } else {
+          return
+        }
+      })
     }
   }
 }
@@ -144,7 +234,7 @@ $cursor: #fff;
 .login-container {
   .el-input {
     display: inline-block;
-    height: 47px;
+    height: 46px;
     width: 85%;
 
     input {
@@ -154,7 +244,8 @@ $cursor: #fff;
       border-radius: 0px;
       padding: 12px 5px 12px 15px;
       color: $light_gray;
-      height: 47px;
+      height: 46px;
+      line-height: 46px;
       caret-color: $cursor;
 
       &:-webkit-autofill {
@@ -169,6 +260,9 @@ $cursor: #fff;
     background: rgba(0, 0, 0, 0.1);
     border-radius: 5px;
     color: #454545;
+    height: 46px;
+    line-height: 46px;
+    //overflow: hidden;
   }
 }
 </style>
@@ -193,12 +287,27 @@ $light_gray:#eee;
     overflow: hidden;
   }
 
-  .svg-container {
-    padding: 6px 5px 6px 15px;
+  .form-left-container {
+    height: 46px;
+    width: 30px;
+    text-align: right;
     color: $dark_gray;
     vertical-align: middle;
+    display: inline-block;
+  }
+
+  .form-right-container {
+    height: 46px;
     width: 30px;
+    text-align: center;
+    color: $dark_gray;
+    vertical-align: middle;
     display: inline-block;
+    cursor: pointer;
+  }
+
+  .icon-eye {
+    font-size: 14px;
   }
 
   .title-container {
@@ -222,5 +331,18 @@ $light_gray:#eee;
     cursor: pointer;
     user-select: none;
   }
+
+  .code_btn {
+    margin-bottom: 22px;
+    width: 115px;
+    height: 46px;
+    margin-left: 10px;
+    font-size: 14px;
+    border-radius: 4px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    cursor: pointer;
+  }
 }
 </style>

+ 167 - 0
src/views/operationLog/components/PersonnelModal.vue

@@ -0,0 +1,167 @@
+<template>
+  <el-dialog
+    title="任务名单"
+    width="800px"
+    :visible.sync="visible"
+    :destroy-on-close="false"
+    :close-on-click-modal="false"
+    custom-class="roster-modal"
+  >
+    <div class="roster-content">
+      <!-- 主体表格 -->
+      <el-table
+        :data="tableData"
+        element-loading-text="加载中..."
+        border
+        fit
+        highlight-current-row
+      >
+        <el-table-column align="center" width="80" label="序号">
+          <template slot-scope="scope">
+            {{
+              (scope.$index + 1)
+            }}
+          </template>
+        </el-table-column>
+        <el-table-column label="姓名" align="center">
+          <template slot-scope="scope">
+            {{ scope.row.certName | matchNull }}
+          </template>
+        </el-table-column>
+        <el-table-column label="身份证号" align="center">
+          <template slot-scope="scope">
+            {{ scope.row.certNo | matchNull }}
+          </template>
+        </el-table-column>
+        <el-table-column label="手机号" align="center">
+          <template slot-scope="scope">
+            {{ scope.row.phone | matchNull }}
+          </template>
+        </el-table-column>
+        <!--分页栏-->
+      </el-table>
+    </div>
+    <div slot="footer" class="dialog-footer">
+      <el-button type="primary" style="width: 160px" @click="onClose"> 关闭 </el-button>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+
+export default {
+  filters: {
+    matchNull(str) {
+      if (!str) {
+        return '-'
+      } else {
+        return str
+      }
+    }
+  },
+  props: {
+    roster: {
+      type: Array,
+      default: () => []
+    }
+  },
+  data() {
+    return {
+      tableData: [],
+      visible: false
+    }
+  },
+  watch: {
+    roster: {
+      deep: true,
+      handler(newVal, oldVal) {
+        console.log(newVal)
+        this.tableData = newVal
+      }
+    }
+  },
+  methods: {
+    open(obj) {
+      this.visible = true
+      this.$nextTick(() => {
+        console.log(this.roster)
+      })
+    },
+    onClose() {
+      this.visible = false
+      this.$emit('success')
+    }
+  }
+}
+</script>
+<style lang="scss">
+.roster-modal {
+  .dialog-footer {
+    width: 100%;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+  }
+}
+</style>
+
+<style scoped lang="scss">
+.roster-content {
+  height: 600px;
+  padding: 10px;
+  overflow: scroll;
+}
+.access_record_item {
+  padding: 10px;
+  width: calc(100% );
+  border: 1px solid #FFE3E3;
+  background: #FFF1F1;
+  //height: 80px;
+  border-radius: 6px;
+  display: flex;
+  flex-direction: row;
+  justify-content: space-between;
+  align-items: center;
+
+  .item_filed {
+    flex: 1;
+    font-size: 14px;
+    height: 160px;
+    display: flex;
+    flex-direction: column;
+    justify-content: space-around;
+    &_card {
+      width: 100%;
+      background: rgba(255, 92, 92, 1);
+      color: #fff;
+      display: flex;
+      flex-direction: column;
+      padding: 10px;
+      border-radius: 12px;
+      &_item {
+        line-height: 24px;
+        height: 24px;
+      }
+    }
+    .filed_value {
+      min-height: 20px;
+      line-height: 20px;
+      font-weight: 400;
+      color: rgba(177, 90, 90, 1);
+      margin-left: 10px;
+      span {
+        color: rgba(51, 51, 51, 1);
+      }
+    }
+  }
+
+  .item_img {
+    width: 160px;
+    margin-left: 10px;
+  }
+
+  &:not(&:first-child) {
+    margin-top: 10px;
+  }
+}
+</style>

+ 209 - 0
src/views/operationLog/components/UploadModal.vue

@@ -0,0 +1,209 @@
+// 导入黑名单
+<template>
+  <div>
+    <el-dialog
+      title="批量导入"
+      width="600px"
+      :visible.sync="dialogVisible"
+      :destroy-on-close="false"
+      :close-on-click-modal="false"
+      @close="dialogClose"
+    >
+      <el-form
+        ref="form"
+        :rules="rules"
+        :model="form"
+        label-position="left"
+        label-width="80px"
+        enctype="multipart/form-data"
+      >
+        <el-form-item label="任务名称" prop="taskName" required>
+          <el-input
+            v-model="form.taskName"
+            placeholder="请输入任务名称"
+            style="width: 100%"
+          />
+        </el-form-item>
+        <el-form-item label="购买方" prop="purchaserId" required>
+          <el-select
+            v-model="form.purchaserId"
+            placeholder="请选择购买方"
+            filterable
+            style="width: 100%"
+          >
+            <el-option
+              v-for="(item, index) in buyList"
+              :key="index"
+              :label="item.companyName"
+              :value="item.purchaserId"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="上传文件" prop="file" required>
+          <a href="javascript:;" class="custom_file">选择文件
+            <input
+              id="fileSelect"
+              ref="referenceUpload"
+              type="file"
+              style="opacity: 0"
+              accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-excel"
+              runat="server"
+              @change="upload"
+            >
+          </a>
+          {{ form.file && form.file.name ? form.file.name : "" }}
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="dialogVisible = false"> 取消 </el-button>
+        <el-button :loading="submitLoading" type="primary" @click="submit"> 确定 </el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+
+import { importBlackTask } from '@/api/blacklist'
+
+export default {
+  components: { },
+  filters: {
+    matchNull(str) {
+      if (!str) {
+        return '暂无'
+      } else {
+        return str
+      }
+    }
+  },
+  props: {
+    buyList: {
+      type: Array,
+      default: () => []
+    }
+  },
+  data() {
+    return {
+      form: {
+        taskName: null, // 任务名称
+        purchaserId: null,
+        file: null
+      },
+      rules: {
+        taskName: [
+          {
+            required: true,
+            message: '任务名称不能为空!',
+            trigger: ['change', 'blur']
+          }
+        ],
+        purchaserId: [
+          {
+            required: true,
+            type: 'number',
+            whitespace: true,
+            message: '购买方不能为空!',
+            trigger: ['change', 'blur']
+          }
+        ],
+        file: [
+          {
+            required: true,
+            message: '文件不能为空!',
+            trigger: ['change', 'blur']
+          }
+        ]
+      },
+      submitLoading: false,
+      dialogVisible: false
+    }
+  },
+  created() {
+  },
+  methods: {
+    open() {
+      this.dialogVisible = true
+      this.$nextTick(() => {
+        this.$refs.form.resetFields()
+      })
+    },
+    dialogClose() {
+    },
+    upload(e) {
+      console.log(e)
+      // 处理excel
+      this.form.file = e.target.files[0]
+    },
+    submit(e) {
+      e.preventDefault()
+      this.$refs.form.validate(async(valid) => {
+        if (!valid) {
+          this.submitLoading = false
+          return false
+        }
+        if (this.submitLoading) {
+          return
+        }
+        this.submitLoading = true
+        console.log(this.form)
+        try {
+          var formData = new FormData() // 必需声明一个FormData对象
+          formData.append('file', this.form.file)
+          formData.append('purchaserId', this.form.purchaserId)
+          formData.append('taskName', this.form.taskName)
+          await importBlackTask(formData)
+          this.dialogVisible = false
+          this.$message.success(`添加成功`)
+          this.$emit('finish')
+        } catch (e) {
+          this.$refs.form.resetFields()
+        } finally {
+          this.submitLoading = false
+        }
+      })
+    }
+  }
+}
+</script>
+
+<style scoped>
+.custom_file {
+  position: relative;
+  margin-right: 10px;
+  background: #d0eeff;
+  border: 1px solid #99d3f5;
+  border-radius: 4px;
+  padding: 10px 20px;
+  box-sizing: border-box;
+  overflow: hidden;
+  color: #1e88c7;
+  text-decoration: none;
+  text-indent: 0;
+  cursor: pointer;
+}
+.custom_file input {
+  position: absolute;
+  font-size: 14px;
+  width: 106px;
+  height: 40px;
+  left: 0;
+  top: 0;
+  opacity: 0;
+  cursor: pointer;
+}
+.custom_file:hover {
+  background: #aadffd;
+  border-color: #78c3f3;
+  color: #004974;
+  text-decoration: none;
+}
+</style>
+<style scoped>
+::v-deep .el-select {
+  width: 100%;
+}
+.table {
+  margin-top: 15px;
+}
+</style>

+ 287 - 0
src/views/operationLog/index.vue

@@ -0,0 +1,287 @@
+<template>
+  <div class="app-container">
+    <div style="margin-bottom: 20px">
+      <el-select
+        v-if="isAdmin"
+        v-model="form.accountId"
+        class="margin-left input"
+        filterable
+        clearable
+        placeholder="请选择所属账号"
+      >
+        <el-option
+          v-for="item in accountList"
+          :key="item.id"
+          :label="item.name"
+          :value="item.id"
+        />
+      </el-select>
+      <el-select
+        v-model="form.tag"
+        class="margin-left input"
+        filterable
+        clearable
+        placeholder="请选择所属模块"
+      >
+        <el-option
+          v-for="item in operationTagList"
+          :key="item.desc"
+          :label="item.desc"
+          :value="item.desc"
+        />
+      </el-select>
+      <el-select
+        v-model="form.businessType"
+        class="margin-left input"
+        filterable
+        clearable
+        placeholder="请选业务类型"
+      >
+        <el-option
+          v-for="item in operationBusinessTagList"
+          :key="item.code"
+          :label="item.desc"
+          :value="item.code"
+        />
+      </el-select>
+      <el-input
+        v-model="form.operName"
+        placeholder="操作人"
+        class="margin-left input"
+      />
+      <el-input
+        v-model="form.operPhone"
+        placeholder="操作手机"
+        class="margin-left input"
+      />
+      <el-date-picker
+        v-model="form.startTimeToEndTime"
+        type="datetimerange"
+        range-separator="至"
+        class="margin-left"
+        start-placeholder="选择开始时间"
+        end-placeholder="选择结束时间"
+      />
+      <el-button
+        class="margin-left"
+        type="primary"
+        icon="el-icon-search"
+        @click="query(1)"
+      >
+        搜索
+      </el-button>
+    </div>
+    <!-- 主体表格 -->
+    <el-table
+      v-loading="tableLoading"
+      :data="tableData"
+      element-loading-text="加载中..."
+      border
+      fit
+      highlight-current-row
+    >
+      <el-table-column align="center" width="80" label="序号">
+        <template slot-scope="scope">
+          {{
+            (pagination.page - 1) * pagination.pageSize + (scope.$index + 1)
+          }}
+        </template>
+      </el-table-column>
+      <el-table-column align="center" prop="accountName" label="所属账号" />
+      <el-table-column align="center" prop="tag" label="操作模块" />
+      <el-table-column align="center" prop="businessTypeName" label="业务类型" />
+      <!--      <el-table-column align="center" prop="purchaserName" label="操作">
+        <template v-slot="{ row }">
+          {{ row.operParam | matchNull }}
+        </template>
+      </el-table-column>-->
+      <el-table-column
+        v-if="isAdmin"
+        align="center"
+        prop="purchaserName"
+        label="操作人"
+      >
+        <template v-slot="{ row }">
+          {{ row.operName | matchNull }}
+        </template>
+      </el-table-column>
+      <el-table-column
+        v-if="isAdmin"
+        align="center"
+        prop="purchaserName"
+        label="操作手机号"
+      >
+        <template v-slot="{ row }">
+          {{ row.operPhone | matchNull }}
+        </template>
+      </el-table-column>
+      <el-table-column label="操作时间">
+        <template slot-scope="scope">
+          {{ scope.row.operTime }}
+        </template>
+      </el-table-column>
+      <!--分页栏-->
+    </el-table>
+    <pagination
+      :total="pagination.total"
+      :page.sync="pagination.page"
+      :limit.sync="pagination.pageSize"
+      :page-sizes="pagination.pageSizeOptions"
+      :layout="pagination.layout"
+      @pagination="handleSizeChange"
+    />
+  </div>
+</template>
+
+<script>
+import Pagination from '@/components/Pagination'
+import { getList, getOperationBusinessTagList, getOperationTagList } from '@/api/operationLog'
+import tableMixins from '@/mixins/tableMixins'
+import { parseTime } from '@/utils'
+import {
+  getAccountList
+} from '@/api/accountAuth'
+export default {
+  components: { Pagination },
+  filters: {
+    matchNull(str) {
+      if (!str) {
+        return '-'
+      } else {
+        return str
+      }
+    }
+  },
+  mixins: [tableMixins],
+  data() {
+    return {
+      accountList: [],
+      operationTagList: [], // 所属模块枚举
+      operationBusinessTagList: [], // 业务类型枚举
+      form: {
+        accountId: null, // 所属账号
+        tag: null, // 操作模块
+        businessType: null, // 业务类型
+        operName: null, // 操作人
+        operPhone: null, // 操作手机
+        startTime: null,
+        endTime: null,
+        startTimeToEndTime: null
+      },
+      pagination: {
+        page: 1,
+        current: 1
+      }
+
+    }
+  },
+  computed: {
+    isAdmin() {
+      return this.$store.getters.authAccountInfo?.accountType == 0
+    }
+  },
+  created() {
+    this.selectInit()
+  },
+  methods: {
+    async selectInit() {
+      this.enumLoading = true
+      const accountAxios = await getAccountList()
+      const operationTagAxios = await getOperationTagList()
+      const operationBusinessTagAxios = await getOperationBusinessTagList()
+      await Promise.all([accountAxios, operationTagAxios, operationBusinessTagAxios]).then(res => {
+        const accountAxiosRes = res[0]
+        this.accountList = (accountAxiosRes.data || [])
+        const operationTagAxiosRes = res[1]
+        this.operationTagList = (operationTagAxiosRes.data || [])
+        const operationBusinessTagAxiosRes = res[2]
+        this.operationBusinessTagList = (operationBusinessTagAxiosRes.data || [])
+      }).finally(() => {
+        this.enumLoading = false
+      })
+    },
+    initData() {
+      this.getTableList()
+    },
+    // 获取表格数据
+    async getTableList() {
+      this.pagination.current = this.pagination.page
+      this.tableLoading = true
+      try {
+        const result = await getList(this.getFilterParams())
+        this.setDataList(result.data)
+      } catch (e) {
+        console.log(e)
+      } finally {
+        this.tableLoading = false
+      }
+    },
+    // 获取筛选信息
+    query(type) {
+      if (type) {
+        this.pagination.page = 1
+        this.pagination.current = 1
+      }
+      this.form.startTime = null
+      this.form.endTime = null
+      if (this.form.startTimeToEndTime && this.form.startTimeToEndTime.length > 0) {
+        this.form.startTime = parseTime(this.form.startTimeToEndTime[0], '{y}-{m}-{d} {h}:{i}:{s}')
+        this.form.endTime = parseTime(this.form.startTimeToEndTime[1], '{y}-{m}-{d} {h}:{i}:{s}')
+      }
+      this.clearPageParams()
+      this.getTableList()
+    },
+    // 增加筛选条件
+    transformFilterForm() {
+      let startTime = null
+      let endTime = null
+      if (this.form.startTimeToEndTime && this.form.startTimeToEndTime.length > 0) {
+        startTime = parseTime(this.form.startTimeToEndTime[0], '{y}-{m}-{d} {h}:{i}:{s}')
+        endTime = parseTime(this.form.startTimeToEndTime[1], '{y}-{m}-{d} {h}:{i}:{s}')
+      }
+      return {
+        ...this.form,
+        startTime: startTime,
+        endTime: endTime
+      }
+    }
+  }
+}
+</script>
+
+<style scoped>
+.table{
+  margin-top: 15px;
+}
+.flex {
+  display: flex;
+  justify-content: flex-start;
+  align-items: center;
+}
+.margin-left {
+  margin: 10px 0 0 10px;
+}
+.search-layout {
+  margin-bottom: 20px;
+}
+.search-layout .filter-item {
+  margin-right: 20px;
+  margin-top: 10px;
+}
+.search-layout .filter-item:last-of-type {
+  margin-right: 0;
+}
+.head {
+  margin-left: 16px;
+  font-weight: 600;
+  font-size: 18px;
+  color: #333333;
+}
+.input {
+  width: 200px;
+  /*margin-right: 15px;*/
+}
+.m-r {
+  margin-right: 15px;
+}
+</style>

+ 1 - 1
src/views/tempoIDManage/components/EditDeviceModule.vue

@@ -771,7 +771,7 @@ export default {
         identityProofRecordUrl: '', // 获取办证记录地址
 
         identityProofRecordPushUrl:
-          'https://tx.hz-hanghui.com:8087/ti-prove/handleProveRecord/push', // 办证记录推送地址
+          'https://common.hz-hanghui.com:8087/ti-prove/handleProveRecord/push', // 办证记录推送地址
 
         printIdentityProof: true, // 是否打印临时证明
         officialSealSwitch: false, // 是否加盖公章

+ 1 - 1
src/views/tempoIDManage/components/edit.vue

@@ -497,7 +497,7 @@ export default {
         noIdcardInputPhone: true,
         identityProofRecordUrl: '',
         identityProofRecordPushUrl:
-          'https://tx.hz-hanghui.com:8087/ti-prove/handleProveRecord/push',
+          'https://common.hz-hanghui.com:8087/ti-prove/handleProveRecord/push',
         printIdentityProof: true,
         riskCheck: false,
         miniAuth: false,

+ 1 - 1
src/views/tempoIDManageTemplate/components/EditDeviceModule.vue

@@ -744,7 +744,7 @@ export default {
         identityProofRecordUrl: '', // 获取办证记录地址
 
         identityProofRecordPushUrl:
-          'https://tx.hz-hanghui.com:8087/ti-prove/handleProveRecord/push', // 办证记录推送地址
+          'https://common.hz-hanghui.com:8087/ti-prove/handleProveRecord/push', // 办证记录推送地址
 
         printIdentityProof: true, // 是否打印临时证明
         officialSealSwitch: false, // 是否加盖公章

+ 1 - 1
src/views/tempoIDManageTemplate/components/edit.vue

@@ -479,7 +479,7 @@ export default {
         noIdcardInputPhone: true,
         identityProofRecordUrl: '',
         identityProofRecordPushUrl:
-          'https://tx.hz-hanghui.com:8087/ti-prove/handleProveRecord/push',
+          'https://common.hz-hanghui.com:8087/ti-prove/handleProveRecord/push',
         printIdentityProof: true,
         riskCheck: false,
         miniAuth: false,

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