Compare commits
6 Commits
7de9d1545a
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| a32dc70c0a | |||
| 6d50bf8cf9 | |||
| 7274619f77 | |||
| c88d8cbabf | |||
| 06007047a4 | |||
| d556c8f0c1 |
@@ -18,4 +18,7 @@ npm run build
|
|||||||
npm run build --report
|
npm run build --report
|
||||||
```
|
```
|
||||||
|
|
||||||
For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader).
|
## 注意
|
||||||
|
|
||||||
|
- node版本不能过高,推荐v12.13.0
|
||||||
|
- 后台添加新权限需登录admin后台添加
|
||||||
|
|||||||
+1
-1
@@ -12,7 +12,7 @@ module.exports = {
|
|||||||
assetsPublicPath: '/',
|
assetsPublicPath: '/',
|
||||||
proxyTable: {
|
proxyTable: {
|
||||||
'/index.php':{
|
'/index.php':{
|
||||||
target:'https://cogapitest.scszsj.com/', //https://cogapi.scszsj.com/ http://cogapitest.scszsj.com/ http://gg.com/
|
target:'http://gg.local', //https://cogapi.scszsj.com/ http://cogapitest.scszsj.com/ http://gg.com/
|
||||||
changeOrigin: true, // 如果接口跨域,需要进行这个参数配置
|
changeOrigin: true, // 如果接口跨域,需要进行这个参数配置
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
<!DOCTYPE html><html><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><title>好游戏计划后台系统</title><link rel=stylesheet type=text/css href=static/css/common.css><link rel=stylesheet type=text/css href=static/css/app.css><script src=./static/UE/ueditor.config.js type=text/javascript charset=utf-8></script><script src=./static/UE/ueditor.all.js type=text/javascript charset=utf-8></script><script src=./static/UE/lang/zh-cn/zh-cn.js type=text/javascript charset=utf-8></script><script src=./static/UE/ueditor.parse.min.js type=text/javascript charset=utf-8></script><link href=/mng/static/css/app.c1444a5e54e1dcbbf95aa3a582e947ae.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=/mng/static/js/manifest.34b24ed356662ae59bb9.js></script><script type=text/javascript src=/mng/static/js/vendor.826cc3a26fa7c3b28cbb.js></script><script type=text/javascript src=/mng/static/js/app.8e0860a10b33441a6cf5.js></script></body></html>
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["webpack:///webpack/bootstrap 1beccf754d5bd345a4ab"],"names":["parentJsonpFunction","window","chunkIds","moreModules","executeModules","moduleId","chunkId","result","i","resolves","length","installedChunks","push","Object","prototype","hasOwnProperty","call","modules","shift","__webpack_require__","s","installedModules","2","exports","module","l","m","c","d","name","getter","o","defineProperty","configurable","enumerable","get","n","__esModule","object","property","p","oe","err","console","error"],"mappings":"aACA,IAAAA,EAAAC,OAAA,aACAA,OAAA,sBAAAC,EAAAC,EAAAC,GAIA,IADA,IAAAC,EAAAC,EAAAC,EAAAC,EAAA,EAAAC,KACQD,EAAAN,EAAAQ,OAAoBF,IAC5BF,EAAAJ,EAAAM,GACAG,EAAAL,IACAG,EAAAG,KAAAD,EAAAL,GAAA,IAEAK,EAAAL,GAAA,EAEA,IAAAD,KAAAF,EACAU,OAAAC,UAAAC,eAAAC,KAAAb,EAAAE,KACAY,EAAAZ,GAAAF,EAAAE,IAIA,IADAL,KAAAE,EAAAC,EAAAC,GACAK,EAAAC,QACAD,EAAAS,OAAAT,GAEA,GAAAL,EACA,IAAAI,EAAA,EAAYA,EAAAJ,EAAAM,OAA2BF,IACvCD,EAAAY,IAAAC,EAAAhB,EAAAI,IAGA,OAAAD,GAIA,IAAAc,KAGAV,GACAW,EAAA,GAIA,SAAAH,EAAAd,GAGA,GAAAgB,EAAAhB,GACA,OAAAgB,EAAAhB,GAAAkB,QAGA,IAAAC,EAAAH,EAAAhB,IACAG,EAAAH,EACAoB,GAAA,EACAF,YAUA,OANAN,EAAAZ,GAAAW,KAAAQ,EAAAD,QAAAC,IAAAD,QAAAJ,GAGAK,EAAAC,GAAA,EAGAD,EAAAD,QAKAJ,EAAAO,EAAAT,EAGAE,EAAAQ,EAAAN,EAGAF,EAAAS,EAAA,SAAAL,EAAAM,EAAAC,GACAX,EAAAY,EAAAR,EAAAM,IACAhB,OAAAmB,eAAAT,EAAAM,GACAI,cAAA,EACAC,YAAA,EACAC,IAAAL,KAMAX,EAAAiB,EAAA,SAAAZ,GACA,IAAAM,EAAAN,KAAAa,WACA,WAA2B,OAAAb,EAAA,SAC3B,WAAiC,OAAAA,GAEjC,OADAL,EAAAS,EAAAE,EAAA,IAAAA,GACAA,GAIAX,EAAAY,EAAA,SAAAO,EAAAC,GAAsD,OAAA1B,OAAAC,UAAAC,eAAAC,KAAAsB,EAAAC,IAGtDpB,EAAAqB,EAAA,QAGArB,EAAAsB,GAAA,SAAAC,GAA8D,MAApBC,QAAAC,MAAAF,GAAoBA","file":"static/js/manifest.34b24ed356662ae59bb9.js","sourcesContent":[" \t// install a JSONP callback for chunk loading\n \tvar parentJsonpFunction = window[\"webpackJsonp\"];\n \twindow[\"webpackJsonp\"] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) {\n \t\t// add \"moreModules\" to the modules object,\n \t\t// then flag all \"chunkIds\" as loaded and fire callback\n \t\tvar moduleId, chunkId, i = 0, resolves = [], result;\n \t\tfor(;i < chunkIds.length; i++) {\n \t\t\tchunkId = chunkIds[i];\n \t\t\tif(installedChunks[chunkId]) {\n \t\t\t\tresolves.push(installedChunks[chunkId][0]);\n \t\t\t}\n \t\t\tinstalledChunks[chunkId] = 0;\n \t\t}\n \t\tfor(moduleId in moreModules) {\n \t\t\tif(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {\n \t\t\t\tmodules[moduleId] = moreModules[moduleId];\n \t\t\t}\n \t\t}\n \t\tif(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules, executeModules);\n \t\twhile(resolves.length) {\n \t\t\tresolves.shift()();\n \t\t}\n \t\tif(executeModules) {\n \t\t\tfor(i=0; i < executeModules.length; i++) {\n \t\t\t\tresult = __webpack_require__(__webpack_require__.s = executeModules[i]);\n \t\t\t}\n \t\t}\n \t\treturn result;\n \t};\n\n \t// The module cache\n \tvar installedModules = {};\n\n \t// objects to store loaded and loading chunks\n \tvar installedChunks = {\n \t\t2: 0\n \t};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, {\n \t\t\t\tconfigurable: false,\n \t\t\t\tenumerable: true,\n \t\t\t\tget: getter\n \t\t\t});\n \t\t}\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"/mng/\";\n\n \t// on error function for async loading\n \t__webpack_require__.oe = function(err) { console.error(err); throw err; };\n\n\n\n// WEBPACK FOOTER //\n// webpack/bootstrap 1beccf754d5bd345a4ab"],"sourceRoot":""}
|
||||||
@@ -3,6 +3,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { fetchCsrfToken } from '@/utils/csrf'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'App',
|
name: 'App',
|
||||||
provide() {
|
provide() {
|
||||||
@@ -15,6 +17,12 @@ export default {
|
|||||||
isRouterAlive: true
|
isRouterAlive: true
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
async created() {
|
||||||
|
try {
|
||||||
|
await fetchCsrfToken()
|
||||||
|
} catch (error) {
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
reload() {
|
reload() {
|
||||||
this.isRouterAlive = false;
|
this.isRouterAlive = false;
|
||||||
|
|||||||
@@ -45,6 +45,10 @@ let assetApi = {
|
|||||||
gameGenDac(data, errorback) {
|
gameGenDac(data, errorback) {
|
||||||
return request._post('/shop/gameasset.GameAsset/genDac', data, errorback);
|
return request._post('/shop/gameasset.GameAsset/genDac', data, errorback);
|
||||||
},
|
},
|
||||||
|
/*数字资产上链审核*/
|
||||||
|
gameAssetAudit(data, errorback) {
|
||||||
|
return request._post('/shop/gameasset.GameAsset/audit', data, errorback);
|
||||||
|
},
|
||||||
/*资产列表*/
|
/*资产列表*/
|
||||||
gameAssetdetail(data, errorback) {
|
gameAssetdetail(data, errorback) {
|
||||||
return request._get('/shop/gameasset.GameAsset/index', data, errorback);
|
return request._get('/shop/gameasset.GameAsset/index', data, errorback);
|
||||||
|
|||||||
@@ -69,6 +69,10 @@ let GameApi = {
|
|||||||
return request._post('/shop/game.applys/index', data, errorback);
|
return request._post('/shop/game.applys/index', data, errorback);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
processApply(data, errorback) {
|
||||||
|
return request._post('/shop/game.apply/processApply', data, errorback);
|
||||||
|
},
|
||||||
|
|
||||||
/*专家选择列表*/
|
/*专家选择列表*/
|
||||||
storeLists(data, errorback) {
|
storeLists(data, errorback) {
|
||||||
return request._post('/shop/data.store/lists', data, errorback);
|
return request._post('/shop/data.store/lists', data, errorback);
|
||||||
|
|||||||
+3
-2
@@ -11,8 +11,9 @@ let IndexApi = {
|
|||||||
getCount(data, errorback) {
|
getCount(data, errorback) {
|
||||||
return request._post('/shop/Index/index', data, errorback);
|
return request._post('/shop/Index/index', data, errorback);
|
||||||
},
|
},
|
||||||
|
getCaptcha(data, errorback) {
|
||||||
|
return request._get('/shop/passport/captcha', data, errorback);
|
||||||
|
},
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,283 @@
|
|||||||
|
# Shop 后台前端 CSRF 防护接入指南
|
||||||
|
|
||||||
|
> 适用对象:Shop 后台 Vue.js SPA 前端开发人员
|
||||||
|
> 关联后端:`app/shop/middleware/CsrfToken.php`、`app/shop/controller/Passport.php`
|
||||||
|
|
||||||
|
## 背景
|
||||||
|
|
||||||
|
后端已为 shop 后台模块添加 CSRF 防护中间件,规则如下:
|
||||||
|
|
||||||
|
- **未登录请求**:直接放行(无认证凭据,无需防护)
|
||||||
|
- **已登录 + GET/HEAD/OPTIONS**:放行
|
||||||
|
- **已登录 + POST/PUT/PATCH/DELETE**:必须携带 `X-CSRF-TOKEN` 请求头,且与 session 中的 token 一致,否则返回 `419`
|
||||||
|
- 登录成功、退出登录会**轮换** session 和 CSRF token
|
||||||
|
|
||||||
|
前端必须配合改造,否则登录后所有写操作都会被拦截。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 改造点总览
|
||||||
|
|
||||||
|
| 改造点 | 位置 | 作用 |
|
||||||
|
|--------|------|------|
|
||||||
|
| ① Token 存储模块 | `src/utils/csrf.js`(新建) | 用内存变量存 token |
|
||||||
|
| ② axios 请求拦截器 | axios 实例配置 | 写操作自动带 `X-CSRF-TOKEN` |
|
||||||
|
| ③ axios 响应拦截器 | axios 实例配置 | 处理 419、登录失效 |
|
||||||
|
| ④ 登录流程 | 登录页 | 登录成功后刷新 token |
|
||||||
|
| ⑤ 应用初始化 | `App.vue` / `main.js` | 刷新页面后恢复 token |
|
||||||
|
| ⑥ 退出登录 | 退出按钮处理函数 | 清除本地 token |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ① 封装 Token 存储
|
||||||
|
|
||||||
|
token 不要存 `localStorage`/`sessionStorage`(XSS 可读),用内存变量 + 工具函数。
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// src/utils/csrf.js
|
||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
|
let csrfToken = ''
|
||||||
|
|
||||||
|
export function setCsrfToken(token) {
|
||||||
|
csrfToken = token || ''
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCsrfToken() {
|
||||||
|
return csrfToken
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从后端获取 token
|
||||||
|
export async function fetchCsrfToken() {
|
||||||
|
const res = await axios.get('/shop/passport/csrfToken')
|
||||||
|
if (res.data.code === 1 && res.data.data.token) {
|
||||||
|
setCsrfToken(res.data.data.token)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ② axios 请求拦截器
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// src/utils/request.js(或现有 axios 实例配置文件)
|
||||||
|
import axios from 'axios'
|
||||||
|
import { getCsrfToken } from './csrf'
|
||||||
|
|
||||||
|
const request = axios.create({
|
||||||
|
baseURL: '/',
|
||||||
|
withCredentials: true, // ★ 必须开启,携带 session cookie
|
||||||
|
})
|
||||||
|
|
||||||
|
// 请求拦截器:写操作自动带上 X-CSRF-TOKEN
|
||||||
|
request.interceptors.request.use(config => {
|
||||||
|
const method = (config.method || 'get').toLowerCase()
|
||||||
|
if (['post', 'put', 'patch', 'delete'].includes(method)) {
|
||||||
|
const token = getCsrfToken()
|
||||||
|
if (token) {
|
||||||
|
config.headers['X-CSRF-TOKEN'] = token
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return config
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
**说明**:后端中间件只校验 POST/PUT/PATCH/DELETE,GET 不校验,因此只在写操作时加 header。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ③ axios 响应拦截器
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import { Message } from 'element-ui' // 或 antd / 自定义提示组件
|
||||||
|
import router from '@/router'
|
||||||
|
import { setCsrfToken } from './csrf'
|
||||||
|
|
||||||
|
request.interceptors.response.use(
|
||||||
|
response => response.data,
|
||||||
|
error => {
|
||||||
|
const status = error.response && error.response.status
|
||||||
|
|
||||||
|
// CSRF 校验失败(token 过期或不匹配)
|
||||||
|
if (status === 419) {
|
||||||
|
Message.error('登录状态已过期,请重新登录')
|
||||||
|
setCsrfToken('')
|
||||||
|
router.replace('/login')
|
||||||
|
return Promise.reject(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 后端自定义的未登录 code(保留原有逻辑)
|
||||||
|
if (error.response && error.response.data.code === -1) {
|
||||||
|
router.replace('/login')
|
||||||
|
return Promise.reject(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.reject(error)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ④ 登录流程
|
||||||
|
|
||||||
|
后端登录接口需要**图形验证码**,且登录成功后 session 会轮换,**必须重新获取 token**。
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// src/views/login/index.vue
|
||||||
|
import { fetchCsrfToken } from '@/utils/csrf'
|
||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
form: { username: '', password: '', captcha: '' },
|
||||||
|
captchaImg: '',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async created() {
|
||||||
|
await this.refreshCaptcha()
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
// 获取验证码图(后端 /shop/passport/captcha 返回 base64)
|
||||||
|
async refreshCaptcha() {
|
||||||
|
const res = await request.get('/shop/passport/captcha')
|
||||||
|
if (res.code === 1) {
|
||||||
|
this.captchaImg = res.data.img
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 提交登录
|
||||||
|
async handleLogin() {
|
||||||
|
const res = await request.post('/shop/passport/login', {
|
||||||
|
username: this.form.username,
|
||||||
|
password: this.form.password,
|
||||||
|
captcha: this.form.captcha,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (res.code !== 1) {
|
||||||
|
// 登录失败,验证码作废,刷新验证码
|
||||||
|
this.form.captcha = ''
|
||||||
|
await this.refreshCaptcha()
|
||||||
|
this.$message.error(res.msg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ★ 关键:登录成功后重新获取 CSRF token
|
||||||
|
// (登录会重新生成 session,旧 token 失效)
|
||||||
|
await fetchCsrfToken()
|
||||||
|
|
||||||
|
this.$message.success('登录成功')
|
||||||
|
this.$router.push('/')
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⑤ 应用初始化
|
||||||
|
|
||||||
|
页面刷新后内存变量丢失,在 App 启动时重新拉取 token:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// src/App.vue 或 src/main.js
|
||||||
|
import { fetchCsrfToken } from '@/utils/csrf'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
async created() {
|
||||||
|
try {
|
||||||
|
await fetchCsrfToken()
|
||||||
|
} catch (e) {
|
||||||
|
// 网络问题忽略;未登录会话也会返回 token,不会报错
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**说明**:后端中间件对未登录请求也会生成并返回 token,但真正生效的 token 是**登录后重新生成**的那个。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⑥ 退出登录
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import { setCsrfToken } from '@/utils/csrf'
|
||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
async function handleLogout() {
|
||||||
|
// logout 是写操作,拦截器会自动带 X-CSRF-TOKEN
|
||||||
|
await request.post('/shop/passport/logout')
|
||||||
|
setCsrfToken('')
|
||||||
|
router.replace('/login')
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 端到端时序图
|
||||||
|
|
||||||
|
```
|
||||||
|
前端 后端
|
||||||
|
│ │
|
||||||
|
│ ① 打开登录页 │
|
||||||
|
│ GET /shop/passport/captcha │
|
||||||
|
│ ────────────────────────────────────────→│ 返回 base64 验证码图
|
||||||
|
│ │ (CsrfToken 中间件顺便生成 token)
|
||||||
|
│ │
|
||||||
|
│ ② 提交登录 │
|
||||||
|
│ POST /shop/passport/login │
|
||||||
|
│ {username, password, captcha} │
|
||||||
|
│ ────────────────────────────────────────→│ 校验验证码 + 密码
|
||||||
|
│ │ 登录成功 → Session::regenerate()
|
||||||
|
│ │ → CsrfToken::regenerateToken()
|
||||||
|
│ │ ★ session 里现在是新 token
|
||||||
|
│ │
|
||||||
|
│ ③ 立即获取新 token │
|
||||||
|
│ GET /shop/passport/csrfToken │
|
||||||
|
│ ────────────────────────────────────────→│ 返回 {token: "新token"}
|
||||||
|
│ │
|
||||||
|
│ ④ 保存 token 到内存 │
|
||||||
|
│ csrfToken = "新token" │
|
||||||
|
│ │
|
||||||
|
│ ⑤ 后续业务请求(自动带 token) │
|
||||||
|
│ POST /shop/auth.user/getRoleList │
|
||||||
|
│ Header: X-CSRF-TOKEN = "新token" │
|
||||||
|
│ ────────────────────────────────────────→│ CsrfToken 中间件校验通过 ✅
|
||||||
|
│ │
|
||||||
|
│ ⑥ token 过期/session 失效 │
|
||||||
|
│ POST /xxx │
|
||||||
|
│ ────────────────────────────────────────→│ 返回 419 ❌
|
||||||
|
│ ←────────────────────────────────────────│
|
||||||
|
│ 响应拦截器捕获 419 → 跳登录页 │
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
| 项 | 说明 |
|
||||||
|
|----|------|
|
||||||
|
| `withCredentials: true` | axios 实例必须配置,否则不带 session cookie |
|
||||||
|
| 不要存 localStorage | XSS 可读取导致 token 泄露;用内存变量即可(刷新时重新拉取) |
|
||||||
|
| 验证码一次性 | 登录失败后验证码作废,**必须刷新验证码图** |
|
||||||
|
| 文件上传也要带 token | `POST /shop/upload/*` 同样校验,拦截器会自动加 header |
|
||||||
|
| 联调顺序建议 | 先做 ①②(拦截器)→ 再做 ④(登录)→ 最后做 ⑤(初始化) |
|
||||||
|
| token 生命周期 | 整个 session 有效(默认 8 小时),登录/退出会轮换 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 联调验证清单
|
||||||
|
|
||||||
|
部署后依次验证:
|
||||||
|
|
||||||
|
- [ ] 登录页能加载验证码图
|
||||||
|
- [ ] 输错验证码 → 报错 + 验证码刷新
|
||||||
|
- [ ] 登录成功 → 接口返回 200,前端拿到新 token
|
||||||
|
- [ ] 登录后任意 POST 请求 → 200(拦截器自动带 header)
|
||||||
|
- [ ] 手动清除 token 后发 POST → 419 → 自动跳登录页
|
||||||
|
- [ ] 退出登录 → 跳登录页 → 再登录流程正常
|
||||||
|
- [ ] 浏览器 DevTools → Application → Cookies → session cookie 显示 `HttpOnly; SameSite=Lax`
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
|
let csrfToken = ''
|
||||||
|
let csrfTokenPromise = null
|
||||||
|
|
||||||
|
export function setCsrfToken(token) {
|
||||||
|
csrfToken = token || ''
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCsrfToken() {
|
||||||
|
return csrfToken
|
||||||
|
}
|
||||||
|
|
||||||
|
// 登录成功或应用初始化后拉取当前会话的 CSRF token。
|
||||||
|
export async function fetchCsrfToken(forceRefresh = false) {
|
||||||
|
if (!forceRefresh && csrfToken) {
|
||||||
|
return csrfToken
|
||||||
|
}
|
||||||
|
|
||||||
|
if (csrfTokenPromise) {
|
||||||
|
return csrfTokenPromise
|
||||||
|
}
|
||||||
|
|
||||||
|
csrfTokenPromise = axios.get('/shop/passport/csrfToken')
|
||||||
|
.then((res) => {
|
||||||
|
const token = res && res.code === 1 && res.data ? res.data.token : ''
|
||||||
|
|
||||||
|
if (!token) {
|
||||||
|
throw new Error('CSRF Token 获取失败')
|
||||||
|
}
|
||||||
|
|
||||||
|
setCsrfToken(token)
|
||||||
|
return token
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
csrfTokenPromise = null
|
||||||
|
})
|
||||||
|
|
||||||
|
return csrfTokenPromise
|
||||||
|
}
|
||||||
+41
-10
@@ -1,13 +1,23 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
import { getCsrfToken,setCsrfToken} from '@/utils/csrf';
|
||||||
import qs from 'qs';
|
import qs from 'qs';
|
||||||
import router from '@/router';
|
import router from '@/router';
|
||||||
import store from '@/store';
|
import store from '@/store';
|
||||||
import {
|
import {
|
||||||
Message,
|
Message
|
||||||
Loading
|
|
||||||
} from 'element-ui';
|
} from 'element-ui';
|
||||||
import {delCookie,deleteSessionStorage} from '@/utils/base.js';
|
import {delCookie,deleteSessionStorage} from '@/utils/base.js';
|
||||||
|
|
||||||
|
function clearLoginState() {
|
||||||
|
delCookie('isLogin');
|
||||||
|
delCookie('baseInfo');
|
||||||
|
delCookie('userinfo');
|
||||||
|
setCsrfToken('');
|
||||||
|
deleteSessionStorage('rolelist');
|
||||||
|
deleteSessionStorage('authlist');
|
||||||
|
store.commit('user/setState',{key:'roles',val:null});
|
||||||
|
}
|
||||||
|
|
||||||
//axios.defaults.timeout = 5000; //响应时间
|
//axios.defaults.timeout = 5000; //响应时间
|
||||||
axios.defaults.headers['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8'; //配置请求头
|
axios.defaults.headers['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8'; //配置请求头
|
||||||
//axios.defaults.baseURL = 'http://www.jjj-shop.com/'; //配置接口地址
|
//axios.defaults.baseURL = 'http://www.jjj-shop.com/'; //配置接口地址
|
||||||
@@ -18,13 +28,22 @@ axios.defaults.responseType = 'json';
|
|||||||
//POST传参序列化(添加请求拦截器)
|
//POST传参序列化(添加请求拦截器)
|
||||||
axios.interceptors.request.use((config) =>
|
axios.interceptors.request.use((config) =>
|
||||||
{
|
{
|
||||||
// console.log(config);
|
|
||||||
//在发送请求之前做某件事
|
//在发送请求之前做某件事
|
||||||
if (config.method === 'post'
|
if (config.method === 'post'
|
||||||
&& config.url != '/shop/file.upload/image'
|
&& config.url != '/shop/file.upload/image'
|
||||||
&& config.url != '/shop/order.operate/batchDelivery'&& config.url != '/shop/risk.GameData/import') {
|
&& config.url != '/shop/order.operate/batchDelivery'&& config.url != '/shop/risk.GameData/import') {
|
||||||
config.data = qs.stringify(config.data);
|
config.data = qs.stringify(config.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 添加 CSRF Token
|
||||||
|
const method = (config.method || 'get').toLowerCase()
|
||||||
|
if (['post', 'put', 'patch', 'delete'].includes(method)) {
|
||||||
|
const token = getCsrfToken()
|
||||||
|
if (token) {
|
||||||
|
config.headers = config.headers || {}
|
||||||
|
config.headers['X-CSRF-TOKEN'] = token
|
||||||
|
}
|
||||||
|
}
|
||||||
return config;
|
return config;
|
||||||
}, (error) =>
|
}, (error) =>
|
||||||
{
|
{
|
||||||
@@ -32,6 +51,7 @@ axios.interceptors.request.use((config) =>
|
|||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
//返回状态判断(添加响应拦截器)
|
//返回状态判断(添加响应拦截器)
|
||||||
axios.interceptors.response.use((res) =>
|
axios.interceptors.response.use((res) =>
|
||||||
{
|
{
|
||||||
@@ -45,13 +65,8 @@ axios.interceptors.response.use((res) =>
|
|||||||
});
|
});
|
||||||
return Promise.reject(res.data);
|
return Promise.reject(res.data);
|
||||||
} else {
|
} else {
|
||||||
delCookie('isLogin');
|
clearLoginState();
|
||||||
delCookie('baseInfo');
|
router.replace({
|
||||||
deleteSessionStorage('rolelist');
|
|
||||||
deleteSessionStorage('authlist');
|
|
||||||
/*删除stores数据*/
|
|
||||||
store.commit('user/setState',{key:'roles',val:null});
|
|
||||||
router.push({
|
|
||||||
path: '/login',
|
path: '/login',
|
||||||
query: {}
|
query: {}
|
||||||
});
|
});
|
||||||
@@ -61,6 +76,22 @@ axios.interceptors.response.use((res) =>
|
|||||||
return res.data;
|
return res.data;
|
||||||
}, (error) =>
|
}, (error) =>
|
||||||
{
|
{
|
||||||
|
const status = error.response && error.response.status;
|
||||||
|
|
||||||
|
if (status === 419) {
|
||||||
|
clearLoginState();
|
||||||
|
Message({
|
||||||
|
showClose: true,
|
||||||
|
message: '登录状态已过期,请重新登录',
|
||||||
|
type: 'error'
|
||||||
|
});
|
||||||
|
router.replace({
|
||||||
|
path: '/login',
|
||||||
|
query: {}
|
||||||
|
});
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
|
||||||
Message({
|
Message({
|
||||||
showClose: true,
|
showClose: true,
|
||||||
message: '接口请求异常,请稍后再试~',
|
message: '接口请求异常,请稍后再试~',
|
||||||
|
|||||||
@@ -17,10 +17,10 @@
|
|||||||
<div class="table-wrap">
|
<div class="table-wrap">
|
||||||
<el-table size="small" :data="tableData" border style="width: 100%" v-loading="loading">
|
<el-table size="small" :data="tableData" border style="width: 100%" v-loading="loading">
|
||||||
<!-- <el-table-column prop="id" label="ID" width="80"></el-table-column> -->
|
<!-- <el-table-column prop="id" label="ID" width="80"></el-table-column> -->
|
||||||
<el-table-column prop="name" label="称呼" width="200"></el-table-column>
|
<el-table-column prop="name" label="称呼" width="110"></el-table-column>
|
||||||
<el-table-column prop="phone" label="联系方式" width="200"></el-table-column>
|
<el-table-column prop="phone" label="联系方式" width="120"></el-table-column>
|
||||||
<el-table-column prop="company" label="企业名称" width="250"></el-table-column>
|
<el-table-column prop="company" label="企业名称" width="220"></el-table-column>
|
||||||
<el-table-column prop="company_type" label="企业类型" width="250"></el-table-column>
|
<el-table-column prop="company_type" label="企业类型" width="100"></el-table-column>
|
||||||
<el-table-column prop="need" label="企业需求" width="440"></el-table-column>
|
<el-table-column prop="need" label="企业需求" width="440"></el-table-column>
|
||||||
<!-- <el-table-column prop="status" label="状态" width="120">
|
<!-- <el-table-column prop="status" label="状态" width="120">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
@@ -28,11 +28,27 @@
|
|||||||
<span v-if="scope.row.status.value == 0" class="gray">禁用</span>
|
<span v-if="scope.row.status.value == 0" class="gray">禁用</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column> -->
|
</el-table-column> -->
|
||||||
|
<el-table-column prop="satisfaction" label="满意度" width="100">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span v-if="scope.row.satisfaction === 5">非常满意</span>
|
||||||
|
<span v-else-if="scope.row.satisfaction === 4">满意</span>
|
||||||
|
<span v-else-if="scope.row.satisfaction === 3">良好</span>
|
||||||
|
<span v-else-if="scope.row.satisfaction === 2">不满意</span>
|
||||||
|
<span v-else-if="scope.row.satisfaction === 1">非常不满意</span>
|
||||||
|
<span v-else>--</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column prop="create_time" label="申请时间" width="140"></el-table-column>
|
<el-table-column prop="create_time" label="申请时间" width="140"></el-table-column>
|
||||||
<!-- <el-table-column prop="update_time" label="修改时间" width="140"></el-table-column> -->
|
<el-table-column prop="update_time" label="处理时间" width="140">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span v-if="scope.row.update_time === scope.row.create_time">--</span>
|
||||||
|
<span v-else>{{scope.row.update_time}}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="操作">
|
<el-table-column label="操作">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-button @click="editClick(scope.row)" type="text" size="small" v-auth="'/game/apply/edit'">详情</el-button>
|
<el-button @click="editClick(scope.row)" type="text" size="small" v-auth="'/game/apply/edit'">详情</el-button>
|
||||||
|
<el-button @click="openProcessDialog(scope.row)" type="text" size="small" v-auth="'/game/apply/edit'" v-if="scope.row.status.value==0">处理</el-button>
|
||||||
<el-button @click="deleteClick(scope.row)" type="text" size="small" v-auth="'/game/apply/delete'">删除</el-button>
|
<el-button @click="deleteClick(scope.row)" type="text" size="small" v-auth="'/game/apply/delete'">删除</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@@ -53,12 +69,35 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 处理满意度弹窗 -->
|
||||||
|
<el-dialog
|
||||||
|
title="处理申请"
|
||||||
|
:visible.sync="processDialogVisible"
|
||||||
|
width="400px"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
>
|
||||||
|
<el-form :model="processForm" label-width="80px">
|
||||||
|
<el-form-item label="满意度" required>
|
||||||
|
<el-select v-model="processForm.satisfaction" placeholder="请选择满意度" style="width: 100%;">
|
||||||
|
<el-option label="非常满意" value="5"></el-option>
|
||||||
|
<el-option label="满意" value="4"></el-option>
|
||||||
|
<el-option label="良好" value="3"></el-option>
|
||||||
|
<el-option label="不满意" value="2"></el-option>
|
||||||
|
<el-option label="非常不满意" value="1"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="processDialogVisible = false">取 消</el-button>
|
||||||
|
<el-button type="primary" :loading="processDialogLoading" @click="processSubmit">确 定</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import GameApi from '@/api/game.js';
|
import GameApi from '@/api/game.js';
|
||||||
|
import { status } from 'nprogress';
|
||||||
export default {
|
export default {
|
||||||
components: {},
|
components: {},
|
||||||
inject: ['reload'],
|
inject: ['reload'],
|
||||||
@@ -83,7 +122,14 @@ export default {
|
|||||||
/*是否打开编辑弹窗*/
|
/*是否打开编辑弹窗*/
|
||||||
open_edit: false,
|
open_edit: false,
|
||||||
/*当前编辑的对象*/
|
/*当前编辑的对象*/
|
||||||
userModel: {}
|
userModel: {},
|
||||||
|
// 处理申请弹窗
|
||||||
|
processDialogVisible: false,
|
||||||
|
processDialogLoading: false,
|
||||||
|
processForm: {
|
||||||
|
id: null,
|
||||||
|
satisfaction: ''
|
||||||
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
@@ -105,7 +151,7 @@ export default {
|
|||||||
this.pageSize = val;
|
this.pageSize = val;
|
||||||
this.getTableList();
|
this.getTableList();
|
||||||
},
|
},
|
||||||
/*搜索查询*/
|
/*搜索查询*/
|
||||||
onSubmit() {
|
onSubmit() {
|
||||||
this.curPage = 1;
|
this.curPage = 1;
|
||||||
this.getTableList();
|
this.getTableList();
|
||||||
@@ -170,6 +216,37 @@ export default {
|
|||||||
.catch(error => {});
|
.catch(error => {});
|
||||||
})
|
})
|
||||||
.catch(() => {});
|
.catch(() => {});
|
||||||
|
},
|
||||||
|
|
||||||
|
/* 打开处理申请弹窗 */
|
||||||
|
openProcessDialog(row) {
|
||||||
|
this.processForm.id = row.id;
|
||||||
|
this.processForm.satisfaction = '';
|
||||||
|
this.processDialogVisible = true;
|
||||||
|
this.processDialogLoading = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
/* 提交处理申请 */
|
||||||
|
processSubmit() {
|
||||||
|
if (!this.processForm.satisfaction) {
|
||||||
|
this.$message.warning('请选择满意度');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.processDialogLoading = true;
|
||||||
|
GameApi.processApply({
|
||||||
|
id: this.processForm.id,
|
||||||
|
satisfaction: this.processForm.satisfaction,
|
||||||
|
status: 1
|
||||||
|
}, true)
|
||||||
|
.then(() => {
|
||||||
|
this.$message.success('处理成功');
|
||||||
|
this.processDialogVisible = false;
|
||||||
|
this.getTableList();
|
||||||
|
})
|
||||||
|
.catch(() => {})
|
||||||
|
.finally(() => {
|
||||||
|
this.processDialogLoading = false;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -28,11 +28,27 @@
|
|||||||
<span v-if="scope.row.status.value == 0" class="gray">禁用</span>
|
<span v-if="scope.row.status.value == 0" class="gray">禁用</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column> -->
|
</el-table-column> -->
|
||||||
|
<el-table-column prop="satisfaction" label="满意度" width="100">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span v-if="scope.row.satisfaction === 5">非常满意</span>
|
||||||
|
<span v-else-if="scope.row.satisfaction === 4">满意</span>
|
||||||
|
<span v-else-if="scope.row.satisfaction === 3">良好</span>
|
||||||
|
<span v-else-if="scope.row.satisfaction === 2">不满意</span>
|
||||||
|
<span v-else-if="scope.row.satisfaction === 1">非常不满意</span>
|
||||||
|
<span v-else>--</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column prop="create_time" label="申请时间" width="180"></el-table-column>
|
<el-table-column prop="create_time" label="申请时间" width="180"></el-table-column>
|
||||||
<!-- <el-table-column prop="update_time" label="修改时间" width="140"></el-table-column> -->
|
<el-table-column prop="update_time" label="处理时间" width="140">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span v-if="scope.row.update_time === scope.row.create_time">--</span>
|
||||||
|
<span v-else>{{scope.row.update_time}}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="操作" >
|
<el-table-column label="操作" >
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-button @click="editClick(scope.row)" type="text" size="small" v-auth="'/game/applys/edit'">详情</el-button>
|
<el-button @click="editClick(scope.row)" type="text" size="small" v-auth="'/game/applys/edit'">详情</el-button>
|
||||||
|
<el-button @click="openProcessDialog(scope.row)" type="text" size="small" v-auth="'/game/apply/edit'" v-if="scope.row.status.value==0">处理</el-button>
|
||||||
<el-button @click="deleteClick(scope.row)" type="text" size="small" v-auth="'/game/applys/delete'">删除</el-button>
|
<el-button @click="deleteClick(scope.row)" type="text" size="small" v-auth="'/game/applys/delete'">删除</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@@ -53,7 +69,29 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 处理满意度弹窗 -->
|
||||||
|
<el-dialog
|
||||||
|
title="处理申请"
|
||||||
|
:visible.sync="processDialogVisible"
|
||||||
|
width="400px"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
>
|
||||||
|
<el-form :model="processForm" label-width="80px">
|
||||||
|
<el-form-item label="满意度" required>
|
||||||
|
<el-select v-model="processForm.satisfaction" placeholder="请选择满意度" style="width: 100%;">
|
||||||
|
<el-option label="非常满意" :value="5"></el-option>
|
||||||
|
<el-option label="满意" :value="4"></el-option>
|
||||||
|
<el-option label="良好" :value="3"></el-option>
|
||||||
|
<el-option label="不满意" :value="2"></el-option>
|
||||||
|
<el-option label="非常不满意" :value="1"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="processDialogVisible = false">取 消</el-button>
|
||||||
|
<el-button type="primary" :loading="processDialogLoading" @click="processSubmit">确 定</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -83,7 +121,14 @@ export default {
|
|||||||
/*是否打开编辑弹窗*/
|
/*是否打开编辑弹窗*/
|
||||||
open_edit: false,
|
open_edit: false,
|
||||||
/*当前编辑的对象*/
|
/*当前编辑的对象*/
|
||||||
userModel: {}
|
userModel: {},
|
||||||
|
/*处理弹窗相关*/
|
||||||
|
processDialogVisible: false,
|
||||||
|
processDialogLoading: false,
|
||||||
|
processForm: {
|
||||||
|
satisfaction: null
|
||||||
|
},
|
||||||
|
processRowId: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
@@ -105,7 +150,7 @@ export default {
|
|||||||
this.pageSize = val;
|
this.pageSize = val;
|
||||||
this.getTableList();
|
this.getTableList();
|
||||||
},
|
},
|
||||||
/*搜索查询*/
|
/*搜索查询*/
|
||||||
onSubmit() {
|
onSubmit() {
|
||||||
this.curPage = 1;
|
this.curPage = 1;
|
||||||
this.getTableList();
|
this.getTableList();
|
||||||
@@ -121,7 +166,7 @@ export default {
|
|||||||
.then(data => {
|
.then(data => {
|
||||||
self.loading = false;
|
self.loading = false;
|
||||||
self.tableData = data.data.list.data;
|
self.tableData = data.data.list.data;
|
||||||
console.log(self.tableData,222)
|
// console.log(self.tableData, 222)
|
||||||
self.totalDataNumber = data.data.list.total;
|
self.totalDataNumber = data.data.list.total;
|
||||||
})
|
})
|
||||||
.catch(error => {});
|
.catch(error => {});
|
||||||
@@ -134,11 +179,9 @@ export default {
|
|||||||
|
|
||||||
/*打开编辑*/
|
/*打开编辑*/
|
||||||
editClick(row) {
|
editClick(row) {
|
||||||
let self = this;
|
|
||||||
let params = row.id;
|
let params = row.id;
|
||||||
this.$router.push({
|
this.$router.push({
|
||||||
path: '/game/applys/edit',
|
path: '/game/applys/edit',
|
||||||
// name: 'mallList',
|
|
||||||
query: {
|
query: {
|
||||||
id: params
|
id: params
|
||||||
}
|
}
|
||||||
@@ -171,6 +214,42 @@ export default {
|
|||||||
.catch(error => {});
|
.catch(error => {});
|
||||||
})
|
})
|
||||||
.catch(() => {});
|
.catch(() => {});
|
||||||
|
},
|
||||||
|
|
||||||
|
/* 打开处理弹窗 */
|
||||||
|
openProcessDialog(row) {
|
||||||
|
this.processDialogVisible = true;
|
||||||
|
this.processDialogLoading = false;
|
||||||
|
this.processForm.satisfaction = null;
|
||||||
|
this.processRowId = row.id;
|
||||||
|
},
|
||||||
|
|
||||||
|
/* 确认处理申请 */
|
||||||
|
processSubmit() {
|
||||||
|
if (!this.processForm.satisfaction) {
|
||||||
|
this.$message({
|
||||||
|
type: 'warning',
|
||||||
|
message: '请选择满意度'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.processDialogLoading = true;
|
||||||
|
GameApi.processApply({
|
||||||
|
id: this.processRowId,
|
||||||
|
satisfaction: this.processForm.satisfaction
|
||||||
|
}, true)
|
||||||
|
.then(() => {
|
||||||
|
this.$message({
|
||||||
|
type: 'success',
|
||||||
|
message: '处理成功'
|
||||||
|
});
|
||||||
|
this.processDialogVisible = false;
|
||||||
|
this.getTableList();
|
||||||
|
})
|
||||||
|
.catch(() => {})
|
||||||
|
.finally(() => {
|
||||||
|
this.processDialogLoading = false;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -68,10 +68,11 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
||||||
<el-table-column fixed="right" label="操作" width="210">
|
<el-table-column fixed="right" label="操作" width="260">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-button v-if="scope.row.status != 'pending'" @click="editClick(scope.row,'edit')" type="text" size="small" v-auth="'/gameasset/gameasset/edit'">查看</el-button>
|
<el-button v-if="scope.row.status != 'pending'" @click="editClick(scope.row,'edit')" type="text" size="small" v-auth="'/gameasset/gameasset/edit'">查看</el-button>
|
||||||
<el-button @click="editClicks(scope.row,'audit')" type="text" size="small" v-auth="'/gameasset/gameasset/recharge'">生成资产</el-button>
|
<el-button @click="editClicks(scope.row,'audit')" type="text" size="small" v-auth="'/gameasset/gameasset/recharge'">生成资产</el-button>
|
||||||
|
<el-button v-if="scope.row.auth_status=='pending'" @click="openAuditDialog(scope.row)" type="text" size="small" v-auth="'/gameasset/gameasset/audit'">审核</el-button>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@@ -93,6 +94,32 @@
|
|||||||
|
|
||||||
<!--会员充值-->
|
<!--会员充值-->
|
||||||
<Recharge v-if="open_add" :open_add="open_add" :form="userModel" @closeDialog="closeDialogFunc($event, 'add')"></Recharge>
|
<Recharge v-if="open_add" :open_add="open_add" :form="userModel" @closeDialog="closeDialogFunc($event, 'add')"></Recharge>
|
||||||
|
|
||||||
|
<!--数字资产上链审核弹窗-->
|
||||||
|
<el-dialog :visible.sync="auditDialogVisible" title="数字资产上链审核" width="420px" @close="onAuditDialogClose">
|
||||||
|
<el-form size="small" :model="auditForm" :rules="auditRules" ref="auditFormRef" label-width="80px">
|
||||||
|
<el-form-item label="审核结果" prop="status">
|
||||||
|
<el-select v-model="auditForm.status" placeholder="请选择审核结果">
|
||||||
|
<el-option label="通过" value="pass"></el-option>
|
||||||
|
<el-option label="拒绝" value="reject"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="审核备注" prop="remark">
|
||||||
|
<el-input
|
||||||
|
v-model="auditForm.remark"
|
||||||
|
type="textarea"
|
||||||
|
placeholder="请输入审核备注"
|
||||||
|
maxlength="255"
|
||||||
|
show-word-limit
|
||||||
|
:autosize="{ minRows: 2, maxRows: 4 }"
|
||||||
|
></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="auditDialogVisible = false">取 消</el-button>
|
||||||
|
<el-button type="primary" @click="submitAudit" :loading="auditLoading">确 定</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -128,7 +155,40 @@ export default {
|
|||||||
tableData: [],
|
tableData: [],
|
||||||
userModel:{},
|
userModel:{},
|
||||||
/*全部分类*/
|
/*全部分类*/
|
||||||
message: 'pages/product/detail/detail?product_id='
|
message: 'pages/product/detail/detail?product_id=',
|
||||||
|
/*审核弹窗相关*/
|
||||||
|
auditDialogVisible: false,
|
||||||
|
auditLoading: false,
|
||||||
|
auditForm: {
|
||||||
|
id: null,
|
||||||
|
status: '',
|
||||||
|
remark: ''
|
||||||
|
},
|
||||||
|
auditTargetRow: null,
|
||||||
|
auditRules: {
|
||||||
|
status: [
|
||||||
|
{ required: true, message: '请选择审核结果', trigger: 'change' }
|
||||||
|
],
|
||||||
|
remark: [
|
||||||
|
{ validator: (rule, value, callback) => {
|
||||||
|
const vm = this;
|
||||||
|
if (vm.auditForm.status === 'reject') {
|
||||||
|
if (!value || !value.trim()) {
|
||||||
|
callback(new Error('拒绝时审核备注不能为空'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (value.length > 255) {
|
||||||
|
callback(new Error('审核备注不能超过255字符'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (value && value.length > 255) {
|
||||||
|
callback(new Error('审核备注不能超过255字符'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
callback();
|
||||||
|
}, trigger: 'blur' }
|
||||||
|
]
|
||||||
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
@@ -205,6 +265,49 @@ export default {
|
|||||||
this.userModel = item;
|
this.userModel = item;
|
||||||
this.open_add = true;
|
this.open_add = true;
|
||||||
},
|
},
|
||||||
|
/*打开审核弹窗*/
|
||||||
|
openAuditDialog(row) {
|
||||||
|
this.auditForm.id = row.id;
|
||||||
|
this.auditForm.status = '';
|
||||||
|
this.auditForm.remark = '';
|
||||||
|
this.auditTargetRow = row;
|
||||||
|
this.auditDialogVisible = true;
|
||||||
|
},
|
||||||
|
/*审核弹窗关闭重置*/
|
||||||
|
onAuditDialogClose() {
|
||||||
|
this.auditDialogVisible = false;
|
||||||
|
this.$refs.auditFormRef && this.$refs.auditFormRef.resetFields();
|
||||||
|
},
|
||||||
|
/*提交审核*/
|
||||||
|
submitAudit() {
|
||||||
|
this.$refs.auditFormRef.validate((valid) => {
|
||||||
|
if (!valid) return;
|
||||||
|
let self = this;
|
||||||
|
// 拒绝时备注二次校验
|
||||||
|
if (self.auditForm.status === 'reject' && (!self.auditForm.remark || !self.auditForm.remark.trim())) {
|
||||||
|
self.$message.warning('请填写审核备注');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.auditLoading = true;
|
||||||
|
assetApi.gameAssetAudit({
|
||||||
|
id: self.auditForm.id,
|
||||||
|
status: self.auditForm.status,
|
||||||
|
remark: self.auditForm.remark
|
||||||
|
}, true).then(data => {
|
||||||
|
//响应拦截器保证此处 code 必为 1
|
||||||
|
self.auditLoading = false;
|
||||||
|
self.$message({
|
||||||
|
message: '审核提交成功',
|
||||||
|
type: 'success'
|
||||||
|
});
|
||||||
|
self.auditDialogVisible = false;
|
||||||
|
self.getData();
|
||||||
|
}).catch(error => {
|
||||||
|
//错误提示已由响应拦截器统一弹出
|
||||||
|
self.auditLoading = false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
/*打开编辑*/
|
/*打开编辑*/
|
||||||
editClick(row, e) {
|
editClick(row, e) {
|
||||||
this.$router.push({
|
this.$router.push({
|
||||||
|
|||||||
@@ -21,10 +21,10 @@
|
|||||||
<div class="table-wrap">
|
<div class="table-wrap">
|
||||||
<el-table size="small" :data="tableData" border style="width: 100%" v-loading="loading">
|
<el-table size="small" :data="tableData" border style="width: 100%" v-loading="loading">
|
||||||
<el-table-column prop="id" label="ID" width="80"></el-table-column>
|
<el-table-column prop="id" label="ID" width="80"></el-table-column>
|
||||||
<el-table-column prop="finance_info.contact_name" label="联系人" width="200"></el-table-column>
|
<el-table-column prop="finance_info.contact_name" label="联系人" width="100"></el-table-column>
|
||||||
<el-table-column prop="finance_info.contact_way" label="联系方式" width="200"></el-table-column>
|
<el-table-column prop="finance_info.contact_way" label="联系方式" width="100"></el-table-column>
|
||||||
<el-table-column prop="company_info.company_name" label="企业名称" width="200"></el-table-column>
|
<el-table-column prop="company_info.company_name" label="企业名称" width="200"></el-table-column>
|
||||||
<el-table-column prop="company_info.company_type" label="企业性质" width="120">
|
<el-table-column prop="company_info.company_type" label="企业性质" width="100">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<span v-if="scope.row.company_info.company_type == 'develop'">研发</span>
|
<span v-if="scope.row.company_info.company_type == 'develop'">研发</span>
|
||||||
<span v-if="scope.row.company_info.company_type == 'publish'">发行</span>
|
<span v-if="scope.row.company_info.company_type == 'publish'">发行</span>
|
||||||
@@ -41,17 +41,33 @@
|
|||||||
<span >{{scope.row.finance_info.fund_amount}}万元</span>
|
<span >{{scope.row.finance_info.fund_amount}}万元</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="status" label="状态" width="120">
|
<el-table-column prop="status" label="状态" width="100">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<span v-if="scope.row.status == 'processed'" class="green">已处理</span>
|
<span v-if="scope.row.status == 'processed'" class="green">已处理</span>
|
||||||
<span v-if="scope.row.status == 'pending'" class="gray">待处理</span>
|
<span v-if="scope.row.status == 'pending'" class="gray">待处理</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<el-table-column prop="satisfaction" label="满意度" width="100">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span v-if="scope.row.satisfaction === '5'">非常满意</span>
|
||||||
|
<span v-else-if="scope.row.satisfaction === '4'">满意</span>
|
||||||
|
<span v-else-if="scope.row.satisfaction === '3'">良好</span>
|
||||||
|
<span v-else-if="scope.row.satisfaction === '2'">不满意</span>
|
||||||
|
<span v-else-if="scope.row.satisfaction === '1'">非常不满意</span>
|
||||||
|
<span v-else>--</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column prop="post_time" label="申请时间" width="140"></el-table-column>
|
<el-table-column prop="post_time" label="申请时间" width="140"></el-table-column>
|
||||||
|
<el-table-column prop="update_time" label="处理时间" width="140">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span v-if="scope.row.update_time === scope.row.post_time">--</span>
|
||||||
|
<span v-else>{{scope.row.update_time}}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="操作">
|
<el-table-column label="操作">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-button @click="editClick(scope.row)" type="text" size="small" v-auth="'/hyt/apply/detail'">详情</el-button>
|
<el-button @click="editClick(scope.row)" type="text" size="small" v-auth="'/hyt/apply/detail'">详情</el-button>
|
||||||
<el-button @click="processClick(scope.row)" type="text" size="small" v-auth="'/hyt/apply/process'" v-if="scope.row.status=='pending'">处理</el-button>
|
<el-button @click="openProcessDialog(scope.row)" type="text" size="small" v-auth="'/hyt/apply/process'" v-if="scope.row.status=='pending'">处理</el-button>
|
||||||
<el-button @click="deleteClick(scope.row)" type="text" size="small" v-auth="'/hyt/apply/delete'">删除</el-button>
|
<el-button @click="deleteClick(scope.row)" type="text" size="small" v-auth="'/hyt/apply/delete'">删除</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@@ -71,6 +87,30 @@
|
|||||||
></el-pagination>
|
></el-pagination>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 处理满意度弹窗 -->
|
||||||
|
<el-dialog
|
||||||
|
title="处理申请"
|
||||||
|
:visible.sync="processDialogVisible"
|
||||||
|
width="400px"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
>
|
||||||
|
<el-form :model="processForm" label-width="80px">
|
||||||
|
<el-form-item label="满意度" required>
|
||||||
|
<el-select v-model="processForm.satisfaction" placeholder="请选择满意度" style="width: 100%;">
|
||||||
|
<el-option label="非常满意" value="5"></el-option>
|
||||||
|
<el-option label="满意" value="4"></el-option>
|
||||||
|
<el-option label="良好" value="3"></el-option>
|
||||||
|
<el-option label="不满意" value="2"></el-option>
|
||||||
|
<el-option label="非常不满意" value="1"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="processDialogVisible = false">取 消</el-button>
|
||||||
|
<el-button type="primary" :loading="processDialogLoading" @click="processSubmit">确 定</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -101,7 +141,14 @@ export default {
|
|||||||
/*是否打开编辑弹窗*/
|
/*是否打开编辑弹窗*/
|
||||||
open_edit: false,
|
open_edit: false,
|
||||||
/*当前编辑的对象*/
|
/*当前编辑的对象*/
|
||||||
userModel: {}
|
userModel: {},
|
||||||
|
// 处理申请弹窗数据
|
||||||
|
processDialogVisible: false,
|
||||||
|
processDialogLoading: false,
|
||||||
|
processForm: {
|
||||||
|
id: null,
|
||||||
|
satisfaction: ''
|
||||||
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
@@ -168,7 +215,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.tableData = list;
|
this.tableData = list;
|
||||||
console.log(list);
|
// console.log(list);
|
||||||
this.totalDataNumber = total;
|
this.totalDataNumber = total;
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
})
|
})
|
||||||
@@ -217,25 +264,42 @@ export default {
|
|||||||
.catch(() => {});
|
.catch(() => {});
|
||||||
},
|
},
|
||||||
|
|
||||||
/*删除*/
|
// 打开处理申请弹窗
|
||||||
processClick(row) {
|
openProcessDialog(row) {
|
||||||
this.$confirm('您确定要处理该申请吗? 是否继续?', '提示', {
|
this.processForm = {
|
||||||
confirmButtonText: '确定',
|
id: row.id,
|
||||||
cancelButtonText: '取消',
|
satisfaction: ''
|
||||||
type: 'warning'
|
};
|
||||||
})
|
this.processDialogVisible = true;
|
||||||
.then(() => {
|
this.processDialogLoading = false;
|
||||||
hytApi.processApply({ id: row.id }, true)
|
},
|
||||||
.then(data => {
|
|
||||||
this.$message({
|
// 处理申请,带满意度
|
||||||
message: '恭喜你,处理成功',
|
processSubmit() {
|
||||||
type: 'success'
|
if (!this.processForm.satisfaction) {
|
||||||
});
|
this.$message.warning('请选择满意度');
|
||||||
this.getTableList();
|
return;
|
||||||
})
|
}
|
||||||
.catch(error => {});
|
this.processDialogLoading = true;
|
||||||
|
hytApi.processApply(
|
||||||
|
{
|
||||||
|
id: this.processForm.id,
|
||||||
|
satisfaction: this.processForm.satisfaction
|
||||||
|
},
|
||||||
|
true
|
||||||
|
)
|
||||||
|
.then(data => {
|
||||||
|
this.$message({
|
||||||
|
message: '恭喜你,处理成功',
|
||||||
|
type: 'success'
|
||||||
|
});
|
||||||
|
this.processDialogVisible = false;
|
||||||
|
this.getTableList();
|
||||||
})
|
})
|
||||||
.catch(() => {});
|
.catch(error => {})
|
||||||
|
.finally(() => {
|
||||||
|
this.processDialogLoading = false;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -50,6 +50,7 @@
|
|||||||
getCookie,
|
getCookie,
|
||||||
deleteSessionStorage
|
deleteSessionStorage
|
||||||
} from '@/utils/base.js';
|
} from '@/utils/base.js';
|
||||||
|
import { setCsrfToken } from '@/utils/csrf';
|
||||||
import store from '@/store';
|
import store from '@/store';
|
||||||
import UpdatePassword from './part/UpdatePassword.vue';
|
import UpdatePassword from './part/UpdatePassword.vue';
|
||||||
export default {
|
export default {
|
||||||
@@ -115,6 +116,10 @@
|
|||||||
delCookie('isLogin');
|
delCookie('isLogin');
|
||||||
/*删除基本信息*/
|
/*删除基本信息*/
|
||||||
delCookie('baseInfo');
|
delCookie('baseInfo');
|
||||||
|
/*删除个人信息*/
|
||||||
|
delCookie('userinfo');
|
||||||
|
/*清空内存中的 csrf token */
|
||||||
|
setCsrfToken('');
|
||||||
/*删除用户菜单*/
|
/*删除用户菜单*/
|
||||||
deleteSessionStorage('rolelist');
|
deleteSessionStorage('rolelist');
|
||||||
/*删除用户权限*/
|
/*删除用户权限*/
|
||||||
|
|||||||
+52
-6
@@ -11,6 +11,26 @@
|
|||||||
<el-form-item prop="account"><el-input type="text" v-model="ruleForm.account" auto-complete="off" placeholder="账号"></el-input></el-form-item>
|
<el-form-item prop="account"><el-input type="text" v-model="ruleForm.account" auto-complete="off" placeholder="账号"></el-input></el-form-item>
|
||||||
<!--密码-->
|
<!--密码-->
|
||||||
<el-form-item prop="checkPass"><el-input type="password" v-model="ruleForm.checkPass" auto-complete="off" placeholder="密码"></el-input></el-form-item>
|
<el-form-item prop="checkPass"><el-input type="password" v-model="ruleForm.checkPass" auto-complete="off" placeholder="密码"></el-input></el-form-item>
|
||||||
|
<!-- 图形验证码 -->
|
||||||
|
<el-form-item prop="captcha">
|
||||||
|
<div style="display: flex; align-items: center;">
|
||||||
|
<el-input
|
||||||
|
type="text"
|
||||||
|
v-model="ruleForm.captcha"
|
||||||
|
auto-complete="off"
|
||||||
|
placeholder="验证码"
|
||||||
|
style="flex: 1;"
|
||||||
|
></el-input>
|
||||||
|
<el-image
|
||||||
|
v-if="code_img"
|
||||||
|
:src="code_img"
|
||||||
|
style="width: 100px; height: 40px; margin-left: 10px; cursor: pointer; flex-shrink: 0;"
|
||||||
|
@click="getCaptcha"
|
||||||
|
alt="点击刷新验证码"
|
||||||
|
title="点击刷新验证码"
|
||||||
|
></el-image>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
<!--登录-->
|
<!--登录-->
|
||||||
<el-form-item><el-button type="primary" style="width:100%;" @click.native.prevent="SubmitFunc" :loading="logining">登录</el-button></el-form-item>
|
<el-form-item><el-button type="primary" style="width:100%;" @click.native.prevent="SubmitFunc" :loading="logining">登录</el-button></el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
@@ -22,6 +42,7 @@ import IndexApi from '@/api/index.js';
|
|||||||
// import bgimg from '@/assets/img/login_bg.jpg';
|
// import bgimg from '@/assets/img/login_bg.jpg';
|
||||||
import UserApi from '@/api/user.js';
|
import UserApi from '@/api/user.js';
|
||||||
import { setCookie, delCookie, setSessionStorage } from '@/utils/base.js';
|
import { setCookie, delCookie, setSessionStorage } from '@/utils/base.js';
|
||||||
|
import { fetchCsrfToken, setCsrfToken } from '@/utils/csrf.js';
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -36,14 +57,17 @@ export default {
|
|||||||
/*用户名*/
|
/*用户名*/
|
||||||
account: '',
|
account: '',
|
||||||
/*密码*/
|
/*密码*/
|
||||||
checkPass: ''
|
checkPass: '',
|
||||||
|
captcha:''
|
||||||
},
|
},
|
||||||
|
code_img:'',
|
||||||
/*验证规则*/
|
/*验证规则*/
|
||||||
rules: {
|
rules: {
|
||||||
/*用户名*/
|
/*用户名*/
|
||||||
account: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
|
account: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
|
||||||
/*密码*/
|
/*密码*/
|
||||||
checkPass: [{ required: true, message: '请输入密码', trigger: 'blur' }]
|
checkPass: [{ required: true, message: '请输入密码', trigger: 'blur' }],
|
||||||
|
captcha: [{ required: true, message: '请输入验证码', trigger: 'blur' }]
|
||||||
},
|
},
|
||||||
/*基础配置*/
|
/*基础配置*/
|
||||||
baseData: {}
|
baseData: {}
|
||||||
@@ -51,6 +75,14 @@ export default {
|
|||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
//this.getData();
|
//this.getData();
|
||||||
|
//清除缓存
|
||||||
|
delCookie('baseInfo');
|
||||||
|
/*保存个人信息*/
|
||||||
|
delCookie('userinfo');
|
||||||
|
/*设置一个登录状态*/
|
||||||
|
delCookie('isLogin');
|
||||||
|
setCsrfToken('');
|
||||||
|
this.getCaptcha();
|
||||||
if(this.$route.query.from&&this.$route.query.from=='admin'){
|
if(this.$route.query.from&&this.$route.query.from=='admin'){
|
||||||
this.saasLogin();
|
this.saasLogin();
|
||||||
}
|
}
|
||||||
@@ -69,6 +101,15 @@ export default {
|
|||||||
self.loading = false;
|
self.loading = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
getCaptcha() {
|
||||||
|
IndexApi.getCaptcha(true)
|
||||||
|
.then(res => {
|
||||||
|
console.log(res,222222);
|
||||||
|
this.code_img = res.data.img;
|
||||||
|
})
|
||||||
|
.catch(error => {});
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
/*登录方法*/
|
/*登录方法*/
|
||||||
SubmitFunc(ev) {
|
SubmitFunc(ev) {
|
||||||
@@ -78,19 +119,22 @@ export default {
|
|||||||
this.logining = true;
|
this.logining = true;
|
||||||
var Params = {
|
var Params = {
|
||||||
username: this.ruleForm.account,
|
username: this.ruleForm.account,
|
||||||
password: this.ruleForm.checkPass
|
password: this.ruleForm.checkPass,
|
||||||
|
captcha: this.ruleForm.captcha
|
||||||
};
|
};
|
||||||
/*调用登录接口*/
|
/*调用登录接口*/
|
||||||
UserApi.login(Params, true)
|
UserApi.login(Params, true)
|
||||||
.then(res => {
|
.then(async res => {
|
||||||
this.logining = false;
|
this.logining = false;
|
||||||
if (res.code == 1) {
|
if (res.code == 1) {
|
||||||
/*保存个人信息*/
|
/*保存个人信息*/
|
||||||
setCookie('userinfo', res.data);
|
setCookie('userinfo', res.data);
|
||||||
/*设置一个登录状态*/
|
/*设置一个登录状态*/
|
||||||
setCookie('isLogin', true);
|
setCookie('isLogin', true);
|
||||||
|
await fetchCsrfToken(true);
|
||||||
/*跳转到首页*/
|
/*跳转到首页*/
|
||||||
this.$router.push({ path: '/' });
|
this.$router.push({ path: '/' });
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
this.$message({
|
this.$message({
|
||||||
message: '登录失败',
|
message: '登录失败',
|
||||||
@@ -101,6 +145,8 @@ export default {
|
|||||||
.catch(error => {
|
.catch(error => {
|
||||||
//接口调用方法统一处理
|
//接口调用方法统一处理
|
||||||
this.logining = false;
|
this.logining = false;
|
||||||
|
this.ruleForm.captcha = '';
|
||||||
|
this.getCaptcha();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -108,14 +154,14 @@ export default {
|
|||||||
saasLogin(){
|
saasLogin(){
|
||||||
/*调用登录接口*/
|
/*调用登录接口*/
|
||||||
UserApi.saasLogin({}, true)
|
UserApi.saasLogin({}, true)
|
||||||
.then(res => {
|
.then(async res => {
|
||||||
console.log(res);
|
|
||||||
if (res.code == 1) {
|
if (res.code == 1) {
|
||||||
delCookie('baseInfo');
|
delCookie('baseInfo');
|
||||||
/*保存个人信息*/
|
/*保存个人信息*/
|
||||||
setCookie('userinfo', res.data);
|
setCookie('userinfo', res.data);
|
||||||
/*设置一个登录状态*/
|
/*设置一个登录状态*/
|
||||||
setCookie('isLogin', true);
|
setCookie('isLogin', true);
|
||||||
|
await fetchCsrfToken(true);
|
||||||
/*跳转到首页*/
|
/*跳转到首页*/
|
||||||
this.$router.push({ path: '/' });
|
this.$router.push({ path: '/' });
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user