hejianhao
2025-04-07 1346881e59c20244fed789e1b346b0d22bea1dc5
登录
1个文件已删除
8个文件已修改
13774 ■■■■■ 已修改文件
package-lock.json 13545 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
package.json 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/index.js 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/store/index.js 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/baseurl.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/request.js 109 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/utils.js 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/view/login/index.vue 72 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/view/login/service.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
package-lock.json
File was deleted
package.json
@@ -14,6 +14,7 @@
    "autofit.js": "^3.0.4",
    "axios": "^1.5.0",
    "core-js": "^2.6.5",
    "crypto-js": "^4.2.0",
    "echarts": "^5.5.0",
    "element-ui": "^2.15.14",
    "ezuikit-js": "^7.7.10",
src/router/index.js
@@ -20,19 +20,19 @@
const router = createRouter()
// 路由守卫
// router.beforeEach((to, from, next) => {
//   const token = store.state.token
//   if (!token && to.path != '/') {
//     // 如果没有 token 并且不是去登录页,重定向到登录页
//     next('/')
//   } else if (token && to.path === '/') {
//     // 如果有 token 并且要去登录页,重定向到首页
//     next('/home')
//   } else {
//     // 清理 localStorage
//     localStorage.removeItem('registerForm')
//     next()
//   }
// })
router.beforeEach((to, from, next) => {
  const token = store.state.token
  if (!token && to.path != '/') {
    // 如果没有 token 并且不是去登录页,重定向到登录页
    next('/')
  } else if (token && to.path === '/') {
    // 如果有 token 并且要去登录页,重定向到首页
    next('/home')
  } else {
    // 清理 localStorage
    localStorage.removeItem('registerForm')
    next()
  }
})
export default router
src/store/index.js
@@ -6,7 +6,7 @@
export default new Vuex.Store({
  state: {
    token: localStorage.getItem('token') || sessionStorage.getItem('token') || '',
    userInfo: {}
    userInfo: localStorage.getItem('userInfo') || {}
  },
  mutations: {
    setToken(state, token) {
@@ -15,10 +15,13 @@
    },
    clearToken(state) {
      state.token = '';
      state.userInfo = {}
      localStorage.clear();
      sessionStorage.clear();
    },
    setUserInfo(state, userInfo) {
      state.userInfo = userInfo;
      localStorage.setItem('userInfo', userInfo)
    }
  },
  actions: {},
src/utils/baseurl.js
@@ -1,7 +1,7 @@
const apiConfig = {
    // 开发环境
    development: {
        baseURL: "",
        baseURL: "http://192.168.110.85:9000",
        mapKey: "67968c82f27c7e2cb9f40c1a9aa3042b",
        secretKey: "37ce61ae86efa5ad82b649a277f5097c",
    },
src/utils/request.js
@@ -1,6 +1,7 @@
import axios from 'axios'
import apiConfig from './baseurl'
import store from '@/store' // 导入 Vuex store
import CryptoJS from 'crypto-js';
import {
  Message
@@ -11,24 +12,108 @@
  withCredentials: false, // 当跨域请求时发送cookie
  timeout: 30000, // request timeout
})
// 对 POST 请求参数进行排序
const sortPostParams = (params) => {
  const keys = Object.keys(params);
  keys.sort((a, b) => {
    for (let i = 0; i < Math.min(a.length, b.length); i++) {
      const asciiA = a.charCodeAt(i);
      const asciiB = b.charCodeAt(i);
      if (asciiA !== asciiB) {
        return asciiA - asciiB;
      }
    }
    // 如果两个字符串长度不同,则较短的字符串排在前面
    return a.length - b.length;
  });
  const sortedParams = {};
  keys.forEach(key => {
    sortedParams[key] = params[key];
  });
  return sortedParams;
}
//去除对象中空值的属性
const removeEmptyProperties = (obj) => {
  return Object.fromEntries(Object.entries(obj).filter(([key, value]) => value !== undefined && value !== null && value !== '' && (Array.isArray(value) ? value.length > 0 : true)));
};
// 将对象转换为字符串用&拼接
const objToString = (obj) => {
  // 使用 Object.keys() 获取对象的 key 数组
  const keys = Object.keys(obj);
  // 使用 Array.prototype.map() 将每个 key-value 对转化为字符串
  const strArray = keys.map(key => {
    const value = obj[key];
    if (Array.isArray(value) || typeof value === 'object' && value !== null) {
      return '';
    } else {
      return `${key}=${value}`;
    }
  });
  // 使用 Array.prototype.filter() 过滤掉空字符串
  const filteredStrArray = strArray.filter(str => str !== '');
  // 使用 Array.prototype.join() 将字符串数组拼接成一个字符串
  const str = filteredStrArray.join('&');
  return str;
}
// 生成随机字符串
const generateRandomString = (length) => {
  const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  const str = [];
  for (let i = 0; i < length; i++) {
    const randomIndex = Math.floor(Math.random() * chars.length);
    str.push(chars[randomIndex]);
  }
  return str.join('');
}
// 对字符串进行 HMAC-SHA1 加密
const encryptWithHMACSHA1 = (message, secret) => {
  return CryptoJS.HmacSHA1(message, secret).toString(CryptoJS.enc.Base64);
};
// 请求拦截
service.interceptors.request.use(
  config => {
    config.headers['Authorization'] = 'Bearer ' + store.state.token
    if (config.method == 'get') {
      if (!config.params) config.params = {};
      config.params = {
        ...config.params,
    const o = config
    let { data, method } = o
    const Authorization = localStorage.getItem('token') || ''
    if (method == 'post') {
      let copyData = JSON.parse(JSON.stringify(data))
      // 对 POST 请求参数进行处理
      let sortData = sortPostParams(copyData)
      sortData = removeEmptyProperties(sortData)
      // 将处理后的 POST 请求参数转换为字符串
      let strData = objToString(sortData)
      // 生成随机字符串  用作加密秘钥
      let nonce_str = generateRandomString(16)
      // 对字符串进行 HMAC-SHA1 加密 并转化为 Base64
      let sign = encryptWithHMACSHA1(strData, nonce_str)
      o.headers = {
        ...o.headers,
        timestamp: new Date().getTime(),
        sign,
        nonce_str,
        Authorization,
        client: localStorage.getItem('client')
      }
    } else {
      o.headers = {
        timestamp: new Date().getTime(),
        ...config.headers,
        Authorization,
        client: localStorage.getItem('client')
      }
    }
    if (config.method == 'post') {
      if (!config.data) config.data = {};
      config.data = {
        ...config.data,
      }
    }
    return config
    return { ...o };
  },
  error => {
    return Promise.reject(error)
src/utils/utils.js
@@ -7,6 +7,16 @@
  }
  return code;
}
// 生成随机字符串
export function generateRandomString(length) {
  const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  const str = [];
  for (let i = 0; i < length; i++) {
    const randomIndex = Math.floor(Math.random() * chars.length);
    str.push(chars[randomIndex]);
  }
  return str.join('');
};
// 导出
export const exportExcell = (name, params, url) => {
src/view/login/index.vue
@@ -9,14 +9,14 @@
      <div class="mt--20 txt-center py--20 px--20 br--10 box-s1">
        <div class="fs--20 fw-bold">登陆</div>
        <div class="mt--20">
          <el-input class="w100" prefix-icon="el-icon-user" placeholder="账号" v-model="account" />
          <el-input class="w100" prefix-icon="el-icon-user" placeholder="账号" v-model="username" />
        </div>
        <div class="mt--20">
          <el-input prefix-icon="el-icon-lock" placeholder="密码" v-model="password" show-password />
        </div>
        <div class="mt--20 flex a-center code_box">
          <div class="flex1">
            <el-input  placeholder="请输入验证码" v-model="code" />
            <el-input placeholder="请输入验证码" v-model="code" />
          </div>
          <div @click="resetCodeStr" class="fs--18 lh--40 border1 pointer">{{ codeStr }}</div>
        </div>
@@ -28,15 +28,20 @@
</template>
<script>
import { generateVerificationCode } from '@/utils/utils';
import { generateVerificationCode, generateRandomString } from '@/utils/utils';
import { mapMutations } from 'vuex';
import { loginPwd } from './service'
import CryptoJS from 'crypto-js';
import {
  Message
} from 'element-ui'
export default {
  components: {},
  props: {},
  data() {
    return {
      loginLoading: false,//登录loading
      account: '',
      username: '',
      password: '',
      code: '',
      codeStr: ''
@@ -52,14 +57,65 @@
    document.removeEventListener("keydown", this.handleKeyDown);
  },
  methods: {
    ...mapMutations(['setToken', 'clearToken']),
    ...mapMutations(['setToken', 'clearToken', 'setUserInfo']),
    login() {
      this.loginLoading = false;
      // this.setToken(res.access_token);
      this.$router.push('/home')
      if (!this.rulesLogin()) return
      this.loginLoading = true;
      loginPwd({
        username: this.username,
        password: CryptoJS.HmacMD5(this.password, 'password').toString(
          CryptoJS.enc.Hex,
        )
      }).then(res => {
        localStorage.setItem('client', generateRandomString(16));
        this.loginLoading = false;
        this.setToken(res.token.access_token);
        this.setUserInfo(JSON.stringify(res.info.sysUser));
        this.$router.push('/home')
      }).catch(() => {
        this.resetCodeStr()
        this.loginLoading = false;
      })
    },
    rulesLogin() {
      if (!this.username) {
        Message({
          message: '请输入账号',
          type: 'warning',
          duration: 2000
        })
        return false
      }
      if (!this.password) {
        Message({
          message: '请输入密码',
          type: 'warning',
          duration: 2000
        })
        return false
      }
      if (!this.code) {
        Message({
          message: '请输入验证码',
          type: 'warning',
          duration: 2000
        })
        return false
      }
      if (!this.matchCaseInsensitive(this.codeStr, this.code)) {
        Message({
          message: "验证码错误",
          type: "warning",
          duration: 1500,
        });
        this.resetCodeStr()
        return false
      }
      return true
    },
    handleKeyDown(event) {
      if (event.key === "Enter") {
        this.login()
      }
    },
    // 校验验证码
src/view/login/service.js
@@ -2,5 +2,5 @@
// 密码登录
export const loginPwd = (data) => {
    return axios.post('/auth/companyLogin', data)
    return axios.post('/auth/login', data)
}