1
0
forked from ruoyi/RuoYi-Vue3

19 Commits

Author SHA1 Message Date
RuoYi
a5f02187f9 若依 3.8.4 2022-09-26 08:07:15 +08:00
RuoYi
7148a8e428 优化日志操作中重置按钮时重复查询的问题 2022-09-19 14:47:49 +08:00
RuoYi
aad94b236e 通用下载方法新增config配置选项 2022-09-19 13:22:52 +08:00
RuoYi
9290e3c318 添加新群号:160110482 2022-08-28 20:11:00 +08:00
RuoYi
fabc195e90 修复多文件上传报错出现的异常问题 2022-08-24 13:40:52 +08:00
RuoYi
439b134c9b 优化页面内嵌iframe切换tab不刷新数据 2022-08-23 20:47:41 +08:00
RuoYi
2952cb2c4f 优化多角色数据权限匹配规则 2022-08-21 22:54:25 +08:00
RuoYi
1d2f37bfe1 修复图片预览组件src属性为null值控制台报错问题(I5KBAS) 2022-08-15 12:07:45 +08:00
RuoYi
0c66bc0624 个人中心修改密码去除多余的user传递 2022-08-12 12:18:15 +08:00
RuoYi
6b236930fd 登录日志新增解锁账户功能 2022-08-08 09:24:33 +08:00
RuoYi
f8e6431684 支持全局主题颜色设置 2022-07-31 17:51:45 +08:00
RuoYi
de7e21457d 升级vue到最新版本3.2.37 2022-07-31 17:51:04 +08:00
RuoYi
d6129f8471 升级vite到最新版本2.9.14 2022-07-31 17:50:44 +08:00
RuoYi
3d29929b57 优化表格上右侧工具条(搜索按钮显隐&右侧样式凸出) 2022-07-29 20:38:55 +08:00
RuoYi
8b49d6572a 修复菜单图标文本框搜索消失问题(I5IHZV) 2022-07-27 14:56:51 +08:00
RuoYi
56680d26e1 添加新群号:104748341 2022-07-25 17:47:08 +08:00
RuoYi
cafff38a6e 优化字典数据使用store存取 2022-07-20 19:38:44 +08:00
RuoYi
6c19bfbd90 修改验证码开关变量名 2022-07-14 19:49:20 +08:00
RuoYi
331dbc44b2 修改验证码开关变量名 2022-07-12 18:13:54 +08:00
35 changed files with 454 additions and 144 deletions

View File

