mirror of
https://gitee.com/ssssssss-team/magic-boot.git
synced 2025-02-21 02:12:50 +08:00
在线用户,验证码,bug修复,代码优化
This commit is contained in:
parent
6401c76e65
commit
bdea28e66e
10
data/magic-api/api/后台/在线用户/group.json
Normal file
10
data/magic-api/api/后台/在线用户/group.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"properties" : { },
|
||||
"id" : "d95a58e77d314370862ffc4cdfdb8283",
|
||||
"name" : "在线用户",
|
||||
"type" : "api",
|
||||
"parentId" : "02df51e4d7184780a98b632f43dc5848",
|
||||
"path" : "/online",
|
||||
"paths" : [ ],
|
||||
"options" : [ ]
|
||||
}
|
126
data/magic-api/api/后台/在线用户/列表.ms
Normal file
126
data/magic-api/api/后台/在线用户/列表.ms
Normal file
@ -0,0 +1,126 @@
|
||||
{
|
||||
"properties" : { },
|
||||
"id" : "d5ccf62c6d2c482e995ce7fe237e9ed3",
|
||||
"script" : null,
|
||||
"groupId" : "d95a58e77d314370862ffc4cdfdb8283",
|
||||
"name" : "列表",
|
||||
"createTime" : null,
|
||||
"updateTime" : 1647396892675,
|
||||
"lock" : null,
|
||||
"createBy" : null,
|
||||
"updateBy" : null,
|
||||
"path" : "/list",
|
||||
"method" : "POST",
|
||||
"parameters" : [ ],
|
||||
"options" : [ ],
|
||||
"requestBody" : "",
|
||||
"headers" : [ ],
|
||||
"paths" : [ ],
|
||||
"responseBody" : "{\n \"code\": 200,\n \"message\": \"success\",\n \"data\": [\"cb63a01c-63d7-4722-a2c4-48fffa4b3502\", \"876dd178-0065-42d5-989a-a6ef460113b1\"],\n \"timestamp\": 1647396771372,\n \"executeTime\": 11\n}",
|
||||
"description" : null,
|
||||
"requestBodyDefinition" : null,
|
||||
"responseBodyDefinition" : {
|
||||
"name" : "",
|
||||
"value" : "",
|
||||
"description" : "",
|
||||
"required" : false,
|
||||
"dataType" : "Object",
|
||||
"type" : null,
|
||||
"defaultValue" : null,
|
||||
"validateType" : "",
|
||||
"error" : "",
|
||||
"expression" : "",
|
||||
"children" : [ {
|
||||
"name" : "code",
|
||||
"value" : "200",
|
||||
"description" : "",
|
||||
"required" : false,
|
||||
"dataType" : "Integer",
|
||||
"type" : null,
|
||||
"defaultValue" : null,
|
||||
"validateType" : "",
|
||||
"error" : "",
|
||||
"expression" : "",
|
||||
"children" : [ ]
|
||||
}, {
|
||||
"name" : "message",
|
||||
"value" : "success",
|
||||
"description" : "",
|
||||
"required" : false,
|
||||
"dataType" : "String",
|
||||
"type" : null,
|
||||
"defaultValue" : null,
|
||||
"validateType" : "",
|
||||
"error" : "",
|
||||
"expression" : "",
|
||||
"children" : [ ]
|
||||
}, {
|
||||
"name" : "data",
|
||||
"value" : "",
|
||||
"description" : "",
|
||||
"required" : false,
|
||||
"dataType" : "Object",
|
||||
"type" : null,
|
||||
"defaultValue" : null,
|
||||
"validateType" : "",
|
||||
"error" : "",
|
||||
"expression" : "",
|
||||
"children" : [ {
|
||||
"name" : "",
|
||||
"value" : "cb63a01c-63d7-4722-a2c4-48fffa4b3502",
|
||||
"description" : "",
|
||||
"required" : false,
|
||||
"dataType" : "String",
|
||||
"type" : null,
|
||||
"defaultValue" : null,
|
||||
"validateType" : "",
|
||||
"error" : "",
|
||||
"expression" : "",
|
||||
"children" : [ ]
|
||||
} ]
|
||||
}, {
|
||||
"name" : "timestamp",
|
||||
"value" : "1647396771372",
|
||||
"description" : "",
|
||||
"required" : false,
|
||||
"dataType" : "Long",
|
||||
"type" : null,
|
||||
"defaultValue" : null,
|
||||
"validateType" : "",
|
||||
"error" : "",
|
||||
"expression" : "",
|
||||
"children" : [ ]
|
||||
}, {
|
||||
"name" : "executeTime",
|
||||
"value" : "11",
|
||||
"description" : "",
|
||||
"required" : false,
|
||||
"dataType" : "Object",
|
||||
"type" : null,
|
||||
"defaultValue" : null,
|
||||
"validateType" : "",
|
||||
"error" : "",
|
||||
"expression" : "",
|
||||
"children" : [ ]
|
||||
} ]
|
||||
}
|
||||
}
|
||||
================================
|
||||
import cn.dev33.satoken.stp.StpUtil
|
||||
var tokens = StpUtil.searchTokenValue("", -1, 0).map(it => it.replace('token:login:token:', ''))
|
||||
|
||||
return db.page("""
|
||||
select
|
||||
su.username,
|
||||
so.name office_name,
|
||||
sll.token,
|
||||
sll.ip,
|
||||
sll.browser,
|
||||
sll.os,
|
||||
sll.create_date
|
||||
from sys_login_log sll left join sys_user su on sll.username = su.username left join sys_office so on su.office_id = so.id
|
||||
where sll.token in (#{tokens})
|
||||
?{username, and su.username like concat('%', #{username}, '%')}
|
||||
?{ip, and sll.ip like concat('%', #{ip}, '%')}
|
||||
order by sll.create_date desc
|
||||
""")
|
26
data/magic-api/api/后台/在线用户/踢人.ms
Normal file
26
data/magic-api/api/后台/在线用户/踢人.ms
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"properties" : { },
|
||||
"id" : "01fb971510764df3be20209905e86fab",
|
||||
"script" : null,
|
||||
"groupId" : "d95a58e77d314370862ffc4cdfdb8283",
|
||||
"name" : "踢人",
|
||||
"createTime" : null,
|
||||
"updateTime" : 1647397091507,
|
||||
"lock" : null,
|
||||
"createBy" : null,
|
||||
"updateBy" : null,
|
||||
"path" : "/logout",
|
||||
"method" : "GET",
|
||||
"parameters" : [ ],
|
||||
"options" : [ ],
|
||||
"requestBody" : "",
|
||||
"headers" : [ ],
|
||||
"paths" : [ ],
|
||||
"responseBody" : null,
|
||||
"description" : null,
|
||||
"requestBodyDefinition" : null,
|
||||
"responseBodyDefinition" : null
|
||||
}
|
||||
================================
|
||||
import cn.dev33.satoken.stp.StpUtil
|
||||
return StpUtil.logoutByTokenValue(token);
|
@ -5,13 +5,49 @@
|
||||
"groupId" : "1952f25c81084e24b55b11385767dc38",
|
||||
"name" : "登录",
|
||||
"createTime" : null,
|
||||
"updateTime" : 1646544815203,
|
||||
"updateTime" : 1647396539380,
|
||||
"lock" : "0",
|
||||
"createBy" : null,
|
||||
"updateBy" : null,
|
||||
"path" : "/login",
|
||||
"method" : "POST",
|
||||
"parameters" : [ ],
|
||||
"parameters" : [ {
|
||||
"name" : "username",
|
||||
"value" : null,
|
||||
"description" : null,
|
||||
"required" : false,
|
||||
"dataType" : "String",
|
||||
"type" : null,
|
||||
"defaultValue" : null,
|
||||
"validateType" : null,
|
||||
"error" : null,
|
||||
"expression" : null,
|
||||
"children" : null
|
||||
}, {
|
||||
"name" : "password",
|
||||
"value" : null,
|
||||
"description" : null,
|
||||
"required" : false,
|
||||
"dataType" : "String",
|
||||
"type" : null,
|
||||
"defaultValue" : null,
|
||||
"validateType" : null,
|
||||
"error" : null,
|
||||
"expression" : null,
|
||||
"children" : null
|
||||
}, {
|
||||
"name" : "code",
|
||||
"value" : null,
|
||||
"description" : null,
|
||||
"required" : false,
|
||||
"dataType" : "String",
|
||||
"type" : null,
|
||||
"defaultValue" : null,
|
||||
"validateType" : null,
|
||||
"error" : null,
|
||||
"expression" : null,
|
||||
"children" : null
|
||||
} ],
|
||||
"options" : [ {
|
||||
"name" : "require_login",
|
||||
"value" : "false",
|
||||
@ -28,7 +64,7 @@
|
||||
"requestBody" : "{\r\n \"username\": \"admin\",\r\n \"password\": \"123456\"\r\n}",
|
||||
"headers" : [ ],
|
||||
"paths" : [ ],
|
||||
"responseBody" : "{\"code\":200,\"message\":\"success\",\"data\":\"60f0ddf8-c42c-4b3e-802f-7482f6bd1075\",\"timestamp\":1646493023758,\"executeTime\":316}",
|
||||
"responseBody" : "{\n \"code\": 200,\n \"message\": \"success\",\n \"data\": \"cb63a01c-63d7-4722-a2c4-48fffa4b3502\",\n \"timestamp\": 1647395770056,\n \"executeTime\": 20\n}",
|
||||
"description" : null,
|
||||
"requestBodyDefinition" : {
|
||||
"name" : "",
|
||||
@ -104,10 +140,10 @@
|
||||
"children" : [ ]
|
||||
}, {
|
||||
"name" : "data",
|
||||
"value" : "60f0ddf8-c42c-4b3e-802f-7482f6bd1075",
|
||||
"value" : "cb63a01c-63d7-4722-a2c4-48fffa4b3502",
|
||||
"description" : "",
|
||||
"required" : false,
|
||||
"dataType" : "String",
|
||||
"dataType" : "Object",
|
||||
"type" : null,
|
||||
"defaultValue" : null,
|
||||
"validateType" : "",
|
||||
@ -116,7 +152,7 @@
|
||||
"children" : [ ]
|
||||
}, {
|
||||
"name" : "timestamp",
|
||||
"value" : "1646493023758",
|
||||
"value" : "1647395770056",
|
||||
"description" : "",
|
||||
"required" : false,
|
||||
"dataType" : "Long",
|
||||
@ -128,7 +164,7 @@
|
||||
"children" : [ ]
|
||||
}, {
|
||||
"name" : "executeTime",
|
||||
"value" : "316",
|
||||
"value" : "20",
|
||||
"description" : "",
|
||||
"required" : false,
|
||||
"dataType" : "Integer",
|
||||
@ -147,6 +183,15 @@ import 'cn.dev33.satoken.secure.SaSecureUtil';
|
||||
import 'cn.dev33.satoken.stp.StpUtil';
|
||||
import env;
|
||||
import request;
|
||||
import org.ssssssss.magicboot.model.CodeCacheMap
|
||||
import cn.hutool.http.useragent.UserAgentUtil
|
||||
import cn.hutool.http.useragent.UserAgent
|
||||
|
||||
UserAgent ua = UserAgentUtil.parse(request.getHeaders("User-Agent")[0])
|
||||
|
||||
if(body.code != CodeCacheMap.get(body.uuid)){
|
||||
exit 0, '验证码错误'
|
||||
}
|
||||
|
||||
var user
|
||||
if(env.get('super-password') == body.password){
|
||||
@ -158,15 +203,21 @@ if(env.get('super-password') == body.password){
|
||||
var loginLog = {
|
||||
username: body.username,
|
||||
type: '成功',
|
||||
ip: request.getClientIP()
|
||||
ip: request.getClientIP(),
|
||||
browser: ua.getBrowser().toString(),
|
||||
os: ua.getOs().toString()
|
||||
}
|
||||
|
||||
if(!user){
|
||||
loginLog.failPassword = body.password
|
||||
loginLog.type = '失败'
|
||||
db.table("sys_login_log").primary("id").save(loginLog);
|
||||
exit 0,'用户名或密码错误'
|
||||
}
|
||||
db.table("sys_login_log").primary("id").save(loginLog);
|
||||
|
||||
StpUtil.login(user.id)
|
||||
var token = StpUtil.getTokenValueByLoginId(user.id)
|
||||
loginLog.token = token
|
||||
db.table("sys_login_log").primary("id").save(loginLog);
|
||||
CodeCacheMap.remove(body.uuid)
|
||||
return StpUtil.getTokenValueByLoginId(user.id)
|
149
data/magic-api/api/后台/安全/验证码.ms
Normal file
149
data/magic-api/api/后台/安全/验证码.ms
Normal file
@ -0,0 +1,149 @@
|
||||
{
|
||||
"properties" : { },
|
||||
"id" : "be89865140ab409085db6d0cc6d82452",
|
||||
"script" : null,
|
||||
"groupId" : "1952f25c81084e24b55b11385767dc38",
|
||||
"name" : "验证码",
|
||||
"createTime" : null,
|
||||
"updateTime" : 1647333578623,
|
||||
"lock" : null,
|
||||
"createBy" : null,
|
||||
"updateBy" : null,
|
||||
"path" : "/verification/code",
|
||||
"method" : "GET",
|
||||
"parameters" : [ ],
|
||||
"options" : [ {
|
||||
"name" : "require_login",
|
||||
"value" : "false",
|
||||
"description" : "该接口需要登录才允许访问",
|
||||
"required" : false,
|
||||
"dataType" : "String",
|
||||
"type" : null,
|
||||
"defaultValue" : null,
|
||||
"validateType" : null,
|
||||
"error" : null,
|
||||
"expression" : null,
|
||||
"children" : null
|
||||
} ],
|
||||
"requestBody" : "",
|
||||
"headers" : [ ],
|
||||
"paths" : [ ],
|
||||
"responseBody" : "{\n \"code\": 200,\n \"message\": \"success\",\n \"data\": {\n \"img\": \"data:image/png;base64,[B@65617f6d\",\n \"uuid\": \"9a080e570e0346bf8b80c035c977c888\"\n },\n \"timestamp\": 1647333558240,\n \"executeTime\": 15\n}",
|
||||
"description" : null,
|
||||
"requestBodyDefinition" : null,
|
||||
"responseBodyDefinition" : {
|
||||
"name" : "",
|
||||
"value" : "",
|
||||
"description" : "",
|
||||
"required" : false,
|
||||
"dataType" : "Object",
|
||||
"type" : null,
|
||||
"defaultValue" : null,
|
||||
"validateType" : "",
|
||||
"error" : "",
|
||||
"expression" : "",
|
||||
"children" : [ {
|
||||
"name" : "code",
|
||||
"value" : "200",
|
||||
"description" : "",
|
||||
"required" : false,
|
||||
"dataType" : "Integer",
|
||||
"type" : null,
|
||||
"defaultValue" : null,
|
||||
"validateType" : "",
|
||||
"error" : "",
|
||||
"expression" : "",
|
||||
"children" : [ ]
|
||||
}, {
|
||||
"name" : "message",
|
||||
"value" : "success",
|
||||
"description" : "",
|
||||
"required" : false,
|
||||
"dataType" : "String",
|
||||
"type" : null,
|
||||
"defaultValue" : null,
|
||||
"validateType" : "",
|
||||
"error" : "",
|
||||
"expression" : "",
|
||||
"children" : [ ]
|
||||
}, {
|
||||
"name" : "data",
|
||||
"value" : "",
|
||||
"description" : "",
|
||||
"required" : false,
|
||||
"dataType" : "Object",
|
||||
"type" : null,
|
||||
"defaultValue" : null,
|
||||
"validateType" : "",
|
||||
"error" : "",
|
||||
"expression" : "",
|
||||
"children" : [ {
|
||||
"name" : "img",
|
||||
"value" : "data:image/png;base64,[B@65617f6d",
|
||||
"description" : "",
|
||||
"required" : false,
|
||||
"dataType" : "String",
|
||||
"type" : null,
|
||||
"defaultValue" : null,
|
||||
"validateType" : "",
|
||||
"error" : "",
|
||||
"expression" : "",
|
||||
"children" : [ ]
|
||||
}, {
|
||||
"name" : "uuid",
|
||||
"value" : "9a080e570e0346bf8b80c035c977c888",
|
||||
"description" : "",
|
||||
"required" : false,
|
||||
"dataType" : "String",
|
||||
"type" : null,
|
||||
"defaultValue" : null,
|
||||
"validateType" : "",
|
||||
"error" : "",
|
||||
"expression" : "",
|
||||
"children" : [ ]
|
||||
} ]
|
||||
}, {
|
||||
"name" : "timestamp",
|
||||
"value" : "1647333558240",
|
||||
"description" : "",
|
||||
"required" : false,
|
||||
"dataType" : "Long",
|
||||
"type" : null,
|
||||
"defaultValue" : null,
|
||||
"validateType" : "",
|
||||
"error" : "",
|
||||
"expression" : "",
|
||||
"children" : [ ]
|
||||
}, {
|
||||
"name" : "executeTime",
|
||||
"value" : "15",
|
||||
"description" : "",
|
||||
"required" : false,
|
||||
"dataType" : "Integer",
|
||||
"type" : null,
|
||||
"defaultValue" : null,
|
||||
"validateType" : "",
|
||||
"error" : "",
|
||||
"expression" : "",
|
||||
"children" : [ ]
|
||||
} ]
|
||||
}
|
||||
}
|
||||
================================
|
||||
import cn.hutool.captcha.LineCaptcha
|
||||
import cn.hutool.captcha.CaptchaUtil
|
||||
import log
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.OutputStream
|
||||
import org.ssssssss.magicboot.model.CodeCacheMap
|
||||
|
||||
LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(100, 48);
|
||||
var uuid = UUID.randomUUID().toString().replace('-', '')
|
||||
CodeCacheMap.put(uuid, lineCaptcha.getCode())
|
||||
OutputStream bOut = new ByteArrayOutputStream();
|
||||
lineCaptcha.write(bOut)
|
||||
var bytes = bOut.toByteArray()
|
||||
return {
|
||||
img: bytes,
|
||||
uuid: uuid
|
||||
}
|
@ -23,10 +23,6 @@
|
||||
border: 1px solid #efefef
|
||||
}
|
||||
body{
|
||||
--el-dialog__wrapper-bottom: 15vh;
|
||||
--el-dialog__wrapper-top: 15vh;
|
||||
--el-dialog__body-max-height: 60vh;
|
||||
|
||||
--mb-main-color: #409EFF;
|
||||
--mb-sidebar-width: 240px;
|
||||
--mb-main-icon-color: #909399;
|
||||
|
@ -24,7 +24,7 @@
|
||||
import {ref, reactive, getCurrentInstance, defineExpose } from 'vue'
|
||||
const { proxy } = getCurrentInstance()
|
||||
const rules = reactive(getRules())
|
||||
const formData = ref(getFormData())
|
||||
const formData = ref(initFormData())
|
||||
const dataForm = ref()
|
||||
const props = defineProps({
|
||||
form: {
|
||||
@ -42,14 +42,14 @@
|
||||
proxy.$common.setDefaultValue(props.form.props, 'labelPosition', 'right')
|
||||
proxy.$common.setDefaultValue(props.form.props, 'labelWidth', '120px')
|
||||
|
||||
if(props.detail.formData){
|
||||
if(props.detail && props.detail.formData){
|
||||
if(props.detail.handlerFormData){
|
||||
props.detail.handlerFormData(props.detail.formData)
|
||||
}
|
||||
formData.value = props.detail.formData
|
||||
}
|
||||
|
||||
if(props.detail.request){
|
||||
if(props.detail && props.detail.request){
|
||||
|
||||
}
|
||||
|
||||
@ -65,16 +65,21 @@
|
||||
return _rules
|
||||
}
|
||||
|
||||
function getFormData() {
|
||||
function initFormData() {
|
||||
var data = {}
|
||||
props.form.rows.forEach(row => {
|
||||
row.cols.forEach(col => {
|
||||
data[col.name] = col.defaultValue === null ? col.defaultValue : col.defaultValue || ''
|
||||
// data[col.name] = col.defaultValue === null ? col.defaultValue : col.defaultValue || ''
|
||||
data[col.name] = undefined
|
||||
})
|
||||
})
|
||||
return data
|
||||
}
|
||||
|
||||
function getFormData(){
|
||||
return formData.value
|
||||
}
|
||||
|
||||
function save(d) {
|
||||
dataForm.value.validate((valid) => {
|
||||
if (valid) {
|
||||
@ -87,7 +92,7 @@
|
||||
type: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
if(props.detail.formData){
|
||||
if(props.detail && props.detail.formData){
|
||||
props.detail.formData = {}
|
||||
}
|
||||
d.hide()
|
||||
@ -109,6 +114,6 @@
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({ save, getDetail })
|
||||
defineExpose({ save, getDetail, getFormData })
|
||||
|
||||
</script>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
|
||||
<mb-search v-if="table.where" :where="table.where" :no-reset="search.noReset" @search="reload" />
|
||||
<mb-search v-if="table.where" :where="table.where" :no-reset="search && search.noReset" @search="reload" />
|
||||
|
||||
<el-row class="toolbar-container">
|
||||
<div v-for="(it, i) in tools" :key="i">
|
||||
|
@ -1,10 +1,22 @@
|
||||
<template>
|
||||
<el-dialog :fullscreen="fullscreen" :width="width" :title="title" v-model="dialogVisible" :close-on-click-modal="false" :key="dialogKey" :append-to-body="true" draggable @opened="opened">
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
:custom-class="customClass"
|
||||
:fullscreen="fullscreen"
|
||||
:width="width"
|
||||
:title="title"
|
||||
:close-on-click-modal="false"
|
||||
:destroy-on-close="destroyOnClose"
|
||||
:append-to-body="true"
|
||||
draggable
|
||||
@open="$emit('open')"
|
||||
@close="$emit('close')"
|
||||
>
|
||||
<slot name="content" />
|
||||
<template #footer>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<slot name="btns">
|
||||
<el-button @click="dialogVisible = false">
|
||||
<el-button @click="hide">
|
||||
关闭
|
||||
</el-button>
|
||||
<el-button type="primary" :loading="confirmLoading" @click="confirmClick">
|
||||
@ -17,9 +29,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
emits: ['confirm-click'],
|
||||
emits: ['confirm-click', 'open', 'close'],
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
@ -33,11 +44,7 @@ export default {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
opened: {
|
||||
type: Function,
|
||||
default: () => {}
|
||||
},
|
||||
autoKey: {
|
||||
destroyOnClose: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
@ -46,19 +53,20 @@ export default {
|
||||
return {
|
||||
dialogVisible: false,
|
||||
confirmLoading: false,
|
||||
dialogKey: 'mbDialog',
|
||||
customClass: 'mbDialog' + this.$common.uuid()
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
dialogVisible(value) {
|
||||
if(value){
|
||||
this.addStyle()
|
||||
}else{
|
||||
this.removeStyle()
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (this.fullscreen) {
|
||||
document.body.style.setProperty('--el-dialog__wrapper-bottom', '0vh')
|
||||
document.body.style.setProperty('--el-dialog__wrapper-top', '0vh')
|
||||
document.body.style.setProperty('--el-dialog__body-max-height', '100vh')
|
||||
} else {
|
||||
document.body.style.setProperty('--el-dialog__wrapper-bottom', '15vh')
|
||||
document.body.style.setProperty('--el-dialog__wrapper-top', '15vh')
|
||||
document.body.style.setProperty('--el-dialog__body-max-height', '60vh')
|
||||
}
|
||||
this.addStyle()
|
||||
},
|
||||
methods: {
|
||||
confirmClick() {
|
||||
@ -71,33 +79,43 @@ export default {
|
||||
this.confirmLoading = false
|
||||
},
|
||||
show() {
|
||||
if(this.autoKey){
|
||||
this.dialogKey = Math.random()
|
||||
}
|
||||
this.dialogVisible = true
|
||||
},
|
||||
hide() {
|
||||
this.dialogVisible = false
|
||||
},
|
||||
addStyle(){
|
||||
var componentStyle = document.createElement("style");
|
||||
var cc = this.customClass
|
||||
if (this.fullscreen) {
|
||||
componentStyle.innerHTML = `
|
||||
.${cc}{
|
||||
margin-top: 0vh;
|
||||
margin-bottom: 0vh;
|
||||
}
|
||||
.${cc} .el-dialog__body{
|
||||
max-height: 100vh;
|
||||
}
|
||||
`
|
||||
} else {
|
||||
componentStyle.innerHTML = `
|
||||
.${cc}{
|
||||
margin-top: 10vh;
|
||||
margin-bottom: 10vh;
|
||||
}
|
||||
.${cc} .el-dialog__body{
|
||||
max-height: 60vh;
|
||||
overflow: auto;
|
||||
}
|
||||
`
|
||||
}
|
||||
componentStyle.id = cc
|
||||
document.head.appendChild(componentStyle);
|
||||
},
|
||||
removeStyle(){
|
||||
document.getElementById(this.customClass) && document.getElementById(this.customClass).remove()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.el-dialog__wrapper{
|
||||
padding-bottom: var(--el-dialog__wrapper-bottom);
|
||||
padding-top: var(--el-dialog__wrapper-top);
|
||||
overflow: hidden;
|
||||
}
|
||||
.el-dialog__wrapper >>> .el-dialog{
|
||||
margin-top: 0vh!important;
|
||||
}
|
||||
.el-dialog__wrapper >>> .el-dialog__body{
|
||||
max-height: var(--el-dialog__body-max-height);
|
||||
overflow: auto;
|
||||
padding: 25px!important;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
@ -1,17 +0,0 @@
|
||||
<template>
|
||||
<el-radio-group v-model="modelValue" v-bind="props.props">
|
||||
<el-radio-button v-for="it in options" :label="it.value" :disabled="it.disabled" :name="it.disabled">{{ it.label }}</el-radio-button>
|
||||
</el-radio-group>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { watch } from 'vue'
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const props = defineProps({
|
||||
modelValue: String,
|
||||
options: Array
|
||||
})
|
||||
watch(() => props.modelValue, (value) => {
|
||||
emit('update:modelValue', value)
|
||||
})
|
||||
</script>
|
@ -1,3 +0,0 @@
|
||||
<template>
|
||||
|
||||
</template>
|
@ -37,7 +37,7 @@
|
||||
|
||||
<script setup>
|
||||
|
||||
import { nextTick, watch } from 'vue'
|
||||
import { nextTick, watch, onMounted } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
where: {
|
||||
@ -60,7 +60,7 @@ watch(() => props.where,() => {
|
||||
console.log(props.where)
|
||||
})
|
||||
|
||||
const emit = defineEmits(['search'])
|
||||
const emit = defineEmits(['search', 'mounted'])
|
||||
|
||||
function input(input){
|
||||
if(input){
|
||||
@ -101,6 +101,10 @@ function reset() {
|
||||
nextTick(() => emit('search'))
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
emit('mounted')
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
@ -1,5 +1,6 @@
|
||||
<template>
|
||||
<el-table-column
|
||||
v-bind="col.props"
|
||||
:key="col.field"
|
||||
:label="col.label"
|
||||
:prop="col.field"
|
||||
|
94
magic-boot-ui/src/components/magic/form/mb-checkbox.vue
Normal file
94
magic-boot-ui/src/components/magic/form/mb-checkbox.vue
Normal file
@ -0,0 +1,94 @@
|
||||
<template>
|
||||
<el-checkbox-group
|
||||
v-model="modelValue"
|
||||
:size="size"
|
||||
:disabled="disabled"
|
||||
:min="min"
|
||||
:max="max"
|
||||
:text-color="textColor"
|
||||
:fill="fill"
|
||||
@change="change"
|
||||
>
|
||||
<el-checkbox-button v-if="button" v-for="it in options" v-bind="it" :label="it[valueField]">
|
||||
{{ it[labelField] }}
|
||||
</el-checkbox-button>
|
||||
<el-checkbox v-if="!button" v-for="it in options" v-bind="it" :label="it[valueField]">
|
||||
{{ it[labelField] }}
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {watch, ref, getCurrentInstance} from "vue";
|
||||
import request from '@/scripts/request'
|
||||
const emit = defineEmits(['update:modelValue', 'change'])
|
||||
const { proxy } = getCurrentInstance()
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
type: String,
|
||||
button: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
options: Array,
|
||||
url: String,
|
||||
params: Object,
|
||||
method: {
|
||||
type: String,
|
||||
default: 'get'
|
||||
},
|
||||
labelField: {
|
||||
type: String,
|
||||
default: 'label'
|
||||
},
|
||||
valueField: {
|
||||
type: String,
|
||||
default: 'value'
|
||||
},
|
||||
size: String,
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
min: Number,
|
||||
max: Number,
|
||||
textColor: {
|
||||
type: String,
|
||||
default: '#fff'
|
||||
},
|
||||
fill: {
|
||||
type: String,
|
||||
default: '#409EFF'
|
||||
}
|
||||
})
|
||||
|
||||
const options = ref([])
|
||||
|
||||
if(props.type){
|
||||
options.value = proxy.$common.getDictType(props.type)
|
||||
}else if(props.url){
|
||||
request({
|
||||
url: props.url,
|
||||
method: props.method,
|
||||
params: props.params,
|
||||
data: props.params
|
||||
}).then(res => {
|
||||
options.value = res.data.list || res.data
|
||||
})
|
||||
}else if(props.options){
|
||||
options.value = props.options
|
||||
}
|
||||
|
||||
function change(value){
|
||||
emit('change', value)
|
||||
}
|
||||
|
||||
watch(() => props.modelValue, (value) => {
|
||||
console.log(value)
|
||||
emit('update:modelValue', value)
|
||||
})
|
||||
</script>
|
@ -12,7 +12,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { watch } from 'vue'
|
||||
import { watch, ref } from 'vue'
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const props = defineProps({
|
||||
modelValue: String,
|
||||
@ -23,11 +23,7 @@
|
||||
},
|
||||
format: {
|
||||
type: String,
|
||||
default: 'yyyy-MM-dd'
|
||||
},
|
||||
valueFormat: {
|
||||
type: String,
|
||||
default: 'yyyy-MM-dd'
|
||||
default: 'YYYY-MM-DD'
|
||||
},
|
||||
startPlaceholder: {
|
||||
type: String,
|
||||
@ -39,6 +35,7 @@
|
||||
},
|
||||
props: Object
|
||||
})
|
||||
const valueFormat = ref(props.format)
|
||||
watch(() => props.modelValue, (value) => {
|
||||
emit('update:modelValue', value)
|
||||
})
|
86
magic-boot-ui/src/components/magic/form/mb-radio.vue
Normal file
86
magic-boot-ui/src/components/magic/form/mb-radio.vue
Normal file
@ -0,0 +1,86 @@
|
||||
<template>
|
||||
<el-radio-group
|
||||
v-model="modelValue"
|
||||
:size="size"
|
||||
:disabled="disabled"
|
||||
:text-color="textColor"
|
||||
:fill="fill"
|
||||
@change="change"
|
||||
>
|
||||
<el-radio-button v-if="button" v-for="it in options" v-bind="it" :label="it[valueField]">
|
||||
{{ it[labelField] }}
|
||||
</el-radio-button>
|
||||
<el-radio v-if="!button" v-for="it in options" v-bind="it" :label="it[valueField]">
|
||||
{{ it[labelField] }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {watch, ref, getCurrentInstance} from "vue";
|
||||
import request from '@/scripts/request'
|
||||
const emit = defineEmits(['update:modelValue', 'change'])
|
||||
const { proxy } = getCurrentInstance()
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: String | Number | Boolean,
|
||||
type: String,
|
||||
button: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
options: Array,
|
||||
url: String,
|
||||
params: Object,
|
||||
method: {
|
||||
type: String,
|
||||
default: 'get'
|
||||
},
|
||||
labelField: {
|
||||
type: String,
|
||||
default: 'label'
|
||||
},
|
||||
valueField: {
|
||||
type: String,
|
||||
default: 'value'
|
||||
},
|
||||
size: String,
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
textColor: {
|
||||
type: String,
|
||||
default: '#fff'
|
||||
},
|
||||
fill: {
|
||||
type: String,
|
||||
default: '#409EFF'
|
||||
}
|
||||
})
|
||||
|
||||
const options = ref([])
|
||||
|
||||
if(props.type){
|
||||
options.value = proxy.$common.getDictType(props.type)
|
||||
}else if(props.url){
|
||||
request({
|
||||
url: props.url,
|
||||
method: props.method,
|
||||
params: props.params,
|
||||
data: props.params
|
||||
}).then(res => {
|
||||
options.value = res.data.list || res.data
|
||||
})
|
||||
}else if(props.options){
|
||||
options.value = props.options
|
||||
}
|
||||
|
||||
function change(value){
|
||||
emit('change', value)
|
||||
}
|
||||
|
||||
watch(() => props.modelValue, (value) => {
|
||||
emit('update:modelValue', value)
|
||||
})
|
||||
</script>
|
@ -11,9 +11,12 @@
|
||||
import { watch } from 'vue'
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const props = defineProps({
|
||||
modelValue: String,
|
||||
activeValue: String,
|
||||
inactiveValue: String,
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
activeValue: Boolean | String | Number,
|
||||
inactiveValue: Boolean | String | Number,
|
||||
props: Object
|
||||
})
|
||||
watch(() => props.modelValue, (value) => {
|
@ -26,11 +26,11 @@
|
||||
|
||||
<script setup>
|
||||
|
||||
import { watch, ref, reactive, defineExpose, nextTick, getCurrentInstance, onBeforeMount } from 'vue'
|
||||
import { watch, ref, reactive, defineExpose, nextTick, getCurrentInstance, onBeforeMount, onMounted } from 'vue'
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
|
||||
const emit = defineEmits(['update:select-values', 'check-change', 'node-click'])
|
||||
const emit = defineEmits(['update:select-values', 'check-change', 'node-click', 'mounted'])
|
||||
|
||||
const props = defineProps({
|
||||
url: {
|
||||
@ -75,6 +75,10 @@ const props = defineProps({
|
||||
}
|
||||
})
|
||||
|
||||
watch(() => props.checkedIds, (value) => {
|
||||
console.log(value)
|
||||
})
|
||||
|
||||
const tree = ref()
|
||||
const treeData = ref([])
|
||||
const searchData = ref([])
|
||||
@ -91,6 +95,10 @@ onBeforeMount(async () => {
|
||||
await loadTreeData()
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
emit('mounted')
|
||||
})
|
||||
|
||||
watch(() => props.selectValues, async () => {
|
||||
await loadTreeData()
|
||||
checkedAll(searchData.value, false)
|
1
magic-boot-ui/src/icons/verification-code.svg
Normal file
1
magic-boot-ui/src/icons/verification-code.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1647393180960" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3075" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M522.854 33.884L902.47 164.732c14.1 4.85 23.54 18.113 23.54 33.011v402.218c-10.714 238.49-386.18 383.703-402.118 389.764A34.91 34.91 0 0 1 511.505 992c-4.217 0-8.439-0.767-12.387-2.275C483.147 983.664 107.648 838.452 97 601.502V197.743c0-14.898 9.442-28.16 23.503-33.011L500.121 33.884a35.24 35.24 0 0 1 22.733 0z m243.984 299.804c-29.29-29.29-76.777-29.29-106.066 0L459.246 535.213l-95.46-95.46c-29.289-29.289-76.776-29.289-106.065 0-29.29 29.29-29.29 76.777 0 106.067l148.492 148.492c14.645 14.645 33.839 21.967 53.033 21.967l1.152-0.009c18.808-0.287 37.53-7.606 51.881-21.958l254.559-254.558c29.289-29.29 29.289-76.777 0-106.066z" fill="#909399" p-id="3076"></path></svg>
|
After Width: | Height: | Size: 1.0 KiB |
@ -56,7 +56,6 @@ import Sidebar from './sidebar/sidebar.vue'
|
||||
import { logout } from '@/scripts/auth'
|
||||
import { getCurrentInstance } from 'vue'
|
||||
const { proxy } = getCurrentInstance()
|
||||
console.log(proxy.$global.user.info.headPortrait)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@ -117,4 +116,4 @@ console.log(proxy.$global.user.info.headPortrait)
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
@ -47,6 +47,8 @@ export function login(data){
|
||||
var token = res.data
|
||||
setToken(token)
|
||||
resolve(token)
|
||||
}).catch((e) => {
|
||||
reject(e)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
132
magic-boot-ui/src/views/examples/test-mb-form.vue
Normal file
132
magic-boot-ui/src/views/examples/test-mb-form.vue
Normal file
@ -0,0 +1,132 @@
|
||||
<template>
|
||||
<mb-form ref="magicForm" v-bind="formOptions" />
|
||||
<el-button @click="getFormData">获取表单数据</el-button>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, getCurrentInstance } from 'vue'
|
||||
const { proxy } = getCurrentInstance()
|
||||
const magicForm = ref()
|
||||
const formOptions = reactive({
|
||||
form: {
|
||||
request: {
|
||||
url: "user/save",
|
||||
method: "post"
|
||||
},
|
||||
rows: [{
|
||||
gutter: 24,
|
||||
cols: [{
|
||||
span: 12,
|
||||
name: 'input',
|
||||
label: 'input'
|
||||
},{
|
||||
span: 12,
|
||||
name: 'switch',
|
||||
component: 'switch',
|
||||
label: 'switch',
|
||||
props: {
|
||||
activeValue: '1',
|
||||
inactiveValue: '0'
|
||||
}
|
||||
},{
|
||||
span: 12,
|
||||
name: 'checkbox',
|
||||
component: 'checkbox',
|
||||
label: 'checkbox',
|
||||
props: {
|
||||
type: 'office_type'
|
||||
}
|
||||
},{
|
||||
span: 12,
|
||||
name: 'checkboxButton',
|
||||
component: 'checkbox',
|
||||
label: 'checkboxButton',
|
||||
props: {
|
||||
type: 'office_type',
|
||||
button: true
|
||||
}
|
||||
},{
|
||||
span: 12,
|
||||
name: 'radio',
|
||||
component: 'radio',
|
||||
label: 'radio',
|
||||
props: {
|
||||
type: 'is_login'
|
||||
}
|
||||
},{
|
||||
span: 12,
|
||||
name: 'radioButton',
|
||||
component: 'radio',
|
||||
label: 'radioButton',
|
||||
props: {
|
||||
type: 'is_login',
|
||||
button: true
|
||||
}
|
||||
},{
|
||||
span: 12,
|
||||
name: 'date',
|
||||
component: 'date',
|
||||
label: 'date',
|
||||
props: {
|
||||
type: 'date'
|
||||
}
|
||||
},{
|
||||
span: 12,
|
||||
name: 'datetime',
|
||||
component: 'date',
|
||||
label: 'datetime',
|
||||
props: {
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss'
|
||||
}
|
||||
},{
|
||||
span: 12,
|
||||
name: 'daterange',
|
||||
component: 'date',
|
||||
label: 'daterange',
|
||||
props: {
|
||||
type: 'daterange'
|
||||
}
|
||||
},{
|
||||
span: 12,
|
||||
name: 'datetimerange',
|
||||
component: 'date',
|
||||
label: 'datetimerange',
|
||||
props: {
|
||||
type: 'datetimerange',
|
||||
format: 'YYYY-MM-DD HH:mm:ss'
|
||||
}
|
||||
},{
|
||||
component: 'treeselect',
|
||||
span: 12,
|
||||
name: 'treeselect',
|
||||
label: 'treeselect',
|
||||
rules: [{ required: true, message: '请选择组织机构', trigger: 'change' }],
|
||||
props: {
|
||||
url: 'user/offices'
|
||||
}
|
||||
}, {
|
||||
component: 'select',
|
||||
span: 12,
|
||||
name: 'select',
|
||||
label: 'select',
|
||||
rules: [{ required: true, message: '请选择角色', trigger: 'change' }],
|
||||
props: {
|
||||
url: 'role/all',
|
||||
placeholder: '请选择角色',
|
||||
el: { multiple: true }
|
||||
}
|
||||
},{
|
||||
span: 12,
|
||||
name: 'head',
|
||||
component: 'upload-image',
|
||||
label: '头像',
|
||||
rules: [{ required: true, message: '请选择头像', trigger: 'change' }]
|
||||
}]
|
||||
}]
|
||||
}
|
||||
})
|
||||
function getFormData(){
|
||||
console.log(magicForm.value.getFormData())
|
||||
}
|
||||
</script>
|
@ -51,25 +51,25 @@ const listOptions = reactive({
|
||||
cols: [
|
||||
{
|
||||
field: 'username',
|
||||
title: '登录名称',
|
||||
label: '登录名称',
|
||||
sortable: 'custom'
|
||||
}, {
|
||||
field: 'name',
|
||||
title: '姓名/昵称',
|
||||
label: '姓名/昵称',
|
||||
sortable: 'custom'
|
||||
}, {
|
||||
field: 'officeName',
|
||||
title: '所属机构'
|
||||
label: '所属机构'
|
||||
}, {
|
||||
field: 'roles',
|
||||
title: '角色'
|
||||
label: '角色'
|
||||
}, {
|
||||
field: 'phone',
|
||||
title: '手机号',
|
||||
label: '手机号',
|
||||
sortable: 'custom'
|
||||
}, {
|
||||
field: 'isLogin',
|
||||
title: '禁止登录',
|
||||
label: '禁止登录',
|
||||
type: 'switch',
|
||||
width: 100,
|
||||
change: (row) => {
|
||||
@ -80,17 +80,17 @@ const listOptions = reactive({
|
||||
}
|
||||
}, {
|
||||
field: 'createDate',
|
||||
title: '创建时间',
|
||||
label: '创建时间',
|
||||
width: 180
|
||||
}, {
|
||||
title: '操作',
|
||||
label: '操作',
|
||||
type: 'btns',
|
||||
width: 140,
|
||||
fixed: 'right',
|
||||
btns: [
|
||||
{
|
||||
permission: 'user:save',
|
||||
title: '修改',
|
||||
label: '修改',
|
||||
type: 'text',
|
||||
icon: 'ElEdit',
|
||||
click: (row) => {
|
||||
@ -101,7 +101,7 @@ const listOptions = reactive({
|
||||
}
|
||||
}, {
|
||||
permission: 'user:delete',
|
||||
title: '删除',
|
||||
label: '删除',
|
||||
type: 'text',
|
||||
icon: 'ElDelete',
|
||||
click: (row) => {
|
||||
@ -136,6 +136,12 @@ const formOptions = reactive({
|
||||
rows: [{
|
||||
gutter: 24,
|
||||
cols: [{
|
||||
span: 12,
|
||||
name: 'head',
|
||||
component: 'upload-image',
|
||||
label: '头像',
|
||||
rules: [{ required: true, message: '请选择头像', trigger: 'change' }]
|
||||
},{
|
||||
span: 12,
|
||||
name: 'username',
|
||||
label: '登录名称',
|
||||
@ -170,7 +176,6 @@ const formOptions = reactive({
|
||||
span: 12,
|
||||
name: 'officeId',
|
||||
label: '组织机构',
|
||||
defaultValue: null,
|
||||
rules: [{ required: true, message: '请选择组织机构', trigger: 'change' }],
|
||||
props: {
|
||||
url: 'user/offices'
|
||||
@ -180,7 +185,6 @@ const formOptions = reactive({
|
||||
span: 12,
|
||||
name: 'roles',
|
||||
label: '选择角色',
|
||||
defaultValue: null,
|
||||
rules: [{ required: true, message: '请选择角色', trigger: 'change' }],
|
||||
props: {
|
||||
url: 'role/all',
|
||||
|
@ -12,6 +12,11 @@
|
||||
<mb-icon icon="password"/>
|
||||
<input class="magic-input" ref="password" v-model="loginForm.password" type="password" placeholder="密码" name="password" tabindex="2" auto-complete="on" @keyup.enter.native="handleLogin" />
|
||||
</div>
|
||||
<div class="magic-login-row">
|
||||
<mb-icon icon="verification-code"/>
|
||||
<input class="magic-input code" ref="code" v-model="loginForm.code" placeholder="验证码" name="code" tabindex="3" @keyup.enter.native="handleLogin" />
|
||||
<img class="code-img" :src="codeImg" @click="refreshCode">
|
||||
</div>
|
||||
<div class="magic-login-row">
|
||||
<button :loading="loading" class="magic-button" type="primary" size="medium" @click.native.prevent="handleLogin">登录</button>
|
||||
</div>
|
||||
@ -24,12 +29,20 @@
|
||||
import { reactive, ref, getCurrentInstance } from 'vue'
|
||||
import { login } from '@/scripts/auth'
|
||||
const { proxy } = getCurrentInstance()
|
||||
|
||||
const codeImg = ref(import.meta.env.VITE_APP_BASE_API + '/security/verification/code')
|
||||
const loginForm = reactive({
|
||||
username: '',
|
||||
password: ''
|
||||
password: '',
|
||||
code: ''
|
||||
})
|
||||
const loading = ref(false)
|
||||
function refreshCode(){
|
||||
proxy.$get('/security/verification/code').then(res => {
|
||||
codeImg.value = 'data:image/png;base64,' + res.data.img
|
||||
loginForm.uuid = res.data.uuid
|
||||
})
|
||||
}
|
||||
refreshCode()
|
||||
function handleLogin() {
|
||||
if(!loginForm.username){
|
||||
proxy.$message({
|
||||
@ -47,12 +60,21 @@
|
||||
showClose: true
|
||||
})
|
||||
return
|
||||
}else if(!loginForm.code){
|
||||
proxy.$message({
|
||||
message: '请输入验证码',
|
||||
type: 'error',
|
||||
duration: 2000,
|
||||
showClose: true
|
||||
})
|
||||
return
|
||||
}else{
|
||||
loading.value = true
|
||||
login(loginForm).then((res) => {
|
||||
proxy.$router.push({ path: '/home' })
|
||||
loading.value = false
|
||||
}).catch(() => {
|
||||
refreshCode()
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
@ -63,6 +85,15 @@
|
||||
*{
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.code{
|
||||
width: 283px;
|
||||
float: left;
|
||||
margin-right: 15px;
|
||||
}
|
||||
.code-img{
|
||||
border: 1px solid #D9D9D9;
|
||||
border-radius: 4px;
|
||||
}
|
||||
input{
|
||||
width: 100%;
|
||||
}
|
||||
|
@ -28,12 +28,26 @@
|
||||
},
|
||||
{
|
||||
field: 'type',
|
||||
label: '登录状态'
|
||||
label: '登录状态',
|
||||
templet: (row) => {
|
||||
return row.type == '成功' ? '<b style="color: #409EFF">成功</b>' : '<b style="color: red">失败</b>'
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'ip',
|
||||
label: 'ip'
|
||||
},
|
||||
{
|
||||
field: 'browser',
|
||||
label: '浏览器'
|
||||
},
|
||||
{
|
||||
field: 'os',
|
||||
label: '系统',
|
||||
props: {
|
||||
"show-overflow-tooltip": true
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'createDate',
|
||||
label: '操作时间'
|
||||
|
113
magic-boot-ui/src/views/system/monitor/online-user.vue
Normal file
113
magic-boot-ui/src/views/system/monitor/online-user.vue
Normal file
@ -0,0 +1,113 @@
|
||||
<template>
|
||||
<mb-list ref="magicList" v-bind="listOptions" />
|
||||
<!-- <mb-dialog ref="magicDialog" title="提示" width="600px" @confirm-click="disable">-->
|
||||
<!-- <template #content>-->
|
||||
<!-- <el-row :gutter="24">-->
|
||||
<!-- <el-col :span="24">-->
|
||||
<!-- 确定要踢“{{currRow.username}}”下线并临时封禁吗?-->
|
||||
<!-- </el-col>-->
|
||||
<!-- <el-col :span="24">-->
|
||||
<!-- <mb-radio v-model="time" :options="options"></mb-radio>-->
|
||||
<!-- </el-col>-->
|
||||
<!-- </el-row>-->
|
||||
<!-- </template>-->
|
||||
<!-- </mb-dialog>-->
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {reactive, ref, getCurrentInstance} from "vue";
|
||||
const { proxy } = getCurrentInstance()
|
||||
const magicList = ref()
|
||||
// const magicDialog = ref()
|
||||
// const currRow = ref()
|
||||
// const time = ref(0)
|
||||
// const options = reactive([{
|
||||
// label: '不封禁',
|
||||
// value: 0
|
||||
// },{
|
||||
// label: '1分钟',
|
||||
// value: 60
|
||||
// },{
|
||||
// label: '10分钟',
|
||||
// value: 10 * 60
|
||||
// },{
|
||||
// label: '1小时',
|
||||
// value: 1 * 60 *60
|
||||
// },{
|
||||
// label: '5小时',
|
||||
// value: 5 * 60 *60
|
||||
// },{
|
||||
// label: '永久',
|
||||
// value: -1
|
||||
// }])
|
||||
const listOptions = reactive({
|
||||
table: {
|
||||
url: 'online/list',
|
||||
where: {
|
||||
username: {
|
||||
label: '登录名称',
|
||||
},
|
||||
ip: {
|
||||
label: 'IP',
|
||||
}
|
||||
},
|
||||
cols: [
|
||||
{
|
||||
field: 'username',
|
||||
label: '登录名称'
|
||||
}, {
|
||||
field: 'officeName',
|
||||
label: '所属机构'
|
||||
}, {
|
||||
field: 'ip',
|
||||
label: 'IP'
|
||||
}, {
|
||||
field: 'browser',
|
||||
label: '浏览器'
|
||||
}, {
|
||||
field: 'os',
|
||||
label: '操作系统',
|
||||
props: {
|
||||
"show-overflow-tooltip": true
|
||||
}
|
||||
}, {
|
||||
field: 'createDate',
|
||||
label: '登录时间'
|
||||
}, {
|
||||
label: '操作',
|
||||
type: 'btns',
|
||||
width: 140,
|
||||
fixed: 'right',
|
||||
btns: [
|
||||
{
|
||||
permission: 'online:logout',
|
||||
label: '踢人',
|
||||
type: 'text',
|
||||
icon: 'ElBicycle',
|
||||
click: (row) => {
|
||||
// currRow.value = row
|
||||
// magicDialog.value.show()
|
||||
proxy.$alert(`确定要踢“${row.username}”下线吗?`, '提示', {
|
||||
confirmButtonText: '确定',
|
||||
callback: (action) => {
|
||||
if (action === 'confirm') {
|
||||
proxy.$get('online/logout',{ token: row.token }).then(() => {
|
||||
magicList.value.reload()
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
// function disable(){
|
||||
// proxy.$get('online/logout',{ id: currRow.value.id, time: time.value }).then(() => {
|
||||
// magicDialog.value.hide()
|
||||
// magicList.value.reload()
|
||||
// })
|
||||
// }
|
||||
</script>
|
@ -40,7 +40,10 @@
|
||||
},
|
||||
{
|
||||
field: 'userAgent',
|
||||
label: '用户代理'
|
||||
label: '用户代理',
|
||||
props: {
|
||||
"show-overflow-tooltip": true
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'username',
|
||||
|
@ -196,7 +196,10 @@ const tableOptions = reactive({
|
||||
type: 'text',
|
||||
icon: 'ElUserFilled',
|
||||
click: (row) => {
|
||||
proxy.$router.push({ path: `/system/user/user-list?officeId=${row.id}` })
|
||||
proxy.$router.push({
|
||||
path: '/system/user/user-list',
|
||||
query: { officeId: row.id }
|
||||
})
|
||||
}
|
||||
}
|
||||
]
|
||||
|
@ -13,11 +13,21 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<div class="left">
|
||||
<mb-tree url="office/tree" :el="{ 'expand-on-click-node': false,'show-checkbox': true }" :checked-ids="[tableOptions.where.officeId]" :expand="false" :search="true" search-width="100%" :checked="false" @check-change="checkChange" />
|
||||
<mb-tree
|
||||
url="office/tree"
|
||||
:el="{ 'expand-on-click-node': false,'show-checkbox': true }"
|
||||
:checked-ids="[tableOptions.where.officeId]"
|
||||
:expand="false"
|
||||
:search="true"
|
||||
search-width="100%"
|
||||
:checked="false"
|
||||
@check-change="checkChange"
|
||||
@mounted="treeMounted"
|
||||
/>
|
||||
</div>
|
||||
<div class="right">
|
||||
|
||||
<mb-search :where="tableOptions.where" @search="reloadTable">
|
||||
<mb-search :where="tableOptions.where" @search="reloadTable" @mounted="searchMounted">
|
||||
<template #btns>
|
||||
<el-button :loading="downloadLoading" class="filter-item" icon="ElDownload" @click="handleDownload">
|
||||
导出
|
||||
@ -71,13 +81,13 @@ const tableOptions = reactive({
|
||||
roleId: {
|
||||
type: 'select',
|
||||
label: '角色',
|
||||
value: proxy.$route.query.roleId,
|
||||
value: '',
|
||||
properties: {
|
||||
url: 'role/all',
|
||||
el: { multiple: true }
|
||||
}
|
||||
},
|
||||
officeId: proxy.$route.query.officeId
|
||||
officeId: ''
|
||||
},
|
||||
cols: [
|
||||
{
|
||||
@ -159,20 +169,17 @@ const userFormDialog = ref()
|
||||
const table = ref()
|
||||
const userForm = ref()
|
||||
|
||||
// onMounted(() => {
|
||||
// setTimeout(function(){
|
||||
// nextTick(() => {
|
||||
// console.log(proxy.$route.query.roleId)
|
||||
// if(proxy.$route.query.roleId){
|
||||
// tableOptions.where.roleId.value = proxy.$route.query.roleId
|
||||
// }
|
||||
// if(proxy.$route.query.officeId){
|
||||
// tableOptions.where.officeId = proxy.$route.query.officeId
|
||||
// }
|
||||
// })
|
||||
function searchMounted(){
|
||||
if(proxy.$route.query.roleId){
|
||||
tableOptions.where.roleId.value = proxy.$route.query.roleId
|
||||
}
|
||||
}
|
||||
|
||||
// },1000)
|
||||
// })
|
||||
function treeMounted(){
|
||||
if(proxy.$route.query.officeId){
|
||||
tableOptions.where.officeId = proxy.$route.query.officeId
|
||||
}
|
||||
}
|
||||
|
||||
function checkChange(values) {
|
||||
tableOptions.where.officeId = values
|
||||
|
@ -14,7 +14,7 @@
|
||||
<description>magic-boot</description>
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<magic-api.version>2.0.0-beta.3</magic-api.version>
|
||||
<magic-api.version>2.0.0</magic-api.version>
|
||||
<druid.version>1.1.10</druid.version>
|
||||
<hutool-all.version>5.7.13</hutool-all.version>
|
||||
<sa-token.version>1.26.0</sa-token.version>
|
||||
|
@ -1,4 +1,4 @@
|
||||
package org.ssssssss.magicboot.config;
|
||||
package org.ssssssss.magicboot.configuration;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpHeaders;
|
@ -0,0 +1,22 @@
|
||||
package org.ssssssss.magicboot.model;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class CodeCacheMap {
|
||||
|
||||
private static Map<String, String> map = new HashMap<String, String>();
|
||||
|
||||
public static void put(String key, String value){
|
||||
map.put(key, value);
|
||||
}
|
||||
|
||||
public static void remove(String key){
|
||||
map.remove(key);
|
||||
}
|
||||
|
||||
public static String get(String key){
|
||||
return map.get(key);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package org.ssssssss.magicboot.provider;
|
||||
|
||||
import cn.dev33.satoken.exception.DisableLoginException;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.ssssssss.magicapi.core.context.RequestEntity;
|
||||
import org.ssssssss.magicapi.core.interceptor.ResultProvider;
|
||||
import org.ssssssss.magicapi.core.model.JsonBean;
|
||||
|
||||
@Component
|
||||
public class ExceptionResultProvider implements ResultProvider {
|
||||
|
||||
@Override
|
||||
public Object buildResult(RequestEntity requestEntity, int code, String message, Object data) {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
return new JsonBean<>(code, message, data, (int) (timestamp - requestEntity.getRequestTime()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object buildException(RequestEntity requestEntity, Throwable throwable) {
|
||||
if(throwable.getCause() instanceof DisableLoginException){
|
||||
return buildResult(requestEntity, 500, "此账号已被临时封禁,请联系管理员");
|
||||
}
|
||||
return buildResult(requestEntity, 500, "系统内部出现错误");
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user