zhongrj
2026-06-01 e06044d2db5e57dade2aec6a2ac398129f61d0f2
feat: 增加登录页面,注释测试监控的播放
9 files modified
5 files added
949 ■■■■ changed files
src/api/user.js 81 ●●●●● patch | view | raw | blame | history
src/config/website.js 43 ●●●●● patch | view | raw | blame | history
src/permission.js 61 ●●●● patch | view | raw | blame | history
src/router/axios.js 70 ●●●●● patch | view | raw | blame | history
src/router/page/index.js 20 ●●●● patch | view | raw | blame | history
src/store/getters.js 17 ●●●●● patch | view | raw | blame | history
src/store/modules/user.js 242 ●●●●● patch | view | raw | blame | history
src/styles/index.scss 1 ●●●● patch | view | raw | blame | history
src/styles/login.scss 157 ●●●●● patch | view | raw | blame | history
src/utils/tool.js 39 ●●●● patch | view | raw | blame | history
src/views/home/index.vue 2 ●●● patch | view | raw | blame | history
src/views/login/index.vue 58 ●●●●● patch | view | raw | blame | history
src/views/login/userlogin.vue 82 ●●●●● patch | view | raw | blame | history
vue.config.js 76 ●●●●● patch | view | raw | blame | history
src/api/user.js
New file
@@ -0,0 +1,81 @@
import request from '@/router/axios';
import website from "@/config/website";
export const loginByUsername = (tenantId, username, password, type, key, code) => request({
  url: '/api/blade-auth/oauth/token',
  method: 'post',
  headers: {
    'Tenant-Id': tenantId,
    'Captcha-Key': key,
    'Captcha-Code': code,
  },
  params: {
    tenantId,
    username,
    password,
    grant_type: (website.captchaMode ? "captcha" : "password"),
    scope: "all",
    type
  }
});
export const loginBySocial = (tenantId, source, code, state) => request({
  url: '/api/blade-auth/oauth/token',
  method: 'post',
  headers: {
    'Tenant-Id': tenantId
  },
  params: {
    tenantId,
    source,
    code,
    state,
    grant_type: "social",
    scope: "all",
  }
})
export const refreshToken = (refresh_token, tenantId) => request({
  url: '/api/blade-auth/oauth/token',
  method: 'post',
  headers: {
    'Tenant-Id': tenantId
  },
  params: {
    tenantId,
    refresh_token,
    grant_type: "refresh_token",
    scope: "all",
  }
});
export const getButtons = () => request({
  url: '/api/blade-system/menu/buttons',
  method: 'get'
});
export const getCaptcha = () => request({
  url: '/api/blade-auth/oauth/captcha',
  method: 'get'
});
export const logout = () => request({
  url: '/api/blade-auth/oauth/logout',
  method: 'get'
});
export const getUserInfo = () => request({
  url: '/api/blade-auth/oauth/user-info',
  method: 'get'
});
export const sendLogs = (list) => request({
  url: '/api/blade-auth/oauth/logout',
  method: 'post',
  data: list
});
export const clearCache = () => request({
  url: '/api/blade-auth/oauth/clear-cache',
  method: 'get'
});
src/config/website.js
New file
@@ -0,0 +1,43 @@
/**
 * 全局配置文件
 */
var fistPage = {
    label: "首页",
    value: "/layout/home",
    params: {},
    query: {},
    meta: {
        i18n: 'dashboard'
    },
    group: [],
    close: false
}
export default {
    title: "保安员考试大屏",
    logo: "S",
    key: 'saber',
    indexTitle: '保安员考试大屏',
    clientId: 'saber',
    clientSecret: 'saber_secret',
    tenantMode: true,
    tenantId: "000000",
    captchaMode: false,
    lockPage: '/lock',
    tokenTime: 3000,
    tokenHeader: 'Blade-Auth',
    statusWhiteList: [],
    isFirstPage: false,
    fistPage: fistPage,
    menu: {
        iconDefault: 'iconfont icon-caidan',
        props: {
            label: 'name',
            path: 'path',
            icon: 'source',
            children: 'children'
        }
    },
    authUrl: 'http://localhost/blade-auth/oauth/render',
}
src/permission.js
@@ -1,40 +1,35 @@
/*
 * @Author: shuishen 1109946754@qq.com
 * @Date: 2022-08-18 16:16:10
 * @LastEditors: shuishen 1109946754@qq.com
 * @LastEditTime: 2023-05-04 11:09:35
 * @FilePath: \web\bigScreen\src\permission.js
 * @Description: 路由守卫
 *
 * Copyright (c) 2022 by shuishen 1109946754@qq.com, All Rights Reserved.
 */
