瀏覽代碼

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

刘琳琳 2 天之前
父節點
當前提交
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";
+}
+

文件差異過大導致無法顯示
+ 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
+    }
+  ]
+}

二進制
src/styles/iconfont/iconfont.ttf


二進制
src/styles/iconfont/iconfont.woff


二進制
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,

部分文件因文件數量過多而無法顯示