整理前端代码,调整命名,调整规范,移除无用代码

This commit is contained in:
valarchie 2022-11-20 12:10:53 +08:00
parent 6cdca2758f
commit 0db495ffac
49 changed files with 1121 additions and 1173 deletions

View File

@ -11,17 +11,32 @@ module.exports = {
},
},
},
extends: ['plugin:vue/vue3-essential', 'airbnb-base', 'prettier'],
// 配置这些自动引入的库 避免vscode报红
extends: [
'plugin:vue/vue3-essential',
'airbnb-base',
'prettier',
// collections
'vue-global-api/reactivity',
'vue-global-api/lifecycle',
'vue-global-api/component',
// single apis
'vue-global-api/ref',
'vue-global-api/toRef',
],
extends: [],
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
},
plugins: ['vue'],
// ESLint 是一个语法规则和代码风格的检查工具,可以用来保证写出语法正确、风格统一的代码。
rules: {
'import/no-cycle': 0,
'import/prefer-default-export': 0,
'import/order': 0,
'no-lonely-if': 0,
'no-undef': 1,
'no-unused-expressions': 0,
'vue/multi-word-component-names': [
'error',

1
.github/FUNDING.yml vendored
View File

@ -1 +0,0 @@
custom: http://doc.ruoyi.vip/ruoyi-vue/other/donate.html

View File

@ -1,3 +1,4 @@
// 代码格式化
module.exports = {
printWidth: 120,
tabWidth: 2,

View File

@ -1,5 +1,5 @@
import request from '@/utils/request';
import { parseStrEmpty } from '@/utils/ruoyi';
import { parseStrEmpty } from '@/utils/common';
// 查询用户列表
export function listUser(query) {

View File

@ -20,8 +20,12 @@
<!-- 上传提示 -->
<div class="el-upload__tip" v-if="showTip">
请上传
<template v-if="fileSize"> 大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b> </template>
<template v-if="fileType"> 格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b> </template>
<template v-if="fileSize">
大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b>
</template>
<template v-if="fileType">
格式为 <b style="color: #f56c6c">{{ fileType.join('/') }}</b>
</template>
的文件
</div>
<!-- 文件列表 -->
@ -39,7 +43,7 @@
</template>
<script setup>
import { getToken } from '@/utils/auth';
import { getToken } from '@/utils/token';
const props = defineProps({
modelValue: [String, Object, Array],
@ -73,28 +77,30 @@ const baseUrl = import.meta.env.VITE_APP_BASE_API;
const uploadFileUrl = ref(`${import.meta.env.VITE_APP_BASE_API}/file/upload`); //
const headers = ref({ Authorization: `Bearer ${getToken()}` });
const fileList = ref([]);
const showTip = computed(
() => props.isShowTip && (props.fileType || props.fileSize),
);
const showTip = computed(() => props.isShowTip && (props.fileType || props.fileSize));
watch(() => props.modelValue, (val) => {
if (val) {
let temp = 1;
//
const list = Array.isArray(val) ? val : props.modelValue.split(',');
//
fileList.value = list.map((item) => {
if (typeof item === 'string') {
item = { name: item, url: item };
}
item.uid = item.uid || new Date().getTime() + temp++;
return item;
});
} else {
fileList.value = [];
return [];
}
}, { deep: true, immediate: true });
watch(
() => props.modelValue,
(val) => {
if (val) {
let temp = 1;
//
const list = Array.isArray(val) ? val : props.modelValue.split(',');
//
fileList.value = list.map((item) => {
if (typeof item === 'string') {
item = { name: item, url: item };
}
item.uid = item.uid || new Date().getTime() + temp++;
return item;
});
} else {
fileList.value = [];
return [];
}
},
{ deep: true, immediate: true },
);
//
function handleBeforeUpload(file) {

View File

@ -1,5 +1,5 @@
<template>
<div :class="{ 'show': show }" class="header-search">
<div :class="{ show: show }" class="header-search">
<svg-icon class-name="search-icon" icon-class="search" @click.stop="click" />
<el-select
ref="headerSearchSelectRef"
@ -12,14 +12,19 @@
class="header-search-select"
@change="change"
>
<el-option v-for="option in options" :key="option.item.path" :value="option.item" :label="option.item.title.join(' > ')" />
<el-option
v-for="option in options"
:key="option.item.path"
:value="option.item"
:label="option.item.title.join(' > ')"
/>
</el-select>
</div>
</template>
<script setup>
import Fuse from 'fuse.js';
import { getNormalPath } from '@/utils/ruoyi';
import { getNormalPath } from '@/utils/common';
import { isHttp } from '@/utils/validate';
const search = ref('');
@ -67,13 +72,16 @@ function initFuse(list) {
distance: 100,
maxPatternLength: 32,
minMatchCharLength: 1,
keys: [{
name: 'title',
weight: 0.7,
}, {
name: 'path',
weight: 0.3,
}],
keys: [
{
name: 'title',
weight: 0.7,
},
{
name: 'path',
weight: 0.3,
},
],
});
}
// Filter out the routes that can be displayed in the sidebar
@ -83,7 +91,9 @@ function generateRoutes(routes, basePath = '', prefixTitle = []) {
for (const r of routes) {
// skip hidden router
if (r.hidden) { continue; }
if (r.hidden) {
continue;
}
const p = r.path.length > 0 && r.path[0] === '/' ? r.path : `/${r.path}`;
const data = {
path: !isHttp(r.path) ? getNormalPath(basePath + p) : r.path,
@ -139,7 +149,7 @@ watch(searchPool, (list) => {
});
</script>
<style lang='scss' scoped>
<style lang="scss" scoped>
.header-search {
font-size: 0 !important;

View File

@ -26,27 +26,19 @@
大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b>
</template>
<template v-if="fileType">
格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b>
格式为 <b style="color: #f56c6c">{{ fileType.join('/') }}</b>
</template>
的文件
</div>
<el-dialog
v-model="dialogVisible"
title="预览"
width="800px"
append-to-body
>
<img
:src="dialogImageUrl"
style="display: block; max-width: 100%; margin: 0 auto"
/>
<el-dialog v-model="dialogVisible" title="预览" width="800px" append-to-body>
<img :src="dialogImageUrl" style="display: block; max-width: 100%; margin: 0 auto" />
</el-dialog>
</div>
</template>
<script setup>
import { getToken } from '@/utils/auth';
import { getToken } from '@/utils/token';
const props = defineProps({
modelValue: [String, Object, Array],
@ -82,30 +74,32 @@ const baseUrl = import.meta.env.VITE_APP_BASE_API;
const uploadImgUrl = ref(`${import.meta.env.VITE_APP_BASE_API}/file/upload`); //
const headers = ref({ Authorization: `Bearer ${getToken()}` });
const fileList = ref([]);
const showTip = computed(
() => props.isShowTip && (props.fileType || props.fileSize),
);
const showTip = computed(() => props.isShowTip && (props.fileType || props.fileSize));
watch(() => props.modelValue, (val) => {
if (val) {
//
const list = Array.isArray(val) ? val : props.modelValue.split(',');
//
fileList.value = list.map((item) => {
if (typeof item === 'string') {
if (item.indexOf(baseUrl) === -1) {
item = { name: baseUrl + item, url: baseUrl + item };
} else {
item = { name: item, url: item };
watch(
() => props.modelValue,
(val) => {
if (val) {
//
const list = Array.isArray(val) ? val : props.modelValue.split(',');
//
fileList.value = list.map((item) => {
if (typeof item === 'string') {
if (item.indexOf(baseUrl) === -1) {
item = { name: baseUrl + item, url: baseUrl + item };
} else {
item = { name: item, url: item };
}
}
}
return item;
});
} else {
fileList.value = [];
return [];
}
}, { deep: true, immediate: true });
return item;
});
} else {
fileList.value = [];
return [];
}
},
{ deep: true, immediate: true },
);
//
function handleRemove(file, files) {
@ -141,9 +135,7 @@ function handleBeforeUpload(file) {
isImg = file.type.indexOf('image') > -1;
}
if (!isImg) {
proxy.$modal.msgError(
`文件格式不正确, 请上传${props.fileType.join('/')}图片格式文件!`,
);
proxy.$modal.msgError(`文件格式不正确, 请上传${props.fileType.join('/')}图片格式文件!`);
return false;
}
if (props.fileSize) {

View File

@ -1,12 +1,16 @@
// 布局设置 在src/store/modules/settings 文件夹进行设置
export default {
/**
* 网页标题
*/
title: import.meta.env.VITE_APP_TITLE,
/**
* 侧边栏主题 深色主题theme-dark浅色主题theme-light
* src\assets\styles\sidebar.scss 具体配置
*/
sideTheme: 'theme-dark',
/**
* 是否系统布局配置
*/

View File

@ -1,7 +1,7 @@
/**
* v-copyText 复制文本内容
* Copyright (c) 2022 ruoyi
*/
* v-copyText 复制文本内容
* Copyright (c) 2022 ruoyi
*/
export default {
beforeMount(el, { value, arg }) {
@ -48,7 +48,7 @@ function copyTextToClipboard(input, { target = document.body } = {}) {
let isSuccess = false;
try {
isSuccess = document.execCommand('copy');
} catch { }
} catch {}
element.remove();

View File

@ -1,9 +1,9 @@
import hasRole from './permission/hasRole';
import hasPermi from './permission/hasPermi';
import hasPermission from './permission/hasPermission';
import copyText from './common/copyText';
export default function directive(app) {
app.directive('hasRole', hasRole);
app.directive('hasPermi', hasPermi);
app.directive('hasPermission', hasPermission);
app.directive('copyText', copyText);
}

View File

@ -1,5 +1,5 @@
/**
* v-hasPermi 操作权限处理
* v-hasPermission 操作权限处理
* Copyright (c) 2019 ruoyi
*/
@ -8,13 +8,15 @@ import store from '@/store';
export default {
mounted(el, binding, vnode) {
const { value } = binding;
const all_permission = '*:*:*';
const permissions = store.getters && store.getters.permissions;
const allPermissions = '*:*:*';
const currentPermissions = store.getters && store.getters.permissions;
if (value && value instanceof Array && value.length > 0) {
const permissionFlag = value;
const hasPermissions = permissions.some((permission) => all_permission === permission || permissionFlag.includes(permission));
const hasPermissions = currentPermissions.some(
(permission) => allPermissions === permission || permissionFlag.includes(permission),
);
if (!hasPermissions) {
el.parentNode && el.parentNode.removeChild(el);

View File

@ -8,13 +8,13 @@ import store from '@/store';
export default {
mounted(el, binding, vnode) {
const { value } = binding;
const super_admin = 'admin';
const superAdmin = 'admin';
const currentRole = store.getters && store.getters.role;
if (value && value instanceof Array && value.length > 0) {
const roleFlag = value;
const hasRole = super_admin === currentRole || roleFlag.includes(currentRole);
const hasRole = superAdmin === currentRole || roleFlag.includes(currentRole);
if (!hasRole) {
el.parentNode && el.parentNode.removeChild(el);

View File

@ -1,10 +1,20 @@
<template>
<div v-if="!item.hidden">
<template v-if="hasOneShowingChild(item.children, item) && (!onlyOneChild.children || onlyOneChild.noShowingChildren) && !item.alwaysShow">
<template
v-if="
hasOneShowingChild(item.children, item) &&
(!onlyOneChild.children || onlyOneChild.noShowingChildren) &&
!item.alwaysShow
"
>
<app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path, onlyOneChild.query)">
<el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{ 'submenu-title-noDropdown': !isNest }">
<svg-icon :icon-class="onlyOneChild.meta.icon || (item.meta && item.meta.icon)"/>
<template #title><span class="menu-title" :title="hasTitle(onlyOneChild.meta.title)">{{ onlyOneChild.meta.title }}</span></template>
<svg-icon :icon-class="onlyOneChild.meta.icon || (item.meta && item.meta.icon)" />
<template #title
><span class="menu-title" :title="hasTitle(onlyOneChild.meta.title)">{{
onlyOneChild.meta.title
}}</span></template
>
</el-menu-item>
</app-link>
</template>
@ -30,7 +40,7 @@
<script setup>
import { isExternal } from '@/utils/validate';
import AppLink from './Link';
import { getNormalPath } from '@/utils/ruoyi';
import { getNormalPath } from '@/utils/common';
const props = defineProps({
// route object

View File

@ -14,36 +14,26 @@
>
{{ tag.title }}
<span v-if="!isAffix(tag)" @click.prevent.stop="closeSelectedTag(tag)">
<close class="el-icon-close" style="width: 1em; height: 1em;vertical-align: middle;" />
<close class="el-icon-close" style="width: 1em; height: 1em; vertical-align: middle" />
</span>
</router-link>
</scroll-pane>
<ul v-show="visible" :style="{ left: left + 'px', top: top + 'px' }" class="contextmenu">
<li @click="refreshSelectedTag(selectedTag)">
<refresh-right style="width: 1em; height: 1em;" /> 刷新页面
</li>
<li @click="refreshSelectedTag(selectedTag)"><refresh-right style="width: 1em; height: 1em" /> 刷新页面</li>
<li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)">
<close style="width: 1em; height: 1em;" /> 关闭当前
</li>
<li @click="closeOthersTags">
<circle-close style="width: 1em; height: 1em;" /> 关闭其他
</li>
<li v-if="!isFirstView()" @click="closeLeftTags">
<back style="width: 1em; height: 1em;" /> 关闭左侧
</li>
<li v-if="!isLastView()" @click="closeRightTags">
<right style="width: 1em; height: 1em;" /> 关闭右侧
</li>
<li @click="closeAllTags(selectedTag)">
<circle-close style="width: 1em; height: 1em;" /> 全部关闭
<close style="width: 1em; height: 1em" /> 关闭当前
</li>
<li @click="closeOthersTags"><circle-close style="width: 1em; height: 1em" /> 关闭其他</li>
<li v-if="!isFirstView()" @click="closeLeftTags"><back style="width: 1em; height: 1em" /> 关闭左侧</li>
<li v-if="!isLastView()" @click="closeRightTags"><right style="width: 1em; height: 1em" /> 关闭右侧</li>
<li @click="closeAllTags(selectedTag)"><circle-close style="width: 1em; height: 1em" /> 全部关闭</li>
</ul>
</div>
</template>
<script setup>
import ScrollPane from './ScrollPane';
import { getNormalPath } from '@/utils/ruoyi';
import { getNormalPath } from '@/utils/common';
const visible = ref(false);
const top = ref(0);
@ -180,7 +170,7 @@ function closeLeftTags() {
});
}
function closeOthersTags() {
router.push(selectedTag.value).catch(() => { });
router.push(selectedTag.value).catch(() => {});
proxy.$tab.closeOtherPage(selectedTag.value).then(() => {
moveToCurrentTag();
});
@ -233,7 +223,7 @@ function handleScroll() {
}
</script>
<style lang='scss' scoped>
<style lang="scss" scoped>
.tags-view-container {
height: 34px;
width: 100%;
@ -265,7 +255,7 @@ function handleScroll() {
color: #fff;
border-color: #42b983;
&::before {
content: "";
content: '';
background: #fff;
display: inline-block;
width: 8px;

View File

@ -1,6 +1,6 @@
<template>
<div :class="classObj" class="app-wrapper" :style="{ '--current-color': theme }">
<div v-if="device === 'mobile' && sidebar.opened" class="drawer-bg" @click="handleClickOutside"/>
<div v-if="device === 'mobile' && sidebar.opened" class="drawer-bg" @click="handleClickOutside" />
<sidebar v-if="!sidebar.hide" class="sidebar-container" />
<div :class="{ hasTagsView: needTagsView, sidebarHide: sidebar.hide }" class="main-container">
<div :class="{ 'fixed-header': fixedHeader }">
@ -16,10 +16,8 @@
<script setup>
import { useWindowSize } from '@vueuse/core';
import Sidebar from './components/Sidebar/index.vue';
import {
AppMain, Navbar, Settings, TagsView,
} from './components';
import defaultSettings from '@/settings';
import { AppMain, Navbar, Settings, TagsView } from './components';
import defaultSettings from '@/config/defaultSettings';
const store = useStore();
const theme = computed(() => store.state.settings.theme);
@ -62,8 +60,8 @@ function setLayout() {
</script>
<style lang="scss" scoped>
@import "@/assets/styles/mixin.scss";
@import "@/assets/styles/variables.module.scss";
@import '@/assets/styles/mixin.scss';
@import '@/assets/styles/variables.module.scss';
.app-wrapper {
@include clearfix;

View File

@ -1,4 +1,4 @@
import {createApp} from 'vue';
import { createApp } from 'vue';
import Cookies from 'js-cookie';
@ -14,17 +14,17 @@ import directive from './directive'; // directive
// 注册指令
import plugins from './plugins'; // plugins
import {download} from '@/utils/request';
import { download } from '@/utils/request';
// svg图标
import 'virtual:svg-icons-register';
import SvgIcon from '@/components/SvgIcon';
import elementIcons from '@/components/SvgIcon/svgicon';
import './permission'; // permission control
import './router/interceptor'; // permission control
import {useDict} from '@/utils/dict';
import {parseTime, resetForm, addDateRange, addTimeRange, handleTree, selectDictLabel} from '@/utils/ruoyi';
import { useDict } from '@/utils/dict';
import { parseTime, resetForm, addTimeRange, handleTree, selectDictLabel } from '@/utils/common';
// 分页组件
import Pagination from '@/components/Pagination';
@ -49,7 +49,6 @@ app.config.globalProperties.download = download;
app.config.globalProperties.parseTime = parseTime;
app.config.globalProperties.resetForm = resetForm;
app.config.globalProperties.handleTree = handleTree;
app.config.globalProperties.addDateRange = addDateRange;
app.config.globalProperties.addTimeRange = addTimeRange;
app.config.globalProperties.selectDictLabel = selectDictLabel;

View File

@ -26,6 +26,7 @@ const sessionCache = {
if (value != null) {
return JSON.parse(value);
}
return null;
},
remove(key) {
sessionStorage.removeItem(key);
@ -59,6 +60,7 @@ const localCache = {
if (value != null) {
return JSON.parse(value);
}
return null;
},
remove(key) {
localStorage.removeItem(key);

View File

@ -1,9 +1,9 @@
import axios from 'axios';
import { ElMessage } from 'element-plus';
import { saveAs } from 'file-saver';
import { getToken } from '@/utils/auth';
import { getToken } from '@/utils/token';
import errorCode from '@/utils/errorCode';
import { blobValidate } from '@/utils/ruoyi';
import { blobValidate } from '@/utils/common';
const baseURL = import.meta.env.VITE_APP_BASE_API;
@ -26,10 +26,10 @@ export default {
});
},
zip(url, name) {
var url = baseURL + url;
const fullURL = baseURL + url;
axios({
method: 'get',
url,
fullURL,
responseType: 'blob',
headers: { Authorization: `Bearer ${getToken()}` },
}).then(async (res) => {

View File

@ -1,5 +1,5 @@
import tab from './tab';
import auth from './auth';
import permissionChecker from './permissionChecker';
import cache from './cache';
import modal from './modal';
import download from './download';
@ -8,7 +8,7 @@ export default function installPlugins(app) {
// 页签操作
app.config.globalProperties.$tab = tab;
// 认证对象
app.config.globalProperties.$auth = auth;
app.config.globalProperties.$permissionChecker = permissionChecker;
// 缓存对象
app.config.globalProperties.$cache = cache;
// 模态框对象

View File

@ -1,34 +1,40 @@
import store from '@/store';
/**
* 现在大部分地方直接用v-hasPermission 指令来判断
* @param {*} permission
* @returns
*/
function authPermission(permission) {
const all_permission = '*:*:*';
const allPermission = '*:*:*';
const permissions = store.getters && store.getters.permissions;
if (permission && permission.length > 0) {
return permissions.some((v) => all_permission === v || v === permission);
return permissions.some((v) => allPermission === v || v === permission);
}
return false;
}
function authRole(role) {
const super_admin = 'admin';
const superAdmin = 'admin';
const currentRole = store.getters && store.getters.role;
if (role && !currentRole) {
return super_admin === currentRole || currentRole === role;
return superAdmin === currentRole || currentRole === role;
}
return false;
}
export default {
// 验证用户是否具备某权限
hasPermi(permission) {
hasPermission(permission) {
return authPermission(permission);
},
// 验证用户是否含有指定权限,只需包含其中一个
hasPermiOr(permissions) {
hasAnyPermission(permissions) {
return permissions.some((item) => authPermission(item));
},
// 验证用户是否含有指定权限,必须全部拥有
hasPermiAnd(permissions) {
hasPermissions(permissions) {
return permissions.every((item) => authPermission(item));
},
// 验证用户是否具备某角色
@ -36,11 +42,11 @@ export default {
return authRole(role);
},
// 验证用户是否含有指定角色,只需包含其中一个
hasRoleOr(roles) {
hasAnyRole(roles) {
return roles.some((item) => authRole(item));
},
// 验证用户是否含有指定角色,必须全部拥有
hasRoleAnd(roles) {
hasRoles(roles) {
return roles.every((item) => authRole(item));
},
};

View File

@ -26,13 +26,15 @@ export default {
closeOpenPage(obj) {
store.dispatch('tagsView/delView', router.currentRoute.value);
if (obj !== undefined) {
return router.push(obj);
router.push(obj);
}
},
// 关闭指定tab页签
closePage(obj) {
if (obj === undefined) {
return store.dispatch('tagsView/delView', router.currentRoute.value).then(({ lastPath }) => router.push(lastPath || '/index'));
return store
.dispatch('tagsView/delView', router.currentRoute.value)
.then(({ lastPath }) => router.push(lastPath || '/index'));
}
return store.dispatch('tagsView/delView', obj);
},

View File

@ -1,11 +1,11 @@
import { ElMessage } from 'element-plus';
import NProgress from 'nprogress';
import router from './router';
import store from './store';
import router from '.';
import store from '../store';
import 'nprogress/nprogress.css';
import { getToken } from '@/utils/auth';
import { getToken } from '@/utils/token';
import { isHttp } from '@/utils/validate';
import { isRelogin } from '@/utils/request';
import { isReLogin } from '@/utils/request';
NProgress.configure({ showSpinner: false });
@ -22,26 +22,29 @@ router.beforeEach((to, from, next) => {
next({ path: '/' });
NProgress.done();
} else if (!store.getters.role) {
console.log("重复获取")
isRelogin.show = true;
console.log('重复获取');
isReLogin.show = true;
// 判断当前用户是否已拉取完user_info信息
store.dispatch('GetInfo').then(() => {
isRelogin.show = false;
store.dispatch('GenerateRoutes').then((accessRoutes) => {
// 根据roles权限生成可访问的路由表
accessRoutes.forEach((route) => {
if (!isHttp(route.path)) {
router.addRoute(route); // 动态添加可访问路由表
}
store
.dispatch('GetInfo')
.then(() => {
isReLogin.show = false;
store.dispatch('GenerateRoutes').then((accessRoutes) => {
// 根据roles权限生成可访问的路由表
accessRoutes.forEach((route) => {
if (!isHttp(route.path)) {
router.addRoute(route); // 动态添加可访问路由表
}
});
next({ ...to, replace: true }); // hack方法 确保addRoutes已完成
});
})
.catch((err) => {
store.dispatch('LogOut').then(() => {
ElMessage.error(err);
next({ path: '/' });
});
next({ ...to, replace: true }); // hack方法 确保addRoutes已完成
});
}).catch((err) => {
store.dispatch('LogOut').then(() => {
ElMessage.error(err);
next({ path: '/' });
});
});
} else {
next();
}

View File

@ -1,9 +1,7 @@
import defaultSettings from '@/settings';
import defaultSettings from '@/config/defaultSettings';
import { useDynamicTitle } from '@/utils/dynamicTitle';
const {
sideTheme, showSettings, topNav, tagsView, fixedHeader, sidebarLogo, dynamicTitle,
} = defaultSettings;
const { sideTheme, showSettings, topNav, tagsView, fixedHeader, sidebarLogo, dynamicTitle } = defaultSettings;
const storageSetting = JSON.parse(localStorage.getItem('layout-setting')) || '';
const state = {

View File

@ -1,5 +1,5 @@
import { login, logout, getLoginUserInfo } from '@/api/login';
import { getToken, setToken, removeToken } from '@/utils/auth';
import { getToken, setToken, removeToken } from '@/utils/token';
import defAva from '@/assets/images/profile.jpg';
const user = {
@ -41,54 +41,61 @@ const user = {
const { code } = userInfo;
const { uuid } = userInfo;
return new Promise((resolve, reject) => {
login(username, password, code, uuid).then((res) => {
setToken(res.token);
commit('SET_TOKEN', res.token);
resolve();
}).catch((error) => {
reject(error);
});
login(username, password, code, uuid)
.then((res) => {
setToken(res.token);
commit('SET_TOKEN', res.token);
resolve();
})
.catch((error) => {
reject(error);
});
});
},
// 获取用户信息
GetInfo({ commit, state }) {
return new Promise((resolve, reject) => {
getLoginUserInfo().then((res) => {
const { user } = res;
// console.log(user);
const avatar = (user.avatar == '' || user.avatar == null) ? defAva : import.meta.env.VITE_APP_BASE_API + user.avatar;
// console.log("获取的key"+ res.roleKey)
// console.log(res.permissions)
if (res.roleKey) {
// 验证返回的roles是否是一个非空数组
commit('SET_ROLE', res.roleKey);
commit('SET_PERMISSIONS', res.permissions);
commit('SET_DICT_TYPES', res.dictTypes);
} else {
commit('SET_ROLE', 'ROLE_DEFAULT');
}
commit('SET_NAME', user.username);
commit('SET_AVATAR', avatar);
resolve(res);
}).catch((error) => {
reject(error);
});
getLoginUserInfo()
.then((res) => {
const { user } = res;
// console.log(user);
const avatar =
user.avatar == '' || user.avatar == null ? defAva : import.meta.env.VITE_APP_BASE_API + user.avatar;
// console.log("获取的key"+ res.roleKey)
// console.log(res.permissions)
if (res.roleKey) {
// 验证返回的roles是否是一个非空数组
commit('SET_ROLE', res.roleKey);
commit('SET_PERMISSIONS', res.permissions);
commit('SET_DICT_TYPES', res.dictTypes);
} else {
commit('SET_ROLE', 'ROLE_DEFAULT');
}
commit('SET_NAME', user.username);
commit('SET_AVATAR', avatar);
resolve(res);
})
.catch((error) => {
reject(error);
});
});
},
// 退出系统
LogOut({ commit, state }) {
return new Promise((resolve, reject) => {
logout(state.token).then(() => {
commit('SET_TOKEN', '');
commit('SET_ROLE', null);
commit('SET_PERMISSIONS', []);
removeToken();
resolve();
}).catch((error) => {
reject(error);
});
logout(state.token)
.then(() => {
commit('SET_TOKEN', '');
commit('SET_ROLE', null);
commit('SET_PERMISSIONS', []);
removeToken();
resolve();
})
.catch((error) => {
reject(error);
});
});
},

View File

@ -35,7 +35,7 @@ export function parseTime(time, pattern) {
s: date.getSeconds(),
a: date.getDay(),
};
const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
const timeStr = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
let value = formatObj[key];
// Note: getDay() returns 0 on Sunday
if (key === 'a') {
@ -46,7 +46,7 @@ export function parseTime(time, pattern) {
}
return value || 0;
});
return time_str;
return timeStr;
}
// 表单重置
@ -56,40 +56,23 @@ export function resetForm(refName) {
}
}
// TODO 弃用 添加日期范围
export function addDateRange(params, dateRange, propName) {
const search = params;
search.params = typeof search.params === 'object'
&& search.params !== null
&& !Array.isArray(search.params)
? search.params
: {};
dateRange = Array.isArray(dateRange) ? dateRange : [];
if (typeof propName === 'undefined') {
search.params.beginTime = dateRange[0];
search.params.endTime = dateRange[1];
} else {
search.params[`begin${propName}`] = dateRange[0];
search.params[`end${propName}`] = dateRange[1];
}
return search;
}
// 添加时间查询参数
export function addTimeRange(params, dateRange) {
params.beginTime = dateRange[0];
params.endTime = dateRange[1];
const [beginTime, endTime] = dateRange;
params.beginTime = beginTime;
params.endTime = endTime;
return params;
}
// 回显数据字典
export function selectDictLabel(datas, value) {
export function selectDictLabel(dicts, value) {
if (value === undefined) {
return '';
}
const actions = [];
Object.keys(datas).some((key) => {
if (datas[key].value == `${value}`) {
actions.push(datas[key].label);
Object.keys(dicts).some((key) => {
if (dicts[key].value === `${value}`) {
actions.push(dicts[key].label);
return true;
}
});
@ -122,25 +105,9 @@ export function selectDictLabels(datas, value, separator) {
return actions.join('').substring(0, actions.join('').length - 1);
}
// 字符串格式化(%s )
export function sprintf(str) {
const args = arguments;
let flag = true;
let i = 1;
str = str.replace(/%s/g, () => {
const arg = args[i++];
if (typeof arg === 'undefined') {
flag = false;
return '';
}
return arg;
});
return flag ? str : '';
}
// 转换字符串undefined,null等转化为""
export function parseStrEmpty(str) {
if (!str || str == 'undefined' || str == 'null') {
if (!str || str === 'undefined' || str === 'null' || str === null) {
return '';
}
return str;
@ -150,7 +117,7 @@ export function parseStrEmpty(str) {
export function mergeRecursive(source, target) {
for (const p in target) {
try {
if (target[p].constructor == Object) {
if (target[p].constructor === Object) {
source[p] = mergeRecursive(source[p], target[p]);
} else {
source[p] = target[p];
@ -217,7 +184,7 @@ export function handleTree(data, id, parentId, children) {
* 参数处理
* @param {*} params 参数
*/
export function tansParams(params) {
export function encodeURIParams(params) {
let result = '';
for (const propName of Object.keys(params)) {
const value = params[propName];
@ -241,7 +208,7 @@ export function tansParams(params) {
// 返回项目路径
export function getNormalPath(p) {
if (p.length === 0 || !p || p == 'undefined') {
if (p.length === 0 || !p || p === undefined) {
return p;
}
const res = p.replace('//', '/');

View File

@ -1,5 +1,5 @@
import store from '@/store';
import defaultSettings from '@/settings';
import defaultSettings from '@/config/defaultSettings';
/**
* 动态修改标题

View File

@ -1,45 +0,0 @@
import store from '@/store';
/**
* 字符权限校验
* @param {Array} value 校验值
* @returns {Boolean}
*/
export function checkPermi(value) {
if (value && value instanceof Array && value.length > 0) {
const permissions = store.getters && store.getters.permissions;
const permissionDatas = value;
const all_permission = '*:*:*';
const hasPermission = permissions.some((permission) => all_permission === permission || permissionDatas.includes(permission));
if (!hasPermission) {
return false;
}
return true;
}
console.error('need roles! Like checkPermi="[\'system:user:add\',\'system:user:edit\']"');
return false;
}
/**
* 角色权限校验
* @param {Array} value 校验值
* @returns {Boolean}
*/
export function checkRole(value) {
if (value && value instanceof Array && value.length > 0) {
const currentRole = store.getters && store.getters.role;
const permissionRoles = value;
const super_admin = 'admin';
const hasRole = super_admin === currentRole || permissionRoles.includes(currentRole);
if (!hasRole) {
return false;
}
return true;
}
console.error('need roles! Like checkRole="[\'admin\',\'editor\']"');
return false;
}

View File

@ -1,17 +1,15 @@
import axios from 'axios';
import {
ElNotification, ElMessageBox, ElMessage, ElLoading,
} from 'element-plus';
import { ElNotification, ElMessageBox, ElMessage, ElLoading } from 'element-plus';
import { saveAs } from 'file-saver';
import store from '@/store';
import { getToken } from '@/utils/auth';
import { getToken } from '@/utils/token';
import errorCode from '@/utils/errorCode';
import { tansParams, blobValidate } from '@/utils/ruoyi';
import { encodeURIParams, blobValidate } from '@/utils/common';
import cache from '@/plugins/cache';
let downloadLoadingInstance;
// 是否显示重新登录
export const isRelogin = { show: false };
export const isReLogin = { show: false };
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8';
// 创建axios实例
@ -23,53 +21,56 @@ const service = axios.create({
});
// request拦截器
service.interceptors.request.use((config) => {
// 是否需要设置 token
const isToken = (config.headers || {}).isToken === false;
// 是否需要防止数据重复提交
const isRepeatSubmit = (config.headers || {}).repeatSubmit === false;
if (getToken() && !isToken) {
config.headers.Authorization = `Bearer ${getToken()}`; // 让每个请求携带自定义token 请根据实际情况自行修改
}
// get请求映射params参数
if (config.method === 'get' && config.params) {
let url = `${config.url}?${tansParams(config.params)}`;
url = url.slice(0, -1);
config.params = {};
config.url = url;
}
if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {
const requestObj = {
url: config.url,
data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data,
time: new Date().getTime(),
};
const sessionObj = cache.session.getJSON('sessionObj');
if (sessionObj === undefined || sessionObj === null || sessionObj === '') {
cache.session.setJSON('sessionObj', requestObj);
} else {
const s_url = sessionObj.url; // 请求地址
const s_data = sessionObj.data; // 请求数据
const s_time = sessionObj.time; // 请求时间
const interval = 1000; // 间隔时间(ms),小于此时间视为重复提交
if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) {
const message = '数据正在处理,请勿重复提交';
console.warn(`[${s_url}]: ${message}`);
return Promise.reject(new Error(message));
}
cache.session.setJSON('sessionObj', requestObj);
service.interceptors.request.use(
(config) => {
// 是否需要设置 token
const isToken = (config.headers || {}).isToken === false;
// 是否需要防止数据重复提交
const isRepeatSubmit = (config.headers || {}).repeatSubmit === false;
if (getToken() && !isToken) {
config.headers.Authorization = `Bearer ${getToken()}`; // 让每个请求携带自定义token 请根据实际情况自行修改
}
}
return config;
}, (error) => {
console.log(error);
Promise.reject(error);
});
// get请求映射params参数
if (config.method === 'get' && config.params) {
let url = `${config.url}?${encodeURIParams(config.params)}`;
url = url.slice(0, -1);
config.params = {};
config.url = url;
}
if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {
const requestObj = {
url: config.url,
data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data,
time: new Date().getTime(),
};
const sessionObj = cache.session.getJSON('sessionObj');
if (sessionObj === undefined || sessionObj === null || sessionObj === '') {
cache.session.setJSON('sessionObj', requestObj);
} else {
const s_url = sessionObj.url; // 请求地址
const s_data = sessionObj.data; // 请求数据
const s_time = sessionObj.time; // 请求时间
const interval = 1000; // 间隔时间(ms),小于此时间视为重复提交
if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) {
const message = '数据正在处理,请勿重复提交';
console.warn(`[${s_url}]: ${message}`);
return Promise.reject(new Error(message));
}
cache.session.setJSON('sessionObj', requestObj);
}
}
return config;
},
(error) => {
console.log(error);
Promise.reject(error);
},
);
// 响应拦截器
service.interceptors.response.use(
(res) => {
// 未设置状态码则默认成功状态
// 未设置状态码则默认成功状态
const code = res.data.code || 200;
// 获取错误信息
const msg = errorCode[code] || res.data.msg || errorCode.default;
@ -78,29 +79,33 @@ service.interceptors.response.use(
return res.data;
}
if (code === 20101) {
if (!isRelogin.show) {
isRelogin.show = true;
if (!isReLogin.show) {
isReLogin.show = true;
ElMessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', {
confirmButtonText: '重新登录',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
isRelogin.show = false;
store.dispatch('LogOut').then(() => {
location.href = '/index';
})
.then(() => {
isReLogin.show = false;
store.dispatch('LogOut').then(() => {
location.href = '/index';
});
})
.catch(() => {
isReLogin.show = false;
});
}).catch(() => {
isRelogin.show = false;
});
}
return Promise.reject('无效的会话,或者会话已过期,请重新登录。');
} if (code === 500) {
}
if (code === 500) {
ElMessage({
message: msg,
type: 'error',
});
return Promise.reject(new Error(msg));
} if (code !== 200) {
}
if (code !== 200) {
ElNotification.error({
title: msg,
});
@ -130,27 +135,30 @@ service.interceptors.response.use(
// 通用下载方法
export function download(url, params, filename) {
downloadLoadingInstance = ElLoading.service({ text: '正在下载数据,请稍候', background: 'rgba(0, 0, 0, 0.7)' });
return service.post(url, params, {
transformRequest: [(params) => tansParams(params)],
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
responseType: 'blob',
}).then(async (data) => {
const isLogin = await blobValidate(data);
if (isLogin) {
const blob = new Blob([data]);
saveAs(blob, filename);
} else {
const resText = await data.text();
const rspObj = JSON.parse(resText);
const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode.default;
ElMessage.error(errMsg);
}
downloadLoadingInstance.close();
}).catch((r) => {
console.error(r);
ElMessage.error('下载文件出现错误,请联系管理员!');
downloadLoadingInstance.close();
});
return service
.post(url, params, {
transformRequest: [(params) => encodeURIParams(params)],
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
responseType: 'blob',
})
.then(async (data) => {
const isLogin = await blobValidate(data);
if (isLogin) {
const blob = new Blob([data]);
saveAs(blob, filename);
} else {
const resText = await data.text();
const rspObj = JSON.parse(resText);
const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode.default;
ElMessage.error(errMsg);
}
downloadLoadingInstance.close();
})
.catch((r) => {
console.error(r);
ElMessage.error('下载文件出现错误,请联系管理员!');
downloadLoadingInstance.close();
});
}
export default service;

View File

@ -1,16 +1,23 @@
Math.easeInOutQuad = function (t, b, c, d) {
t /= d / 2;
if (t < 1) {
return c / 2 * t * t + b;
return (c / 2) * t * t + b;
}
t--;
return -c / 2 * (t * (t - 2) - 1) + b;
return (-c / 2) * (t * (t - 2) - 1) + b;
};
// requestAnimationFrame for Smart Animating http://goo.gl/sx5sts
const requestAnimFrame = (function () {
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function (callback) { window.setTimeout(callback, 1000 / 60); };
}());
return (
window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function (callback) {
window.setTimeout(callback, 1000 / 60);
}
);
})();
/**
* Because it's so fucking difficult to detect the scrolling element, just move them all
@ -36,8 +43,8 @@ export function scrollTo(to, duration, callback) {
const change = to - start;
const increment = 20;
let currentTime = 0;
duration = (typeof (duration) === 'undefined') ? 500 : duration;
var animateScroll = function () {
duration = typeof duration === 'undefined' ? 500 : duration;
const animateScroll = function () {
// increment the time
currentTime += increment;
// find the value with the quadratic in-out easing function
@ -47,7 +54,7 @@ export function scrollTo(to, duration, callback) {
// do the animation unless its over
if (currentTime < duration) {
requestAnimFrame(animateScroll);
} else if (callback && typeof (callback) === 'function') {
} else if (callback && typeof callback === 'function') {
// the animation is done so lets callback
callback();
}

View File

@ -3,7 +3,7 @@
<el-form ref="loginRef" :model="loginForm" :rules="loginRules" class="login-form">
<h3 class="title">AgileBoot后台管理系统</h3>
<el-form-item prop="username">
<el-input v-model="loginForm.username" type="text" size="large" auto-complete="off" placeholder="账号">
<el-input v-model="loginForm.username" type="link" size="large" auto-complete="off" placeholder="账号">
<template #prefix><svg-icon icon-class="user" class="el-input__icon input-icon" /></template>
</el-input>
</el-form-item>

View File

@ -48,17 +48,17 @@
icon="Delete"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['system:logininfor:remove']"
v-hasPermission="['system:logininfor:remove']"
>删除</el-button
>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" @click="handleClean" v-hasPermi="['system:logininfor:remove']"
<el-button type="danger" plain icon="Delete" @click="handleClean" v-hasPermission="['system:logininfor:remove']"
>清空</el-button
>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:logininfor:export']"
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermission="['system:logininfor:export']"
>导出</el-button
>
</el-col>

View File

@ -1,63 +1,54 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryRef" :inline="true" label-width="68px">
<el-form-item label="登录地址" prop="ipaddr">
<el-input
v-model="queryParams.ipaddr"
placeholder="请输入登录地址"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="用户名称" prop="userName">
<el-input
v-model="queryParams.userName"
placeholder="请输入用户名称"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-table
v-loading="loading"
:data="onlineList.slice((pageNum - 1) * pageSize, pageNum * pageSize)"
style="width: 100%;"
>
<el-table-column label="序号" width="50" type="index" align="center">
<template #default="scope">
<span>{{ (pageNum - 1) * pageSize + scope.$index + 1 }}</span>
</template>
</el-table-column>
<el-table-column label="会话编号" align="center" prop="tokenId" :show-overflow-tooltip="true" />
<el-table-column label="登录名称" align="center" prop="userName" :show-overflow-tooltip="true" />
<el-table-column label="所属部门" align="center" prop="deptName" :show-overflow-tooltip="true" />
<el-table-column label="主机" align="center" prop="ipaddr" :show-overflow-tooltip="true" />
<el-table-column label="登录地点" align="center" prop="loginLocation" :show-overflow-tooltip="true" />
<el-table-column label="操作系统" align="center" prop="os" :show-overflow-tooltip="true" />
<el-table-column label="浏览器" align="center" prop="browser" :show-overflow-tooltip="true" />
<el-table-column label="登录时间" align="center" prop="loginTime" width="180">
<template #default="scope">
<span>{{ parseTime(scope.row.loginTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-button
type="text"
icon="Delete"
@click="handleForceLogout(scope.row)"
v-hasPermi="['monitor:online:forceLogout']"
>强退</el-button>
</template>
</el-table-column>
</el-table>
<div class="app-container">
<el-form :model="queryParams" ref="queryRef" :inline="true" label-width="68px">
<el-form-item label="登录地址" prop="ipaddr">
<el-input v-model="queryParams.ipaddr" placeholder="请输入登录地址" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="用户名称" prop="userName">
<el-input v-model="queryParams.userName" placeholder="请输入用户名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-table
v-loading="loading"
:data="onlineList.slice((pageNum - 1) * pageSize, pageNum * pageSize)"
style="width: 100%"
>
<el-table-column label="序号" width="50" type="index" align="center">
<template #default="scope">
<span>{{ (pageNum - 1) * pageSize + scope.$index + 1 }}</span>
</template>
</el-table-column>
<el-table-column label="会话编号" align="center" prop="tokenId" :show-overflow-tooltip="true" />
<el-table-column label="登录名称" align="center" prop="userName" :show-overflow-tooltip="true" />
<el-table-column label="所属部门" align="center" prop="deptName" :show-overflow-tooltip="true" />
<el-table-column label="主机" align="center" prop="ipaddr" :show-overflow-tooltip="true" />
<el-table-column label="登录地点" align="center" prop="loginLocation" :show-overflow-tooltip="true" />
<el-table-column label="操作系统" align="center" prop="os" :show-overflow-tooltip="true" />
<el-table-column label="浏览器" align="center" prop="browser" :show-overflow-tooltip="true" />
<el-table-column label="登录时间" align="center" prop="loginTime" width="180">
<template #default="scope">
<span>{{ parseTime(scope.row.loginTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-button
link
icon="Delete"
@click="handleForceLogout(scope.row)"
v-hasPermission="['monitor:online:forceLogout']"
>强退</el-button
>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="pageNum" v-model:limit="pageSize" />
</div>
<pagination v-show="total > 0" :total="total" v-model:page="pageNum" v-model:limit="pageSize" />
</div>
</template>
<script setup name="Online">
@ -79,12 +70,14 @@ const queryParams = ref({
/** 查询登录日志列表 */
function getList() {
loading.value = true;
initData(queryParams.value).then((response) => {
onlineList.value = response.rows;
total.value = response.total;
}).finally(() => {
loading.value = false;
});
initData(queryParams.value)
.then((response) => {
onlineList.value = response.rows;
total.value = response.total;
})
.finally(() => {
loading.value = false;
});
}
/** 搜索按钮操作 */
function handleQuery() {
@ -98,10 +91,14 @@ function resetQuery() {
}
/** 强退按钮操作 */
function handleForceLogout(row) {
proxy.$modal.confirm(`是否确认强退名称为"${row.userName}"的用户?`).then(() => forceLogout(row.tokenId)).then(() => {
getList();
proxy.$modal.msgSuccess('删除成功');
}).catch(() => {});
proxy.$modal
.confirm(`是否确认强退名称为"${row.userName}"的用户?`)
.then(() => forceLogout(row.tokenId))
.then(() => {
getList();
proxy.$modal.msgSuccess('删除成功');
})
.catch(() => {});
}
getList();

View File

@ -53,17 +53,22 @@
icon="Delete"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['system:operlog:remove']"
v-hasPermission="['system:operlog:remove']"
>删除</el-button
>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" @click="handleClean" v-hasPermi="['system:operlog:remove']"
<el-button type="danger" plain icon="Delete" @click="handleClean" v-hasPermission="['system:operlog:remove']"
>清空</el-button
>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:operlog:export']"
<el-button
type="warning"
plain
icon="Download"
@click="handleExport"
v-hasPermission="['system:operlog:export']"
>导出</el-button
>
</el-col>
@ -117,10 +122,10 @@
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-button
type="text"
link
icon="View"
@click="handleView(scope.row, scope.index)"
v-hasPermi="['system:operlog:query']"
v-hasPermission="['system:operlog:query']"
>详细
</el-button>
</template>
@ -181,10 +186,10 @@
</template>
<script setup name="Operlog">
import {list, deleteOperationLog, cleanOperlog} from '@/api/monitor/operlog';
import { list, deleteOperationLog, cleanOperlog } from '@/api/monitor/operlog';
const {proxy} = getCurrentInstance();
const {sys_operation_type, sys_operation_status} = proxy.useDict('sys_operation_type', 'sys_operation_status');
const { proxy } = getCurrentInstance();
const { sys_operation_type, sys_operation_status } = proxy.useDict('sys_operation_type', 'sys_operation_status');
const operlogList = ref([]);
const open = ref(false);
@ -196,7 +201,7 @@ const multiple = ref(true);
const total = ref(0);
const requestModule = ref('');
const dateRange = ref([]);
const defaultSort = ref({prop: 'operationTime', order: 'descending'});
const defaultSort = ref({ prop: 'operationTime', order: 'descending' });
const data = reactive({
form: {},
@ -210,7 +215,7 @@ const data = reactive({
},
});
const {queryParams, form} = toRefs(data);
const { queryParams, form } = toRefs(data);
/** 查询登录日志 */
function getList() {

View File

@ -3,13 +3,7 @@
<el-form ref="registerRef" :model="registerForm" :rules="registerRules" class="register-form">
<h3 class="title">AgileBoot后台管理系统</h3>
<el-form-item prop="username">
<el-input
v-model="registerForm.username"
type="text"
size="large"
auto-complete="off"
placeholder="账号"
>
<el-input v-model="registerForm.username" link size="large" auto-complete="off" placeholder="账号">
<template #prefix><svg-icon icon-class="user" class="el-input__icon input-icon" /></template>
</el-input>
</el-form-item>
@ -34,7 +28,7 @@
placeholder="确认密码"
@keyup.enter="handleRegister"
>
<template #prefix><svg-icon icon-class="password" class="el-input__icon input-icon" /></template>
<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="isCaptchaOn">
@ -49,28 +43,22 @@
<template #prefix><svg-icon icon-class="validCode" class="el-input__icon input-icon" /></template>
</el-input>
<div class="register-code">
<img :src="codeUrl" @click="getCode" class="register-code-img"/>
<img :src="codeUrl" @click="getCode" class="register-code-img" />
</div>
</el-form-item>
<el-form-item style="width:100%;">
<el-button
:loading="loading"
size="large"
type="primary"
style="width:100%;"
@click.prevent="handleRegister"
>
<el-form-item style="width: 100%">
<el-button :loading="loading" size="large" type="primary" style="width: 100%" @click.prevent="handleRegister">
<span v-if="!loading"> </span>
<span v-else> 中...</span>
</el-button>
<div style="float: right;">
<div style="float: right">
<router-link class="link-type" :to="'/login'">使用已有账户登录</router-link>
</div>
</el-form-item>
</el-form>
<!-- 底部 -->
<div class="el-register-footer">
<span>Copyright © 2018-2022 agileboot All Rights Reserved.</span>
<span>Copyright © 2018-2022 Agileboot All Rights Reserved.</span>
</div>
</div>
</template>
@ -102,13 +90,19 @@ const registerRules = {
username: [
{ required: true, trigger: 'blur', message: '请输入您的账号' },
{
min: 2, max: 20, message: '用户账号长度必须介于 2 和 20 之间', trigger: 'blur',
min: 2,
max: 20,
message: '用户账号长度必须介于 2 和 20 之间',
trigger: 'blur',
},
],
password: [
{ required: true, trigger: 'blur', message: '请输入您的密码' },
{
min: 5, max: 20, message: '用户密码长度必须介于 5 和 20 之间', trigger: 'blur',
min: 5,
max: 20,
message: '用户密码长度必须介于 5 和 20 之间',
trigger: 'blur',
},
],
confirmPassword: [
@ -126,20 +120,24 @@ function handleRegister() {
proxy.$refs.registerRef.validate((valid) => {
if (valid) {
loading.value = true;
register(registerForm.value).then((res) => {
const { username } = registerForm.value;
ElMessageBox.alert(`<font color='red'>恭喜你,您的账号 ${username} 注册成功!</font>`, '系统提示', {
dangerouslyUseHTMLString: true,
type: 'success',
}).then(() => {
router.push('/login');
}).catch(() => {});
}).catch(() => {
loading.value = false;
if (isCaptchaOn) {
getCode();
}
});
register(registerForm.value)
.then((res) => {
const { username } = registerForm.value;
ElMessageBox.alert(`<font color='red'>恭喜你,您的账号 ${username} 注册成功!</font>`, '系统提示', {
dangerouslyUseHTMLString: true,
type: 'success',
})
.then(() => {
router.push('/login');
})
.catch(() => {});
})
.catch(() => {
loading.value = false;
if (isCaptchaOn) {
getCode();
}
});
}
});
}
@ -157,13 +155,13 @@ function getCode() {
getCode();
</script>
<style lang='scss' scoped>
<style lang="scss" scoped>
.register {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
background-image: url("../assets/images/login-background.jpg");
background-image: url('../assets/images/login-background.jpg');
background-size: cover;
}
.title {

View File

@ -1,155 +1,137 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="参数名称" prop="configName">
<el-input
v-model="queryParams.configName"
placeholder="请输入参数名称"
clearable
style="width: 240px"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="参数键名" prop="configKey">
<el-input
v-model="queryParams.configKey"
placeholder="请输入参数键名"
clearable
style="width: 240px"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="允许修改" prop="isAllowChange">
<el-select v-model="queryParams.isAllowChange" placeholder="允许修改" clearable>
<el-option
v-for="dict in sys_yes_no"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="创建时间" style="width: 308px;">
<el-date-picker
v-model="dateRange"
value-format="YYYY-MM-DD"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
<div class="app-container">
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="参数名称" prop="configName">
<el-input
v-model="queryParams.configName"
placeholder="请输入参数名称"
clearable
style="width: 240px"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="参数键名" prop="configKey">
<el-input
v-model="queryParams.configKey"
placeholder="请输入参数键名"
clearable
style="width: 240px"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="允许修改" prop="isAllowChange">
<el-select v-model="queryParams.isAllowChange" placeholder="允许修改" clearable>
<el-option v-for="dict in sys_yes_no" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
<el-form-item label="创建时间" style="width: 308px">
<el-date-picker
v-model="dateRange"
value-format="YYYY-MM-DD"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button 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="success"
plain
icon="Edit"
:disabled="single"
@click="handleUpdate"
v-hasPermission="['system:config:edit']"
>修改</el-button
>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="Refresh"
@click="handleRefreshCache"
v-hasPermission="['system:config:remove']"
>刷新缓存</el-button
>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="configList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="参数主键" align="center" prop="configId" />
<el-table-column label="参数名称" align="center" prop="configName" :show-overflow-tooltip="true" />
<el-table-column label="参数键名" align="center" prop="configKey" :show-overflow-tooltip="true" />
<el-table-column label="参数键值" align="center" prop="configValue" />
<el-table-column label="可选值" align="center" prop="configOptions" />
<el-table-column label="允许修改" align="center" prop="isAllowChangeStr" />
<el-table-column label="备注" align="center" prop="remark" :show-overflow-tooltip="true" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template #default="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="150" class-name="small-padding fixed-width">
<template #default="scope">
<el-button link icon="Edit" @click="handleUpdate(scope.row)" v-hasPermission="['system:config:edit']"
>修改</el-button
>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
<!-- 添加或修改参数配置对话框 -->
<el-dialog :title="title" v-model="open" width="500px" append-to-body>
<el-form ref="configRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="参数名称" prop="configName">
<el-input v-model="form.configName" placeholder="请输入参数名称" :disabled="true" />
</el-form-item>
<el-form-item label="参数键名" prop="configKey">
<el-input v-model="form.configKey" placeholder="请输入参数键名" :disabled="true" />
</el-form-item>
<el-form-item label="参数键值" prop="configValue">
<el-select v-if="form.configOptions.length > 0" v-model="form.configValue" placeholder="Select">
<el-option v-for="item in form.configOptions" :key="item" :label="item" :value="item" />
</el-select>
<el-input v-else v-model="form.configValue" placeholder="请输入参数键值" />
</el-form-item>
<el-form-item label="允许修改" prop="isAllowChange">
<el-radio-group v-model="form.isAllowChange" :disabled="true">
<el-radio v-for="dict in sys_yes_no" :key="dict.value" :label="dict.value">{{ dict.label }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" :disabled="true" />
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="success"
plain
icon="Edit"
:disabled="single"
@click="handleUpdate"
v-hasPermi="['system:config:edit']"
>修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="Refresh"
@click="handleRefreshCache"
v-hasPermi="['system:config:remove']"
>刷新缓存</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="configList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="参数主键" align="center" prop="configId" />
<el-table-column label="参数名称" align="center" prop="configName" :show-overflow-tooltip="true" />
<el-table-column label="参数键名" align="center" prop="configKey" :show-overflow-tooltip="true" />
<el-table-column label="参数键值" align="center" prop="configValue" />
<el-table-column label="可选值" align="center" prop="configOptions" />
<el-table-column label="允许修改" align="center" prop="isAllowChangeStr"/>
<el-table-column label="备注" align="center" prop="remark" :show-overflow-tooltip="true" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template #default="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="150" class-name="small-padding fixed-width">
<template #default="scope">
<el-button
type="text"
icon="Edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['system:config:edit']"
>修改</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
<!-- 添加或修改参数配置对话框 -->
<el-dialog :title="title" v-model="open" width="500px" append-to-body>
<el-form ref="configRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="参数名称" prop="configName" >
<el-input v-model="form.configName" placeholder="请输入参数名称" :disabled="true"/>
</el-form-item>
<el-form-item label="参数键名" prop="configKey">
<el-input v-model="form.configKey" placeholder="请输入参数键名" :disabled="true"/>
</el-form-item>
<el-form-item label="参数键值" prop="configValue">
<el-select v-if="form.configOptions.length > 0" v-model="form.configValue" placeholder="Select">
<el-option
v-for="item in form.configOptions"
:key="item"
:label="item"
:value="item"
/>
</el-select>
<el-input v-else
v-model="form.configValue" placeholder="请输入参数键值"/>
</el-form-item>
<el-form-item label="允许修改" prop="isAllowChange">
<el-radio-group v-model="form.isAllowChange" :disabled="true">
<el-radio
v-for="dict in sys_yes_no"
:key="dict.value"
:label="dict.value"
>{{ dict.label }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" :disabled="true"/>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
</div>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="Config">
import {
listConfig, getConfig, updateConfig, refreshCache,
} from '@/api/system/config';
import { listConfig, getConfig, updateConfig, refreshCache } from '@/api/system/config';
const { proxy } = getCurrentInstance();
const { sys_yes_no } = proxy.useDict('sys_yes_no');
@ -186,12 +168,14 @@ const { queryParams, form, rules } = toRefs(data);
/** 查询参数列表 */
function getList() {
loading.value = true;
listConfig(proxy.addTimeRange(queryParams.value, dateRange.value)).then((response) => {
configList.value = response.rows;
total.value = response.total;
}).finally(() => {
loading.value = false;
});
listConfig(proxy.addTimeRange(queryParams.value, dateRange.value))
.then((response) => {
configList.value = response.rows;
total.value = response.total;
})
.finally(() => {
loading.value = false;
});
}
/** 取消按钮 */
function cancel() {

View File

@ -17,7 +17,9 @@
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:dept:add']">新增</el-button>
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermission="['system:dept:add']"
>新增</el-button
>
</el-col>
<el-col :span="1.5">
<el-button type="info" plain icon="Sort" @click="toggleExpandAll">展开/折叠</el-button>
@ -31,7 +33,7 @@
:data="deptList"
row-key="deptId"
:default-expand-all="isExpandAll"
:tree-props="{children: 'children', hasChildren: 'hasChildren'}"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
>
<el-table-column prop="deptName" label="部门名称" width="260"></el-table-column>
<el-table-column prop="orderNum" label="排序" width="200"></el-table-column>
@ -47,18 +49,18 @@
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-button type="text" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:dept:edit']"
<el-button link icon="Edit" @click="handleUpdate(scope.row)" v-hasPermission="['system:dept:edit']"
>修改</el-button
>
<el-button type="text" icon="Plus" @click="handleAdd(scope.row)" v-hasPermi="['system:dept:add']"
<el-button link icon="Plus" @click="handleAdd(scope.row)" v-hasPermission="['system:dept:add']"
>新增</el-button
>
<el-button
v-if="scope.row.parentId != 0"
type="text"
link
icon="Delete"
@click="handleDelete(scope.row)"
v-hasPermi="['system:dept:remove']"
v-hasPermission="['system:dept:remove']"
>删除</el-button
>
</template>
@ -74,7 +76,7 @@
<el-tree-select
v-model="form.parentId"
:data="deptOptions"
:props="{value: 'deptId', label: 'deptName', children: 'children'}"
:props="{ value: 'deptId', label: 'deptName', children: 'children' }"
value-key="deptId"
placeholder="选择上级部门"
check-strictly
@ -126,10 +128,17 @@
</template>
<script setup name="Dept">
import {listDept, getDept, deleteDept, addDept, updateDept, listDeptExcludeCurrentDeptItself} from '@/api/system/dept';
import {
listDept,
getDept,
deleteDept,
addDept,
updateDept,
listDeptExcludeCurrentDeptItself,
} from '@/api/system/dept';
const {proxy} = getCurrentInstance();
const {sys_status} = proxy.useDict('sys_status');
const { proxy } = getCurrentInstance();
const { sys_status } = proxy.useDict('sys_status');
const deptList = ref([]);
const open = ref(false);
@ -147,15 +156,15 @@ const data = reactive({
status: undefined,
},
rules: {
parentId: [{required: true, message: '上级部门不能为空', trigger: 'blur'}],
deptName: [{required: true, message: '部门名称不能为空', trigger: 'blur'}],
orderNum: [{required: true, message: '显示排序不能为空', trigger: 'blur'}],
email: [{type: 'email', message: '请输入正确的邮箱地址', trigger: ['blur', 'change']}],
phone: [{pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: '请输入正确的手机号码', trigger: 'blur'}],
parentId: [{ required: true, message: '上级部门不能为空', trigger: 'blur' }],
deptName: [{ required: true, message: '部门名称不能为空', trigger: 'blur' }],
orderNum: [{ required: true, message: '显示排序不能为空', trigger: 'blur' }],
email: [{ type: 'email', message: '请输入正确的邮箱地址', trigger: ['blur', 'change'] }],
phone: [{ pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: '请输入正确的手机号码', trigger: 'blur' }],
},
});
const {queryParams, form, rules} = toRefs(data);
const { queryParams, form, rules } = toRefs(data);
/** 查询部门列表 */
function getList() {

View File

@ -17,7 +17,9 @@
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:menu:add']">新增</el-button>
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermission="['system:menu:add']"
>新增</el-button
>
</el-col>
<el-col :span="1.5">
<el-button type="info" plain icon="Sort" @click="toggleExpandAll">展开/折叠</el-button>
@ -31,7 +33,7 @@
:data="menuList"
row-key="menuId"
:default-expand-all="isExpandAll"
:tree-props="{children: 'children', hasChildren: 'hasChildren'}"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
>
<el-table-column prop="menuName" label="菜单名称" :show-overflow-tooltip="true" width="160"></el-table-column>
<el-table-column prop="icon" label="图标" align="center" width="100">
@ -54,13 +56,13 @@
</el-table-column>
<el-table-column label="操作" align="center" width="200" class-name="small-padding fixed-width">
<template #default="scope">
<el-button type="text" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:menu:edit']"
<el-button link icon="Edit" @click="handleUpdate(scope.row)" v-hasPermission="['system:menu:edit']"
>修改</el-button
>
<el-button type="text" icon="Plus" @click="handleAdd(scope.row)" v-hasPermi="['system:menu:add']"
<el-button link icon="Plus" @click="handleAdd(scope.row)" v-hasPermission="['system:menu:add']"
>新增</el-button
>
<el-button type="text" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:menu:remove']"
<el-button link icon="Delete" @click="handleDelete(scope.row)" v-hasPermission="['system:menu:remove']"
>删除</el-button
>
</template>
@ -76,7 +78,7 @@
<el-tree-select
v-model="form.parentId"
:data="menuOptions"
:props="{value: 'menuId', label: 'menuName', children: 'children'}"
:props="{ value: 'menuId', label: 'menuName', children: 'children' }"
value-key="menuId"
placeholder="选择上级菜单"
check-strictly
@ -178,7 +180,7 @@
<template #label>
<span>
<el-tooltip
content="控制器中定义的权限字符,如:@PreAuthorize(`@ss.hasPermi('system:user:list')`)"
content="控制器中定义的权限字符,如:@PreAuthorize(`@permission.has('system:config:list')`)"
placement="top"
>
<el-icon><question-filled /></el-icon>
@ -263,12 +265,12 @@
</template>
<script setup name="Menu">
import {addMenu, deleteMenu, getMenu, listMenu, updateMenu} from '@/api/system/menu';
import { addMenu, deleteMenu, getMenu, listMenu, updateMenu } from '@/api/system/menu';
import SvgIcon from '@/components/SvgIcon';
import IconSelect from '@/components/IconSelect';
const {proxy} = getCurrentInstance();
const {sys_visible, sys_status} = proxy.useDict('sys_visible', 'sys_status');
const { proxy } = getCurrentInstance();
const { sys_visible, sys_status } = proxy.useDict('sys_visible', 'sys_status');
const menuList = ref([]);
const open = ref(false);
@ -288,13 +290,13 @@ const data = reactive({
isVisible: undefined,
},
rules: {
menuName: [{required: true, message: '菜单名称不能为空', trigger: 'blur'}],
orderNum: [{required: true, message: '菜单顺序不能为空', trigger: 'blur'}],
path: [{required: true, message: '路由地址不能为空', trigger: 'blur'}],
menuName: [{ required: true, message: '菜单名称不能为空', trigger: 'blur' }],
orderNum: [{ required: true, message: '菜单顺序不能为空', trigger: 'blur' }],
path: [{ required: true, message: '路由地址不能为空', trigger: 'blur' }],
},
});
const {queryParams, form, rules} = toRefs(data);
const { queryParams, form, rules } = toRefs(data);
/** 查询菜单列表 */
function getList() {
@ -311,7 +313,7 @@ function getList() {
function getTreeSelect() {
menuOptions.value = [];
listMenu().then((response) => {
const menu = {menuId: 0, menuName: '主类目', children: []};
const menu = { menuId: 0, menuName: '主类目', children: [] };
menu.children = proxy.handleTree(response, 'menuId');
menuOptions.value.push(menu);
});

View File

@ -1,180 +1,143 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="公告标题" prop="noticeTitle">
<el-input
v-model="queryParams.noticeTitle"
placeholder="请输入公告标题"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="操作人员" prop="creatorName">
<el-input
v-model="queryParams.creatorName"
placeholder="请输入操作人员"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="类型" prop="noticeType">
<el-select v-model="queryParams.noticeType" placeholder="公告类型" clearable>
<el-option
<div class="app-container">
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="公告标题" prop="noticeTitle">
<el-input v-model="queryParams.noticeTitle" placeholder="请输入公告标题" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="操作人员" prop="creatorName">
<el-input v-model="queryParams.creatorName" placeholder="请输入操作人员" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="类型" prop="noticeType">
<el-select v-model="queryParams.noticeType" placeholder="公告类型" clearable>
<el-option v-for="dict in sys_notice_type" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button 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="Plus" @click="handleAdd" v-hasPermission="['system:notice:add']"
>新增</el-button
>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="Edit"
:disabled="single"
@click="handleUpdate"
v-hasPermission="['system:notice:edit']"
>修改</el-button
>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="Delete"
:disabled="multiple"
@click="handleDelete"
v-hasPermission="['system:notice:remove']"
>删除</el-button
>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="noticeList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="序号" align="center" prop="noticeId" width="100" />
<el-table-column label="公告标题" align="center" prop="noticeTitle" :show-overflow-tooltip="true" />
<el-table-column label="公告类型" align="center" prop="noticeType" width="100">
<template #default="scope">
<dict-tag :options="sys_notice_type" :value="scope.row.noticeType" />
</template>
</el-table-column>
<el-table-column label="状态" align="center" prop="status" width="100">
<template #default="scope">
<dict-tag :options="sys_notice_status" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="创建者" align="center" prop="creatorName" width="100" />
<el-table-column label="创建时间" align="center" prop="createTime" width="100">
<template #default="scope">
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-button link icon="Edit" @click="handleUpdate(scope.row)" v-hasPermission="['system:notice:edit']"
>修改</el-button
>
<el-button link icon="Delete" @click="handleDelete(scope.row)" v-hasPermission="['system:notice:remove']"
>删除</el-button
>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
<!-- 添加或修改公告对话框 -->
<el-dialog :title="title" v-model="open" width="780px" append-to-body>
<el-form ref="noticeRef" :model="form" :rules="rules" label-width="80px">
<el-row>
<el-col :span="12">
<el-form-item label="公告标题" prop="noticeTitle">
<el-input v-model="form.noticeTitle" placeholder="请输入公告标题" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="公告类型" prop="noticeType">
<el-select v-model="form.noticeType" placeholder="请选择">
<el-option
v-for="dict in sys_notice_type"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="状态">
<el-radio-group v-model="form.status">
<el-radio v-for="dict in sys_notice_status" :key="dict.value" :label="dict.value">{{
dict.label
}}</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="内容">
<el-input :rows="6" type="textarea" placeholder="请输入内容" v-model="form.noticeContent" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="Plus"
@click="handleAdd"
v-hasPermi="['system:notice:add']"
>新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="Edit"
:disabled="single"
@click="handleUpdate"
v-hasPermi="['system:notice:edit']"
>修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="Delete"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['system:notice:remove']"
>删除</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="noticeList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="序号" align="center" prop="noticeId" width="100" />
<el-table-column
label="公告标题"
align="center"
prop="noticeTitle"
:show-overflow-tooltip="true"
/>
<el-table-column label="公告类型" align="center" prop="noticeType" width="100">
<template #default="scope">
<dict-tag :options="sys_notice_type" :value="scope.row.noticeType" />
</template>
</el-table-column>
<el-table-column label="状态" align="center" prop="status" width="100">
<template #default="scope">
<dict-tag :options="sys_notice_status" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="创建者" align="center" prop="creatorName" width="100" />
<el-table-column label="创建时间" align="center" prop="createTime" width="100">
<template #default="scope">
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-button
type="text"
icon="Edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['system:notice:edit']"
>修改</el-button>
<el-button
type="text"
icon="Delete"
@click="handleDelete(scope.row)"
v-hasPermi="['system:notice:remove']"
>删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
<!-- 添加或修改公告对话框 -->
<el-dialog :title="title" v-model="open" width="780px" append-to-body>
<el-form ref="noticeRef" :model="form" :rules="rules" label-width="80px">
<el-row>
<el-col :span="12">
<el-form-item label="公告标题" prop="noticeTitle">
<el-input v-model="form.noticeTitle" placeholder="请输入公告标题" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="公告类型" prop="noticeType">
<el-select v-model="form.noticeType" placeholder="请选择">
<el-option
v-for="dict in sys_notice_type"
:key="dict.value"
:label="dict.label"
:value="dict.value"
></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="状态">
<el-radio-group v-model="form.status">
<el-radio
v-for="dict in sys_notice_status"
:key="dict.value"
:label="dict.value"
>{{ dict.label }}</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="内容">
<el-input
:rows="6"
type="textarea"
placeholder="请输入内容"
v-model="form.noticeContent"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
</div>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="Notice">
import {
listNotice, getNotice, deleteNotice, addNotice, updateNotice,
} from '@/api/system/notice';
import { listNotice, getNotice, deleteNotice, addNotice, updateNotice } from '@/api/system/notice';
const { proxy } = getCurrentInstance();
const { sys_notice_status, sys_notice_type } = proxy.useDict('sys_notice_status', 'sys_notice_type');
@ -209,12 +172,14 @@ const { queryParams, form, rules } = toRefs(data);
/** 查询公告列表 */
function getList() {
loading.value = true;
listNotice(queryParams.value).then((response) => {
noticeList.value = response.rows;
total.value = response.total;
}).finally(() => {
loading.value = false;
});
listNotice(queryParams.value)
.then((response) => {
noticeList.value = response.rows;
total.value = response.total;
})
.finally(() => {
loading.value = false;
});
}
/** 取消按钮 */
function cancel() {
@ -287,10 +252,14 @@ function submitForm() {
/** 删除按钮操作 */
function handleDelete(row) {
const noticeIds = row.noticeId || ids.value;
proxy.$modal.confirm(`是否确认删除公告编号为"${noticeIds}"的数据项?`).then(() => deleteNotice(noticeIds)).then(() => {
getList();
proxy.$modal.msgSuccess('删除成功');
}).catch(() => {});
proxy.$modal
.confirm(`是否确认删除公告编号为"${noticeIds}"的数据项?`)
.then(() => deleteNotice(noticeIds))
.then(() => {
getList();
proxy.$modal.msgSuccess('删除成功');
})
.catch(() => {});
}
getList();

View File

@ -1,161 +1,128 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="岗位编码" prop="postCode">
<el-input
v-model="queryParams.postCode"
placeholder="请输入岗位编码"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="岗位名称" prop="postName">
<el-input
v-model="queryParams.postName"
placeholder="请输入岗位名称"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="岗位状态" clearable>
<el-option
v-for="dict in sys_status"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
<div class="app-container">
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="岗位编码" prop="postCode">
<el-input v-model="queryParams.postCode" placeholder="请输入岗位编码" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="岗位名称" prop="postName">
<el-input v-model="queryParams.postName" placeholder="请输入岗位名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="岗位状态" clearable>
<el-option v-for="dict in sys_status" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button 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="Plus" @click="handleAdd" v-hasPermission="['system:post:add']"
>新增</el-button
>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="Edit"
:disabled="single"
@click="handleUpdate"
v-hasPermission="['system:post:edit']"
>修改</el-button
>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="Delete"
:disabled="multiple"
@click="handleDelete"
v-hasPermission="['system:post:remove']"
>删除</el-button
>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermission="['system:post:export']"
>导出</el-button
>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="postList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="岗位编号" align="center" prop="postId" />
<el-table-column label="岗位编码" align="center" prop="postCode" />
<el-table-column label="岗位名称" align="center" prop="postName" />
<el-table-column label="岗位排序" align="center" prop="postSort" />
<el-table-column label="状态" align="center" prop="status">
<template #default="scope">
<dict-tag :options="sys_status" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template #default="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-button link icon="Edit" @click="handleUpdate(scope.row)" v-hasPermission="['system:post:edit']"
>修改</el-button
>
<el-button link icon="Delete" @click="handleDelete(scope.row)" v-hasPermission="['system:post:remove']"
>删除</el-button
>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
<!-- 添加或修改岗位对话框 -->
<el-dialog :title="title" v-model="open" width="500px" append-to-body>
<el-form ref="postRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="岗位名称" prop="postName">
<el-input v-model="form.postName" placeholder="请输入岗位名称" />
</el-form-item>
<el-form-item label="岗位编码" prop="postCode">
<el-input v-model="form.postCode" placeholder="请输入编码名称" />
</el-form-item>
<el-form-item label="岗位顺序" prop="postSort">
<el-input-number v-model="form.postSort" controls-position="right" :min="0" />
</el-form-item>
<el-form-item label="岗位状态" prop="status">
<el-radio-group v-model="form.status">
<el-radio v-for="dict in sys_status" :key="dict.value" :label="dict.value">{{ dict.label }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="Plus"
@click="handleAdd"
v-hasPermi="['system:post:add']"
>新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="Edit"
:disabled="single"
@click="handleUpdate"
v-hasPermi="['system:post:edit']"
>修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="Delete"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['system:post:remove']"
>删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="Download"
@click="handleExport"
v-hasPermi="['system:post:export']"
>导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="postList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="岗位编号" align="center" prop="postId" />
<el-table-column label="岗位编码" align="center" prop="postCode" />
<el-table-column label="岗位名称" align="center" prop="postName" />
<el-table-column label="岗位排序" align="center" prop="postSort" />
<el-table-column label="状态" align="center" prop="status">
<template #default="scope">
<dict-tag :options="sys_status" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template #default="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-button
type="text"
icon="Edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['system:post:edit']"
>修改</el-button>
<el-button
type="text"
icon="Delete"
@click="handleDelete(scope.row)"
v-hasPermi="['system:post:remove']"
>删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
<!-- 添加或修改岗位对话框 -->
<el-dialog :title="title" v-model="open" width="500px" append-to-body>
<el-form ref="postRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="岗位名称" prop="postName">
<el-input v-model="form.postName" placeholder="请输入岗位名称" />
</el-form-item>
<el-form-item label="岗位编码" prop="postCode">
<el-input v-model="form.postCode" placeholder="请输入编码名称" />
</el-form-item>
<el-form-item label="岗位顺序" prop="postSort">
<el-input-number v-model="form.postSort" controls-position="right" :min="0" />
</el-form-item>
<el-form-item label="岗位状态" prop="status">
<el-radio-group v-model="form.status">
<el-radio
v-for="dict in sys_status"
:key="dict.value"
:label="dict.value"
>{{ dict.label }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
</div>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="Post">
import {
listPost, addPost, deletePost, getPost, updatePost,
} from '@/api/system/post';
import { listPost, addPost, deletePost, getPost, updatePost } from '@/api/system/post';
const { proxy } = getCurrentInstance();
const { sys_status } = proxy.useDict('sys_status');
@ -191,12 +158,14 @@ const { queryParams, form, rules } = toRefs(data);
/** 查询岗位列表 */
function getList() {
loading.value = true;
listPost(queryParams.value).then((response) => {
postList.value = response.rows;
total.value = response.total;
}).finally(() => {
loading.value = false;
});
listPost(queryParams.value)
.then((response) => {
postList.value = response.rows;
total.value = response.total;
})
.finally(() => {
loading.value = false;
});
}
/** 取消按钮 */
function cancel() {
@ -270,16 +239,24 @@ function submitForm() {
/** 删除按钮操作 */
function handleDelete(row) {
const postIds = row.postId || ids.value;
proxy.$modal.confirm(`是否确认删除岗位编号为"${postIds}"的数据项?`).then(() => deletePost(postIds)).then(() => {
getList();
proxy.$modal.msgSuccess('删除成功');
}).catch(() => {});
proxy.$modal
.confirm(`是否确认删除岗位编号为"${postIds}"的数据项?`)
.then(() => deletePost(postIds))
.then(() => {
getList();
proxy.$modal.msgSuccess('删除成功');
})
.catch(() => {});
}
/** 导出按钮操作 */
function handleExport() {
proxy.download('system/post/export', {
...queryParams.value,
}, `post_${new Date().getTime()}.xlsx`);
proxy.download(
'system/post/export',
{
...queryParams.value,
},
`post_${new Date().getTime()}.xlsx`,
);
}
getList();

View File

@ -27,7 +27,7 @@
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="openSelectUser" v-hasPermi="['system:role:add']"
<el-button type="primary" plain icon="Plus" @click="openSelectUser" v-hasPermission="['system:role:add']"
>添加用户</el-button
>
</el-col>
@ -38,7 +38,7 @@
icon="CircleClose"
:disabled="multiple"
@click="cancelAuthUserAll"
v-hasPermi="['system:role:remove']"
v-hasPermission="['system:role:remove']"
>批量取消授权</el-button
>
</el-col>
@ -66,11 +66,7 @@
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-button
type="text"
icon="CircleClose"
@click="cancelAuthUser(scope.row)"
v-hasPermi="['system:role:remove']"
<el-button link icon="CircleClose" @click="cancelAuthUser(scope.row)" v-hasPermission="['system:role:remove']"
>取消授权</el-button
>
</template>
@ -90,11 +86,11 @@
<script setup name="AuthUser">
import selectUser from './selectUser';
import {allocatedUserList, deleteRoleOfUser, deleteRoleOfSomeUser} from '@/api/system/role';
import { allocatedUserList, deleteRoleOfUser, deleteRoleOfSomeUser } from '@/api/system/role';
const route = useRoute();
const {proxy} = getCurrentInstance();
const {sys_status} = proxy.useDict('sys_status');
const { proxy } = getCurrentInstance();
const { sys_status } = proxy.useDict('sys_status');
const userList = ref([]);
const loading = ref(true);
@ -125,7 +121,7 @@ function getList() {
}
//
function handleClose() {
const obj = {path: '/system/role'};
const obj = { path: '/system/role' };
proxy.$tab.closeOpenPage(obj);
}
/** 搜索按钮操作 */
@ -151,7 +147,7 @@ function openSelectUser() {
function cancelAuthUser(row) {
proxy.$modal
.confirm(`确认要取消该用户"${row.username}"角色吗?`)
.then(() => deleteRoleOfSomeUser({userIds: row.userId}))
.then(() => deleteRoleOfSomeUser({ userIds: row.userId }))
.then(() => {
getList();
proxy.$modal.msgSuccess('取消授权成功');
@ -160,11 +156,11 @@ function cancelAuthUser(row) {
}
/** 批量取消授权按钮操作 */
function cancelAuthUserAll(row) {
const {roleId} = queryParams;
const { roleId } = queryParams;
const uIds = userIds.value.join(',');
proxy.$modal
.confirm('是否取消选中用户授权数据项?')
.then(() => deleteRoleOfSomeUser({roleId, userIds: uIds}))
.then(() => deleteRoleOfSomeUser({ roleId, userIds: uIds }))
.then(() => {
getList();
proxy.$modal.msgSuccess('取消授权成功');

View File

@ -41,7 +41,9 @@
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:role:add']">新增</el-button>
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermission="['system:role:add']"
>新增</el-button
>
</el-col>
<el-col :span="1.5">
<el-button
@ -50,7 +52,7 @@
icon="Edit"
:disabled="single"
@click="handleUpdate"
v-hasPermi="['system:role:edit']"
v-hasPermission="['system:role:edit']"
>修改</el-button
>
</el-col>
@ -61,12 +63,12 @@
icon="Delete"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['system:role:remove']"
v-hasPermission="['system:role:remove']"
>删除</el-button
>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:role:export']"
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermission="['system:role:export']"
>导出</el-button
>
</el-col>
@ -99,34 +101,34 @@
<template #default="scope">
<el-tooltip content="修改" placement="top" v-if="scope.row.roleId !== 1">
<el-button
type="text"
link
icon="Edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['system:role:edit']"
v-hasPermission="['system:role:edit']"
></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top" v-if="scope.row.roleId !== 1">
<el-button
type="text"
link
icon="Delete"
@click="handleDelete(scope.row)"
v-hasPermi="['system:role:remove']"
v-hasPermission="['system:role:remove']"
></el-button>
</el-tooltip>
<el-tooltip content="数据权限" placement="top" v-if="scope.row.roleId !== 1">
<el-button
type="text"
link
icon="CircleCheck"
@click="handleDataScope(scope.row)"
v-hasPermi="['system:role:edit']"
v-hasPermission="['system:role:edit']"
></el-button>
</el-tooltip>
<el-tooltip content="分配用户" placement="top" v-if="scope.row.roleId !== 1">
<el-button
type="text"
link
icon="User"
@click="handleAuthUser(scope.row)"
v-hasPermi="['system:role:edit']"
v-hasPermission="['system:role:edit']"
></el-button>
</el-tooltip>
</template>
@ -182,7 +184,7 @@
node-key="menuId"
:check-strictly="!form.menuCheckStrictly"
empty-text="加载中,请稍候"
:props="{label: 'label', children: 'children'}"
:props="{ label: 'label', children: 'children' }"
></el-tree>
</el-form-item>
<el-form-item label="备注">
@ -233,7 +235,7 @@
node-key="id"
:check-strictly="!form.deptCheckStrictly"
empty-text="加载中,请稍候"
:props="{label: 'label', children: 'children'}"
:props="{ label: 'label', children: 'children' }"
></el-tree>
</el-form-item>
</el-form>
@ -248,13 +250,21 @@
</template>
<script setup name="Role">
import {addRole, changeRoleStatus, changeDataScope, deleteRole, getRole, listRole, updateRole} from '@/api/system/role';
import {getMenuSelectTreeByRole, getMenuSelectTree} from '@/api/system/menu';
import {getDeptSelectTree, getDeptTreeSelectByRole} from '@/api/system/dept';
import {
addRole,
changeRoleStatus,
changeDataScope,
deleteRole,
getRole,
listRole,
updateRole,
} from '@/api/system/role';
import { getMenuSelectTreeByRole, getMenuSelectTree } from '@/api/system/menu';
import { getDeptSelectTree, getDeptTreeSelectByRole } from '@/api/system/dept';
const router = useRouter();
const {proxy} = getCurrentInstance();
const {sys_status} = proxy.useDict('sys_status');
const { proxy } = getCurrentInstance();
const { sys_status } = proxy.useDict('sys_status');
const roleList = ref([]);
const open = ref(false);
@ -278,11 +288,11 @@ const deptRef = ref(null);
/** 数据范围选项 */
const dataScopeOptions = ref([
{value: '1', label: '全部数据权限'},
{value: '2', label: '自定数据权限'},
{value: '3', label: '本部门数据权限'},
{value: '4', label: '本部门及以下数据权限'},
{value: '5', label: '仅本人数据权限'},
{ value: '1', label: '全部数据权限' },
{ value: '2', label: '自定数据权限' },
{ value: '3', label: '本部门数据权限' },
{ value: '4', label: '本部门及以下数据权限' },
{ value: '5', label: '仅本人数据权限' },
]);
const data = reactive({
@ -295,13 +305,13 @@ const data = reactive({
status: undefined,
},
rules: {
roleName: [{required: true, message: '角色名称不能为空', trigger: 'blur'}],
roleKey: [{required: true, message: '权限字符不能为空', trigger: 'blur'}],
roleSort: [{required: true, message: '角色顺序不能为空', trigger: 'blur'}],
roleName: [{ required: true, message: '角色名称不能为空', trigger: 'blur' }],
roleKey: [{ required: true, message: '权限字符不能为空', trigger: 'blur' }],
roleSort: [{ required: true, message: '角色顺序不能为空', trigger: 'blur' }],
},
});
const {queryParams, form, rules} = toRefs(data);
const { queryParams, form, rules } = toRefs(data);
/** 查询角色列表 */
function getList() {
@ -440,7 +450,7 @@ function handleUpdate(row) {
open.value = true;
nextTick(() => {
roleMenu.then((res) => {
const {checkedKeys} = res;
const { checkedKeys } = res;
checkedKeys.forEach((v) => {
nextTick(() => {
menuRef.value.setChecked(v, true, false);

View File

@ -15,7 +15,7 @@
<div class="head-container">
<el-tree
:data="deptOptions"
:props="{label: 'label', children: 'children'}"
:props="{ label: 'label', children: 'children' }"
:expand-on-click-node="false"
:filter-node-method="filterNode"
ref="deptTreeRef"
@ -69,7 +69,7 @@
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:user:add']"
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermission="['system:user:add']"
>新增</el-button
>
</el-col>
@ -80,7 +80,7 @@
icon="Edit"
:disabled="single"
@click="handleUpdate"
v-hasPermi="['system:user:edit']"
v-hasPermission="['system:user:edit']"
>修改</el-button
>
</el-col>
@ -91,17 +91,22 @@
icon="Delete"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['system:user:remove']"
v-hasPermission="['system:user:remove']"
>删除</el-button
>
</el-col>
<el-col :span="1.5">
<el-button type="info" plain icon="Upload" @click="handleImport" v-hasPermi="['system:user:import']"
<el-button type="info" plain icon="Upload" @click="handleImport" v-hasPermission="['system:user:import']"
>导入</el-button
>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:user:export']"
<el-button
type="warning"
plain
icon="Download"
@click="handleExport"
v-hasPermission="['system:user:export']"
>导出</el-button
>
</el-col>
@ -162,34 +167,34 @@
<template #default="scope">
<el-tooltip content="修改" placement="top" v-if="scope.row.userId !== 1">
<el-button
type="text"
link
icon="Edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['system:user:edit']"
v-hasPermission="['system:user:edit']"
></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top" v-if="scope.row.userId !== 1">
<el-button
type="text"
link
icon="Delete"
@click="handleDelete(scope.row)"
v-hasPermi="['system:user:remove']"
v-hasPermission="['system:user:remove']"
></el-button>
</el-tooltip>
<el-tooltip content="重置密码" placement="top" v-if="scope.row.userId !== 1">
<el-button
type="text"
link
icon="Key"
@click="handleResetPwd(scope.row)"
v-hasPermi="['system:user:resetPwd']"
v-hasPermission="['system:user:resetPwd']"
></el-button>
</el-tooltip>
<el-tooltip content="分配角色" placement="top" v-if="scope.row.userId !== 1">
<el-button
type="text"
link
icon="CircleCheck"
@click="handleAuthRole(scope.row)"
v-hasPermi="['system:user:edit']"
v-hasPermission="['system:user:edit']"
></el-button>
</el-tooltip>
</template>
@ -219,7 +224,7 @@
<el-tree-select
v-model="form.deptId"
:data="deptOptions"
:props="{value: 'id', label: 'label', children: 'children'}"
:props="{ value: 'id', label: 'label', children: 'children' }"
value-key="id"
placeholder="请选择归属部门"
check-strictly
@ -363,13 +368,13 @@
</template>
<script setup name="User">
import {getToken} from '@/utils/auth';
import {getDeptSelectTree} from '@/api/system/dept';
import {changeUserStatus, listUser, resetUserPwd, delUser, getUser, updateUser, addUser} from '@/api/system/user';
import { getToken } from '@/utils/token';
import { getDeptSelectTree } from '@/api/system/dept';
import { changeUserStatus, listUser, resetUserPwd, delUser, getUser, updateUser, addUser } from '@/api/system/user';
const router = useRouter();
const {proxy} = getCurrentInstance();
const {sys_status, sys_user_sex} = proxy.useDict('sys_status', 'sys_user_sex');
const { proxy } = getCurrentInstance();
const { sys_status, sys_user_sex } = proxy.useDict('sys_status', 'sys_user_sex');
const userList = ref([]);
const open = ref(false);
@ -397,19 +402,19 @@ const upload = reactive({
//
updateSupport: 0,
//
headers: {Authorization: `Bearer ${getToken()}`},
headers: { Authorization: `Bearer ${getToken()}` },
//
url: `${import.meta.env.VITE_APP_BASE_API}/system/user/importData`,
});
//
const columns = ref([
{key: 0, label: '用户编号', visible: true},
{key: 1, label: '用户名称', visible: true},
{key: 2, label: '用户昵称', visible: true},
{key: 3, label: '部门', visible: true},
{key: 4, label: '手机号码', visible: true},
{key: 5, label: '状态', visible: true},
{key: 6, label: '创建时间', visible: true},
{ key: 0, label: '用户编号', visible: true },
{ key: 1, label: '用户名称', visible: true },
{ key: 2, label: '用户昵称', visible: true },
{ key: 3, label: '部门', visible: true },
{ key: 4, label: '手机号码', visible: true },
{ key: 5, label: '状态', visible: true },
{ key: 6, label: '创建时间', visible: true },
]);
const data = reactive({
@ -424,7 +429,7 @@ const data = reactive({
},
rules: {
username: [
{required: true, message: '用户名称不能为空', trigger: 'blur'},
{ required: true, message: '用户名称不能为空', trigger: 'blur' },
{
min: 2,
max: 20,
@ -432,9 +437,9 @@ const data = reactive({
trigger: 'blur',
},
],
nickName: [{required: true, message: '用户昵称不能为空', trigger: 'blur'}],
nickName: [{ required: true, message: '用户昵称不能为空', trigger: 'blur' }],
password: [
{required: true, message: '用户密码不能为空', trigger: 'blur'},
{ required: true, message: '用户密码不能为空', trigger: 'blur' },
{
min: 5,
max: 20,
@ -442,12 +447,12 @@ const data = reactive({
trigger: 'blur',
},
],
email: [{type: 'email', message: '请输入正确的邮箱地址', trigger: ['blur', 'change']}],
phoneNumber: [{pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: '请输入正确的手机号码', trigger: 'blur'}],
email: [{ type: 'email', message: '请输入正确的邮箱地址', trigger: ['blur', 'change'] }],
phoneNumber: [{ pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: '请输入正确的手机号码', trigger: 'blur' }],
},
});
const {queryParams, form, rules} = toRefs(data);
const { queryParams, form, rules } = toRefs(data);
/** 通过条件过滤节点 */
const filterNode = (value, data) => {
@ -542,7 +547,7 @@ function handleCommand(command, row) {
}
/** 跳转角色分配 */
function handleAuthRole(row) {
const {userId} = row;
const { userId } = row;
router.push(`/system/user-auth/role/${userId}`);
}
/** 重置密码按钮操作 */
@ -555,7 +560,7 @@ function handleResetPwd(row) {
inputPattern: /^.{5,20}$/,
inputErrorMessage: '用户密码长度必须介于 5 和 20 之间',
})
.then(({value}) => {
.then(({ value }) => {
resetUserPwd(row.userId, value).then((response) => {
proxy.$modal.msgSuccess(`修改成功,新密码是:${value}`);
});
@ -589,7 +594,7 @@ const handleFileSuccess = (response, file, fileList) => {
proxy.$alert(
`<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>${response.msg}</div>`,
'导入结果',
{dangerouslyUseHTMLString: true},
{ dangerouslyUseHTMLString: true },
);
getList();
};

View File

@ -1,23 +1,24 @@
import {defineConfig, loadEnv} from 'vite';
import { defineConfig, loadEnv } from 'vite';
import path from 'path';
import createVitePlugins from './vite/plugins';
// https://vitejs.dev/config/
export default defineConfig(({mode, command}) => {
export default defineConfig(({ mode, command }) => {
const env = loadEnv(mode, process.cwd());
const {VITE_APP_ENV} = env;
const { VITE_APP_ENV } = env;
return {
// 部署生产环境和开发环境下的URL。
// 默认情况下vite 会假设你的应用是被部署在一个域名的根路径上
// 例如 https://www.ruoyi.vip/。如果应用被部署在一个子路径上,你就需要用这个选项指定这个子路径。例如,如果你的应用被部署在 https://www.ruoyi.vip/admin/,则设置 baseUrl 为 /admin/。
// 例如 https://www.ruoyi.vip/。如果应用被部署在一个子路径上,你就需要用这个选项指定这个子路径。
// 例如,如果你的应用被部署在 https://www.ruoyi.vip/admin/,则设置 baseUrl 为 /admin/。
base: VITE_APP_ENV === 'production' ? '/' : '/',
plugins: createVitePlugins(env, command === 'build'),
resolve: {
// https://cn.vitejs.dev/config/#resolve-alias
alias: {
// 设置路径
// 设置路径别名
'~': path.resolve(__dirname, './'),
// 设置别名
// 设置路径别名
'@': path.resolve(__dirname, './src'),
},
// https://cn.vitejs.dev/config/#resolve-extensions
@ -31,8 +32,10 @@ export default defineConfig(({mode, command}) => {
proxy: {
// https://cn.vitejs.dev/config/#server-proxy
'/dev-api': {
// 后端地址
target: 'http://localhost:8080',
changeOrigin: true,
// 将/dev-api路径去掉
rewrite: (p) => p.replace(/^\/dev-api/, ''),
},
},

View File

@ -1,14 +1,17 @@
import autoImport from 'unplugin-auto-import/vite'
import autoImport from 'unplugin-auto-import/vite';
// 自动导入插件
// 配置完成之后使用ref reactive watch 等无须import 导入 ,可以直接使用
export default function createAutoImport() {
return autoImport({
imports: [
'vue',
'vue-router',
{
'vuex': ['useStore']
}
],
dts: false
})
return autoImport({
imports: [
'vue',
'vue-router',
{
vuex: ['useStore'],
},
],
// 可以选择auto-import.d.ts生成的位置使用ts建议设置为'src/auto-import.d.ts'
dts: false,
});
}

View File

@ -1,28 +1,29 @@
import compression from 'vite-plugin-compression'
import compression from 'vite-plugin-compression';
// 根据env.production或其他环境的配置文件中的VITE_BUILD_COMPRESS参数 来设定使用哪种压缩方式
export default function createCompression(env) {
const { VITE_BUILD_COMPRESS } = env
const plugin = []
if (VITE_BUILD_COMPRESS) {
const compressList = VITE_BUILD_COMPRESS.split(',')
if (compressList.includes('gzip')) {
// http://doc.ruoyi.vip/ruoyi-vue/other/faq.html#使用gzip解压缩静态文件
plugin.push(
compression({
ext: '.gz',
deleteOriginFile: false
})
)
}
if (compressList.includes('brotli')) {
plugin.push(
compression({
ext: '.br',
algorithm: 'brotliCompress',
deleteOriginFile: false
})
)
}
const { VITE_BUILD_COMPRESS } = env;
const plugin = [];
if (VITE_BUILD_COMPRESS) {
const compressList = VITE_BUILD_COMPRESS.split(',');
if (compressList.includes('gzip')) {
// http://doc.ruoyi.vip/ruoyi-vue/other/faq.html#使用gzip解压缩静态文件
plugin.push(
compression({
ext: '.gz',
deleteOriginFile: false,
}),
);
}
return plugin
if (compressList.includes('brotli')) {
plugin.push(
compression({
ext: '.br',
algorithm: 'brotliCompress',
deleteOriginFile: false,
}),
);
}
}
return plugin;
}

View File

@ -1,15 +1,16 @@
import vue from '@vitejs/plugin-vue'
import vue from '@vitejs/plugin-vue';
import createAutoImport from './auto-import'
import createSvgIcon from './svg-icon'
import createCompression from './compression'
import createSetupExtend from './setup-extend'
import createAutoImport from './auto-import';
import createSvgIcon from './svg-icon';
import createCompression from './compression';
import createSetupExtend from './setup-extend';
// 加载插件入口
export default function createVitePlugins(viteEnv, isBuild = false) {
const vitePlugins = [vue()]
vitePlugins.push(createAutoImport())
vitePlugins.push(createSetupExtend())
vitePlugins.push(createSvgIcon(isBuild))
isBuild && vitePlugins.push(...createCompression(viteEnv))
return vitePlugins
const vitePlugins = [vue()];
vitePlugins.push(createAutoImport());
vitePlugins.push(createSetupExtend());
vitePlugins.push(createSvgIcon(isBuild));
isBuild && vitePlugins.push(...createCompression(viteEnv));
return vitePlugins;
}

View File

@ -1,5 +1,9 @@
import setupExtend from 'vite-plugin-vue-setup-extend'
import setupExtend from 'vite-plugin-vue-setup-extend';
// 在使用 Vue3.2 的 setup 语法糖后,无法优雅的定义组件的 name 值,
// 虽然 vite 会根据组件的文件名自动生成组件名,但是需要自定义的组件名时,就很不方便。
// <script setup name="Logininfor">
// 就可以这样自定义组件名
export default function createSetupExtend() {
return setupExtend()
return setupExtend();
}

View File

@ -1,10 +1,13 @@
import svgIcon from 'vite-plugin-svg-icons'
import path from 'path'
import svgIcon from 'vite-plugin-svg-icons';
import path from 'path';
// 当你使用该插件的时候指定好存放svg的文件夹。再按照指定的方式去访问svg图片。就可以再不产生http请求的情况下渲染出svg图片。
// 使用方式如下
// <svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M121.718 73.272v9.953c3.957-7.584 6.199-16.05 6.199-24.995C127.917 26.079 99.273 0 63.958 0 28.644 0 0 26.079 0 58.23c0 .403.028.806.028 1.21l22.97-25.953h13.34l-19.76 27.187h6.42V53.77l13.728-19.477v49.361H22.998V73.272H2.158c5.951 20.284 23.608 36.208 45.998 41.399-1.44 3.3-5.618 11.263-12.565 12.674-8.607 1.764 23.358.428 46.163-13.178 17.519-4.611 31.938-15.849 39.77-30.513h-13.506V73.272H85.02V59.464l22.998-25.977h13.008l-19.429 27.187h6.421v-7.433l13.727-19.402v39.433h-.027zm-78.24 2.822a10.516 10.516 0 0 1-.996-4.535V44.548c0-1.613.332-3.124.996-4.535a11.66 11.66 0 0 1 2.713-3.68c1.134-1.032 2.49-1.864 4.04-2.468 1.55-.605 3.21-.908 4.982-.908h11.292c1.77 0 3.431.303 4.981.908 1.522.604 2.85 1.41 3.986 2.418l-12.26 16.303v-2.898a1.96 1.96 0 0 0-.665-1.512c-.443-.403-.996-.604-1.66-.604-.665 0-1.218.201-1.661.604a1.96 1.96 0 0 0-.664 1.512v9.071L44.364 77.606a10.556 10.556 0 0 1-.886-1.512zm35.73-4.535c0 1.613-.332 3.124-.997 4.535a11.66 11.66 0 0 1-2.712 3.68c-1.134 1.032-2.49 1.864-4.04 2.469-1.55.604-3.21.907-4.982.907H55.185c-1.77 0-3.431-.303-4.981-.907-1.55-.605-2.906-1.437-4.041-2.47a12.49 12.49 0 0 1-1.384-1.512l13.727-18.217v6.375c0 .605.222 1.109.665 1.512.442.403.996.604 1.66.604.664 0 1.218-.201 1.66-.604a1.96 1.96 0 0 0 .665-1.512V53.87L75.97 36.838c.913.932 1.66 1.99 2.214 3.175.664 1.41.996 2.922.996 4.535v27.011h.028z"/></svg>
export default function createSvgIcon(isBuild) {
return svgIcon({
iconDirs: [path.resolve(process.cwd(), 'src/assets/icons/svg')],
symbolId: 'icon-[dir]-[name]',
svgoOptions: isBuild
})
return svgIcon({
iconDirs: [path.resolve(process.cwd(), 'src/assets/icons/svg')],
symbolId: 'icon-[dir]-[name]',
svgoOptions: isBuild,
});
}