import router from "@/router/page/index";
import store from './store'
import { getToken } from "./utils/auth";
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
NProgress.configure({showSpinner: false});
router.beforeEach((to, from, next) => {
  // window._axiosPromiseArr && window._axiosPromiseArr.length && window._axiosPromiseArr.forEach((ele, index) => {
  //     ele.cancel()
  //     delete window._axiosPromiseArr[index]
  //     console.clear()
  // })
  const meta = to.meta || {};
  next();
  // if (getToken()) {
  //     if (to.path === '/login') { // 如果登录成功访问登录页跳转到主页
  //         next({
  //             path: '/'
  //         })
  //     } else {
  //         next()
  //     }
  // } else {
  //     // 判断是否需要认证,没有登录访问去登录页
  //     if (meta.isAuth === false) {
  //         next()
  //     } else {
  //         window.open('https://sk.hubeishuiyi.cn/business/', "_self")
  //     }
  // }
  if (getToken()) {
    if (to.path === '/login') {
      next({ path: '/layout/home' })
    } else {
      if (store.getters.token.length === 0) {
        store.dispatch('FedLogOut').then(() => {
          next({ path: '/login' })
        })
      } else {
        next()
      }
    }
  } else {
    if (meta.isAuth === false) {
      next()
    } else {
      next('/login')
    }
  }
});
router.afterEach(() => {
  NProgress.done();
});
src/router/axios.js
@@ -1,40 +1,42 @@
/*
 * @Author: shuishen 1109946754@qq.com
 * @Date: 2022-07-29 15:19:13
 * @LastEditors: shuishen 1109946754@qq.com
 * @LastEditTime: 2023-04-21 10:16:58
 * @FilePath: \web\bigScreen\src\router\axios.js
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
/**
 * 全站http配置
 *
 * axios参数说明
 * isSerialize是否开启form表单提交
 * isToken是否需要token
 */
