第一次提交

master
caolc 3 years ago
parent 56635a948b
commit a31960fc1a
  1. 19
      .env.development
  2. 14
      .env.production
  3. 38
      .github/ISSUE_TEMPLATE/bug_report.md
  4. 20
      .github/ISSUE_TEMPLATE/feature_request.md
  5. 17
      docs/README.md
  6. 10
      docs/advance/skill.md
  7. 5
      docs/develop/README.md
  8. 161
      docs/develop/service.md
  9. 5
      docs/other/README.md
  10. 8
      docs/other/community.md
  11. 5
      docs/other/upgrade.md
  12. 5
      docs/start/README.md
  13. 22
      docs/start/faq.md
  14. 18
      docs/start/use.md
  15. 8
      package.json
  16. 52
      src/api/identity/customerLoginSystem.js
  17. 41
      src/api/identity/user.js
  18. BIN
      src/assets/img/logo.png
  19. 6
      src/bootstrap.js
  20. 8
      src/config/config.js
  21. 12
      src/config/default/setting.config.js
  22. 1
      src/layouts/CommonLayout.vue
  23. 8
      src/layouts/header/AdminHeader.vue
  24. 4
      src/layouts/header/HeaderAvatar.vue
  25. 12
      src/layouts/header/HeaderNotice.vue
  26. 2
      src/layouts/header/HeaderSearch.vue
  27. 8
      src/main.js
  28. 57
      src/pages/login/Login.vue
  29. 3
      src/plugins/authority-plugin.js
  30. 7
      src/router/config.js
  31. 13
      src/router/guards.js
  32. 2
      src/router/index.js
  33. 56
      src/store/getters.js
  34. 0
      src/store/global.js
  35. 6
      src/store/index.js
  36. 0
      src/store/modules/global.js
  37. 3
      src/store/modules/index.js
  38. 1
      src/store/modules/setting.js
  39. 114
      src/store/modules/user.js
  40. 64
      src/utils/auth.js
  41. 47
      src/utils/encode.js
  42. 309
      src/utils/request.js
  43. 5
      src/utils/routerUtil.js
  44. 35
      vue.config.js
  45. 19526
      yarn.lock

@ -1 +1,20 @@
VUE_APP_API_BASE_URL=http://dev.iczer.com
ENV = 'development'
# base api
# PRIVATE -加密 PUBLIC-不加密
VUE_APP_ENCRYPT = 'PUBLIC'
VUE_APP_BASE_API = '/api'
VUE_APP_BASE_LSM_API = '/lsm'cls
# 文件上传
VUE_APP_UPLOAD_URL = '/api/oil-oss/obejct/uploadFile'
VUE_APP_UPLOAD_PRIVATE_URL = '/api/oil-dict/imageHandler/uploadProtectedImg'
VUE_APP_DOMAIN = '127.0.0.1:38080'
# 开发环境,不会进行加密,会打印出数据1
VUE_APP_ENV = 'production'
#production
VUE_CLI_BABEL_TRANSPILE_MODULES = true

@ -0,0 +1,14 @@
ENV = 'production'
# base api
VUE_APP_ENCRYPT = 'PRIVATE'
VUE_APP_BASE_API = '/adminapi'
VUE_APP_BASE_LSM_API = '/lsm'
# 文件上传
VUE_APP_UPLOAD_URL = '/adminapi/oil-oss/obejct/uploadFile'
VUE_APP_UPLOAD_PRIVATE_URL = '/adminapi/oil-dict/imageHandler/uploadProtectedImg'
# 生产环境,进行加密
VUE_APP_ENV = 'production'
VUE_APP_DOMAIN ='www.xingoil.com/ws'

@ -1,38 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.

@ -1,20 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

@ -1,17 +0,0 @@
---
title: 首页
home: true
heroImage: /logo.png
heroText: Vue Antd Admin
tagline: 开箱即用的中台前端/设计解决方案
actionText: 快速上手 →
actionLink: /start/use
features:
- title: 简洁
details: 以 Markdown 为中心的项目结构,以最少的配置帮助你专注于写作。
- title: 优雅
details: 享受 Vue + webpack 的开发体验,在 Markdown 中使用 Vue 组件,同时可以使用 Vue 来开发自定义主题。
- title: 自然
details: VuePress 为每个页面预渲染生成静态的 HTML,同时在页面被加载的时候,将作为 SPA 运行。
footer: MIT Licensed | Copyright © 2018-present iczer
---

@ -1,10 +0,0 @@
---
title: 108个小技巧
lang: zn-CN
---
# 108个小技巧
## 自定义菜单icon
## 隐藏页面标题
## 关闭页签API
## 权限校验PI

@ -1,5 +0,0 @@
---
title: 开发
lang: zh-CN
---
# 开发

