kevin_zhangl преди 2 години
родител
ревизия
5159bd36ee

+ 36 - 0
src/api/site.js

@@ -0,0 +1,36 @@
+import request from '@/utils/request'
+
+/**
+ * pageData
+ * @param data
+ * @returns {*}
+ */
+export function getPageData(data) {
+  return request({
+    url: '/v1/site/page-data',
+    method: 'get',
+    data
+  })
+}
+
+/**
+ * 查看是否存在当前时间差,如果不存在,则向服务器请求时间差
+ * @returns {*}
+ */
+export function daysDiff() {
+  return request({
+    url: '/v1/site/days-diff',
+    method: 'get'
+  })
+}
+
+/**
+ * 基本信息
+ * @returns {*}
+ */
+export function getBaseInfo() {
+  return request({
+    url: '/v1/site/base-info',
+    method: 'get'
+  })
+}

+ 25 - 8
src/api/user.js

@@ -1,24 +1,41 @@
 import request from '@/utils/request'
 
-export function login(data) {
+/**
+ * 登录
+ * @param data
+ * @param pageId
+ * @returns {*}
+ */
+export function login(data, pageId) {
   return request({
-    url: '/vue-element-admin/user/login',
+    url: '/v1/oauth/login?page_id=' + pageId,
     method: 'post',
     data
   })
 }
 
-export function getInfo(token) {
+/**
+ * 验证码
+ * @param data
+ * @returns {*}
+ */
+export function captcha(data) {
   return request({
-    url: '/vue-element-admin/user/info',
+    url: '/v1/site/captcha',
     method: 'get',
-    params: { token }
+    params: data
   })
 }
 
-export function logout() {
+/**
+ * 获取用户信息
+ * @param token
+ * @returns {*}
+ */
+export function getUserInfo(token) {
   return request({
-    url: '/vue-element-admin/user/logout',
-    method: 'post'
+    url: '/v1/oauth/info',
+    method: 'get',
+    params: { token }
   })
 }

+ 7 - 7
src/components/LangSelect/index.vue

@@ -10,12 +10,12 @@
       <el-dropdown-item :disabled="language==='en'" command="en">
         English
       </el-dropdown-item>
-      <el-dropdown-item :disabled="language==='es'" command="es">
-        Español
-      </el-dropdown-item>
-      <el-dropdown-item :disabled="language==='ja'" command="ja">
-        日本語
-      </el-dropdown-item>
+      <!--      <el-dropdown-item :disabled="language==='es'" command="es">-->
+      <!--        Español-->
+      <!--      </el-dropdown-item>-->
+      <!--      <el-dropdown-item :disabled="language==='ja'" command="ja">-->
+      <!--        日本語-->
+      <!--      </el-dropdown-item>-->
     </el-dropdown-menu>
   </el-dropdown>
 </template>
@@ -32,7 +32,7 @@ export default {
       this.$i18n.locale = lang
       this.$store.dispatch('app/setLanguage', lang)
       this.$message({
-        message: 'Switch Language Success',
+        message: this.$t('common.switchLanguageHints'),
         type: 'success'
       })
     }

+ 32 - 5
src/lang/en.js

@@ -73,13 +73,11 @@ export default {
     size: 'Global Size'
   },
   login: {
-    title: 'Login Form',
+    title: 'LOGIN',
     logIn: 'Login',
-    username: 'Username',
+    username: 'UserName',
     password: 'Password',
-    any: 'any',
-    thirdparty: 'Or connect with',
-    thirdpartyTips: 'Can not be simulated on local, so please combine you own business simulation! ! !'
+    verifyCode: 'Verification Code'
   },
   documentation: {
     documentation: 'Documentation',
@@ -171,5 +169,34 @@ export default {
     tagsView: 'Open Tags-View',
     fixedHeader: 'Fixed Header',
     sidebarLogo: 'Sidebar Logo'
+  },
+
+  common: {
+    save: 'Save',
+    modify: 'Modify',
+    submit: 'Submit',
+    delete: 'Delete',
+    edit: 'Edit',
+    action: 'Action',
+    yes: 'YES',
+    no: 'NO',
+    view: 'View',
+    select: 'Select',
+    reset: 'Reset',
+    startDate: 'Start Date',
+    endDate: 'End Date',
+    deleteTips: 'Are you sure you want to delete the selected data?',
+    hint: 'Hint',
+    cancel: 'Cancel',
+    confirm: 'Confirm',
+    successfully: 'Successfully',
+    close: 'Close',
+    note: 'Note',
+    awaitData: 'Retrieving data, please wait.',
+    switchLanguageHints: 'Switch Language Success',
+    websiteTitle: 'Member Management System',
+    uploadHints: 'Uploading, please hold on. Do not close the window!',
+    canNotBeBlank: ' cannot be blank',
+    require: 'is required'
   }
 }

+ 30 - 3
src/lang/zh.js

@@ -77,9 +77,7 @@ export default {
     logIn: '登录',
     username: '账号',
     password: '密码',
-    any: '随便填',
-    thirdparty: '第三方登录',
-    thirdpartyTips: '本地不能模拟,请结合自己业务进行模拟!!!'
+    verifyCode: '验证码'
   },
   documentation: {
     documentation: '文档',
@@ -171,5 +169,34 @@ export default {
     tagsView: '开启 Tags-View',
     fixedHeader: '固定 Header',
     sidebarLogo: '侧边栏 Logo'
+  },
+
+  common: {
+    save: '保存',
+    modify: '修改',
+    submit: '提交',
+    delete: '删除',
+    edit: '编辑',
+    action: '操作',
+    yes: '是',
+    no: '否',
+    view: '查看',
+    select: '查询',
+    reset: '清空',
+    startDate: '开始时间',
+    endDate: '结束时间',
+    deleteTips: '确认删除所选数据?',
+    hint: '提示',
+    confirm: '确定',
+    cancel: '取消',
+    successfully: '成功',
+    close: '关闭',
+    note: '备注',
+    awaitData: '正在获取数据,请稍后',
+    switchLanguageHints: '语言切换成功',
+    websiteTitle: '会员管理系统',
+    uploadHints: '正在上传,请稍后。请勿关闭窗口!',
+    canNotBeBlank: '不能为空',
+    require: '必须填写'
   }
 }

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

@@ -22,7 +22,7 @@
 
       <el-dropdown class="avatar-container right-menu-item hover-effect" trigger="click">
         <div class="avatar-wrapper">
-          <img :src="avatar+'?imageView2/1/w/80/h/80'" class="user-avatar">
+          <img :src="avatar+'?imageView2/1/w/80/h/80'" class="user-avatar" alt="">
           <i class="el-icon-caret-bottom" />
         </div>
         <el-dropdown-menu slot="dropdown">

+ 0 - 7
src/layout/components/Settings/index.vue

@@ -22,13 +22,6 @@
         <span>{{ $t('settings.sidebarLogo') }}</span>
         <el-switch v-model="sidebarLogo" class="drawer-switch" />
       </div>
-      <a v-if="isShowJob" href="https://panjiachen.github.io/vue-element-admin-site/zh/job/" target="_blank" class="job-link">
-        <el-alert
-          title="部门目前非常缺人!有兴趣的可以点击了解详情。坐标: 字节跳动"
-          type="success"
-          :closable="false"
-        />
-      </a>
 
       <div v-if="lang === 'zh'" class="drawer-item">
         <span>菜单支持拼音搜索</span>

+ 6 - 0
src/layout/components/Sidebar/index.vue

@@ -23,9 +23,15 @@ import { mapGetters } from 'vuex'
 import Logo from './Logo'
 import SidebarItem from './SidebarItem'
 import variables from '@/styles/variables.scss'
+import store from '@/store'
 
 export default {
   components: { SidebarItem, Logo },
+  created() {
+    if (this.permission_routes.length <= 0) {
+      store.dispatch('permission/generateRoutes', [])
+    }
+  },
   computed: {
     ...mapGetters([
       'permission_routes',

+ 8 - 11
src/permission.js

@@ -1,10 +1,9 @@
-import router from './router'
+import router, { resetRouter } from './router'
 import store from './store'
-import { Message } from 'element-ui'
 import NProgress from 'nprogress' // progress bar
 import 'nprogress/nprogress.css' // progress bar style
-import { getToken } from '@/utils/auth' // get token from cookie
 import getPageTitle from '@/utils/get-page-title'
+import usersInfo from '@/utils/usersInfo'
 
 NProgress.configure({ showSpinner: false }) // NProgress Configuration
 
@@ -18,7 +17,7 @@ router.beforeEach(async(to, from, next) => {
   document.title = getPageTitle(to.meta.title)
 
   // determine whether the user has logged in
-  const hasToken = getToken()
+  const hasToken = usersInfo.hasLogin()
 
   if (hasToken) {
     if (to.path === '/login') {
@@ -34,21 +33,19 @@ router.beforeEach(async(to, from, next) => {
         try {
           // get user info
           // note: roles must be a object array! such as: ['admin'] or ,['developer','editor']
-          const { roles } = await store.dispatch('user/getInfo')
-
+          const { roles } = ['admin']
+          resetRouter()
           // generate accessible routes map based on roles
           const accessRoutes = await store.dispatch('permission/generateRoutes', roles)
-
+          console.log(accessRoutes)
           // dynamically add accessible routes
           router.addRoutes(accessRoutes)
-
           // hack method to ensure that addRoutes is complete
           // set the replace: true, so the navigation will not leave a history record
           next({ ...to, replace: true })
         } catch (error) {
-          // remove token and go to login page to re-login
-          await store.dispatch('user/resetToken')
-          Message.error(error || 'Has Error')
+          console.log('error', error)
+          usersInfo.clear()
           next(`/login?redirect=${to.path}`)
           NProgress.done()
         }

+ 8 - 1
src/router/index.js

@@ -403,7 +403,7 @@ export const asyncRoutes = [
 const createRouter = () => new Router({
   // mode: 'history', // require service support
   scrollBehavior: () => ({ y: 0 }),
-  routes: constantRoutes
+  routes: constantRoutes.concat(asyncRoutes)
 })
 
 const router = createRouter()
@@ -414,4 +414,11 @@ export function resetRouter() {
   router.matcher = newRouter.matcher // reset router
 }
 
+export function selfAddRoutes(params) {
+  const newRouter = createRouter()
+  router.matcher = newRouter.matcher
+  router.addRoutes(params)
+}
+
 export default router
+

+ 14 - 0
src/store/modules/region.js

@@ -0,0 +1,14 @@
+const regionInfo = {
+  provinceAndCityData: [],
+  regionData: [],
+  provinceAndCityDataPlus: [],
+  regionDataPlus: [],
+  CodeToText: {
+    '': null
+  },
+  TextToCode: {}
+}
+
+export default {
+  regionInfo: regionInfo
+}

+ 11 - 0
src/store/modules/settings.js

@@ -1,5 +1,6 @@
 import variables from '@/styles/element-variables.scss'
 import defaultSettings from '@/settings'
+import { getPageData } from '@/api/site'
 
 const { showSettings, tagsView, fixedHeader, sidebarLogo, supportPinyinSearch } = defaultSettings
 
@@ -24,6 +25,16 @@ const mutations = {
 const actions = {
   changeSetting({ commit }, data) {
     commit('CHANGE_SETTING', data)
+  },
+
+  getPageData({ commit }, data) {
+    return new Promise((resolve, reject) => {
+      getPageData({}).then(response => {
+        resolve(response)
+      }).catch(error => {
+        reject(error)
+      })
+    })
   }
 }
 

+ 53 - 61
src/store/modules/user.js

@@ -1,13 +1,17 @@
-import { login, logout, getInfo } from '@/api/user'
+import { login, logout, getUserInfo } from '@/api/user'
 import { getToken, setToken, removeToken } from '@/utils/auth'
+import usersInfo from '@/utils/usersInfo'
 import router, { resetRouter } from '@/router'
+import { Message } from 'element-ui'
+import { getBaseInfo } from '@/api/site'
+import baseInfo from '@/utils/baseInfo'
 
 const state = {
   token: getToken(),
   name: '',
   avatar: '',
   introduction: '',
-  roles: []
+  roles: ['admin']
 }
 
 const mutations = {
@@ -31,67 +35,75 @@ const mutations = {
 const actions = {
   // user login
   login({ commit }, userInfo) {
-    const { username, password } = userInfo
+    const requestData = {
+      adminName: userInfo.username,
+      password: userInfo.password,
+      verifyCode: userInfo.verifyCode
+    }
     return new Promise((resolve, reject) => {
-      login({ username: username.trim(), password: password }).then(response => {
-        const { data } = response
-        commit('SET_TOKEN', data.token)
-        setToken(data.token)
+      login({ LoginForm: requestData }, userInfo.pageId).then(response => {
+        // 更新本地accessToken
+        usersInfo.updateLoginAllInfo(response.data)
+
+        commit('SET_TOKEN', response.data.accessToken)
+        commit('SET_ROLES', ['admin'])
+        commit('SET_NAME', 'userName')
+        commit('SET_AVATAR', 'avatar')
+        commit('SET_INTRODUCTION', 'introduction')
         resolve()
       }).catch(error => {
+        Message({
+          message: error.message,
+          type: 'error',
+          duration: 5 * 1000
+        })
+
+        usersInfo.clear()
         reject(error)
       })
     })
   },
 
-  // get user info
-  getInfo({ commit, state }) {
+  getUserInfo({ commit }, data) {
     return new Promise((resolve, reject) => {
-      getInfo(state.token).then(response => {
-        const { data } = response
-
-        if (!data) {
-          reject('Verification failed, please Login again.')
-        }
-
-        const { roles, name, avatar, introduction } = data
-
-        // roles must be a non-empty array
-        if (!roles || roles.length <= 0) {
-          reject('getInfo: roles must be a non-null array!')
-        }
-
-        commit('SET_ROLES', roles)
-        commit('SET_NAME', name)
-        commit('SET_AVATAR', avatar)
-        commit('SET_INTRODUCTION', introduction)
-        resolve(data)
+      getUserInfo(data).then(response => {
+        const result = response.data
+        // 更新本地userInfo
+        usersInfo.userId(result.ID)
+        usersInfo.adminName(result.ADMIN_NAME)
+        usersInfo.baseData({ roleId: result.ROLE_ID })
+        resolve(result)
       }).catch(error => {
         reject(error)
       })
     })
   },
 
-  // user logout
-  logout({ commit, state, dispatch }) {
+  getBaseInfo({ commit }, data) {
     return new Promise((resolve, reject) => {
-      logout(state.token).then(() => {
-        commit('SET_TOKEN', '')
-        commit('SET_ROLES', [])
-        removeToken()
-        resetRouter()
-
-        // reset visited views and cached views
-        // to fixed https://github.com/PanJiaChen/vue-element-admin/issues/2485
-        dispatch('tagsView/delAllViews', null, { root: true })
-
-        resolve()
+      getBaseInfo().then(response => {
+        baseInfo.set(response.data)
+        resolve(response)
       }).catch(error => {
         reject(error)
       })
     })
   },
 
+  // user logout
+  logout({ commit, state, dispatch }) {
+    return new Promise((resolve, reject) => {
+      commit('SET_TOKEN', '')
+      commit('SET_ROLES', [])
+      usersInfo.clear()
+      usersInfo.baseData()
+      removeToken()
+      resetRouter()
+      dispatch('tagsView/delAllViews', null, { root: true })
+      resolve()
+    })
+  },
+
   // remove token
   resetToken({ commit }) {
     return new Promise(resolve => {
@@ -100,26 +112,6 @@ const actions = {
       removeToken()
       resolve()
     })
-  },
-
-  // dynamically modify permissions
-  async changeRoles({ commit, dispatch }, role) {
-    const token = role + '-token'
-
-    commit('SET_TOKEN', token)
-    setToken(token)
-
-    const { roles } = await dispatch('getInfo')
-
-    resetRouter()
-
-    // generate accessible routes map based on roles
-    const accessRoutes = await dispatch('permission/generateRoutes', roles, { root: true })
-    // dynamically add accessible routes
-    router.addRoutes(accessRoutes)
-
-    // reset visited views and cached views
-    dispatch('tagsView/delAllViews', null, { root: true })
   }
 }
 

+ 81 - 0
src/utils/baseInfo.js

@@ -0,0 +1,81 @@
+const baseInfo = {
+  set(info) {
+    localStorage.setItem('baseInfo', JSON.stringify(info))
+  },
+  get(...arg) {
+    const baseInfo = JSON.parse(localStorage.getItem('baseInfo'))
+    if (baseInfo === null) {
+      return null
+    }
+    if (arg.length === 0 || arg[0] === '') {
+      return baseInfo
+    } else {
+      return baseInfo[arg[0]]
+    }
+  },
+  clear() {
+    localStorage.removeItem('baseInfo')
+  },
+  menu() {
+    return this.get('menu')
+  },
+  decLevels() {
+    return this.get('decLevels')
+  },
+  empLevels() {
+    return this.get('empLevels')
+  },
+  crownLevels() {
+    return this.get('crownLevels')
+  },
+  regTypes() {
+    return this.get('regTypes')
+  },
+  adminRoles() {
+    return this.get('adminRoles')
+  },
+  superAdminRoleId() {
+    return this.get('superAdminRoleId')
+  },
+  setDaysDiff(daysDiff) {
+    const baseInfo = {
+      daysDiff: daysDiff
+    }
+    localStorage.setItem('baseInfo', JSON.stringify(baseInfo))
+  },
+  daysDiff() {
+    return this.get('daysDiff')
+  },
+  shopWalletType() {
+    return this.get('shopWalletType')
+  },
+  decRoles() {
+    return this.get('decRoles')
+  },
+  subCompanies() {
+    return this.get('subCompanies')
+  },
+  allStatus() {
+    return this.get('allStatus')
+  },
+  dealTypes() {
+    return this.get('dealTypes')
+  },
+  systems() {
+    return this.get('systems')
+  },
+  allOpenBank() {
+    return this.get('allOpenBank')
+  },
+  allNation() {
+    return this.get('allNation')
+  },
+  nowPeriodNum() {
+    return this.get('nowPeriodNum')
+  },
+  exchangeRate() {
+    return this.get('exchangeRate')
+  }
+}
+
+export default baseInfo

+ 73 - 50
src/utils/request.js

@@ -1,13 +1,14 @@
 import axios from 'axios'
 import { MessageBox, Message } from 'element-ui'
 import store from '@/store'
-import { getToken } from '@/utils/auth'
+import usersInfo from '@/utils/usersInfo'
+import Cookies from 'js-cookie'
 
 // create an axios instance
 const service = axios.create({
   baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
   // withCredentials: true, // send cookies when cross-domain requests
-  timeout: 5000 // request timeout
+  timeout: 12000 // request timeout
 })
 
 // request interceptor
@@ -15,12 +16,16 @@ service.interceptors.request.use(
   config => {
     // do something before request is sent
 
-    if (store.getters.token) {
-      // let each request carry token
-      // ['X-Token'] is a custom headers key
-      // please modify it according to the actual situation
-      config.headers['X-Token'] = getToken()
+    // if (store.getters.token) {
+    // let each request carry token
+    // ['X-Token'] is a custom headers key
+    // please modify it according to the actual situation
+    if (usersInfo.accessToken()) {
+      config.headers['Authorization'] = process.env.VUE_APP_ACCESS_TOKEN_PREFIX + usersInfo.accessToken()
     }
+    // 设置语言
+    config.headers['language'] = Cookies.get('language') ?? 'en'
+    // }
     return config
   },
   error => {
@@ -32,53 +37,71 @@ service.interceptors.request.use(
 
 // response interceptor
 service.interceptors.response.use(
-  /**
-   * If you want to get http information such as headers or status
-   * Please return  response => response
-  */
-
-  /**
-   * Determine the request status by custom code
-   * Here is just an example
-   * You can also judge the status by HTTP Status Code
-   */
   response => {
-    const res = response.data
-
-    // if the custom code is not 20000, it is judged as an error.
-    if (res.code !== 20000) {
-      Message({
-        message: res.message || 'Error',
-        type: 'error',
-        duration: 5 * 1000
-      })
-
-      // 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
-      if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
-        // to re-login
-        MessageBox.confirm('You have been logged out, you can cancel to stay on this page, or log in again', 'Confirm logout', {
-          confirmButtonText: 'Re-Login',
-          cancelButtonText: 'Cancel',
-          type: 'warning'
-        }).then(() => {
-          store.dispatch('user/resetToken').then(() => {
-            location.reload()
-          })
-        })
-      }
-      return Promise.reject(new Error(res.message || 'Error'))
+    const responseData = response.data
+    const data = responseData.data
+    if ((responseData.success === false) || (responseData.code && responseData.code !== 200)) {
+      // Message({
+      //   message: responseData.message || 'Error',
+      //   type: 'error',
+      //   duration: 5 * 1000
+      // })
+      return Promise.reject(responseData.message)
     } else {
-      return res
+      if (!data) {
+        return { data: responseData }
+      }
+      return data
     }
   },
-  error => {
-    console.log('err' + error) // for debug
-    Message({
-      message: error.message,
-      type: 'error',
-      duration: 5 * 1000
-    })
-    return Promise.reject(error)
+  err => {
+    if (err && err.response && err.response.data && err.response.data.message) {
+      err.message = err.response.data.message
+    } else if (err && err.response) {
+      switch (err.response.data.status) {
+        case 400:
+          err.message = '请求错误'
+          break
+        case 401:
+          err.message = '未授权,请登录'
+          break
+        case 403:
+          err.message = '拒绝访问'
+          break
+        case 404:
+          err.message = `请求地址出错: ${err.response.data.config.url}`
+          break
+        case 408:
+          err.message = '请求超时'
+          break
+        case 500:
+          err.message = '服务器内部错误'
+          break
+        case 501:
+          err.message = '服务未实现'
+          break
+        case 502:
+          err.message = '网关错误'
+          break
+        case 503:
+          err.message = '服务不可用'
+          break
+        case 504:
+          err.message = '网关超时'
+          break
+        case 505:
+          err.message = 'HTTP版本不受支持'
+          break
+        default:
+      }
+    }
+    // Message({
+    //   message: err.message || 'Error',
+    //   type: 'error',
+    //   duration: 5 * 1000
+    // })
+
+    return Promise.reject(err)
   }
 )
 

+ 241 - 0
src/utils/tool.js

@@ -0,0 +1,241 @@
+// import errorCode from './errorCode'
+// import baseInfo from './baseInfo'
+// import {PRICE_IS_ROUND,SERVER_API_HTTP_TYPE,SERVER_API_DOMAIN,CDN_BASE_URL} from './config'
+import usersInfo from '@/utils/usersInfo'
+import errorCode from 'core-js/internals/internal-state'
+
+const tool = {
+  /**
+	 * 设置JS存在客户端的Storage值
+	 * @param key
+	 * @param value
+	 */
+  setStorage(key, value) {
+    localStorage.setItem(key, value)
+  },
+  /**
+	 * 获取Storage值
+	 * @param key
+	 * @returns {string | null}
+	 */
+  getStorage(key) {
+    return localStorage.getItem(key)
+  },
+  /**
+	 * 移除Storage值
+	 * @param key
+	 */
+  removeStorage(key) {
+    localStorage.removeItem(key)
+  },
+  /**
+	 * 获取当前时间戳(精确到秒)
+	 * @returns {number}
+	 */
+  getTimestamp(date = null) {
+    const days = usersInfo.daysDiff()
+    let dateObj
+    if (date !== null) {
+      dateObj = new Date(date)
+    } else {
+      dateObj = new Date()
+    }
+    return +Math.round(dateObj.getTime() / 1000 + (days * 86400))
+  },
+  formatDate(timestamp, withTime = true) {
+    const newDate = new Date()
+    timestamp = parseInt(timestamp)
+    if (timestamp) {
+      newDate.setTime(timestamp * 1000)
+    } else {
+      return ''
+    }
+    const Y = newDate.getFullYear() + '-'
+    const M = (newDate.getMonth() + 1 < 10 ? '0' + (newDate.getMonth() + 1) : newDate.getMonth() + 1) + '-'
+    const D = (newDate.getDate() < 10 ? '0' + (newDate.getDate()) : newDate.getDate()) + ' '
+    const h = (newDate.getHours() < 10 ? '0' + newDate.getHours() : newDate.getHours()) + ':'
+    const m = (newDate.getMinutes() < 10 ? '0' + newDate.getMinutes() : newDate.getMinutes()) + ':'
+    const s = (newDate.getSeconds() < 10 ? '0' + newDate.getSeconds() : newDate.getSeconds())
+    if (withTime) return Y + M + D + h + m + s
+    return Y + M + D
+  },
+  /**
+	 * 处理错误结果
+	 * @param error
+	 * @returns {{message: string, todo: *}}
+	 */
+  errorHandle(error) {
+    let message = ''
+    let status = 0
+    let todo
+    if (errorCode.has(error)) {
+      const errorResult = errorCode.get(error)
+      message = errorResult.message
+      status = errorResult.status ? errorResult.status : 0
+      todo = errorResult.todo
+    } else {
+      message = error.message
+      todo = null
+      status = error.data.status ? error.data.status : 0
+    }
+    if (todo || status === 401) {
+      message = todo ? '未获得授权,请重新登录' : (message === 'Your request was made with invalid credentials.' ? '未获得授权,请重新登录' : message)
+      usersInfo.logout()
+    }
+    if (todo || status === 402) {
+      message = todo ? '长时间未进行操作,请重新登录' : (message === 'Connection not operated for too long' ? '长时间未进行操作,请重新登录' : message)
+      usersInfo.logout()
+    }
+    return { message, todo, status }
+  },
+  /**
+	 * 解析URL地址
+	 * @param url
+	 * var myURL = parseURL('http://abc.com:8080/dir/index.html?id=255&m=hello#top');
+   myURL.file; // = 'index.html'
+   myURL.hash; // = 'top'
+   myURL.host; // = 'abc.com'
+   myURL.query; // = '?id=255&m=hello'
+   myURL.params; // = Object = { id: 255, m: hello }
+   myURL.path; // = '/dir/index.html'
+   myURL.segments; // = Array = ['dir', 'index.html']
+   myURL.port; // = '8080'
+   myURL.protocol; // = 'http'
+   myURL.source; // = 'http://abc.com:8080/dir/index.html?id=255&m=hello#top'
+	 * @returns {{source: *, protocol: string, host: string, port: string, query: string, params, file: string | *, hash: string, path: string, relative: string | *, segments: string[]}}
+	 */
+  parseURL(url) {
+    const a = document.createElement('a')
+    a.href = url
+    return {
+      source: url,
+      protocol: a.protocol.replace(':', ''),
+      host: a.hostname,
+      port: a.port,
+      query: a.search,
+      params: (function() {
+        const waitUrl = a.hash.replace(/^#\/[\-_a-zA-Z0-9]+\?/, '\?') || a.search
+        const ret = {}
+        const seg = waitUrl.replace(/^\?/, '').split('&')
+        const len = seg.length
+        let i = 0
+        let s
+        for (; i < len; i++) {
+          if (!seg[i]) {
+            continue
+          }
+          s = seg[i].split('=')
+          ret[s[0]] = s[1]
+        }
+        return ret
+      })(),
+      file: (a.pathname.match(/\/([^\/?#]+)$/i) || [, ''])[1],
+      hash: a.hash.replace('#', ''),
+      path: a.pathname.replace(/^([^\/])/, '/$1'),
+      relative: (a.href.match(/tps?:\/\/[^\/]+(.+)/) || [, ''])[1],
+      segments: a.pathname.replace(/^\//, '').split('/')
+    }
+  },
+  removeFromArr(arr, item) {
+    for (let i = arr.length - 1; i >= 0; i--) {
+      if (arr[i] === item) {
+        arr.splice(i, 1)
+        return arr
+      }
+    }
+  },
+  isInArray(arr, value) {
+    for (const i in arr) {
+      if (value === arr[i]) {
+        return true
+      }
+    }
+    return false
+  },
+  isString(val) {
+    return typeof val === 'string' || val instanceof String
+  },
+  /**
+	 * 格式化金额数值为两位小数,是否四舍五入
+	 * @param val
+	 * @returns {string}
+	 */
+  formatPrice(val) {
+    val = Number.parseFloat(val)
+    return val.toFixed(2)
+  },
+  /**
+	 * 根据状态返回颜色
+	 * @param val
+	 * @returns {string}
+	 */
+  statusType(val) {
+    switch (val) {
+      case '0':
+        return 'info'
+      case '1':
+        return ''
+      case '2':
+        return 'danger'
+      case '3':
+        return 'warning'
+      case '4':
+        return 'success'
+      default:
+        return 'info'
+    }
+  },
+  sum(arr) { // 求数组总和
+    var s = 0
+    for (var i = arr.length - 1; i >= 0; i--) {
+      s += arr[i]
+    }
+    return s
+  },
+  /**
+	 * 获取table显示高度
+	 * @param hasStatusBar
+	 * @returns {number}
+	 */
+  getTableHeight(hasStatusBar = false) {
+    if (hasStatusBar) return window.innerHeight - 320
+    return window.innerHeight - 260
+  },
+
+  // /**
+  //  * 拼装图片地址.
+  //  * @param imageUrl
+  //  * @param path
+  //  * @returns {string}
+  //  */
+  // getLocaleLink(imageUrl, path = '') {
+  //   return imageUrl.indexOf('http') > -1 ? imageUrl : `${process.env.VUE_APP_CDN_API}${path}${imageUrl}`
+  // },
+
+  // 计算商品税额
+  calculateTax(amount, taxRate, count = 1) {
+    const taxAmount = (amount - amount / (1 + taxRate / 100)) * count
+    return Math.round(taxAmount * 100) / 100
+  },
+
+  // 计算商品BV
+  calculateBV(amount, count = 1) {
+    return Math.round(amount * count)
+  },
+
+  /**
+	 * 拼装图片
+	 * @param imageUrl 图片
+	 * @param path 路径
+	 * @returns {string}
+	 */
+  getArImage(imageUrl, path) {
+    return process.env.VUE_APP_CDN_API + `/${path}/${imageUrl}`
+  },
+
+  getEmpLv(id) {
+    // return id.length > 0 ? getBaseInfo().empLevels : 'No Rank';
+  }
+}
+
+export default tool

+ 204 - 0
src/utils/usersInfo.js

@@ -0,0 +1,204 @@
+import tool from './tool'
+import router from '@/router'
+
+const usersInfo = {
+  refreshToken(...arg) {
+    if (arg.length === 0) {
+      return localStorage.getItem('refreshToken')
+    }
+    if (arg[0] === '') {
+      localStorage.removeItem('refreshToken')
+      return
+    }
+    localStorage.setItem('refreshToken', arg[0])
+  },
+  accessToken(...arg) {
+    if (arg.length === 0) {
+      return localStorage.getItem('accessToken')
+    }
+    if (arg[0] === '') {
+      localStorage.removeItem('accessToken')
+      return
+    }
+    localStorage.setItem('accessToken', arg[0])
+  },
+  accessTokenUpdateAt(...arg) {
+    if (arg.length === 0) {
+      return localStorage.getItem('accessTokenUpdateAt')
+    }
+    if (arg[0] === '') {
+      localStorage.removeItem('accessTokenUpdateAt')
+      return
+    }
+    localStorage.setItem('accessTokenUpdateAt', arg[0])
+  },
+  accessTokenExpiresIn(...arg) {
+    if (arg.length === 0) {
+      return localStorage.getItem('accessTokenExpiresIn')
+    }
+    if (arg[0] === '') {
+      localStorage.removeItem('accessTokenExpiresIn')
+      return
+    }
+    localStorage.setItem('accessTokenExpiresIn', arg[0])
+  },
+  refreshTokenUpdateAt(...arg) {
+    if (arg.length === 0) {
+      return localStorage.getItem('refreshTokenUpdateAt')
+    }
+    if (arg[0] === '') {
+      localStorage.removeItem('refreshTokenUpdateAt')
+      return
+    }
+    localStorage.setItem('refreshTokenUpdateAt', arg[0])
+  },
+  refreshTokenExpiresIn(...arg) {
+    if (arg.length === 0) {
+      return localStorage.getItem('refreshTokenExpiresIn')
+    }
+    if (arg[0] === '') {
+      localStorage.removeItem('refreshTokenExpiresIn')
+      return
+    }
+    localStorage.setItem('refreshTokenExpiresIn', arg[0])
+  },
+  loginTimestamp(...arg) {
+    if (arg.length === 0) {
+      return localStorage.getItem('loginTimestamp')
+    }
+    if (arg[0] === '') {
+      localStorage.removeItem('loginTimestamp')
+      return
+    }
+    localStorage.setItem('loginTimestamp', arg[0])
+  },
+  baseData(...arg) {
+    if (arg.length === 0) {
+      return JSON.parse(localStorage.getItem('baseData'))
+    }
+    if (arg[0] === '') {
+      localStorage.removeItem('baseData')
+      return
+    }
+    localStorage.setItem('baseData', JSON.stringify(arg[0]))
+  },
+  clear() {
+    localStorage.removeItem('userId')
+    localStorage.removeItem('adminName')
+    localStorage.removeItem('accessToken')
+    localStorage.removeItem('accessTokenExpiresIn')
+    localStorage.removeItem('accessTokenUpdateAt')
+    localStorage.removeItem('refreshToken')
+    localStorage.removeItem('refreshTokenExpiresIn')
+    localStorage.removeItem('refreshTokenUpdateAt')
+    localStorage.removeItem('loginTimestamp')
+    localStorage.removeItem('baseData')
+    localStorage.removeItem('baseInfo')
+    localStorage.removeItem('MenuPrmStore')
+    localStorage.removeItem('roleList')
+  },
+  clearBaseInfo() {
+    localStorage.removeItem('baseInfo')
+  },
+  setBaseInfo(info) {
+    localStorage.setItem('baseInfo', JSON.stringify(info))
+  },
+  getBaseInfo(...arg) {
+    let baseInfo = null
+    try {
+      baseInfo = JSON.parse(localStorage.getItem('baseInfo'))
+    } catch (err) {
+      return null
+    }
+    if (baseInfo === null) {
+      return null
+    }
+    if (arg.length === 0 || arg[0] === '') {
+      return baseInfo
+    } else {
+      return baseInfo[arg[0]]
+    }
+  },
+  userId(...arg) {
+    if (arg.length === 0) {
+      return localStorage.getItem('userId')
+    }
+    if (arg[0] === '') {
+      localStorage.removeItem('userId')
+      return
+    }
+    localStorage.setItem('userId', arg[0])
+  },
+  adminName(...arg) {
+    if (arg.length === 0) {
+      return localStorage.getItem('adminName')
+    }
+    if (arg[0] === '') {
+      localStorage.removeItem('adminName')
+      return
+    }
+    localStorage.setItem('adminName', arg[0])
+  },
+  userEmail(...arg) {
+    if (arg.length === 0) {
+      return localStorage.getItem('userEmail')
+    }
+    if (arg[0] === '') {
+      localStorage.removeItem('userEmail')
+      return
+    }
+    localStorage.setItem('userEmail', arg[0])
+  },
+  updateLoginAllInfo(response) {
+    // 更新本地accessToken
+    this.accessToken(response.accessToken)
+    this.accessTokenExpiresIn(response.accessTokenExpiresIn)
+    this.accessTokenUpdateAt(response.accessTokenUpdateAt)
+    this.refreshToken(response.refreshToken)
+    this.refreshTokenExpiresIn(response.refreshTokenExpiresIn)
+    this.refreshTokenUpdateAt(response.refreshTokenUpdateAt)
+    this.loginTimestamp(response.accessTokenUpdateAt)
+  },
+  hasLogin() {
+    const accessToken = this.accessToken()
+    if (!accessToken) {
+      return false
+    }
+
+    // 检测会员的refreshToken是否已经失效,如果已经失效,那么则清空登录信息,返回失败
+    const refreshToken = this.refreshToken()
+    const refreshTokenExpiresIn = this.refreshTokenExpiresIn()
+    const refreshTokenUpdateAt = this.refreshTokenUpdateAt()
+    if (refreshToken && refreshTokenExpiresIn && parseInt(refreshTokenExpiresIn) - 30 + parseInt(refreshTokenUpdateAt) > parseInt(tool.getTimestamp())) {
+      return true
+    } else {
+      this.clear()
+      // 跳转到登录页
+      if (router.currentRoute.path !== '/login') {
+        router.push('/login')
+      }
+      return false
+    }
+  },
+  daysDiff() {
+    return this.get('daysDiff')
+  },
+  get(...arg) {
+    let baseInfo = null
+    try {
+      baseInfo = JSON.parse(localStorage.getItem('baseInfo'))
+    } catch (err) {
+      return null
+    }
+    if (baseInfo === null) {
+      return null
+    }
+    if (arg.length === 0 || arg[0] === '') {
+      return baseInfo
+    } else {
+      return baseInfo[arg[0]]
+    }
+  }
+}
+
+export default usersInfo

+ 1 - 1
src/views/dashboard/admin/components/TransactionTable.vue

@@ -42,7 +42,7 @@ export default {
     }
   },
   created() {
-    this.fetchData()
+    // this.fetchData()
   },
   methods: {
     fetchData() {

+ 117 - 63
src/views/login/index.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="login-container">
-    <el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form" autocomplete="on" label-position="left">
+    <el-form ref="loginForm" :model="loginForm" class="login-form" autocomplete="on" label-position="left">
 
       <div class="title-container">
         <h3 class="title">
@@ -48,89 +48,76 @@
         </el-form-item>
       </el-tooltip>
 
-      <el-button :loading="loading" type="primary" style="width:100%;margin-bottom:30px;" @click.native.prevent="handleLogin">
+      <el-form-item label-width="0px" class="border-bottom key-name">
+        <span class="svg-container">
+          <i class="el-icon-camera-solid" />
+        </span>
+        <el-input
+          v-model="loginForm.verifyCode"
+          type="text"
+          auto-complete="off"
+          :placeholder="$t('login.verifyCode')"
+          @keyup.enter.native="handleLogin"
+        />
+      </el-form-item>
+      <el-image :src="captchaUrl" class="login-captcha" @click="changeCaptcha" />
+
+      <el-button :loading="loading" type="primary" style="width: 100%; margin-bottom: 30px;" @click.native.prevent="handleLogin">
         {{ $t('login.logIn') }}
       </el-button>
-
-      <div style="position:relative">
-        <div class="tips">
-          <span>{{ $t('login.username') }} : admin</span>
-          <span>{{ $t('login.password') }} : {{ $t('login.any') }}</span>
-        </div>
-        <div class="tips">
-          <span style="margin-right:18px;">
-            {{ $t('login.username') }} : editor
-          </span>
-          <span>{{ $t('login.password') }} : {{ $t('login.any') }}</span>
-        </div>
-
-        <el-button class="thirdparty-button" type="primary" @click="showDialog=true">
-          {{ $t('login.thirdparty') }}
-        </el-button>
-      </div>
     </el-form>
-
-    <el-dialog :title="$t('login.thirdparty')" :visible.sync="showDialog">
-      {{ $t('login.thirdpartyTips') }}
-      <br>
-      <br>
-      <br>
-      <social-sign />
-    </el-dialog>
   </div>
 </template>
 
 <script>
-import { validUsername } from '@/utils/validate'
 import LangSelect from '@/components/LangSelect'
 import SocialSign from './components/SocialSignin'
+import { daysDiff } from '@/api/site'
+import baseInfo from '@/utils/baseInfo'
+import usersInfo from '@/utils/usersInfo'
 
 export default {
   name: 'Login',
   components: { LangSelect, SocialSign },
   data() {
-    const validateUsername = (rule, value, callback) => {
-      if (!validUsername(value)) {
-        callback(new Error('Please enter the correct user name'))
-      } else {
-        callback()
-      }
-    }
-    const validatePassword = (rule, value, callback) => {
-      if (value.length < 6) {
-        callback(new Error('The password can not be less than 6 digits'))
-      } else {
-        callback()
-      }
-    }
     return {
       loginForm: {
-        username: 'admin',
-        password: '111111'
-      },
-      loginRules: {
-        username: [{ required: true, trigger: 'blur', validator: validateUsername }],
-        password: [{ required: true, trigger: 'blur', validator: validatePassword }]
+        username: '',
+        password: '',
+        verifyCode: ''
       },
+
       passwordType: 'password',
       capsTooltip: false,
       loading: false,
       showDialog: false,
       redirect: undefined,
-      otherQuery: {}
+      otherQuery: {},
+      pageId: '',
+      captchaUrl: ''
     }
   },
   watch: {
-    $route: {
-      handler: function(route) {
-        const query = route.query
-        if (query) {
-          this.redirect = query.redirect
-          this.otherQuery = this.getOtherQuery(query)
-        }
-      },
-      immediate: true
-    }
+    // $route: {
+    //   handler: function(route) {
+    //     const query = route.query
+    //     if (query) {
+    //       this.redirect = query.redirect
+    //       this.otherQuery = this.getOtherQuery(query)
+    //     }
+    //   },
+    //   immediate: true
+    // }
+  },
+  beforeCreate() {
+    this.$store.dispatch('settings/getPageData', {})
+      .then(response => {
+        this.pageId = response.data.pageId
+        this.captchaUrl = process.env.VUE_APP_BASE_API + '/v1/site/captcha?page_id=' + this.pageId + '&v=' + Math.random()
+      })
+      .catch((error) => {
+        console.log(error)
+      })
   },
   created() {
     // window.addEventListener('storage', this.afterQRScan)
@@ -161,16 +148,80 @@ export default {
       })
     },
     handleLogin() {
+      if (!this.loginForm.username) {
+        this.$message({
+          message: this.$t('login.username') + this.$t('common.canNotBeBlank'),
+          type: 'error'
+        })
+        return false
+      }
+      if (!this.loginForm.password) {
+        this.$message({
+          message: this.$t('login.password') + this.$t('common.canNotBeBlank'),
+          type: 'error'
+        })
+        return false
+      }
+      if (!this.loginForm.verifyCode) {
+        this.$message({
+          message: this.$t('login.verifyCode') + this.$t('common.canNotBeBlank'),
+          type: 'error'
+        })
+        return false
+      }
+
       this.$refs.loginForm.validate(valid => {
         if (valid) {
           this.loading = true
-          this.$store.dispatch('user/login', this.loginForm)
-            .then(() => {
-              this.$router.push({ path: this.redirect || '/', query: this.otherQuery })
+          const loginData = {
+            username: this.loginForm.username,
+            password: this.loginForm.password,
+            verifyCode: this.loginForm.verifyCode,
+            pageId: this.pageId
+          }
+
+          if (!baseInfo.daysDiff()) {
+            daysDiff().then(response => {
+              // 更新本地baseInfo
+              baseInfo.setDaysDiff(response.data.daysDiff)
+            }).catch(error => {
               this.loading = false
+              this.$message({
+                message: error,
+                type: 'error'
+              })
+              return false
             })
-            .catch(() => {
+          }
+
+          // 登录
+          this.$store.dispatch('user/login', loginData)
+            .then(() => {})
+            .then(() => {
+              return this.$store.dispatch('user/getUserInfo', {})
+            }).then(() => {
+              return this.$store.dispatch('user/getBaseInfo', {})
+            }).then(() => {
+              this.loading = false
+              this.$router.push({ path: this.redirect || '/', query: this.otherQuery })
+            }).catch(error => {
               this.loading = false
+              // 强制修改密码
+              if (error.message === 'ERROR_IS_MODIFY_PASSWORD') {
+                this.$router.push(`/modify-password/${this.loginForm.adminName}`)
+              } else {
+                // 清除登录数据
+                usersInfo.clear()
+                // 刷新验证码
+                this.changeCaptcha()
+                this.loading = false
+
+                this.$message({
+                  message: error,
+                  type: 'error'
+                })
+                return false
+              }
             })
         } else {
           console.log('error submit!!')
@@ -185,6 +236,9 @@ export default {
         }
         return acc
       }, {})
+    },
+    changeCaptcha() {
+      this.captchaUrl = process.env.VUE_APP_BASE_API + '/v1/site/captcha?page_id=' + this.pageId + '&v=' + Math.random()
     }
     // afterQRScan() {
     //   if (e.key === 'x-admin-oauth-code') {

+ 1 - 1
vue.config.js

@@ -38,7 +38,7 @@ module.exports = {
     },
     proxy: {
       [process.env.VUE_APP_BASE_API]:{
-        target:"http://local.ng.frontend.api.com",
+        target:"http://local.ng.backend.api.com",
         changeOrigin:true,
         pathRewrite:{
           ["^" + process.env.VUE_APP_BASE_API] : ""