import axios from "axios";
import store from '@/store/';
import router from '@/router/page';
import { getToken } from '@/utils/auth';
import { MessageBox } from 'element-ui';
import website from '@/config/website';
import { Base64 } from 'js-base64';
import NProgress from 'nprogress';
import 'nprogress/nprogress.css';
// 调用后台管理的
const service = axios.create({
  timeout: 600000, // request timeout
  timeout: 600000,
});
// 返回其他状态码
service.defaults.validateStatus = function (status) {
  return status >= 200 && status <= 500;
};
// 跨域请求,允许保存cookie
// service.defaults.withCredentials = true
service.defaults.withCredentials = true;
// http request拦截
NProgress.configure({
  showSpinner: false
});
service.interceptors.request.use(
  (config) => {
    if (config.apiKey) {
      config.headers["apikey"] =
        "F1DBECD719108635189480CF60E6553ADB3109616426BD537F25A430DFC613B491A025C4A51E77FD08C6E5B7CBE05917A461286E7B6D69F1AB1B14F946149D2065B0C675F8FEDF4B9B05C1496881BC5A";
    NProgress.start();
    const meta = (config.meta || {});
    const isToken = meta.isToken === false;
    config.headers['Authorization'] = `Basic ${Base64.encode(`${website.clientId}:${website.clientSecret}`)}`;
    if (getToken() && !isToken) {
      config.headers[website.tokenHeader] = 'bearer ' + getToken()
    }
    if (config.text === true) {
      config.headers["Content-Type"] = "text/plain";
    }
    config.cancelToken = new axios.CancelToken((cancel) => {
      window._axiosPromiseArr.push({ cancel });
    });
@@ -46,10 +48,25 @@
  }
);
// http response 拦截
service.interceptors.response.use(
  (res) => {
    const code = res.data.code;
    NProgress.done();
    const status = res.data.code || res.status;
    const statusWhiteList = website.statusWhiteList || [];
    const message = res.data.msg || res.data.error_description || '未知错误';
    if (statusWhiteList.includes(status)) return Promise.reject(res);
    if (status === 401) {
      store.dispatch('FedLogOut').then(() => {
        router.push({ path: '/login' });
      });
    }
    if (status !== 200 && status !== 201) {
      MessageBox.alert(message);
      return Promise.reject(new Error(message))
    }
    if (res.data.resultList && res.data.resultList.length > 0) {
      res.data.resultList = res.data.resultList.map((item) => {
@@ -68,6 +85,7 @@
    return res;
  },
  (error) => {
    NProgress.done();
    return Promise.reject(new Error(error));
  }
);
src/router/page/index.js
@@ -1,10 +1,3 @@
/*
 * @Author: Morpheus
 * @Date: 2021-04-30 14:12:09
 * @Last Modified by: Morpheus
 * @Last Modified time: 2022-07-29 09:15:23
 */
import Vue from 'vue'
import VueRouter from 'vue-router'
@@ -14,11 +7,20 @@
const home = () => import('../../views/home/index.vue')
const home_02 = () => import('../../views/home/index_02.vue')
const home_03 = () => import('../../views/home/index_03.vue')
const login = () => import('../../views/login/index.vue')
const routes = [
    {
        path: '/',
        redirect: '/layout'
        redirect: '/login'
    },
    {
        path: '/login',
        meta: {
            title: '登录',
            isAuth: false
        },
        component: login,
    },
    {
        path: '/layout',
@@ -52,7 +54,6 @@
            }
        ]
    },
    {
        path: '/ssjk',
        meta: {
@@ -60,7 +61,6 @@
        },
        component: () => import('../../views/ywsys/index.vue'),
    },
    {
        path: '/csmodel',
        meta: {
src/store/getters.js
@@ -1,16 +1,13 @@
/*
 * @Author: shuishen 1109946754@qq.com
 * @Date: 2023-03-02 16:36:47
 * @LastEditors: shuishen 1109946754@qq.com
 * @LastEditTime: 2023-04-04 10:54:12
 * @FilePath: \srs-police-affairs\src\store\getters.js
 * @Description:
 *
 * Copyright (c) 2023 by ${git_name_email}, All Rights Reserved.
 */
const getters = {
    examId: (state) => state.user.examId,
    isShowVideoDialog: (state) => state.user.isShowVideoDialog,
    token: (state) => state.user.token,
    userInfo: (state) => state.user.userInfo,
    tenantId: (state) => state.user.tenantId,
    permission: (state) => state.user.permission,
    roles: (state) => state.user.roles,
    menu: (state) => state.user.menu,
    menuAll: (state) => state.user.menuAll,
}
export default getters
src/store/modules/user.js
@@ -1,27 +1,245 @@
/*
 * @Author: shuishen 1109946754@qq.com
 * @Date: 2022-12-27 09:33:01
 * @LastEditors: shuishen 1109946754@qq.com
 * @LastEditTime: 2023-04-02 12:18:26
 * @FilePath: \srs-police-affairs\src\store\modules\user.js
 * @Description:
 *
 * Copyright (c) 2023 by ${git_name_email}, All Rights Reserved.
 */
import { setToken, setRefreshToken, removeToken, removeRefreshToken } from '@/utils/auth'
import { Message } from 'element-ui'
import { setStore, getStore } from '@/utils/store'
import { isURL, validatenull } from '@/utils/validate'
import { deepClone } from '@/utils/tool'
import website from '@/config/website'
import { loginByUsername, loginBySocial, getUserInfo, logout, refreshToken, getButtons } from '@/api/user'
import md5 from 'js-md5'
function addPath(ele, first) {
  const menu = website.menu;
  const propsConfig = menu.props;
  const propsDefault = {
    label: propsConfig.label || 'name',
    path: propsConfig.path || 'path',
    icon: propsConfig.icon || 'icon',
    children: propsConfig.children || 'children'
  }
  const icon = ele[propsDefault.icon];
  ele[propsDefault.icon] = validatenull(icon) ? menu.iconDefault : icon;
  const isChild = ele[propsDefault.children] && ele[propsDefault.children].length !== 0;
  if (!isChild) ele[propsDefault.children] = [];
  if (!isChild && first && !isURL(ele[propsDefault.path])) {
    ele[propsDefault.path] = ele[propsDefault.path] + '/index'
  } else {
    ele[propsDefault.children].forEach(child => {
      addPath(child);
    })
  }
}
const user = {
    state: {
    tenantId: getStore({ name: 'tenantId' }) || '',
    userInfo: getStore({ name: 'userInfo' }) || [],
    permission: getStore({ name: 'permission' }) || {},
    roles: [],
    menuId: {},
    menu: getStore({ name: 'menu' }) || [],
    menuAll: getStore({ name: 'menuAll' }) || [],
    token: getStore({ name: 'token' }) || '',
    refreshToken: getStore({ name: 'refreshToken' }) || '',
    hideHome: false,
        examId: '',
        isShowVideoDialog: ''
    },
    actions: {
    LoginByUsername({ commit }, userInfo) {
      return new Promise((resolve, reject) => {
        loginByUsername(userInfo.tenantId, userInfo.username, md5(userInfo.password), userInfo.type, userInfo.key, userInfo.code).then(res => {
          const data = res.data;
          if (data.error_description) {
            Message({
              message: data.error_description,
              type: 'error'
            })
          } else {
            commit('SET_TOKEN', data.access_token);
            commit('SET_REFRESH_TOKEN', data.refresh_token);
            commit('SET_TENANT_ID', data.tenant_id);
            commit('SET_USER_INFO', data);
            commit('DEL_ALL_TAG');
            commit('CLEAR_LOCK');
          }
          resolve(data);
        }).catch(error => {
          reject(error);
        })
      })
    },
    LoginByPhone({ commit }, userInfo) {
      return new Promise((resolve) => {
        loginByUsername(userInfo.phone, userInfo.code).then(res => {
          const data = res.data.data;
          commit('SET_TOKEN', data);
          commit('DEL_ALL_TAG');
          commit('CLEAR_LOCK');
          resolve();
        })
      })
    },
    LoginBySocial({ commit }, userInfo) {
      return new Promise((resolve) => {
        loginBySocial(userInfo.tenantId, userInfo.source, userInfo.code, userInfo.state).then(res => {
          const data = res.data;
          if (data.error_description) {
            Message({
              message: data.error_description,
              type: 'error'
            })
          } else {
            commit('SET_TOKEN', data.access_token);
            commit('SET_REFRESH_TOKEN', data.refresh_token);
            commit('SET_USER_INFO', data);
            commit('DEL_ALL_TAG');
            commit('CLEAR_LOCK');
          }
          resolve();
        })
      })
    },
    GetUserInfo({ commit }) {
      return new Promise((resolve, reject) => {
        getUserInfo().then((res) => {
          const data = res.data.data;
          commit('SET_ROLES', data.roles);
          resolve(data);
        }).catch(err => {
          reject(err);
        })
      })
    },
    refreshToken({ state, commit }) {
      return new Promise((resolve, reject) => {
        refreshToken(state.refreshToken, state.tenantId).then(res => {
          const data = res.data;
          commit('SET_TOKEN', data.access_token);
          commit('SET_REFRESH_TOKEN', data.refresh_token);
          resolve();
        }).catch(error => {
          reject(error)
        })
      })
    },
    LogOut({ commit }) {
      return new Promise((resolve, reject) => {
        logout().then(() => {
          commit('SET_TOKEN', '');
          commit('SET_MENU', []);
          commit('SET_MENU_ALL_NULL', []);
          commit('SET_ROLES', []);
          commit('SET_TAG_LIST', []);
          commit('DEL_ALL_TAG');
          commit('CLEAR_LOCK');
          removeToken();
          removeRefreshToken();
          resolve();
        }).catch(error => {
          reject(error)
        })
      })
    },
    FedLogOut({ commit }) {
      return new Promise(resolve => {
        commit('SET_TOKEN', '');
        commit('SET_MENU_ALL_NULL', []);
        commit('SET_MENU', []);
        commit('SET_ROLES', []);
        commit('SET_TAG_LIST', []);
        commit('DEL_ALL_TAG');
        commit('CLEAR_LOCK');
        removeToken();
        removeRefreshToken();
        resolve();
      })
    },
    GetButtons({ commit }) {
      return new Promise((resolve) => {
        getButtons().then(res => {
          const data = res.data.data;
          commit('SET_PERMISSION', data);
          resolve();
        })
      })
    },
    },
    mutations: {
    set_hideHome: (state, val) => {
      state.hideHome = val;
    },
    SET_TOKEN: (state, token) => {
      setToken(token);
      state.token = token;
      setStore({ name: 'token', content: state.token })
    },
    SET_MENU_ID(state, menuId) {
      state.menuId = menuId;
    },
    SET_MENU_ALL: (state, menuAll) => {
      let menu = state.menuAll;
      menuAll.forEach(ele => {
        if (!menu.find(item => item.label === ele.label && item.path === ele.path)) {
          menu.push(ele);
        }
      })
      state.menuAll = menu
      setStore({ name: 'menuAll', content: state.menuAll })
    },
    SET_MENU_ALL_NULL: (state) => {
      state.menuAll = []
      setStore({ name: 'menuAll', content: state.menuAll })
    },
    SET_MENU: (state, menu) => {
      state.menu = menu
      setStore({ name: 'menu', content: state.menu })
    },
    SET_REFRESH_TOKEN: (state, refreshToken) => {
      setRefreshToken(refreshToken)
      state.refreshToken = refreshToken;
      setStore({ name: 'refreshToken', content: state.refreshToken })
    },
    SET_TENANT_ID: (state, tenantId) => {
      state.tenantId = tenantId;
      setStore({ name: 'tenantId', content: state.tenantId })
    },
    SET_USER_INFO: (state, userInfo) => {
      if (validatenull(userInfo.avatar)) {
        userInfo.avatar = "http://61.131.136.25:2081/zhba/upload/picture/mrtx.png";
      }
      state.userInfo = userInfo;
      setStore({ name: 'userInfo', content: state.userInfo })
    },
    SET_ROLES: (state, roles) => {
      state.roles = roles;
    },
    SET_PERMISSION: (state, permission) => {
      let result = [];
      function getCode(list) {
        list.forEach(ele => {
          if (typeof (ele) === 'object') {
            const chiildren = ele.children;
            const code = ele.code;
            if (chiildren) {
              getCode(chiildren)
            } else {
              result.push(code);
            }
          }
        })
      }
      getCode(permission);
      state.permission = {};
      result.forEach(ele => {
        state.permission[ele] = true;
      });
      setStore({ name: 'permission', content: state.permission })
    },
        SET_EXAMID: (state, active) => {
            state.examId = active
        },
        SET_ISSHOWVIDEODIALOG: (state, active) => {
            state.isShowVideoDialog = active
        },
src/styles/index.scss
@@ -3,6 +3,7 @@
@import "./icon/index.scss";
@import "./tool/index.scss";
@import "./element-ui/element-ui.scss";
@import "./login.scss";
@font-face {
    font-family: Roboto-Bold;
src/styles/login.scss
New file
@@ -0,0 +1,157 @@
html, body, #app {
  height: 100%;
  margin: 0;
  padding: 0;
}
.login-container {
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  width: 100%;
  height: 100%;
  background-image: url("/images/pro-bg.png");
  background-size: cover;
  background-position: center;
}
.login-weaper {
  display: flex;
  width: 800px;
  box-shadow: -4px 5px 20px rgba(0, 0, 0, 0.3);
  border-radius: 8px;
  overflow: hidden;
}
.login-left,
.login-border {
  min-height: 450px;
  display: flex;
  align-items: center;
}
.login-left {
  border-top-left-radius: 8px;
  border-bottom-left-radius: 8px;
  justify-content: center;
  flex-direction: column;
  background: linear-gradient(135deg, #409eff 0%, #67b8ff 100%);
  color: #fff;
  width: 45%;
  padding: 30px;
  box-sizing: border-box;
}
.login-time {
  position: absolute;
  top: 30px;
  left: 30px;
  color: rgba(255, 255, 255, 0.9);
  font-weight: 300;
  font-size: 16px;
}
.login-left .img {
  width: 120px;
  margin-bottom: 20px;
}
.login-left .title {
  text-align: center;
  color: #fff;
  font-weight: 400;
  letter-spacing: 3px;
  font-size: 22px;
  margin: 0;
}
.login-border {
  border-top-right-radius: 8px;
  border-bottom-right-radius: 8px;
  background-color: #fff;
  width: 55%;
  padding: 40px 30px;
  box-sizing: border-box;
}
.login-main {
  width: 100%;
}
.login-title {
  color: #333;
  margin-bottom: 8px;
  font-weight: 500;
  font-size: 20px;
  text-align: center;
  letter-spacing: 2px;
}
.login-title-child {
  color: #999;
  margin-bottom: 30px;
  font-weight: 300;
  font-size: 12px;
  text-align: center;
  letter-spacing: 1px;
}
.login-form {
  margin: 0;
  .el-form-item {
    margin-bottom: 20px;
  }
  .el-input {
    input {
      padding: 12px 15px;
      border: 1px solid #e4e7ed;
      border-radius: 6px;
      font-size: 14px;
      transition: border-color 0.3s;
      &:focus {
        border-color: #409eff;
        outline: none;
      }
    }
    .el-input__prefix {
      i {
        color: #909399;
        font-size: 16px;
      }
    }
    .el-input__suffix {
      i {
        color: #909399;
        cursor: pointer;
      }
    }
  }
}
.login-submit {
  width: 100%;
  height: 42px;
  background: linear-gradient(135deg, #409eff 0%, #67b8ff 100%);
  border: none;
  border-radius: 6px;
  font-size: 16px;
  letter-spacing: 2px;
  color: #fff;
  cursor: pointer;
  margin-top: 10px;
  transition: opacity 0.3s;
  &:hover {
    opacity: 0.9;
  }
  &:active {
    opacity: 0.8;
  }
}
src/utils/tool.js
@@ -1,42 +1,45 @@
/*
 * @Author: shuishen 1109946754@qq.com
 * @Date: 2022-10-17 09:34:19
 * @LastEditors: shuishen 1109946754@qq.com
 * @LastEditTime: 2022-10-17 09:45:20
 * @FilePath: \srs-police-affairs\src\utils\tool.js
 * @Description:
 *
 * Copyright (c) 2022 by shuishen 1109946754@qq.com, All Rights Reserved.
 */
function executeFunction (callbackArray, amount) {
    return new Promise(function (resolve, reject) {
        let index = 0
        let currentIndex = 1
        function execute () {
            try {
                callbackArray[index]()
                index++
            } catch (e) {
                return new Error(e)
            } finally {
                if (currentIndex == callbackArray.length) {
                    resolve
                }
                execute()
                currentIndex++
            }
        }
        execute()
    })
}
export function deepClone(obj) {
    if (obj === null || typeof obj !== 'object') {
        return obj
    }
    if (obj instanceof Date) {
        return new Date(obj.getTime())
    }
    if (obj instanceof Array) {
        return obj.map(item => deepClone(item))
    }
    if (typeof obj === 'object') {
        const clonedObj = {}
        for (let key in obj) {
            if (Object.prototype.hasOwnProperty.call(obj, key)) {
                clonedObj[key] = deepClone(obj[key])
            }
        }
        return clonedObj
    }
}
export default executeFunction
src/views/home/index.vue
@@ -235,7 +235,7 @@
        // 点击播放视频
        openVideoPlayer () {
            this.$store.commit("SET_ISSHOWVIDEODIALOG", true)
            // this.$store.commit("SET_ISSHOWVIDEODIALOG", true)
        },
    },
src/views/login/index.vue
New file
@@ -0,0 +1,58 @@
<template>
  <div class="login-container" ref="login" @keyup.enter.native="handleLogin">
    <div class="login-weaper">
      <div class="login-left">
        <div class="login-time">
          {{ time }}
        </div>
        <img class="img" src="/images/logo.png" alt="" />
        <p class="title">保安员考试大屏</p>
      </div>
      <div class="login-border">
        <div class="login-main">
          <h4 class="login-title">
            系统登录
          </h4>
          <h4 class="login-title-child">Security Guard Exam System</h4>
          <userLogin></userLogin>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
import userLogin from "./userlogin";
import { dateFormat } from "@/utils/date";
export default {
  name: "login",
  components: {
    userLogin,
  },
  data() {
    return {
      time: "",
    };
  },
  created() {
    document.title = "保安员考试大屏"
    this.$store.commit("SET_WEBTITLE", '保安员考试大屏')
    this.getTime();
  },
  mounted() {},
  props: [],
  methods: {
    getTime() {
      setInterval(() => {
        this.time = dateFormat(new Date());
      }, 1000);
    },
    handleLogin() {
    },
  },
};
</script>
<style lang="scss">
@import "@/styles/login.scss";
</style>
src/views/login/userlogin.vue
New file
@@ -0,0 +1,82 @@
<template>
  <el-form class="login-form" status-icon :rules="loginRules" ref="loginForm" :model="loginForm" label-width="0">
    <el-form-item prop="username">
      <el-input size="small" @keyup.enter.native="handleLogin" v-model="loginForm.username" auto-complete="off"
                placeholder="请输入用户名">
        <i slot="prefix" class="fa fa-user"/>
      </el-input>
    </el-form-item>
    <el-form-item prop="password">
      <el-input size="small" @keyup.enter.native="handleLogin" :type="passwordType" v-model="loginForm.password"
                auto-complete="off" placeholder="请输入密码">
        <i class="el-icon-view el-input__icon" slot="suffix" @click="showPassword"/>
        <i slot="prefix" class="fa fa-lock"/>
      </el-input>
    </el-form-item>
    <el-form-item>
      <el-button type="primary" size="small" @click.native.prevent="handleLogin" class="login-submit">登录
      </el-button>
    </el-form-item>
  </el-form>
</template>
<script>
import {Message} from "element-ui";
export default {
  name: "userlogin",
  data() {
    return {
      loginForm: {
        tenantId: "000000",
        username: "",
        password: "",
        type: "account",
      },
      loginRules: {
        username: [
          {required: true, message: "请输入用户名", trigger: "blur"},
        ],
        password: [
          {required: true, message: "请输入密码", trigger: "blur"},
          {min: 6, message: "密码长度最少为6位", trigger: "blur"},
        ],
      },
      passwordType: "password",
    }
  },
  mounted() {
  },
  methods: {
    showPassword() {
      this.passwordType = this.passwordType === "password" ? "" : "password"
    },
    handleLogin() {
      this.$refs.loginForm.validate((valid) => {
        if (valid) {
          const loading = this.$loading({
            lock: true,
            text: "登录中,请稍后...",
            spinner: "el-icon-loading",
          })
          this.$store
            .dispatch("LoginByUsername", this.loginForm)
            .then((data) => {
              if (data.error_description) {
                loading.close()
              }else{
                this.$router.push({path: '/layout/home'})
                loading.close()
              }
            })
            .catch(() => {
              loading.close()
            })
        }
      })
    },
  },
}
</script>
<style></style>
vue.config.js
@@ -29,36 +29,6 @@
            scss: {
                prependData: `@import "~@/styles/scssFile.scss";`,
            },
            // postcss: {
            //     plugins: [   //配置px转rem进行适配
            //         require('postcss-px2rem')({
            //             // 根标签字号大小根据设计稿宽度/flexible.js里的份数得到,这里假定设计稿宽度为3840 3840/24 = 160px
            //             remUnit: 80
            //         }),
            //     ]
            // }
            // postcss: {
            //     //给postcss-loader传递选项
            //     plugins: [
            //         new pxtovw({
            //             unitToConvert: 'px', //需要转换的单位,默认为"px";
            //             viewportWidth: 1920, //设计稿的视口宽度
            //             viewportHeight: 1080, //设计稿的视口宽度
            //             unitPrecision: 5, //单位转换后保留的小数位数
            //             propList: ['*'], //要进行转换的属性列表,*表示匹配所有,!表示不转换
            //             viewportUnit: 'vw', //转换后的视口单位
            //             fontViewportUnit: 'vw', //转换后字体使用的视口单位
            //             selectorBlackList: [], //不进行转换的css选择器,继续使用原有单位
            //             minPixelValue: 1, //设置最小的转换数值
            //             mediaQuery: false, //设置媒体查询里的单位是否需要转换单位
            //             replace: true, //是否直接更换属性值,而不添加备用属性
            //             exclude: [/node_modules/], //忽略某些文件夹下的文件
            //             // landscape: true,
            //             // landscapeUnit: 'vw',
            //             // landscapeWidth: 1920
            //         })
            //     ]
            // }
        },
    },
@@ -94,11 +64,6 @@
                }
            ],
        ])
        // config.plugin("define").tap((args) => {
        //     args[0]["process"] = { ...args[0]["process.env"] }
        //     return args
        // })
    },
    configureWebpack: {
@@ -125,56 +90,19 @@
                maxChunks: 5,
                minChunkSize: 100,
            }),
            // 提供带 Content-Encoding 编码的压缩版的资源
            // new CompressionPlugin({
            //     algorithm: 'gzip',
            //     test: /\.js$|\.html$|\.css/, // 匹配文件名
            //     // test: /\.(js|css)$/,
            //     threshold: 10240, // 对超过10k的数据压缩
            //     deleteOriginalAssets: false, // 不删除源文件
            //     minRatio: 0.8 // 压缩比
            // })
        ],
    },
    devServer: {
        // open: false, // 编译完成是否打开网页
        // host: "0.0.0.0", // 指定使用地址,默认localhost,0.0.0.0代表可以被外界访问
        // // port: 82, // 访问端口
        // https: false, // 编译失败时刷新页面
        // hot: true, // 开启热加载
        // hotOnly: false,
        // proxy: {
        //     [process.env.VUE_APP_API]: {//拦截器(拦截链接中有/api)
        //         target: process.env.VUE_API_DEV_TARGET,//'http://192.168.1.114:3000', //process.env.VUE_API_DEV_TARGET, //API服务器的地址
        //         changeOrigin: true,
        //         pathRewrite: {
        //             //'^/api': '/'
        //             [`^${process.env.VUE_APP_API}`]: '',
        //             //[`^${process.env.VUE_APP_API}`]: '/', //配置出来的接口没有 /api
        //         }
        //     }
        // }
        proxy: {
            // 中台接口
            "/services": {
                target: "https://sk.hubeishuiyi.cn",
                changeOrigin: true, //开启代理跨域
                // pathRewrite: {
                //     "^/services": "/",
                // },
            },
            // 业务平台接口
            "/api": {
                // target用于配置你允许访问数据的计算机名称,即是你的api接口的服务器地址
                target: "http://60.220.177.113:2081",
                target: "http://124.164.10.118:2081",
                // target: "http://192.168.0.102:81",
                // ws: true, //启用webSocket6566
                changeOrigin: true, //开启代理跨域
                // pathRewrite: {//本地地址没有api需要去掉
                //     '^/api': '/'
                // }
                pathRewrite: {}//后端接口包含/api前缀,不需要重写
            },
        },
    },