mirror of
https://gitee.com/hhyykk/ipms-sjy.git
synced 2025-07-10 17:15:07 +08:00
Merge branch 'master' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into feature/1.8.0-uniapp
Conflicts: yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileController.java yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/UserController.java yudao-ui-admin/src/components/ImageUpload/index.vue
This commit is contained in:
@ -1,4 +1,6 @@
|
||||
import request from '@/utils/request'
|
||||
import {getRefreshToken} from "@/utils/auth";
|
||||
import service from "@/utils/request";
|
||||
|
||||
// 登录方法
|
||||
export function login(username, password, code, uuid) {
|
||||
@ -26,7 +28,7 @@ export function getInfo() {
|
||||
// 退出方法
|
||||
export function logout() {
|
||||
return request({
|
||||
url: '/system/logout',
|
||||
url: '/system/auth/logout',
|
||||
method: 'post'
|
||||
})
|
||||
}
|
||||
@ -99,3 +101,48 @@ export function smsLogin(mobile, code) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 刷新访问令牌
|
||||
export function refreshToken() {
|
||||
return service({
|
||||
url: '/system/auth/refresh-token?refreshToken=' + getRefreshToken(),
|
||||
method: 'post'
|
||||
})
|
||||
}
|
||||
|
||||
// ========== OAUTH 2.0 相关 ==========
|
||||
|
||||
export function getAuthorize(clientId) {
|
||||
return request({
|
||||
url: '/system/oauth2/authorize?clientId=' + clientId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function authorize(responseType, clientId, redirectUri, state,
|
||||
autoApprove, checkedScopes, uncheckedScopes) {
|
||||
// 构建 scopes
|
||||
const scopes = {};
|
||||
for (const scope of checkedScopes) {
|
||||
scopes[scope] = true;
|
||||
}
|
||||
for (const scope of uncheckedScopes) {
|
||||
scopes[scope] = false;
|
||||
}
|
||||
// 发起请求
|
||||
return service({
|
||||
url: '/system/oauth2/authorize',
|
||||
headers:{
|
||||
'Content-type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
params: {
|
||||
response_type: responseType,
|
||||
client_id: clientId,
|
||||
redirect_uri: redirectUri,
|
||||
state: state,
|
||||
auto_approve: autoApprove,
|
||||
scope: JSON.stringify(scopes)
|
||||
},
|
||||
method: 'post'
|
||||
})
|
||||
}
|
||||
|
44
yudao-ui-admin/src/api/system/oauth2/oauth2Client.js
Executable file
44
yudao-ui-admin/src/api/system/oauth2/oauth2Client.js
Executable file
@ -0,0 +1,44 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 创建 OAuth2 客户端
|
||||
export function createOAuth2Client(data) {
|
||||
return request({
|
||||
url: '/system/oauth2-client/create',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 更新 OAuth2 客户端
|
||||
export function updateOAuth2Client(data) {
|
||||
return request({
|
||||
url: '/system/oauth2-client/update',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除 OAuth2 客户端
|
||||
export function deleteOAuth2Client(id) {
|
||||
return request({
|
||||
url: '/system/oauth2-client/delete?id=' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 获得 OAuth2 客户端
|
||||
export function getOAuth2Client(id) {
|
||||
return request({
|
||||
url: '/system/oauth2-client/get?id=' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 获得 OAuth2 客户端分页
|
||||
export function getOAuth2ClientPage(query) {
|
||||
return request({
|
||||
url: '/system/oauth2-client/page',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
18
yudao-ui-admin/src/api/system/oauth2/oauth2Token.js
Normal file
18
yudao-ui-admin/src/api/system/oauth2/oauth2Token.js
Normal file
@ -0,0 +1,18 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 获得访问令牌分页
|
||||
export function getAccessTokenPage(query) {
|
||||
return request({
|
||||
url: '/system/oauth2-token/page',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 删除访问令牌
|
||||
export function deleteAccessToken(accessToken) {
|
||||
return request({
|
||||
url: '/system/oauth2-token/delete?accessToken=' + accessToken,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询在线用户列表
|
||||
export function list(query) {
|
||||
return request({
|
||||
url: '/system/user-session/page',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 强退用户
|
||||
export function forceLogout(tokenId) {
|
||||
return request({
|
||||
url: '/system/user-session/delete?id=' + tokenId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-upload
|
||||
:action="uploadUrl"
|
||||
:action="uploadFileUrl"
|
||||
:before-upload="handleBeforeUpload"
|
||||
:on-success="handleUploadSuccess"
|
||||
:on-error="handleUploadError"
|
||||
@ -10,7 +10,7 @@
|
||||
:headers="headers"
|
||||
style="display: none"
|
||||
ref="upload"
|
||||
v-if="this.type == 'url'"
|
||||
v-if="this.type === 'url'"
|
||||
>
|
||||
</el-upload>
|
||||
<div class="editor" ref="editor" :style="styles"></div>
|
||||
@ -22,7 +22,7 @@ import Quill from "quill";
|
||||
import "quill/dist/quill.core.css";
|
||||
import "quill/dist/quill.snow.css";
|
||||
import "quill/dist/quill.bubble.css";
|
||||
import { getToken } from "@/utils/auth";
|
||||
import { getAccessToken } from "@/utils/auth";
|
||||
|
||||
export default {
|
||||
name: "Editor",
|
||||
@ -60,10 +60,8 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
uploadUrl: process.env.VUE_APP_BASE_API + "/common/upload", // 上传的图片服务器地址
|
||||
headers: {
|
||||
Authorization: "Bearer " + getToken()
|
||||
},
|
||||
uploadFileUrl: process.env.VUE_APP_BASE_API + "/admin-api/infra/file/upload", // 请求地址
|
||||
headers: { Authorization: "Bearer " + getAccessToken() }, // 设置上传的请求头部
|
||||
Quill: null,
|
||||
currentValue: "",
|
||||
options: {
|
||||
@ -126,7 +124,7 @@ export default {
|
||||
const editor = this.$refs.editor;
|
||||
this.Quill = new Quill(editor, this.options);
|
||||
// 如果设置了上传地址则自定义图片上传事件
|
||||
if (this.type == 'url') {
|
||||
if (this.type === 'url') {
|
||||
let toolbar = this.Quill.getModule("toolbar");
|
||||
toolbar.addHandler("image", (value) => {
|
||||
this.uploadType = "image";
|
||||
@ -172,11 +170,13 @@ export default {
|
||||
// 获取富文本组件实例
|
||||
let quill = this.Quill;
|
||||
// 如果上传成功
|
||||
if (res.code == 200) {
|
||||
// edit by 芋道源码
|
||||
if (res.code === 200 || res.code === 0) {
|
||||
// 获取光标所在位置
|
||||
let length = quill.getSelection().index;
|
||||
// 插入图片 res.url为服务器返回的图片地址
|
||||
quill.insertEmbed(length, "image", process.env.VUE_APP_BASE_API + res.fileName);
|
||||
// edit by 芋道源码
|
||||
quill.insertEmbed(length, "image", res.data);
|
||||
// 调整光标到最后
|
||||
quill.setSelection(length + 1);
|
||||
} else {
|
||||
|
@ -28,7 +28,7 @@
|
||||
<!-- 文件列表 -->
|
||||
<transition-group class="upload-file-list el-upload-list el-upload-list--text" name="el-fade-in-linear" tag="ul">
|
||||
<li :key="file.url" class="el-upload-list__item ele-upload-list__item-content" v-for="(file, index) in fileList">
|
||||
<el-link :href="`${baseUrl}${file.url}`" :underline="false" target="_blank">
|
||||
<el-link :href="`${file.url}`" :underline="false" target="_blank">
|
||||
<span class="el-icon-document"> {{ getFileName(file.name) }} </span>
|
||||
</el-link>
|
||||
<div class="ele-upload-list__item-content-action">
|
||||
@ -40,7 +40,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getToken } from "@/utils/auth";
|
||||
import { getAccessToken } from "@/utils/auth";
|
||||
|
||||
export default {
|
||||
name: "FileUpload",
|
||||
@ -72,11 +72,8 @@ export default {
|
||||
return {
|
||||
number: 0,
|
||||
uploadList: [],
|
||||
baseUrl: process.env.VUE_APP_BASE_API,
|
||||
uploadFileUrl: process.env.VUE_APP_BASE_API + "/common/upload", // 上传的图片服务器地址
|
||||
headers: {
|
||||
Authorization: "Bearer " + getToken(),
|
||||
},
|
||||
uploadFileUrl: process.env.VUE_APP_BASE_API + "/admin-api/infra/file/upload", // 请求地址
|
||||
headers: { Authorization: "Bearer " + getAccessToken() }, // 设置上传的请求头部
|
||||
fileList: [],
|
||||
};
|
||||
},
|
||||
@ -121,8 +118,7 @@ export default {
|
||||
}
|
||||
const isTypeOk = this.fileType.some((type) => {
|
||||
if (file.type.indexOf(type) > -1) return true;
|
||||
if (fileExtension && fileExtension.indexOf(type) > -1) return true;
|
||||
return false;
|
||||
return !!(fileExtension && fileExtension.indexOf(type) > -1);
|
||||
});
|
||||
if (!isTypeOk) {
|
||||
this.$modal.msgError(`文件格式不正确, 请上传${this.fileType.join("/")}格式文件!`);
|
||||
@ -152,7 +148,8 @@ export default {
|
||||
},
|
||||
// 上传成功回调
|
||||
handleUploadSuccess(res) {
|
||||
this.uploadList.push({ name: res.fileName, url: res.fileName });
|
||||
// edit by 芋道源码
|
||||
this.uploadList.push({ name: res.data, url: res.data });
|
||||
if (this.uploadList.length === this.number) {
|
||||
this.fileList = this.fileList.concat(this.uploadList);
|
||||
this.uploadList = [];
|
||||
@ -181,7 +178,7 @@ export default {
|
||||
for (let i in list) {
|
||||
strs += list[i].url + separator;
|
||||
}
|
||||
return strs != '' ? strs.substr(0, strs.length - 1) : '';
|
||||
return strs !== '' ? strs.substr(0, strs.length - 1) : '';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,21 +1,21 @@
|
||||
<template>
|
||||
<div class="component-upload-image">
|
||||
<el-upload
|
||||
multiple
|
||||
:action="uploadImgUrl"
|
||||
list-type="picture-card"
|
||||
:on-success="handleUploadSuccess"
|
||||
:before-upload="handleBeforeUpload"
|
||||
:limit="limit"
|
||||
:on-error="handleUploadError"
|
||||
:on-exceed="handleExceed"
|
||||
name="file"
|
||||
:on-remove="handleRemove"
|
||||
:show-file-list="true"
|
||||
:headers="headers"
|
||||
:file-list="fileList"
|
||||
:on-preview="handlePictureCardPreview"
|
||||
:class="{hide: this.fileList.length >= this.limit}"
|
||||
multiple
|
||||
:action="uploadFileUrl"
|
||||
list-type="picture-card"
|
||||
:on-success="handleUploadSuccess"
|
||||
:before-upload="handleBeforeUpload"
|
||||
:limit="limit"
|
||||
:on-error="handleUploadError"
|
||||
:on-exceed="handleExceed"
|
||||
name="file"
|
||||
:on-remove="handleRemove"
|
||||
:show-file-list="true"
|
||||
:headers="headers"
|
||||
:file-list="fileList"
|
||||
:on-preview="handlePictureCardPreview"
|
||||
:class="{hide: this.fileList.length >= this.limit}"
|
||||
>
|
||||
<i class="el-icon-plus"></i>
|
||||
</el-upload>
|
||||
@ -29,21 +29,21 @@
|
||||
</div>
|
||||
|
||||
<el-dialog
|
||||
:visible.sync="dialogVisible"
|
||||
title="预览"
|
||||
width="800"
|
||||
append-to-body
|
||||
:visible.sync="dialogVisible"
|
||||
title="预览"
|
||||
width="800"
|
||||
append-to-body
|
||||
>
|
||||
<img
|
||||
:src="dialogImageUrl"
|
||||
style="display: block; max-width: 100%; margin: 0 auto"
|
||||
:src="dialogImageUrl"
|
||||
style="display: block; max-width: 100%; margin: 0 auto"
|
||||
/>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {getToken} from "@/utils/auth";
|
||||
import { getAccessToken } from "@/utils/auth";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
@ -76,12 +76,8 @@ export default {
|
||||
dialogImageUrl: "",
|
||||
dialogVisible: false,
|
||||
hideUpload: false,
|
||||
// todo /infra/file/upload 返回的都是带 host 的可访问地址, baseUrl 有点没必要
|
||||
baseUrl: process.env.VUE_APP_BASE_API,
|
||||
uploadImgUrl: process.env.VUE_APP_BASE_API + "/admin-api/infra/file/upload", // 上传的图片服务器地址
|
||||
headers: {
|
||||
Authorization: "Bearer " + getToken(),
|
||||
},
|
||||
uploadFileUrl: process.env.VUE_APP_BASE_API + "/admin-api/infra/file/upload", // 请求地址
|
||||
headers: { Authorization: "Bearer " + getAccessToken() }, // 设置上传的请求头部
|
||||
fileList: []
|
||||
};
|
||||
},
|
||||
@ -94,12 +90,8 @@ export default {
|
||||
// 然后将数组转为对象数组
|
||||
this.fileList = list.map(item => {
|
||||
if (typeof item === "string") {
|
||||
// 不带有 http 的路径, 才拼接 baseUrl.
|
||||
if (item.indexOf("http") === -1) {
|
||||
item = {name: this.baseUrl + item, url: this.baseUrl + item};
|
||||
} else {
|
||||
item = {name: item, url: item};
|
||||
}
|
||||
// edit by 芋道源码
|
||||
item = { name: item, url: item };
|
||||
}
|
||||
return item;
|
||||
});
|
||||
@ -129,7 +121,8 @@ export default {
|
||||
},
|
||||
// 上传成功回调
|
||||
handleUploadSuccess(res) {
|
||||
this.uploadList.push({name: res.data.fileName, url: res.data.fileUrl});
|
||||
// edit by 芋道源码
|
||||
this.uploadList.push({ name: res.data, url: res.data });
|
||||
if (this.uploadList.length === this.number) {
|
||||
this.fileList = this.fileList.concat(this.uploadList);
|
||||
this.uploadList = [];
|
||||
@ -188,10 +181,9 @@ export default {
|
||||
let strs = "";
|
||||
separator = separator || ",";
|
||||
for (let i in list) {
|
||||
// 由于 infra-file 返回带有 host , 不需要替换 baseUrl // .replace(this.baseUrl, "")
|
||||
strs += list[i].url + separator;
|
||||
strs += list[i].url.replace(this.baseUrl, "") + separator;
|
||||
}
|
||||
return strs != '' ? strs.substr(0, strs.length - 1) : '';
|
||||
return strs !== '' ? strs.substr(0, strs.length - 1) : '';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,68 +0,0 @@
|
||||
<template>
|
||||
<div class="component-upload-image">
|
||||
<el-upload
|
||||
:action="uploadImgUrl"
|
||||
list-type="picture-card"
|
||||
:on-success="handleUploadSuccess"
|
||||
:before-upload="handleBeforeUpload"
|
||||
:on-error="handleUploadError"
|
||||
name="file"
|
||||
:show-file-list="false"
|
||||
:headers="headers"
|
||||
style="display: inline-block; vertical-align: top"
|
||||
>
|
||||
<img v-if="value" :src="value" class="avatar" />
|
||||
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
|
||||
</el-upload>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getToken } from "@/utils/auth";
|
||||
|
||||
export default {
|
||||
components: {},
|
||||
data() {
|
||||
return {
|
||||
uploadImgUrl: process.env.VUE_APP_BASE_API + "/common/upload", // 上传的图片服务器地址
|
||||
headers: {
|
||||
Authorization: "Bearer " + getToken(),
|
||||
},
|
||||
};
|
||||
},
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleUploadSuccess(res) {
|
||||
this.$emit("input", res.url);
|
||||
this.loading.close();
|
||||
},
|
||||
handleBeforeUpload() {
|
||||
this.loading = this.$loading({
|
||||
lock: true,
|
||||
text: "上传中",
|
||||
background: "rgba(0, 0, 0, 0.7)",
|
||||
});
|
||||
},
|
||||
handleUploadError() {
|
||||
this.$message({
|
||||
type: "error",
|
||||
message: "上传失败",
|
||||
});
|
||||
this.loading.close();
|
||||
},
|
||||
},
|
||||
watch: {},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.avatar {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
@ -499,7 +499,8 @@ export const selectComponents = [
|
||||
__slot__: {
|
||||
'list-type': true
|
||||
},
|
||||
action: 'https://jsonplaceholder.typicode.com/posts/',
|
||||
// action: process.env.VUE_APP_BASE_API + "/admin-api/infra/file/upload", // 请求地址
|
||||
action: '/infra/file/upload', // 请求地址
|
||||
disabled: false,
|
||||
accept: '',
|
||||
name: 'file',
|
||||
|
@ -1,6 +1,7 @@
|
||||
<script>
|
||||
import { deepClone } from '@/utils/index'
|
||||
import render from '@/components/render/render.js'
|
||||
import {getAccessToken} from "@/utils/auth";
|
||||
|
||||
const ruleTrigger = {
|
||||
'el-input': 'blur',
|
||||
@ -79,10 +80,51 @@ function formBtns(h) {
|
||||
}
|
||||
|
||||
function renderFormItem(h, elementList) {
|
||||
const that = this
|
||||
const data = this[this.formConf.formModel]
|
||||
// const formRef = that.$refs[that.formConf.formRef] // 这里直接添加有问题,此时还找不到表单 $refs
|
||||
return elementList.map(scheme => {
|
||||
const config = scheme.__config__
|
||||
const layout = layouts[config.layout]
|
||||
|
||||
// edit by 芋道源码,解决 el-upload 上传的问题
|
||||
// 参考 https://github.com/JakHuang/form-generator/blob/master/src/components/parser/example/Index.vue 实现
|
||||
const vModel = scheme.__vModel__
|
||||
const val = data[vModel]
|
||||
if (scheme.__config__.tag === 'el-upload') {
|
||||
// 回显图片
|
||||
scheme['file-list'] = (val || []).map(url => ({ name: url, url }))
|
||||
// 上传地址 + 请求头
|
||||
scheme.action = process.env.VUE_APP_BASE_API + "/admin-api/infra/file/upload"
|
||||
scheme.headers = { Authorization: "Bearer " + getAccessToken() }
|
||||
// 注意 on-success 不能绑定箭头函数!!!
|
||||
scheme['on-success'] = function (response, file, fileList) {
|
||||
if (response.code !== 0) {
|
||||
return;
|
||||
}
|
||||
// 添加到 data 中
|
||||
const prev = data[vModel] || []
|
||||
this.$set(data, vModel, [
|
||||
...prev,
|
||||
response.data
|
||||
])
|
||||
// 发起表单校验
|
||||
that.$refs[that.formConf.formRef].validateField(vModel)
|
||||
}
|
||||
// 注意 on-remove 不能绑定箭头函数!!!
|
||||
scheme['on-remove'] = function (file, fileList) {
|
||||
// 移除从 data 中
|
||||
const prev = data[vModel] || []
|
||||
const index = prev.indexOf(file.response.data)
|
||||
if (index === -1) {
|
||||
return
|
||||
}
|
||||
prev.splice(index, 1) // 直接移除即可,无需重复 set,因为 array 是引用
|
||||
// 发起表单校验
|
||||
that.$refs[that.formConf.formRef].validateField(vModel)
|
||||
}
|
||||
}
|
||||
|
||||
if (layout) {
|
||||
return layout.call(this, h, scheme)
|
||||
}
|
||||
|
@ -152,31 +152,24 @@ export default {
|
||||
})
|
||||
},
|
||||
refreshSelectedTag(view) {
|
||||
this.$store.dispatch('tagsView/delCachedView', view).then(() => {
|
||||
const { fullPath } = view
|
||||
this.$nextTick(() => {
|
||||
this.$router.replace({
|
||||
path: '/redirect' + fullPath
|
||||
})
|
||||
})
|
||||
})
|
||||
this.$tab.refreshPage(view);
|
||||
},
|
||||
closeSelectedTag(view) {
|
||||
this.$store.dispatch('tagsView/delView', view).then(({ visitedViews }) => {
|
||||
this.$tab.closePage(view).then(({ visitedViews }) => {
|
||||
if (this.isActive(view)) {
|
||||
this.toLastView(visitedViews, view)
|
||||
}
|
||||
})
|
||||
},
|
||||
closeRightTags() {
|
||||
this.$store.dispatch('tagsView/delRightTags', this.selectedTag).then(visitedViews => {
|
||||
this.$tab.closeRightPage(this.selectedTag).then(visitedViews => {
|
||||
if (!visitedViews.find(i => i.fullPath === this.$route.fullPath)) {
|
||||
this.toLastView(visitedViews)
|
||||
}
|
||||
})
|
||||
},
|
||||
closeLeftTags() {
|
||||
this.$store.dispatch('tagsView/delLeftTags', this.selectedTag).then(visitedViews => {
|
||||
this.$tab.closeLeftPage(this.selectedTag).then(visitedViews => {
|
||||
if (!visitedViews.find(i => i.fullPath === this.$route.fullPath)) {
|
||||
this.toLastView(visitedViews)
|
||||
}
|
||||
@ -184,12 +177,12 @@ export default {
|
||||
},
|
||||
closeOthersTags() {
|
||||
this.$router.push(this.selectedTag).catch(()=>{});
|
||||
this.$store.dispatch('tagsView/delOthersViews', this.selectedTag).then(() => {
|
||||
this.$tab.closeOtherPage(this.selectedTag).then(() => {
|
||||
this.moveToCurrentTag()
|
||||
})
|
||||
},
|
||||
closeAllTags(view) {
|
||||
this.$store.dispatch('tagsView/delAllViews').then(({ visitedViews }) => {
|
||||
this.$tab.closeAllPage().then(({ visitedViews }) => {
|
||||
if (this.affixTags.some(tag => tag.path === this.$route.path)) {
|
||||
return
|
||||
}
|
||||
|
@ -69,8 +69,9 @@ import "bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css";
|
||||
import Tinymce from '@/components/tinymce/index.vue'
|
||||
Vue.component('tinymce', Tinymce)
|
||||
import '@/icons'
|
||||
import axios from 'axios'
|
||||
Vue.prototype.$axios = axios
|
||||
import request from "@/utils/request" // 实现 form generator 使用自己定义的 axios request 对象
|
||||
console.log(request)
|
||||
Vue.prototype.$axios = request
|
||||
import '@/styles/index.scss'
|
||||
|
||||
/**
|
||||
|
@ -3,7 +3,7 @@ import store from './store'
|
||||
import { Message } from 'element-ui'
|
||||
import NProgress from 'nprogress'
|
||||
import 'nprogress/nprogress.css'
|
||||
import { getToken } from '@/utils/auth'
|
||||
import { getAccessToken } from '@/utils/auth'
|
||||
import { isRelogin } from '@/utils/request'
|
||||
|
||||
NProgress.configure({ showSpinner: false })
|
||||
@ -13,7 +13,7 @@ const whiteList = ['/login', '/social-login', '/auth-redirect', '/bind', '/regi
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
NProgress.start()
|
||||
if (getToken()) {
|
||||
if (getAccessToken()) {
|
||||
to.meta.title && store.dispatch('settings/setTitle', to.meta.title)
|
||||
/* has token*/
|
||||
if (to.path === '/login') {
|
||||
|
@ -42,6 +42,11 @@ export const constantRoutes = [
|
||||
component: (resolve) => require(['@/views/login'], resolve),
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: '/sso',
|
||||
component: (resolve) => require(['@/views/sso'], resolve),
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: '/social-login',
|
||||
component: (resolve) => require(['@/views/socialLogin'], resolve),
|
||||
|
@ -2,6 +2,7 @@ import { constantRoutes } from '@/router'
|
||||
import { getRouters } from '@/api/menu'
|
||||
import Layout from '@/layout/index'
|
||||
import ParentView from '@/components/ParentView';
|
||||
import { toCamelCase } from "@/utils";
|
||||
|
||||
const permission = {
|
||||
state: {
|
||||
@ -56,6 +57,8 @@ function filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false) {
|
||||
icon: route.icon,
|
||||
noCache: !route.keepAlive,
|
||||
}
|
||||
// 路由地址转首字母大写驼峰,作为路由名称,适配keepAlive
|
||||
route.name = toCamelCase(route.path, true)
|
||||
route.hidden = !route.visible
|
||||
// 处理 component 属性
|
||||
if (route.children) { // 父节点
|
||||
|
@ -63,7 +63,7 @@ const mutations = {
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
DEL_RIGHT_VIEWS: (state, view) => {
|
||||
const index = state.visitedViews.findIndex(v => v.path === view.path)
|
||||
if (index === -1) {
|
||||
@ -79,6 +79,23 @@ const mutations = {
|
||||
}
|
||||
return false
|
||||
})
|
||||
},
|
||||
|
||||
DEL_LEFT_VIEWS: (state, view) => {
|
||||
const index = state.visitedViews.findIndex(v => v.path === view.path)
|
||||
if (index === -1) {
|
||||
return
|
||||
}
|
||||
state.visitedViews = state.visitedViews.filter((item, idx) => {
|
||||
if (idx >= index || (item.meta && item.meta.affix)) {
|
||||
return true
|
||||
}
|
||||
const i = state.cachedViews.indexOf(item.name)
|
||||
if (i > -1) {
|
||||
state.cachedViews.splice(i, 1)
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -172,7 +189,14 @@ const actions = {
|
||||
commit('DEL_RIGHT_VIEWS', view)
|
||||
resolve([...state.visitedViews])
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
delLeftTags({ commit }, view) {
|
||||
return new Promise(resolve => {
|
||||
commit('DEL_LEFT_VIEWS', view)
|
||||
resolve([...state.visitedViews])
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
export default {
|
||||
|
@ -1,9 +1,8 @@
|
||||
import {login, logout, getInfo, socialQuickLogin, socialBindLogin, smsLogin} from '@/api/login'
|
||||
import { getToken, setToken, removeToken } from '@/utils/auth'
|
||||
import {getAccessToken, setToken, removeToken, getRefreshToken} from '@/utils/auth'
|
||||
|
||||
const user = {
|
||||
state: {
|
||||
token: getToken(),
|
||||
id: 0, // 用户编号
|
||||
name: '',
|
||||
avatar: '',
|
||||
@ -15,9 +14,6 @@ const user = {
|
||||
SET_ID: (state, id) => {
|
||||
state.id = id
|
||||
},
|
||||
SET_TOKEN: (state, token) => {
|
||||
state.token = token
|
||||
},
|
||||
SET_NAME: (state, name) => {
|
||||
state.name = name
|
||||
},
|
||||
@ -42,8 +38,8 @@ const user = {
|
||||
return new Promise((resolve, reject) => {
|
||||
login(username, password, code, uuid).then(res => {
|
||||
res = res.data;
|
||||
setToken(res.token)
|
||||
commit('SET_TOKEN', res.token)
|
||||
// 设置 token
|
||||
setToken(res)
|
||||
resolve()
|
||||
}).catch(error => {
|
||||
reject(error)
|
||||
@ -59,8 +55,8 @@ const user = {
|
||||
return new Promise((resolve, reject) => {
|
||||
socialQuickLogin(type, code, state).then(res => {
|
||||
res = res.data;
|
||||
setToken(res.token)
|
||||
commit('SET_TOKEN', res.token)
|
||||
// 设置 token
|
||||
setToken(res)
|
||||
resolve()
|
||||
}).catch(error => {
|
||||
reject(error)
|
||||
@ -78,8 +74,8 @@ const user = {
|
||||
return new Promise((resolve, reject) => {
|
||||
socialBindLogin(type, code, state, username, password).then(res => {
|
||||
res = res.data;
|
||||
setToken(res.token)
|
||||
commit('SET_TOKEN', res.token)
|
||||
// 设置 token
|
||||
setToken(res)
|
||||
resolve()
|
||||
}).catch(error => {
|
||||
reject(error)
|
||||
@ -93,8 +89,8 @@ const user = {
|
||||
return new Promise((resolve, reject) => {
|
||||
smsLogin(mobile,mobileCode).then(res => {
|
||||
res = res.data;
|
||||
setToken(res.token)
|
||||
commit('SET_TOKEN', res.token)
|
||||
// 设置 token
|
||||
setToken(res)
|
||||
resolve()
|
||||
}).catch(error => {
|
||||
reject(error)
|
||||
@ -142,7 +138,6 @@ const user = {
|
||||
LogOut({ commit, state }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
logout(state.token).then(() => {
|
||||
commit('SET_TOKEN', '')
|
||||
commit('SET_ROLES', [])
|
||||
commit('SET_PERMISSIONS', [])
|
||||
removeToken()
|
||||
@ -151,15 +146,6 @@ const user = {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// 前端 登出
|
||||
FedLogOut({ commit }) {
|
||||
return new Promise(resolve => {
|
||||
commit('SET_TOKEN', '')
|
||||
removeToken()
|
||||
resolve()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,96 @@
|
||||
import Cookies from 'js-cookie'
|
||||
import {decrypt, encrypt} from "@/utils/jsencrypt";
|
||||
|
||||
const TokenKey = 'Admin-Token'
|
||||
const AccessTokenKey = 'ACCESS_TOKEN'
|
||||
const RefreshTokenKey = 'REFRESH_TOKEN'
|
||||
|
||||
export function getToken() {
|
||||
return Cookies.get(TokenKey)
|
||||
// ========== Token 相关 ==========
|
||||
|
||||
export function getAccessToken() {
|
||||
return localStorage.getItem(AccessTokenKey)
|
||||
}
|
||||
|
||||
export function getRefreshToken() {
|
||||
return localStorage.getItem(RefreshTokenKey)
|
||||
}
|
||||
|
||||
export function setToken(token) {
|
||||
return Cookies.set(TokenKey, token)
|
||||
localStorage.setItem(AccessTokenKey, token.accessToken)
|
||||
localStorage.setItem(RefreshTokenKey, token.refreshToken)
|
||||
}
|
||||
|
||||
export function removeToken() {
|
||||
return Cookies.remove(TokenKey)
|
||||
localStorage.removeItem(AccessTokenKey)
|
||||
localStorage.removeItem(RefreshTokenKey)
|
||||
}
|
||||
|
||||
// ========== 账号相关 ==========
|
||||
|
||||
const UsernameKey = 'USERNAME'
|
||||
const PasswordKey = 'PASSWORD'
|
||||
const RememberMeKey = 'REMEMBER_ME'
|
||||
|
||||
export function getUsername() {
|
||||
return localStorage.getItem(UsernameKey)
|
||||
}
|
||||
|
||||
export function setUsername(username) {
|
||||
localStorage.setItem(UsernameKey, username)
|
||||
}
|
||||
|
||||
export function removeUsername() {
|
||||
localStorage.removeItem(UsernameKey)
|
||||
}
|
||||
|
||||
export function getPassword() {
|
||||
const password = localStorage.getItem(PasswordKey)
|
||||
return password ? decrypt(password) : undefined
|
||||
}
|
||||
|
||||
export function setPassword(password) {
|
||||
localStorage.setItem(PasswordKey, encrypt(password))
|
||||
}
|
||||
|
||||
export function removePassword() {
|
||||
localStorage.removeItem(PasswordKey)
|
||||
}
|
||||
|
||||
export function getRememberMe() {
|
||||
return localStorage.getItem(RememberMeKey) === 'true'
|
||||
}
|
||||
|
||||
export function setRememberMe(rememberMe) {
|
||||
localStorage.setItem(RememberMeKey, rememberMe)
|
||||
}
|
||||
|
||||
export function removeRememberMe() {
|
||||
localStorage.removeItem(RememberMeKey)
|
||||
}
|
||||
|
||||
// ========== 租户相关 ==========
|
||||
|
||||
const TenantIdKey = 'TENANT_ID'
|
||||
const TenantNameKey = 'TENANT_NAME'
|
||||
|
||||
export function getTenantName() {
|
||||
return localStorage.getItem(TenantNameKey)
|
||||
}
|
||||
|
||||
export function setTenantName(username) {
|
||||
localStorage.setItem(TenantNameKey, username)
|
||||
}
|
||||
|
||||
export function removeTenantName() {
|
||||
localStorage.removeItem(TenantNameKey)
|
||||
}
|
||||
|
||||
export function getTenantId() {
|
||||
return localStorage.getItem(TenantIdKey)
|
||||
}
|
||||
|
||||
export function setTenantId(username) {
|
||||
localStorage.setItem(TenantIdKey, username)
|
||||
}
|
||||
|
||||
export function removeTenantId() {
|
||||
localStorage.removeItem(TenantIdKey)
|
||||
}
|
||||
|
@ -75,13 +75,13 @@ export const SystemUserSocialTypeEnum = {
|
||||
title: "钉钉",
|
||||
type: 20,
|
||||
source: "dingtalk",
|
||||
img: "https://cdn.jsdelivr.net/gh/justauth/justauth-oauth-logo@1.11/dingtalk.png",
|
||||
img: "https://s1.ax1x.com/2022/05/22/OzMDRs.png",
|
||||
},
|
||||
WECHAT_ENTERPRISE: {
|
||||
title: "企业微信",
|
||||
type: 30,
|
||||
source: "wechat_enterprise",
|
||||
img: "https://cdn.jsdelivr.net/gh/justauth/justauth-oauth-logo@1.11/wechat_enterprise.png",
|
||||
img: "https://s1.ax1x.com/2022/05/22/OzMrzn.png",
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,7 @@ export const DICT_TYPE = {
|
||||
SYSTEM_SMS_SEND_STATUS: 'system_sms_send_status',
|
||||
SYSTEM_SMS_RECEIVE_STATUS: 'system_sms_receive_status',
|
||||
SYSTEM_ERROR_CODE_TYPE: 'system_error_code_type',
|
||||
SYSTEM_OAUTH2_GRANT_TYPE: 'system_oauth2_grant_type',
|
||||
|
||||
// ========== INFRA 模块 ==========
|
||||
INFRA_BOOLEAN_STRING: 'infra_boolean_string',
|
||||
|
@ -427,3 +427,15 @@ export function isNumberStr(str) {
|
||||
return /^[+-]?(0|([1-9]\d*))(\.\d+)?$/g.test(str)
|
||||
}
|
||||
|
||||
// -转驼峰
|
||||
export function toCamelCase(str, upperCaseFirst) {
|
||||
str = (str || '').toLowerCase().replace(/-(.)/g, function (match, group1) {
|
||||
return group1.toUpperCase();
|
||||
});
|
||||
|
||||
if (upperCaseFirst && str) {
|
||||
str = str.charAt(0).toUpperCase() + str.slice(1);
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
@ -1,13 +1,24 @@
|
||||
import axios from 'axios'
|
||||
import { Notification, MessageBox, Message } from 'element-ui'
|
||||
import {Message, MessageBox, Notification} from 'element-ui'
|
||||
import store from '@/store'
|
||||
import { getToken } from '@/utils/auth'
|
||||
import {getAccessToken, getRefreshToken, getTenantId, setToken} from '@/utils/auth'
|
||||
import errorCode from '@/utils/errorCode'
|
||||
import Cookies from "js-cookie";
|
||||
import {getPath, getTenantEnable} from "@/utils/ruoyi";
|
||||
import {refreshToken} from "@/api/login";
|
||||
|
||||
// 需要忽略的提示。忽略后,自动 Promise.reject('error')
|
||||
const ignoreMsgs = [
|
||||
"无效的刷新令牌", // 刷新令牌被删除时,不用提示
|
||||
"刷新令牌已过期" // 使用刷新令牌,刷新获取新的访问令牌时,结果因为过期失败,此时需要忽略。否则,会导致继续 401,无法跳转到登出界面
|
||||
]
|
||||
|
||||
// 是否显示重新登录
|
||||
export let isRelogin = { show: false };
|
||||
// Axios 无感知刷新令牌,参考 https://www.dashingdog.cn/article/11 与 https://segmentfault.com/a/1190000020210980 实现
|
||||
// 请求队列
|
||||
let requestList = []
|
||||
// 是否正在刷新中
|
||||
let isRefreshToken = false
|
||||
|
||||
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
|
||||
// 创建axios实例
|
||||
@ -15,18 +26,20 @@ const service = axios.create({
|
||||
// axios中请求配置有baseURL选项,表示请求URL公共部分
|
||||
baseURL: process.env.VUE_APP_BASE_API + '/admin-api/', // 此处的 /admin-api/ 地址,原因是后端的基础路径为 /admin-api/
|
||||
// 超时
|
||||
timeout: 30000
|
||||
timeout: 30000,
|
||||
// 禁用 Cookie 等信息
|
||||
withCredentials: false,
|
||||
})
|
||||
// request拦截器
|
||||
service.interceptors.request.use(config => {
|
||||
// 是否需要设置 token
|
||||
const isToken = (config.headers || {}).isToken === false
|
||||
if (getToken() && !isToken) {
|
||||
config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
|
||||
if (getAccessToken() && !isToken) {
|
||||
config.headers['Authorization'] = 'Bearer ' + getAccessToken() // 让每个请求携带自定义token 请根据实际情况自行修改
|
||||
}
|
||||
// 设置租户
|
||||
if (getTenantEnable()) {
|
||||
const tenantId = Cookies.get('tenantId');
|
||||
const tenantId = getTenantId();
|
||||
if (tenantId) {
|
||||
config.headers['tenant-id'] = tenantId;
|
||||
}
|
||||
@ -60,66 +73,84 @@ service.interceptors.request.use(config => {
|
||||
})
|
||||
|
||||
// 响应拦截器
|
||||
service.interceptors.response.use(res => {
|
||||
// 未设置状态码则默认成功状态
|
||||
const code = res.data.code || 200;
|
||||
// 获取错误信息
|
||||
const msg = errorCode[code] || res.data.msg || errorCode['default']
|
||||
if (code === 401) {
|
||||
if (!isRelogin.show) {
|
||||
isRelogin.show = true;
|
||||
MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', {
|
||||
confirmButtonText: '重新登录',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}
|
||||
).then(() => {
|
||||
isRelogin.show = false;
|
||||
store.dispatch('LogOut').then(() => {
|
||||
location.href = getPath('/index');
|
||||
})
|
||||
}).catch(() => {
|
||||
isRelogin.show = false;
|
||||
});
|
||||
service.interceptors.response.use(async res => {
|
||||
// 未设置状态码则默认成功状态
|
||||
const code = res.data.code || 200;
|
||||
// 获取错误信息
|
||||
const msg = res.data.msg || errorCode[code] || errorCode['default']
|
||||
if (ignoreMsgs.indexOf(msg) !== -1) { // 如果是忽略的错误码,直接返回 msg 异常
|
||||
return Promise.reject(msg)
|
||||
} else if (code === 401) {
|
||||
// 如果未认证,并且未进行刷新令牌,说明可能是访问令牌过期了
|
||||
if (!isRefreshToken) {
|
||||
isRefreshToken = true;
|
||||
// 1. 如果获取不到刷新令牌,则只能执行登出操作
|
||||
if (!getRefreshToken()) {
|
||||
return handleAuthorized();
|
||||
}
|
||||
return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
|
||||
} else if (code === 500) {
|
||||
Message({
|
||||
message: msg,
|
||||
type: 'error'
|
||||
// 2. 进行刷新访问令牌
|
||||
try {
|
||||
const refreshTokenRes = await refreshToken()
|
||||
// 2.1 刷新成功,则回放队列的请求 + 当前请求
|
||||
setToken(refreshTokenRes.data)
|
||||
requestList.forEach(cb => cb())
|
||||
return service(res.config)
|
||||
} catch (e) {// 为什么需要 catch 异常呢?刷新失败时,请求因为 Promise.reject 触发异常。
|
||||
// 2.2 刷新失败,只回放队列的请求
|
||||
requestList.forEach(cb => cb())
|
||||
// 提示是否要登出。即不回放当前请求!不然会形成递归
|
||||
return handleAuthorized();
|
||||
} finally {
|
||||
requestList = []
|
||||
isRefreshToken = false
|
||||
}
|
||||
} else {
|
||||
// 添加到队列,等待刷新获取到新的令牌
|
||||
return new Promise(resolve => {
|
||||
requestList.push(() => {
|
||||
res.config.headers['Authorization'] = 'Bearer ' + getAccessToken() // 让每个请求携带自定义token 请根据实际情况自行修改
|
||||
resolve(service(res.config))
|
||||
})
|
||||
})
|
||||
return Promise.reject(new Error(msg))
|
||||
} else if (code === 901) {
|
||||
Message({
|
||||
type: 'error',
|
||||
duration: 0,
|
||||
dangerouslyUseHTMLString: true,
|
||||
message: '<div>演示模式,无法进行写操作</div>'
|
||||
+ '<div> </div>'
|
||||
+ '<div>参考 https://doc.iocoder.cn/ 教程</div>'
|
||||
+ '<div> </div>'
|
||||
+ '<div>5 分钟搭建本地环境</div>',
|
||||
})
|
||||
return Promise.reject(new Error(msg))
|
||||
} else if (code !== 200) {
|
||||
}
|
||||
} else if (code === 500) {
|
||||
Message({
|
||||
message: msg,
|
||||
type: 'error'
|
||||
})
|
||||
return Promise.reject(new Error(msg))
|
||||
} else if (code === 901) {
|
||||
Message({
|
||||
type: 'error',
|
||||
duration: 0,
|
||||
dangerouslyUseHTMLString: true,
|
||||
message: '<div>演示模式,无法进行写操作</div>'
|
||||
+ '<div> </div>'
|
||||
+ '<div>参考 https://doc.iocoder.cn/ 教程</div>'
|
||||
+ '<div> </div>'
|
||||
+ '<div>5 分钟搭建本地环境</div>',
|
||||
})
|
||||
return Promise.reject(new Error(msg))
|
||||
} else if (code !== 200) {
|
||||
if (msg === '无效的刷新令牌') { // hard coding:忽略这个提示,直接登出
|
||||
console.log(msg)
|
||||
} else {
|
||||
Notification.error({
|
||||
title: msg
|
||||
})
|
||||
return Promise.reject('error')
|
||||
} else {
|
||||
return res.data
|
||||
}
|
||||
},
|
||||
error => {
|
||||
return Promise.reject('error')
|
||||
} else {
|
||||
return res.data
|
||||
}
|
||||
}, error => {
|
||||
console.log('err' + error)
|
||||
let { message } = error;
|
||||
let {message} = error;
|
||||
if (message === "Network Error") {
|
||||
message = "后端接口连接异常";
|
||||
}
|
||||
else if (message.includes("timeout")) {
|
||||
} else if (message.includes("timeout")) {
|
||||
message = "系统接口请求超时";
|
||||
}
|
||||
else if (message.includes("Request failed with status code")) {
|
||||
} else if (message.includes("Request failed with status code")) {
|
||||
message = "系统接口" + message.substr(message.length - 3) + "异常";
|
||||
}
|
||||
Message({
|
||||
@ -133,9 +164,29 @@ service.interceptors.response.use(res => {
|
||||
|
||||
export function getBaseHeader() {
|
||||
return {
|
||||
'Authorization': "Bearer " + getToken(),
|
||||
'tenant-id': Cookies.get('tenantId'),
|
||||
'Authorization': "Bearer " + getAccessToken(),
|
||||
'tenant-id': getTenantId(),
|
||||
}
|
||||
}
|
||||
|
||||
function handleAuthorized() {
|
||||
if (!isRelogin.show) {
|
||||
isRelogin.show = true;
|
||||
MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', {
|
||||
confirmButtonText: '重新登录',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}
|
||||
).then(() => {
|
||||
isRelogin.show = false;
|
||||
store.dispatch('LogOut').then(() => {
|
||||
location.href = getPath('/index');
|
||||
})
|
||||
}).catch(() => {
|
||||
isRelogin.show = false;
|
||||
});
|
||||
}
|
||||
return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
|
||||
}
|
||||
|
||||
export default service
|
||||
|
@ -429,7 +429,7 @@ export default {
|
||||
this.$modal.confirm('是否删除该流程!!').then(function() {
|
||||
deleteModel(row.id).then(response => {
|
||||
that.getList();
|
||||
that.msgSuccess("删除成功");
|
||||
that.$modal.msgSuccess("删除成功");
|
||||
})
|
||||
}).catch(() => {});
|
||||
},
|
||||
@ -439,7 +439,7 @@ export default {
|
||||
this.$modal.confirm('是否部署该流程!!').then(function() {
|
||||
deployModel(row.id).then(response => {
|
||||
that.getList();
|
||||
that.msgSuccess("部署成功");
|
||||
that.$modal.msgSuccess("部署成功");
|
||||
})
|
||||
}).catch(() => {});
|
||||
},
|
||||
|
@ -55,10 +55,9 @@
|
||||
<script>
|
||||
import {getProcessDefinitionBpmnXML, getProcessDefinitionList} from "@/api/bpm/definition";
|
||||
import {DICT_TYPE, getDictDatas} from "@/utils/dict";
|
||||
import {getForm} from "@/api/bpm/form";
|
||||
import {decodeFields} from "@/utils/formGenerator";
|
||||
import Parser from '@/components/parser/Parser'
|
||||
import {createProcessInstance, getMyProcessInstancePage} from "@/api/bpm/processInstance";
|
||||
import {createProcessInstance} from "@/api/bpm/processInstance";
|
||||
|
||||
// 流程实例的发起
|
||||
export default {
|
||||
|
@ -58,7 +58,7 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="当前审批任务" align="center" prop="tasks">
|
||||
<template slot-scope="scope">
|
||||
<el-button v-for="task in scope.row.tasks" type="text" @click="handleFormDetail(task.id)">
|
||||
<el-button v-for="task in scope.row.tasks" :key="task.id" type="text" @click="handleFormDetail(task.id)">
|
||||
<span>{{ task.name }}</span>
|
||||
</el-button>
|
||||
</template>
|
||||
|
@ -12,7 +12,7 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="规则范围" align="center" prop="options" width="440px">
|
||||
<template slot-scope="scope">
|
||||
<el-tag size="medium" v-if="scope.row.options" v-for="option in scope.row.options">
|
||||
<el-tag size="medium" v-if="scope.row.options" :key="option" v-for="option in scope.row.options">
|
||||
{{ getAssignRuleOptionName(scope.row.type, option) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
|
@ -77,7 +77,7 @@
|
||||
|
||||
<script>
|
||||
import { deleteFile, getFilePage } from "@/api/infra/file";
|
||||
import {getToken} from "@/utils/auth";
|
||||
import {getAccessToken} from "@/utils/auth";
|
||||
|
||||
export default {
|
||||
name: "File",
|
||||
@ -108,7 +108,7 @@ export default {
|
||||
title: "", // 弹出层标题
|
||||
isUploading: false, // 是否禁用上传
|
||||
url: process.env.VUE_APP_BASE_API + "/admin-api/infra/file/upload", // 请求地址
|
||||
headers: { Authorization: "Bearer " + getToken() }, // 设置上传的请求头部
|
||||
headers: { Authorization: "Bearer " + getAccessToken() }, // 设置上传的请求头部
|
||||
data: {} // 上传的额外数据,用于文件名
|
||||
},
|
||||
};
|
||||
|
@ -107,9 +107,17 @@
|
||||
import {getCodeImg, sendSmsCode, socialAuthRedirect} from "@/api/login";
|
||||
import {getTenantIdByName} from "@/api/system/tenant";
|
||||
import Cookies from "js-cookie";
|
||||
import {decrypt, encrypt} from '@/utils/jsencrypt'
|
||||
import {SystemUserSocialTypeEnum} from "@/utils/constants";
|
||||
import {getTenantEnable} from "@/utils/ruoyi";
|
||||
import {
|
||||
getPassword,
|
||||
getRememberMe, getTenantName,
|
||||
getUsername,
|
||||
removePassword, removeRememberMe, removeTenantName,
|
||||
removeUsername,
|
||||
setPassword, setRememberMe, setTenantId, setTenantName,
|
||||
setUsername
|
||||
} from "@/utils/auth";
|
||||
|
||||
export default {
|
||||
name: "Login",
|
||||
@ -161,7 +169,7 @@ export default {
|
||||
const tenantId = res.data;
|
||||
if (tenantId && tenantId >= 0) {
|
||||
// 设置租户
|
||||
Cookies.set("tenantId", tenantId);
|
||||
setTenantId(tenantId)
|
||||
callback();
|
||||
} else {
|
||||
callback('租户不存在');
|
||||
@ -172,8 +180,6 @@ export default {
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
|
||||
loading: false,
|
||||
redirect: undefined,
|
||||
// 枚举
|
||||
@ -213,21 +219,16 @@ export default {
|
||||
});
|
||||
},
|
||||
getCookie() {
|
||||
const username = Cookies.get("username");
|
||||
const password = Cookies.get("password");
|
||||
const rememberMe = Cookies.get('rememberMe')
|
||||
const tenantName = Cookies.get('tenantName');
|
||||
const mobile = Cookies.get('mobile');
|
||||
const mobileCode = Cookies.get('mobileCode');
|
||||
const loginType = Cookies.get('loginType');
|
||||
const username = getUsername();
|
||||
const password = getPassword();
|
||||
const rememberMe = getRememberMe();
|
||||
const tenantName = getTenantName();
|
||||
this.loginForm = {
|
||||
username: username === undefined ? this.loginForm.username : username,
|
||||
password: password === undefined ? this.loginForm.password : decrypt(password),
|
||||
rememberMe: rememberMe === undefined ? false : Boolean(rememberMe),
|
||||
tenantName: tenantName === undefined ? this.loginForm.tenantName : tenantName,
|
||||
mobile: mobile === undefined ? this.loginForm.mobile : mobile,
|
||||
mobileCode: mobileCode === undefined ? this.loginForm.mobileCode : mobileCode,
|
||||
loginType: loginType === undefined ? this.loginForm.loginType : loginType,
|
||||
...this.loginForm,
|
||||
username: username ? username : this.loginForm.username,
|
||||
password: password ? password : this.loginForm.password,
|
||||
rememberMe: rememberMe ? getRememberMe() : false,
|
||||
tenantName: tenantName ? tenantName : this.loginForm.tenantName,
|
||||
};
|
||||
},
|
||||
handleLogin() {
|
||||
@ -236,18 +237,18 @@ export default {
|
||||
this.loading = true;
|
||||
// 设置 Cookie
|
||||
if (this.loginForm.rememberMe) {
|
||||
Cookies.set("username", this.loginForm.username, {expires: 30});
|
||||
Cookies.set("password", encrypt(this.loginForm.password), {expires: 30});
|
||||
Cookies.set('rememberMe', this.loginForm.rememberMe, {expires: 30});
|
||||
Cookies.set('tenantName', this.loginForm.tenantName, {expires: 30});
|
||||
setUsername(this.loginForm.username)
|
||||
setPassword(this.loginForm.password)
|
||||
setRememberMe(this.loginForm.rememberMe)
|
||||
setTenantName(this.loginForm.tenantName)
|
||||
} else {
|
||||
Cookies.remove("username");
|
||||
Cookies.remove("password");
|
||||
Cookies.remove('rememberMe');
|
||||
Cookies.remove('tenantName');
|
||||
removeUsername()
|
||||
removePassword()
|
||||
removeRememberMe()
|
||||
removeTenantName()
|
||||
}
|
||||
// 发起登陆
|
||||
console.log("发起登录", this.loginForm);
|
||||
// console.log("发起登录", this.loginForm);
|
||||
this.$store.dispatch(this.loginForm.loginType === "sms" ? "SmsLogin" : "Login", this.loginForm).then(() => {
|
||||
this.$router.push({path: this.redirect || "/"}).catch(() => {
|
||||
});
|
||||
|
@ -74,7 +74,7 @@
|
||||
<!-- <el-table-column label="商户名称" align="center" prop="merchantName" width="120"/>-->
|
||||
<!-- <el-table-column label="应用名称" align="center" prop="appName" width="120"/>-->
|
||||
<el-table-column label="支付渠道" align="center" width="130">
|
||||
<template v-slot="scope">
|
||||
<template slot-scope="scope">
|
||||
<el-popover trigger="hover" placement="top">
|
||||
<p>商户名称: {{ scope.row.merchantName }}</p>
|
||||
<p>应用名称: {{ scope.row.appName }}</p>
|
||||
@ -88,7 +88,7 @@
|
||||
<!-- <el-table-column label="交易订单号" align="center" prop="tradeNo" width="140"/>-->
|
||||
<!-- <el-table-column label="商户订单编号" align="center" prop="merchantOrderId" width="140"/>-->
|
||||
<el-table-column label="商户订单号" align="left" width="230">
|
||||
<template v-slot="scope">
|
||||
<template slot-scope="scope">
|
||||
<p class="order-font">
|
||||
<el-tag size="mini">退款</el-tag>
|
||||
{{ scope.row.merchantRefundNo }}
|
||||
@ -100,7 +100,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="支付订单号" align="center" prop="merchantRefundNo" width="250">
|
||||
<template v-slot="scope">
|
||||
<template slot-scope="scope">
|
||||
<p class="order-font">
|
||||
<el-tag size="mini">交易</el-tag>
|
||||
{{ scope.row.tradeNo }}
|
||||
@ -112,7 +112,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="支付金额(元)" align="center" prop="payAmount" width="100">
|
||||
<template v-slot="scope" class="">
|
||||
<template slot-scope="scope" class="">
|
||||
¥{{ parseFloat(scope.row.payAmount / 100).toFixed(2) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
@ -122,17 +122,17 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="退款类型" align="center" prop="type" width="80">
|
||||
<template v-slot="scope">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :type="DICT_TYPE.PAY_REFUND_ORDER_TYPE" :value="scope.row.type" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="退款状态" align="center" prop="status">
|
||||
<template v-slot="scope">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :type="DICT_TYPE.PAY_REFUND_ORDER_STATUS" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="回调状态" align="center" prop="notifyStatus">
|
||||
<template v-slot="scope">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :type="DICT_TYPE.PAY_ORDER_NOTIFY_STATUS" :value="scope.row.notifyStatus" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
@ -183,7 +183,7 @@
|
||||
<el-tag class="tag-purple" size="mini">{{ parseFloat(refundDetail.refundAmount / 100).toFixed(2) }}</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="退款类型">
|
||||
<template v-slot="scope">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :type="DICT_TYPE.PAY_REFUND_ORDER_TYPE" :value="refundDetail.type" />
|
||||
</template>
|
||||
</el-descriptions-item>
|
||||
|
@ -19,7 +19,7 @@
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<div>
|
||||
<el-form ref="loginForm" :model="loginForm" :rules="LoginRules" class="login-form">
|
||||
<el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form">
|
||||
<!-- 账号密码登录 -->
|
||||
<el-form-item prop="username">
|
||||
<el-input v-model="loginForm.username" type="text" auto-complete="off" placeholder="账号">
|
||||
@ -65,16 +65,28 @@
|
||||
<script>
|
||||
import Cookies from "js-cookie";
|
||||
import { encrypt, decrypt } from '@/utils/jsencrypt'
|
||||
import {
|
||||
getPassword, getRememberMe,
|
||||
getUsername,
|
||||
removePassword,
|
||||
removeUsername,
|
||||
setPassword,
|
||||
setRememberMe,
|
||||
setUsername
|
||||
} from "@/utils/auth";
|
||||
import {getCodeImg} from "@/api/login";
|
||||
|
||||
export default {
|
||||
name: "ThirdLogin",
|
||||
data() {
|
||||
return {
|
||||
codeUrl: "",
|
||||
captchaEnable: true,
|
||||
loginForm: {
|
||||
loginType: "uname",
|
||||
username: "admin",
|
||||
password: "admin123",
|
||||
rememberMe: false, // TODO 芋艿:后面看情况,去掉这块
|
||||
rememberMe: false,
|
||||
},
|
||||
loginRules: {
|
||||
username: [
|
||||
@ -104,6 +116,7 @@ export default {
|
||||
this.getCookie();
|
||||
// 重定向地址
|
||||
this.redirect = this.$route.query.redirect;
|
||||
this.getCode();
|
||||
// 社交登录相关
|
||||
this.type = this.$route.query.type;
|
||||
this.code = this.$route.query.code;
|
||||
@ -119,16 +132,30 @@ export default {
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
getCode() {
|
||||
// 只有开启的状态,才加载验证码。默认开启
|
||||
if (!this.captchaEnable) {
|
||||
return;
|
||||
}
|
||||
// 请求远程,获得验证码
|
||||
getCodeImg().then(res => {
|
||||
res = res.data;
|
||||
this.captchaEnable = res.enable;
|
||||
if (this.captchaEnable) {
|
||||
this.codeUrl = "data:image/gif;base64," + res.img;
|
||||
this.loginForm.uuid = res.uuid;
|
||||
}
|
||||
});
|
||||
},
|
||||
getCookie() {
|
||||
const username = Cookies.get("username");
|
||||
const password = Cookies.get("password");
|
||||
const rememberMe = Cookies.get('rememberMe')
|
||||
const loginType = Cookies.get('loginType');
|
||||
const username = getUsername();
|
||||
const password = getPassword();
|
||||
const rememberMe = getRememberMe();
|
||||
this.loginForm = {
|
||||
username: username === undefined ? this.loginForm.username : username,
|
||||
password: password === undefined ? this.loginForm.password : decrypt(password),
|
||||
rememberMe: rememberMe === undefined ? false : Boolean(rememberMe),
|
||||
loginType: loginType === undefined ? this.loginForm.loginType : loginType,
|
||||
username: username ? username : this.loginForm.username,
|
||||
password: password ? password : this.loginForm.password,
|
||||
rememberMe: rememberMe ? getRememberMe() : false,
|
||||
loginType: this.loginForm.loginType,
|
||||
};
|
||||
},
|
||||
handleLogin() {
|
||||
@ -136,11 +163,12 @@ export default {
|
||||
if (valid) {
|
||||
this.loading = true;
|
||||
if (this.loginForm.rememberMe) {
|
||||
Cookies.set("username", this.loginForm.username, { expires: 30 });
|
||||
Cookies.set("password", encrypt(this.loginForm.password), { expires: 30 });
|
||||
setUsername(this.loginForm.username)
|
||||
setPassword(this.loginForm.password)
|
||||
setRememberMe(this.loginForm.rememberMe)
|
||||
} else {
|
||||
Cookies.remove("username");
|
||||
Cookies.remove("password");
|
||||
removeUsername()
|
||||
removePassword()
|
||||
}
|
||||
this.$store.dispatch("SocialLogin2", {
|
||||
code: this.code,
|
||||
|
236
yudao-ui-admin/src/views/sso.vue
Normal file
236
yudao-ui-admin/src/views/sso.vue
Normal file
@ -0,0 +1,236 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<div class="logo"></div>
|
||||
<!-- 登录区域 -->
|
||||
<div class="content">
|
||||
<!-- 配图 -->
|
||||
<div class="pic"></div>
|
||||
<!-- 表单 -->
|
||||
<div class="field">
|
||||
<!-- [移动端]标题 -->
|
||||
<h2 class="mobile-title">
|
||||
<h3 class="title">芋道后台管理系统</h3>
|
||||
</h2>
|
||||
|
||||
<!-- 表单 -->
|
||||
<div class="form-cont">
|
||||
<el-tabs class="form" style=" float:none;" value="uname">
|
||||
<el-tab-pane :label="'三方授权(' + client.name + ')'" name="uname">
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<div>
|
||||
<el-form ref="loginForm" :model="loginForm" :rules="LoginRules" class="login-form">
|
||||
<el-form-item prop="tenantName" v-if="tenantEnable">
|
||||
<el-input v-model="loginForm.tenantName" type="text" auto-complete="off" placeholder='租户'>
|
||||
<svg-icon slot="prefix" icon-class="tree" class="el-input__icon input-icon"/>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<!-- 授权范围的选择 -->
|
||||
此第三方应用请求获得以下权限:
|
||||
<el-form-item prop="scopes">
|
||||
<el-checkbox-group v-model="loginForm.scopes">
|
||||
<el-checkbox v-for="scope in params.scopes" :label="scope" :key="scope"
|
||||
style="display: block; margin-bottom: -10px;">{{formatScope(scope)}}</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
<!-- 下方的登录按钮 -->
|
||||
<el-form-item style="width:100%;">
|
||||
<el-button :loading="loading" size="medium" type="primary" style="width:60%;"
|
||||
@click.native.prevent="handleAuthorize(true)">
|
||||
<span v-if="!loading">同意授权</span>
|
||||
<span v-else>授 权 中...</span>
|
||||
</el-button>
|
||||
<el-button size="medium" style="width:36%"
|
||||
@click.native.prevent="handleAuthorize(false)">拒绝</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- footer -->
|
||||
<div class="footer">
|
||||
Copyright © 2020-2022 iocoder.cn All Rights Reserved.
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {getTenantIdByName} from "@/api/system/tenant";
|
||||
import {getTenantEnable} from "@/utils/ruoyi";
|
||||
import {authorize, getAuthorize} from "@/api/login";
|
||||
import {getTenantName, setTenantId} from "@/utils/auth";
|
||||
|
||||
export default {
|
||||
name: "Login",
|
||||
data() {
|
||||
return {
|
||||
tenantEnable: true,
|
||||
loginForm: {
|
||||
tenantName: "芋道源码",
|
||||
scopes: [], // 已选中的 scope 数组
|
||||
},
|
||||
params: { // URL 上的 client_id、scope 等参数
|
||||
responseType: undefined,
|
||||
clientId: undefined,
|
||||
redirectUri: undefined,
|
||||
state: undefined,
|
||||
scopes: [], // 优先从 query 参数获取;如果未传递,从后端获取
|
||||
},
|
||||
client: { // 客户端信息
|
||||
name: '',
|
||||
logo: '',
|
||||
},
|
||||
LoginRules: {
|
||||
tenantName: [
|
||||
{required: true, trigger: "blur", message: "租户不能为空"},
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
// debugger
|
||||
getTenantIdByName(value).then(res => {
|
||||
const tenantId = res.data;
|
||||
if (tenantId && tenantId >= 0) {
|
||||
// 设置租户
|
||||
setTenantId(tenantId)
|
||||
callback();
|
||||
} else {
|
||||
callback('租户不存在');
|
||||
}
|
||||
});
|
||||
},
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
},
|
||||
loading: false
|
||||
};
|
||||
},
|
||||
created() {
|
||||
// 租户开关
|
||||
this.tenantEnable = getTenantEnable();
|
||||
this.getCookie();
|
||||
|
||||
// 解析参数
|
||||
// 例如说【自动授权不通过】:client_id=default&redirect_uri=https%3A%2F%2Fwww.iocoder.cn&response_type=code&scope=user.read%20user.write
|
||||
// 例如说【自动授权通过】:client_id=default&redirect_uri=https%3A%2F%2Fwww.iocoder.cn&response_type=code&scope=user.read
|
||||
this.params.responseType = this.$route.query.response_type
|
||||
this.params.clientId = this.$route.query.client_id
|
||||
this.params.redirectUri = this.$route.query.redirect_uri
|
||||
this.params.state = this.$route.query.state
|
||||
if (this.$route.query.scope) {
|
||||
this.params.scopes = this.$route.query.scope.split(' ')
|
||||
}
|
||||
|
||||
// 如果有 scope 参数,先执行一次自动授权,看看是否之前都授权过了。
|
||||
if (this.params.scopes.length > 0) {
|
||||
this.doAuthorize(true, this.params.scopes, []).then(res => {
|
||||
const href = res.data
|
||||
if (!href) {
|
||||
console.log('自动授权未通过!')
|
||||
return;
|
||||
}
|
||||
location.href = href
|
||||
})
|
||||
}
|
||||
|
||||
// 获取授权页的基本信息
|
||||
getAuthorize(this.params.clientId).then(res => {
|
||||
this.client = res.data.client
|
||||
// 解析 scope
|
||||
let scopes
|
||||
// 1.1 如果 params.scope 非空,则过滤下返回的 scopes
|
||||
if (this.params.scopes.length > 0) {
|
||||
scopes = []
|
||||
for (const scope of res.data.scopes) {
|
||||
if (this.params.scopes.indexOf(scope.key) >= 0) {
|
||||
scopes.push(scope)
|
||||
}
|
||||
}
|
||||
// 1.2 如果 params.scope 为空,则使用返回的 scopes 设置它
|
||||
} else {
|
||||
scopes = res.data.scopes
|
||||
for (const scope of scopes) {
|
||||
this.params.scopes.push(scope.key)
|
||||
}
|
||||
}
|
||||
// 生成已选中的 checkedScopes
|
||||
for (const scope of scopes) {
|
||||
if (scope.value) {
|
||||
this.loginForm.scopes.push(scope.key)
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
getCookie() {
|
||||
const tenantName = getTenantName();
|
||||
this.loginForm = {
|
||||
...this.loginForm,
|
||||
tenantName: tenantName ? tenantName : this.loginForm.tenantName,
|
||||
};
|
||||
},
|
||||
handleAuthorize(approved) {
|
||||
this.$refs.loginForm.validate(valid => {
|
||||
if (!valid) {
|
||||
return
|
||||
}
|
||||
this.loading = true
|
||||
// 计算 checkedScopes + uncheckedScopes
|
||||
let checkedScopes;
|
||||
let uncheckedScopes;
|
||||
if (approved) { // 同意授权,按照用户的选择
|
||||
checkedScopes = this.loginForm.scopes
|
||||
uncheckedScopes = this.params.scopes.filter(item => checkedScopes.indexOf(item) === -1)
|
||||
} else { // 拒绝,则都是取消
|
||||
checkedScopes = []
|
||||
uncheckedScopes = this.params.scopes
|
||||
}
|
||||
// 提交授权的请求
|
||||
this.doAuthorize(false, checkedScopes, uncheckedScopes).then(res => {
|
||||
const href = res.data
|
||||
if (!href) {
|
||||
return;
|
||||
}
|
||||
location.href = href
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
})
|
||||
},
|
||||
doAuthorize(autoApprove, checkedScopes, uncheckedScopes) {
|
||||
return authorize(this.params.responseType, this.params.clientId, this.params.redirectUri, this.params.state,
|
||||
autoApprove, checkedScopes, uncheckedScopes)
|
||||
},
|
||||
formatScope(scope) {
|
||||
// 格式化 scope 授权范围,方便用户理解。
|
||||
// 这里仅仅是一个 demo,可以考虑录入到字典数据中,例如说字典类型 "system_oauth2_scope",它的每个 scope 都是一条字典数据。
|
||||
switch (scope) {
|
||||
case 'user.read': return '访问你的个人信息'
|
||||
case 'user.write': return '修改你的个人信息'
|
||||
default: return scope
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "~@/assets/styles/login.scss";
|
||||
.oauth-login {
|
||||
display: flex;
|
||||
align-items: cen;
|
||||
cursor:pointer;
|
||||
}
|
||||
.oauth-login-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.oauth-login-item img {
|
||||
height: 25px;
|
||||
width: 25px;
|
||||
}
|
||||
.oauth-login-item span:hover {
|
||||
text-decoration: underline red;
|
||||
color: red;
|
||||
}
|
||||
</style>
|
306
yudao-ui-admin/src/views/system/oauth2/client/index.vue
Executable file
306
yudao-ui-admin/src/views/system/oauth2/client/index.vue
Executable file
@ -0,0 +1,306 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
|
||||
<el-form-item label="应用名" prop="name">
|
||||
<el-input v-model="queryParams.name" placeholder="请输入应用名" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable size="small">
|
||||
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.COMMON_STATUS)"
|
||||
:key="dict.value" :label="dict.label" :value="dict.value"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<!-- 操作工具栏 -->
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
|
||||
v-hasPermi="['system:oauth2-client:create']">新增</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<!-- 列表 -->
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="客户端编号" align="center" prop="clientId" />
|
||||
<el-table-column label="客户端密钥" align="center" prop="secret" />
|
||||
<el-table-column label="应用名" align="center" prop="name" />
|
||||
<el-table-column label="应用图标" align="center" prop="logo">
|
||||
<template slot-scope="scope">
|
||||
<img width="40px" height="40px" :src="scope.row.logo">
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" align="center" prop="status">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="访问令牌的有效期" align="center" prop="accessTokenValiditySeconds">
|
||||
<template slot-scope="scope">{{ scope.row.accessTokenValiditySeconds }} 秒</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="刷新令牌的有效期" align="center" prop="refreshTokenValiditySeconds">
|
||||
<template slot-scope="scope">{{ scope.row.refreshTokenValiditySeconds }} 秒</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="授权类型" align="center" prop="authorizedGrantTypes">
|
||||
<template slot-scope="scope">
|
||||
<el-tag :disable-transitions="true" :key="index" v-for="(authorizedGrantType, index) in scope.row.authorizedGrantTypes" :index="index">
|
||||
{{ authorizedGrantType }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['system:oauth2-client:update']">修改</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
|
||||
v-hasPermi="['system:oauth2-client:delete']">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页组件 -->
|
||||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
|
||||
@pagination="getList"/>
|
||||
|
||||
<!-- 对话框(添加 / 修改) -->
|
||||
<el-dialog :title="title" :visible.sync="open" width="700px" append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="160px">
|
||||
<el-form-item label="客户端编号" prop="secret">
|
||||
<el-input v-model="form.clientId" placeholder="请输入客户端编号" />
|
||||
</el-form-item>
|
||||
<el-form-item label="客户端密钥" prop="secret">
|
||||
<el-input v-model="form.secret" placeholder="请输入客户端密钥" />
|
||||
</el-form-item>
|
||||
<el-form-item label="应用名" prop="name">
|
||||
<el-input v-model="form.name" placeholder="请输入应用名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="应用图标">
|
||||
<imageUpload v-model="form.logo" :limit="1"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="应用描述">
|
||||
<el-input type="textarea" v-model="form.description" placeholder="请输入应用名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-radio-group v-model="form.status">
|
||||
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.COMMON_STATUS)"
|
||||
:key="dict.value" :label="parseInt(dict.value)">{{dict.label}}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="访问令牌的有效期" prop="accessTokenValiditySeconds">
|
||||
<el-input-number v-model="form.accessTokenValiditySeconds" placeholder="单位:秒" />
|
||||
</el-form-item>
|
||||
<el-form-item label="刷新令牌的有效期" prop="refreshTokenValiditySeconds">
|
||||
<el-input-number v-model="form.refreshTokenValiditySeconds" placeholder="单位:秒" />
|
||||
</el-form-item>
|
||||
<el-form-item label="授权类型" prop="authorizedGrantTypes">
|
||||
<el-select v-model="form.authorizedGrantTypes" multiple filterable placeholder="请输入授权类型" style="width: 500px" >
|
||||
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYSTEM_OAUTH2_GRANT_TYPE)"
|
||||
:key="dict.value" :label="dict.label" :value="dict.value"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="授权范围" prop="scopes">
|
||||
<el-select v-model="form.scopes" multiple filterable allow-create placeholder="请输入授权范围" style="width: 500px" >
|
||||
<el-option v-for="scope in form.scopes" :key="scope" :label="scope" :value="scope"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="自动授权范围" prop="autoApproveScopes">
|
||||
<el-select v-model="form.autoApproveScopes" multiple filterable placeholder="请输入授权范围" style="width: 500px" >
|
||||
<el-option v-for="scope in form.scopes" :key="scope" :label="scope" :value="scope"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="可重定向的 URI 地址" prop="redirectUris">
|
||||
<el-select v-model="form.redirectUris" multiple filterable allow-create placeholder="请输入可重定向的 URI 地址" style="width: 500px" >
|
||||
<el-option v-for="redirectUri in form.redirectUris" :key="redirectUri" :label="redirectUri" :value="redirectUri"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="权限" prop="authorities">
|
||||
<el-select v-model="form.authorities" multiple filterable allow-create placeholder="请输入权限" style="width: 500px" >
|
||||
<el-option v-for="authority in form.authorities" :key="authority" :label="authority" :value="authority"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="资源" prop="resourceIds">
|
||||
<el-select v-model="form.resourceIds" multiple filterable allow-create placeholder="请输入资源" style="width: 500px" >
|
||||
<el-option v-for="resourceId in form.resourceIds" :key="resourceId" :label="resourceId" :value="resourceId"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="附加信息" prop="additionalInformation">
|
||||
<el-input type="textarea" v-model="form.additionalInformation" placeholder="请输入附加信息,JSON 格式数据" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { createOAuth2Client, updateOAuth2Client, deleteOAuth2Client, getOAuth2Client, getOAuth2ClientPage } from "@/api/system/oauth2/oauth2Client";
|
||||
import ImageUpload from '@/components/ImageUpload';
|
||||
import Editor from '@/components/Editor';
|
||||
import {CommonStatusEnum} from "@/utils/constants";
|
||||
import FileUpload from "@/components/FileUpload";
|
||||
|
||||
export default {
|
||||
name: "OAuth2Client",
|
||||
components: {
|
||||
FileUpload,
|
||||
ImageUpload,
|
||||
Editor,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 导出遮罩层
|
||||
exportLoading: false,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// OAuth2 客户端列表
|
||||
list: [],
|
||||
// 弹出层标题
|
||||
title: "",
|
||||
// 是否显示弹出层
|
||||
open: false,
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
name: null,
|
||||
status: null,
|
||||
},
|
||||
// 表单参数
|
||||
form: {},
|
||||
// 表单校验
|
||||
rules: {
|
||||
clientId: [{ required: true, message: "客户端编号不能为空", trigger: "blur" }],
|
||||
secret: [{ required: true, message: "客户端密钥不能为空", trigger: "blur" }],
|
||||
name: [{ required: true, message: "应用名不能为空", trigger: "blur" }],
|
||||
logo: [{ required: true, message: "应用图标不能为空", trigger: "blur" }],
|
||||
status: [{ required: true, message: "状态不能为空", trigger: "blur" }],
|
||||
accessTokenValiditySeconds: [{ required: true, message: "访问令牌的有效期不能为空", trigger: "blur" }],
|
||||
refreshTokenValiditySeconds: [{ required: true, message: "刷新令牌的有效期不能为空", trigger: "blur" }],
|
||||
redirectUris: [{ required: true, message: "可重定向的 URI 地址不能为空", trigger: "blur" }],
|
||||
authorizedGrantTypes: [{ required: true, message: "授权类型不能为空", trigger: "blur" }],
|
||||
}
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
/** 查询列表 */
|
||||
getList() {
|
||||
this.loading = true;
|
||||
// 处理查询参数
|
||||
let params = {...this.queryParams};
|
||||
// 执行查询
|
||||
getOAuth2ClientPage(params).then(response => {
|
||||
this.list = response.data.list;
|
||||
this.total = response.data.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
/** 取消按钮 */
|
||||
cancel() {
|
||||
this.open = false;
|
||||
this.reset();
|
||||
},
|
||||
/** 表单重置 */
|
||||
reset() {
|
||||
this.form = {
|
||||
id: undefined,
|
||||
clientId: undefined,
|
||||
secret: undefined,
|
||||
name: undefined,
|
||||
logo: undefined,
|
||||
description: undefined,
|
||||
status: CommonStatusEnum.ENABLE,
|
||||
accessTokenValiditySeconds: 30 * 60,
|
||||
refreshTokenValiditySeconds: 30 * 24 * 60,
|
||||
redirectUris: [],
|
||||
authorizedGrantTypes: [],
|
||||
scopes: [],
|
||||
autoApproveScopes: [],
|
||||
authorities: [],
|
||||
resourceIds: [],
|
||||
additionalInformation: undefined,
|
||||
};
|
||||
this.resetForm("form");
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNo = 1;
|
||||
this.getList();
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
/** 新增按钮操作 */
|
||||
handleAdd() {
|
||||
this.reset();
|
||||
this.open = true;
|
||||
this.title = "添加 OAuth2 客户端";
|
||||
},
|
||||
/** 修改按钮操作 */
|
||||
handleUpdate(row) {
|
||||
this.reset();
|
||||
const id = row.id;
|
||||
getOAuth2Client(id).then(response => {
|
||||
this.form = response.data;
|
||||
this.open = true;
|
||||
this.title = "修改 OAuth2 客户端";
|
||||
});
|
||||
},
|
||||
/** 提交按钮 */
|
||||
submitForm() {
|
||||
this.$refs["form"].validate(valid => {
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
// 修改的提交
|
||||
if (this.form.id != null) {
|
||||
updateOAuth2Client(this.form).then(response => {
|
||||
this.$modal.msgSuccess("修改成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
});
|
||||
return;
|
||||
}
|
||||
// 添加的提交
|
||||
createOAuth2Client(this.form).then(response => {
|
||||
this.$modal.msgSuccess("新增成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
});
|
||||
});
|
||||
},
|
||||
/** 删除按钮操作 */
|
||||
handleDelete(row) {
|
||||
const id = row.id;
|
||||
this.$modal.confirm('是否确认删除客户端编号为"' + row.clientId + '"的数据项?').then(function() {
|
||||
return deleteOAuth2Client(id);
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("删除成功");
|
||||
}).catch(() => {});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
@ -3,11 +3,14 @@
|
||||
<doc-alert title="用户体系" url="https://doc.iocoder.cn/user-center/" />
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" label-width="68px">
|
||||
<el-form-item label="登录地址" prop="userIp">
|
||||
<el-input v-model="queryParams.userIp" placeholder="请输入登录地址" clearable @keyup.enter.native="handleQuery"/>
|
||||
<el-form-item label="用户编号" prop="userId">
|
||||
<el-input v-model="queryParams.userId" placeholder="请输入用户编号" clearable @keyup.enter.native="handleQuery"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="用户名称" prop="username">
|
||||
<el-input v-model="queryParams.username" placeholder="请输入用户名称" clearable @keyup.enter.native="handleQuery"/>
|
||||
<el-form-item label="用户类型" prop="userType">
|
||||
<el-select v-model="queryParams.userType" placeholder="请选择用户类型" clearable>
|
||||
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.USER_TYPE)"
|
||||
:key="dict.value" :label="dict.label" :value="dict.value"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
|
||||
@ -16,20 +19,28 @@
|
||||
|
||||
</el-form>
|
||||
<el-table v-loading="loading" :data="list" style="width: 100%;">
|
||||
<el-table-column label="会话编号" align="center" prop="id" width="300" />
|
||||
<el-table-column label="登录名称" align="center" prop="username" width="100" />
|
||||
<el-table-column label="部门名称" align="center" prop="deptName" width="100" />
|
||||
<el-table-column label="登录地址" align="center" prop="userIp" width="100" />
|
||||
<el-table-column label="userAgent" align="center" prop="userAgent" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="登录时间" align="center" prop="createTime" width="180">
|
||||
<el-table-column label="访问令牌" align="center" prop="accessToken" width="300" />
|
||||
<el-table-column label="刷新令牌" align="center" prop="refreshToken" width="300" />
|
||||
<el-table-column label="用户编号" align="center" prop="userId" />
|
||||
<el-table-column label="用户类型" align="center" prop="userType" width="100">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :type="DICT_TYPE.USER_TYPE" :value="scope.row.userType"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="过期时间" align="center" prop="expiresTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.expiresTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleForceLogout(scope.row)"
|
||||
v-hasPermi="['system:user-session:delete']">强退</el-button>
|
||||
v-hasPermi="['system:oauth2-token:delete']">强退</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@ -40,10 +51,10 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { list, forceLogout } from "@/api/system/session";
|
||||
import { getAccessTokenPage, deleteAccessToken } from "@/api/system/oauth2/oauth2Token";
|
||||
|
||||
export default {
|
||||
name: "Online",
|
||||
name: "OAuth2Token",
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
@ -56,8 +67,8 @@ export default {
|
||||
queryParams: {
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
userIp: undefined,
|
||||
username: undefined
|
||||
userId: undefined,
|
||||
userType: undefined
|
||||
}
|
||||
};
|
||||
},
|
||||
@ -68,7 +79,7 @@ export default {
|
||||
/** 查询登录日志列表 */
|
||||
getList() {
|
||||
this.loading = true;
|
||||
list(this.queryParams).then(response => {
|
||||
getAccessTokenPage(this.queryParams).then(response => {
|
||||
this.list = response.data.list;
|
||||
this.total = response.data.total;
|
||||
this.loading = false;
|
||||
@ -86,8 +97,8 @@ export default {
|
||||
},
|
||||
/** 强退按钮操作 */
|
||||
handleForceLogout(row) {
|
||||
this.$modal.confirm('是否确认强退名称为"' + row.username + '"的数据项?').then(function() {
|
||||
return forceLogout(row.id);
|
||||
this.$modal.confirm('是否确认强退令牌为"' + row.accessToken + '"的数据项?').then(function() {
|
||||
return deleteAccessToken(row.accessToken);
|
||||
}).then(() => {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("强退成功");
|
@ -54,7 +54,7 @@
|
||||
<el-table-column label="描述" align="center" prop="description"/>
|
||||
<el-table-column label="标签" align="center" prop="tags">
|
||||
<template slot-scope="scope">
|
||||
<el-tag :disable-transitions="true" v-for="(tag, index) in scope.row.tags" :index="index">
|
||||
<el-tag :disable-transitions="true" :key="index" v-for="(tag, index) in scope.row.tags" :index="index">
|
||||
{{ tag }}
|
||||
</el-tag>
|
||||
</template>
|
||||
|
@ -147,7 +147,7 @@
|
||||
<el-form-item label="手机号" prop="mobile">
|
||||
<el-input v-model="sendSmsForm.mobile" placeholder="请输入手机号" />
|
||||
</el-form-item>
|
||||
<el-form-item v-for="param in sendSmsForm.params" :label="'参数 {' + param + '}'" :prop="'templateParams.' + param">
|
||||
<el-form-item v-for="param in sendSmsForm.params" :key="param" :label="'参数 {' + param + '}'" :prop="'templateParams.' + param">
|
||||
<el-input v-model="sendSmsForm.templateParams[param]" :placeholder="'请输入 ' + param + ' 参数'" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
@ -111,7 +111,7 @@
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="归属部门" prop="deptId">
|
||||
<treeselect v-model="form.deptId" :options="deptOptions" :show-count="true"
|
||||
<treeselect v-model="form.deptId" :options="deptOptions" :show-count="true" :clearable="false"
|
||||
placeholder="请选择归属部门" :normalizer="normalizer"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@ -237,7 +237,7 @@ import {
|
||||
resetUserPwd,
|
||||
updateUser
|
||||
} from "@/api/system/user";
|
||||
import {getToken} from "@/utils/auth";
|
||||
import {getAccessToken} from "@/utils/auth";
|
||||
import Treeselect from "@riophae/vue-treeselect";
|
||||
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
|
||||
|
||||
@ -304,7 +304,7 @@ export default {
|
||||
// 设置上传的请求头部
|
||||
headers: getBaseHeader(),
|
||||
// 上传的地址
|
||||
url: process.env.VUE_APP_BASE_API + '/admin-api/' + "/system/user/import"
|
||||
url: process.env.VUE_APP_BASE_API + '/admin-api/system/user/import'
|
||||
},
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
|
Reference in New Issue
Block a user