@ -1,161 +0,0 @@
---
title: 服务端交互
lang: zh-CN
---
# 服务端交互
数据服务是一个应用的灵魂,它驱动着应用的各个功能模块的正常运转。Vue Antd Admin 在 service 模块封装了服务端交互,通过 API 的形式可以和任何技术栈的服务端应用一起工作。
## 服务交互流程
在 Vue Antd Admin 中,服务端交互流程如下:
* 组件内调用 service 服务 API
* service 服务 API 封装请求数据,通过 request.js 发送请求
* 组件获取 service 返回的数据,更新视图数据或触发其它行为
我们以登录为例,Login.vue 组件内,用户输入账号密码,点击登录,调用 services/user/login api
```vue {5,17}
<template>
...
</template>
<script>
import {login} from '@/services/user'
...
export default {
name: 'Login',
methods: {
onSubmit (e) {
e.preventDefault()
this.form.validateFields((err) => {
if (!err) {
this.logging = true
const name = this.form.getFieldValue('name')
const password = this.form.getFieldValue('password')
login(name, password).then(res => this.afterLogin(res))
}
})
}
}
}
</script>
```
`services/user/login` 封装账户密码数据,通过 `request.js` 发送登录服务请求
```js
import {request, METHOD} from '@/utils/request'
/**
* 登录服务
* @param name 账户名
* @param password 账户密码
* @returns {Promise<AxiosResponse<T>>}
*/
async function login(name, password) {
return request(LOGIN, METHOD.POST, {
name: name,
password: password
})
}
```
Login.vue 获取登录服务返回的数据,进行后续操作
```vue {14,18-23}
<template>
...
</template>
<script>
import {login} from '@/services/user'
...
export default {
name: 'Login',
methods: {
onSubmit (e) {
this.form.validateFields((err) => {
if (!err) {
...
login(name, password).then(res => this.afterLogin(res))
}
})
},
afterLogin(res) {
if (res.data.code >= 0) { //登录成功
...
} else { //登录失败
this.error = loginRes.message
}
}
}
}
</script>
```
## 服务模块结构
服务模块结构如下:
```bash
...
├── src
│ └── services # 数据服务模块
│ ├── user.js # 用户数据服务
│ ├── product.js # 产品服务
│ ...
│ ├── api.js # api 地址管理
│ └── index.js # 服务模块导出
...
│ └── utils # 数据服务模块
│ ├── request.js # 基于 axios 的 http 请求工具
...
```
services 文件夹下, api.js 用于服务请求地址的统一管理,index.js 用于模块化导出服务,其它 *.js 文件对应各个服务模块。
## request.js
request.js 基于 axios 封装了一些常用的函数,如下:
```js
export {
METHOD, //http method 常量
AUTH_TYPE, //凭证认证类型 常量
request, //http请求函数
setAuthorization, //设置身份凭证函数
removeAuthorization, //移除身份凭证函数
checkAuthorization //检查身份凭证是否过期函数
}
```
:::tip
凭证认证类型默认为 [Bearer](https://www.jianshu.com/p/8f7009456abc),你可以根据自己的需要实现其它类型的认证
:::
## Base url 配置
你可以在项目根目录下的环境变量文件(.env 和 .env.development)中配置你的 API 服务 base url 地址。
生产环境,.env 文件
```properties
VUE_APP_API_BASE_URL=https://www.server.com
```
开发环境,.env.development 文件:
```properties
VUE_APP_API_BASE_URL=https://localhost:8000
```
## 跨域设置
在开发环境中,通常我们的Vue应用和服务应用运行在不同的地址或端口上。我们可以通过简单的设置,代理前端请求,来避免跨域问题。如下:
首先,在 services/api.js 文件中设置 API_PROXY_PREFIX 常量,BASE_URL 像下面这样设置:
```js {2,4}
//跨域代理前缀
const API_PROXY_PREFIX='/api'
//base url
const BASE_URL = process.env.NODE_ENV === 'production' ? process.env.VUE_APP_API_BASE_URL : API_PROXY_PREFIX
//导出api服务地址
module.exports = {
LOGIN: `${BASE_URL}/login`,
ROUTES: `${BASE_URL}/routes`
}
```
然后,在 vue.config.js 文件中配置代理:
```js
model.exports = {
devServer: {
proxy: {
'/api': { //此处要与 /services/api.js 中的 API_PROXY_PREFIX 值保持一致
target: process.env.VUE_APP_API_BASE_URL,
changeOrigin: true,
pathRewrite: {
'^/api': ''
}
}
}
}
}
```
:::tip
此代理配置仅适用于开发环境,生产环境的跨域代理请在自己的web服务器配置。
:::

@ -1,5 +0,0 @@
---
title: 其它
lang: zh-CN
---
# 其它

@ -1,8 +0,0 @@
---
title: 社区
lang: zh-CN
---
# 社区
## 交流学习
### QQ群:812277510、610090280(已满)

@ -1,5 +0,0 @@
---
title: 更新日志
lang: zh-CN
---
# 更新日志

@ -1,5 +0,0 @@
---
title: 开始
lang: zh-CN
---
## 开始

@ -1,22 +0,0 @@
---
title: 常见问题
lang: zh-CN
---
# 常见问题
### 为什么不是 Ant Design Pro Vue ?
[Ant Design Pro Vue](https://github.com/vueComponent/ant-design-vue-pro) 是 [Ant Design Pro](https://github.com/ant-design/ant-design-pro) 的 Vue 版本,其中项目结构、组件、
布局和使用方法等基本与 Ant Design Pro 的 react 版本保持一致。如果你比较熟悉 react 版,或者你已经在使用它,这确实是一个不错的选择。
[Vue Antd Admin](https://github.com/iczer/vue-antd-admin) 同样实现了 Ant Design Pro 的所有功能。与此同时,我们还根据 Vue 的特性,对 Ant Design Pro 的一些组件和布局作出了相应的修改及优化,同时不影响保持与 Ant Design Pro 的一致。
另外,我们还在添加一些 Ant Design Pro 没有的功能,比如全局动画、多页签模式等。
如果你想使用 Ant Design Pro,但又觉得它缺乏一些你想要的功能,不妨看看 [Vue Antd Admin](https://github.com/iczer/vue-antd-admin),我们会认真考虑每个用户的需求。
因此,如果你有一些不错的想法和建议,欢迎随时和我们交流,很可能你的想法就在我们下一个版本中实现。
### 如何使用 Vue Antd Admin ?
请阅读文档 [开始使用](./use.md)。有任何疑问,欢迎在 github 上给我们提交 [issue](https://github.com/iczer/vue-antd-admin/issues/new)。
### 是否支持国际化 ?
Vue Antd Admin 引入了 vue-i18n 支持。因此你可以使用 vue-i18n 的特性对项目做国际化修改,详细请查看 [国际化](../advance/i18n.md)

@ -2,13 +2,6 @@
title: 使用
lang: zh-CN
---
# 使用
## 准备
你的本地环境需要安装 yarn、node 和 git。我们的技术栈基于 ES2015+、Vue、Antd,提前学习这些知识会非常有帮助。
## 安装
克隆本项目到本地
```bash
$ git clone https://github.com/iczer/vue-antd-admin.git
```
安装依赖
```bash
@ -16,17 +9,7 @@ $ yarn install
or
$ npm install
```
:::tip
master 分支是 Vue Antd Admin 的标准版代码,此分支代码适合用于用于学习研究,不推荐在此分支做正式开发。
我们在 basic 分支提供了 Vue Antd Admin 的基础版代码,正式开发请切换至此分支,以便于后续的版本更新。
:::
:::warning
如果基于 `master分支` 进行开发,在版本更新时遇到的代码冲突问题请自行解决,我们不对基于 `master分支` 开发时遇到的问题提供技术支持。
再次强调,`master分支` 仅推荐用于学习参考,正式开发请切换至 `basic` 分支!!!
:::
## 目录结构
我们已经为你生成了一个完整的开发框架,提供了涵盖中后台开发的各类功能和坑位,下面是整个项目的目录结构。
```bash
├── docs # 使用文档
├── public
@ -59,4 +42,3 @@ $ yarn serve
or
$ npm run serve
```
启动成功后,会看到一个本地预览地址,通常是 http://localhost:8080 。接下来就可以修改代码,并实时预览修改结果啦!

@ -4,7 +4,7 @@
"homepage": "https://iczer.github.io/vue-antd-admin",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"dev": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint",
"predeploy": "yarn build",
@ -18,12 +18,15 @@
"animate.css": "^4.1.0",
"ant-design-vue": "1.7.2",
"axios": "^0.19.2",
"bcryptjs": "^2.4.3",
"clipboard": "^2.0.6",
"core-js": "^3.6.5",
"crypto-js": "^4.1.1",
"date-fns": "^2.14.0",
"enquire.js": "^2.1.6",
"highlight.js": "^10.2.1",
"js-cookie": "^2.2.1",
"js-md5": "^0.7.3",
"mockjs": "^1.1.0",
"nprogress": "^0.2.0",
"viser-vue": "^2.4.8",
@ -31,7 +34,8 @@
"vue-i18n": "^8.18.2",
"vue-router": "^3.3.4",
"vuedraggable": "^2.23.2",
"vuex": "^3.4.0"
"vuex": "^3.4.0",
"vuex-persistedstate": "^4.1.0"
},
"devDependencies": {
"@ant-design/colors": "^4.0.1",

@ -0,0 +1,52 @@
import request from '@/utils/request'
var service_name = 'oil-identity'
var group_name = 'customerLoginSystem'
export default {
getByPage(page) { // 分页查询
return request({
url: `/${service_name}/${group_name}/getByPage`,
method: 'post',
data: page
})
},
save(oilDicMark) { // 保存
return request({
url: `/${service_name}/${group_name}/save`,
method: 'post',
data: oilDicMark
})
},
get(id) { // 根据id查询
return request({
url: `/${service_name}/${group_name}/get/${id}`,
method: 'get'
})
},
getUserLoginSystem(customerId) { // 所有系统以及用户登录权限
return request({
url: `/${service_name}/${group_name}/getUserLoginSystem/${customerId}`,
method: 'get'
})
},
toggleAuth(oilDicMark) { // 修改登录授权状态
return request({
url: `/${service_name}/${group_name}/toggleAuth`,
method: 'put',
data: oilDicMark
})
},
update(oilDicMark) { // 更新
return request({
url: `/${service_name}/${group_name}/update`,
method: 'put',
data: oilDicMark
})
},
deleteById(id) { // 根据id删除
return request({
url: `/${service_name}/${group_name}/delete`,
method: 'put',
data: { id: id }
})
}
}

@ -0,0 +1,41 @@
import request from '@/utils/request'
export function login(data) {
return request({
url: '/oil-identity/operationUser/loginPwd',
method: 'post',
data
})
}
export function sendManagerLoginSms(phone) {
return request({
url: '/oil-identity/operationUser/sendManagerLoginSms',
method: 'post',
data: {
phone
}
})
}
export function loginSms(data) {
return request({
url: '/oil-identity/operationUser/loginSms',
method: 'post',
data
})
}
export function getInfo() {
return request({
url: '/oil-identity/unionAuth/info',
method: 'get'
})
}
export function logout() {
return request({
url: '/oil-identity/authorization/logout',
method: 'get'
})
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

6
src/bootstrap.js vendored

@ -1,7 +1,7 @@
import {loadRoutes, loadGuards, setAppOptions} from '@/utils/routerUtil'
import {loadInterceptors} from '@/utils/request'
// import {loadInterceptors} from '@/utils/request'
import guards from '@/router/guards'
import interceptors from '@/utils/axios-interceptors'
// import interceptors from '@/utils/axios-interceptors'
/**
* 启动引导方法
@ -15,7 +15,7 @@ function bootstrap({router, store, i18n, message}) {
// 设置应用配置
setAppOptions({router, store, i18n})
// 加载 axios 拦截器
loadInterceptors(interceptors, {router, store, i18n, message})
// loadInterceptors(interceptors, {router, store, i18n, message})
// 加载路由
loadRoutes()
// 加载路由守卫

@ -1,12 +1,16 @@
// 自定义配置,参考 ./default/setting.config.js,需要自定义的属性在这里配置即可
module.exports = {
theme: {
color: '#13c2c2',
color: '#1890ff',
mode: 'dark',
success: '#52c41a',
warning: '#faad14',
error: '#f5222f'
},
multiPage: true,
animate: {
name: 'lightSpeed',
name: 'fade',
direction: 'left'
}
}

@ -17,8 +17,8 @@ module.exports = {
multiPage: false, //多页签模式,true:开启,false:不开启
cachePage: true, //是否缓存页面数据,仅多页签模式下生效,true 缓存, false 不缓存
hideSetting: false, //隐藏设置抽屉,true:隐藏,false:不隐藏
systemName: 'Vue Antd Admin', //系统名称
copyright: '2018 ICZER 工作室出品', //copyright
systemName: '星油能源', //系统名称
copyright: '星油能源工作室', //copyright
asyncRoutes: false, //异步加载路由,true:开启,false:不开启
showPageTitle: true, //是否显示页面标题(PageLayout 布局中的页面标题),true:显示,false:不显示
filterMenu: true, //根据权限过滤菜单,true:过滤,false:不过滤
@ -27,9 +27,9 @@ module.exports = {
name: 'bounce', //动画效果,支持的动画效果可参考 ./animate.config.js
direction: 'left' //动画方向,切换页面时动画的方向,参考 ./animate.config.js
},
footerLinks: [ //页面底部链接,{link: '链接地址', name: '名称/显示文字', icon: '图标,支持 ant design vue 图标库'}
{link: 'https://pro.ant.design', name: 'Pro首页'},
{link: 'https://github.com/iczer/vue-antd-admin', icon: 'github'},
{link: 'https://ant.design', name: 'Ant Design'}
footerLinks: [ //页面底部链接,{link: '链接地址', name: '名称/显示文字', icon: '图标,支持 ant design vue 图标库'} 'https://pro.ant.design'
{link: 'http://www.xingoil.com/', name: '星油官网管理平台'},
// {link: 'https://github.com/iczer/vue-antd-admin', icon: 'github'},
// {link: 'https://ant.design', name: 'Ant Design'}
],
}

@ -34,7 +34,6 @@ export default {
padding: 32px 0;
flex: 1;
@media (min-width: 768px){
padding: 112px 0 24px;
}
}

@ -12,11 +12,11 @@
</div>
<div :class="['admin-header-right', headerTheme]">
<header-search class="header-item" @active="val => searchActive = val" />
<a-tooltip class="header-item" title="帮助文档" placement="bottom" >
<!-- <a-tooltip class="header-item" title="帮助文档" placement="bottom" >
<a href="https://iczer.gitee.io/vue-antd-admin-docs/" target="_blank">
<a-icon type="question-circle-o" />
</a>
</a-tooltip>
</a-tooltip> -->
<header-notice class="header-item"/>
<header-avatar class="header-item"/>
<a-dropdown class="lang header-item">
@ -47,8 +47,8 @@ export default {
return {
langList: [
{key: 'CN', name: '简体中文', alias: '简体'},
{key: 'HK', name: '繁體中文', alias: '繁體'},
{key: 'US', name: 'English', alias: 'English'}
// {key: 'HK', name: '', alias: ''},
// {key: 'US', name: 'English', alias: 'English'}
],
searchActive: false
}

@ -24,7 +24,7 @@
<script>
import {mapGetters} from 'vuex'
import {logout} from '@/services/user'
import {removeToken} from '@/utils/auth'
export default {
name: 'HeaderAvatar',
@ -33,7 +33,7 @@ export default {
},
methods: {
logout() {
logout()
removeToken()
this.$router.push('/login')
}
}

@ -5,18 +5,18 @@
<a-tabs class="dropdown-tabs" :tabBarStyle="{textAlign: 'center'}" :style="{width: '297px'}">
<a-tab-pane tab="通知" key="1">
<a-list class="tab-pane">
<a-list-item>
<!-- <a-list-item>
<a-list-item-meta title="你收到了 14 份新周报" description="一年前">
<a-avatar style="background-color: white" slot="avatar" src="https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png"/>
</a-list-item-meta>
</a-list-item>
<a-list-item>
</a-list-item> -->
<!-- <a-list-item>
<a-list-item-meta title="你推荐的 曲妮妮 已通过第三轮面试" description="一年前">
<a-avatar style="background-color: white" slot="avatar" src="https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png"/>
</a-list-item-meta>
</a-list-item>
</a-list-item> -->
<a-list-item>
<a-list-item-meta title="这种模板可以区分多种通知类型" description="一年前">
<a-list-item-meta title="欢迎使用" description="1秒前">
<a-avatar style="background-color: white" slot="avatar" src="https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png"/>
</a-list-item-meta>
</a-list-item>
@ -32,7 +32,7 @@
</a-spin>
</div>
<span @click="fetchNotice" class="header-notice">
<a-badge class="notice-badge" count="12">
<a-badge class="notice-badge" count="1">
<a-icon :class="['header-notice-icon']" type="bell" />
</a-badge>
</span>

@ -18,7 +18,7 @@ export default {
name: 'HeaderSearch',
data () {
return {
dataSource: ['选项一', '选项二'],
dataSource: [],
searchMode: false
}
},

@ -2,8 +2,8 @@ import Vue from 'vue'
import App from './App.vue'
import {initRouter} from './router'
import './theme/index.less'
import Antd from 'ant-design-vue'
import Viser from 'viser-vue'
import Antd from 'ant-design-vue' //组件库
import Viser from 'viser-vue' //可视化数据插件
import '@/mock'
import store from './store'
import 'animate.css/source/animate.css'
@ -11,7 +11,7 @@ import Plugins from '@/plugins'
import {initI18n} from '@/utils/i18n'
import bootstrap from '@/bootstrap'
import 'moment/locale/zh-cn'
//初始化路由
const router = initRouter(store.state.setting.asyncRoutes)
const i18n = initI18n('CN', 'US')
@ -19,7 +19,7 @@ Vue.use(Antd)
Vue.config.productionTip = false
Vue.use(Viser)
Vue.use(Plugins)
//程序初始化
bootstrap({router, store, i18n, message: Vue.prototype.$message})
new Vue({

@ -5,7 +5,7 @@
<img alt="logo" class="logo" src="@/assets/img/logo.png" />
<span class="title">{{systemName}}</span>
</div>
<div class="desc">Ant Design 是西湖区最具影响力的 Web 设计规范</div>
<div class="desc">星油官网后台管理网站</div>
</div>
<div class="login">
<a-form @submit="onSubmit" :form="form">
@ -16,7 +16,7 @@
<a-input
autocomplete="autocomplete"
size="large"
placeholder="admin"
placeholder="请输入账号手机号"
v-decorator="['name', {rules: [{ required: true, message: '请输入账户名', whitespace: true}]}]"
>
<a-icon slot="prefix" type="user" />
@ -25,7 +25,7 @@
<a-form-item>
<a-input
size="large"
placeholder="888888"
placeholder="请输入密码"
autocomplete="autocomplete"
type="password"
v-decorator="['password', {rules: [{ required: true, message: '请输入密码', whitespace: true}]}]"
@ -34,7 +34,7 @@
</a-input>
</a-form-item>
</a-tab-pane>
<a-tab-pane tab="手机号登录" key="2">
<!-- <a-tab-pane tab="手机号登录" key="2">
<a-form-item>
<a-input size="large" placeholder="mobile number" >
<a-icon slot="prefix" type="mobile" />
@ -52,7 +52,7 @@
</a-col>
</a-row>
</a-form-item>
</a-tab-pane>
</a-tab-pane> -->
</a-tabs>
<div>
<a-checkbox :checked="true" >自动登录</a-checkbox>
@ -61,13 +61,13 @@
<a-form-item>
<a-button :loading="logging" style="width: 100%;margin-top: 24px" size="large" htmlType="submit" type="primary">登录</a-button>
</a-form-item>
<div>
<!-- <div>
其他登录方式
<a-icon class="icon" type="alipay-circle" />
<a-icon class="icon" type="taobao-circle" />
<a-icon class="icon" type="weibo-circle" />
<router-link style="float: right" to="/dashboard/workplace" >注册账户</router-link>
</div>
</div> -->
</a-form>
</div>
</common-layout>
@ -75,8 +75,8 @@
<script>
import CommonLayout from '@/layouts/CommonLayout'
import {login, getRoutesConfig} from '@/services/user'
import {setAuthorization} from '@/utils/request'
import {getRoutesConfig} from '@/services/user'
// import {setAuthorization} from '@/utils/request'
import {loadRoutes} from '@/utils/routerUtil'
import {mapMutations} from 'vuex'
@ -99,34 +99,35 @@ export default {
...mapMutations('account', ['setUser', 'setPermissions', 'setRoles']),
onSubmit (e) {
e.preventDefault()
this.form.validateFields((err) => {
this.form.validateFields( (err) => {
if (!err) {
this.logging = true
const name = this.form.getFieldValue('name')
const password = this.form.getFieldValue('password')
login(name, password).then(this.afterLogin)
const username = this.form.getFieldValue('name')
const password = this.form.getFieldValue('password');
this.$store.dispatch('user/login', {username,password}).then((result) => {(result.code==20000)&&this.$router.push('/dashboard/workplace');})
}
})
},
afterLogin(res) {
this.logging = false
const loginRes = res.data
if (loginRes.code >= 0) {
const {user, permissions, roles} = loginRes.data
this.setUser(user)
this.setPermissions(permissions)
this.setRoles(roles)
setAuthorization({token: loginRes.data.token, expireAt: new Date(loginRes.data.expireAt)})
//
getRoutesConfig().then(result => {
afterLogin() {
// this.logging = false
// const loginRes = res.data
getRoutesConfig().then(result => {
const routesConfig = result.data.data
loadRoutes(routesConfig)
this.$router.push('/dashboard/workplace')
this.$message.success(loginRes.message, 3)
// this.$message.success(loginRes.message, 3)
})
} else {
this.error = loginRes.message
}
// if (loginRes.code >= 0) {
// // const {user, permissions, roles} = loginRes.data
// // this.setUser(user)
// // this.setPermissions(permissions)
// // this.setRoles(roles)
// // setAuthorization({token: loginRes.data.token, expireAt: new Date(loginRes.data.expireAt)})
// //
// } else {
// this.error = loginRes.message
// }
}
}
}

@ -134,7 +134,7 @@ const AuthorityPlugin = {
this[`$${check}Failure`] = onFailure
return this[`$${check}Failure`](check)
} else {
this.$message.error(`对不起,您没有操作权限:${check}`)
this.$message.error(`对不起,您没有操作权限:${check},请联系管理员`)
}
return 0
}
@ -159,6 +159,7 @@ const AuthorityPlugin = {
const role = getRouteRole(roles, this.$route)
return auth.apply(this, [{check, type}, permission, role, permissions, roles])
}
}
})
}

@ -25,6 +25,9 @@ const options = {
name: '首页',
component: TabsView,
redirect: '/login',
meta:{
exhibition:true
},
children: [
{
path: 'dashboard',
@ -90,9 +93,7 @@ const options = {
{
path: 'query',
name: '查询表格',
meta: {
authority: 'queryForm',
},
meta: {},
component: () => import('@/pages/list/QueryList'),
},
{

@ -1,8 +1,9 @@
import {hasAuthority} from '@/utils/authority-utils'
import {loginIgnore} from '@/router/index'
import {checkAuthorization} from '@/utils/request'
import NProgress from 'nprogress'
import { getToken } from '@/utils/auth'
import { message } from 'ant-design-vue';
// import {loginIgnore} from '@/router/index'
import NProgress from 'nprogress' //进度条组件
NProgress.configure({ showSpinner: false })
/**
@ -26,9 +27,9 @@ const progressStart = (to, from, next) => {
* @param next
* @param options
*/
const loginGuard = (to, from, next, options) => {
const {message} = options
if (!loginIgnore.includes(to) && !checkAuthorization()) {
const loginGuard = (to, from, next,) => {
let TOKEN = getToken()
if (!TOKEN&&to.path!=='/login') {
message.warning('登录已失效,请重新登录')
next({path: '/login'})
} else {

@ -24,7 +24,7 @@ const loginIgnore = {
* @returns {VueRouter}
*/
function initRouter(isAsync) {
const options = isAsync ? require('./async/config.async').default : require('./config').default
const options = isAsync ? require('./async/config.async').default : require('./config').default;
formatRoutes(options.routes)
return new Router(options)
}

@ -0,0 +1,56 @@
import Cookies from 'js-cookie'
const getters = {
sidebar: state => state.app.sidebar,
size: state => state.app.size,
markData: state => state.global.markData,
device: state => state.app.device,
visitedViews: state => state.tagsView.visitedViews,
cachedViews: state => state.tagsView.cachedViews,
token: state => state.user.token,
avatar: state => state.user.avatar,
name: state => state.user.name,
introduction: state => state.user.introduction,
role: state => state.user.role,
auths: state => state.user.auths,
permission_routes: state => state.permission.routes,
sysUserList: state => state.user.sysUserList,
user: state => state.user.user,
areaTree: state => state.global.areaTree,
networkList: state => state.global.networkList,
departmentList: state => state.global.departmentList,
fleetList: state => state.global.fleetList,
waybillId: state => state.global.waybillId,
plateNumber: state => state.global.plateNumber,
bannerInfoList: state => state.global.bannerInfoList,
trackCss: state => state.global.trackCss,
Link: state => state.global.Link,
helpInfo: state => state.global.helpInfo,
drawerFixed: state => state.global.drawerFixed,
vehicleTypeList: state => state.global.vehicleTypeList,
largeAreaList: state => state.global.largeAreaList,
incomeBankList: state => state.global.incomeBankList,
currentIncomeBank: state => {
let incomeBank = state.global.currentIncomeBank
if (!incomeBank.incomeBankId) {
incomeBank = Cookies.get('currentIncome')
if (incomeBank) {
incomeBank = JSON.parse(incomeBank)
}
}
return incomeBank
},
autoCheckDocument: state => state.status.autoCheckDocument,
uploadToken: state => {
const token = state.global.uploadToken.pop()
return token
},
tokenEncrypt: state => {
const token = state.global.tokenEncrypt.pop()
return token
}
}
export default getters

@ -1,8 +1,10 @@
import Vue from 'vue'
import Vuex from 'vuex'
import modules from './modules'
import getters from './getters'
Vue.use(Vuex)
const store = new Vuex.Store({modules})
console.log(modules,'11')
const store = new Vuex.Store({modules,getters})
console.log(store,'store')
export default store

@ -1,4 +1,5 @@
import account from './account'
import setting from './setting'
import user from './user'
export default {account, setting}
export default {account, setting,user}

@ -32,6 +32,7 @@ export default {
},
firstMenu(state, getters) {
const {menuData} = getters
console.log(menuData)
if (menuData.length > 0 && !menuData[0].fullPath) {
formatFullPath(menuData)
}

@ -0,0 +1,114 @@
import { login, loginSms, logout, getInfo } from '@/api/identity/user'
import { getToken, setToken, removeToken, setUsername, setPassword, setChecked, removeChecked, removeUsername, removePassword } from '@/utils/auth'
import md5 from 'js-md5'
import store from "@/store";
const state = {
token: getToken(),
role: null, // 角色
auths: [], // 权限
user: {}// 存储用户信息
}
const mutations = {
SET_TOKEN: (state, token) => {
state.token = token
},
SET_ROLE: (state, role) => {
state.role = role
},
SET_AUTHS: (state, auths) => {
state.auths = auths
},
SET_USER: (state, user) => {
state.user = user
}
}
const actions = {
login({ commit }, userInfo) {
const { username, password, checked, verifyCode } = userInfo
return new Promise((resolve, reject) => {
login({ username: username.trim(), password: md5(password), verifyCode }).then(res => {
// 存到vuex
commit('SET_TOKEN', res.data.accessToken)
// 存到cookie
setToken(res.data.accessToken)
// 将账号密码和公司存入cookie中
if (checked) {
setChecked(checked)
setUsername(username.trim())
setPassword(md5(password))
}
resolve(res)
}).catch(error => {
reject(error)
})
})
},
loginSms({ commit }, userInfo) {
console.log('666666')
return new Promise((resolve, reject) => {
loginSms(userInfo).then(res => {
// 存到vuex
commit('SET_TOKEN', res.data.accessToken)
// 存到cookie
setToken(res.data.accessToken)
resolve()
}).catch(error => {
reject(error)
})
})
},
getInfo({ commit }) {
return new Promise((resolve, reject) => {
getInfo().then( response => {
const data = response.data
store.dispatch('user/createWebsocket',data.id)
commit('SET_ROLE', data.role)
commit('SET_AUTHS', data.authList)
commit('SET_USER', data)
resolve(data)
}).catch(error => {
reject(error)
})
})
},
// user logout
logout({ commit }) {
return new Promise((resolve, reject) => {
logout().then(() => {
commit('SET_TOKEN', '')
commit('SET_ROLE', null)
commit('SET_AUTHS', [])
removeToken()
removeChecked()
removeUsername()
removePassword()
// websocket.closeWebSocket()
resolve()
}).catch(error => {
reject(error)
})
})
},
resetToken({ commit }) {
return new Promise(resolve => {
commit('SET_TOKEN', '')
commit('SET_ROLE', null)
commit('SET_AUTHS', [])
commit('SET_USER', {})
removeToken()
resolve()
})
}
}
export default {
namespaced: true,
state,
mutations,
actions
}

@ -0,0 +1,64 @@
import Cookies from 'js-cookie'
const TokenKey = 'Authorization'
const NetWorkId = 'networkId'
const UserName = 'username'
const PassWord = 'password'
const Checked = 'checked'
export function getToken() {
return Cookies.get(TokenKey)
}
export function setToken(token) {
return Cookies.set(TokenKey, token, { expires: 1 })
}
export function removeToken() {
return Cookies.remove(TokenKey)
}
export function setNet(networkId) {
return Cookies.set(NetWorkId, networkId, { expires: 7 })
}
export function setUsername(username) {
return Cookies.set(UserName, username, { expires: 7 })
}
export function setPassword(password) {
return Cookies.set(PassWord, password, { expires: 7 })
}
export function getNet() {
return Cookies.get(NetWorkId)
}
export function getUsername() {
return Cookies.get(UserName)
}
export function getPassword() {
return Cookies.get(PassWord)
}
export function setChecked(checked) {
return Cookies.set(Checked, checked, { expires: 7 })
}
export function getChecked() {
return Cookies.get(Checked)
}
export function removeNet() {
return Cookies.remove(NetWorkId)
}
export function removeChecked() {
return Cookies.remove(Checked)
}
export function removeUsername() {
return Cookies.remove(UserName)
}
export function removePassword() {
return Cookies.remove(PassWord)
}

@ -0,0 +1,47 @@
import CryptoJS from 'crypto-js'
import bcrypt from 'bcryptjs'
import md5 from 'js-md5'
var keyStr = 'qDfajQ*v@W1mCruZ'
export default {
/**
* @param {*需要加密的字符串 对象转化为json字符串再加密} word
* @param {*aes加密需要的key值这个key值后端同学会告诉你} keyStr
*/
encrypt(word) { // 加密
var key = CryptoJS.enc.Utf8.parse(keyStr)
var srcs = CryptoJS.enc.Utf8.parse(word)
var encrypted = CryptoJS.AES.encrypt(srcs, key, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 }) // 加密模式为ECB,补码方式为PKCS5Padding(也就是PKCS7)
return encrypted.toString()
},
decrypt(word) { // 解密
var key = CryptoJS.enc.Utf8.parse(keyStr)
var decrypt = CryptoJS.AES.decrypt(word, key, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 })
return CryptoJS.enc.Utf8.stringify(decrypt).toString()
},
md5Salt(str) { // md5盐加密
return md5(str + 'kdq*&qflbn1gga?aDq')
},
md5NoSalt(str) {
return md5(str)
},
uuid() { // 获取uuid
var s = []
var hexDigits = '0123456789abcdef'
for (var i = 0; i < 36; i++) {
s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1)
}
s[14] = '4'
s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1)
s[8] = s[13] = s[18] = s[23] = '-'
var uuid = s.join('')
return uuid
},
bcrypt(str) {
var salt = bcrypt.genSaltSync(10) // 定义密码加密的计算强度,默认10
var hash = bcrypt.hashSync(this.md5Salt(str), salt) // 把要加密的内容带进去,变量hash就是加密后的密码
return hash
}
}

@ -1,168 +1,167 @@
import axios from 'axios'
import Cookie from 'js-cookie'
import utils from '@/utils/encode'
import store from '@/store'
import { getToken } from '@/utils/auth'
import { message } from 'ant-design-vue';
const CancelToken = axios.CancelToken;
// 跨域认证信息 header 名
const xsrfHeaderName = 'Authorization'
const timeout = 20000
const WhiteApiErr = ['userSavePermissionByPhone','batchEnableDisable','batchPriceEnableDisable','getPushOrderInfo']
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API,
timeout: timeout // 默认请求超时时间
})
const timeoutWhite = ['/oil-finance/oilOrderInfo/importExcelOrder']
axios.defaults.timeout = 5000
axios.defaults.withCredentials= true
axios.defaults.xsrfHeaderName= xsrfHeaderName
axios.defaults.xsrfCookieName= xsrfHeaderName
// 认证类型
const AUTH_TYPE = {
BEARER: 'Bearer',
BASIC: 'basic',
AUTH1: 'auth1',
AUTH2: 'auth2',
}
// http method
const METHOD = {
GET: 'get',
POST: 'post'
}
/**
* axios请求
* @param url 请求地址
* @param method {METHOD} http method
* @param params 请求参数
* @returns {Promise<AxiosResponse<T>>}
*/
async function request(url, method, params, config) {
switch (method) {
case METHOD.GET:
return axios.get(url, {params, ...config})
case METHOD.POST:
return axios.post(url, params, config)
default:
return axios.get(url, {params, ...config})
}
}
/**
* 设置认证信息
* @param auth {Object}
* @param authType {AUTH_TYPE} 认证类型默认{AUTH_TYPE.BEARER}
*/
function setAuthorization(auth, authType = AUTH_TYPE.BEARER) {
switch (authType) {
case AUTH_TYPE.BEARER:
Cookie.set(xsrfHeaderName, 'Bearer ' + auth.token, {expires: auth.expireAt})
break
case AUTH_TYPE.BASIC:
case AUTH_TYPE.AUTH1:
case AUTH_TYPE.AUTH2:
default:
break
}
}
/**
* 移出认证信息
* @param authType {AUTH_TYPE} 认证类型
*/
function removeAuthorization(authType = AUTH_TYPE.BEARER) {
switch (authType) {
case AUTH_TYPE.BEARER:
Cookie.remove(xsrfHeaderName)
break
case AUTH_TYPE.BASIC:
case AUTH_TYPE.AUTH1:
case AUTH_TYPE.AUTH2:
default:
break
}
}
// 加密白名单
const encryptWhite = ['/oil-finance/oilOrderInfo/importExcelOrder']
let a = []
// request 拦截器
service.interceptors.request.use(
config => {
let source = CancelToken.source();
config.cancelToken = source.token;
if(a.includes(config.url)){
source.cancel(`tooRapid`);
return config
}
a.push(config.url)
if ( timeoutWhite.includes(config.url)) {
config.timeout = 0
} else {
config.timeout = timeout
}
const notEncrypt = config.notEncrypt
config.headers['dataSources'] = 'WEB'
if (store.getters.token) {
config.headers['Authorization'] = getToken()
}
const JSESSIONID = utils.uuid()
config.headers['JSESSIONID'] = JSESSIONID
config.headers['token'] = utils.bcrypt(JSESSIONID)
const env = process.env.VUE_APP_ENV
if (env === 'test') {
console.log('这里是测试')
// 测试环境,加密,打印参数
// 设置jsessionid和token
if (!notEncrypt && encryptWhite.indexOf(config.url) < 0) {
const data = { // 用于存储加密
params: '' // 加密后的密文
}
// 要加密
console.log('请求路径', config.url, '参数加密前', config.data)
data.params = utils.encrypt(JSON.stringify(config.data))
config.data = data
}
}
if (env === 'development') {
console.log('这里是测试')
// 测试环境,不加密,打印参数
console.log('请求路径', config.url, '参数加密前', config.data)
}
/**
* 检查认证信息
* @param authType
* @returns {boolean}
*/
function checkAuthorization(authType = AUTH_TYPE.BEARER) {
switch (authType) {
case AUTH_TYPE.BEARER:
if (Cookie.get(xsrfHeaderName)) {
return true
if (env === 'production') {
console.log('这里是生产')
// 生产环境,加密,不输出任何东西
// 设置jsessionid和token
const JSESSIONID = utils.uuid()
config.headers['JSESSIONID'] = JSESSIONID
config.headers['token'] = utils.bcrypt(JSESSIONID)
if (!notEncrypt && encryptWhite.indexOf(config.url) < 0) {
const data = { // 用于存储加密
params: '' // 加密后的密文
}
// 要加密
data.params = utils.encrypt(JSON.stringify(config.data))
config.data = data
}
break
case AUTH_TYPE.BASIC:
case AUTH_TYPE.AUTH1:
case AUTH_TYPE.AUTH2:
default:
break
}
return config
},
error => {
console.log(error.message,'req')
return Promise.reject(error)
}
return false
}
)
/**
* 加载 axios 拦截器
* @param interceptors
* @param options
*/
function loadInterceptors(interceptors, options) {
const {request, response} = interceptors
// 加载请求拦截器
request.forEach(item => {
let {onFulfilled, onRejected} = item
if (!onFulfilled || typeof onFulfilled !== 'function') {
onFulfilled = config => config
// response 拦截器
service.interceptors.response.use(
response => {
let urlIndex = a.indexOf(response.config.url)
if(urlIndex!==-1){
a.splice(urlIndex,1);
console.log(a,'防抖移除')
}
if (!onRejected || typeof onRejected !== 'function') {
onRejected = error => Promise.reject(error)
console.log(a,response.config.url,'闭包')
// 这里设置loading
const res = response.data
const env = process.env.VUE_APP_ENV
if (response.headers['content-type'].indexOf('application/json') !== -1) {
if (env === 'test') {
// 测试环境,进行加密解密,打印路径和数据
if (res.encrypt === 1) {
// 加密的数据,需要解密
console.log('请求路径', response.config.url, '返回结果解密前', res)
const dataParam = JSON.parse(utils.decrypt(res.data))
res.data = JSON.stringify(dataParam) === '{}' ? null : dataParam
console.log('请求路径', response.config.url, '返回结果解密后', res)
console.log('-------------------------------------------')
} else {
console.log('请求路径', response.config.url, '返回结果未加密', res)
console.log('-------------------------------------------')
}
}
if (env === 'production') {
// 生产环境,进行加密解密,不输出日志
if (res.encrypt === 1) {
// 加密的数据,需要解密
const dataParam = JSON.parse(utils.decrypt(res.data))
res.data = JSON.stringify(dataParam) === '{}' ? null : dataParam
}
}
} else {
return res
}
axios.interceptors.request.use(
config => onFulfilled(config, options),
error => onRejected(error, options)
)
})
// 加载响应拦截器
response.forEach(item => {
let {onFulfilled, onRejected} = item
if (!onFulfilled || typeof onFulfilled !== 'function') {
onFulfilled = response => response
// 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
if (res && res.code) {
if (res.code === 42011) {
message.error(res.msg||'您的登录已失效,请重新登录')
// to re-login
// Message({
// message: res.msg || '您的登录已失效,请重新登录',
// type: 'error',
// duration: 5 * 1000
// })
store.dispatch('user/resetToken').then(() => {
location.reload()
})
// 排除自定义的车队返回结果状态码
} else if (res.code !== 20000 && res.code !== 30001 && res.code !== 30002 && res.code !== 20001) {
if(WhiteApiErr.includes(response.config.url.split('/')[3])){
return res
}else{
message.error(res.msg||'操作失败')
return Promise.reject(new Error(res.message || '操作失败'))
}
} else {
return res
}
}
if (!onRejected || typeof onRejected !== 'function') {
onRejected = error => Promise.reject(error)
},
(error) => {
if(error.message=='tooRapid'){
console.log(error,'res防抖拦截')
message.warning ('请求过于频繁!')
}else{
let urlIndex = a.indexOf(error.config.url)
if(urlIndex!==-1){
a.splice(urlIndex,1);
}
message.error ('操作失败!')
}
axios.interceptors.response.use(
response => onFulfilled(response, options),
error => onRejected(error, options)
)
})
}
/**
* 解析 url 中的参数
* @param url
* @returns {Object}
*/
function parseUrlParams(url) {
const params = {}
if (!url || url === '' || typeof url !== 'string') {
return params
}
const paramsStr = url.split('?')[1]
if (!paramsStr) {
return params
}
const paramsArr = paramsStr.replace(/&|=/g, ' ').split(' ')
for (let i = 0; i < paramsArr.length / 2; i++) {
const value = paramsArr[i * 2 + 1]
params[paramsArr[i * 2]] = value === 'true' ? true : (value === 'false' ? false : value)
return Promise.reject(error)
}
return params
}
)
export {
METHOD,
AUTH_TYPE,
request,
setAuthorization,
removeAuthorization,
checkAuthorization,
loadInterceptors,
parseUrlParams
}
export default service

@ -253,13 +253,18 @@ function getI18nKey(path) {
* @param options
*/
function loadGuards(guards, options) {
//获取参数
const {beforeEach, afterEach} = guards
const {router} = options
//初始化路由配置
beforeEach.forEach(guard => {
// 检测配置参数
if (guard && typeof guard === 'function') {
//运行各种配置
router.beforeEach((to, from, next) => guard(to, from, next, options))
}
})
//同上
afterEach.forEach(guard => {
if (guard && typeof guard === 'function') {
router.afterEach((to, from) => guard(to, from, options))

@ -36,15 +36,32 @@ const assetsCDN = {
module.exports = {
devServer: {
// proxy: {
// '/api': { //此处要与 /services/api.js 中的 API_PROXY_PREFIX 值保持一致
// target: process.env.VUE_APP_API_BASE_URL,
// changeOrigin: true,
// pathRewrite: {
// '^/api': ''
// }
// }
// }
proxy: {
[process.env.VUE_APP_BASE_API]: {
// target: `cls`,
// target: `http://192.168.0.65:38080`,
// target: `https://www.xingoil.com/adminapi`,
target: 'http://uat.xingoil.com/adminapi',
changeOrigin: true,
pathRewrite: {
['^' + process.env.VUE_APP_BASE_API]: ''
}
},
[process.env.VUE_APP_UPLOAD_URL]: {
target: `http://127.0.0.1:38080`,
changeOrigin: true,
pathRewrite: {
['^' + process.env.VUE_APP_BASE_API]: ''
}
},
[process.env.VUE_APP_BASE_LSM_API]: {
target: `http://121.41.3.91:8085`,
changeOrigin: true,
pathRewrite: {
['^' + process.env.VUE_APP_BASE_LSM_API]: ''
}
}
}
},
pluginOptions: {
'style-resources-loader': {

19526
yarn.lock

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save