@@ -1,11 +1,11 @@
<p align="center">
<img alt="logo" src="https://oscimg.oschina.net/oscnet/up-d3d0a9303e11d522a06cd263f3079027715.png">
</p>
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">RuoYi v3.8.3</h1>
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">RuoYi v3.8.4</h1>
<h4 align="center">基于SpringBoot+Vue3前后端分离的Java快速开发框架</h4>
<p align="center">
<a href="https://gitee.com/y_project/RuoYi-Vue/stargazers"><img src="https://gitee.com/y_project/RuoYi-Vue/badge/star.svg?theme=dark"></a>
<a href="https://gitee.com/y_project/RuoYi-Vue"><img src="https://img.shields.io/badge/RuoYi-v3.8.3-brightgreen.svg"></a>
<a href="https://gitee.com/y_project/RuoYi-Vue"><img src="https://img.shields.io/badge/RuoYi-v3.8.4-brightgreen.svg"></a>
<a href="https://gitee.com/y_project/RuoYi-Vue/blob/master/LICENSE"><img src="https://img.shields.io/github/license/mashape/apistatus.svg"></a>
</p>
@@ -106,4 +106,4 @@ yarn dev
## 若依前后端分离交流群
QQ群 [![加入QQ群](https://img.shields.io/badge/已满-937441-blue.svg)](https://jq.qq.com/?_wv=1027&k=5bVB1og) [![加入QQ群](https://img.shields.io/badge/已满-887144332-blue.svg)](https://jq.qq.com/?_wv=1027&k=5eiA4DH) [![加入QQ群](https://img.shields.io/badge/已满-180251782-blue.svg)](https://jq.qq.com/?_wv=1027&k=5AxMKlC) [![加入QQ群](https://img.shields.io/badge/已满-104180207-blue.svg)](https://jq.qq.com/?_wv=1027&k=51G72yr) [![加入QQ群](https://img.shields.io/badge/已满-186866453-blue.svg)](https://jq.qq.com/?_wv=1027&k=VvjN2nvu) [![加入QQ群](https://img.shields.io/badge/已满-201396349-blue.svg)](https://jq.qq.com/?_wv=1027&k=5vYAqA05) [![加入QQ群](https://img.shields.io/badge/已满-101456076-blue.svg)](https://jq.qq.com/?_wv=1027&k=kOIINEb5) [![加入QQ群](https://img.shields.io/badge/已满-101539465-blue.svg)](https://jq.qq.com/?_wv=1027&k=UKtX5jhs) [![加入QQ群](https://img.shields.io/badge/已满-264312783-blue.svg)](https://jq.qq.com/?_wv=1027&k=EI9an8lJ) [![加入QQ群](https://img.shields.io/badge/已满-167385320-blue.svg)](https://jq.qq.com/?_wv=1027&k=SWCtLnMz) [![加入QQ群](https://img.shields.io/badge/139821253-blue.svg)](https://jq.qq.com/?_wv=1027&k=njiWDmkj) 点击按钮入群。
QQ群 [![加入QQ群](https://img.shields.io/badge/已满-937441-blue.svg)](https://jq.qq.com/?_wv=1027&k=5bVB1og) [![加入QQ群](https://img.shields.io/badge/已满-887144332-blue.svg)](https://jq.qq.com/?_wv=1027&k=5eiA4DH) [![加入QQ群](https://img.shields.io/badge/已满-180251782-blue.svg)](https://jq.qq.com/?_wv=1027&k=5AxMKlC) [![加入QQ群](https://img.shields.io/badge/已满-104180207-blue.svg)](https://jq.qq.com/?_wv=1027&k=51G72yr) [![加入QQ群](https://img.shields.io/badge/已满-186866453-blue.svg)](https://jq.qq.com/?_wv=1027&k=VvjN2nvu) [![加入QQ群](https://img.shields.io/badge/已满-201396349-blue.svg)](https://jq.qq.com/?_wv=1027&k=5vYAqA05) [![加入QQ群](https://img.shields.io/badge/已满-101456076-blue.svg)](https://jq.qq.com/?_wv=1027&k=kOIINEb5) [![加入QQ群](https://img.shields.io/badge/已满-101539465-blue.svg)](https://jq.qq.com/?_wv=1027&k=UKtX5jhs) [![加入QQ群](https://img.shields.io/badge/已满-264312783-blue.svg)](https://jq.qq.com/?_wv=1027&k=EI9an8lJ) [![加入QQ群](https://img.shields.io/badge/已满-167385320-blue.svg)](https://jq.qq.com/?_wv=1027&k=SWCtLnMz) [![加入QQ群](https://img.shields.io/badge/已满-104748341-blue.svg)](https://jq.qq.com/?_wv=1027&k=96Dkdq0k) [![加入QQ群](https://img.shields.io/badge/160110482-blue.svg)](https://jq.qq.com/?_wv=1027&k=0fsNiYZt) 点击按钮入群。

View File

@@ -1,6 +1,6 @@
{
"name": "ruoyi",
"version": "3.8.3",
"version": "3.8.4",
"description": "若依管理系统",
"author": "若依",
"license": "MIT",
@@ -26,7 +26,7 @@
"jsencrypt": "3.2.1",
"nprogress": "0.2.0",
"pinia": "2.0.14",
"vue": "3.2.31",
"vue": "3.2.37",
"vue-cropper": "1.0.3",
"vue-router": "4.0.14"
},
@@ -35,7 +35,7 @@
"@vue/compiler-sfc": "3.2.36",
"sass": "1.52.1",
"unplugin-auto-import": "0.8.5",
"vite": "2.9.9",
"vite": "2.9.14",
"vite-plugin-compression": "0.5.1",
"vite-plugin-svg-icons": "2.0.1",
"vite-plugin-vue-setup-extend": "0.4.0"

View File

@@ -1,3 +1,15 @@
<template>
<router-view />
</template>
<script setup>
import useSettingsStore from '@/store/modules/settings'
import { handleThemeStyle } from '@/utils/theme'
onMounted(() => {
nextTick(() => {
// 初始化主题样式
handleThemeStyle(useSettingsStore().theme)
})
})
</script>

View File

@@ -17,6 +17,14 @@ export function delLogininfor(infoId) {
})
}
// 解锁用户登录状态
export function unlockLogininfor(userName) {
return request({
url: '/monitor/logininfor/unlock/' + userName,
method: 'get'
})
}
// 清空登录日志
export function cleanLogininfor() {
return request({

View File

@@ -25,22 +25,6 @@ export function getDept(deptId) {
})
}
// 查询部门下拉树结构
export function treeselect() {
return request({
url: '/system/dept/treeselect',
method: 'get'
})
}
// 根据角色ID查询部门树结构
export function roleDeptTreeselect(roleId) {
return request({
url: '/system/dept/roleDeptTreeselect/' + roleId,
method: 'get'
})
}
// 新增部门
export function addDept(data) {
return request({

View File

@@ -109,3 +109,11 @@ export function authUserSelectAll(data) {
params: data
})
}
// 根据角色ID查询部门树结构
export function deptTreeSelect(roleId) {
return request({
url: '/system/role/deptTree/' + roleId,
method: 'get'
})
}

View File

@@ -125,3 +125,11 @@ export function updateAuthRole(data) {
params: data
})
}
// 查询部门下拉树结构
export function deptTreeSelect() {
return request({
url: '/system/user/deptTree',
method: 'get'
})
}

View File

@@ -12,11 +12,16 @@
}
/* fade-transform */
.fade-transform--move,
.fade-transform-leave-active,
.fade-transform-enter-active {
transition: all .5s;
}
.fade-transform-leave-active {
position: absolute;
}
.fade-transform-enter {
opacity: 0;
transform: translateX(-30px);

View File

@@ -12,7 +12,7 @@
:show-file-list="false"
:headers="headers"
class="upload-file-uploader"
ref="upload"
ref="fileUpload"
>
<!-- 上传按钮 -->
<el-button type="primary">选取文件</el-button>
@@ -70,7 +70,7 @@ const emit = defineEmits();
const number = ref(0);
const uploadList = ref([]);
const baseUrl = import.meta.env.VITE_APP_BASE_API;
const uploadFileUrl = ref(import.meta.env.VITE_APP_BASE_API + "/common/upload"); // 上传的图片服务器地址
const uploadFileUrl = ref(import.meta.env.VITE_APP_BASE_API + "/common/upload"); // 上传文件服务器地址
const headers = ref({ Authorization: "Bearer " + getToken() });
const fileList = ref([]);
const showTip = computed(
@@ -139,13 +139,15 @@ function handleUploadError(err) {
// 上传成功回调
function handleUploadSuccess(res, file) {
uploadList.value.push({ name: res.fileName, url: res.fileName });
if (uploadList.value.length === number.value) {
fileList.value = fileList.value.filter(f => f.url !== undefined).concat(uploadList.value);
uploadList.value = [];
number.value = 0;
emit("update:modelValue", listToString(fileList.value));
if (res.code === 200) {
uploadList.value.push({ name: res.fileName, url: res.fileName });
uploadedSuccessfully();
} else {
number.value--;
proxy.$modal.closeLoading();
proxy.$modal.msgError(res.msg);
proxy.$refs.fileUpload.handleRemove(file);
uploadedSuccessfully();
}
}
@@ -155,6 +157,17 @@ function handleDelete(index) {
emit("update:modelValue", listToString(fileList.value));
}
// 上传结束处理
function uploadedSuccessfully() {
if (number.value > 0 && uploadList.value.length === number.value) {
fileList.value = fileList.value.filter(f => f.url !== undefined).concat(uploadList.value);
uploadList.value = [];
number.value = 0;
emit("update:modelValue", listToString(fileList.value));
proxy.$modal.closeLoading();
}
}
// 获取文件名称
function getFileName(name) {
if (name.lastIndexOf("/") > -1) {
@@ -169,7 +182,7 @@ function listToString(list, separator) {
let strs = "";
separator = separator || ",";
for (let i in list) {
if(undefined !== list[i].url) {
if (list[i].url) {
strs += list[i].url + separator;
}
}

View File

@@ -20,7 +20,7 @@ import { isExternal } from "@/utils/validate";
const props = defineProps({
src: {
type: String,
required: true
default: ""
},
width: {
type: [Number, String],
@@ -33,6 +33,9 @@ const props = defineProps({
});
const realSrc = computed(() => {
if (!props.src) {
return;
}
let real_src = props.src.split(",")[0];
if (isExternal(real_src)) {
return real_src;
@@ -41,6 +44,9 @@ const realSrc = computed(() => {
});
const realSrcList = computed(() => {
if (!props.src) {
return;
}
let real_src_list = props.src.split(",");
let srcList = [];
real_src_list.forEach(item => {

View File

@@ -9,8 +9,8 @@
:limit="limit"
:on-error="handleUploadError"
:on-exceed="handleExceed"
name="file"
:on-remove="handleRemove"
ref="imageUpload"
:before-remove="handleDelete"
:show-file-list="true"
:headers="headers"
:file-list="fileList"
@@ -107,23 +107,6 @@ watch(() => props.modelValue, val => {
}
},{ deep: true, immediate: true });
// 删除图片
function handleRemove(file, files) {
emit("update:modelValue", listToString(fileList.value));
}
// 上传成功回调
function handleUploadSuccess(res) {
uploadList.value.push({ name: res.fileName, url: res.fileName });
if (uploadList.value.length === number.value) {
fileList.value = fileList.value.filter(f => f.url !== undefined).concat(uploadList.value);
uploadList.value = [];
number.value = 0;
emit("update:modelValue", listToString(fileList.value));
proxy.$modal.closeLoading();
}
}
// 上传前loading加载
function handleBeforeUpload(file) {
let isImg = false;
@@ -162,6 +145,41 @@ function handleExceed() {
proxy.$modal.msgError(`上传文件数量不能超过 ${props.limit} 个!`);
}
// 上传成功回调
function handleUploadSuccess(res, file) {
if (res.code === 200) {
uploadList.value.push({ name: res.fileName, url: res.fileName });
uploadedSuccessfully();
} else {
number.value--;
proxy.$modal.closeLoading();
proxy.$modal.msgError(res.msg);
proxy.$refs.imageUpload.handleRemove(file);
uploadedSuccessfully();
}
}
// 删除图片
function handleDelete(file) {
const findex = fileList.value.map(f => f.name).indexOf(file.name);
if (findex > -1 && uploadList.value.length === number.value) {
fileList.value.splice(findex, 1);
emit("update:modelValue", listToString(fileList.value));
return false;
}
}
// 上传结束处理
function uploadedSuccessfully() {
if (number.value > 0 && uploadList.value.length === number.value) {
fileList.value = fileList.value.filter(f => f.url !== undefined).concat(uploadList.value);
uploadList.value = [];
number.value = 0;
emit("update:modelValue", listToString(fileList.value));
proxy.$modal.closeLoading();
}
}
// 上传失败
function handleUploadError() {
proxy.$modal.msgError("上传图片失败");

View File

@@ -1,7 +1,7 @@
<template>
<div class="top-right-btn">
<div class="top-right-btn" :style="style">
<el-row>
<el-tooltip class="item" effect="dark" :content="showSearch ? '隐藏搜索' : '显示搜索'" placement="top">
<el-tooltip class="item" effect="dark" :content="showSearch ? '隐藏搜索' : '显示搜索'" placement="top" v-if="search">
<el-button circle icon="Search" @click="toggleSearch()" />
</el-tooltip>
<el-tooltip class="item" effect="dark" content="刷新" placement="top">
@@ -31,6 +31,14 @@ const props = defineProps({
columns: {
type: Array,
},
search: {
type: Boolean,
default: true,
},
gutter: {
type: Number,
default: 10,
},
})
const emits = defineEmits(['update:showSearch', 'queryTable']);
@@ -42,6 +50,14 @@ const title = ref("显示/隐藏");
// 是否显示弹出层
const open = ref(false);
const style = computed(() => {
const ret = {};
if (props.gutter) {
ret.marginRight = `${props.gutter / 2}px`;
}
return ret;
});
// 搜索
function toggleSearch() {
emits("update:showSearch", !props.showSearch);

View File

@@ -2,23 +2,20 @@
<section class="app-main">
<router-view v-slot="{ Component, route }">
<transition name="fade-transform" mode="out-in">
<keep-alive :include="cachedViews">
<component :is="Component" :key="route.path"/>
<keep-alive :include="tagsViewStore.cachedViews">
<component v-if="!route.meta.link" :is="Component" :key="route.path"/>
</keep-alive>
</transition>
</router-view>
<iframe-toggle />
</section>
</template>
<script setup>
import iframeToggle from "./IframeToggle/index"
import useTagsViewStore from '@/store/modules/tagsView'
const tagsViewStore = useTagsViewStore()
const route = useRoute()
tagsViewStore.addCachedView(route)
const cachedViews = computed(() => {
return tagsViewStore.cachedViews
})
</script>
<style lang="scss" scoped>

View File

@@ -0,0 +1,19 @@
<template>
<transition-group name="fade-transform" mode="out-in">
<inner-link
v-for="(item, index) in tagsViewStore.iframeViews"
:key="item.path"
:iframeId="'iframe' + index"
v-show="route.path === item.path"
:src="item.meta.link"
></inner-link>
</transition-group>
</template>
<script setup>
import InnerLink from "../InnerLink/index"
import useTagsViewStore from '@/store/modules/tagsView'
const route = useRoute();
const tagsViewStore = useTagsViewStore()
</script>

View File

@@ -1,30 +1,43 @@
<script>
export default {
setup() {
const route = useRoute();
const link = route.meta.link;
if (link === "") {
return "404";
}
let url = link;
const height = document.documentElement.clientHeight - 94.5 + "px";
const style = { height: height };
<template>
<div :style="'height:' + height" v-loading="loading" element-loading-text="正在加载页面,请稍候!">
<iframe
:id="iframeId"
style="width: 100%; height: 100%"
:src="src"
frameborder="no"
></iframe>
</div>
</template>
// 返回渲染函数
return () =>
h(
"div",
{
style: style,
},
h("iframe", {
src: url,
frameborder: "no",
width: "100%",
height: "100%",
scrolling: "auto",
})
);
<script setup>
const props = defineProps({
src: {
type: String,
default: "/"
},
};
iframeId: {
type: String
}
});
const height = ref(document.documentElement.clientHeight - 94.5 + "px");
const loading = ref(false);
onMounted(() => {
const { proxy } = getCurrentInstance()
const iframeId = ("#" + props.iframeId).replace(/\//g, "\\/");
const iframe = document.querySelector(iframeId);
// iframe页面loading控制
if (iframe.attachEvent) {
loading.value = true;
iframe.attachEvent("onload", function () {
proxy.loading = false;
});
} else {
loading.value = true;
iframe.onload = function () {
proxy.loading = false;
};
}
})
</script>

View File

@@ -87,6 +87,7 @@ import { useDynamicTitle } from '@/utils/dynamicTitle'
import useAppStore from '@/store/modules/app'
import useSettingsStore from '@/store/modules/settings'
import usePermissionStore from '@/store/modules/permission'
import { handleThemeStyle } from '@/utils/theme'
const { proxy } = getCurrentInstance();
const appStore = useAppStore()
@@ -143,6 +144,7 @@ const dynamicTitle = computed({
function themeChange(val) {
settingsStore.changeSetting({ key: 'theme', value: val })
theme.value = val;
handleThemeStyle(val);
}
function handleTheme(val) {
settingsStore.changeSetting({ key: 'sideTheme', value: val })

View File

@@ -141,6 +141,9 @@ function addTags() {
const { name } = route
if (name) {
useTagsViewStore().addView(route)
if (route.meta.link) {
useTagsViewStore().addIframeView(route);
}
}
return false
}
@@ -159,6 +162,9 @@ function moveToCurrentTag() {
}
function refreshSelectedTag(view) {
proxy.$tab.refreshPage(view);
if (route.meta.link) {
useTagsViewStore().delIframeView(route);
}
}
function closeSelectedTag(view) {
proxy.$tab.closePage(view).then(({ visitedViews }) => {

View File

@@ -1,6 +1,5 @@
import Cookies from 'js-cookie'
const useAppStore = defineStore(
'app',
{

57
src/store/modules/dict.js Normal file
View File

@@ -0,0 +1,57 @@
const useDictStore = defineStore(
'dict',
{
state: () => ({
dict: new Array()
}),
actions: {
// 获取字典
getDict(_key) {
if (_key == null && _key == "") {
return null;
}
try {
for (let i = 0; i < this.dict.length; i++) {
if (this.dict[i].key == _key) {
return this.dict[i].value;
}
}
} catch (e) {
return null;
}
},
// 设置字典
setDict(_key, value) {
if (_key !== null && _key !== "") {
this.dict.push({
key: _key,
value: value
});
}
},
// 删除字典
removeDict(_key) {
var bln = false;
try {
for (let i = 0; i < this.dict.length; i++) {
if (this.dict[i].key == _key) {
this.dict.splice(i, 1);
return true;
}
}
} catch (e) {
bln = false;
}
return bln;
},
// 清空字典
cleanDict() {
this.dict = new Array();
},
// 初始字典
initDict() {
}
}
})
export default useDictStore

View File

@@ -3,13 +3,22 @@ const useTagsViewStore = defineStore(
{
state: () => ({
visitedViews: [],
cachedViews: []
cachedViews: [],
iframeViews: []
}),
actions: {
addView(view) {
this.addVisitedView(view)
this.addCachedView(view)
},
addIframeView(view) {
if (this.iframeViews.some(v => v.path === view.path)) return
this.iframeViews.push(
Object.assign({}, view, {
title: view.meta.title || 'no-name'
})
)
},
addVisitedView(view) {
if (this.visitedViews.some(v => v.path === view.path)) return
this.visitedViews.push(
@@ -42,9 +51,16 @@ const useTagsViewStore = defineStore(
break
}
}
this.iframeViews = this.iframeViews.filter(item => item.path !== view.path)
resolve([...this.visitedViews])
})
},
delIframeView(view) {
return new Promise(resolve => {
this.iframeViews = this.iframeViews.filter(item => item.path !== view.path)
resolve([...this.iframeViews])
})
},
delCachedView(view) {
return new Promise(resolve => {
const index = this.cachedViews.indexOf(view.name)
@@ -67,6 +83,7 @@ const useTagsViewStore = defineStore(
this.visitedViews = this.visitedViews.filter(v => {
return v.meta.affix || v.path === view.path
})
this.iframeViews = this.iframeViews.filter(item => item.path === view.path)
resolve([...this.visitedViews])
})
},
@@ -95,6 +112,7 @@ const useTagsViewStore = defineStore(
return new Promise(resolve => {
const affixTags = this.visitedViews.filter(tag => tag.meta.affix)
this.visitedViews = affixTags
this.iframeViews = []
resolve([...this.visitedViews])
})
},
@@ -126,6 +144,10 @@ const useTagsViewStore = defineStore(
if (i > -1) {
this.cachedViews.splice(i, 1)
}
if(item.meta.link) {
const fi = this.iframeViews.findIndex(v => v.path === item.path)
this.iframeViews.splice(fi, 1)
}
return false
})
resolve([...this.visitedViews])
@@ -145,6 +167,10 @@ const useTagsViewStore = defineStore(
if (i > -1) {
this.cachedViews.splice(i, 1)
}
if(item.meta.link) {
const fi = this.iframeViews.findIndex(v => v.path === item.path)
this.iframeViews.splice(fi, 1)
}
return false
})
resolve([...this.visitedViews])

View File

@@ -1,3 +1,4 @@
import useDictStore from '@/store/modules/dict'
import { getDicts } from '@/api/system/dict/data'
/**
@@ -6,11 +7,17 @@ import { getDicts } from '@/api/system/dict/data'
export function useDict(...args) {
const res = ref({});
return (() => {
args.forEach((d, index) => {
res.value[d] = [];
getDicts(d).then(resp => {
res.value[d] = resp.data.map(p => ({ label: p.dictLabel, value: p.dictValue, elTagType: p.listClass, elTagClass: p.cssClass }))
})
args.forEach((dictType, index) => {
res.value[dictType] = [];
const dicts = useDictStore().getDict(dictType);
if (dicts) {
res.value[dictType] = dicts;
} else {
getDicts(dictType).then(resp => {
res.value[dictType] = resp.data.map(p => ({ label: p.dictLabel, value: p.dictValue, elTagType: p.listClass, elTagClass: p.cssClass }))
useDictStore().setDict(dictType, res.value[dictType]);
})
}
})
return toRefs(res.value);
})()

View File

@@ -130,12 +130,13 @@ service.interceptors.response.use(res => {
)
// 通用下载方法
export function download(url, params, filename) {
export function download(url, params, filename, config) {
downloadLoadingInstance = ElLoading.service({ text: "正在下载数据,请稍候", background: "rgba(0, 0, 0, 0.7)", })
return service.post(url, params, {
transformRequest: [(params) => { return tansParams(params) }],
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
responseType: 'blob'
responseType: 'blob',
...config
}).then(async (data) => {
const isLogin = await blobValidate(data);
if (isLogin) {

49
src/utils/theme.js Normal file
View File

@@ -0,0 +1,49 @@
// 处理主题样式
export function handleThemeStyle(theme) {
document.documentElement.style.setProperty('--el-color-primary', theme)
for (let i = 1; i <= 9; i++) {
document.documentElement.style.setProperty(`--el-color-primary-light-${i}`, `${getLightColor(theme, i / 10)}`)
}
for (let i = 1; i <= 9; i++) {
document.documentElement.style.setProperty(`--el-color-primary-dark-${i}`, `${getDarkColor(theme, i / 10)}`)
}
}
// hex颜色转rgb颜色
export function hexToRgb(str) {
str = str.replace('#', '')
let hexs = str.match(/../g)
for (let i = 0; i < 3; i++) {
hexs[i] = parseInt(hexs[i], 16)
}
return hexs
}
// rgb颜色转Hex颜色
export function rgbToHex(r, g, b) {
let hexs = [r.toString(16), g.toString(16), b.toString(16)]
for (let i = 0; i < 3; i++) {
if (hexs[i].length == 1) {
hexs[i] = `0${hexs[i]}`
}
}
return `#${hexs.join('')}`
}
// 变浅颜色值
export function getLightColor(color, level) {
let rgb = hexToRgb(color)
for (let i = 0; i < 3; i++) {
rgb[i] = Math.floor((255 - rgb[i]) * level + rgb[i])
}
return rgbToHex(rgb[0], rgb[1], rgb[2])
}
// 变深颜色值
export function getDarkColor(color, level) {
let rgb = hexToRgb(color)
for (let i = 0; i < 3; i++) {
rgb[i] = Math.floor(rgb[i] * (1 - level))
}
return rgbToHex(rgb[0], rgb[1], rgb[2])
}

View File

@@ -121,8 +121,8 @@
<i class="el-icon-user-solid"></i> QQ群<s>满937441</s> <s>满887144332</s>
<s>满180251782</s> <s>满104180207</s> <s>满186866453</s> <s>满201396349</s>
<s>满101456076</s> <s>满101539465</s> <s>满264312783</s> <s>满167385320</s>
<a href="https://jq.qq.com/?_wv=1027&k=DuWxuULe" target="_blank">
139821253</a
<s>满104748341</s> <a href="https://jq.qq.com/?_wv=1027&k=0fsNiYZt" target="_blank">
160110482</a
>
</p>
<p>
@@ -149,6 +149,42 @@
</div>
</template>
<el-collapse accordion>
<el-collapse-item title="v3.8.4 - 2022-09-26">
<ol>
<li>数据逻辑删除不进行唯一验证</li>
<li>Excel注解支持导出对象的子列表方法</li>
<li>Excel注解支持自定义隐藏属性列</li>
<li>Excel注解支持backgroundColor属性设置背景色</li>
<li>支持配置密码最大错误次数/锁定时间</li>
<li>登录日志新增解锁账户功能</li>
<li>通用下载方法新增config配置选项</li>
<li>支持多权限字符匹配角色数据权限</li>
<li>页面内嵌iframe切换tab不刷新数据</li>
<li>操作日志记录支持排除敏感属性字段</li>
<li>修复多文件上传报错出现的异常问题</li>
<li>修复图片预览组件src属性为null值控制台报错问题</li>
<li>升级oshi到最新版本6.2.2</li>
<li>升级fastjson到最新版2.0.14</li>
<li>升级pagehelper到最新版1.4.3</li>
<li>升级core-js到最新版本3.25.2</li>
<li>升级element-ui到最新版本2.15.10</li>
<li>优化任务过期不执行调度</li>
<li>优化字典数据使用store存取</li>
<li>优化修改资料头像被覆盖的问题</li>
<li>优化修改用户登录账号重复验证</li>
<li>优化代码生成同步后值NULL问题</li>
<li>优化定时任务支持执行父类方法</li>
<li>优化用户个人信息接口防止修改部门</li>
<li>优化布局设置使用el-drawer抽屉显示</li>
<li>优化没有权限的用户编辑部门缺少数据</li>
<li>优化日志注解记录限制请求地址的长度</li>
<li>优化excel/scale属性导出单元格数值类型</li>
<li>优化日志操作中重置按钮时重复查询的问题</li>
<li>优化多个相同角色数据导致权限SQL重复问题</li>
<li>优化表格上右侧工具条搜索按钮显隐&右侧样式凸出</li>
<li>其他细节优化</li>
</ol>
</el-collapse-item>
<el-collapse-item title="v3.8.3 - 2022-06-27">
<ol>
<li>新增缓存列表菜单功能</li>

View File

@@ -25,7 +25,7 @@
<template #prefix><svg-icon icon-class="password" class="el-input__icon input-icon" /></template>
</el-input>
</el-form-item>
<el-form-item prop="code" v-if="captchaOnOff">
<el-form-item prop="code" v-if="captchaEnabled">
<el-input
v-model="loginForm.code"
size="large"
@@ -91,7 +91,7 @@ const loginRules = {
const codeUrl = ref("");
const loading = ref(false);
// 验证码开关
const captchaOnOff = ref(true);
const captchaEnabled = ref(true);
// 注册开关
const register = ref(false);
const redirect = ref(undefined);
@@ -117,7 +117,7 @@ function handleLogin() {
}).catch(() => {
loading.value = false;
// 重新获取验证码
if (captchaOnOff.value) {
if (captchaEnabled.value) {
getCode();
}
});
@@ -127,8 +127,8 @@ function handleLogin() {
function getCode() {
getCodeImg().then(res => {
captchaOnOff.value = res.captchaOnOff === undefined ? true : res.captchaOnOff;
if (captchaOnOff.value) {
captchaEnabled.value = res.captchaEnabled === undefined ? true : res.captchaEnabled;
if (captchaEnabled.value) {
codeUrl.value = "data:image/gif;base64," + res.img;
loginForm.value.uuid = res.uuid;
}

View File

@@ -58,7 +58,7 @@
icon="Delete"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['system:logininfor:remove']"
v-hasPermi="['monitor:logininfor:remove']"
>删除</el-button>
</el-col>
<el-col :span="1.5">
@@ -67,16 +67,26 @@
plain
icon="Delete"
@click="handleClean"
v-hasPermi="['system:logininfor:remove']"
v-hasPermi="['monitor:logininfor:remove']"
>清空</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="Unlock"
:disabled="single"
@click="handleUnlock"
v-hasPermi="['monitor:logininfor:unlock']"
>解锁</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="Download"
@click="handleExport"
v-hasPermi="['system:logininfor:export']"
v-hasPermi="['monitor:logininfor:export']"
>导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
@@ -114,7 +124,7 @@
</template>
<script setup name="Logininfor">
import { list, delLogininfor, cleanLogininfor } from "@/api/monitor/logininfor";
import { list, delLogininfor, cleanLogininfor, unlockLogininfor } from "@/api/monitor/logininfor";
const { proxy } = getCurrentInstance();
const { sys_common_status } = proxy.useDict("sys_common_status");
@@ -123,7 +133,9 @@ const logininforList = ref([]);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref([]);
const single = ref(true);
const multiple = ref(true);
const selectName = ref("");
const total = ref(0);
const dateRange = ref([]);
const defaultSort = ref({ prop: "loginTime", order: "descending" });
@@ -157,13 +169,15 @@ function handleQuery() {
function resetQuery() {
dateRange.value = [];
proxy.resetForm("queryRef");
queryParams.value.pageNum = 1;
proxy.$refs["logininforRef"].sort(defaultSort.value.prop, defaultSort.value.order);
handleQuery();
}
/** 多选框选中数据 */
function handleSelectionChange(selection) {
ids.value = selection.map(item => item.infoId);
multiple.value = !selection.length;
single.value = selection.length != 1;
selectName.value = selection.map(item => item.userName);
}
/** 排序触发事件 */
function handleSortChange(column, prop, order) {
@@ -190,6 +204,15 @@ function handleClean() {
proxy.$modal.msgSuccess("清空成功");
}).catch(() => {});
}
/** 解锁按钮操作 */
function handleUnlock() {
const username = selectName.value;
proxy.$modal.confirm('是否确认解锁用户"' + username + '"数据项?').then(function () {
return unlockLogininfor(username);
}).then(() => {
proxy.$modal.msgSuccess("用户" + username + "解锁成功");
}).catch(() => {});
}
/** 导出按钮操作 */
function handleExport() {
proxy.download("monitor/logininfor/export", {

View File

@@ -73,7 +73,7 @@
icon="Delete"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['system:operlog:remove']"
v-hasPermi="['monitor:operlog:remove']"
>删除</el-button>
</el-col>
<el-col :span="1.5">
@@ -82,7 +82,7 @@
plain
icon="Delete"
@click="handleClean"
v-hasPermi="['system:operlog:remove']"
v-hasPermi="['monitor:operlog:remove']"
>清空</el-button>
</el-col>
<el-col :span="1.5">
@@ -91,7 +91,7 @@
plain
icon="Download"
@click="handleExport"
v-hasPermi="['system:operlog:export']"
v-hasPermi="['monitor:operlog:export']"
>导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
@@ -125,7 +125,7 @@
type="text"
icon="View"
@click="handleView(scope.row, scope.index)"
v-hasPermi="['system:operlog:query']"
v-hasPermi="['monitor:operlog:query']"
>详细</el-button>
</template>
</el-table-column>
@@ -239,8 +239,8 @@ function handleQuery() {
function resetQuery() {
dateRange.value = [];
proxy.resetForm("queryRef");
queryParams.value.pageNum = 1;
proxy.$refs["operlogRef"].sort(defaultSort.value.prop, defaultSort.value.order);
handleQuery();
}
/** 多选框选中数据 */
function handleSelectionChange(selection) {

View File

@@ -37,7 +37,7 @@
<template #prefix><svg-icon icon-class="password" class="el-input__icon input-icon" /></template>
</el-input>
</el-form-item>
<el-form-item prop="code" v-if="captchaOnOff">
<el-form-item prop="code" v-if="captchaEnabled">
<el-input
size="large"
v-model="registerForm.code"
@@ -116,7 +116,7 @@ const registerRules = {
const codeUrl = ref("");
const loading = ref(false);
const captchaOnOff = ref(true);
const captchaEnabled = ref(true);
function handleRegister() {
proxy.$refs.registerRef.validate(valid => {
@@ -132,7 +132,7 @@ function handleRegister() {
}).catch(() => {});
}).catch(() => {
loading.value = false;
if (captchaOnOff) {
if (captchaEnabled) {
getCode();
}
});
@@ -142,8 +142,8 @@ function handleRegister() {
function getCode() {
getCodeImg().then(res => {
captchaOnOff.value = res.captchaOnOff === undefined ? true : res.captchaOnOff;
if (captchaOnOff.value) {
captchaEnabled.value = res.captchaEnabled === undefined ? true : res.captchaEnabled;
if (captchaEnabled.value) {
codeUrl.value = "data:image/gif;base64," + res.img;
registerForm.value.uuid = res.uuid;
}

View File

@@ -185,6 +185,7 @@
</template>
<script setup name="Data">
import useDictStore from '@/store/modules/dict'
import { optionselect as getDictOptionselect, getType } from "@/api/system/dict/type";
import { listData, getData, delData, addData, updateData } from "@/api/system/dict/data";
@@ -319,12 +320,14 @@ function submitForm() {
if (valid) {
if (form.value.dictCode != undefined) {
updateData(form.value).then(response => {
useDictStore().removeDict(queryParams.value.dictType);
proxy.$modal.msgSuccess("修改成功");
open.value = false;
getList();
});
} else {
addData(form.value).then(response => {
useDictStore().removeDict(queryParams.value.dictType);
proxy.$modal.msgSuccess("新增成功");
open.value = false;
getList();
@@ -341,6 +344,7 @@ function handleDelete(row) {
}).then(() => {
getList();
proxy.$modal.msgSuccess("删除成功");
useDictStore().removeDict(queryParams.value.dictType);
}).catch(() => {});
}
/** 导出按钮操作 */

View File

@@ -182,6 +182,7 @@
</template>
<script setup name="Dict">
import useDictStore from '@/store/modules/dict'
import { listType, getType, delType, addType, updateType, refreshCache } from "@/api/system/dict/type";
const { proxy } = getCurrentInstance();
@@ -313,6 +314,7 @@ function handleExport() {
function handleRefreshCache() {
refreshCache().then(() => {
proxy.$modal.msgSuccess("刷新成功");
useDictStore().cleanDict();
});
}

View File

@@ -375,8 +375,12 @@ function selected(name) {
showChooseIcon.value = false;
}
/** 图标外层点击隐藏下拉列表 */
function hideSelectIcon() {
showChooseIcon.value = false;
function hideSelectIcon(event) {
var elem = event.relatedTarget || event.srcElement || event.target || event.currentTarget;
var className = elem.className;
if (className !== "el-input__inner") {
showChooseIcon.value = false;
}
}
/** 搜索按钮操作 */
function handleQuery() {

View File

@@ -262,9 +262,8 @@
</template>
<script setup name="Role">
import { addRole, changeRoleStatus, dataScope, delRole, getRole, listRole, updateRole } from "@/api/system/role";
import { addRole, changeRoleStatus, dataScope, delRole, getRole, listRole, updateRole, deptTreeSelect } from "@/api/system/role";
import { roleMenuTreeselect, treeselect as menuTreeselect } from "@/api/system/menu";
import { treeselect as deptTreeselect, roleDeptTreeselect } from "@/api/system/dept";
const router = useRouter();
const { proxy } = getCurrentInstance();
@@ -462,8 +461,8 @@ function getRoleMenuTreeselect(roleId) {
});
}
/** 根据角色ID查询部门树结构 */
function getRoleDeptTreeselect(roleId) {
return roleDeptTreeselect(roleId).then(response => {
function getDeptTree(roleId) {
return deptTreeSelect(roleId).then(response => {
deptOptions.value = response.depts;
return response;
});
@@ -543,12 +542,12 @@ function dataScopeSelectChange(value) {
/** 分配数据权限操作 */
function handleDataScope(row) {
reset();
const roleDeptTreeselect = getRoleDeptTreeselect(row.roleId);
const deptTreeSelect = getDeptTree(row.roleId);
getRole(row.roleId).then(response => {
form.value = response.data;
openDataScope.value = true;
nextTick(() => {
roleDeptTreeselect.then(res => {
deptTreeSelect.then(res => {
nextTick(() => {
if (deptRef.value) {
deptRef.value.setCheckedKeys(res.checkedKeys);

View File

@@ -350,8 +350,7 @@
<script setup name="User">
import { getToken } from "@/utils/auth";
import { treeselect } from "@/api/system/dept";
import { changeUserStatus, listUser, resetUserPwd, delUser, getUser, updateUser, addUser } from "@/api/system/user";
import { changeUserStatus, listUser, resetUserPwd, delUser, getUser, updateUser, addUser, deptTreeSelect } from "@/api/system/user";
const router = useRouter();
const { proxy } = getCurrentInstance();
@@ -429,8 +428,8 @@ watch(deptName, val => {
proxy.$refs["deptTreeRef"].filter(val);
});
/** 查询部门下拉树结构 */
function getTreeselect() {
treeselect().then(response => {
function getDeptTree() {
deptTreeSelect().then(response => {
deptOptions.value = response.data;
});
};
@@ -550,15 +549,6 @@ const handleFileSuccess = (response, file, fileList) => {
function submitFileForm() {
proxy.$refs["uploadRef"].submit();
};
/** 初始化部门数据 */
function initTreeData() {
// 判断部门的数据是否存在,存在不获取,不存在则获取
if (deptOptions.value === undefined) {
treeselect().then(response => {
deptOptions.value = response.data;
});
}
};
/** 重置操作表单 */
function reset() {
form.value = {
@@ -585,7 +575,6 @@ function cancel() {
/** 新增按钮操作 */
function handleAdd() {
reset();
initTreeData();
getUser().then(response => {
postOptions.value = response.posts;
roleOptions.value = response.roles;
@@ -597,7 +586,6 @@ function handleAdd() {
/** 修改按钮操作 */
function handleUpdate(row) {
reset();
initTreeData();
const userId = row.userId || ids.value;
getUser(userId).then(response => {
form.value = response.data;
@@ -631,6 +619,6 @@ function submitForm() {
});
};
getTreeselect();
getDeptTree();
getList();
</script>

View File

@@ -53,7 +53,7 @@
<userInfo :user="state.user" />
</el-tab-pane>
<el-tab-pane label="修改密码" name="resetPwd">
<resetPwd :user="state.user" />
<resetPwd />
</el-tab-pane>
</el-tabs>
</el-card>

View File

@@ -7,7 +7,7 @@
<el-input v-model="user.newPassword" placeholder="请输入新密码" type="password" show-password />
</el-form-item>
<el-form-item label="确认密码" prop="confirmPassword">
<el-input v-model="user.confirmPassword" placeholder="请确认密码" type="password" show-password/>
<el-input v-model="user.confirmPassword" placeholder="请确认密码" type="password" show-password/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submit">保存</el-button>