Merge branch 'ts' into future/flowable

This commit is contained in:
gssong 2023-07-29 11:30:10 +08:00
commit 375a21a300
99 changed files with 4557 additions and 3258 deletions

View File

@ -13,7 +13,13 @@ VITE_APP_CONTEXT_PATH = '/'
# 监控地址 # 监控地址
VITE_APP_MONITRO_ADMIN = 'http://localhost:9090/admin/applications' VITE_APP_MONITRO_ADMIN = 'http://localhost:9090/admin/applications'
# xxl-job 控制台地址 # powerjob 控制台地址
VITE_APP_XXL_JOB_ADMIN = 'http://localhost:9100/xxl-job-admin' VITE_APP_POWERJOB_ADMIN = 'http://localhost:7700/'
VITE_APP_PORT = 80 VITE_APP_PORT = 80
# 接口加密传输 RSA 公钥与后端解密私钥对应 如更换需前后端一同更换
VITE_APP_RSA_PUBLIC_KEY = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ=='
# 客户端id
VITE_APP_CLIENT_ID = 'e5cd7e4891bf95d1d19206ce24a7b32e'

View File

@ -10,8 +10,8 @@ VITE_APP_CONTEXT_PATH = '/'
# 监控地址 # 监控地址
VITE_APP_MONITRO_ADMIN = '/admin/applications' VITE_APP_MONITRO_ADMIN = '/admin/applications'
# 监控地址 # powerjob 控制台地址
VITE_APP_XXL_JOB_ADMIN = '/xxl-job-admin' VITE_APP_POWERJOB_ADMIN = '/powerjob'
# 生产环境 # 生产环境
VITE_APP_BASE_API = '/prod-api' VITE_APP_BASE_API = '/prod-api'
@ -20,3 +20,9 @@ VITE_APP_BASE_API = '/prod-api'
VITE_BUILD_COMPRESS = gzip VITE_BUILD_COMPRESS = gzip
VITE_APP_PORT = 80 VITE_APP_PORT = 80
# 接口加密传输 RSA 公钥与后端解密私钥对应 如更换需前后端一同更换
VITE_APP_RSA_PUBLIC_KEY = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ=='
# 客户端id
VITE_APP_CLIENT_ID = 'e5cd7e4891bf95d1d19206ce24a7b32e'

View File

@ -1,5 +1,36 @@
{ {
"globals": { "globals": {
"ComponentInternalInstance": true,
"TransferKey": true,
"ElFormRules": true,
"CheckboxValueType": true,
"PropType": true,
"DateModelType": true,
"UploadFile": true,
"ElFormInstance": true,
"ElTableInstance": true,
"ElTreeInstance": true,
"ElTreeSelectInstance": true,
"ElSelectInstance": true,
"ElUploadInstance": true,
"ElCardInstance": true,
"ElDialogInstance": true,
"ElInputInstance": true,
"ElInputNumberInstance": true,
"ElRadioInstance": true,
"ElRadioGroupInstance": true,
"ElRadioButtonInstance": true,
"ElCheckboxInstance": true,
"ElCheckboxGroupInstance": true,
"ElSwitchInstance": true,
"ElDatePickerInstance": true,
"ElTimePickerInstance": true,
"ElTimeSelectInstance": true,
"ElScrollbarInstance": true,
"ElCascaderInstance": true,
"ElColorPickerInstance": true,
"ElRateInstance": true,
"ElSliderInstance": true,
"useRouter": true, "useRouter": true,
"useRoute": true, "useRoute": true,
"EffectScope": true, "EffectScope": true,

View File

@ -29,7 +29,8 @@ module.exports = {
// 关闭空类型检查 {} // 关闭空类型检查 {}
extendDefaults: true, extendDefaults: true,
types: { types: {
'{}': false '{}': false,
'Function': false
} }
} }
] ]

1
.gitignore vendored
View File

@ -1,4 +1,5 @@
.DS_Store .DS_Store
.history
node_modules/ node_modules/
dist/ dist/
npm-debug.log* npm-debug.log*

View File

@ -1,6 +1,6 @@
{ {
"name": "ruoyi-vue-plus", "name": "ruoyi-vue-plus",
"version": "5.0.0", "version": "5.1.0",
"description": "RuoYi-Vue-Plus多租户管理系统", "description": "RuoYi-Vue-Plus多租户管理系统",
"author": "LionLi", "author": "LionLi",
"license": "MIT", "license": "MIT",
@ -29,19 +29,23 @@
"fuse.js": "6.6.2", "fuse.js": "6.6.2",
"js-cookie": "3.0.1", "js-cookie": "3.0.1",
"jsencrypt": "3.3.1", "jsencrypt": "3.3.1",
"crypto-js": "^4.1.1",
"nprogress": "0.2.0", "nprogress": "0.2.0",
"path-browserify": "1.0.1", "path-browserify": "1.0.1",
"path-to-regexp": "6.2.0", "path-to-regexp": "6.2.0",
"pinia": "2.0.22", "pinia": "2.0.22",
"screenfull": "6.0.0", "screenfull": "6.0.0",
"vform3-builds": "3.0.8",
"vue": "3.2.45", "vue": "3.2.45",
"vue-cropper": "1.0.3", "vue-cropper": "1.0.3",
"vue-i18n": "9.2.2", "vue-i18n": "9.2.2",
"vue-router": "4.1.4" "vue-router": "4.1.4",
"vue-types": "^5.0.3"
}, },
"devDependencies": { "devDependencies": {
"@iconify/json": "^2.2.40", "@iconify/json": "^2.2.40",
"@intlify/unplugin-vue-i18n": "0.8.2", "@intlify/unplugin-vue-i18n": "0.8.2",
"@types/crypto-js": "^4.1.1",
"@types/file-saver": "2.0.5", "@types/file-saver": "2.0.5",
"@types/js-cookie": "3.0.3", "@types/js-cookie": "3.0.3",
"@types/node": "18.14.2", "@types/node": "18.14.2",
@ -69,11 +73,11 @@
"unplugin-auto-import": "0.13.0", "unplugin-auto-import": "0.13.0",
"unplugin-icons": "0.15.1", "unplugin-icons": "0.15.1",
"unplugin-vue-components": "0.23.0", "unplugin-vue-components": "0.23.0",
"vite": "4.3.1",
"vite-plugin-compression": "0.5.1", "vite-plugin-compression": "0.5.1",
"vite-plugin-svg-icons": "2.0.1", "vite-plugin-svg-icons": "2.0.1",
"vite-plugin-vue-setup-extend": "^0.4.0", "vite-plugin-vue-setup-extend": "^0.4.0",
"vitest": "^0.29.7", "vitest": "^0.29.7",
"vite": "4.3.1",
"vue-eslint-parser": "9.1.0", "vue-eslint-parser": "9.1.0",
"vue-tsc": "0.35.0" "vue-tsc": "0.35.0"
} }

View File

@ -3,22 +3,24 @@ import { AxiosPromise } from 'axios';
import { LoginData, LoginResult, VerifyCodeResult, TenantInfo } from './types'; import { LoginData, LoginResult, VerifyCodeResult, TenantInfo } from './types';
import { UserInfo } from '@/api/system/user/types'; import { UserInfo } from '@/api/system/user/types';
// pc端固定客户端授权id
const clientId = import.meta.env.VITE_APP_CLIENT_ID;
/** /**
* @param data {LoginData} * @param data {LoginData}
* @returns * @returns
*/ */
export function login(data: LoginData): AxiosPromise<LoginResult> { export function login(data: LoginData): AxiosPromise<LoginResult> {
const params = { const params = {
tenantId: data.tenantId, ...data,
username: data.username.trim(), clientId: data.clientId || clientId,
password: data.password, grantType: data.grantType || 'password'
code: data.code,
uuid: data.uuid
}; };
return request({ return request({
url: '/auth/login', url: '/auth/login',
headers: { headers: {
isToken: false isToken: false,
isEncrypt: true
}, },
method: 'post', method: 'post',
data: params data: params
@ -52,7 +54,7 @@ export function logout() {
*/ */
export function getCodeImg(): AxiosPromise<VerifyCodeResult> { export function getCodeImg(): AxiosPromise<VerifyCodeResult> {
return request({ return request({
url: '/code', url: '/auth/code',
headers: { headers: {
isToken: false isToken: false
}, },
@ -61,6 +63,22 @@ export function getCodeImg(): AxiosPromise<VerifyCodeResult> {
}); });
} }
/**
*
*/
export function callback(data: LoginData): AxiosPromise<any> {
const LoginData = {
...data,
clientId: clientId,
grantType: 'social'
};
return request({
url: '/auth/social/callback',
method: 'post',
data: LoginData
});
}
// 获取用户详细信息 // 获取用户详细信息
export function getInfo(): AxiosPromise<UserInfo> { export function getInfo(): AxiosPromise<UserInfo> {
return request({ return request({

View File

@ -0,0 +1,80 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { ClientVO, ClientForm, ClientQuery } from '@/api/system/client/types';
/**
*
* @param query
* @returns {*}
*/
export const listClient = (query?: ClientQuery): AxiosPromise<ClientVO[]> => {
return request({
url: '/system/client/list',
method: 'get',
params: query
});
};
/**
*
* @param id
*/
export const getClient = (id: string | number): AxiosPromise<ClientVO> => {
return request({
url: '/system/client/' + id,
method: 'get'
});
};
/**
*
* @param data
*/
export const addClient = (data: ClientForm) => {
return request({
url: '/system/client',
method: 'post',
data: data
});
};
/**
*
* @param data
*/
export const updateClient = (data: ClientForm) => {
return request({
url: '/system/client',
method: 'put',
data: data
});
};
/**
*
* @param id
*/
export const delClient = (id: string | number | Array<string | number>) => {
return request({
url: '/system/client/' + id,
method: 'delete'
});
};
/**
*
* @param id ID
* @param status
*/
export function changeStatus(id: number | string, status: string) {
const data = {
id,
status
};
return request({
url: '/system/client/changeStatus',
method: 'put',
data: data
});
}

View File

@ -0,0 +1,138 @@
export interface ClientVO {
/**
* id
*/
id: string | number;
/**
* id
*/
clientId: string | number;
/**
* key
*/
clientKey: string;
/**
*
*/
clientSecret: string;
/**
*
*/
grantTypeList: string[];
/**
*
*/
deviceType: string;
/**
* token活跃超时时间
*/
activeTimeout: number;
/**
* token固定超时
*/
timeout: number;
/**
* 0 1
*/
status: string;
}
export interface ClientForm extends BaseEntity {
/**
* id
*/
id?: string | number;
/**
* id
*/
clientId?: string | number;
/**
* key
*/
clientKey?: string;
/**
*
*/
clientSecret?: string;
/**
*
*/
grantTypeList?: string[];
/**
*
*/
deviceType?: string;
/**
* token活跃超时时间
*/
activeTimeout?: number;
/**
* token固定超时
*/
timeout?: number;
/**
* 0 1
*/
status?: string;
}
export interface ClientQuery extends PageQuery {
/**
* id
*/
clientId?: string | number;
/**
* key
*/
clientKey?: string;
/**
*
*/
clientSecret?: string;
/**
*
*/
grantType?: string;
/**
*
*/
deviceType?: string;
/**
* token活跃超时时间
*/
activeTimeout?: number;
/**
* token固定超时
*/
timeout?: number;
/**
* 0 1
*/
status?: string;
}

View File

@ -0,0 +1,24 @@
import request from '@/utils/request';
// 绑定账号
export function authBinding(source: string) {
return request({
url: '/auth/binding/' + source,
method: 'get'
});
}
// 解绑账号
export function authUnlock(authId: string) {
return request({
url: '/auth/unlock/' + authId,
method: 'delete'
});
}
//获取授权列表
export function getAuthList() {
return request({
url: '/system/social/list',
method: 'get'
});
}

View File

@ -9,64 +9,64 @@ import { parseStrEmpty } from '@/utils/ruoyi';
* *
* @param query * @param query
*/ */
export function listUser(query: UserQuery): AxiosPromise<UserVO[]> { export const listUser = (query: UserQuery): AxiosPromise<UserVO[]> => {
return request({ return request({
url: '/system/user/list', url: '/system/user/list',
method: 'get', method: 'get',
params: query params: query
}); });
} };
/** /**
* *
* @param userId * @param userId
*/ */
export function getUser(userId?: string | number): AxiosPromise<UserInfoVO> { export const getUser = (userId?: string | number): AxiosPromise<UserInfoVO> => {
return request({ return request({
url: '/system/user/' + parseStrEmpty(userId), url: '/system/user/' + parseStrEmpty(userId),
method: 'get' method: 'get'
}); });
} };
/** /**
* *
*/ */
export function addUser(data: UserForm) { export const addUser = (data: UserForm) => {
return request({ return request({
url: '/system/user', url: '/system/user',
method: 'post', method: 'post',
data: data data: data
}); });
} };
/** /**
* *
*/ */
export function updateUser(data: UserForm) { export const updateUser = (data: UserForm) => {
return request({ return request({
url: '/system/user', url: '/system/user',
method: 'put', method: 'put',
data: data data: data
}); });
} };
/** /**
* *
* @param userId ID * @param userId ID
*/ */
export function delUser(userId: Array<string | number> | string | number) { export const delUser = (userId: Array<string | number> | string | number) => {
return request({ return request({
url: '/system/user/' + userId, url: '/system/user/' + userId,
method: 'delete' method: 'delete'
}); });
} };
/** /**
* *
* @param userId ID * @param userId ID
* @param password * @param password
*/ */
export function resetUserPwd(userId: string | number, password: string) { export const resetUserPwd = (userId: string | number, password: string) => {
const data = { const data = {
userId, userId,
password password
@ -76,14 +76,14 @@ export function resetUserPwd(userId: string | number, password: string) {
method: 'put', method: 'put',
data: data data: data
}); });
} };
/** /**
* *
* @param userId ID * @param userId ID
* @param status * @param status
*/ */
export function changeUserStatus(userId: number | string, status: string) { export const changeUserStatus = (userId: number | string, status: string) => {
const data = { const data = {
userId, userId,
status status
@ -93,36 +93,36 @@ export function changeUserStatus(userId: number | string, status: string) {
method: 'put', method: 'put',
data: data data: data
}); });
} };
/** /**
* *
*/ */
export function getUserProfile(): AxiosPromise<UserInfoVO> { export const getUserProfile = (): AxiosPromise<UserInfoVO> => {
return request({ return request({
url: '/system/user/profile', url: '/system/user/profile',
method: 'get' method: 'get'
}); });
} };
/** /**
* *
* @param data * @param data
*/ */
export function updateUserProfile(data: UserForm) { export const updateUserProfile = (data: UserForm) => {
return request({ return request({
url: '/system/user/profile', url: '/system/user/profile',
method: 'put', method: 'put',
data: data data: data
}); });
} };
/** /**
* *
* @param oldPassword * @param oldPassword
* @param newPassword * @param newPassword
*/ */
export function updateUserPwd(oldPassword: string, newPassword: string) { export const updateUserPwd = (oldPassword: string, newPassword: string) => {
const data = { const data = {
oldPassword, oldPassword,
newPassword newPassword
@ -132,49 +132,66 @@ export function updateUserPwd(oldPassword: string, newPassword: string) {
method: 'put', method: 'put',
params: data params: data
}); });
} };
/** /**
* *
* @param data * @param data
*/ */
export function uploadAvatar(data: FormData) { export const uploadAvatar = (data: FormData) => {
return request({ return request({
url: '/system/user/profile/avatar', url: '/system/user/profile/avatar',
method: 'post', method: 'post',
data: data data: data
}); });
} };
/** /**
* *
* @param userId ID * @param userId ID
*/ */
export function getAuthRole(userId: string | number): AxiosPromise<{ user: UserVO; roles: RoleVO[] }> { export const getAuthRole = (userId: string | number): AxiosPromise<{ user: UserVO; roles: RoleVO[] }> => {
return request({ return request({
url: '/system/user/authRole/' + userId, url: '/system/user/authRole/' + userId,
method: 'get' method: 'get'
}); });
} };
/** /**
* *
* @param data ID * @param data ID
*/ */
export function updateAuthRole(data: { userId: string; roleIds: string }) { export const updateAuthRole = (data: { userId: string; roleIds: string }) => {
return request({ return request({
url: '/system/user/authRole', url: '/system/user/authRole',
method: 'put', method: 'put',
params: data params: data
}); });
} };
/** /**
* *
*/ */
export function deptTreeSelect(): AxiosPromise<DeptVO[]> { export const deptTreeSelect = (): AxiosPromise<DeptVO[]> => {
return request({ return request({
url: '/system/user/deptTree', url: '/system/user/deptTree',
method: 'get' method: 'get'
}); });
} };
export default {
listUser,
getUser,
addUser,
updateUser,
delUser,
resetUserPwd,
changeUserStatus,
getUserProfile,
updateUserProfile,
updateUserPwd,
uploadAvatar,
getAuthRole,
updateAuthRole,
deptTreeSelect
};

View File

@ -15,19 +15,24 @@ export type RegisterForm = {
* *
*/ */
export interface LoginData { export interface LoginData {
tenantId: string; tenantId?: string;
username: string; username?: string;
password: string; password?: string;
rememberMe?: boolean; rememberMe?: boolean;
socialCode?: string;
socialState?: string;
source?: string;
code?: string; code?: string;
uuid?: string; uuid?: string;
clientId: string;
grantType: string;
} }
/** /**
* *
*/ */
export interface LoginResult { export interface LoginResult {
token: string; access_token: string;
} }
/** /**

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1686919908144" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2521" width="200" height="200" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M512 992C246.895625 992 32 777.104375 32 512S246.895625 32 512 32s480 214.895625 480 480-214.895625 480-480 480z m242.9521875-533.3278125h-272.56875a23.7121875 23.7121875 0 0 0-23.71125 23.7121875l-0.024375 59.255625c0 13.08 10.6078125 23.7121875 23.6878125 23.7121875h165.96c13.104375 0 23.7121875 10.6078125 23.7121875 23.6878125v11.855625a71.1121875 71.1121875 0 0 1-71.1121875 71.1121875h-225.215625a23.7121875 23.7121875 0 0 1-23.6878125-23.7121875V423.1278125a71.1121875 71.1121875 0 0 1 71.0878125-71.1121875h331.824375a23.7121875 23.7121875 0 0 0 23.6878125-23.71125l0.0721875-59.2565625a23.7121875 23.7121875 0 0 0-23.68875-23.7121875H423.08a177.76875 177.76875 0 0 0-177.76875 177.7921875V754.953125c0 13.1034375 10.60875 23.7121875 23.713125 23.7121875h349.63125a159.984375 159.984375 0 0 0 159.984375-159.984375V482.36a23.7121875 23.7121875 0 0 0-23.7121875-23.6878125z" fill="#515151" p-id="2522"></path></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.3 KiB

View File

@ -26,6 +26,10 @@ html {
box-sizing: border-box; box-sizing: border-box;
} }
html.dark .svg-icon, html.dark svg {
fill: var(--el-text-color-regular);
}
#app { #app {
height: 100%; height: 100%;
} }
@ -137,6 +141,7 @@ aside {
border: 1px solid var(--el-border-color-light); border: 1px solid var(--el-border-color-light);
background-color: var(--el-bg-color-overlay); background-color: var(--el-bg-color-overlay);
padding: 0.75rem; padding: 0.75rem;
transition: all ease 0.3s;
&:hover { &:hover {
box-shadow: 0 2px 12px #0000001a; box-shadow: 0 2px 12px #0000001a;

View File

@ -90,8 +90,8 @@ h6 {
.el-table__fixed-header-wrapper { .el-table__fixed-header-wrapper {
th { th {
word-break: break-word; word-break: break-word;
background-color: #f8f8f9 !important; background-color: $table-header-bg !important;
color: #515a6e; color: $table-header-text-color;
height: 40px !important; height: 40px !important;
font-size: 13px; font-size: 13px;
} }

View File

@ -84,7 +84,7 @@
.sub-menu-title-noDropdown, .sub-menu-title-noDropdown,
.el-sub-menu__title { .el-sub-menu__title {
&:hover { &:hover {
background-color: rgba(0, 0, 0, 0.06) !important; background-color: $base-sub-menu-title-hover !important;
} }
} }
@ -211,7 +211,7 @@
.el-menu-item { .el-menu-item {
&:hover { &:hover {
// you can use $sub-menuHover // you can use $sub-menuHover
background-color: rgba(0, 0, 0, 0.06) !important; background-color: $base-menu-hover !important;
} }
} }

View File

@ -1,3 +1,40 @@
// 全局SCSS变量
:root {
--menuBg: #304156;
--menuColor: #bfcbd9;
--menuActiveText: #f4f4f5;
--menuHover: #263445;
--subMenuBg: #1f2d3d;
--subMenuActiveText: #f4f4f5;
--subMenuHover: #001528;
--subMenuTitleHover: #293444;
--fixedHeaderBg: #ffffff;
--tableHeaderBg: #f8f8f9;
--tableHeaderTextColor: #515a6e;
}
html.dark {
--menuBg: #1d1e1f;
--menuColor: #bfcbd9;
--menuActiveText: #f4f4f5;
--menuHover: #171819;
--subMenuBg: #1d1e1f;
--subMenuActiveText: #1d1e1f;
--subMenuHover: #171819;
--subMenuTitleHover: #171819;
--fixedHeaderBg: #171819;
--tableHeaderBg: var(--el-bg-color);
--tableHeaderTextColor: var(--el-text-color);
// 覆盖ele 高亮当前行的标准暗色
.el-tree-node__content {
--el-color-primary-light-9: #262727;
}
}
// base color // base color
$blue: #324157; $blue: #324157;
$light-blue: #3a71a8; $light-blue: #3a71a8;
@ -9,32 +46,23 @@ $yellow: #fec171;
$panGreen: #30b08f; $panGreen: #30b08f;
// 默认菜单主题风格 // 默认菜单主题风格
$base-menu-color: #bfcbd9; $base-menu-color: var(--menuColor);
$base-menu-color-active: #f4f4f5; $base-menu-hover: var(--menuHover);
$base-menu-background: #304156; $base-menu-color-active: var(--menuActiveText);
$base-menu-background: var(--menuBg);
$base-logo-title-color: #ffffff; $base-logo-title-color: #ffffff;
$base-menu-light-color: rgba(0, 0, 0, 0.7); $base-menu-light-color: rgba(0, 0, 0, 0.7);
$base-menu-light-background: #ffffff; $base-menu-light-background: #ffffff;
$base-logo-light-title-color: #001529; $base-logo-light-title-color: #001529;
$base-sub-menu-background: #1f2d3d; $base-sub-menu-background: var(--subMenuBg);
$base-sub-menu-hover: #001528; $base-sub-menu-hover: var(--subMenuHover);
$base-sub-menu-title-hover: var(--subMenuTitleHover);
// 自定义暗色菜单风格 // 表单头背景色和标题颜色
/** $fixed-header-bg: var(--fixedHeaderBg);
$base-menu-color:hsla(0,0%,100%,.65); $table-header-bg: var(--tableHeaderBg);
$base-menu-color-active:#fff; $table-header-text-color: var(--tableHeaderTextColor);
$base-menu-background:#001529;
$base-logo-title-color: #ffffff;
$base-menu-light-color:rgba(0,0,0,.70);
$base-menu-light-background:#ffffff;
$base-logo-light-title-color: #001529;
$base-sub-menu-background:#000c17;
$base-sub-menu-hover:#001528;
*/
$--color-primary: #409eff; $--color-primary: #409eff;
$--color-success: #67c23a; $--color-success: #67c23a;

View File

@ -18,34 +18,34 @@ const router = useRouter();
const levelList = ref<RouteLocationMatched[]>([]) const levelList = ref<RouteLocationMatched[]>([])
const getBreadcrumb = () => { const getBreadcrumb = () => {
// only show routes with meta.title // only show routes with meta.title
let matched = route.matched.filter(item => item.meta && item.meta.title); let matched = route.matched.filter(item => item.meta && item.meta.title);
const first = matched[0] const first = matched[0]
// //
if (!isDashboard(first)) { if (!isDashboard(first)) {
matched = ([{ path: '/index', meta: { title: '首页' } }] as any).concat(matched) matched = ([{ path: '/index', meta: { title: '首页' } }] as any).concat(matched)
} }
levelList.value = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false) levelList.value = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false)
} }
const isDashboard = (route: RouteLocationMatched) => { const isDashboard = (route: RouteLocationMatched) => {
const name = route && route.name as string const name = route && route.name as string
if (!name) { if (!name) {
return false return false
} }
return name.trim() === 'Index' return name.trim() === 'Index'
} }
const handleLink = (item: RouteLocationMatched) => { const handleLink = (item: RouteLocationMatched) => {
const { redirect, path } = item const { redirect, path } = item
redirect ? router.push(redirect as string) : router.push(path) redirect ? router.push(redirect as string) : router.push(path)
} }
watchEffect(() => { watchEffect(() => {
// if you go to the redirect page, do not update the breadcrumbs // if you go to the redirect page, do not update the breadcrumbs
if (route.path.startsWith('/redirect/')) return if (route.path.startsWith('/redirect/')) return
getBreadcrumb() getBreadcrumb()
}) })
onMounted(() => { onMounted(() => {
getBreadcrumb(); getBreadcrumb();
}) })
</script> </script>

View File

@ -0,0 +1,64 @@
<!-- 代码构建 -->
<script setup lang="ts">
const props = defineProps({
showBtn: {
type: Boolean,
default: false
},
formJson: {
type: Object,
default: undefined
}
})
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const buildRef = ref();
const emits = defineEmits(['reJson', 'saveDesign']);
//json
const getJson = () => {
const formJson = JSON.stringify(buildRef.value.getFormJson())
const fieldJson = JSON.stringify(buildRef.value.getFieldWidgets())
let data = {
formJson, fieldJson
}
emits("saveDesign", data)
}
onMounted(() => {
if (props.formJson) {
buildRef.value.setFormJson(props.formJson)
}
})
</script>
<template>
<div>
<v-form-designer
class="build"
ref="buildRef"
:designer-config="{ importJsonButton: true, exportJsonButton: true, exportCodeButton: true, generateSFCButton: true, formTemplates: true }"
>
<template #customToolButtons v-if="showBtn">
<el-button link type="primary" icon="Select" @click="getJson">保存</el-button>
</template>
</v-form-designer>
</div>
</template>
<style lang="scss">
.build {
margin: 0 !important;
overflow-y: auto !important;
& header.main-header {
display: none;
}
& .right-toolbar-con {
text-align: right !important;
}
}
</style>

View File

@ -0,0 +1,62 @@
<!-- 动态表单渲染 -->
<script setup name="Render">
const props = defineProps({
formJson: {
type: [String, Object],
default: {}
},
formData: {
type: [String, Object],
default: {}
},
isView: {
type: Boolean,
default: false
}
})
const vFormRef = ref(null)
// -
const getFormData = () => {
return vFormRef.value.getFormData()
}
/**
* 设置表单内容
* @param {表单配置} formConf
* formConfig{ formTemplate表单模板formData表单数据hiddenField需要隐藏的字段字符串集合disabledField需要禁用的自读字符串集合}
*/
const initForm = (formConf) => {
const { formTemplate, formData, hiddenField, disabledField } = toRaw(formConf)
if (formTemplate) {
vFormRef.value.setFormJson(formTemplate)
if (formData) {
vFormRef.value.setFormData(formData)
}
if (disabledField && disabledField.length > 0) {
setTimeout(() => {
vFormRef.value.disableWidgets(disabledField)
}, 200)
}
if (hiddenField && hiddenField.length > 0) {
setTimeout(() => {
vFormRef.value.hideWidgets(hiddenField)
}, 200)
}
if (props.isView) {
console.log(props.isView)
setTimeout(() => {
vFormRef.value.disableForm()
}, 100)
}
}
}
defineExpose({ getFormData, initForm })
</script>
<template>
<div class="">
<v-form-render ref="vFormRef" :form-json="formJson" :form-data="formData" />
</div>
</template>

View File

@ -24,7 +24,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { PropType } from 'vue'; import { propTypes } from '@/utils/propTypes';
const props = defineProps({ const props = defineProps({
@ -36,10 +36,7 @@ const props = defineProps({
// //
value: [Number, String, Array] as PropType<number | string | Array<number | string>>, value: [Number, String, Array] as PropType<number | string | Array<number | string>>,
// value // value
showValue: { showValue: propTypes.bool.def(true),
type: Boolean as PropType<boolean>,
default: true,
},
}); });
const values = computed(() => { const values = computed(() => {

View File

@ -29,153 +29,140 @@
<script setup lang="ts"> <script setup lang="ts">
import { QuillEditor, Quill } from '@vueup/vue-quill'; import { QuillEditor, Quill } from '@vueup/vue-quill';
import '@vueup/vue-quill/dist/vue-quill.snow.css'; import '@vueup/vue-quill/dist/vue-quill.snow.css';
import { getToken } from "@/utils/auth"; import { propTypes } from '@/utils/propTypes';
import { ComponentInternalInstance } from "vue"; import { globalHeaders } from "@/utils/request";
const props = defineProps({ const props = defineProps({
/* 编辑器的内容 */ /* 编辑器的内容 */
modelValue: { modelValue: propTypes.string,
type: String, /* 高度 */
}, height: propTypes.number.def(400),
/* 高度 */ /* 最小高度 */
height: { minHeight: propTypes.number.def(400),
type: Number, /* 只读 */
default: null, readOnly: propTypes.bool.def(false),
}, /* 上传文件大小限制(MB) */
/* 最小高度 */ fileSize: propTypes.number.def(5),
minHeight: { /* 类型base64格式、url格式 */
type: Number, type: propTypes.string.def('url')
default: null,
},
/* 只读 */
readOnly: {
type: Boolean,
default: false,
},
/* 上传文件大小限制(MB) */
fileSize: {
type: Number,
default: 5,
},
/* 类型base64格式、url格式 */
type: {
type: String,
default: "url",
}
}); });
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const upload = reactive<UploadOption>({ const upload = reactive<UploadOption>({
headers: { Authorization: "Bearer " + getToken() }, headers: globalHeaders,
url: import.meta.env.VITE_APP_BASE_API + '/resource/oss/upload' url: import.meta.env.VITE_APP_BASE_API + '/resource/oss/upload'
}) })
const myQuillEditor = ref(); const myQuillEditor = ref();
const options = ref({ const options = ref({
theme: "snow", theme: "snow",
bounds: document.body, bounds: document.body,
debug: "warn", debug: "warn",
modules: { modules: {
// //
toolbar: { toolbar: {
container: [ container: [
["bold", "italic", "underline", "strike"], // 线 线 ["bold", "italic", "underline", "strike"], // 线 线
["blockquote", "code-block"], // ["blockquote", "code-block"], //
[{ list: "ordered" }, { list: "bullet"} ], // [{ list: "ordered" }, { list: "bullet" }], //
[{ indent: "-1" }, { indent: "+1" }], // [{ indent: "-1" }, { indent: "+1" }], //
[{ size: ["small", false, "large", "huge"] }], // [{ size: ["small", false, "large", "huge"] }], //
[{ header: [1, 2, 3, 4, 5, 6, false] }], // [{ header: [1, 2, 3, 4, 5, 6, false] }], //
[{ color: [] }, { background: [] }], // [{ color: [] }, { background: [] }], //
[{ align: [] }], // [{ align: [] }], //
["clean"], // ["clean"], //
["link", "image", "video"] // ["link", "image", "video"] //
], ],
handlers: { handlers: {
image: function (value: any) { image: function (value: any) {
if (value) { if (value) {
// element // element
(document.querySelector(".editor-img-uploader>.el-upload") as HTMLDivElement)?.click(); (document.querySelector(".editor-img-uploader>.el-upload") as HTMLDivElement)?.click();
} else { } else {
Quill.format("image", true); Quill.format("image", true);
} }
}, },
}, },
} }
}, },
placeholder: '请输入内容', placeholder: '请输入内容',
readOnly: props.readOnly, readOnly: props.readOnly,
}); });
const styles = computed(() => { const styles = computed(() => {
let style: any = {}; let style: any = {};
if (props.minHeight) { if (props.minHeight) {
style.minHeight = `${props.minHeight}px`; style.minHeight = `${props.minHeight}px`;
} }
if (props.height) { if (props.height) {
style.height = `${props.height}px`; style.height = `${props.height}px`;
} }
return style; return style;
}) })
const content = ref(""); const content = ref("");
watch(() => props.modelValue, (v) => { watch(() => props.modelValue, (v) => {
if (v !== content.value) { if (v !== content.value) {
content.value = v === undefined ? "<p></p>" : v; content.value = v === undefined ? "<p></p>" : v;
} }
}, { immediate: true }); }, { immediate: true });
// //
const handleUploadSuccess = (res: any) => { const handleUploadSuccess = (res: any) => {
// //
let quill = toRaw(myQuillEditor.value).getQuill(); let quill = toRaw(myQuillEditor.value).getQuill();
// //
if (res.code === 200) { if (res.code === 200) {
// //
let length = quill.selection.savedRange.index; let length = quill.selection.savedRange.index;
// res // res
quill.insertEmbed(length, "image", res.data.url); quill.insertEmbed(length, "image", res.data.url);
// //
quill.setSelection(length + 1); quill.setSelection(length + 1);
proxy?.$modal.closeLoading(); proxy?.$modal.closeLoading();
} else { } else {
proxy?.$modal.loading(res.msg); proxy?.$modal.loading(res.msg);
proxy?.$modal.closeLoading(); proxy?.$modal.closeLoading();
} }
} }
// //
const handleBeforeUpload = (file: any) => { const handleBeforeUpload = (file: any) => {
// //
if (props.fileSize) { if (props.fileSize) {
const isLt = file.size / 1024 / 1024 < props.fileSize; const isLt = file.size / 1024 / 1024 < props.fileSize;
if (!isLt) { if (!isLt) {
proxy?.$modal.msgError(`上传文件大小不能超过 ${props.fileSize} MB!`); proxy?.$modal.msgError(`上传文件大小不能超过 ${props.fileSize} MB!`);
return false; return false;
}
} }
proxy?.$modal.loading('正在上传文件,请稍候...'); }
return true; proxy?.$modal.loading('正在上传文件,请稍候...');
return true;
} }
// //
const handleUploadError = (err: any) => { const handleUploadError = (err: any) => {
console.error(err); console.error(err);
proxy?.$modal.msgError('上传文件失败'); proxy?.$modal.msgError('上传文件失败');
} }
</script> </script>
<style> <style>
.editor, .ql-toolbar { .editor,
.ql-toolbar {
white-space: pre-wrap !important; white-space: pre-wrap !important;
line-height: normal !important; line-height: normal !important;
} }
.quill-img { .quill-img {
display: none; display: none;
} }
.ql-snow .ql-tooltip[data-mode="link"]::before { .ql-snow .ql-tooltip[data-mode="link"]::before {
content: "请输入链接地址:"; content: "请输入链接地址:";
} }
.ql-snow .ql-tooltip.ql-editing a.ql-action::after { .ql-snow .ql-tooltip.ql-editing a.ql-action::after {
border-right: 0; border-right: 0;
content: "保存"; content: "保存";
@ -190,14 +177,17 @@ const handleUploadError = (err: any) => {
.ql-snow .ql-picker.ql-size .ql-picker-item::before { .ql-snow .ql-picker.ql-size .ql-picker-item::before {
content: "14px"; content: "14px";
} }
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="small"]::before, .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="small"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="small"]::before { .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="small"]::before {
content: "10px"; content: "10px";
} }
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="large"]::before, .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="large"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="large"]::before { .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="large"]::before {
content: "18px"; content: "18px";
} }
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="huge"]::before, .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="huge"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="huge"]::before { .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="huge"]::before {
content: "32px"; content: "32px";
@ -207,26 +197,32 @@ const handleUploadError = (err: any) => {
.ql-snow .ql-picker.ql-header .ql-picker-item::before { .ql-snow .ql-picker.ql-header .ql-picker-item::before {
content: "文本"; content: "文本";
} }
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before, .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before { .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before {
content: "标题1"; content: "标题1";
} }
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before, .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before { .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before {
content: "标题2"; content: "标题2";
} }
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before, .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before { .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before {
content: "标题3"; content: "标题3";
} }
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before, .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before { .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before {
content: "标题4"; content: "标题4";
} }
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before, .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before { .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before {
content: "标题5"; content: "标题5";
} }
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before, .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before { .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before {
content: "标题6"; content: "标题6";
@ -236,10 +232,12 @@ const handleUploadError = (err: any) => {
.ql-snow .ql-picker.ql-font .ql-picker-item::before { .ql-snow .ql-picker.ql-font .ql-picker-item::before {
content: "标准字体"; content: "标准字体";
} }
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="serif"]::before, .ql-snow .ql-picker.ql-font .ql-picker-label[data-value="serif"]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="serif"]::before { .ql-snow .ql-picker.ql-font .ql-picker-item[data-value="serif"]::before {
content: "衬线字体"; content: "衬线字体";
} }
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="monospace"]::before, .ql-snow .ql-picker.ql-font .ql-picker-label[data-value="monospace"]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="monospace"]::before { .ql-snow .ql-picker.ql-font .ql-picker-item[data-value="monospace"]::before {
content: "等宽字体"; content: "等宽字体";

View File

@ -43,33 +43,20 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { getToken } from "@/utils/auth";
import { listByIds, delOss } from "@/api/system/oss"; import { listByIds, delOss } from "@/api/system/oss";
import { ComponentInternalInstance } from "vue"; import { propTypes } from '@/utils/propTypes';
import { ElUpload, UploadFile } from "element-plus"; import { globalHeaders } from "@/utils/request";
const props = defineProps({ const props = defineProps({
modelValue: [String, Object, Array], modelValue: [String, Object, Array],
// //
limit: { limit: propTypes.number.def(5),
type: Number,
default: 5,
},
// (MB) // (MB)
fileSize: { fileSize: propTypes.number.def(5),
type: Number,
default: 5,
},
// , ['png', 'jpg', 'jpeg'] // , ['png', 'jpg', 'jpeg']
fileType: { fileType: propTypes.array.def(["doc", "xls", "ppt", "txt", "pdf"]),
type: Array,
default: () => ["doc", "xls", "ppt", "txt", "pdf"],
},
// //
isShowTip: { isShowTip: propTypes.bool.def(true),
type: Boolean,
default: true
}
}); });
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@ -79,14 +66,14 @@ const uploadList = ref<any[]>([]);
const baseUrl = import.meta.env.VITE_APP_BASE_API; const baseUrl = import.meta.env.VITE_APP_BASE_API;
const uploadFileUrl = ref(baseUrl + "/resource/oss/upload"); // const uploadFileUrl = ref(baseUrl + "/resource/oss/upload"); //
const headers = ref({ Authorization: "Bearer " + getToken() }); const headers = ref(globalHeaders);
const fileList = ref<any[]>([]); const fileList = ref<any[]>([]);
const showTip = computed( const showTip = computed(
() => props.isShowTip && (props.fileType || props.fileSize) () => props.isShowTip && (props.fileType || props.fileSize)
); );
const fileUploadRef = ref(ElUpload); const fileUploadRef = ref<ElUploadInstance>();
watch(() => props.modelValue, async val => { watch(() => props.modelValue, async val => {
if (val) { if (val) {
@ -96,7 +83,7 @@ watch(() => props.modelValue, async val => {
if (Array.isArray(val)) { if (Array.isArray(val)) {
list = val; list = val;
} else { } else {
const res = await listByIds(val as string) const res = await listByIds(val as string)
list = res.data.map((oss) => { list = res.data.map((oss) => {
const data = { name: oss.originalName, url: oss.url, ossId: oss.ossId }; const data = { name: oss.originalName, url: oss.url, ossId: oss.ossId };
return data; return data;
@ -104,7 +91,7 @@ watch(() => props.modelValue, async val => {
} }
// //
fileList.value = list.map(item => { fileList.value = list.map(item => {
item = {name: item.name, url: item.url, ossId: item.ossId}; item = { name: item.name, url: item.url, ossId: item.ossId };
item.uid = item.uid || new Date().getTime() + temp++; item.uid = item.uid || new Date().getTime() + temp++;
return item; return item;
}); });
@ -112,7 +99,7 @@ watch(() => props.modelValue, async val => {
fileList.value = []; fileList.value = [];
return []; return [];
} }
},{ deep: true, immediate: true }); }, { deep: true, immediate: true });
// //
const handleBeforeUpload = (file: any) => { const handleBeforeUpload = (file: any) => {
@ -150,7 +137,7 @@ const handleUploadError = () => {
} }
// //
const handleUploadSuccess = (res:any, file: UploadFile) => { const handleUploadSuccess = (res: any, file: UploadFile) => {
if (res.code === 200) { if (res.code === 200) {
uploadList.value.push({ name: res.data.fileName, url: res.data.url, ossId: res.data.ossId }); uploadList.value.push({ name: res.data.fileName, url: res.data.url, ossId: res.data.ossId });
uploadedSuccessfully(); uploadedSuccessfully();
@ -158,7 +145,7 @@ const handleUploadSuccess = (res:any, file: UploadFile) => {
number.value--; number.value--;
proxy?.$modal.closeLoading(); proxy?.$modal.closeLoading();
proxy?.$modal.msgError(res.msg); proxy?.$modal.msgError(res.msg);
fileUploadRef.value.handleRemove(file); fileUploadRef.value?.handleRemove(file);
uploadedSuccessfully(); uploadedSuccessfully();
} }
} }
@ -172,7 +159,7 @@ const handleDelete = (index: number) => {
} }
// //
const uploadedSuccessfully =() => { const uploadedSuccessfully = () => {
if (number.value > 0 && uploadList.value.length === number.value) { if (number.value > 0 && uploadList.value.length === number.value) {
fileList.value = fileList.value.filter(f => f.url !== undefined).concat(uploadList.value); fileList.value = fileList.value.filter(f => f.url !== undefined).concat(uploadList.value);
uploadList.value = []; uploadList.value = [];
@ -207,21 +194,24 @@ const listToString = (list: any[], separator?: string) => {
<style scoped lang="scss"> <style scoped lang="scss">
.upload-file-uploader { .upload-file-uploader {
margin-bottom: 5px; margin-bottom: 5px;
} }
.upload-file-list .el-upload-list__item { .upload-file-list .el-upload-list__item {
border: 1px solid #e4e7ed; border: 1px solid #e4e7ed;
line-height: 2; line-height: 2;
margin-bottom: 10px; margin-bottom: 10px;
position: relative; position: relative;
} }
.upload-file-list .ele-upload-list__item-content { .upload-file-list .ele-upload-list__item-content {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
color: inherit; color: inherit;
} }
.ele-upload-list__item-content-action .el-link { .ele-upload-list__item-content-action .el-link {
margin-right: 10px; margin-right: 10px;
} }
</style> </style>

View File

@ -1,6 +1,6 @@
<template> <template>
<div style="padding: 0 15px;" @click="toggleClick"> <div style="padding: 0 15px;" @click="toggleClick">
<svg :class="{'is-active':isActive}" class="hamburger" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="64" height="64"> <svg :class="{ 'is-active': isActive }" class="hamburger" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="64" height="64">
<path <path
d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z" d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z"
/> />
@ -9,16 +9,15 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { propTypes } from '@/utils/propTypes';
defineProps({ defineProps({
isActive: { isActive: propTypes.bool.def(false)
type: Boolean,
default: false
}
}) })
const emit = defineEmits(['toggleClick']) const emit = defineEmits(['toggleClick'])
const toggleClick = () => { const toggleClick = () => {
emit('toggleClick'); emit('toggleClick');
} }
</script> </script>

View File

@ -17,12 +17,12 @@
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts" name="HeaderSearch">
import Fuse from 'fuse.js' import Fuse from 'fuse.js';
import { getNormalPath } from '@/utils/ruoyi' import { getNormalPath } from '@/utils/ruoyi';
import { isHttp } from '@/utils/validate' import { isHttp } from '@/utils/validate';
import usePermissionStore from '@/store/modules/permission' import usePermissionStore from '@/store/modules/permission';
import { RouteOption } from 'vue-router' import { RouteOption } from 'vue-router';
type Router = Array<{ type Router = Array<{
path: string; path: string;
@ -34,7 +34,7 @@ const options = ref<any>([]);
const searchPool = ref<Router>([]); const searchPool = ref<Router>([]);
const show = ref(false); const show = ref(false);
const fuse = ref(); const fuse = ref();
const headerSearchSelectRef = ref(ElSelect); const headerSearchSelectRef = ref<ElSelectInstance>();
const router = useRouter(); const router = useRouter();
const routes = computed(() => usePermissionStore().routes); const routes = computed(() => usePermissionStore().routes);
@ -123,9 +123,9 @@ onMounted(() => {
searchPool.value = generateRoutes(routes.value); searchPool.value = generateRoutes(routes.value);
}) })
watchEffect(() => { // watchEffect(() => {
searchPool.value = generateRoutes(routes.value) // searchPool.value = generateRoutes(routes.value)
}) // })
watch(show, (value) => { watch(show, (value) => {
if (value) { if (value) {
@ -142,40 +142,40 @@ watch(searchPool, (list) => {
<style lang="scss" scoped> <style lang="scss" scoped>
.header-search { .header-search {
font-size: 0 !important; font-size: 0 !important;
.search-icon { .search-icon {
cursor: pointer; cursor: pointer;
font-size: 18px; font-size: 18px;
vertical-align: middle; vertical-align: middle;
}
.header-search-select {
font-size: 18px;
transition: width 0.2s;
width: 0;
overflow: hidden;
background: transparent;
border-radius: 0;
display: inline-block;
vertical-align: middle;
:deep(.el-input__inner) {
border-radius: 0;
border: 0;
padding-left: 0;
padding-right: 0;
box-shadow: none !important;
border-bottom: 1px solid #d9d9d9;
vertical-align: middle;
} }
}
&.show {
.header-search-select { .header-search-select {
width: 210px; font-size: 18px;
margin-left: 10px; transition: width 0.2s;
width: 0;
overflow: hidden;
background: transparent;
border-radius: 0;
display: inline-block;
vertical-align: middle;
:deep(.el-input__inner) {
border-radius: 0;
border: 0;
padding-left: 0;
padding-right: 0;
box-shadow: none !important;
border-bottom: 1px solid #d9d9d9;
vertical-align: middle;
}
}
&.show {
.header-search-select {
width: 210px;
margin-left: 10px;
}
} }
}
} }
</style> </style>

View File

@ -31,17 +31,11 @@
<script setup lang="ts"> <script setup lang="ts">
import icons from '@/components/IconSelect/requireIcons'; import icons from '@/components/IconSelect/requireIcons';
import { propTypes } from '@/utils/propTypes';
const props = defineProps({ const props = defineProps({
modelValue: { modelValue: propTypes.string.isRequired,
type: String, width: propTypes.string.def('400px')
require: true
},
width: {
type: String,
require: false,
default: '400px'
}
}); });
const emit = defineEmits(['update:modelValue']); const emit = defineEmits(['update:modelValue']);

View File

@ -9,47 +9,46 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { propTypes } from '@/utils/propTypes';
const props = defineProps({ const props = defineProps({
src: { src: propTypes.string.def(''),
type: String, width: {
default: "" type: [Number, String],
}, default: ""
width: { },
type: [Number, String], height: {
default: "" type: [Number, String],
}, default: ""
height: { }
type: [Number, String],
default: ""
}
}); });
const realSrc = computed(() => { const realSrc = computed(() => {
if (!props.src) { if (!props.src) {
return; return;
} }
let real_src = props.src.split(",")[0]; let real_src = props.src.split(",")[0];
return real_src; return real_src;
}); });
const realSrcList = computed(() => { const realSrcList = computed(() => {
if (!props.src) { if (!props.src) {
return; return;
} }
let real_src_list = props.src.split(","); let real_src_list = props.src.split(",");
let srcList:string[] = []; let srcList: string[] = [];
real_src_list.forEach(item => { real_src_list.forEach(item => {
return srcList.push(item); return srcList.push(item);
}); });
return srcList; return srcList;
}); });
const realWidth = computed(() => const realWidth = computed(() =>
typeof props.width == "string" ? props.width : `${props.width}px` typeof props.width == "string" ? props.width : `${props.width}px`
); );
const realHeight = computed(() => const realHeight = computed(() =>
typeof props.height == "string" ? props.height : `${props.height}px` typeof props.height == "string" ? props.height : `${props.height}px`
); );
</script> </script>
@ -58,13 +57,16 @@ const realHeight = computed(() =>
border-radius: 5px; border-radius: 5px;
background-color: #ebeef5; background-color: #ebeef5;
box-shadow: 0 0 5px 1px #ccc; box-shadow: 0 0 5px 1px #ccc;
:deep(.el-image__inner) { :deep(.el-image__inner) {
transition: all 0.3s; transition: all 0.3s;
cursor: pointer; cursor: pointer;
&:hover { &:hover {
transform: scale(1.2); transform: scale(1.2);
} }
} }
:deep(.image-slot) { :deep(.image-slot) {
display: flex; display: flex;
justify-content: center; justify-content: center;

View File

@ -17,7 +17,9 @@
:on-preview="handlePictureCardPreview" :on-preview="handlePictureCardPreview"
:class="{ hide: fileList.length >= limit }" :class="{ hide: fileList.length >= limit }"
> >
<el-icon class="avatar-uploader-icon"><plus /></el-icon> <el-icon class="avatar-uploader-icon">
<plus />
</el-icon>
</el-upload> </el-upload>
<!-- 上传提示 --> <!-- 上传提示 -->
<div class="el-upload__tip" v-if="showTip"> <div class="el-upload__tip" v-if="showTip">
@ -38,29 +40,20 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { getToken } from "@/utils/auth";
import { listByIds, delOss } from "@/api/system/oss"; import { listByIds, delOss } from "@/api/system/oss";
import { ComponentInternalInstance, PropType } from "vue"; import { ComponentInternalInstance } from "vue";
import { OssVO } from "@/api/system/oss/types"; import { OssVO } from "@/api/system/oss/types";
import { ElUpload, UploadFile } from "element-plus"; import { propTypes } from '@/utils/propTypes';
import {globalHeaders} from "@/utils/request";
const props = defineProps({ const props = defineProps({
modelValue: [String, Object, Array], modelValue: [String, Object, Array],
// //
limit: { limit: propTypes.number.def(5),
type: Number,
default: 5,
},
// (MB) // (MB)
fileSize: { fileSize: propTypes.number.def(5),
type: Number,
default: 5,
},
// , ['png', 'jpg', 'jpeg'] // , ['png', 'jpg', 'jpeg']
fileType: { fileType: propTypes.array.def(["png", "jpg", "jpeg"]),
type: Array as PropType<string[]>,
default: () => ["png", "jpg", "jpeg"],
},
// //
isShowTip: { isShowTip: {
type: Boolean, type: Boolean,
@ -77,19 +70,19 @@ const dialogVisible = ref(false);
const baseUrl = import.meta.env.VITE_APP_BASE_API; const baseUrl = import.meta.env.VITE_APP_BASE_API;
const uploadImgUrl = ref(baseUrl + "/resource/oss/upload"); // const uploadImgUrl = ref(baseUrl + "/resource/oss/upload"); //
const headers = ref({ Authorization: "Bearer " + getToken() }); const headers = ref(globalHeaders);
const fileList = ref<any[]>([]); const fileList = ref<any[]>([]);
const showTip = computed( const showTip = computed(
() => props.isShowTip && (props.fileType || props.fileSize) () => props.isShowTip && (props.fileType || props.fileSize)
); );
const imageUploadRef = ref(ElUpload); const imageUploadRef = ref<ElUploadInstance>();
watch(() => props.modelValue, async val => { watch(() => props.modelValue, async val => {
if (val) { if (val) {
// //
let list:OssVO[] = []; let list: OssVO[] = [];
if (Array.isArray(val)) { if (Array.isArray(val)) {
list = val as OssVO[]; list = val as OssVO[];
} else { } else {
@ -112,7 +105,7 @@ watch(() => props.modelValue, async val => {
fileList.value = []; fileList.value = [];
return []; return [];
} }
},{ deep: true, immediate: true }); }, { deep: true, immediate: true });
/** 上传前loading加载 */ /** 上传前loading加载 */
const handleBeforeUpload = (file: any) => { const handleBeforeUpload = (file: any) => {
@ -122,7 +115,7 @@ const handleBeforeUpload = (file: any) => {
if (file.name.lastIndexOf(".") > -1) { if (file.name.lastIndexOf(".") > -1) {
fileExtension = file.name.slice(file.name.lastIndexOf(".") + 1); fileExtension = file.name.slice(file.name.lastIndexOf(".") + 1);
} }
isImg = props.fileType.some((type) => { isImg = props.fileType.some((type: any) => {
if (file.type.indexOf(type) > -1) return true; if (file.type.indexOf(type) > -1) return true;
if (fileExtension && fileExtension.indexOf(type) > -1) return true; if (fileExtension && fileExtension.indexOf(type) > -1) return true;
return false; return false;
@ -161,7 +154,7 @@ const handleUploadSuccess = (res: any, file: UploadFile) => {
number.value--; number.value--;
proxy?.$modal.closeLoading(); proxy?.$modal.closeLoading();
proxy?.$modal.msgError(res.msg); proxy?.$modal.msgError(res.msg);
imageUploadRef.value.handleRemove(file); imageUploadRef.value?.handleRemove(file);
uploadedSuccessfully(); uploadedSuccessfully();
} }
} }
@ -207,7 +200,7 @@ const listToString = (list: any[], separator?: string) => {
let strs = ""; let strs = "";
separator = separator || ","; separator = separator || ",";
for (let i in list) { for (let i in list) {
if(undefined !== list[i].ossId && list[i].url.indexOf("blob:") !== 0) { if (undefined !== list[i].ossId && list[i].url.indexOf("blob:") !== 0) {
strs += list[i].ossId + separator; strs += list[i].ossId + separator;
} }
} }

View File

@ -22,52 +22,23 @@ export default {
<script setup lang="ts"> <script setup lang="ts">
import { scrollTo } from '@/utils/scroll-to' import { scrollTo } from '@/utils/scroll-to'
import { PropType } from "vue"; import { propTypes } from "@/utils/propTypes";
const props = defineProps({ const props = defineProps({
total: { total: propTypes.number,
required: true, page: propTypes.number.def(1),
type: Number limit: propTypes.number.def(20),
},
page: {
type: Number,
default: 1
},
limit: {
type: Number,
default: 20
},
pageSizes: { pageSizes: {
type: Array as PropType<number[]>, type: Array as PropType<number[]>,
default() { default: () => [10, 20, 30, 50]
return [10, 20, 30, 50]
}
}, },
// 5 // 5
pagerCount: { pagerCount: propTypes.number.def(document.body.clientWidth < 992 ? 5 : 7),
type: Number, layout: propTypes.string.def('total, sizes, prev, pager, next, jumper'),
default: document.body.clientWidth < 992 ? 5 : 7 background: propTypes.bool.def(true),
}, autoScroll: propTypes.bool.def(true),
layout: { hidden: propTypes.bool.def(false),
type: String, float: propTypes.string.def('right')
default: 'total, sizes, prev, pager, next, jumper'
},
background: {
type: Boolean,
default: true
},
autoScroll: {
type: Boolean,
default: true
},
hidden: {
type: Boolean,
default: false
},
float: {
type: String,
default: 'right'
}
}) })
const emit = defineEmits(['update:page', 'update:limit', 'pagination']); const emit = defineEmits(['update:page', 'update:limit', 'pagination']);
@ -106,7 +77,6 @@ function handleCurrentChange(val: number) {
<style lang="scss" scoped> <style lang="scss" scoped>
.pagination-container { .pagination-container {
background: #fff;
padding: 32px 16px; padding: 32px 16px;
.el-pagination{ .el-pagination{
float: v-bind(float); float: v-bind(float);

View File

@ -18,25 +18,15 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { TransferKey } from "element-plus"; import { propTypes } from '@/utils/propTypes';
import { PropType } from "vue";
const props = defineProps({ const props = defineProps({
showSearch: { showSearch: propTypes.bool.def(true),
type: Boolean,
default: true,
},
columns: { columns: {
type: Array as PropType<FieldOption[]>, type: Array as PropType<FieldOption[]>,
}, },
search: { search: propTypes.bool.def(true),
type: Boolean, gutter: propTypes.number.def(10),
default: true,
},
gutter: {
type: Number,
default: 10,
},
}) })
const emits = defineEmits(['update:showSearch', 'queryTable']); const emits = defineEmits(['update:showSearch', 'queryTable']);

View File

@ -5,7 +5,7 @@
</template> </template>
<script setup> <script setup>
const url = ref('https://javalionli.gitee.io/plus-doc'); const url = ref('https://plus-doc.dromara.org/');
function goto() { function goto() {
window.open(url.value) window.open(url.value)

View File

@ -5,19 +5,12 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { propTypes } from '@/utils/propTypes';
const props = defineProps({ const props = defineProps({
iconClass: { iconClass: propTypes.string.isRequired,
type: String, className: propTypes.string.def(''),
required: true color: propTypes.string.def(''),
},
className: {
type: String,
default: ''
},
color: {
type: String,
default: ''
},
}) })
const iconName = computed(() => `#icon-${props.iconClass}`); const iconName = computed(() => `#icon-${props.iconClass}`);
const svgClass = computed(() => { const svgClass = computed(() => {

View File

@ -29,94 +29,93 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ElTreeSelect } from 'element-plus'
const props = defineProps({ const props = defineProps({
/* 配置项 */ /* 配置项 */
objMap: { objMap: {
type: Object, type: Object,
default: () => { default: () => {
return { return {
value: 'id', // ID value: 'id', // ID
label: 'label', // label: 'label', //
children: 'children' // children: 'children' //
}
} }
}
}, },
/* 自动收起 */ /* 自动收起 */
accordion: { accordion: {
type: Boolean, type: Boolean,
default: () => { default: () => {
return false return false
} }
}, },
/**当前双向数据绑定的值 */ /**当前双向数据绑定的值 */
value: { value: {
type: [String, Number], type: [String, Number],
default: '' default: ''
}, },
/**当前的数据 */ /**当前的数据 */
options: { options: {
type: Array, type: Array,
default: () => [] default: () => []
}, },
/**输入框内部的文字 */ /**输入框内部的文字 */
placeholder: { placeholder: {
type: String, type: String,
default: '' default: ''
} }
}) })
const selectTree = ref(ElTreeSelect); const selectTree = ref<ElTreeSelectInstance>();
const emit = defineEmits(['update:value']); const emit = defineEmits(['update:value']);
const valueId = computed({ const valueId = computed({
get: () => props.value, get: () => props.value,
set: (val) => { set: (val) => {
emit('update:value', val) emit('update:value', val)
} }
}); });
const valueTitle = ref(''); const valueTitle = ref('');
const defaultExpandedKey = ref<any[]>([]); const defaultExpandedKey = ref<any[]>([]);
function initHandle() { const initHandle = () => {
nextTick(() => { nextTick(() => {
const selectedValue = valueId.value; const selectedValue = valueId.value;
if(selectedValue !== null && typeof (selectedValue) !== 'undefined') { if (selectedValue !== null && typeof (selectedValue) !== 'undefined') {
const node = selectTree.value.getNode(selectedValue) const node = selectTree.value?.getNode(selectedValue)
if (node) { if (node) {
valueTitle.value = node.data[props.objMap.label] valueTitle.value = node.data[props.objMap.label]
selectTree.value.setCurrentKey(selectedValue) // selectTree.value?.setCurrentKey(selectedValue) //
defaultExpandedKey.value = [selectedValue] // defaultExpandedKey.value = [selectedValue] //
}
} else {
clearHandle()
} }
} else {
clearHandle()
}
}) })
} }
function handleNodeClick(node: any) { const handleNodeClick = (node: any) => {
valueTitle.value = node[props.objMap.label] valueTitle.value = node[props.objMap.label]
valueId.value = node[props.objMap.value]; valueId.value = node[props.objMap.value];
defaultExpandedKey.value = []; defaultExpandedKey.value = [];
selectTree.value.blur() selectTree.value?.blur()
selectFilterData('') selectFilterData('')
} }
function selectFilterData(val: any) { const selectFilterData = (val: any) => {
selectTree.value.filter(val) selectTree.value?.filter(val)
} }
function filterNode(value: any, data: any) { const filterNode = (value: any, data: any) => {
if (!value) return true if (!value) return true
return data[props.objMap['label']].indexOf(value) !== -1 return data[props.objMap['label']].indexOf(value) !== -1
} }
function clearHandle() { const clearHandle = () => {
valueTitle.value = '' valueTitle.value = ''
valueId.value = '' valueId.value = ''
defaultExpandedKey.value = []; defaultExpandedKey.value = [];
clearSelected() clearSelected()
} }
function clearSelected() { const clearSelected = () => {
const allNode = document.querySelectorAll('#tree-option .el-tree-node') const allNode = document.querySelectorAll('#tree-option .el-tree-node')
allNode.forEach((element) => element.classList.remove('is-current')) allNode.forEach((element) => element.classList.remove('is-current'))
} }
@ -132,6 +131,7 @@ watch(valueId, () => {
<style lang="scss" scoped> <style lang="scss" scoped>
@import "@/assets/styles/variables.module.scss"; @import "@/assets/styles/variables.module.scss";
.el-scrollbar .el-scrollbar__view .el-select-dropdown__item { .el-scrollbar .el-scrollbar__view .el-select-dropdown__item {
padding: 0; padding: 0;
background-color: #fff; background-color: #fff;

View File

@ -5,11 +5,10 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { propTypes } from '@/utils/propTypes';
const props = defineProps({ const props = defineProps({
src: { src: propTypes.string.isRequired
type: String,
required: true
}
}) })
const height = ref(document.documentElement.clientHeight - 94.5 + "px;") const height = ref(document.documentElement.clientHeight - 94.5 + "px;")

View File

@ -20,8 +20,13 @@
<template #prefix><svg-icon icon-class="company" class="el-input__icon input-icon" /></template> <template #prefix><svg-icon icon-class="company" class="el-input__icon input-icon" /></template>
</el-select> </el-select>
<header-search id="header-search" class="right-menu-item" /> <!-- <header-search id="header-search" class="right-menu-item" /> -->
<search-menu ref="searchMenuRef" />
<el-tooltip content="搜索" effect="dark" placement="bottom">
<div class="right-menu-item hover-effect" @click="openSearchMenu">
<svg-icon class-name="search-icon" icon-class="search" />
</div>
</el-tooltip>
<el-tooltip content="Github" effect="dark" placement="bottom"> <el-tooltip content="Github" effect="dark" placement="bottom">
<ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" /> <ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" />
</el-tooltip> </el-tooltip>
@ -68,17 +73,18 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import useAppStore from '@/store/modules/app' import SearchMenu from './topBar/search.vue';
import useUserStore from '@/store/modules/user' import useAppStore from '@/store/modules/app';
import useSettingsStore from '@/store/modules/settings' import useUserStore from '@/store/modules/user';
import useSettingsStore from '@/store/modules/settings';
import { getTenantList } from "@/api/login"; import { getTenantList } from "@/api/login";
import { dynamicClear, dynamicTenant } from "@/api/system/tenant"; import { dynamicClear, dynamicTenant } from "@/api/system/tenant";
import { ComponentInternalInstance } from "vue"; import { ComponentInternalInstance } from "vue";
import { TenantVO } from "@/api/types"; import { TenantVO } from "@/api/types";
const appStore = useAppStore() const appStore = useAppStore();
const userStore = useUserStore() const userStore = useUserStore();
const settingsStore = useSettingsStore() const settingsStore = useSettingsStore();
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@ -89,46 +95,52 @@ const tenantList = ref<TenantVO[]>([]);
const dynamic = ref(false); const dynamic = ref(false);
// //
const tenantEnabled = ref(true); const tenantEnabled = ref(true);
//
const searchMenuRef = ref<InstanceType<typeof SearchMenu>>();
const openSearchMenu = () => {
searchMenuRef.value?.openSearch();
}
// //
const dynamicTenantEvent = async (tenantId: string) => { const dynamicTenantEvent = async (tenantId: string) => {
if (companyName.value != null && companyName.value !== '') { if (companyName.value != null && companyName.value !== '') {
await dynamicTenant(tenantId); await dynamicTenant(tenantId);
dynamic.value = true; dynamic.value = true;
proxy?.$tab.closeAllPage(); proxy?.$tab.closeAllPage();
proxy?.$router.push('/'); proxy?.$router.push('/');
} }
} }
const dynamicClearEvent = async () => { const dynamicClearEvent = async () => {
await dynamicClear(); await dynamicClear();
dynamic.value = false; dynamic.value = false;
proxy?.$tab.closeAllPage(); proxy?.$tab.closeAllPage();
proxy?.$router.push('/') proxy?.$router.push('/');
} }
/** 租户列表 */ /** 租户列表 */
const initTenantList = async () => { const initTenantList = async () => {
const { data } = await getTenantList(); const { data } = await getTenantList();
tenantEnabled.value = data.tenantEnabled === undefined ? true : data.tenantEnabled; tenantEnabled.value = data.tenantEnabled === undefined ? true : data.tenantEnabled;
if (tenantEnabled.value) { if (tenantEnabled.value) {
tenantList.value = data.voList; tenantList.value = data.voList;
} }
} }
defineExpose({ defineExpose({
initTenantList, initTenantList,
}) })
const toggleSideBar = () => { const toggleSideBar = () => {
appStore.toggleSideBar(false) appStore.toggleSideBar(false);
} }
const logout = async () => { const logout = async () => {
await ElMessageBox.confirm('确定注销并退出系统吗?', '提示', { await ElMessageBox.confirm('确定注销并退出系统吗?', '提示', {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning' type: 'warning'
}) })
await userStore.logout() await userStore.logout()
location.href = import.meta.env.VITE_APP_CONTEXT_PATH + 'index'; location.href = import.meta.env.VITE_APP_CONTEXT_PATH + 'index';
@ -169,7 +181,7 @@ const handleCommand = (command: string) => {
height: 50px; height: 50px;
overflow: hidden; overflow: hidden;
position: relative; position: relative;
background: #fff; //background: #fff;
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08); box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
.hamburger-container { .hamburger-container {

View File

@ -1,8 +1,7 @@
<template> <template>
<el-drawer v-model="showSettings" :withHeader="false" direction="rtl" size="300px" close-on-click-modal> <el-drawer v-model="showSettings" :withHeader="false" direction="rtl" size="300px" close-on-click-modal>
<div class="setting-drawer-title"> <h3 class="drawer-title">主题风格设置</h3>
<h3 class="drawer-title">主题风格设置</h3>
</div>
<div class="setting-drawer-block-checbox"> <div class="setting-drawer-block-checbox">
<div class="setting-drawer-block-checbox-item" @click="handleTheme('theme-dark')"> <div class="setting-drawer-block-checbox-item" @click="handleTheme('theme-dark')">
<img src="@/assets/images/dark.svg" alt="dark" /> <img src="@/assets/images/dark.svg" alt="dark" />
@ -35,6 +34,13 @@
<el-color-picker v-model="theme" :predefine="predefineColors" @change="themeChange" /> <el-color-picker v-model="theme" :predefine="predefineColors" @change="themeChange" />
</span> </span>
</div> </div>
<div class="drawer-item">
<span>深色模式</span>
<span class="comp-style">
<el-switch v-model="isDark" @change="toggleDark" class="drawer-switch" />
</span>
</div>
<el-divider /> <el-divider />
<h3 class="drawer-title">系统布局配置</h3> <h3 class="drawer-title">系统布局配置</h3>
@ -102,7 +108,15 @@ const sideTheme = ref(settingsStore.sideTheme);
const storeSettings = computed(() => settingsStore); const storeSettings = computed(() => settingsStore);
const predefineColors = ref(["#409EFF", "#ff4500", "#ff8c00", "#ffd700", "#90ee90", "#00ced1", "#1e90ff", "#c71585"]); const predefineColors = ref(["#409EFF", "#ff4500", "#ff8c00", "#ffd700", "#90ee90", "#00ced1", "#1e90ff", "#c71585"]);
/** 是否需要topnav */ //
const isDark = useDark({
storageKey: 'useDarkKey',
valueDark: 'dark',
valueLight: 'light',
});
const toggleDark = () => useToggle(isDark);
/** 是否需要topNav */
const topNav = computed({ const topNav = computed({
get: () => storeSettings.value.topNav, get: () => storeSettings.value.topNav,
set: (val) => { set: (val) => {
@ -234,7 +248,6 @@ defineExpose({
} }
.drawer-item { .drawer-item {
color: rgba(0, 0, 0, 0.65);
padding: 12px 0; padding: 12px 0;
font-size: 14px; font-size: 14px;

View File

@ -0,0 +1,89 @@
<template>
<div v-loading="loading" class="social-callback"></div>
</template>
<script setup lang="ts">
import { login, callback } from '@/api/login';
import { setToken } from '@/utils/auth';
import Cookies from 'js-cookie';
import { getToken } from '@/utils/auth';
import { LoginData } from '@/api/types';
const route = useRoute();
const loading = ref(true);
/**
* 接收Route传递的参数
* @param {Object} route.query.
*/
const code = route.query.code as string;
const state = route.query.state as string;
const source = route.query.source as string;
const tenantId = Cookies.get("tenantId") ? Cookies.get("tenantId") as string : '000000';
const processResponse = async (res: any) => {
if (res.code !== 200) {
throw new Error(res.msg);
}
if (res.data !== null) {
setToken(res.data.access_token);
}
ElMessage.success(res.msg);
setTimeout(() => {
location.href = import.meta.env.VITE_APP_CONTEXT_PATH + 'index';
}, 2000);
};
const handleError = (error: any) => {
ElMessage.error(error.message);
setTimeout(() => {
location.href = import.meta.env.VITE_APP_CONTEXT_PATH + 'index';
}, 2000);
};
const callbackByCode = async (data: LoginData) => {
try {
const res = await callback(data);
await processResponse(res);
loading.value = false;
} catch (error) {
handleError(error);
}
};
const loginByCode = async (data: LoginData) => {
console.log(2)
try {
const res = await login(data);
await processResponse(res);
loading.value = false;
} catch (error) {
handleError(error);
}
};
const init = async () => {
const data: LoginData = {
socialCode: code,
socialState: state,
tenantId: tenantId,
source: source,
clientId: 'e5cd7e4891bf95d1d19206ce24a7b32e',
grantType: 'social'
};
if (!getToken()) {
await loginByCode(data);
} else {
await callbackByCode(data);
}
};
onMounted(() => {
nextTick(() => {
init();
});
});
</script>

View File

@ -6,22 +6,21 @@
<script setup lang="ts"> <script setup lang="ts">
import useTagsViewStore from '@/store/modules/tagsView' import useTagsViewStore from '@/store/modules/tagsView'
import { ElScrollbar } from 'element-plus';
import { TagView } from 'vue-router' import { TagView } from 'vue-router'
const tagAndTagSpacing = ref(4); const tagAndTagSpacing = ref(4);
const scrollContainerRef = ref(ElScrollbar) const scrollContainerRef = ref<ElScrollbarInstance>()
const scrollWrapper = computed(() => scrollContainerRef.value.$refs.wrapRef); const scrollWrapper = computed(() => scrollContainerRef.value?.$refs.wrapRef as any);
onMounted(() => { onMounted(() => {
scrollWrapper.value.addEventListener('scroll', emitScroll, true) scrollWrapper.value?.addEventListener('scroll', emitScroll, true)
}) })
onBeforeUnmount(() => { onBeforeUnmount(() => {
scrollWrapper.value.removeEventListener('scroll', emitScroll) scrollWrapper.value?.removeEventListener('scroll', emitScroll)
}) })
const handleScroll = (e: WheelEvent) => { const handleScroll = (e: WheelEvent) => {
const eventDelta = (e as any).wheelDelta || -e.deltaY * 40 const eventDelta = (e as any).wheelDelta || - e.deltaY * 40
const $scrollWrapper = scrollWrapper.value; const $scrollWrapper = scrollWrapper.value;
$scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4 $scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4
} }
@ -34,7 +33,7 @@ const tagsViewStore = useTagsViewStore()
const visitedViews = computed(() => tagsViewStore.visitedViews); const visitedViews = computed(() => tagsViewStore.visitedViews);
const moveToTarget = (currentTag: TagView) => { const moveToTarget = (currentTag: TagView) => {
const $container = scrollContainerRef.value.$el const $container = scrollContainerRef.value?.$el
const $containerWidth = $container.offsetWidth const $containerWidth = $container.offsetWidth
const $scrollWrapper = scrollWrapper.value; const $scrollWrapper = scrollWrapper.value;
@ -96,7 +95,7 @@ defineExpose({
bottom: 0px; bottom: 0px;
} }
:deep(.el-scrollbar__wrap) { :deep(.el-scrollbar__wrap) {
height: 39px; height: 49px;
} }
} }
</style> </style>

View File

@ -125,6 +125,9 @@ const initTags = () => {
} }
const addTags = () => { const addTags = () => {
const { name } = route; const { name } = route;
if(route.query.title) {
route.meta.title = route.query.title;
}
if (name) { if (name) {
useTagsViewStore().addView(route); useTagsViewStore().addView(route);
if (route.meta.link) { if (route.meta.link) {
@ -237,8 +240,8 @@ onMounted(() => {
.tags-view-container { .tags-view-container {
height: 34px; height: 34px;
width: 100%; width: 100%;
background: #fff; background-color: var(--el-bg-color);
border-bottom: 1px solid #d8dce5; border: 1px solid var(--el-border-color-light);
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 0 3px 0 rgba(0, 0, 0, 0.04); box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 0 3px 0 rgba(0, 0, 0, 0.04);
.tags-view-wrapper { .tags-view-wrapper {
.tags-view-item { .tags-view-item {
@ -247,13 +250,16 @@ onMounted(() => {
cursor: pointer; cursor: pointer;
height: 26px; height: 26px;
line-height: 23px; line-height: 23px;
border: 1px solid #d8dce5; background-color: var(--el-bg-color);
border: 1px solid var(--el-border-color-light);
color: #495060; color: #495060;
background: #fff;
padding: 0 8px; padding: 0 8px;
font-size: 12px; font-size: 12px;
margin-left: 5px; margin-left: 5px;
margin-top: 4px; margin-top: 4px;
&:hover {
color: var(--el-color-primary);
}
&:first-of-type { &:first-of-type {
margin-left: 15px; margin-left: 15px;
} }
@ -279,7 +285,7 @@ onMounted(() => {
} }
.contextmenu { .contextmenu {
margin: 0; margin: 0;
background: #fff; background: var(--el-bg-color);
z-index: 3000; z-index: 3000;
position: absolute; position: absolute;
list-style-type: none; list-style-type: none;
@ -287,7 +293,6 @@ onMounted(() => {
border-radius: 4px; border-radius: 4px;
font-size: 12px; font-size: 12px;
font-weight: 400; font-weight: 400;
color: #333;
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.3); box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.3);
li { li {
margin: 0; margin: 0;

View File

@ -0,0 +1,158 @@
<template>
<div class="layout-search-dialog">
<el-dialog v-model="state.isShowSearch" destroy-on-close :show-close="false">
<template #footer>
<el-autocomplete
v-model="state.menuQuery"
:fetch-suggestions="menuSearch"
placeholder="搜索"
ref="layoutMenuAutocompleteRef"
@select="onHandleSelect"
:fit-input-width="true"
>
<template #prefix>
<svg-icon class-name="search-icon" icon-class="search" />
</template>
<template #default="{ item }">
<div>
<svg-icon :icon-class="item.icon" class="mr5" />
{{ item.title }}
</div>
</template>
</el-autocomplete>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts" name="layoutBreadcrumbSearch">
import { getNormalPath } from '@/utils/ruoyi';
import { isHttp } from '@/utils/validate';
import usePermissionStore from '@/store/modules/permission';
import { RouteOption } from 'vue-router';
type Router = Array<{
path: string;
icon: string;
title: string[];
}>
type SearchState<T = any> = {
isShowSearch: boolean;
menuQuery: string;
menuList: T[];
};
//
const layoutMenuAutocompleteRef = ref();
const router = useRouter();
const routes = computed(() => usePermissionStore().routes);
const state = reactive<SearchState>({
isShowSearch: false,
menuQuery: '',
menuList: [],
});
//
const openSearch = () => {
state.menuQuery = '';
state.isShowSearch = true;
state.menuList = generateRoutes(routes.value);
nextTick(() => {
setTimeout(() => {
layoutMenuAutocompleteRef.value.focus();
});
});
};
//
const closeSearch = () => {
state.isShowSearch = false;
};
//
const menuSearch = (queryString: string, cb: Function) => {
let options = state.menuList.filter((item) => {
return item.title.indexOf(queryString) > -1;
});
cb(options);
};
// Filter out the routes that can be displayed in the sidebar
// And generate the internationalized title
const generateRoutes = (routes: RouteOption[], basePath = '', prefixTitle: string[] = []) => {
let res: Router = []
routes.forEach(r => {
// skip hidden router
if (!r.hidden) {
const p = r.path.length > 0 && r.path[0] === '/' ? r.path : '/' + r.path;
const data: any = {
path: !isHttp(r.path) ? getNormalPath(basePath + p) : r.path,
icon: r.meta?.icon,
title: [...prefixTitle]
}
if (r.meta && r.meta.title) {
data.title = [...data.title, r.meta.title];
if (r.redirect !== 'noRedirect') {
// only push the routes with title
// special case: need to exclude parent router without redirect
res.push(data);
}
}
// recursive child routes
if (r.children) {
const tempRoutes = generateRoutes(r.children, data.path, data.title);
if (tempRoutes.length >= 1) {
res = [...res, ...tempRoutes];
}
}
}
})
res.forEach((item: any) => {
if (item.title instanceof Array) {
item.title = item.title.join('/');
}
});
return res;
}
//
const onHandleSelect = (val: any) => {
const paths = val.path;
if (isHttp(paths)) {
// http(s)://
const pindex = paths.indexOf("http");
window.open(paths.substring(pindex, paths.length), "_blank");
} else {
router.push(paths);
}
state.menuQuery = ''
closeSearch();
};
//
defineExpose({
openSearch
});
</script>
<style scoped lang="scss">
.layout-search-dialog {
position: relative;
:deep(.el-dialog) {
.el-dialog__header,
.el-dialog__body {
display: none;
}
.el-dialog__footer {
width: 100%;
position: absolute;
left: 50%;
transform: translateX(-50%);
top: -53vh;
}
}
:deep(.el-autocomplete) {
width: 560px;
position: absolute;
top: 150px;
left: 50%;
transform: translateX(-50%);
}
}
</style>

View File

@ -107,6 +107,7 @@ const setLayout = () => {
z-index: 9; z-index: 9;
width: calc(100% - #{$base-sidebar-width}); width: calc(100% - #{$base-sidebar-width});
transition: width 0.28s; transition: width 0.28s;
background: $fixed-header-bg;
} }
.hideSidebar .fixed-header { .hideSidebar .fixed-header {

View File

@ -10,7 +10,7 @@ import useSettingsStore from '@/store/modules/settings';
import usePermissionStore from '@/store/modules/permission'; import usePermissionStore from '@/store/modules/permission';
NProgress.configure({ showSpinner: false }); NProgress.configure({ showSpinner: false });
const whiteList = ['/login', '/register']; const whiteList = ['/login', '/register', '/social-callback'];
router.beforeEach(async (to, from, next) => { router.beforeEach(async (to, from, next) => {
NProgress.start(); NProgress.start();

View File

@ -1,9 +1,9 @@
import axios from 'axios'; import axios from 'axios';
import FileSaver from 'file-saver'; import FileSaver from 'file-saver';
import { getToken } from '@/utils/auth';
import errorCode from '@/utils/errorCode'; import errorCode from '@/utils/errorCode';
import { blobValidate } from '@/utils/ruoyi'; import { blobValidate } from '@/utils/ruoyi';
import { LoadingInstance } from 'element-plus/es/components/loading/src/loading'; import { LoadingInstance } from 'element-plus/es/components/loading/src/loading';
import { globalHeaders } from "@/utils/request";
const baseURL = import.meta.env.VITE_APP_BASE_API; const baseURL = import.meta.env.VITE_APP_BASE_API;
let downloadLoadingInstance: LoadingInstance; let downloadLoadingInstance: LoadingInstance;
@ -16,7 +16,7 @@ export default {
method: 'get', method: 'get',
url: url, url: url,
responseType: 'blob', responseType: 'blob',
headers: { Authorization: 'Bearer ' + getToken() } headers: globalHeaders,
}); });
const isBlob = blobValidate(res.data); const isBlob = blobValidate(res.data);
if (isBlob) { if (isBlob) {
@ -34,21 +34,26 @@ export default {
}, },
async zip(url: string, name: string) { async zip(url: string, name: string) {
url = baseURL + url; url = baseURL + url;
const res = await axios({ downloadLoadingInstance = ElLoading.service({ text: '正在下载数据,请稍候', background: 'rgba(0, 0, 0, 0.7)' });
method: 'get', try {
url: url, const res = await axios({
responseType: 'blob', method: 'get',
headers: { url: url,
Authorization: 'Bearer ' + getToken(), responseType: 'blob',
datasource: localStorage.getItem('dataName') headers: globalHeaders,
});
const isBlob = blobValidate(res.data);
if (isBlob) {
const blob = new Blob([res.data], { type: 'application/zip' });
FileSaver.saveAs(blob, name);
} else {
this.printErrMsg(res.data);
} }
}); downloadLoadingInstance.close();
const isBlob = blobValidate(res.data); } catch (r) {
if (isBlob) { console.error(r);
const blob = new Blob([res.data], { type: 'application/zip' }); ElMessage.error('下载文件出现错误,请联系管理员!');
FileSaver.saveAs(blob, name); downloadLoadingInstance.close();
} else {
this.printErrMsg(res.data);
} }
}, },
async printErrMsg(data: any) { async printErrMsg(data: any) {

View File

@ -1,57 +1,57 @@
import { ElMessage, ElMessageBox, ElNotification, ElLoading, MessageBoxData } from 'element-plus'; import { MessageBoxData } from 'element-plus';
import { LoadingInstance } from 'element-plus/es/components/loading/src/loading'; import { LoadingInstance } from 'element-plus/es/components/loading/src/loading';
let loadingInstance: LoadingInstance; let loadingInstance: LoadingInstance;
export default { export default {
// 消息提示 // 消息提示
msg(content: string) { msg(content: any) {
ElMessage.info(content); ElMessage.info(content);
}, },
// 错误消息 // 错误消息
msgError(content: string) { msgError(content: any) {
ElMessage.error(content); ElMessage.error(content);
}, },
// 成功消息 // 成功消息
msgSuccess(content: string) { msgSuccess(content: any) {
ElMessage.success(content); ElMessage.success(content);
}, },
// 警告消息 // 警告消息
msgWarning(content: string) { msgWarning(content: any) {
ElMessage.warning(content); ElMessage.warning(content);
}, },
// 弹出提示 // 弹出提示
alert(content: string) { alert(content: any) {
ElMessageBox.alert(content, '系统提示'); ElMessageBox.alert(content, '系统提示');
}, },
// 错误提示 // 错误提示
alertError(content: string) { alertError(content: any) {
ElMessageBox.alert(content, '系统提示', { type: 'error' }); ElMessageBox.alert(content, '系统提示', { type: 'error' });
}, },
// 成功提示 // 成功提示
alertSuccess(content: string, s: string, p: { dangerouslyUseHTMLString: boolean }) { alertSuccess(content: any) {
ElMessageBox.alert(content, '系统提示', { type: 'success' }); ElMessageBox.alert(content, '系统提示', { type: 'success' });
}, },
// 警告提示 // 警告提示
alertWarning(content: string) { alertWarning(content: any) {
ElMessageBox.alert(content, '系统提示', { type: 'warning' }); ElMessageBox.alert(content, '系统提示', { type: 'warning' });
}, },
// 通知提示 // 通知提示
notify(content: string) { notify(content: any) {
ElNotification.info(content); ElNotification.info(content);
}, },
// 错误通知 // 错误通知
notifyError(content: string) { notifyError(content: any) {
ElNotification.error(content); ElNotification.error(content);
}, },
// 成功通知 // 成功通知
notifySuccess(content: string) { notifySuccess(content: any) {
ElNotification.success(content); ElNotification.success(content);
}, },
// 警告通知 // 警告通知
notifyWarning(content: string) { notifyWarning(content: any) {
ElNotification.warning(content); ElNotification.warning(content);
}, },
// 确认窗体 // 确认窗体
confirm(content: string): Promise<MessageBoxData> { confirm(content: any): Promise<MessageBoxData> {
return ElMessageBox.confirm(content, '系统提示', { return ElMessageBox.confirm(content, '系统提示', {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
@ -59,7 +59,7 @@ export default {
}); });
}, },
// 提交内容 // 提交内容
prompt(content: string) { prompt(content: any) {
return ElMessageBox.prompt(content, '系统提示', { return ElMessageBox.prompt(content, '系统提示', {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',

View File

@ -3,8 +3,11 @@ import router from '@/router';
import { TagView, RouteLocationRaw } from 'vue-router'; import { TagView, RouteLocationRaw } from 'vue-router';
export default { export default {
// 刷新当前tab页签 /**
async refreshPage(obj: TagView): Promise<void> { * tab页签
* @param obj
*/
async refreshPage(obj?: TagView): Promise<void> {
const { path, query, matched } = router.currentRoute.value; const { path, query, matched } = router.currentRoute.value;
if (obj === undefined) { if (obj === undefined) {
matched.forEach((m) => { matched.forEach((m) => {
@ -15,11 +18,16 @@ export default {
} }
}); });
} }
// prettier-ignore let query1: undefined | {} = {};
await useTagsViewStore().delCachedView(obj) let path1: undefined | string = '';
router.replace({ if (obj) {
path: '/redirect' + obj.path, query1 = obj.query;
query: obj.query path1 = obj.path;
}
await useTagsViewStore().delCachedView(obj);
await router.replace({
path: '/redirect' + path1,
query: query1
}); });
}, },
// 关闭当前tab页签打开新页签 // 关闭当前tab页签打开新页签
@ -34,9 +42,9 @@ export default {
if (obj === undefined) { if (obj === undefined) {
// prettier-ignore // prettier-ignore
const { visitedViews } = await useTagsViewStore().delView(router.currentRoute.value) as any const { visitedViews } = await useTagsViewStore().delView(router.currentRoute.value) as any
const latestView = visitedViews.slice(-1)[0] const latestView = visitedViews.slice(-1)[0];
if (latestView) { if (latestView) {
return router.push(latestView.fullPath) return router.push(latestView.fullPath);
} }
return router.push('/'); return router.push('/');
} }
@ -47,22 +55,31 @@ export default {
return useTagsViewStore().delAllViews(); return useTagsViewStore().delAllViews();
}, },
// 关闭左侧tab页签 // 关闭左侧tab页签
closeLeftPage(obj: TagView) { closeLeftPage(obj?: TagView) {
return useTagsViewStore().delLeftTags(obj || router.currentRoute.value); return useTagsViewStore().delLeftTags(obj || router.currentRoute.value);
}, },
// 关闭右侧tab页签 // 关闭右侧tab页签
closeRightPage(obj: TagView) { closeRightPage(obj?: TagView) {
return useTagsViewStore().delRightTags(obj || router.currentRoute.value); return useTagsViewStore().delRightTags(obj || router.currentRoute.value);
}, },
// 关闭其他tab页签 // 关闭其他tab页签
closeOtherPage(obj: TagView) { closeOtherPage(obj?: TagView) {
return useTagsViewStore().delOthersViews(obj || router.currentRoute.value); return useTagsViewStore().delOthersViews(obj || router.currentRoute.value);
}, },
// 打开tab页签 /**
openPage(url: RouteLocationRaw) { * tab页签
return router.push(url); * @param url
* @param title
* @param query
*/
openPage(url: string, title?: string, query?: any) {
const obj = { path: url, query: { ...query, title } };
return router.push(obj);
}, },
// 修改tab页签 /**
* tab页签
* @param obj
*/
updatePage(obj: TagView) { updatePage(obj: TagView) {
return useTagsViewStore().updateVisitedView(obj); return useTagsViewStore().updateVisitedView(obj);
} }

View File

@ -37,6 +37,11 @@ export const constantRoutes: RouteOption[] = [
} }
] ]
}, },
{
path: '/social-callback',
hidden: true,
component: () => import('@/layout/components/SocialCallback/index.vue')
},
{ {
path: '/login', path: '/login',
component: () => import('@/views/login.vue'), component: () => import('@/views/login.vue'),
@ -176,4 +181,5 @@ const router = createRouter({
} }
}); });
export default router; export default router;

View File

@ -54,8 +54,11 @@ export const useTagsViewStore = defineStore('tagsView', () => {
resolve([...visitedViews.value]); resolve([...visitedViews.value]);
}); });
}; };
const delCachedView = (view: TagView): Promise<string[]> => { const delCachedView = (view?: TagView): Promise<string[]> => {
const viewName = view.name as string; let viewName = '';
if (view) {
viewName = view.name as string;
}
return new Promise((resolve) => { return new Promise((resolve) => {
const index = cachedViews.value.indexOf(viewName); const index = cachedViews.value.indexOf(viewName);
index > -1 && cachedViews.value.splice(index, 1); index > -1 && cachedViews.value.splice(index, 1);
@ -167,6 +170,7 @@ export const useTagsViewStore = defineStore('tagsView', () => {
const addCachedView = (view: TagView): void => { const addCachedView = (view: TagView): void => {
const viewName = view.name as string; const viewName = view.name as string;
if (!viewName) return;
if (cachedViews.value.includes(viewName)) return; if (cachedViews.value.includes(viewName)) return;
if (!view.meta?.noCache) { if (!view.meta?.noCache) {
cachedViews.value.push(viewName); cachedViews.value.push(viewName);

View File

@ -23,8 +23,8 @@ export const useUserStore = defineStore('user', () => {
const [err, res] = await to(loginApi(userInfo)); const [err, res] = await to(loginApi(userInfo));
if (res) { if (res) {
const data = res.data; const data = res.data;
setToken(data.token); setToken(data.access_token);
token.value = data.token; token.value = data.access_token;
return Promise.resolve(); return Promise.resolve();
} }
return Promise.reject(err); return Promise.reject(err);

View File

@ -1 +1,35 @@
declare type ElTagType = '' | 'success' | 'warning' | 'info' | 'danger' | 'default' | 'primary'; import type * as ep from 'element-plus';
declare global {
declare type ElTagType = '' | 'success' | 'warning' | 'info' | 'danger' | 'default' | 'primary';
declare type ElFormInstance = InstanceType<typeof ep.ElForm>;
declare type ElTableInstance = InstanceType<typeof ep.ElTable>;
declare type ElTreeInstance = InstanceType<typeof ep.ElTree>;
declare type ElTreeSelectInstance = InstanceType<typeof ep.ElTreeSelect>;
declare type ElSelectInstance = InstanceType<typeof ep.ElSelect>;
declare type ElUploadInstance = InstanceType<typeof ep.ElUpload>;
declare type ElCardInstance = InstanceType<typeof ep.ElCard>;
declare type ElDialogInstance = InstanceType<typeof ep.ElDialog>;
declare type ElInputInstance = InstanceType<typeof ep.ElInput>;
declare type ElInputNumberInstance = InstanceType<typeof ep.ElInputNumber>;
declare type ElRadioInstance = InstanceType<typeof ep.ElRadio>;
declare type ElRadioGroupInstance = InstanceType<typeof ep.ElRadioGroup>;
declare type ElRadioButtonInstance = InstanceType<typeof ep.ElRadioButton>;
declare type ElCheckboxInstance = InstanceType<typeof ep.ElCheckbox>;
declare type ElCheckboxGroupInstance = InstanceType<typeof ep.ElCheckboxGroup>;
declare type ElSwitchInstance = InstanceType<typeof ep.ElSwitch>;
declare type ElDatePickerInstance = InstanceType<typeof ep.ElDatePicker>;
declare type ElTimePickerInstance = InstanceType<typeof ep.ElTimePicker>;
declare type ElTimeSelectInstance = InstanceType<typeof ep.ElTimeSelect>;
declare type ElCascaderInstance = InstanceType<typeof ep.ElCascader>;
declare type ElColorPickerInstance = InstanceType<typeof ep.ElColorPicker>;
declare type ElRateInstance = InstanceType<typeof ep.ElRate>;
declare type ElSliderInstance = InstanceType<typeof ep.ElSlider>;
declare type ElUploadInstance = InstanceType<typeof ep.ElUpload>;
declare type ElScrollbarInstance = InstanceType<typeof ep.ElScrollbar>;
declare type TransferKey = ep.TransferKey;
declare type CheckboxValueType = ep.CheckboxValueType;
declare type ElFormRules = ep.FormRules;
declare type DateModelType = ep.DateModelType;
declare type UploadFile = typeof ep.UploadFile;
}

4
src/types/env.d.ts vendored
View File

@ -65,8 +65,10 @@ interface ImportMetaEnv {
VITE_APP_BASE_URL: string; VITE_APP_BASE_URL: string;
VITE_APP_CONTEXT_PATH: string; VITE_APP_CONTEXT_PATH: string;
VITE_APP_MONITRO_ADMIN: string; VITE_APP_MONITRO_ADMIN: string;
VITE_APP_XXL_JOB_ADMIN: string; VITE_APP_POWERJOB_ADMIN: string;
VITE_APP_ENV: string; VITE_APP_ENV: string;
VITE_APP_RSA_PUBLIC_KEY: string;
VITE_APP_CLIENT_ID: string;
} }
interface ImportMeta { interface ImportMeta {
readonly env: ImportMetaEnv; readonly env: ImportMetaEnv;

26
src/types/global.d.ts vendored
View File

@ -1,9 +1,15 @@
import { FormRules } from 'element-plus'; import type { ComponentInternalInstance as ComponentInstance, PropType as VuePropType } from 'vue';
declare global { declare global {
/** vue Instance */
declare type ComponentInternalInstance = ComponentInstance;
/**vue */
declare type PropType<T> = VuePropType<T>;
/** /**
* *
*/ */
interface FieldOption { declare interface FieldOption {
key: number; key: number;
label: string; label: string;
visible: boolean; visible: boolean;
@ -12,7 +18,7 @@ declare global {
/** /**
* *
*/ */
interface DialogOption { declare interface DialogOption {
/** /**
* *
*/ */
@ -23,7 +29,7 @@ declare global {
visible: boolean; visible: boolean;
} }
interface UploadOption { declare interface UploadOption {
/** 设置上传的请求头部 */ /** 设置上传的请求头部 */
headers: { [key: string]: any }; headers: { [key: string]: any };
@ -34,7 +40,7 @@ declare global {
/** /**
* *
*/ */
interface ImportOption extends UploadOption { declare interface ImportOption extends UploadOption {
/** 是否显示弹出层 */ /** 是否显示弹出层 */
open: boolean; open: boolean;
/** 弹出层标题 */ /** 弹出层标题 */
@ -48,14 +54,14 @@ declare global {
/** /**
* *
*/ */
interface DictDataOption { declare interface DictDataOption {
label: string; label: string;
value: string; value: string;
elTagType?: ElTagType; elTagType?: ElTagType;
elTagClass?: string; elTagClass?: string;
} }
interface BaseEntity { declare interface BaseEntity {
createBy?: any; createBy?: any;
createDept?: any; createDept?: any;
createTime?: string; createTime?: string;
@ -68,15 +74,15 @@ declare global {
* T : 表单数据 * T : 表单数据
* D : 查询参数 * D : 查询参数
*/ */
interface PageData<T, D> { declare interface PageData<T, D> {
form: T; form: T;
queryParams: D; queryParams: D;
rules: FormRules; rules: ElFormRules;
} }
/** /**
* *
*/ */
interface PageQuery { declare interface PageQuery {
pageNum: number; pageNum: number;
pageSize: number; pageSize: number;
} }

26
src/types/module.d.ts vendored
View File

@ -1,23 +1,27 @@
import modal from '@/plugins/modal'; import type modal from '@/plugins/modal';
import tab from '@/plugins/tab'; import type tab from '@/plugins/tab';
import { useDict } from '@/utils/dict'; import type download from '@/plugins/download';
import { addDateRange, handleTree, selectDictLabel, selectDictLabels, parseTime } from '@/utils/ruoyi'; import type auth from '@/plugins/auth';
import { getConfigKey, updateConfigByKey } from '@/api/system/config'; import type cache from '@/plugins/cache';
import { download as download1 } from '@/utils/request'; import type animate from '@/animate';
import download from '@/plugins/download'; import type { useDict } from '@/utils/dict';
import animate from '@/animate'; import type { addDateRange, handleTree, selectDictLabel, selectDictLabels, parseTime } from '@/utils/ruoyi';
import type { getConfigKey, updateConfigByKey } from '@/api/system/config';
import type { download as rd } from '@/utils/request';
declare module 'vue' { declare module '@vue/runtime-core' {
export interface ComponentCustomProperties { interface ComponentCustomProperties {
// 全局方法声明 // 全局方法声明
$modal: typeof modal; $modal: typeof modal;
$tab: typeof tab; $tab: typeof tab;
$download: typeof download; $download: typeof download;
$auth: typeof auth;
$cache: typeof cache;
animate: typeof animate; animate: typeof animate;
useDict: typeof useDict; useDict: typeof useDict;
addDateRange: typeof addDateRange; addDateRange: typeof addDateRange;
download: typeof download1; download: typeof rd;
handleTree: typeof handleTree; handleTree: typeof handleTree;
getConfigKey: typeof getConfigKey; getConfigKey: typeof getConfigKey;
updateConfigByKey: typeof updateConfigByKey; updateConfigByKey: typeof updateConfigByKey;

View File

@ -1,7 +1,7 @@
import { RouteRecordRaw } from 'vue-router'; import { RouteRecordRaw } from 'vue-router';
declare module 'vue-router' { declare module 'vue-router' {
type RouteOption = { declare type RouteOption = {
hidden?: boolean; hidden?: boolean;
permissions?: string[]; permissions?: string[];
roles?: string[]; roles?: string[];
@ -16,15 +16,15 @@ declare module 'vue-router' {
query?: string; query?: string;
} & RouteRecordRaw; } & RouteRecordRaw;
interface _RouteLocationBase { declare interface _RouteLocationBase {
children?: RouteOption[]; children?: RouteOption[];
} }
interface RouteLocationOptions { declare interface RouteLocationOptions {
fullPath?: string; fullPath?: string;
} }
interface TagView extends Partial<_RouteLocationBase> { declare interface TagView extends Partial<_RouteLocationBase> {
title?: string; title?: string;
meta?: { meta?: {
link?: string; link?: string;

4
src/types/vform3-builds.d.ts vendored Normal file
View File

@ -0,0 +1,4 @@
declare module 'vform3-builds' {
const content: any;
export = content;
}

View File

@ -4,6 +4,6 @@ const tokenStorage = useStorage<null | string>(TokenKey, null);
export const getToken = () => tokenStorage.value; export const getToken = () => tokenStorage.value;
export const setToken = (token: string) => (tokenStorage.value = token); export const setToken = (access_token: string) => (tokenStorage.value = access_token);
export const removeToken = () => (tokenStorage.value = null); export const removeToken = () => (tokenStorage.value = null);

45
src/utils/crypto.ts Normal file
View File

@ -0,0 +1,45 @@
import CryptoJS from 'crypto-js';
/**
* 32
* @returns {string}
*/
const generateRandomString = () => {
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let result = '';
const charactersLength = characters.length;
for (let i = 0; i < 32; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
};
/**
* aes
* @returns {string}
*/
export const generateAesKey = () => {
return CryptoJS.enc.Utf8.parse(generateRandomString());
};
/**
* base64
* @returns {string}
*/
export const encryptBase64 = (str: CryptoJS.lib.WordArray) => {
return CryptoJS.enc.Base64.stringify(str);
};
/**
* 使
* @param message
* @param aesKey
* @returns {string}
*/
export const encryptWithAes = (message: string, aesKey: CryptoJS.lib.WordArray) => {
const encrypted = CryptoJS.AES.encrypt(message, aesKey, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
return encrypted.toString();
};

View File

@ -1,18 +1,10 @@
import JSEncrypt from 'jsencrypt'; import JSEncrypt from 'jsencrypt';
// 密钥对生成 http://web.chacuo.net/netrsakeypair // 密钥对生成 http://web.chacuo.net/netrsakeypair
const publicKey = const publicKey = import.meta.env.VITE_APP_RSA_PUBLIC_KEY;
'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdH\n' + 'nzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==';
const privateKey = // 前端不建议存放私钥 不建议解密数据 因为都是透明的意义不大
'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY\n' + const privateKey = '**********';
'7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKN\n' +
'PuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gA\n' +
'kM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWow\n' +
'cSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99Ecv\n' +
'DQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthh\n' +
'YhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3\n' +
'UP8iWi1Qw0Y=';
// 加密 // 加密
export const encrypt = (txt: string) => { export const encrypt = (txt: string) => {

25
src/utils/propTypes.ts Normal file
View File

@ -0,0 +1,25 @@
import { CSSProperties } from 'vue';
import VueTypes, { createTypes, toValidableType, VueTypeValidableDef, VueTypesInterface } from 'vue-types';
type PropTypes = VueTypesInterface & {
readonly style: VueTypeValidableDef<CSSProperties>;
};
const propTypes = createTypes({
func: undefined,
bool: undefined,
string: undefined,
number: undefined,
object: undefined,
integer: undefined
}) as PropTypes;
export default class ProjectTypes extends VueTypes {
static get style() {
return toValidableType('style', {
type: [String, Object],
default: undefined
});
}
}
export { propTypes };

View File

@ -8,12 +8,19 @@ import { errorCode } from '@/utils/errorCode';
import { LoadingInstance } from 'element-plus/es/components/loading/src/loading'; import { LoadingInstance } from 'element-plus/es/components/loading/src/loading';
import FileSaver from 'file-saver'; import FileSaver from 'file-saver';
import { getLanguage } from '@/lang'; import { getLanguage } from '@/lang';
import { encryptBase64, encryptWithAes, generateAesKey } from '@/utils/crypto';
import { encrypt } from '@/utils/jsencrypt';
let downloadLoadingInstance: LoadingInstance; let downloadLoadingInstance: LoadingInstance;
// 是否显示重新登录 // 是否显示重新登录
export const isRelogin = { show: false }; export const isRelogin = { show: false };
export const globalHeaders = {
Authorization: "Bearer " + getToken(),
clientid: import.meta.env.VITE_APP_CLIENT_ID
}
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'; axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8';
axios.defaults.headers['clientid'] = import.meta.env.VITE_APP_CLIENT_ID;
// 创建 axios 实例 // 创建 axios 实例
const service = axios.create({ const service = axios.create({
baseURL: import.meta.env.VITE_APP_BASE_API, baseURL: import.meta.env.VITE_APP_BASE_API,
@ -29,6 +36,8 @@ service.interceptors.request.use(
const isToken = (config.headers || {}).isToken === false; const isToken = (config.headers || {}).isToken === false;
// 是否需要防止数据重复提交 // 是否需要防止数据重复提交
const isRepeatSubmit = (config.headers || {}).repeatSubmit === false; const isRepeatSubmit = (config.headers || {}).repeatSubmit === false;
// 是否需要加密
const isEncrypt = (config.headers || {}).isEncrypt === 'true';
if (getToken() && !isToken) { if (getToken() && !isToken) {
config.headers['Authorization'] = 'Bearer ' + getToken(); // 让每个请求携带自定义token 请根据实际情况自行修改 config.headers['Authorization'] = 'Bearer ' + getToken(); // 让每个请求携带自定义token 请根据实际情况自行修改
} }
@ -63,6 +72,13 @@ service.interceptors.request.use(
} }
} }
} }
// 当开启参数加密
if (isEncrypt && (config.method === 'post' || config.method === 'put')) {
// 生成一个 AES 密钥
const aesKey = generateAesKey();
config.headers['encrypt-key'] = encrypt(encryptBase64(aesKey));
config.data = typeof config.data === 'object' ? encryptWithAes(JSON.stringify(config.data), aesKey) : encryptWithAes(config.data, aesKey);
}
// FormData数据去请求头Content-Type // FormData数据去请求头Content-Type
if (config.data instanceof FormData) { if (config.data instanceof FormData) {
delete config.headers['Content-Type']; delete config.headers['Content-Type'];

View File

@ -1,32 +1,34 @@
<template> <template>
<div class="p-2"> <div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="search" v-show="showSearch"> <div class="mb-[10px]" v-show="showSearch">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px"> <el-card shadow="hover">
<el-form-item label="部门id" prop="deptId"> <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
<el-input v-model="queryParams.deptId" placeholder="请输入部门id" clearable @keyup.enter="handleQuery" /> <el-form-item label="部门id" prop="deptId">
</el-form-item> <el-input v-model="queryParams.deptId" placeholder="请输入部门id" clearable @keyup.enter="handleQuery" />
<el-form-item label="用户id" prop="userId"> </el-form-item>
<el-input v-model="queryParams.userId" placeholder="请输入用户id" clearable @keyup.enter="handleQuery" /> <el-form-item label="用户id" prop="userId">
</el-form-item> <el-input v-model="queryParams.userId" placeholder="请输入用户id" clearable @keyup.enter="handleQuery" />
<el-form-item label="排序号" prop="orderNum"> </el-form-item>
<el-input v-model="queryParams.orderNum" placeholder="请输入排序号" clearable @keyup.enter="handleQuery" /> <el-form-item label="排序号" prop="orderNum">
</el-form-item> <el-input v-model="queryParams.orderNum" placeholder="请输入排序号" clearable @keyup.enter="handleQuery" />
<el-form-item label="key键" prop="testKey"> </el-form-item>
<el-input v-model="queryParams.testKey" placeholder="请输入key键" clearable @keyup.enter="handleQuery" /> <el-form-item label="key键" prop="testKey">
</el-form-item> <el-input v-model="queryParams.testKey" placeholder="请输入key键" clearable @keyup.enter="handleQuery" />
<el-form-item label="值" prop="value"> </el-form-item>
<el-input v-model="queryParams.value" placeholder="请输入值" clearable @keyup.enter="handleQuery" /> <el-form-item label="值" prop="value">
</el-form-item> <el-input v-model="queryParams.value" placeholder="请输入值" clearable @keyup.enter="handleQuery" />
<el-form-item> </el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button> <el-form-item>
<el-button icon="Refresh" @click="resetQuery">重置</el-button> <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
</el-form-item> <el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form> </el-form-item>
</el-form>
</el-card>
</div> </div>
</transition> </transition>
<el-card shadow="never"> <el-card shadow="hover">
<template #header> <template #header>
<el-row :gutter="10" class="mb8"> <el-row :gutter="10" class="mb8">
<el-col :span="1.5"> <el-col :span="1.5">
@ -36,7 +38,9 @@
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['demo:demo:edit']">修改</el-button> <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['demo:demo:edit']">修改</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['demo:demo:remove']">删除</el-button> <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['demo:demo:remove']"
>删除</el-button
>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['demo:demo:export']">导出</el-button> <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['demo:demo:export']">导出</el-button>
@ -65,13 +69,7 @@
</el-table-column> </el-table-column>
</el-table> </el-table>
<pagination <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
v-show="total>0"
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</el-card> </el-card>
<!-- 添加或修改测试单对话框 --> <!-- 添加或修改测试单对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body> <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
@ -105,8 +103,6 @@
<script setup name="Demo" lang="ts"> <script setup name="Demo" lang="ts">
import { listDemo, getDemo, delDemo, addDemo, updateDemo } from '@/api/demo/demo'; import { listDemo, getDemo, delDemo, addDemo, updateDemo } from '@/api/demo/demo';
import { DemoVO, DemoQuery, DemoForm } from '@/api/demo/demo/types'; import { DemoVO, DemoQuery, DemoForm } from '@/api/demo/demo/types';
import { ComponentInternalInstance } from 'vue';
import { ElForm } from 'element-plus';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@ -119,8 +115,8 @@ const single = ref(true);
const multiple = ref(true); const multiple = ref(true);
const total = ref(0); const total = ref(0);
const queryFormRef = ref(ElForm); const queryFormRef = ref<ElFormInstance>();
const demoFormRef = ref(ElForm); const demoFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({ const dialog = reactive<DialogOption>({
visible: false, visible: false,
@ -136,7 +132,7 @@ const initFormData: DemoForm = {
value: undefined, value: undefined,
} }
const data = reactive<PageData<DemoForm, DemoQuery>>({ const data = reactive<PageData<DemoForm, DemoQuery>>({
form: {...initFormData}, form: { ...initFormData },
queryParams: { queryParams: {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
@ -187,8 +183,8 @@ const cancel = () => {
/** 表单重置 */ /** 表单重置 */
const reset = () => { const reset = () => {
form.value = {...initFormData}; form.value = { ...initFormData };
demoFormRef.value.resetFields(); demoFormRef.value?.resetFields();
} }
/** 搜索按钮操作 */ /** 搜索按钮操作 */
@ -199,7 +195,7 @@ const handleQuery = () => {
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
queryFormRef.value.resetFields(); queryFormRef.value?.resetFields();
handleQuery(); handleQuery();
} }
@ -212,36 +208,30 @@ const handleSelectionChange = (selection: DemoVO[]) => {
/** 新增按钮操作 */ /** 新增按钮操作 */
const handleAdd = () => { const handleAdd = () => {
reset();
dialog.visible = true; dialog.visible = true;
dialog.title = "添加测试单"; dialog.title = "添加测试单";
nextTick(() => {
reset();
});
} }
/** 修改按钮操作 */ /** 修改按钮操作 */
const handleUpdate = (row?: DemoVO) => { const handleUpdate = async (row?: DemoVO) => {
loading.value = true reset();
const _id = row?.id || ids.value[0]
const res = await getDemo(_id);
Object.assign(form.value, res.data);
dialog.visible = true; dialog.visible = true;
dialog.title = "修改测试单"; dialog.title = "修改测试单";
nextTick(async () => {
reset();
const _id = row?.id || ids.value[0]
const res = await getDemo(_id);
loading.value = false;
Object.assign(form.value, res.data);
});
} }
/** 提交按钮 */ /** 提交按钮 */
const submitForm = () => { const submitForm = () => {
demoFormRef.value.validate(async (valid: boolean) => { demoFormRef.value?.validate(async (valid: boolean) => {
if (valid) { if (valid) {
buttonLoading.value = true; buttonLoading.value = true;
if (form.value.id) { if (form.value.id) {
await updateDemo(form.value).finally(() => buttonLoading.value = false); await updateDemo(form.value).finally(() => buttonLoading.value = false);
} else { } else {
await addDemo(form.value).finally(() => buttonLoading.value = false); await addDemo(form.value).finally(() => buttonLoading.value = false);
} }
proxy?.$modal.msgSuccess("修改成功"); proxy?.$modal.msgSuccess("修改成功");
dialog.visible = false; dialog.visible = false;

View File

@ -1,20 +1,22 @@
<template> <template>
<div class="p-2"> <div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="search" v-show="showSearch"> <div class="mb-[10px]" v-show="showSearch">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px"> <el-card shadow="hover">
<el-form-item label="树节点名" prop="treeName"> <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
<el-input v-model="queryParams.treeName" placeholder="请输入树节点名" clearable @keyup.enter="handleQuery" /> <el-form-item label="树节点名" prop="treeName">
</el-form-item> <el-input v-model="queryParams.treeName" placeholder="请输入树节点名" clearable @keyup.enter="handleQuery" />
<el-form-item> </el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button> <el-form-item>
<el-button icon="Refresh" @click="resetQuery">重置</el-button> <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
</el-form-item> <el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form> </el-form-item>
</el-form>
</el-card>
</div> </div>
</transition> </transition>
<el-card shadow="never"> <el-card shadow="hover">
<template #header> <template #header>
<el-row :gutter="10" class="mb8"> <el-row :gutter="10" class="mb8">
<el-col :span="1.5"> <el-col :span="1.5">
@ -89,8 +91,6 @@
<script setup name="Tree" lang="ts"> <script setup name="Tree" lang="ts">
import { listTree, getTree, delTree, addTree, updateTree } from "@/api/demo/tree"; import { listTree, getTree, delTree, addTree, updateTree } from "@/api/demo/tree";
import { TreeVO, TreeQuery, TreeForm } from '@/api/demo/tree/types'; import { TreeVO, TreeQuery, TreeForm } from '@/api/demo/tree/types';
import { ComponentInternalInstance } from 'vue';
import { ElForm, ElTable } from 'element-plus';
type TreeOption = { type TreeOption = {
@ -109,9 +109,9 @@ const showSearch = ref(true);
const isExpandAll = ref(true); const isExpandAll = ref(true);
const loading = ref(false); const loading = ref(false);
const queryFormRef = ref(ElForm); const queryFormRef = ref<ElFormInstance>();
const treeFormRef = ref(ElForm); const treeFormRef = ref<ElFormInstance>();
const treeTableRef = ref(ElTable) const treeTableRef = ref<ElTableInstance>()
const dialog = reactive<DialogOption>({ const dialog = reactive<DialogOption>({
visible: false, visible: false,
@ -185,7 +185,7 @@ const cancel = () => {
// //
const reset = () => { const reset = () => {
form.value = {...initFormData} form.value = {...initFormData}
treeFormRef.value.resetFields(); treeFormRef.value?.resetFields();
} }
/** 搜索按钮操作 */ /** 搜索按钮操作 */
@ -195,23 +195,21 @@ const handleQuery = () => {
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
queryFormRef.value.resetFields(); queryFormRef.value?.resetFields();
handleQuery(); handleQuery();
} }
/** 新增按钮操作 */ /** 新增按钮操作 */
const handleAdd = (row?: TreeVO) => { const handleAdd = (row?: TreeVO) => {
reset();
getTreeselect();
if (row && row.id) {
form.value.parentId = row.id;
} else {
form.value.parentId = 0;
}
dialog.visible = true; dialog.visible = true;
dialog.title = "添加测试树"; dialog.title = "添加测试树";
nextTick(() => {
reset();
getTreeselect();
if (row != null && row.id) {
form.value.parentId = row.id;
} else {
form.value.parentId = 0;
}
});
} }
/** 展开/折叠操作 */ /** 展开/折叠操作 */
@ -223,31 +221,27 @@ const handleToggleExpandAll = () => {
/** 展开/折叠操作 */ /** 展开/折叠操作 */
const toggleExpandAll = (data: TreeVO[], status: boolean) => { const toggleExpandAll = (data: TreeVO[], status: boolean) => {
data.forEach((item) => { data.forEach((item) => {
treeTableRef.value.toggleRowExpansion(item, status) treeTableRef.value?.toggleRowExpansion(item, status)
if (item.children && item.children.length > 0) toggleExpandAll(item.children, status) if (item.children && item.children.length > 0) toggleExpandAll(item.children, status)
}) })
} }
/** 修改按钮操作 */ /** 修改按钮操作 */
const handleUpdate = (row: TreeVO) => { const handleUpdate = async (row: TreeVO) => {
loading.value = true; reset();
await getTreeselect();
if (row) {
form.value.parentId = row.id;
}
const res = await getTree(row.id);
Object.assign(form.value, res.data);
dialog.visible = true; dialog.visible = true;
dialog.title = "修改测试树"; dialog.title = "修改测试树";
nextTick(async () => {
reset();
await getTreeselect();
if (row != null) {
form.value.parentId = row.id;
}
const res = await getTree(row.id);
loading.value = false;
Object.assign(form.value, res.data);
});
} }
/** 提交按钮 */ /** 提交按钮 */
const submitForm = () => { const submitForm = () => {
treeFormRef.value.validate(async (valid: boolean) => { treeFormRef.value?.validate(async (valid: boolean) => {
if (valid) { if (valid) {
buttonLoading.value = true; buttonLoading.value = true;
if (form.value.id) { if (form.value.id) {
@ -257,7 +251,7 @@ const submitForm = () => {
} }
proxy?.$modal.msgSuccess("操作成功"); proxy?.$modal.msgSuccess("操作成功");
dialog.visible = false; dialog.visible = false;
getList(); await getList();
} }
}); });
} }

View File

@ -21,7 +21,6 @@
<script setup lang="ts"> <script setup lang="ts">
import errImage from '@/assets/401_images/401.gif'; import errImage from '@/assets/401_images/401.gif';
import { ComponentInternalInstance } from "vue";
let { proxy } = getCurrentInstance() as ComponentInternalInstance; let { proxy } = getCurrentInstance() as ComponentInternalInstance;

View File

@ -21,7 +21,7 @@
* 分布式锁 Lock4j 注解锁工具锁 多种多样<br /> * 分布式锁 Lock4j 注解锁工具锁 多种多样<br />
* 分布式幂等 Lock4j 基于分布式锁实现<br /> * 分布式幂等 Lock4j 基于分布式锁实现<br />
* 分布式链路追踪 SkyWalking 支持链路追踪网格分析度量聚合可视化<br /> * 分布式链路追踪 SkyWalking 支持链路追踪网格分析度量聚合可视化<br />
* 分布式任务调度 Xxl-Job 高性能 高可靠 易扩展<br /> * 分布式任务调度 PowerJob 高性能 高可靠 易扩展<br />
* 文件存储 Minio 本地存储<br /> * 文件存储 Minio 本地存储<br />
* 文件存储 七牛阿里腾讯 云存储<br /> * 文件存储 七牛阿里腾讯 云存储<br />
* 监控框架 SpringBoot-Admin 全方位服务监控<br /> * 监控框架 SpringBoot-Admin 全方位服务监控<br />
@ -33,14 +33,14 @@
* 部署方式 Docker 容器编排 一键部署业务集群<br /> * 部署方式 Docker 容器编排 一键部署业务集群<br />
* 国际化 SpringMessage Spring标准国际化方案<br /> * 国际化 SpringMessage Spring标准国际化方案<br />
</p> </p>
<p><b>当前版本:</b> <span>v5.0.0</span></p> <p><b>当前版本:</b> <span>v5.1.0</span></p>
<p> <p>
<el-tag type="danger">&yen;免费开源</el-tag> <el-tag type="danger">&yen;免费开源</el-tag>
</p> </p>
<p> <p>
<el-button type="primary" icon="Cloudy" plain @click="goTarget('https://gitee.com/dromara/RuoYi-Vue-Plus')">访问码云</el-button> <el-button type="primary" icon="Cloudy" plain @click="goTarget('https://gitee.com/dromara/RuoYi-Vue-Plus')">访问码云</el-button>
<el-button type="primary" icon="Cloudy" plain @click="goTarget('https://github.com/dromara/RuoYi-Vue-Plus')">访问GitHub</el-button> <el-button type="primary" icon="Cloudy" plain @click="goTarget('https://github.com/dromara/RuoYi-Vue-Plus')">访问GitHub</el-button>
<el-button type="primary" icon="Cloudy" plain @click="goTarget('https://javalionli.gitee.io/plus-doc/#/ruoyi-vue-plus/changlog')" <el-button type="primary" icon="Cloudy" plain @click="goTarget('https://plus-doc.dromara.org/#/ruoyi-vue-plus/changlog')"
>更新日志</el-button >更新日志</el-button
> >
</p> </p>
@ -78,14 +78,14 @@
* 分布式监控 PrometheusGrafana 全方位性能监控<br /> * 分布式监控 PrometheusGrafana 全方位性能监控<br />
* 其余与 Vue 版本一致<br /> * 其余与 Vue 版本一致<br />
</p> </p>
<p><b>当前版本:</b> <span>v2.0.0</span></p> <p><b>当前版本:</b> <span>v2.1.0</span></p>
<p> <p>
<el-tag type="danger">&yen;免费开源</el-tag> <el-tag type="danger">&yen;免费开源</el-tag>
</p> </p>
<p> <p>
<el-button type="primary" icon="Cloudy" plain @click="goTarget('https://gitee.com/dromara/RuoYi-Cloud-Plus')">访问码云</el-button> <el-button type="primary" icon="Cloudy" plain @click="goTarget('https://gitee.com/dromara/RuoYi-Cloud-Plus')">访问码云</el-button>
<el-button type="primary" icon="Cloudy" plain @click="goTarget('https://github.com/dromara/RuoYi-Cloud-Plus')">访问GitHub</el-button> <el-button type="primary" icon="Cloudy" plain @click="goTarget('https://github.com/dromara/RuoYi-Cloud-Plus')">访问GitHub</el-button>
<el-button type="primary" icon="Cloudy" plain @click="goTarget('https://javalionli.gitee.io/plus-doc/#/ruoyi-cloud-plus/changlog')" <el-button type="primary" icon="Cloudy" plain @click="goTarget('https://plus-doc.dromara.org/#/ruoyi-cloud-plus/changlog')"
>更新日志</el-button >更新日志</el-button
> >
</p> </p>

View File

@ -4,7 +4,7 @@
<h3 class="title">RuoYi-Vue-Plus多租户管理系统</h3> <h3 class="title">RuoYi-Vue-Plus多租户管理系统</h3>
<el-form-item prop="tenantId" v-if="tenantEnabled"> <el-form-item prop="tenantId" v-if="tenantEnabled">
<el-select v-model="loginForm.tenantId" filterable placeholder="请选择/输入公司名称" style="width: 100%"> <el-select v-model="loginForm.tenantId" filterable placeholder="请选择/输入公司名称" style="width: 100%">
<el-option v-for="item in tenantList" :key="item.tenantId" :label="item.companyName" :value="item.tenantId"> </el-option> <el-option v-for="item in tenantList" :key="item.tenantId" :label="item.companyName" :value="item.tenantId"></el-option>
<template #prefix><svg-icon icon-class="company" class="el-input__icon input-icon" /></template> <template #prefix><svg-icon icon-class="company" class="el-input__icon input-icon" /></template>
</el-select> </el-select>
</el-form-item> </el-form-item>
@ -27,6 +27,20 @@
</div> </div>
</el-form-item> </el-form-item>
<el-checkbox v-model="loginForm.rememberMe" style="margin:0px 0px 25px 0px;">记住密码</el-checkbox> <el-checkbox v-model="loginForm.rememberMe" style="margin:0px 0px 25px 0px;">记住密码</el-checkbox>
<el-form-item style="float: right;">
<el-button circle title="微信登录" @click="doSocialLogin('wechat')">
<svg-icon icon-class="wechat" />
</el-button>
<el-button circle title="MaxKey登录" @click="doSocialLogin('maxkey')">
<svg-icon icon-class="maxkey" />
</el-button>
<el-button circle title="Gitee登录" @click="doSocialLogin('gitee')">
<svg-icon icon-class="gitee" />
</el-button>
<el-button circle title="Github登录" @click="doSocialLogin('github')">
<svg-icon icon-class="github" />
</el-button>
</el-form-item>
<el-form-item style="width:100%;"> <el-form-item style="width:100%;">
<el-button :loading="loading" size="large" type="primary" style="width:100%;" @click.prevent="handleLogin"> <el-button :loading="loading" size="large" type="primary" style="width:100%;" @click.prevent="handleLogin">
<span v-if="!loading"> </span> <span v-if="!loading"> </span>
@ -46,30 +60,30 @@
<script setup lang="ts"> <script setup lang="ts">
import { getCodeImg, getTenantList } from '@/api/login'; import { getCodeImg, getTenantList } from '@/api/login';
import { authBinding } from '@/api/system/social/auth';
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import { encrypt, decrypt } from '@/utils/jsencrypt';
import { useUserStore } from '@/store/modules/user'; import { useUserStore } from '@/store/modules/user';
import { LoginData, TenantVO } from '@/api/types'; import { LoginData, TenantVO } from '@/api/types';
import { FormRules } from 'element-plus';
import { to } from 'await-to-js'; import { to } from 'await-to-js';
import { HttpStatus } from "@/enums/RespEnum";
const userStore = useUserStore(); const userStore = useUserStore();
const router = useRouter(); const router = useRouter();
const loginForm = ref<LoginData>({ const loginForm = ref<LoginData>({
tenantId: "000000", tenantId: '000000',
username: 'admin', username: 'admin',
password: 'admin123', password: 'admin123',
rememberMe: false, rememberMe: false,
code: '', code: '',
uuid: '' uuid: ''
}); } as LoginData);
const loginRules: FormRules = { const loginRules: ElFormRules = {
tenantId: [{ required: true, trigger: "blur", message: "请输入您的租户编号" }], tenantId: [{ required: true, trigger: "blur", message: "请输入您的租户编号" }],
username: [{ required: true, trigger: 'blur', message: '请输入您的账号' }], username: [{ required: true, trigger: 'blur', message: '请输入您的账号' }],
password: [{ required: true, trigger: 'blur', message: '请输入您的密码' }], password: [{ required: true, trigger: 'blur', message: '请输入您的密码' }],
code: [{ required: true, trigger: 'change', message: '请输入验证码' }] code: [{ required: true, trigger: 'change', message: '请输入验证码' }]
}; };
const codeUrl = ref(''); const codeUrl = ref('');
@ -83,69 +97,68 @@ const tenantEnabled = ref(true);
// //
const register = ref(false); const register = ref(false);
const redirect = ref(undefined); const redirect = ref(undefined);
const loginRef = ref(ElForm); const loginRef = ref<ElFormInstance>();
// //
const tenantList = ref<TenantVO[]>([]); const tenantList = ref<TenantVO[]>([]);
const handleLogin = () => { const handleLogin = () => {
loginRef.value.validate(async (valid:boolean, fields: any) => { loginRef.value?.validate(async (valid: boolean, fields: any) => {
if (valid) { if (valid) {
loading.value = true; loading.value = true;
// cookie // cookie
if (loginForm.value.rememberMe) { if (loginForm.value.rememberMe) {
Cookies.set("tenantId", loginForm.value.tenantId, { expires: 30 }); Cookies.set("tenantId", String(loginForm.value.tenantId), { expires: 30 });
Cookies.set('username', loginForm.value.username, { expires: 30 }); Cookies.set('username', String(loginForm.value.username), { expires: 30 });
Cookies.set('password', String(encrypt(loginForm.value.password)), { expires: 30 }); Cookies.set('password', String(loginForm.value.password), { expires: 30 });
Cookies.set('rememberMe', String(loginForm.value.rememberMe), { expires: 30 }); Cookies.set('rememberMe', String(loginForm.value.rememberMe), { expires: 30 });
} else { } else {
// //
Cookies.remove("tenantId"); Cookies.remove("tenantId");
Cookies.remove('username'); Cookies.remove('username');
Cookies.remove('password'); Cookies.remove('password');
Cookies.remove('rememberMe'); Cookies.remove('rememberMe');
} }
// action // action
// prittier-ignore const [err] = await to(userStore.login(loginForm.value));
const [err] = await to(userStore.login(loginForm.value)); if (!err) {
if (!err) { await router.push({ path: redirect.value || '/' });
await router.push({ path: redirect.value || '/' }); } else {
} else { loading.value = false;
loading.value = false; //
// if (captchaEnabled.value) {
if (captchaEnabled.value) { await getCode();
await getCode();
}
}
} else {
console.log('error submit!', fields);
} }
}); }
} else {
console.log('error submit!', fields);
}
});
}; };
/** /**
* 获取验证码 * 获取验证码
*/ */
const getCode = async () => { const getCode = async () => {
const res = await getCodeImg(); const res = await getCodeImg();
const { data } = res; const { data } = res;
captchaEnabled.value = data.captchaEnabled === undefined ? true : data.captchaEnabled; captchaEnabled.value = data.captchaEnabled === undefined ? true : data.captchaEnabled;
if (captchaEnabled.value) { if (captchaEnabled.value) {
codeUrl.value = 'data:image/gif;base64,' + data.img; codeUrl.value = 'data:image/gif;base64,' + data.img;
loginForm.value.uuid = data.uuid; loginForm.value.uuid = data.uuid;
} }
}; };
const getCookie = () => { const getCookie = () => {
const tenantId = Cookies.get("tenantId"); const tenantId = Cookies.get("tenantId");
const username = Cookies.get('username'); const username = Cookies.get('username');
const password = Cookies.get('password'); const password = Cookies.get('password');
const rememberMe = Cookies.get('rememberMe'); const rememberMe = Cookies.get('rememberMe');
loginForm.value = { loginForm.value = {
tenantId: tenantId === undefined ? loginForm.value.tenantId : tenantId, tenantId: tenantId === undefined ? String(loginForm.value.tenantId) : tenantId,
username: username === undefined ? loginForm.value.username : username, username: username === undefined ? String(loginForm.value.username) : username,
password: password === undefined ? loginForm.value.password : (decrypt(password) as string), password: password === undefined ? String(loginForm.value.password) : String(password),
rememberMe: rememberMe === undefined ? false : Boolean(rememberMe) rememberMe: rememberMe === undefined ? false : Boolean(rememberMe)
}; } as LoginData;
} }
@ -153,20 +166,42 @@ const getCookie = () => {
* 获取租户列表 * 获取租户列表
*/ */
const initTenantList = async () => { const initTenantList = async () => {
const { data } = await getTenantList(); const { data } = await getTenantList();
tenantEnabled.value = data.tenantEnabled === undefined ? true : data.tenantEnabled; tenantEnabled.value = data.tenantEnabled === undefined ? true : data.tenantEnabled;
if (tenantEnabled.value) { if (tenantEnabled.value) {
tenantList.value = data.voList; tenantList.value = data.voList;
if (tenantList.value != null && tenantList.value.length !== 0) { if (tenantList.value != null && tenantList.value.length !== 0) {
loginForm.value.tenantId = tenantList.value[0].tenantId; loginForm.value.tenantId = tenantList.value[0].tenantId;
}
} }
}
} }
//
watch(() => loginForm.value.tenantId, () => {
Cookies.set("tenantId", String(loginForm.value.tenantId), { expires: 30 })
});
/**
* 第三方登录
* @param type
*/
const doSocialLogin = (type: string) => {
authBinding(type).then((res: any) => {
if (res.code === HttpStatus.SUCCESS) {
//
window.location.href = res.data;
} else {
ElMessage.error(res.msg);
}
});
};
onMounted(() => { onMounted(() => {
getCode(); getCode();
initTenantList(); initTenantList();
getCookie(); getCookie();
}); });
</script> </script>
@ -179,6 +214,7 @@ onMounted(() => {
background-image: url("../assets/images/login-background.jpg"); background-image: url("../assets/images/login-background.jpg");
background-size: cover; background-size: cover;
} }
.title { .title {
margin: 0px auto 30px auto; margin: 0px auto 30px auto;
text-align: center; text-align: center;
@ -190,32 +226,39 @@ onMounted(() => {
background: #ffffff; background: #ffffff;
width: 400px; width: 400px;
padding: 25px 25px 5px 25px; padding: 25px 25px 5px 25px;
.el-input { .el-input {
height: 40px; height: 40px;
input { input {
height: 40px; height: 40px;
} }
} }
.input-icon { .input-icon {
height: 39px; height: 39px;
width: 14px; width: 14px;
margin-left: 0px; margin-left: 0px;
} }
} }
.login-tip { .login-tip {
font-size: 13px; font-size: 13px;
text-align: center; text-align: center;
color: #bfbfbf; color: #bfbfbf;
} }
.login-code { .login-code {
width: 33%; width: 33%;
height: 40px; height: 40px;
float: right; float: right;
img { img {
cursor: pointer; cursor: pointer;
vertical-align: middle; vertical-align: middle;
} }
} }
.el-login-footer { .el-login-footer {
height: 40px; height: 40px;
line-height: 40px; line-height: 40px;
@ -224,10 +267,11 @@ onMounted(() => {
width: 100%; width: 100%;
text-align: center; text-align: center;
color: #fff; color: #fff;
font-family: Arial,serif; font-family: Arial, serif;
font-size: 12px; font-size: 12px;
letter-spacing: 1px; letter-spacing: 1px;
} }
.login-code-img { .login-code-img {
height: 40px; height: 40px;
padding-left: 12px; padding-left: 12px;

View File

@ -2,7 +2,7 @@
<div class="p-2"> <div class="p-2">
<el-row> <el-row>
<el-col :span="24" class="card-box"> <el-col :span="24" class="card-box">
<el-card> <el-card shadow="hover">
<template #header> <template #header>
<Monitor style="width: 1em; height: 1em; vertical-align: middle;" /> <Monitor style="width: 1em; height: 1em; vertical-align: middle;" />
<span style="vertical-align: middle;">基本信息</span> <span style="vertical-align: middle;">基本信息</span>
@ -98,7 +98,7 @@
</el-col> </el-col>
<el-col :span="12" class="card-box"> <el-col :span="12" class="card-box">
<el-card> <el-card shadow="hover">
<template #header> <template #header>
<PieChart style="width: 1em; height: 1em; vertical-align: middle;" /> <PieChart style="width: 1em; height: 1em; vertical-align: middle;" />
<span style="vertical-align: middle;">命令统计</span> <span style="vertical-align: middle;">命令统计</span>
@ -110,7 +110,7 @@
</el-col> </el-col>
<el-col :span="12" class="card-box"> <el-col :span="12" class="card-box">
<el-card> <el-card shadow="hover">
<template #header> <template #header>
<Odometer style="width: 1em; height: 1em; vertical-align: middle;" /> <span style="vertical-align: middle;">内存信息</span> <Odometer style="width: 1em; height: 1em; vertical-align: middle;" /> <span style="vertical-align: middle;">内存信息</span>
</template> </template>
@ -126,7 +126,6 @@
<script setup name="Cache" lang="ts"> <script setup name="Cache" lang="ts">
import { getCache } from '@/api/monitor/cache'; import { getCache } from '@/api/monitor/cache';
import * as echarts from 'echarts'; import * as echarts from 'echarts';
import { ComponentInternalInstance } from "vue";
const cache = ref<any>({}); const cache = ref<any>({});
const commandstats = ref(); const commandstats = ref();
@ -134,56 +133,59 @@ const usedmemory = ref();
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const getList = async () => { const getList = async () => {
proxy?.$modal.loading("正在加载缓存监控数据,请稍候!"); proxy?.$modal.loading("正在加载缓存监控数据,请稍候!");
const res = await getCache(); const res = await getCache();
proxy?.$modal.closeLoading(); proxy?.$modal.closeLoading();
cache.value = res.data; cache.value = res.data;
const commandstatsIntance = echarts.init(commandstats.value, "macarons"); const commandstatsIntance = echarts.init(commandstats.value, "macarons");
commandstatsIntance.setOption({ commandstatsIntance.setOption({
tooltip: { tooltip: {
trigger: "item", trigger: "item",
formatter: "{a} <br/>{b} : {c} ({d}%)" formatter: "{a} <br/>{b} : {c} ({d}%)"
},
series: [
{
name: "命令",
type: "pie",
roseType: "radius",
radius: [15, 95],
center: ["50%", "38%"],
data: res.data.commandStats,
animationEasing: "cubicInOut",
animationDuration: 1000
}
]
});
const usedmemoryInstance = echarts.init(usedmemory.value, "macarons");
usedmemoryInstance.setOption({
tooltip: {
formatter: "{b} <br/>{a} : " + cache.value.info.used_memory_human
},
series: [
{
name: "峰值",
type: "gauge",
min: 0,
max: 1000,
detail: {
formatter: cache.value.info.used_memory_human
}, },
series: [ data: [
{ {
name: "命令", value: parseFloat(cache.value.info.used_memory_human),
type: "pie", name: "内存消耗"
roseType: "radius", }
radius: [15, 95],
center: ["50%", "38%"],
data: res.data.commandStats,
animationEasing: "cubicInOut",
animationDuration: 1000
}
] ]
}); }
]
const usedmemoryInstance = echarts.init(usedmemory.value, "macarons"); })
usedmemoryInstance.setOption({ window.addEventListener("resize",()=>{
tooltip: { commandstatsIntance.resize()
formatter: "{b} <br/>{a} : " + cache.value.info.used_memory_human usedmemoryInstance.resize()
}, });
series: [
{
name: "峰值",
type: "gauge",
min: 0,
max: 1000,
detail: {
formatter: cache.value.info.used_memory_human
},
data: [
{
value: parseFloat(cache.value.info.used_memory_human),
name: "内存消耗"
}
]
}
]
})
} }
onMounted(() => { onMounted(() => {
getList(); getList();
}) })
</script> </script>

View File

@ -1,39 +1,41 @@
<template> <template>
<div class="p-2"> <div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="search" v-show="showSearch"> <div class="mb-[10px]" v-show="showSearch">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px"> <el-card shadow="hover">
<el-form-item label="登录地址" prop="ipaddr"> <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
<el-input v-model="queryParams.ipaddr" placeholder="请输入登录地址" clearable style="width: 240px;" @keyup.enter="handleQuery" /> <el-form-item label="登录地址" prop="ipaddr">
</el-form-item> <el-input v-model="queryParams.ipaddr" placeholder="请输入登录地址" clearable style="width: 240px;" @keyup.enter="handleQuery" />
<el-form-item label="用户名称" prop="userName"> </el-form-item>
<el-input v-model="queryParams.userName" placeholder="请输入用户名称" clearable style="width: 240px;" @keyup.enter="handleQuery" /> <el-form-item label="用户名称" prop="userName">
</el-form-item> <el-input v-model="queryParams.userName" placeholder="请输入用户名称" clearable style="width: 240px;" @keyup.enter="handleQuery" />
<el-form-item label="状态" prop="status"> </el-form-item>
<el-select v-model="queryParams.status" placeholder="登录状态" clearable style="width: 240px"> <el-form-item label="状态" prop="status">
<el-option v-for="dict in sys_common_status" :key="dict.value" :label="dict.label" :value="dict.value" /> <el-select v-model="queryParams.status" placeholder="登录状态" clearable style="width: 240px">
</el-select> <el-option v-for="dict in sys_common_status" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-form-item> </el-select>
<el-form-item label="登录时间" style="width: 308px"> </el-form-item>
<el-date-picker <el-form-item label="登录时间" style="width: 308px">
v-model="dateRange" <el-date-picker
value-format="YYYY-MM-DD HH:mm:ss" v-model="dateRange"
type="daterange" value-format="YYYY-MM-DD HH:mm:ss"
range-separator="-" type="daterange"
start-placeholder="开始日期" range-separator="-"
end-placeholder="结束日期" start-placeholder="开始日期"
:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]" end-placeholder="结束日期"
></el-date-picker> :default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
</el-form-item> ></el-date-picker>
<el-form-item> </el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button> <el-form-item>
<el-button icon="Refresh" @click="resetQuery">重置</el-button> <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
</el-form-item> <el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form> </el-form-item>
</el-form>
</el-card>
</div> </div>
</transition> </transition>
<el-card shadow="never"> <el-card shadow="hover">
<template #header> <template #header>
<el-row :gutter="10" class="mb8"> <el-row :gutter="10" class="mb8">
<el-col :span="1.5"> <el-col :span="1.5">
@ -98,9 +100,7 @@
<script setup name="Logininfor" lang="ts"> <script setup name="Logininfor" lang="ts">
import { list, delLoginInfo, cleanLoginInfo, unlockLoginInfo } from "@/api/monitor/loginInfo"; import { list, delLoginInfo, cleanLoginInfo, unlockLoginInfo } from "@/api/monitor/loginInfo";
import { ComponentInternalInstance } from "vue";
import { LoginInfoQuery, LoginInfoVO } from "@/api/monitor/loginInfo/types"; import { LoginInfoQuery, LoginInfoVO } from "@/api/monitor/loginInfo/types";
import { DateModelType } from 'element-plus';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { sys_common_status } = toRefs<any>(proxy?.useDict("sys_common_status")); const { sys_common_status } = toRefs<any>(proxy?.useDict("sys_common_status"));
@ -116,8 +116,8 @@ const total = ref(0);
const dateRange = ref<[DateModelType,DateModelType]>(['', '']); const dateRange = ref<[DateModelType,DateModelType]>(['', '']);
const defaultSort = ref<any>({ prop: "loginTime", order: "descending" }); const defaultSort = ref<any>({ prop: "loginTime", order: "descending" });
const queryFormRef = ref(ElForm); const queryFormRef = ref<ElFormInstance>();
const loginInfoTableRef = ref(ElTable); const loginInfoTableRef = ref<ElTableInstance>();
// //
const queryParams = ref<LoginInfoQuery>({ const queryParams = ref<LoginInfoQuery>({
pageNum: 1, pageNum: 1,
@ -145,9 +145,9 @@ const handleQuery = () => {
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
dateRange.value = ['', '']; dateRange.value = ['', ''];
queryFormRef.value.resetFields(); queryFormRef.value?.resetFields();
queryParams.value.pageNum = 1; queryParams.value.pageNum = 1;
loginInfoTableRef.value.sort(defaultSort.value.prop, defaultSort.value.order); loginInfoTableRef.value?.sort(defaultSort.value.prop, defaultSort.value.order);
} }
/** 多选框选中数据 */ /** 多选框选中数据 */
const handleSelectionChange = (selection: LoginInfoVO[]) => { const handleSelectionChange = (selection: LoginInfoVO[]) => {
@ -167,14 +167,14 @@ const handleDelete = async (row?: LoginInfoVO) => {
const infoIds = row?.infoId || ids.value; const infoIds = row?.infoId || ids.value;
await proxy?.$modal.confirm('是否确认删除访问编号为"' + infoIds + '"的数据项?'); await proxy?.$modal.confirm('是否确认删除访问编号为"' + infoIds + '"的数据项?');
await delLoginInfo(infoIds); await delLoginInfo(infoIds);
getList(); await getList();
proxy?.$modal.msgSuccess("删除成功"); proxy?.$modal.msgSuccess("删除成功");
} }
/** 清空按钮操作 */ /** 清空按钮操作 */
const handleClean = async () => { const handleClean = async () => {
await proxy?.$modal.confirm("是否确认清空所有登录日志数据项?"); await proxy?.$modal.confirm("是否确认清空所有登录日志数据项?");
await cleanLoginInfo(); await cleanLoginInfo();
getList(); await getList();
proxy?.$modal.msgSuccess("清空成功"); proxy?.$modal.msgSuccess("清空成功");
} }
/** 解锁按钮操作 */ /** 解锁按钮操作 */

View File

@ -1,20 +1,22 @@
<template> <template>
<div class="p-2"> <div class="p-2">
<div class="search"> <div class="mb-[10px]">
<el-form :model="queryParams" ref="queryFormRef" :inline="true"> <el-card shadow="hover">
<el-form-item label="登录地址" prop="ipaddr"> <el-form :model="queryParams" ref="queryFormRef" :inline="true">
<el-input v-model="queryParams.ipaddr" placeholder="请输入登录地址" clearable style="width: 200px" @keyup.enter="handleQuery" /> <el-form-item label="登录地址" prop="ipaddr">
</el-form-item> <el-input v-model="queryParams.ipaddr" placeholder="请输入登录地址" clearable style="width: 200px" @keyup.enter="handleQuery" />
<el-form-item label="用户名称" prop="userName"> </el-form-item>
<el-input v-model="queryParams.userName" placeholder="请输入用户名称" clearable style="width: 200px" @keyup.enter="handleQuery" /> <el-form-item label="用户名称" prop="userName">
</el-form-item> <el-input v-model="queryParams.userName" placeholder="请输入用户名称" clearable style="width: 200px" @keyup.enter="handleQuery" />
<el-form-item> </el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button> <el-form-item>
<el-button icon="Refresh" @click="resetQuery">重置</el-button> <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
</el-form-item> <el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form> </el-form-item>
</el-form>
</el-card>
</div> </div>
<div class="panel"> <el-card shadow="hover">
<el-table <el-table
v-loading="loading" v-loading="loading"
:data="onlineList.slice((queryParams.pageNum - 1) * queryParams.pageSize, queryParams.pageNum * queryParams.pageSize)" :data="onlineList.slice((queryParams.pageNum - 1) * queryParams.pageSize, queryParams.pageNum * queryParams.pageSize)"
@ -48,13 +50,12 @@
</el-table> </el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" /> <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" />
</div> </el-card>
</div> </div>
</template> </template>
<script setup name="Online" lang="ts"> <script setup name="Online" lang="ts">
import { forceLogout, list as initData } from "@/api/monitor/online"; import { forceLogout, list as initData } from "@/api/monitor/online";
import { ComponentInternalInstance } from "vue";
import { OnlineQuery, OnlineVO } from "@/api/monitor/online/types"; import { OnlineQuery, OnlineVO } from "@/api/monitor/online/types";
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@ -63,42 +64,42 @@ const onlineList = ref<OnlineVO[]>([]);
const loading = ref(true); const loading = ref(true);
const total = ref(0); const total = ref(0);
const queryFormRef = ref(ElForm); const queryFormRef = ref<ElFormInstance>();
const queryParams = ref<OnlineQuery>({ const queryParams = ref<OnlineQuery>({
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
ipaddr: '', ipaddr: '',
userName: '' userName: ''
}); });
/** 查询登录日志列表 */ /** 查询登录日志列表 */
const getList = async () => { const getList = async () => {
loading.value = true; loading.value = true;
const res = await initData(queryParams.value); const res = await initData(queryParams.value);
onlineList.value = res.rows; onlineList.value = res.rows;
total.value = res.total; total.value = res.total;
loading.value = false; loading.value = false;
} }
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
queryParams.value.pageNum = 1; queryParams.value.pageNum = 1;
getList(); getList();
} }
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
queryFormRef.value.resetFields(); queryFormRef.value?.resetFields();
handleQuery(); handleQuery();
} }
/** 强退按钮操作 */ /** 强退按钮操作 */
const handleForceLogout = async (row: OnlineVO) => { const handleForceLogout = async (row: OnlineVO) => {
await proxy?.$modal.confirm('是否确认强退名称为"' + row.userName + '"的用户?'); await proxy?.$modal.confirm('是否确认强退名称为"' + row.userName + '"的用户?');
await forceLogout(row.tokenId); await forceLogout(row.tokenId);
getList(); await getList();
proxy?.$modal.msgSuccess("删除成功"); proxy?.$modal.msgSuccess("删除成功");
} }
onMounted(() => { onMounted(() => {
getList(); getList();
}) })
</script> </script>

View File

@ -1,44 +1,46 @@
<template> <template>
<div class="p-2"> <div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="search" v-show="showSearch"> <div class="mb-[10px]">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px"> <el-card shadow="hover">
<el-form-item label="系统模块" prop="title"> <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
<el-input v-model="queryParams.title" placeholder="请输入系统模块" clearable style="width: 240px;" @keyup.enter="handleQuery" /> <el-form-item label="系统模块" prop="title">
</el-form-item> <el-input v-model="queryParams.title" placeholder="请输入系统模块" clearable style="width: 240px;" @keyup.enter="handleQuery" />
<el-form-item label="操作人员" prop="operName"> </el-form-item>
<el-input v-model="queryParams.operName" placeholder="请输入操作人员" clearable style="width: 240px;" @keyup.enter="handleQuery" /> <el-form-item label="操作人员" prop="operName">
</el-form-item> <el-input v-model="queryParams.operName" placeholder="请输入操作人员" clearable style="width: 240px;" @keyup.enter="handleQuery" />
<el-form-item label="类型" prop="businessType"> </el-form-item>
<el-select v-model="queryParams.businessType" placeholder="操作类型" clearable style="width: 240px"> <el-form-item label="类型" prop="businessType">
<el-option v-for="dict in sys_oper_type" :key="dict.value" :label="dict.label" :value="dict.value" /> <el-select v-model="queryParams.businessType" placeholder="操作类型" clearable style="width: 240px">
</el-select> <el-option v-for="dict in sys_oper_type" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-form-item> </el-select>
<el-form-item label="状态" prop="status"> </el-form-item>
<el-select v-model="queryParams.status" placeholder="操作状态" clearable style="width: 240px"> <el-form-item label="状态" prop="status">
<el-option v-for="dict in sys_common_status" :key="dict.value" :label="dict.label" :value="dict.value" /> <el-select v-model="queryParams.status" placeholder="操作状态" clearable style="width: 240px">
</el-select> <el-option v-for="dict in sys_common_status" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-form-item> </el-select>
<el-form-item label="操作时间" style="width: 308px"> </el-form-item>
<el-date-picker <el-form-item label="操作时间" style="width: 308px">
v-model="dateRange" <el-date-picker
value-format="YYYY-MM-DD HH:mm:ss" v-model="dateRange"
type="daterange" value-format="YYYY-MM-DD HH:mm:ss"
range-separator="-" type="daterange"
start-placeholder="开始日期" range-separator="-"
end-placeholder="结束日期" start-placeholder="开始日期"
:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]" end-placeholder="结束日期"
></el-date-picker> :default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
</el-form-item> ></el-date-picker>
<el-form-item> </el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button> <el-form-item>
<el-button icon="Refresh" @click="resetQuery">重置</el-button> <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
</el-form-item> <el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form> </el-form-item>
</el-form>
</el-card>
</div> </div>
</transition> </transition>
<el-card shadow="never"> <el-card shadow="hover">
<template #header> <template #header>
<el-row :gutter="10" class="mb8"> <el-row :gutter="10" class="mb8">
<el-col :span="1.5"> <el-col :span="1.5">
@ -132,7 +134,7 @@
<el-form-item label="操作方法:">{{ form.method }}</el-form-item> <el-form-item label="操作方法:">{{ form.method }}</el-form-item>
</el-col> </el-col>
<el-col :span="24"> <el-col :span="24">
<el-form-item label="请求参数:">{{form.operParam}}</el-form-item> <el-form-item label="请求参数:">{{ form.operParam }}</el-form-item>
</el-col> </el-col>
<el-col :span="24"> <el-col :span="24">
<el-form-item label="返回参数:">{{ form.jsonResult }}</el-form-item> <el-form-item label="返回参数:">{{ form.jsonResult }}</el-form-item>
@ -165,12 +167,10 @@
<script setup name="Operlog" lang="ts"> <script setup name="Operlog" lang="ts">
import { list, delOperlog, cleanOperlog } from '@/api/monitor/operlog'; import { list, delOperlog, cleanOperlog } from '@/api/monitor/operlog';
import { ComponentInternalInstance } from 'vue';
import { OperLogForm, OperLogQuery, OperLogVO } from '@/api/monitor/operlog/types'; import { OperLogForm, OperLogQuery, OperLogVO } from '@/api/monitor/operlog/types';
import { DateModelType } from 'element-plus';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { sys_oper_type, sys_common_status } = toRefs<any>(proxy?.useDict("sys_oper_type","sys_common_status")); const { sys_oper_type, sys_common_status } = toRefs<any>(proxy?.useDict("sys_oper_type", "sys_common_status"));
const operlogList = ref<OperLogVO[]>([]); const operlogList = ref<OperLogVO[]>([]);
const loading = ref(true); const loading = ref(true);
@ -181,116 +181,116 @@ const total = ref(0);
const dateRange = ref<[DateModelType, DateModelType]>(['', '']); const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
const defaultSort = ref<any>({ prop: "operTime", order: "descending" }); const defaultSort = ref<any>({ prop: "operTime", order: "descending" });
const operLogTableRef = ref(ElTable); const operLogTableRef = ref<ElTableInstance>();
const queryFormRef = ref(ElForm); const queryFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({ const dialog = reactive<DialogOption>({
visible: false, visible: false,
title: '' title: ''
}); });
const data = reactive<PageData<OperLogForm, OperLogQuery>>({ const data = reactive<PageData<OperLogForm, OperLogQuery>>({
form: { form: {
operId: undefined, operId: undefined,
tenantId: undefined, tenantId: undefined,
title: '', title: '',
businessType: 0, businessType: 0,
businessTypes: undefined, businessTypes: undefined,
method: '', method: '',
requestMethod: '', requestMethod: '',
operatorType: 0, operatorType: 0,
operName: '', operName: '',
deptName: '', deptName: '',
operUrl: '', operUrl: '',
operIp: '', operIp: '',
operLocation: '', operLocation: '',
operParam: '', operParam: '',
jsonResult: '', jsonResult: '',
status: 0, status: 0,
errorMsg: '', errorMsg: '',
operTime: '', operTime: '',
costTime: 0 costTime: 0
}, },
queryParams: { queryParams: {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
title: '', title: '',
operName: '', operName: '',
businessType: '', businessType: '',
status: '', status: '',
orderByColumn: defaultSort.value.prop, orderByColumn: defaultSort.value.prop,
isAsc: defaultSort.value.order isAsc: defaultSort.value.order
}, },
rules: {} rules: {}
}); });
const { queryParams, form } = toRefs(data); const { queryParams, form } = toRefs(data);
/** 查询登录日志 */ /** 查询登录日志 */
const getList = async () => { const getList = async () => {
loading.value = true; loading.value = true;
const res = await list(proxy?.addDateRange(queryParams.value, dateRange.value)); const res = await list(proxy?.addDateRange(queryParams.value, dateRange.value));
operlogList.value = res.rows; operlogList.value = res.rows;
total.value = res.total; total.value = res.total;
loading.value = false; loading.value = false;
} }
/** 操作日志类型字典翻译 */ /** 操作日志类型字典翻译 */
const typeFormat = (row: OperLogForm) => { const typeFormat = (row: OperLogForm) => {
return proxy?.selectDictLabel(sys_oper_type.value, row.businessType); return proxy?.selectDictLabel(sys_oper_type.value, row.businessType);
} }
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
queryParams.value.pageNum = 1; queryParams.value.pageNum = 1;
getList(); getList();
} }
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
dateRange.value = ['', '']; dateRange.value = ['', ''];
queryFormRef.value.resetFields(); queryFormRef.value?.resetFields();
queryParams.value.pageNum = 1; queryParams.value.pageNum = 1;
operLogTableRef.value.sort(defaultSort.value.prop, defaultSort.value.order); operLogTableRef.value?.sort(defaultSort.value.prop, defaultSort.value.order);
} }
/** 多选框选中数据 */ /** 多选框选中数据 */
const handleSelectionChange = (selection: OperLogVO[]) => { const handleSelectionChange = (selection: OperLogVO[]) => {
ids.value = selection.map(item => item.operId); ids.value = selection.map(item => item.operId);
multiple.value = !selection.length; multiple.value = !selection.length;
} }
/** 排序触发事件 */ /** 排序触发事件 */
const handleSortChange = (column: any) => { const handleSortChange = (column: any) => {
queryParams.value.orderByColumn = column.prop; queryParams.value.orderByColumn = column.prop;
queryParams.value.isAsc = column.order; queryParams.value.isAsc = column.order;
getList(); getList();
} }
/** 详细按钮操作 */ /** 详细按钮操作 */
const handleView = (row: OperLogVO) => { const handleView = (row: OperLogVO) => {
dialog.visible = true; dialog.visible = true;
form.value = row; form.value = row;
} }
/** 删除按钮操作 */ /** 删除按钮操作 */
const handleDelete = async (row?: OperLogVO) => { const handleDelete = async (row?: OperLogVO) => {
const operIds = row?.operId || ids.value; const operIds = row?.operId || ids.value;
await proxy?.$modal.confirm('是否确认删除日志编号为"' + operIds + '"的数据项?'); await proxy?.$modal.confirm('是否确认删除日志编号为"' + operIds + '"的数据项?');
await delOperlog(operIds); await delOperlog(operIds);
getList(); await getList();
proxy?.$modal.msgSuccess("删除成功"); proxy?.$modal.msgSuccess("删除成功");
} }
/** 清空按钮操作 */ /** 清空按钮操作 */
const handleClean = async () => { const handleClean = async () => {
await proxy?.$modal.confirm("是否确认清空所有操作日志数据项?"); await proxy?.$modal.confirm("是否确认清空所有操作日志数据项?");
await cleanOperlog(); await cleanOperlog();
getList(); await getList();
proxy?.$modal.msgSuccess("清空成功"); proxy?.$modal.msgSuccess("清空成功");
} }
/** 导出按钮操作 */ /** 导出按钮操作 */
const handleExport = () => { const handleExport = () => {
proxy?.download("monitor/operlog/export", { proxy?.download("monitor/operlog/export", {
...queryParams.value, ...queryParams.value,
}, `config_${new Date().getTime()}.xlsx`); }, `config_${new Date().getTime()}.xlsx`);
} }
onMounted(() => { onMounted(() => {
getList(); getList();
}) })
</script> </script>

View File

@ -5,5 +5,5 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
const url = ref(import.meta.env.VITE_APP_XXL_JOB_ADMIN); const url = ref(import.meta.env.VITE_APP_POWERJOB_ADMIN);
</script> </script>

View File

@ -58,19 +58,18 @@
<script setup lang="ts"> <script setup lang="ts">
import { getCodeImg, register, getTenantList } from '@/api/login'; import { getCodeImg, register, getTenantList } from '@/api/login';
import { RegisterForm, TenantVO } from '@/api/types'; import { RegisterForm, TenantVO } from '@/api/types';
import { FormRules } from 'element-plus';
import { to } from 'await-to-js'; import { to } from 'await-to-js';
const router = useRouter(); const router = useRouter();
const registerForm = ref<RegisterForm>({ const registerForm = ref<RegisterForm>({
tenantId: "", tenantId: "",
username: "", username: "",
password: "", password: "",
confirmPassword: "", confirmPassword: "",
code: "", code: "",
uuid: "", uuid: "",
userType: "sys_user" userType: "sys_user"
}); });
// //
@ -78,30 +77,30 @@ const tenantEnabled = ref(true);
const equalToPassword = (rule: any, value: string, callback: any) => { const equalToPassword = (rule: any, value: string, callback: any) => {
if (registerForm.value.password !== value) { if (registerForm.value.password !== value) {
callback(new Error("两次输入的密码不一致")); callback(new Error("两次输入的密码不一致"));
} else { } else {
callback(); callback();
} }
}; };
const registerRules: FormRules = { const registerRules: ElFormRules = {
tenantId: [ tenantId: [
{ required: true, trigger: "blur", message: "请输入您的租户编号" } { required: true, trigger: "blur", message: "请输入您的租户编号" }
], ],
username: [ username: [
{ required: true, trigger: "blur", message: "请输入您的账号" }, { required: true, trigger: "blur", message: "请输入您的账号" },
{ min: 2, max: 20, message: "用户账号长度必须介于 2 和 20 之间", trigger: "blur" } { min: 2, max: 20, message: "用户账号长度必须介于 2 和 20 之间", trigger: "blur" }
], ],
password: [ password: [
{ required: true, trigger: "blur", message: "请输入您的密码" }, { required: true, trigger: "blur", message: "请输入您的密码" },
{ min: 5, max: 20, message: "用户密码长度必须介于 5 和 20 之间", trigger: "blur" } { min: 5, max: 20, message: "用户密码长度必须介于 5 和 20 之间", trigger: "blur" }
], ],
confirmPassword: [ confirmPassword: [
{ required: true, trigger: "blur", message: "请再次输入您的密码" }, { required: true, trigger: "blur", message: "请再次输入您的密码" },
{ required: true, validator: equalToPassword, trigger: "blur" } { required: true, validator: equalToPassword, trigger: "blur" }
], ],
code: [{ required: true, trigger: "change", message: "请输入验证码" }] code: [{ required: true, trigger: "change", message: "请输入验证码" }]
}; };
const codeUrl = ref(""); const codeUrl = ref("");
const loading = ref(false); const loading = ref(false);
@ -111,50 +110,50 @@ const registerRef = ref(ElForm);
const tenantList = ref<TenantVO[]>([]); const tenantList = ref<TenantVO[]>([]);
const handleRegister = () => { const handleRegister = () => {
registerRef.value.validate(async (valid: boolean) => { registerRef.value.validate(async (valid: boolean) => {
if (valid) { if (valid) {
loading.value = true; loading.value = true;
const [err] = await to(register(registerForm.value)); const [err] = await to(register(registerForm.value));
if (!err) { if (!err) {
const username = registerForm.value.username; const username = registerForm.value.username;
await ElMessageBox.alert("<font color='red'>恭喜你,您的账号 " + username + " 注册成功!</font>", "系统提示", { await ElMessageBox.alert("<font color='red'>恭喜你,您的账号 " + username + " 注册成功!</font>", "系统提示", {
dangerouslyUseHTMLString: true, dangerouslyUseHTMLString: true,
type: "success", type: "success",
}); });
await router.push("/login"); await router.push("/login");
} else { } else {
loading.value = false; loading.value = false;
if (captchaEnabled) { if (captchaEnabled) {
getCode(); getCode();
}
}
} }
}); }
}
});
} }
const getCode = async () => { const getCode = async () => {
const { data } = await getCodeImg(); const { data } = await getCodeImg();
captchaEnabled.value = data.captchaEnabled === undefined ? true : data.captchaEnabled; captchaEnabled.value = data.captchaEnabled === undefined ? true : data.captchaEnabled;
if (captchaEnabled.value) { if (captchaEnabled.value) {
codeUrl.value = "data:image/gif;base64," + data.img; codeUrl.value = "data:image/gif;base64," + data.img;
registerForm.value.uuid = data.uuid; registerForm.value.uuid = data.uuid;
} }
} }
const initTenantList = async () => { const initTenantList = async () => {
const { data } = await getTenantList(); const { data } = await getTenantList();
tenantEnabled.value = data.tenantEnabled === undefined ? true : data.tenantEnabled; tenantEnabled.value = data.tenantEnabled === undefined ? true : data.tenantEnabled;
if (tenantEnabled.value) { if (tenantEnabled.value) {
tenantList.value = data.voList; tenantList.value = data.voList;
if (tenantList.value != null && tenantList.value.length !== 0) { if (tenantList.value != null && tenantList.value.length !== 0) {
registerForm.value.tenantId = tenantList.value[0].tenantId; registerForm.value.tenantId = tenantList.value[0].tenantId;
}
} }
}
} }
onMounted(() => { onMounted(() => {
getCode(); getCode();
initTenantList(); initTenantList();
}) })
</script> </script>

View File

@ -0,0 +1,328 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="search" v-show="showSearch">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="100px">
<el-form-item label="客户端key" prop="clientKey">
<el-input v-model="queryParams.clientKey" placeholder="请输入客户端key" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="客户端秘钥" prop="clientSecret">
<el-input v-model="queryParams.clientSecret" 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_normal_disable" :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>
</div>
</transition>
<el-card shadow="never">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:client:add']">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:client:edit']">
修改
</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:client:remove']">
删除
</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:client:export']">导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="clientList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="id" align="center" prop="id" v-if="true" />
<el-table-column label="客户端id" align="center" prop="clientId" />
<el-table-column label="客户端key" align="center" prop="clientKey" />
<el-table-column label="客户端秘钥" align="center" prop="clientSecret" />
<el-table-column label="授权类型" align="center">
<template #default="scope">
<div>
<template v-for="(type, index) in scope.row.grantTypeList" :key="index">
<dict-tag class="el-check-tag" :options="sys_grant_type" :value="type" />
</template>
</div>
</template>
</el-table-column>
<el-table-column label="设备类型" align="center">
<template #default="scope">
<dict-tag :options="sys_device_type" :value="scope.row.deviceType" />
</template>
</el-table-column>
<el-table-column label="Token活跃超时时间" align="center" prop="activeTimeout" />
<el-table-column label="Token固定超时时间" align="center" prop="timeout" />
<el-table-column label="状态" align="center" key="status">
<template #default="scope">
<el-switch v-model="scope.row.status" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)"></el-switch>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="修改" placement="top">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:client:edit']"></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:client:remove']"></el-button>
</el-tooltip>
</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-card>
<!-- 添加或修改客户端管理对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
<el-form ref="clientFormRef" :model="form" :rules="rules" label-width="100px">
<el-form-item label="客户端key" prop="clientKey">
<el-input v-model="form.clientKey" :disabled="form.id != null" placeholder="请输入客户端key" />
</el-form-item>
<el-form-item label="客户端秘钥" prop="clientSecret">
<el-input v-model="form.clientSecret" :disabled="form.id != null" placeholder="请输入客户端秘钥" />
</el-form-item>
<el-form-item label="授权类型" prop="grantTypeList">
<el-select v-model="form.grantTypeList" multiple placeholder="请输入授权类型">
<el-option v-for="dict in sys_grant_type" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
</el-select>
</el-form-item>
<el-form-item label="设备类型" prop="deviceType">
<el-select v-model="form.deviceType" placeholder="请输入设备类型">
<el-option v-for="dict in sys_device_type" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
</el-select>
</el-form-item>
<el-form-item prop="activeTimeout" label-width="auto">
<template #label>
<span>
<el-tooltip content="指定时间无操作则过期单位默认30分钟1800秒" placement="top">
<el-icon><question-filled /></el-icon>
</el-tooltip>
Token活跃超时时间
</span>
</template>
<el-input v-model="form.activeTimeout" placeholder="请输入Token活跃超时时间" />
</el-form-item>
<el-form-item prop="timeout" label-width="auto">
<template #label>
<span>
<el-tooltip content="指定时间必定过期单位默认七天604800秒" placement="top">
<el-icon><question-filled /></el-icon>
</el-tooltip>
Token固定超时时间
</span>
</template>
<el-input v-model="form.timeout" placeholder="请输入Token固定超时时间" />
</el-form-item>
<el-form-item label="状态">
<el-radio-group v-model="form.status">
<el-radio v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.value">
{{ dict.label }}
</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="Client" lang="ts">
import { listClient, getClient, delClient, addClient, updateClient, changeStatus } from '@/api/system/client';
import { ClientVO, ClientQuery, ClientForm } from '@/api/system/client/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { sys_normal_disable } = toRefs<any>(proxy?.useDict("sys_normal_disable"));
const { sys_grant_type } = toRefs<any>(proxy?.useDict("sys_grant_type"));
const { sys_device_type } = toRefs<any>(proxy?.useDict("sys_device_type"));
const clientList = ref<ClientVO[]>([]);
const buttonLoading = ref(false);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref<Array<string | number>>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const queryFormRef = ref<ElFormInstance>();
const clientFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
const initFormData: ClientForm = {
id: undefined,
clientId: undefined,
clientKey: undefined,
clientSecret: undefined,
grantTypeList: undefined,
deviceType: undefined,
activeTimeout: undefined,
timeout: undefined,
status: undefined,
}
const data = reactive<PageData<ClientForm, ClientQuery>>({
form: {...initFormData},
queryParams: {
pageNum: 1,
pageSize: 10,
clientId: undefined,
clientKey: undefined,
clientSecret: undefined,
grantType: undefined,
deviceType: undefined,
activeTimeout: undefined,
timeout: undefined,
status: undefined,
},
rules: {
id: [
{ required: true, message: "id不能为空", trigger: "blur" }
],
clientId: [
{ required: true, message: "客户端id不能为空", trigger: "blur" }
],
clientKey: [
{ required: true, message: "客户端key不能为空", trigger: "blur" }
],
clientSecret: [
{ required: true, message: "客户端秘钥不能为空", trigger: "blur" }
],
grantTypeList: [
{ required: true, message: "授权类型不能为空", trigger: "change" }
],
deviceType: [
{ required: true, message: "设备类型不能为空", trigger: "change" }
],
}
});
const { queryParams, form, rules } = toRefs(data);
/** 查询客户端管理列表 */
const getList = async () => {
loading.value = true;
const res = await listClient(queryParams.value);
clientList.value = res.rows;
total.value = res.total;
loading.value = false;
}
/** 取消按钮 */
const cancel = () => {
reset();
dialog.visible = false;
}
/** 表单重置 */
const reset = () => {
form.value = {...initFormData};
clientFormRef.value?.resetFields();
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
handleQuery();
}
/** 多选框选中数据 */
const handleSelectionChange = (selection: ClientVO[]) => {
ids.value = selection.map(item => item.id);
single.value = selection.length != 1;
multiple.value = !selection.length;
}
/** 新增按钮操作 */
const handleAdd = () => {
reset();
dialog.visible = true;
dialog.title = "添加客户端管理";
}
/** 修改按钮操作 */
const handleUpdate = async (row?: ClientVO) => {
reset();
const _id = row?.id || ids.value[0]
const res = await getClient(_id);
Object.assign(form.value, res.data);
dialog.visible = true;
dialog.title = "修改客户端管理";
}
/** 提交按钮 */
const submitForm = () => {
clientFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
buttonLoading.value = true;
if (form.value.id) {
await updateClient(form.value).finally(() => buttonLoading.value = false);
} else {
await addClient(form.value).finally(() => buttonLoading.value = false);
}
proxy?.$modal.msgSuccess("修改成功");
dialog.visible = false;
await getList();
}
});
}
/** 删除按钮操作 */
const handleDelete = async (row?: ClientVO) => {
const _ids = row?.id || ids.value;
await proxy?.$modal.confirm('是否确认删除客户端管理编号为"' + _ids + '"的数据项?').finally(() => loading.value = false);
await delClient(_ids);
proxy?.$modal.msgSuccess("删除成功");
await getList();
}
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download('system/client/export', {
...queryParams.value
}, `client_${new Date().getTime()}.xlsx`)
}
/** 状态修改 */
const handleStatusChange = async (row: ClientVO) => {
let text = row.status === "0" ? "启用" : "停用"
try {
await proxy?.$modal.confirm('确认要"' + text + '"吗?');
await changeStatus(row.id, row.status);
proxy?.$modal.msgSuccess(text + "成功");
} catch (err) {
row.status = row.status === "0" ? "1" : "0";
}
}
onMounted(() => {
getList();
});
</script>

View File

@ -1,38 +1,40 @@
<template> <template>
<div class="p-2"> <div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="search" v-show="showSearch"> <div class="mb-[10px]" v-show="showSearch">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px"> <el-card shadow="hover">
<el-form-item label="参数名称" prop="configName"> <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
<el-input v-model="queryParams.configName" placeholder="请输入参数名称" clearable style="width: 240px" @keyup.enter="handleQuery" /> <el-form-item label="参数名称" prop="configName">
</el-form-item> <el-input v-model="queryParams.configName" placeholder="请输入参数名称" clearable style="width: 240px" @keyup.enter="handleQuery" />
<el-form-item label="参数键名" prop="configKey"> </el-form-item>
<el-input v-model="queryParams.configKey" placeholder="请输入参数键名" clearable style="width: 240px" @keyup.enter="handleQuery" /> <el-form-item label="参数键名" prop="configKey">
</el-form-item> <el-input v-model="queryParams.configKey" placeholder="请输入参数键名" clearable style="width: 240px" @keyup.enter="handleQuery" />
<el-form-item label="系统内置" prop="configType"> </el-form-item>
<el-select v-model="queryParams.configType" placeholder="系统内置" clearable> <el-form-item label="系统内置" prop="configType">
<el-option v-for="dict in sys_yes_no" :key="dict.value" :label="dict.label" :value="dict.value" /> <el-select v-model="queryParams.configType" placeholder="系统内置" clearable>
</el-select> <el-option v-for="dict in sys_yes_no" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-form-item> </el-select>
<el-form-item label="创建时间" style="width: 308px;"> </el-form-item>
<el-date-picker <el-form-item label="创建时间" style="width: 308px;">
v-model="dateRange" <el-date-picker
value-format="YYYY-MM-DD HH:mm:ss" v-model="dateRange"
type="daterange" value-format="YYYY-MM-DD HH:mm:ss"
range-separator="-" type="daterange"
start-placeholder="开始日期" range-separator="-"
end-placeholder="结束日期" start-placeholder="开始日期"
:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]" end-placeholder="结束日期"
></el-date-picker> :default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
</el-form-item> ></el-date-picker>
<el-form-item> </el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button> <el-form-item>
<el-button icon="Refresh" @click="resetQuery">重置</el-button> <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
</el-form-item> <el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form> </el-form-item>
</el-form>
</el-card>
</div> </div>
</transition> </transition>
<el-card shadow="never"> <el-card shadow="hover">
<template #header> <template #header>
<el-row :gutter="10" class="mb8"> <el-row :gutter="10" class="mb8">
<el-col :span="1.5"> <el-col :span="1.5">
@ -123,8 +125,6 @@
<script setup name="Config" lang="ts"> <script setup name="Config" lang="ts">
import { listConfig, getConfig, delConfig, addConfig, updateConfig, refreshCache } from "@/api/system/config"; import { listConfig, getConfig, delConfig, addConfig, updateConfig, refreshCache } from "@/api/system/config";
import { ConfigForm, ConfigQuery, ConfigVO } from "@/api/system/config/types"; import { ConfigForm, ConfigQuery, ConfigVO } from "@/api/system/config/types";
import { ComponentInternalInstance } from "vue";
import { DateModelType } from 'element-plus';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { sys_yes_no } = toRefs<any>(proxy?.useDict("sys_yes_no")); const { sys_yes_no } = toRefs<any>(proxy?.useDict("sys_yes_no"));
@ -138,124 +138,120 @@ const multiple = ref(true);
const total = ref(0); const total = ref(0);
const dateRange = ref<[DateModelType, DateModelType]>(['', '']); const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
const queryFormRef = ref(ElForm); const queryFormRef = ref<ElFormInstance>();
const configFormRef = ref(ElForm); const configFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({ const dialog = reactive<DialogOption>({
visible: false, visible: false,
title: '' title: ''
}); });
const initFormData: ConfigForm = { const initFormData: ConfigForm = {
configId: undefined, configId: undefined,
configName: '', configName: '',
configKey: '', configKey: '',
configValue: '', configValue: '',
configType: "Y", configType: "Y",
remark: '' remark: ''
} }
const data = reactive<PageData<ConfigForm, ConfigQuery>>({ const data = reactive<PageData<ConfigForm, ConfigQuery>>({
form: {...initFormData}, form: { ...initFormData },
queryParams: { queryParams: {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
configName: '', configName: '',
configKey: '', configKey: '',
configType: '', configType: '',
}, },
rules: { rules: {
configName: [{ required: true, message: "参数名称不能为空", trigger: "blur" }], configName: [{ required: true, message: "参数名称不能为空", trigger: "blur" }],
configKey: [{ required: true, message: "参数键名不能为空", trigger: "blur" }], configKey: [{ required: true, message: "参数键名不能为空", trigger: "blur" }],
configValue: [{ required: true, message: "参数键值不能为空", trigger: "blur" }] configValue: [{ required: true, message: "参数键值不能为空", trigger: "blur" }]
} }
}); });
const { queryParams, form, rules } = toRefs(data); const { queryParams, form, rules } = toRefs(data);
/** 查询参数列表 */ /** 查询参数列表 */
const getList = async () => { const getList = async () => {
loading.value = true; loading.value = true;
const res = await listConfig(proxy?.addDateRange(queryParams.value, dateRange.value)); const res = await listConfig(proxy?.addDateRange(queryParams.value, dateRange.value));
configList.value = res.rows; configList.value = res.rows;
total.value = res.total; total.value = res.total;
loading.value = false; loading.value = false;
} }
/** 取消按钮 */ /** 取消按钮 */
const cancel = () => { const cancel = () => {
reset(); reset();
dialog.visible = false; dialog.visible = false;
} }
/** 表单重置 */ /** 表单重置 */
const reset = () => { const reset = () => {
form.value = {...initFormData}; form.value = { ...initFormData };
configFormRef.value.resetFields(); configFormRef.value?.resetFields();
} }
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
queryParams.value.pageNum = 1; queryParams.value.pageNum = 1;
getList(); getList();
} }
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
dateRange.value = ['', '']; dateRange.value = ['', ''];
queryFormRef.value.resetFields(); queryFormRef.value?.resetFields();
handleQuery(); handleQuery();
} }
/** 多选框选中数据 */ /** 多选框选中数据 */
const handleSelectionChange = (selection: ConfigVO[]) => { const handleSelectionChange = (selection: ConfigVO[]) => {
ids.value = selection.map(item => item.configId); ids.value = selection.map(item => item.configId);
single.value = selection.length != 1; single.value = selection.length != 1;
multiple.value = !selection.length; multiple.value = !selection.length;
} }
/** 新增按钮操作 */ /** 新增按钮操作 */
const handleAdd = () => { const handleAdd = () => {
dialog.visible = true; reset();
dialog.title = "添加参数"; dialog.visible = true;
nextTick(() => { dialog.title = "添加参数";
reset();
})
} }
/** 修改按钮操作 */ /** 修改按钮操作 */
const handleUpdate = (row?: ConfigVO) => { const handleUpdate = async (row?: ConfigVO) => {
dialog.visible = true; reset();
dialog.title = "修改参数"; const configId = row?.configId || ids.value[0];
const configId = row?.configId || ids.value[0]; const res = await getConfig(configId);
nextTick(async () => { Object.assign(form.value, res.data);
reset(); dialog.visible = true;
const res = await getConfig(configId); dialog.title = "修改参数";
form.value = res.data;
})
} }
/** 提交按钮 */ /** 提交按钮 */
const submitForm = () => { const submitForm = () => {
configFormRef.value.validate(async (valid: boolean) => { configFormRef.value?.validate(async (valid: boolean) => {
if (valid) { if (valid) {
form.value.configId ? await updateConfig(form.value) : await addConfig(form.value); form.value.configId ? await updateConfig(form.value) : await addConfig(form.value);
proxy?.$modal.msgSuccess("操作成功"); proxy?.$modal.msgSuccess("操作成功");
dialog.visible = false; dialog.visible = false;
getList(); await getList();
} }
}); });
} }
/** 删除按钮操作 */ /** 删除按钮操作 */
const handleDelete = async (row?: ConfigVO) => { const handleDelete = async (row?: ConfigVO) => {
const configIds = row?.configId || ids.value; const configIds = row?.configId || ids.value;
await proxy?.$modal.confirm('是否确认删除参数编号为"' + configIds + '"的数据项?'); await proxy?.$modal.confirm('是否确认删除参数编号为"' + configIds + '"的数据项?');
await delConfig(configIds); await delConfig(configIds);
getList(); await getList();
proxy?.$modal.msgSuccess("删除成功"); proxy?.$modal.msgSuccess("删除成功");
} }
/** 导出按钮操作 */ /** 导出按钮操作 */
const handleExport = () => { const handleExport = () => {
proxy?.download("system/config/export", { proxy?.download("system/config/export", {
...queryParams.value ...queryParams.value
}, `config_${new Date().getTime()}.xlsx`); }, `config_${new Date().getTime()}.xlsx`);
} }
/** 刷新缓存按钮操作 */ /** 刷新缓存按钮操作 */
const handleRefreshCache = async () => { const handleRefreshCache = async () => {
await refreshCache(); await refreshCache();
proxy?.$modal.msgSuccess("刷新缓存成功"); proxy?.$modal.msgSuccess("刷新缓存成功");
} }
onMounted(() => { onMounted(() => {
getList(); getList();
}) })
</script> </script>

View File

@ -1,25 +1,27 @@
<template> <template>
<div class="p-2"> <div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="search" v-show="showSearch"> <div class="mb-[10px]" v-show="showSearch">
<el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px"> <el-card shadow="hover">
<el-form-item label="菜单名称" prop="menuName"> <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px">
<el-input v-model="queryParams.deptName" placeholder="请输入部门名称" clearable @keyup.enter="handleQuery" /> <el-form-item label="部门名称" prop="deptName">
</el-form-item> <el-input v-model="queryParams.deptName" placeholder="请输入部门名称" clearable @keyup.enter="handleQuery" />
<el-form-item label="状态" prop="status"> </el-form-item>
<el-select v-model="queryParams.status" placeholder="部门状态" clearable> <el-form-item label="状态" prop="status">
<el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" /> <el-select v-model="queryParams.status" placeholder="部门状态" clearable>
</el-select> <el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-form-item> </el-select>
<el-form-item> </el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button> <el-form-item>
<el-button icon="Refresh" @click="resetQuery">重置</el-button> <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
</el-form-item> <el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form> </el-form-item>
</el-form>
</el-card>
</div> </div>
</transition> </transition>
<el-card shadow="never"> <el-card shadow="hover">
<template #header> <template #header>
<el-row :gutter="10"> <el-row :gutter="10">
<el-col :span="1.5"> <el-col :span="1.5">
@ -130,13 +132,12 @@
<script setup name="Dept" lang="ts"> <script setup name="Dept" lang="ts">
import { listDept, getDept, delDept, addDept, updateDept, listDeptExcludeChild } from "@/api/system/dept" import { listDept, getDept, delDept, addDept, updateDept, listDeptExcludeChild } from "@/api/system/dept"
import { ComponentInternalInstance } from 'vue';
import { DeptForm, DeptQuery, DeptVO } from "@/api/system/dept/types"; import { DeptForm, DeptQuery, DeptVO } from "@/api/system/dept/types";
interface DeptOptionsType { interface DeptOptionsType {
deptId: number | string; deptId: number | string;
deptName: string; deptName: string;
children: DeptOptionsType[]; children: DeptOptionsType[];
} }
@ -151,142 +152,143 @@ const isExpandAll = ref(true)
const dialog = reactive<DialogOption>({ const dialog = reactive<DialogOption>({
visible: false, visible: false,
title: '' title: ''
}); });
const deptTableRef = ref(ElTable); const deptTableRef = ref<ElTableInstance>();
const queryFormRef = ref(ElForm); const queryFormRef = ref<ElFormInstance>();
const deptFormRef = ref(ElForm); const deptFormRef = ref<ElFormInstance>();
const initFormData: DeptForm = { const initFormData: DeptForm = {
deptId: undefined, deptId: undefined,
parentId: undefined, parentId: undefined,
deptName: undefined, deptName: undefined,
orderNum: 0, orderNum: 0,
leader: undefined, leader: undefined,
phone: undefined, phone: undefined,
email: undefined, email: undefined,
status: "0" status: "0"
} }
const data = reactive<PageData<DeptForm, DeptQuery>>({ const data = reactive<PageData<DeptForm, DeptQuery>>({
form: {...initFormData}, form: { ...initFormData },
queryParams: { queryParams: {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
deptName: undefined, deptName: undefined,
status: undefined status: undefined
}, },
rules: { rules: {
parentId: [{ required: true, message: "上级部门不能为空", trigger: "blur" }], parentId: [{ required: true, message: "上级部门不能为空", trigger: "blur" }],
deptName: [{ required: true, message: "部门名称不能为空", trigger: "blur" }], deptName: [{ required: true, message: "部门名称不能为空", trigger: "blur" }],
orderNum: [{ required: true, message: "显示排序不能为空", trigger: "blur" }], orderNum: [{ required: true, message: "显示排序不能为空", trigger: "blur" }],
email: [{ type: "email", message: "请输入正确的邮箱地址", trigger: ["blur", "change"] }], email: [{ type: "email", message: "请输入正确的邮箱地址", trigger: ["blur", "change"] }],
phone: [{ pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: "请输入正确的手机号码", trigger: "blur" }] phone: [{ pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: "请输入正确的手机号码", trigger: "blur" }]
}, },
}) })
const { queryParams, form, rules } = toRefs<PageData<DeptForm, DeptQuery>>(data) const { queryParams, form, rules } = toRefs<PageData<DeptForm, DeptQuery>>(data)
/** 查询菜单列表 */ /** 查询菜单列表 */
const getList = async () => { const getList = async () => {
loading.value = true; loading.value = true;
const res = await listDept(queryParams.value); const res = await listDept(queryParams.value);
const data = proxy?.handleTree<DeptVO>(res.data, "deptId") const data = proxy?.handleTree<DeptVO>(res.data, "deptId")
if (data) { if (data) {
deptList.value = data deptList.value = data
} }
loading.value = false loading.value = false
} }
/** 取消按钮 */ /** 取消按钮 */
const cancel = () => { const cancel = () => {
reset() reset()
dialog.visible = false dialog.visible = false
} }
/** 表单重置 */ /** 表单重置 */
const reset = () => { const reset = () => {
form.value = {...initFormData}; form.value = { ...initFormData };
deptFormRef.value.resetFields(); deptFormRef.value?.resetFields();
} }
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
getList(); getList();
} }
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
queryFormRef.value.resetFields(); queryFormRef.value?.resetFields();
handleQuery() handleQuery()
}
/** 新增按钮操作 */
const handleAdd = (row?: DeptVO) => {
listDept().then(res => {
const data = proxy?.handleTree<DeptOptionsType>(res.data, "deptId");
if (data) {
deptOptions.value = data
dialog.visible = true;
dialog.title = "添加部门";
nextTick(() => {
reset();
if (row && row.deptId) {
form.value.parentId = row?.deptId;
}
})
}
})
} }
/** 展开/折叠操作 */ /** 展开/折叠操作 */
const handleToggleExpandAll = () => { const handleToggleExpandAll = () => {
isExpandAll.value = !isExpandAll.value; isExpandAll.value = !isExpandAll.value;
toggleExpandAll(deptList.value, isExpandAll.value) toggleExpandAll(deptList.value, isExpandAll.value)
} }
/** 展开/折叠所有 */ /** 展开/折叠所有 */
const toggleExpandAll = (data: DeptVO[], status: boolean) => { const toggleExpandAll = (data: DeptVO[], status: boolean) => {
data.forEach((item) => { data.forEach((item) => {
deptTableRef.value.toggleRowExpansion(item, status) deptTableRef.value?.toggleRowExpansion(item, status)
if(item.children && item.children.length > 0) toggleExpandAll(item.children, status) if (item.children && item.children.length > 0) toggleExpandAll(item.children, status)
}) })
}
/** 新增按钮操作 */
const handleAdd = async (row?: DeptVO) => {
reset();
const res = await listDept();
const data = proxy?.handleTree<DeptOptionsType>(res.data, "deptId");
if (data) {
deptOptions.value = data
if (row && row.deptId) {
form.value.parentId = row?.deptId;
}
dialog.visible = true;
dialog.title = "添加部门";
}
} }
/** 修改按钮操作 */ /** 修改按钮操作 */
const handleUpdate = async (row: DeptVO) => { const handleUpdate = async (row: DeptVO) => {
const res = await getDept(row.deptId); reset();
dialog.visible = true; const res = await getDept(row.deptId);
dialog.title = "修改部门"; form.value = res.data
nextTick(async () => { const response = await listDeptExcludeChild(row.deptId);
reset(); const data = proxy?.handleTree<DeptOptionsType>(response.data, "deptId")
form.value = res.data if (data) {
const response = await listDeptExcludeChild(row.deptId); deptOptions.value = data;
const data = proxy?.handleTree<DeptOptionsType>(response.data, "deptId") if (data.length === 0) {
if (data) { const noResultsOptions: DeptOptionsType = {
deptOptions.value = data; deptId: res.data.parentId,
if (data.length === 0) { deptName: res.data.parentName,
const noResultsOptions: DeptOptionsType = { deptId: res.data.parentId, deptName: res.data.parentName, children: [] }; children: []
deptOptions.value.push(noResultsOptions); };
} deptOptions.value.push(noResultsOptions);
} }
}) }
dialog.visible = true;
dialog.title = "修改部门";
} }
/** 提交按钮 */ /** 提交按钮 */
const submitForm = () => { const submitForm = () => {
deptFormRef.value.validate(async (valid: boolean) => { deptFormRef.value?.validate(async (valid: boolean) => {
if (valid) { if (valid) {
form.value.deptId ? await updateDept(form.value) : await addDept(form.value); form.value.deptId ? await updateDept(form.value) : await addDept(form.value);
proxy?.$modal.msgSuccess("操作成功"); proxy?.$modal.msgSuccess("操作成功");
dialog.visible = false; dialog.visible = false;
getList(); await getList();
} }
}) })
} }
/** 删除按钮操作 */ /** 删除按钮操作 */
const handleDelete = async (row: DeptVO) => { const handleDelete = async (row: DeptVO) => {
await proxy?.$modal.confirm('是否确认删除名称为"' + row.deptName + '"的数据项?'); await proxy?.$modal.confirm('是否确认删除名称为"' + row.deptName + '"的数据项?');
await delDept(row.deptId); await delDept(row.deptId);
getList(); await getList();
proxy?.$modal.msgSuccess("删除成功"); proxy?.$modal.msgSuccess("删除成功");
} }
onMounted(() => { onMounted(() => {
getList(); getList();
}); });
</script> </script>

View File

@ -1,29 +1,31 @@
<template> <template>
<div class="p-2"> <div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="search" v-show="showSearch"> <div class="mb-[10px]" v-show="showSearch">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px"> <el-card shadow="hover">
<el-form-item label="字典名称" prop="dictType"> <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
<el-select v-model="queryParams.dictType" style="width: 200px"> <el-form-item label="字典名称" prop="dictType">
<el-option v-for="item in typeOptions" :key="item.dictId" :label="item.dictName" :value="item.dictType" /> <el-select v-model="queryParams.dictType" style="width: 200px">
</el-select> <el-option v-for="item in typeOptions" :key="item.dictId" :label="item.dictName" :value="item.dictType" />
</el-form-item> </el-select>
<el-form-item label="字典标签" prop="dictLabel"> </el-form-item>
<el-input v-model="queryParams.dictLabel" placeholder="请输入字典标签" clearable style="width: 200px" @keyup.enter="handleQuery" /> <el-form-item label="字典标签" prop="dictLabel">
</el-form-item> <el-input v-model="queryParams.dictLabel" placeholder="请输入字典标签" clearable style="width: 200px" @keyup.enter="handleQuery" />
<el-form-item label="状态" prop="status"> </el-form-item>
<el-select v-model="queryParams.status" placeholder="数据状态" clearable style="width: 200px"> <el-form-item label="状态" prop="status">
<el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" /> <el-select v-model="queryParams.status" placeholder="数据状态" clearable style="width: 200px">
</el-select> <el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-form-item> </el-select>
<el-form-item> </el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button> <el-form-item>
<el-button icon="Refresh" @click="resetQuery">重置</el-button> <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
</el-form-item> <el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form> </el-form-item>
</el-form>
</el-card>
</div> </div>
</transition> </transition>
<el-card shadow="never"> <el-card shadow="hover">
<template #header> <template #header>
<el-row :gutter="10" class="mb8"> <el-row :gutter="10" class="mb8">
<el-col :span="1.5"> <el-col :span="1.5">
@ -136,9 +138,7 @@ import useDictStore from '@/store/modules/dict'
import { optionselect as getDictOptionselect, getType } from "@/api/system/dict/type"; import { optionselect as getDictOptionselect, getType } from "@/api/system/dict/type";
import { listData, getData, delData, addData, updateData } from "@/api/system/dict/data"; import { listData, getData, delData, addData, updateData } from "@/api/system/dict/data";
import { DictTypeVO } from '@/api/system/dict/type/types'; import { DictTypeVO } from '@/api/system/dict/type/types';
import { ComponentInternalInstance } from "vue";
import { DictDataForm, DictDataQuery, DictDataVO } from "@/api/system/dict/data/types"; import { DictDataForm, DictDataQuery, DictDataVO } from "@/api/system/dict/data/types";
import { ElForm } from 'element-plus';
const { proxy } = getCurrentInstance() as ComponentInternalInstance const { proxy } = getCurrentInstance() as ComponentInternalInstance
const { sys_normal_disable } = toRefs<any>(proxy?.useDict("sys_normal_disable")); const { sys_normal_disable } = toRefs<any>(proxy?.useDict("sys_normal_disable"));
@ -154,159 +154,155 @@ const total = ref(0);
const defaultDictType = ref(""); const defaultDictType = ref("");
const typeOptions = ref<DictTypeVO[]>([]); const typeOptions = ref<DictTypeVO[]>([]);
const dataFormRef = ref(ElForm); const dataFormRef = ref<ElFormInstance>();
const queryFormRef = ref(ElForm); const queryFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({ const dialog = reactive<DialogOption>({
visible: false, visible: false,
title: '' title: ''
}); });
// //
const listClassOptions = ref<Array<{ value: string, label: string }>>([ const listClassOptions = ref<Array<{ value: string, label: string }>>([
{ value: "default", label: "默认" }, { value: "default", label: "默认" },
{ value: "primary", label: "主要" }, { value: "primary", label: "主要" },
{ value: "success", label: "成功" }, { value: "success", label: "成功" },
{ value: "info", label: "信息" }, { value: "info", label: "信息" },
{ value: "warning", label: "警告" }, { value: "warning", label: "警告" },
{ value: "danger", label: "危险" } { value: "danger", label: "危险" }
]); ]);
const initFormData: DictDataForm = { const initFormData: DictDataForm = {
dictCode: undefined, dictCode: undefined,
dictLabel: '', dictLabel: '',
dictValue: '', dictValue: '',
cssClass: '', cssClass: '',
listClass: "default", listClass: "default",
dictSort: 0, dictSort: 0,
status: "0", status: "0",
remark: '' remark: ''
} }
const data = reactive<PageData<DictDataForm, DictDataQuery>>({ const data = reactive<PageData<DictDataForm, DictDataQuery>>({
form: { ...initFormData }, form: { ...initFormData },
queryParams: { queryParams: {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
dictName: '', dictName: '',
dictType: '', dictType: '',
status: '', status: '',
dictLabel: '' dictLabel: ''
}, },
rules: { rules: {
dictLabel: [{ required: true, message: "数据标签不能为空", trigger: "blur" }], dictLabel: [{ required: true, message: "数据标签不能为空", trigger: "blur" }],
dictValue: [{ required: true, message: "数据键值不能为空", trigger: "blur" }], dictValue: [{ required: true, message: "数据键值不能为空", trigger: "blur" }],
dictSort: [{ required: true, message: "数据顺序不能为空", trigger: "blur" }] dictSort: [{ required: true, message: "数据顺序不能为空", trigger: "blur" }]
} }
}); });
const { queryParams, form, rules } = toRefs(data); const { queryParams, form, rules } = toRefs(data);
/** 查询字典类型详细 */ /** 查询字典类型详细 */
const getTypes = async (dictId: string | number) => { const getTypes = async (dictId: string | number) => {
const { data } = await getType(dictId); const { data } = await getType(dictId);
queryParams.value.dictType = data.dictType; queryParams.value.dictType = data.dictType;
defaultDictType.value = data.dictType; defaultDictType.value = data.dictType;
getList(); getList();
} }
/** 查询字典类型列表 */ /** 查询字典类型列表 */
const getTypeList = async () => { const getTypeList = async () => {
const res = await getDictOptionselect() const res = await getDictOptionselect()
typeOptions.value = res.data; typeOptions.value = res.data;
} }
/** 查询字典数据列表 */ /** 查询字典数据列表 */
const getList = async () => { const getList = async () => {
loading.value = true; loading.value = true;
const res = await listData(queryParams.value); const res = await listData(queryParams.value);
dataList.value = res.rows; dataList.value = res.rows;
total.value = res.total; total.value = res.total;
loading.value = false; loading.value = false;
} }
/** 取消按钮 */ /** 取消按钮 */
const cancel = () => { const cancel = () => {
dialog.visible = false; dialog.visible = false;
reset(); reset();
} }
/** 表单重置 */ /** 表单重置 */
const reset = () => { const reset = () => {
form.value = { ...initFormData }; form.value = { ...initFormData };
dataFormRef.value.resetFields(); dataFormRef.value?.resetFields();
} }
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
queryParams.value.pageNum = 1; queryParams.value.pageNum = 1;
getList(); getList();
} }
/** 返回按钮操作 */ /** 返回按钮操作 */
const handleClose = () => { const handleClose = () => {
const obj = { path: "/system/dict" }; const obj = { path: "/system/dict" };
proxy?.$tab.closeOpenPage(obj); proxy?.$tab.closeOpenPage(obj);
} }
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
queryFormRef.value.resetFields(); queryFormRef.value?.resetFields();
queryParams.value.dictType = defaultDictType.value; queryParams.value.dictType = defaultDictType.value;
handleQuery(); handleQuery();
} }
/** 新增按钮操作 */ /** 新增按钮操作 */
const handleAdd = () => { const handleAdd = () => {
dialog.visible = true; reset();
dialog.title = "添加字典数据"; form.value.dictType = queryParams.value.dictType;
nextTick(() => { dialog.visible = true;
reset(); dialog.title = "添加字典数据";
form.value.dictType = queryParams.value.dictType;
})
} }
/** 多选框选中数据 */ /** 多选框选中数据 */
const handleSelectionChange = (selection: DictDataVO[]) => { const handleSelectionChange = (selection: DictDataVO[]) => {
ids.value = selection.map(item => item.dictCode); ids.value = selection.map(item => item.dictCode);
single.value = selection.length != 1; single.value = selection.length != 1;
multiple.value = !selection.length; multiple.value = !selection.length;
} }
/** 修改按钮操作 */ /** 修改按钮操作 */
const handleUpdate = (row?: DictDataVO) => { const handleUpdate = async (row?: DictDataVO) => {
const dictCode = row?.dictCode || ids.value[0]; reset();
dialog.visible = true; const dictCode = row?.dictCode || ids.value[0];
dialog.title = "修改字典数据"; const res = await getData(dictCode);
nextTick(async () => { Object.assign(form.value, res.data);
const res = await getData(dictCode); dialog.visible = true;
reset(); dialog.title = "修改字典数据";
form.value = res.data;
})
} }
/** 提交按钮 */ /** 提交按钮 */
const submitForm = () => { const submitForm = () => {
dataFormRef.value.validate(async (valid: boolean) => { dataFormRef.value?.validate(async (valid: boolean) => {
if (valid) { if (valid) {
form.value.dictCode ? await updateData(form.value) : await addData(form.value); form.value.dictCode ? await updateData(form.value) : await addData(form.value);
useDictStore().removeDict(queryParams.value.dictType); useDictStore().removeDict(queryParams.value.dictType);
proxy?.$modal.msgSuccess("操作成功"); proxy?.$modal.msgSuccess("操作成功");
dialog.visible = false; dialog.visible = false;
getList(); await getList();
} }
}); });
} }
/** 删除按钮操作 */ /** 删除按钮操作 */
const handleDelete = async (row?: DictDataVO) => { const handleDelete = async (row?: DictDataVO) => {
const dictCodes = row?.dictCode || ids.value; const dictCodes = row?.dictCode || ids.value;
await proxy?.$modal.confirm('是否确认删除字典编码为"' + dictCodes + '"的数据项?'); await proxy?.$modal.confirm('是否确认删除字典编码为"' + dictCodes + '"的数据项?');
await delData(dictCodes); await delData(dictCodes);
getList(); await getList();
proxy?.$modal.msgSuccess("删除成功"); proxy?.$modal.msgSuccess("删除成功");
useDictStore().removeDict(queryParams.value.dictType); useDictStore().removeDict(queryParams.value.dictType);
} }
/** 导出按钮操作 */ /** 导出按钮操作 */
const handleExport = () => { const handleExport = () => {
proxy?.download("system/dict/data/export", { proxy?.download("system/dict/data/export", {
...queryParams.value ...queryParams.value
}, `dict_data_${new Date().getTime()}.xlsx`); }, `dict_data_${new Date().getTime()}.xlsx`);
} }
onMounted(() => { onMounted(() => {
getTypes(route.params && route.params.dictId as string); getTypes(route.params && route.params.dictId as string);
getTypeList(); getTypeList();
}) })
</script> </script>

View File

@ -1,38 +1,40 @@
<template> <template>
<div class="p-2"> <div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="search" v-show="showSearch"> <div class="mb-[10px]" v-show="showSearch">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px"> <el-card shadow="hover">
<el-form-item label="字典名称" prop="dictName"> <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
<el-input v-model="queryParams.dictName" placeholder="请输入字典名称" clearable style="width: 240px" @keyup.enter="handleQuery" /> <el-form-item label="字典名称" prop="dictName">
</el-form-item> <el-input v-model="queryParams.dictName" placeholder="请输入字典名称" clearable style="width: 240px" @keyup.enter="handleQuery" />
<el-form-item label="字典类型" prop="dictType"> </el-form-item>
<el-input v-model="queryParams.dictType" placeholder="请输入字典类型" clearable style="width: 240px" @keyup.enter="handleQuery" /> <el-form-item label="字典类型" prop="dictType">
</el-form-item> <el-input v-model="queryParams.dictType" placeholder="请输入字典类型" clearable style="width: 240px" @keyup.enter="handleQuery" />
<el-form-item label="状态" prop="status"> </el-form-item>
<el-select v-model="queryParams.status" placeholder="字典状态" clearable style="width: 240px"> <el-form-item label="状态" prop="status">
<el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" /> <el-select v-model="queryParams.status" placeholder="字典状态" clearable style="width: 240px">
</el-select> <el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-form-item> </el-select>
<el-form-item label="创建时间" style="width: 308px"> </el-form-item>
<el-date-picker <el-form-item label="创建时间" style="width: 308px">
v-model="dateRange" <el-date-picker
value-format="YYYY-MM-DD HH:mm:ss" v-model="dateRange"
type="daterange" value-format="YYYY-MM-DD HH:mm:ss"
range-separator="-" type="daterange"
start-placeholder="开始日期" range-separator="-"
end-placeholder="结束日期" start-placeholder="开始日期"
:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]" end-placeholder="结束日期"
></el-date-picker> :default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
</el-form-item> ></el-date-picker>
<el-form-item> </el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button> <el-form-item>
<el-button icon="Refresh" @click="resetQuery">重置</el-button> <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
</el-form-item> <el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form> </el-form-item>
</el-form>
</el-card>
</div> </div>
</transition> </transition>
<el-card shadow="never"> <el-card shadow="hover">
<template #header> <template #header>
<el-row :gutter="10" class="mb8"> <el-row :gutter="10" class="mb8">
<el-col :span="1.5"> <el-col :span="1.5">
@ -123,9 +125,7 @@
<script setup name="Dict" lang="ts"> <script setup name="Dict" lang="ts">
import useDictStore from '@/store/modules/dict' import useDictStore from '@/store/modules/dict'
import { listType, getType, delType, addType, updateType, refreshCache } from "@/api/system/dict/type"; import { listType, getType, delType, addType, updateType, refreshCache } from "@/api/system/dict/type";
import { ComponentInternalInstance } from "vue";
import { DictTypeForm, DictTypeQuery, DictTypeVO } from "@/api/system/dict/type/types"; import { DictTypeForm, DictTypeQuery, DictTypeVO } from "@/api/system/dict/type/types";
import { DateModelType } from 'element-plus';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { sys_normal_disable } = toRefs<any>(proxy?.useDict("sys_normal_disable")) const { sys_normal_disable } = toRefs<any>(proxy?.useDict("sys_normal_disable"))
@ -139,128 +139,123 @@ const multiple = ref(true);
const total = ref(0); const total = ref(0);
const dateRange = ref<[DateModelType, DateModelType]>(['', '']); const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
const dictFormRef = ref(ElForm); const dictFormRef = ref<ElFormInstance>();
const queryFormRef = ref(ElForm); const queryFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({ const dialog = reactive<DialogOption>({
visible: false, visible: false,
title: '' title: ''
}); });
const initFormData: DictTypeForm = { const initFormData: DictTypeForm = {
dictId: undefined, dictId: undefined,
dictName: '', dictName: '',
dictType: '', dictType: '',
status: "0", status: "0",
remark: '' remark: ''
} }
const data = reactive<PageData<DictTypeForm, DictTypeQuery>>({ const data = reactive<PageData<DictTypeForm, DictTypeQuery>>({
form: {...initFormData}, form: { ...initFormData },
queryParams: { queryParams: {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
dictName: '', dictName: '',
dictType: '', dictType: '',
status: '' status: ''
}, },
rules: { rules: {
dictName: [{ required: true, message: "字典名称不能为空", trigger: "blur" }], dictName: [{ required: true, message: "字典名称不能为空", trigger: "blur" }],
dictType: [{ required: true, message: "字典类型不能为空", trigger: "blur" }] dictType: [{ required: true, message: "字典类型不能为空", trigger: "blur" }]
}, },
}); });
const { queryParams, form, rules } = toRefs(data); const { queryParams, form, rules } = toRefs(data);
/** 查询字典类型列表 */ /** 查询字典类型列表 */
const getList = () => { const getList = () => {
loading.value = true; loading.value = true;
listType(proxy?.addDateRange(queryParams.value, dateRange.value)).then(res => { listType(proxy?.addDateRange(queryParams.value, dateRange.value)).then(res => {
typeList.value = res.rows; typeList.value = res.rows;
total.value = res.total; total.value = res.total;
loading.value = false; loading.value = false;
}); });
} }
/** 取消按钮 */ /** 取消按钮 */
const cancel = () => { const cancel = () => {
reset(); reset();
dialog.visible = false; dialog.visible = false;
} }
/** 表单重置 */ /** 表单重置 */
const reset = () => { const reset = () => {
form.value = {...initFormData}; form.value = { ...initFormData };
dictFormRef.value.resetFields(); dictFormRef.value?.resetFields();
} }
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
queryParams.value.pageNum = 1; queryParams.value.pageNum = 1;
getList(); getList();
} }
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
dateRange.value = ['', '']; dateRange.value = ['', ''];
queryFormRef.value.resetFields(); queryFormRef.value?.resetFields();
handleQuery(); handleQuery();
} }
/** 新增按钮操作 */ /** 新增按钮操作 */
const handleAdd = () => { const handleAdd = () => {
dialog.visible = true; reset();
dialog.title = "添加字典类型"; dialog.visible = true;
nextTick(() => { dialog.title = "添加字典类型";
reset();
})
} }
/** 多选框选中数据 */ /** 多选框选中数据 */
const handleSelectionChange = (selection: DictTypeVO[]) => { const handleSelectionChange = (selection: DictTypeVO[]) => {
ids.value = selection.map(item => item.dictId); ids.value = selection.map(item => item.dictId);
single.value = selection.length != 1; single.value = selection.length != 1;
multiple.value = !selection.length; multiple.value = !selection.length;
} }
/** 修改按钮操作 */ /** 修改按钮操作 */
const handleUpdate = (row?: DictTypeVO) => { const handleUpdate = async (row?: DictTypeVO) => {
dialog.visible = true; reset();
dialog.title = "修改字典类型"; const dictId = row?.dictId || ids.value[0];
const dictId = row?.dictId || ids.value[0]; const res = await getType(dictId);
nextTick(async () => { Object.assign(form.value, res.data);
reset(); dialog.visible = true;
const res = await getType(dictId); dialog.title = "修改字典类型";
form.value = res.data;
})
} }
/** 提交按钮 */ /** 提交按钮 */
const submitForm = () => { const submitForm = () => {
dictFormRef.value.validate(async (valid: boolean) => { dictFormRef.value?.validate(async (valid: boolean) => {
if (valid) { if (valid) {
form.value.dictId ? await updateType(form.value) : await addType(form.value); form.value.dictId ? await updateType(form.value) : await addType(form.value);
proxy?.$modal.msgSuccess("操作成功"); proxy?.$modal.msgSuccess("操作成功");
dialog.visible = false; dialog.visible = false;
getList(); getList();
} }
}); });
} }
/** 删除按钮操作 */ /** 删除按钮操作 */
const handleDelete = async (row?: DictTypeVO) => { const handleDelete = async (row?: DictTypeVO) => {
const dictIds = row?.dictId || ids.value; const dictIds = row?.dictId || ids.value;
await proxy?.$modal.confirm('是否确认删除字典编号为"' + dictIds + '"的数据项?'); await proxy?.$modal.confirm('是否确认删除字典编号为"' + dictIds + '"的数据项?');
await delType(dictIds); await delType(dictIds);
getList(); getList();
proxy?.$modal.msgSuccess("删除成功"); proxy?.$modal.msgSuccess("删除成功");
} }
/** 导出按钮操作 */ /** 导出按钮操作 */
const handleExport = () => { const handleExport = () => {
proxy?.download("system/dict/type/export", { proxy?.download("system/dict/type/export", {
...queryParams.value ...queryParams.value
}, `dict_${new Date().getTime()}.xlsx`); }, `dict_${new Date().getTime()}.xlsx`);
} }
/** 刷新缓存按钮操作 */ /** 刷新缓存按钮操作 */
const handleRefreshCache = async () => { const handleRefreshCache = async () => {
await refreshCache(); await refreshCache();
proxy?.$modal.msgSuccess("刷新成功"); proxy?.$modal.msgSuccess("刷新成功");
useDictStore().cleanDict(); useDictStore().cleanDict();
} }
onMounted(()=>{ onMounted(() => {
getList(); getList();
}) })
</script> </script>

View File

@ -1,25 +1,27 @@
<template> <template>
<div class="p-2"> <div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="search" v-show="showSearch"> <div class="mb-[10px]" v-show="showSearch">
<el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px"> <el-card shadow="hover">
<el-form-item label="菜单名称" prop="menuName"> <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px">
<el-input v-model="queryParams.menuName" placeholder="请输入菜单名称" clearable @keyup.enter="handleQuery" /> <el-form-item label="菜单名称" prop="menuName">
</el-form-item> <el-input v-model="queryParams.menuName" placeholder="请输入菜单名称" clearable @keyup.enter="handleQuery" />
<el-form-item label="状态" prop="status"> </el-form-item>
<el-select v-model="queryParams.status" placeholder="菜单状态" clearable> <el-form-item label="状态" prop="status">
<el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" /> <el-select v-model="queryParams.status" placeholder="菜单状态" clearable>
</el-select> <el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-form-item> </el-select>
<el-form-item> </el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button> <el-form-item>
<el-button icon="Refresh" @click="resetQuery">重置</el-button> <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
</el-form-item> <el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form> </el-form-item>
</el-form>
</el-card>
</div> </div>
</transition> </transition>
<el-card shadow="never"> <el-card shadow="hover">
<template #header> <template #header>
<el-row :gutter="10"> <el-row :gutter="10">
<el-col :span="1.5"> <el-col :span="1.5">
@ -37,7 +39,6 @@
:data="menuList" :data="menuList"
row-key="menuId" row-key="menuId"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }" :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
border
ref="menuTableRef" ref="menuTableRef"
:default-expand-all="isExpandAll" :default-expand-all="isExpandAll"
> >
@ -180,7 +181,7 @@
</el-col> </el-col>
<el-col :span="12" v-if="form.menuType === 'C'"> <el-col :span="12" v-if="form.menuType === 'C'">
<el-form-item> <el-form-item>
<el-input v-model="form.query" placeholder="请输入路由参数" maxlength="255" /> <el-input v-model="form.queryParam" placeholder="请输入路由参数" maxlength="255" />
<template #label> <template #label>
<span> <span>
<el-tooltip content='访问路由的默认传递参数,如:`{"id": 1, "name": "ry"}`' placement="top"> <el-tooltip content='访问路由的默认传递参数,如:`{"id": 1, "name": "ry"}`' placement="top">
@ -262,14 +263,12 @@
<script setup name="Menu" lang="ts"> <script setup name="Menu" lang="ts">
import { addMenu, delMenu, getMenu, listMenu, updateMenu } from '@/api/system/menu'; import { addMenu, delMenu, getMenu, listMenu, updateMenu } from '@/api/system/menu';
import { MenuForm, MenuQuery, MenuVO } from '@/api/system/menu/types'; import { MenuForm, MenuQuery, MenuVO } from '@/api/system/menu/types';
import { ComponentInternalInstance } from 'vue';
import { MenuTypeEnum } from '@/enums/MenuTypeEnum'; import { MenuTypeEnum } from '@/enums/MenuTypeEnum';
import { ElTable, ElForm } from 'element-plus';
interface MenuOptionsType { interface MenuOptionsType {
menuId: number; menuId: number;
menuName: string; menuName: string;
children: MenuOptionsType[] | undefined; children: MenuOptionsType[] | undefined;
} }
const { proxy } = getCurrentInstance() as ComponentInternalInstance const { proxy } = getCurrentInstance() as ComponentInternalInstance
@ -282,136 +281,130 @@ const menuOptions = ref<MenuOptionsType[]>([])
const isExpandAll = ref(false) const isExpandAll = ref(false)
const dialog = reactive<DialogOption>({ const dialog = reactive<DialogOption>({
visible: false, visible: false,
title: '' title: ''
}); });
const queryFormRef = ref(ElForm); const queryFormRef = ref<ElFormInstance>();
const menuFormRef = ref(ElForm); const menuFormRef = ref<ElFormInstance>();
const initFormData = { const initFormData = {
path: '', path: '',
menuId: undefined, menuId: undefined,
parentId: 0, parentId: 0,
menuName: '', menuName: '',
icon: '', icon: '',
menuType: MenuTypeEnum.M, menuType: MenuTypeEnum.M,
orderNum: 1, orderNum: 1,
isFrame: "1", isFrame: "1",
isCache: "0", isCache: "0",
visible: "0", visible: "0",
status: "0" status: "0"
} }
const data = reactive<PageData<MenuForm, MenuQuery>>({ const data = reactive<PageData<MenuForm, MenuQuery>>({
form: { ...initFormData }, form: { ...initFormData },
queryParams: { queryParams: {
menuName: undefined, menuName: undefined,
status: undefined status: undefined
}, },
rules: { rules: {
menuName: [{ required: true, message: "菜单名称不能为空", trigger: "blur" }], menuName: [{ required: true, message: "菜单名称不能为空", trigger: "blur" }],
orderNum: [{ required: true, message: "菜单顺序不能为空", trigger: "blur" }], orderNum: [{ required: true, message: "菜单顺序不能为空", trigger: "blur" }],
path: [{ required: true, message: "路由地址不能为空", trigger: "blur" }] path: [{ required: true, message: "路由地址不能为空", trigger: "blur" }]
}, },
}) })
const menuTableRef = ref(ElTable); const menuTableRef = ref<ElTableInstance>();
const { queryParams, form, rules } = toRefs<PageData<MenuForm, MenuQuery>>(data) const { queryParams, form, rules } = toRefs<PageData<MenuForm, MenuQuery>>(data)
/** 查询菜单列表 */ /** 查询菜单列表 */
const getList = async () => { const getList = async () => {
loading.value = true loading.value = true
const res = await listMenu(queryParams.value); const res = await listMenu(queryParams.value);
const data = proxy?.handleTree<MenuVO>(res.data, "menuId") const data = proxy?.handleTree<MenuVO>(res.data, "menuId")
if (data) { if (data) {
menuList.value = data menuList.value = data
} }
loading.value = false loading.value = false
} }
/** 查询菜单下拉树结构 */ /** 查询菜单下拉树结构 */
const getTreeselect = async () => { const getTreeselect = async () => {
menuOptions.value = [] menuOptions.value = []
const response = await listMenu(); const response = await listMenu();
const menu: MenuOptionsType = { menuId: 0, menuName: "主类目", children: [] } const menu: MenuOptionsType = { menuId: 0, menuName: "主类目", children: [] }
menu.children = proxy?.handleTree<MenuOptionsType>(response.data, "menuId") menu.children = proxy?.handleTree<MenuOptionsType>(response.data, "menuId")
menuOptions.value.push(menu) menuOptions.value.push(menu)
} }
/** 取消按钮 */ /** 取消按钮 */
const cancel = () => { const cancel = () => {
reset() reset()
dialog.visible = false dialog.visible = false
} }
/** 表单重置 */ /** 表单重置 */
const reset = () => { const reset = () => {
form.value = { ...initFormData }; form.value = { ...initFormData };
menuFormRef.value.resetFields(); menuFormRef.value?.resetFields();
} }
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
getList(); getList();
} }
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
queryFormRef.value.resetFields(); queryFormRef.value?.resetFields();
handleQuery(); handleQuery();
} }
/** 新增按钮操作 */ /** 新增按钮操作 */
const handleAdd = (row?: MenuVO) => { const handleAdd = (row?: MenuVO) => {
dialog.visible = true; reset();
dialog.title = "添加菜单"; getTreeselect();
getTreeselect(); row && row.menuId ? form.value.parentId = row.menuId : form.value.parentId = 0;
nextTick(() => { dialog.visible = true;
reset(); dialog.title = "添加菜单";
row && row.menuId ? form.value.parentId = row.menuId : form.value.parentId = 0;
})
} }
/** 展开/折叠操作 */ /** 展开/折叠操作 */
const handleToggleExpandAll = () => { const handleToggleExpandAll = () => {
isExpandAll.value = !isExpandAll.value; isExpandAll.value = !isExpandAll.value;
toggleExpandAll(menuList.value, isExpandAll.value) toggleExpandAll(menuList.value, isExpandAll.value)
} }
/** 展开/折叠所有 */ /** 展开/折叠所有 */
const toggleExpandAll = (data: MenuVO[], status: boolean) => { const toggleExpandAll = (data: MenuVO[], status: boolean) => {
data.forEach((item: MenuVO) => { data.forEach((item: MenuVO) => {
menuTableRef.value.toggleRowExpansion(item, status) menuTableRef.value?.toggleRowExpansion(item, status)
if (item.children && item.children.length > 0) toggleExpandAll(item.children, status) if (item.children && item.children.length > 0) toggleExpandAll(item.children, status)
}) })
} }
/** 修改按钮操作 */ /** 修改按钮操作 */
const handleUpdate = async (row: MenuVO) => { const handleUpdate = async (row: MenuVO) => {
await getTreeselect(); reset();
dialog.visible = true; await getTreeselect();
dialog.title = "修改菜单"; if (row.menuId) {
await nextTick(async () => { const { data } = await getMenu(row.menuId);
if (row.menuId) { form.value = data;
const { data } = await getMenu(row.menuId); }
reset(); dialog.visible = true;
form.value = data; dialog.title = "修改菜单";
}
})
} }
/** 提交按钮 */ /** 提交按钮 */
const submitForm = () => { const submitForm = () => {
menuFormRef.value.validate(async (valid: boolean) => { menuFormRef.value?.validate(async (valid: boolean) => {
if (valid) { if (valid) {
form.value.menuId ? await updateMenu(form.value) : await addMenu(form.value); form.value.menuId ? await updateMenu(form.value) : await addMenu(form.value);
proxy?.$modal.msgSuccess("操作成功"); proxy?.$modal.msgSuccess("操作成功");
dialog.visible = false; dialog.visible = false;
getList(); await getList();
} }
}) })
} }
/** 删除按钮操作 */ /** 删除按钮操作 */
const handleDelete = async (row: MenuVO) => { const handleDelete = async (row: MenuVO) => {
await proxy?.$modal.confirm('是否确认删除名称为"' + row.menuName + '"的数据项?'); await proxy?.$modal.confirm('是否确认删除名称为"' + row.menuName + '"的数据项?');
await delMenu(row.menuId); await delMenu(row.menuId);
getList(); await getList();
proxy?.$modal.msgSuccess("删除成功"); proxy?.$modal.msgSuccess("删除成功");
} }
onMounted(() => { onMounted(() => {
getList(); getList();
}); });
</script> </script>

View File

@ -1,28 +1,30 @@
<template> <template>
<div class="p-2"> <div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="search" v-show="showSearch"> <div class="mb-[10px]" v-show="showSearch">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px"> <el-card shadow="hover">
<el-form-item label="公告标题" prop="noticeTitle"> <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
<el-input v-model="queryParams.noticeTitle" placeholder="请输入公告标题" clearable style="width: 200px" @keyup.enter="handleQuery" /> <el-form-item label="公告标题" prop="noticeTitle">
</el-form-item> <el-input v-model="queryParams.noticeTitle" placeholder="请输入公告标题" clearable style="width: 200px" @keyup.enter="handleQuery" />
<el-form-item label="操作人员" prop="createByName"> </el-form-item>
<el-input v-model="queryParams.createByName" placeholder="请输入操作人员" clearable style="width: 200px" @keyup.enter="handleQuery" /> <el-form-item label="操作人员" prop="createByName">
</el-form-item> <el-input v-model="queryParams.createByName" placeholder="请输入操作人员" clearable style="width: 200px" @keyup.enter="handleQuery" />
<el-form-item label="类型" prop="noticeType"> </el-form-item>
<el-select v-model="queryParams.noticeType" placeholder="公告类型" clearable style="width: 200px"> <el-form-item label="类型" prop="noticeType">
<el-option v-for="dict in sys_notice_type" :key="dict.value" :label="dict.label" :value="dict.value" /> <el-select v-model="queryParams.noticeType" placeholder="公告类型" clearable style="width: 200px">
</el-select> <el-option v-for="dict in sys_notice_type" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-form-item> </el-select>
<el-form-item> </el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button> <el-form-item>
<el-button icon="Refresh" @click="resetQuery">重置</el-button> <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
</el-form-item> <el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form> </el-form-item>
</el-form>
</el-card>
</div> </div>
</transition> </transition>
<el-card shadow="never"> <el-card shadow="hover">
<template #header> <template #header>
<el-row :gutter="10" class="mb8"> <el-row :gutter="10" class="mb8">
<el-col :span="1.5"> <el-col :span="1.5">
@ -119,9 +121,7 @@
<script setup name="Notice" lang="ts"> <script setup name="Notice" lang="ts">
import { listNotice, getNotice, delNotice, addNotice, updateNotice } from "@/api/system/notice"; import { listNotice, getNotice, delNotice, addNotice, updateNotice } from "@/api/system/notice";
import { ComponentInternalInstance } from "vue";
import { NoticeForm, NoticeQuery, NoticeVO } from "@/api/system/notice/types"; import { NoticeForm, NoticeQuery, NoticeVO } from "@/api/system/notice/types";
import { ElForm } from 'element-plus';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { sys_notice_status, sys_notice_type } = toRefs<any>(proxy?.useDict("sys_notice_status", "sys_notice_type")); const { sys_notice_status, sys_notice_type } = toRefs<any>(proxy?.useDict("sys_notice_status", "sys_notice_type"));
@ -134,116 +134,112 @@ const single = ref(true);
const multiple = ref(true); const multiple = ref(true);
const total = ref(0); const total = ref(0);
const queryFormRef = ref(ElForm); const queryFormRef = ref<ElFormInstance>();
const noticeFormRef = ref(ElForm); const noticeFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({ const dialog = reactive<DialogOption>({
visible: false, visible: false,
title: '' title: ''
}); });
const initFormData: NoticeForm = { const initFormData: NoticeForm = {
noticeId: undefined, noticeId: undefined,
noticeTitle: '', noticeTitle: '',
noticeType: '', noticeType: '',
noticeContent: '', noticeContent: '',
status: "0", status: "0",
remark: '', remark: '',
createByName: '' createByName: ''
} }
const data = reactive<PageData<NoticeForm, NoticeQuery>>({ const data = reactive<PageData<NoticeForm, NoticeQuery>>({
form: { ...initFormData }, form: { ...initFormData },
queryParams: { queryParams: {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
noticeTitle: '', noticeTitle: '',
createByName: '', createByName: '',
status: '', status: '',
noticeType: '' noticeType: ''
}, },
rules: { rules: {
noticeTitle: [{ required: true, message: "公告标题不能为空", trigger: "blur" }], noticeTitle: [{ required: true, message: "公告标题不能为空", trigger: "blur" }],
noticeType: [{ required: true, message: "公告类型不能为空", trigger: "change" }] noticeType: [{ required: true, message: "公告类型不能为空", trigger: "change" }]
}, },
}); });
const { queryParams, form, rules } = toRefs(data); const { queryParams, form, rules } = toRefs(data);
/** 查询公告列表 */ /** 查询公告列表 */
const getList = async () => { const getList = async () => {
loading.value = true; loading.value = true;
const res = await listNotice(queryParams.value); const res = await listNotice(queryParams.value);
noticeList.value = res.rows; noticeList.value = res.rows;
total.value = res.total; total.value = res.total;
loading.value = false; loading.value = false;
} }
/** 取消按钮 */ /** 取消按钮 */
const cancel = () => { const cancel = () => {
reset(); reset();
dialog.visible = false; dialog.visible = false;
} }
/** 表单重置 */ /** 表单重置 */
const reset = () => { const reset = () => {
form.value = { ...initFormData }; form.value = { ...initFormData };
noticeFormRef.value.resetFields(); noticeFormRef.value?.resetFields();
} }
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
queryParams.value.pageNum = 1; queryParams.value.pageNum = 1;
getList(); getList();
} }
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
queryFormRef.value.resetFields(); queryFormRef.value?.resetFields();
handleQuery(); handleQuery();
} }
/** 多选框选中数据 */ /** 多选框选中数据 */
const handleSelectionChange = (selection: NoticeVO[]) => { const handleSelectionChange = (selection: NoticeVO[]) => {
ids.value = selection.map(item => item.noticeId); ids.value = selection.map(item => item.noticeId);
single.value = selection.length != 1; single.value = selection.length != 1;
multiple.value = !selection.length; multiple.value = !selection.length;
} }
/** 新增按钮操作 */ /** 新增按钮操作 */
const handleAdd = () => { const handleAdd = () => {
dialog.visible = true; reset();
dialog.title = "添加公告"; dialog.visible = true;
nextTick(() => { dialog.title = "添加公告";
reset();
})
} }
/**修改按钮操作 */ /**修改按钮操作 */
const handleUpdate = (row?: NoticeVO) => { const handleUpdate = async (row?: NoticeVO) => {
dialog.visible = true; reset();
dialog.title = "修改公告"; const noticeId = row?.noticeId || ids.value[0];
nextTick(async () => { const { data } = await getNotice(noticeId);
const noticeId = row?.noticeId || ids.value[0]; Object.assign(form.value, data);
reset(); dialog.visible = true;
const { data } = await getNotice(noticeId); dialog.title = "修改公告";
form.value = data;
})
} }
/** 提交按钮 */ /** 提交按钮 */
const submitForm = () => { const submitForm = () => {
noticeFormRef.value.validate(async (valid: boolean) => { noticeFormRef.value?.validate(async (valid: boolean) => {
if (valid) { if (valid) {
form.value.noticeId ? await updateNotice(form.value) : await addNotice(form.value); form.value.noticeId ? await updateNotice(form.value) : await addNotice(form.value);
proxy?.$modal.msgSuccess("修改成功"); proxy?.$modal.msgSuccess("修改成功");
dialog.visible = false; dialog.visible = false;
getList(); await getList();
} }
}); });
} }
/** 删除按钮操作 */ /** 删除按钮操作 */
const handleDelete = async (row?: NoticeVO) => { const handleDelete = async (row?: NoticeVO) => {
const noticeIds = row?.noticeId || ids.value const noticeIds = row?.noticeId || ids.value
await proxy?.$modal.confirm('是否确认删除公告编号为"' + noticeIds + '"的数据项?'); await proxy?.$modal.confirm('是否确认删除公告编号为"' + noticeIds + '"的数据项?');
await delNotice(noticeIds); await delNotice(noticeIds);
getList(); await getList();
proxy?.$modal.msgSuccess("删除成功"); proxy?.$modal.msgSuccess("删除成功");
} }
onMounted(() => { onMounted(() => {
getList(); getList();
}) })
</script> </script>

View File

@ -1,29 +1,31 @@
<template> <template>
<div class="p-2"> <div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="search" v-show="showSearch"> <div class="mb-[10px]" v-show="showSearch">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px"> <el-card shadow="hover">
<el-form-item label="配置key" prop="configKey"> <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
<el-input v-model="queryParams.configKey" placeholder="配置key" clearable style="width: 200px" @keyup.enter="handleQuery" /> <el-form-item label="配置key" prop="configKey">
</el-form-item> <el-input v-model="queryParams.configKey" placeholder="配置key" clearable style="width: 200px" @keyup.enter="handleQuery" />
<el-form-item label="桶名称" prop="bucketName"> </el-form-item>
<el-input v-model="queryParams.bucketName" placeholder="请输入桶名称" clearable style="width: 200px" @keyup.enter="handleQuery" /> <el-form-item label="桶名称" prop="bucketName">
</el-form-item> <el-input v-model="queryParams.bucketName" placeholder="请输入桶名称" clearable style="width: 200px" @keyup.enter="handleQuery" />
<el-form-item label="是否默认" prop="status"> </el-form-item>
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable style="width: 200px"> <el-form-item label="是否默认" prop="status">
<el-option key="0" label="是" value="0" /> <el-select v-model="queryParams.status" placeholder="请选择状态" clearable style="width: 200px">
<el-option key="1" label="否" value="1" /> <el-option key="0" label="是" value="0" />
</el-select> <el-option key="1" label="否" value="1" />
</el-form-item> </el-select>
<el-form-item> </el-form-item>
<el-button type="primary" icon="search" @click="handleQuery">搜索</el-button> <el-form-item>
<el-button icon="Refresh" @click="resetQuery">重置</el-button> <el-button type="primary" icon="search" @click="handleQuery">搜索</el-button>
</el-form-item> <el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form> </el-form-item>
</el-form>
</el-card>
</div> </div>
</transition> </transition>
<el-card shadow="never"> <el-card shadow="hover">
<template #header> <template #header>
<el-row :gutter="10" class="mb8"> <el-row :gutter="10" class="mb8">
<el-col :span="1.5"> <el-col :span="1.5">
@ -62,7 +64,7 @@
<el-switch v-model="scope.row.status" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)"></el-switch> <el-switch v-model="scope.row.status" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)"></el-switch>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" align="center" width="150" class-name="small-padding fixed-width"> <el-table-column label="操作" fixed="right" align="center" width="150" class-name="small-padding">
<template #default="scope"> <template #default="scope">
<el-tooltip content="修改" placement="top"> <el-tooltip content="修改" placement="top">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:oss:edit']"></el-button> <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:oss:edit']"></el-button>
@ -131,16 +133,14 @@
<script setup name="OssConfig" lang="ts"> <script setup name="OssConfig" lang="ts">
import { import {
listOssConfig, listOssConfig,
getOssConfig, getOssConfig,
delOssConfig, delOssConfig,
addOssConfig, addOssConfig,
updateOssConfig, updateOssConfig,
changeOssConfigStatus changeOssConfigStatus
} from "@/api/system/ossConfig"; } from "@/api/system/ossConfig";
import { ComponentInternalInstance } from "vue";
import { OssConfigForm, OssConfigQuery, OssConfigVO } from "@/api/system/ossConfig/types"; import { OssConfigForm, OssConfigQuery, OssConfigVO } from "@/api/system/ossConfig/types";
import { ElForm } from 'element-plus';
const { proxy } = getCurrentInstance() as ComponentInternalInstance const { proxy } = getCurrentInstance() as ComponentInternalInstance
@ -155,193 +155,187 @@ const single = ref(true);
const multiple = ref(true); const multiple = ref(true);
const total = ref(0); const total = ref(0);
const queryFormRef = ref(ElForm); const queryFormRef = ref<ElFormInstance>();
const ossConfigFormRef = ref(ElForm); const ossConfigFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({ const dialog = reactive<DialogOption>({
visible: false, visible: false,
title: '' title: ''
}); });
// //
const columns = ref<FieldOption[]>([ const columns = ref<FieldOption[]>([
{ key: 0, label: `主建`, visible: true }, { key: 0, label: `主建`, visible: true },
{ key: 1, label: `配置key`, visible: false }, { key: 1, label: `配置key`, visible: false },
{ key: 2, label: `访问站点`, visible: true }, { key: 2, label: `访问站点`, visible: true },
{ key: 3, label: `自定义域名`, visible: true }, { key: 3, label: `自定义域名`, visible: true },
{ key: 4, label: `桶名称`, visible: true }, { key: 4, label: `桶名称`, visible: true },
{ key: 5, label: `前缀`, visible: true }, { key: 5, label: `前缀`, visible: true },
{ key: 6, label: ``, visible: true }, { key: 6, label: ``, visible: true },
{ key: 7, label: `桶权限类型`, visible: true }, { key: 7, label: `桶权限类型`, visible: true },
{ key: 8, label: `状态`, visible: true } { key: 8, label: `状态`, visible: true }
]); ]);
const initFormData: OssConfigForm = { const initFormData: OssConfigForm = {
ossConfigId: undefined, ossConfigId: undefined,
configKey: '', configKey: '',
accessKey: '', accessKey: '',
secretKey: '', secretKey: '',
bucketName: '', bucketName: '',
prefix: '', prefix: '',
endpoint: '', endpoint: '',
domain: '', domain: '',
isHttps: "N", isHttps: "N",
accessPolicy: "1", accessPolicy: "1",
region: '', region: '',
status: "1", status: "1",
remark: '', remark: '',
} }
const data = reactive<PageData<OssConfigForm, OssConfigQuery>>({ const data = reactive<PageData<OssConfigForm, OssConfigQuery>>({
form: { ...initFormData }, form: { ...initFormData },
// //
queryParams: { queryParams: {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
configKey: '', configKey: '',
bucketName: '', bucketName: '',
status: '', status: '',
}, },
rules: { rules: {
configKey: [{ required: true, message: "configKey不能为空", trigger: "blur" },], configKey: [{ required: true, message: "configKey不能为空", trigger: "blur" },],
accessKey: [ accessKey: [
{ required: true, message: "accessKey不能为空", trigger: "blur" }, { required: true, message: "accessKey不能为空", trigger: "blur" },
{ {
min: 2, min: 2,
max: 200, max: 200,
message: "accessKey长度必须介于 2 和 100 之间", message: "accessKey长度必须介于 2 和 100 之间",
trigger: "blur", trigger: "blur",
}, },
], ],
secretKey: [ secretKey: [
{ required: true, message: "secretKey不能为空", trigger: "blur" }, { required: true, message: "secretKey不能为空", trigger: "blur" },
{ {
min: 2, min: 2,
max: 100, max: 100,
message: "secretKey长度必须介于 2 和 100 之间", message: "secretKey长度必须介于 2 和 100 之间",
trigger: "blur", trigger: "blur",
}, },
], ],
bucketName: [ bucketName: [
{ required: true, message: "bucketName不能为空", trigger: "blur" }, { required: true, message: "bucketName不能为空", trigger: "blur" },
{ {
min: 2, min: 2,
max: 100, max: 100,
message: "bucketName长度必须介于 2 和 100 之间", message: "bucketName长度必须介于 2 和 100 之间",
trigger: "blur", trigger: "blur",
}, },
], ],
endpoint: [ endpoint: [
{ required: true, message: "endpoint不能为空", trigger: "blur" }, { required: true, message: "endpoint不能为空", trigger: "blur" },
{ {
min: 2, min: 2,
max: 100, max: 100,
message: "endpoint名称长度必须介于 2 和 100 之间", message: "endpoint名称长度必须介于 2 和 100 之间",
trigger: "blur", trigger: "blur",
}, },
], ],
accessPolicy: [{ required: true, message: "accessPolicy不能为空", trigger: "blur" }] accessPolicy: [{ required: true, message: "accessPolicy不能为空", trigger: "blur" }]
} }
}); });
const { queryParams, form, rules } = toRefs(data); const { queryParams, form, rules } = toRefs(data);
/** 查询对象存储配置列表 */ /** 查询对象存储配置列表 */
const getList = async () => { const getList = async () => {
loading.value = true; loading.value = true;
const res = await listOssConfig(queryParams.value); const res = await listOssConfig(queryParams.value);
ossConfigList.value = res.rows; ossConfigList.value = res.rows;
total.value = res.total; total.value = res.total;
loading.value = false; loading.value = false;
} }
/** 取消按钮 */ /** 取消按钮 */
const cancel = () => { const cancel = () => {
dialog.visible = false; dialog.visible = false;
reset(); reset();
} }
/** 表单重置 */ /** 表单重置 */
const reset = () => { const reset = () => {
form.value = { ...initFormData }; form.value = { ...initFormData };
ossConfigFormRef.value.resetFields(); ossConfigFormRef.value?.resetFields();
} }
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
queryParams.value.pageNum = 1; queryParams.value.pageNum = 1;
getList(); getList();
} }
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
queryFormRef.value.resetFields(); queryFormRef.value?.resetFields();
handleQuery(); handleQuery();
} }
/** 选择条数 */ /** 选择条数 */
const handleSelectionChange = (selection: OssConfigVO[]) => { const handleSelectionChange = (selection: OssConfigVO[]) => {
ids.value = selection.map(item => item.ossConfigId); ids.value = selection.map(item => item.ossConfigId);
single.value = selection.length != 1; single.value = selection.length != 1;
multiple.value = !selection.length; multiple.value = !selection.length;
} }
/** 新增按钮操作 */ /** 新增按钮操作 */
const handleAdd = () => { const handleAdd = () => {
dialog.visible = true; reset();
dialog.title = "添加对象存储配置"; dialog.visible = true;
nextTick(() => { dialog.title = "添加对象存储配置";
reset();
})
} }
/** 修改按钮操作 */ /** 修改按钮操作 */
const handleUpdate = (row?: OssConfigVO) => { const handleUpdate = async (row?: OssConfigVO) => {
loading.value = true; reset();
dialog.visible = true; const ossConfigId = row?.ossConfigId || ids.value[0];
dialog.title = "修改对象存储配置"; const res = await getOssConfig(ossConfigId);
const ossConfigId = row?.ossConfigId || ids.value[0]; Object.assign(form.value, res.data);
nextTick(async () => { dialog.visible = true;
reset(); dialog.title = "修改对象存储配置";
const res = await getOssConfig(ossConfigId);
loading.value = false;
form.value = res.data;
})
} }
/** 提交按钮 */ /** 提交按钮 */
const submitForm = () => { const submitForm = () => {
ossConfigFormRef.value.validate(async (valid: boolean) => { ossConfigFormRef.value?.validate(async (valid: boolean) => {
if (valid) { if (valid) {
buttonLoading.value = true; buttonLoading.value = true;
if (form.value.ossConfigId) { if (form.value.ossConfigId) {
await updateOssConfig(form.value).finally(() => buttonLoading.value = false); await updateOssConfig(form.value).finally(() => buttonLoading.value = false);
} else { } else {
await addOssConfig(form.value).finally(() => buttonLoading.value = false); await addOssConfig(form.value).finally(() => buttonLoading.value = false);
} }
proxy?.$modal.msgSuccess("新增成功"); proxy?.$modal.msgSuccess("新增成功");
dialog.visible = false; dialog.visible = false;
getList(); await getList();
} }
}); });
} }
/** 状态修改 */ /** 状态修改 */
const handleStatusChange = async (row: OssConfigVO) => { const handleStatusChange = async (row: OssConfigVO) => {
let text = row.status === "0" ? "启用" : "停用"; let text = row.status === "0" ? "启用" : "停用";
try { try {
await proxy?.$modal.confirm('确认要"' + text + '""' + row.configKey + '"配置吗?'); await proxy?.$modal.confirm('确认要"' + text + '""' + row.configKey + '"配置吗?');
await changeOssConfigStatus(row.ossConfigId, row.status, row.configKey); await changeOssConfigStatus(row.ossConfigId, row.status, row.configKey);
getList() await getList()
proxy?.$modal.msgSuccess(text + "成功"); proxy?.$modal.msgSuccess(text + "成功");
} catch { return } finally { } catch { return } finally {
row.status = row.status === "0" ? "1" : "0"; row.status = row.status === "0" ? "1" : "0";
} }
} }
/** 删除按钮操作 */ /** 删除按钮操作 */
const handleDelete = async (row?: OssConfigVO) => { const handleDelete = async (row?: OssConfigVO) => {
const ossConfigIds = row?.ossConfigId || ids.value; const ossConfigIds = row?.ossConfigId || ids.value;
await proxy?.$modal.confirm('是否确认删除OSS配置编号为"' + ossConfigIds + '"的数据项?'); await proxy?.$modal.confirm('是否确认删除OSS配置编号为"' + ossConfigIds + '"的数据项?');
loading.value = true; loading.value = true;
await delOssConfig(ossConfigIds).finally(() => loading.value = false); await delOssConfig(ossConfigIds).finally(() => loading.value = false);
getList(); await getList();
proxy?.$modal.msgSuccess("删除成功"); proxy?.$modal.msgSuccess("删除成功");
} }
onMounted(() => { onMounted(() => {
getList(); getList();
}) })
</script> </script>

View File

@ -1,40 +1,42 @@
<template> <template>
<div class="p-2"> <div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="search" v-show="showSearch"> <div class="mb-[10px]" v-show="showSearch">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px"> <el-card shadow="hover">
<el-form-item label="文件名" prop="fileName"> <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
<el-input v-model="queryParams.fileName" placeholder="请输入文件名" clearable style="width: 200px" @keyup.enter="handleQuery" /> <el-form-item label="文件名" prop="fileName">
</el-form-item> <el-input v-model="queryParams.fileName" placeholder="请输入文件名" clearable style="width: 200px" @keyup.enter="handleQuery" />
<el-form-item label="原名" prop="originalName"> </el-form-item>
<el-input v-model="queryParams.originalName" placeholder="请输入原名" clearable style="width: 200px" @keyup.enter="handleQuery" /> <el-form-item label="原名" prop="originalName">
</el-form-item> <el-input v-model="queryParams.originalName" placeholder="请输入原名" clearable style="width: 200px" @keyup.enter="handleQuery" />
<el-form-item label="文件后缀" prop="fileSuffix"> </el-form-item>
<el-input v-model="queryParams.fileSuffix" placeholder="请输入文件后缀" clearable style="width: 200px" @keyup.enter="handleQuery" /> <el-form-item label="文件后缀" prop="fileSuffix">
</el-form-item> <el-input v-model="queryParams.fileSuffix" placeholder="请输入文件后缀" clearable style="width: 200px" @keyup.enter="handleQuery" />
<el-form-item label="创建时间"> </el-form-item>
<el-date-picker <el-form-item label="创建时间">
v-model="daterangeCreateTime" <el-date-picker
value-format="YYYY-MM-DD HH:mm:ss" v-model="dateRangeCreateTime"
type="daterange" value-format="YYYY-MM-DD HH:mm:ss"
range-separator="-" type="daterange"
start-placeholder="开始日期" range-separator="-"
end-placeholder="结束日期" start-placeholder="开始日期"
:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]" end-placeholder="结束日期"
></el-date-picker> :default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
</el-form-item> ></el-date-picker>
<el-form-item label="服务商" prop="service"> </el-form-item>
<el-input v-model="queryParams.service" placeholder="请输入服务商" clearable style="width: 200px" @keyup.enter="handleQuery" /> <el-form-item label="服务商" prop="service">
</el-form-item> <el-input v-model="queryParams.service" placeholder="请输入服务商" clearable style="width: 200px" @keyup.enter="handleQuery" />
<el-form-item> </el-form-item>
<el-button type="primary" icon="search" @click="handleQuery">搜索</el-button> <el-form-item>
<el-button icon="Refresh" @click="resetQuery">重置</el-button> <el-button type="primary" icon="search" @click="handleQuery">搜索</el-button>
</el-form-item> <el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form> </el-form-item>
</el-form>
</el-card>
</div> </div>
</transition> </transition>
<el-card shadow="never"> <el-card shadow="hover">
<template #header> <template #header>
<el-row :gutter="10" class="mb8"> <el-row :gutter="10" class="mb8">
<el-col :span="1.5"> <el-col :span="1.5">
@ -133,9 +135,7 @@
<script setup name="Oss" lang="ts"> <script setup name="Oss" lang="ts">
import { listOss, delOss } from "@/api/system/oss"; import { listOss, delOss } from "@/api/system/oss";
import ImagePreview from "@/components/ImagePreview/index.vue"; import ImagePreview from "@/components/ImagePreview/index.vue";
import { ComponentInternalInstance } from "vue";
import { OssForm, OssQuery, OssVO } from "@/api/system/oss/types"; import { OssForm, OssQuery, OssVO } from "@/api/system/oss/types";
import { DateModelType } from 'element-plus';
const router = useRouter(); const router = useRouter();
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@ -151,189 +151,185 @@ const multiple = ref(true);
const total = ref(0); const total = ref(0);
const type = ref(0); const type = ref(0);
const previewListResource = ref(true); const previewListResource = ref(true);
const daterangeCreateTime = ref<[DateModelType, DateModelType]>(['', '']); const dateRangeCreateTime = ref<[DateModelType, DateModelType]>(['', '']);
const dialog = reactive<DialogOption>({ const dialog = reactive<DialogOption>({
visible: false, visible: false,
title: '' title: ''
}); });
// //
const defaultSort = ref({ prop: 'createTime', order: 'ascending' }); const defaultSort = ref({ prop: 'createTime', order: 'ascending' });
const ossFormRef = ref(ElForm); const ossFormRef = ref<ElFormInstance>();
const queryFormRef = ref(ElForm); const queryFormRef = ref<ElFormInstance>();
const initFormData = { const initFormData = {
file: undefined, file: undefined,
} }
const data = reactive<PageData<OssForm, OssQuery>>({ const data = reactive<PageData<OssForm, OssQuery>>({
form: { ...initFormData }, form: { ...initFormData },
// //
queryParams: { queryParams: {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
fileName: '', fileName: '',
originalName: '', originalName: '',
fileSuffix: '', fileSuffix: '',
createTime: '', createTime: '',
service: '', service: '',
orderByColumn: defaultSort.value.prop, orderByColumn: defaultSort.value.prop,
isAsc: defaultSort.value.order isAsc: defaultSort.value.order
}, },
rules: { rules: {
file: [ file: [
{ required: true, message: "文件不能为空", trigger: "blur" } { required: true, message: "文件不能为空", trigger: "blur" }
] ]
} }
}); });
const { queryParams, form, rules } = toRefs(data); const { queryParams, form, rules } = toRefs(data);
/** 查询OSS对象存储列表 */ /** 查询OSS对象存储列表 */
const getList = async () => { const getList = async () => {
loading.value = true; loading.value = true;
const res = await proxy?.getConfigKey("sys.oss.previewListResource"); const res = await proxy?.getConfigKey("sys.oss.previewListResource");
previewListResource.value = res?.msg === undefined ? true : res.msg === 'true'; previewListResource.value = res?.msg === undefined ? true : res.msg === 'true';
const response = await listOss(proxy?.addDateRange(queryParams.value, daterangeCreateTime.value, "CreateTime")); const response = await listOss(proxy?.addDateRange(queryParams.value, dateRangeCreateTime.value, "CreateTime"));
ossList.value = response.rows; ossList.value = response.rows;
total.value = response.total; total.value = response.total;
loading.value = false; loading.value = false;
showTable.value = true; showTable.value = true;
} }
function checkFileSuffix(fileSuffix: string[]) { function checkFileSuffix(fileSuffix: string[]) {
let arr = ["png", "jpg", "jpeg"]; let arr = ["png", "jpg", "jpeg"];
return arr.some(type => { return arr.some(type => {
return fileSuffix.indexOf(type) > -1; return fileSuffix.indexOf(type) > -1;
}); });
} }
/** 取消按钮 */ /** 取消按钮 */
function cancel() { function cancel() {
dialog.visible = false; dialog.visible = false;
reset(); reset();
} }
/** 表单重置 */ /** 表单重置 */
function reset() { function reset() {
form.value = { ...initFormData }; form.value = { ...initFormData };
ossFormRef.value.resetFields(); ossFormRef.value?.resetFields();
} }
/** 搜索按钮操作 */ /** 搜索按钮操作 */
function handleQuery() { function handleQuery() {
queryParams.value.pageNum = 1; queryParams.value.pageNum = 1;
getList(); getList();
} }
/** 重置按钮操作 */ /** 重置按钮操作 */
function resetQuery() { function resetQuery() {
showTable.value = false; showTable.value = false;
daterangeCreateTime.value = ['', '']; dateRangeCreateTime.value = ['', ''];
queryFormRef.value.resetFields(); queryFormRef.value?.resetFields();
queryParams.value.orderByColumn = defaultSort.value.prop; queryParams.value.orderByColumn = defaultSort.value.prop;
queryParams.value.isAsc = defaultSort.value.order; queryParams.value.isAsc = defaultSort.value.order;
handleQuery(); handleQuery();
} }
/** 选择条数 */ /** 选择条数 */
function handleSelectionChange(selection: OssVO[]) { function handleSelectionChange(selection: OssVO[]) {
ids.value = selection.map(item => item.ossId); ids.value = selection.map(item => item.ossId);
single.value = selection.length != 1; single.value = selection.length != 1;
multiple.value = !selection.length; multiple.value = !selection.length;
} }
/** 设置列的排序为我们自定义的排序 */ /** 设置列的排序为我们自定义的排序 */
const handleHeaderClass = ({ column }: any): any => { const handleHeaderClass = ({ column }: any): any => {
column.order = column.multiOrder column.order = column.multiOrder
} }
/** 点击表头进行排序 */ /** 点击表头进行排序 */
const handleHeaderCLick = (column: any) => { const handleHeaderCLick = (column: any) => {
if (column.sortable !== 'custom') { if (column.sortable !== 'custom') {
return return
} }
switch (column.multiOrder) { switch (column.multiOrder) {
case 'descending': case 'descending':
column.multiOrder = 'ascending'; column.multiOrder = 'ascending';
break; break;
case 'ascending': case 'ascending':
column.multiOrder = ''; column.multiOrder = '';
break; break;
default: default:
column.multiOrder = 'descending'; column.multiOrder = 'descending';
break; break;
} }
handleOrderChange(column.property, column.multiOrder) handleOrderChange(column.property, column.multiOrder)
} }
const handleOrderChange = (prop: string, order: string) => { const handleOrderChange = (prop: string, order: string) => {
let orderByArr = queryParams.value.orderByColumn ? queryParams.value.orderByColumn.split(",") : []; let orderByArr = queryParams.value.orderByColumn ? queryParams.value.orderByColumn.split(",") : [];
let isAscArr = queryParams.value.isAsc ? queryParams.value.isAsc.split(",") : []; let isAscArr = queryParams.value.isAsc ? queryParams.value.isAsc.split(",") : [];
let propIndex = orderByArr.indexOf(prop) let propIndex = orderByArr.indexOf(prop)
if (propIndex !== -1) { if (propIndex !== -1) {
if (order) { if (order) {
// //
isAscArr[propIndex] = order; isAscArr[propIndex] = order;
} else {
//ordernull
isAscArr.splice(propIndex, 1);//
orderByArr.splice(propIndex, 1);//
}
} else { } else {
// //ordernull
orderByArr.push(prop); isAscArr.splice(propIndex, 1);//
isAscArr.push(order); orderByArr.splice(propIndex, 1);//
} }
// } else {
queryParams.value.orderByColumn = orderByArr.join(","); //
queryParams.value.isAsc = isAscArr.join(","); orderByArr.push(prop);
getList(); isAscArr.push(order);
}
//
queryParams.value.orderByColumn = orderByArr.join(",");
queryParams.value.isAsc = isAscArr.join(",");
getList();
} }
/** 任务日志列表查询 */ /** 任务日志列表查询 */
const handleOssConfig = () => { const handleOssConfig = () => {
router.push('/system/oss-config/index') router.push('/system/oss-config/index')
} }
/** 文件按钮操作 */ /** 文件按钮操作 */
const handleFile = () => { const handleFile = () => {
dialog.visible = true; reset();
dialog.title = "上传文件"; type.value = 0;
nextTick(() => { dialog.visible = true;
reset(); dialog.title = "上传文件";
type.value = 0;
})
} }
/** 图片按钮操作 */ /** 图片按钮操作 */
const handleImage = () => { const handleImage = () => {
dialog.visible = true; reset();
dialog.title = "上传图片"; type.value = 1;
nextTick(() => { dialog.visible = true;
reset(); dialog.title = "上传图片";
type.value = 1;
})
} }
/** 提交按钮 */ /** 提交按钮 */
const submitForm = () => { const submitForm = () => {
dialog.visible = false; dialog.visible = false;
getList(); getList();
} }
/** 下载按钮操作 */ /** 下载按钮操作 */
const handleDownload = (row: OssVO) => { const handleDownload = (row: OssVO) => {
proxy?.$download.oss(row.ossId) proxy?.$download.oss(row.ossId)
} }
/** 用户状态修改 */ /** 用户状态修改 */
const handlePreviewListResource = async (preview: boolean) => { const handlePreviewListResource = async (preview: boolean) => {
let text = preview ? "启用" : "停用"; let text = preview ? "启用" : "停用";
try { try {
await proxy?.$modal.confirm('确认要"' + text + '""预览列表图片"配置吗?'); await proxy?.$modal.confirm('确认要"' + text + '""预览列表图片"配置吗?');
await proxy?.updateConfigByKey("sys.oss.previewListResource", preview); await proxy?.updateConfigByKey("sys.oss.previewListResource", preview);
getList() await getList()
proxy?.$modal.msgSuccess(text + "成功"); proxy?.$modal.msgSuccess(text + "成功");
} catch { return } } catch { return }
} }
/** 删除按钮操作 */ /** 删除按钮操作 */
const handleDelete = async (row?: OssVO) => { const handleDelete = async (row?: OssVO) => {
const ossIds = row?.ossId || ids.value; const ossIds = row?.ossId || ids.value;
await proxy?.$modal.confirm('是否确认删除OSS对象存储编号为"' + ossIds + '"的数据项?'); await proxy?.$modal.confirm('是否确认删除OSS对象存储编号为"' + ossIds + '"的数据项?');
loading.value = true; loading.value = true;
await delOss(ossIds).finally(() => loading.value = false); await delOss(ossIds).finally(() => loading.value = false);
getList(); await getList();
proxy?.$modal.msgSuccess("删除成功"); proxy?.$modal.msgSuccess("删除成功");
} }
onMounted(() => { onMounted(() => {
getList(); getList();
}) })
</script> </script>

View File

@ -1,27 +1,29 @@
<template> <template>
<div class="p-2"> <div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="search" v-show="showSearch"> <div class="mb-[10px]" v-show="showSearch">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="70"> <el-card shadow="hover">
<el-form-item label="岗位编码" prop="postCode"> <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="70">
<el-input v-model="queryParams.postCode" placeholder="请输入岗位编码" clearable style="width: 200px" @keyup.enter="handleQuery" /> <el-form-item label="岗位编码" prop="postCode">
</el-form-item> <el-input v-model="queryParams.postCode" placeholder="请输入岗位编码" clearable style="width: 200px" @keyup.enter="handleQuery" />
<el-form-item label="岗位名称" prop="postName"> </el-form-item>
<el-input v-model="queryParams.postName" placeholder="请输入岗位名称" clearable style="width: 200px" @keyup.enter="handleQuery" /> <el-form-item label="岗位名称" prop="postName">
</el-form-item> <el-input v-model="queryParams.postName" placeholder="请输入岗位名称" clearable style="width: 200px" @keyup.enter="handleQuery" />
<el-form-item label="状态" prop="status"> </el-form-item>
<el-select v-model="queryParams.status" placeholder="岗位状态" clearable style="width: 200px"> <el-form-item label="状态" prop="status">
<el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" /> <el-select v-model="queryParams.status" placeholder="岗位状态" clearable style="width: 200px">
</el-select> <el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-form-item> </el-select>
<el-form-item> </el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button> <el-form-item>
<el-button icon="Refresh" @click="resetQuery">重置</el-button> <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
</el-form-item> <el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form> </el-form-item>
</el-form>
</el-card>
</div> </div>
</transition> </transition>
<el-card shadow="never"> <el-card shadow="hover">
<template #header> <template #header>
<el-row :gutter="10" class="mb8"> <el-row :gutter="10" class="mb8">
<el-col :span="1.5"> <el-col :span="1.5">
@ -107,7 +109,6 @@
<script setup name="Post" lang="ts"> <script setup name="Post" lang="ts">
import { listPost, addPost, delPost, getPost, updatePost } from "@/api/system/post"; import { listPost, addPost, delPost, getPost, updatePost } from "@/api/system/post";
import { PostForm, PostQuery, PostVO } from "@/api/system/post/types"; import { PostForm, PostQuery, PostVO } from "@/api/system/post/types";
import { ComponentInternalInstance } from "vue";
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { sys_normal_disable } = toRefs<any>(proxy?.useDict("sys_normal_disable")); const { sys_normal_disable } = toRefs<any>(proxy?.useDict("sys_normal_disable"));
@ -120,121 +121,117 @@ const single = ref(true);
const multiple = ref(true); const multiple = ref(true);
const total = ref(0); const total = ref(0);
const postFormRef = ref(ElForm); const postFormRef = ref<ElFormInstance>();
const queryFormRef = ref(ElForm); const queryFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({ const dialog = reactive<DialogOption>({
visible: false, visible: false,
title: '' title: ''
}); });
const initFormData: PostForm = { const initFormData: PostForm = {
postId: undefined, postId: undefined,
postCode: '', postCode: '',
postName: '', postName: '',
postSort: 0, postSort: 0,
status: "0", status: "0",
remark: '' remark: ''
} }
const data = reactive<PageData<PostForm, PostQuery>>({ const data = reactive<PageData<PostForm, PostQuery>>({
form: {...initFormData}, form: { ...initFormData },
queryParams: { queryParams: {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
postCode: '', postCode: '',
postName: '', postName: '',
status: '' status: ''
}, },
rules: { rules: {
postName: [{ required: true, message: "岗位名称不能为空", trigger: "blur" }], postName: [{ required: true, message: "岗位名称不能为空", trigger: "blur" }],
postCode: [{ required: true, message: "岗位编码不能为空", trigger: "blur" }], postCode: [{ required: true, message: "岗位编码不能为空", trigger: "blur" }],
postSort: [{ required: true, message: "岗位顺序不能为空", trigger: "blur" }], postSort: [{ required: true, message: "岗位顺序不能为空", trigger: "blur" }],
} }
}); });
const { queryParams, form, rules } = toRefs<PageData<PostForm, PostQuery>>(data); const { queryParams, form, rules } = toRefs<PageData<PostForm, PostQuery>>(data);
/** 查询岗位列表 */ /** 查询岗位列表 */
const getList = async () => { const getList = async () => {
loading.value = true; loading.value = true;
const res = await listPost(queryParams.value); const res = await listPost(queryParams.value);
postList.value = res.rows; postList.value = res.rows;
total.value = res.total; total.value = res.total;
loading.value = false; loading.value = false;
} }
/** 取消按钮 */ /** 取消按钮 */
const cancel = () => { const cancel = () => {
reset(); reset();
dialog.visible = false; dialog.visible = false;
} }
/** 表单重置 */ /** 表单重置 */
const reset = () => { const reset = () => {
form.value = {...initFormData}; form.value = { ...initFormData };
postFormRef.value.resetFields(); postFormRef.value?.resetFields();
} }
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
queryParams.value.pageNum = 1; queryParams.value.pageNum = 1;
getList(); getList();
} }
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
queryFormRef.value.resetFields(); queryFormRef.value?.resetFields();
handleQuery(); handleQuery();
} }
/** 多选框选中数据 */ /** 多选框选中数据 */
const handleSelectionChange = (selection: PostVO[]) => { const handleSelectionChange = (selection: PostVO[]) => {
ids.value = selection.map(item => item.postId); ids.value = selection.map(item => item.postId);
single.value = selection.length != 1; single.value = selection.length != 1;
multiple.value = !selection.length; multiple.value = !selection.length;
} }
/** 新增按钮操作 */ /** 新增按钮操作 */
const handleAdd = () => { const handleAdd = () => {
dialog.visible = true; reset();
dialog.title = "添加岗位"; dialog.visible = true;
nextTick(() => { dialog.title = "添加岗位";
reset();
})
} }
/** 修改按钮操作 */ /** 修改按钮操作 */
const handleUpdate = (row?: PostVO) => { const handleUpdate = async (row?: PostVO) => {
dialog.visible = true; reset();
dialog.title = "修改岗位"; const postId = row?.postId || ids.value[0];
nextTick(async () => { const res = await getPost(postId);
reset(); Object.assign(form.value, res.data);
const postId = row?.postId || ids.value[0]; dialog.visible = true;
const res = await getPost(postId); dialog.title = "修改岗位";
form.value = res.data;
})
} }
/** 提交按钮 */ /** 提交按钮 */
const submitForm = () => { const submitForm = () => {
postFormRef.value.validate(async (valid: boolean) => { postFormRef.value?.validate(async (valid: boolean) => {
if (valid) { if (valid) {
form.value.postId ? await updatePost(form.value) : await addPost(form.value); form.value.postId ? await updatePost(form.value) : await addPost(form.value);
proxy?.$modal.msgSuccess("操作成功"); proxy?.$modal.msgSuccess("操作成功");
dialog.visible = false; dialog.visible = false;
getList(); await getList();
} }
}); });
} }
/** 删除按钮操作 */ /** 删除按钮操作 */
const handleDelete = async (row?: PostVO) => { const handleDelete = async (row?: PostVO) => {
const postIds = row?.postId || ids.value; const postIds = row?.postId || ids.value;
await proxy?.$modal.confirm('是否确认删除岗位编号为"' + postIds + '"的数据项?'); await proxy?.$modal.confirm('是否确认删除岗位编号为"' + postIds + '"的数据项?');
await delPost(postIds); await delPost(postIds);
getList(); await getList();
proxy?.$modal.msgSuccess("删除成功"); proxy?.$modal.msgSuccess("删除成功");
} }
/** 导出按钮操作 */ /** 导出按钮操作 */
const handleExport = () => { const handleExport = () => {
proxy?.download("system/post/export", { proxy?.download("system/post/export", {
...queryParams.value ...queryParams.value
}, `post_${new Date().getTime()}.xlsx`); }, `post_${new Date().getTime()}.xlsx`);
} }
onMounted(() => { onMounted(() => {
getList(); getList();
}); });
</script> </script>

View File

@ -58,13 +58,7 @@
</el-table-column> </el-table-column>
</el-table> </el-table>
<pagination <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
<select-user ref="selectRef" :roleId="queryParams.roleId" @ok="handleQuery" /> <select-user ref="selectRef" :roleId="queryParams.roleId" @ok="handleQuery" />
</el-card> </el-card>
</div> </div>
@ -73,10 +67,8 @@
<script setup name="AuthUser" lang="ts"> <script setup name="AuthUser" lang="ts">
import { allocatedUserList, authUserCancel, authUserCancelAll } from "@/api/system/role"; import { allocatedUserList, authUserCancel, authUserCancelAll } from "@/api/system/role";
import { UserQuery } from "@/api/system/user/types"; import { UserQuery } from "@/api/system/user/types";
import { ComponentInternalInstance } from "vue";
import { UserVO } from "@/api/system/user/types"; import { UserVO } from "@/api/system/user/types";
import SelectUser from "./selectUser.vue"; import SelectUser from "./selectUser.vue";
// import { ElForm, ElSelect} from 'element-plus';
const route = useRoute(); const route = useRoute();
@ -90,68 +82,68 @@ const multiple = ref(true);
const total = ref(0); const total = ref(0);
const userIds = ref<Array<string | number>>([]); const userIds = ref<Array<string | number>>([]);
const queryFormRef = ref(ElForm); const queryFormRef = ref<ElFormInstance>();
const selectRef = ref(SelectUser); const selectRef = ref<InstanceType<typeof SelectUser>>();
const queryParams = reactive<UserQuery>({ const queryParams = reactive<UserQuery>({
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
roleId: route.params.roleId as string, roleId: route.params.roleId as string,
userName: undefined, userName: undefined,
phonenumber: undefined, phonenumber: undefined,
}); });
/** 查询授权用户列表 */ /** 查询授权用户列表 */
const getList = async () => { const getList = async () => {
loading.value = true; loading.value = true;
const res = await allocatedUserList(queryParams); const res = await allocatedUserList(queryParams);
userList.value = res.rows; userList.value = res.rows;
total.value = res.total; total.value = res.total;
loading.value = false; loading.value = false;
} }
// //
const handleClose = () => { const handleClose = () => {
const obj = { path: "/system/role" }; const obj = { path: "/system/role" };
proxy?.$tab.closeOpenPage(obj); proxy?.$tab.closeOpenPage(obj);
} }
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery=() => { const handleQuery = () => {
queryParams.pageNum = 1; queryParams.pageNum = 1;
getList(); getList();
} }
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery=() =>{ const resetQuery = () => {
queryFormRef.value.resetFields(); queryFormRef.value?.resetFields();
handleQuery(); handleQuery();
} }
// //
const handleSelectionChange = (selection: UserVO[]) =>{ const handleSelectionChange = (selection: UserVO[]) => {
userIds.value = selection.map(item => item.userId); userIds.value = selection.map(item => item.userId);
multiple.value = !selection.length; multiple.value = !selection.length;
} }
/** 打开授权用户表弹窗 */ /** 打开授权用户表弹窗 */
const openSelectUser = () => { const openSelectUser = () => {
selectRef.value.show(); selectRef.value?.show();
} }
/** 取消授权按钮操作 */ /** 取消授权按钮操作 */
const cancelAuthUser = async (row: UserVO) => { const cancelAuthUser = async (row: UserVO) => {
await proxy?.$modal.confirm('确认要取消该用户"' + row.userName + '"角色吗?'); await proxy?.$modal.confirm('确认要取消该用户"' + row.userName + '"角色吗?');
await authUserCancel({ userId: row.userId, roleId: queryParams.roleId }); await authUserCancel({ userId: row.userId, roleId: queryParams.roleId });
getList(); await getList();
proxy?.$modal.msgSuccess("取消授权成功"); proxy?.$modal.msgSuccess("取消授权成功");
} }
/** 批量取消授权按钮操作 */ /** 批量取消授权按钮操作 */
const cancelAuthUserAll = async () => { const cancelAuthUserAll = async () => {
const roleId = queryParams.roleId; const roleId = queryParams.roleId;
const uIds = userIds.value.join(","); const uIds = userIds.value.join(",");
await proxy?.$modal.confirm("是否取消选中用户授权数据项?"); await proxy?.$modal.confirm("是否取消选中用户授权数据项?");
await authUserCancelAll({ roleId: roleId, userIds: uIds }); await authUserCancelAll({ roleId: roleId, userIds: uIds });
getList(); await getList();
proxy?.$modal.msgSuccess("取消授权成功"); proxy?.$modal.msgSuccess("取消授权成功");
} }
onMounted(() => { onMounted(() => {
getList(); getList();
}); });
</script> </script>

View File

@ -1,40 +1,42 @@
<template> <template>
<div class="p-2"> <div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="search" v-show="showSearch"> <div class="mb-[10px]" v-show="showSearch">
<el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px"> <el-card shadow="hover">
<el-form-item label="角色名称" prop="roleName"> <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px">
<el-input v-model="queryParams.roleName" placeholder="请输入角色名称" clearable style="width: 240px" @keyup.enter="handleQuery" /> <el-form-item label="角色名称" prop="roleName">
</el-form-item> <el-input v-model="queryParams.roleName" placeholder="请输入角色名称" clearable style="width: 240px" @keyup.enter="handleQuery" />
<el-form-item label="权限字符" prop="roleKey"> </el-form-item>
<el-input v-model="queryParams.roleKey" placeholder="请输入权限字符" clearable style="width: 240px" @keyup.enter="handleQuery" /> <el-form-item label="权限字符" prop="roleKey">
</el-form-item> <el-input v-model="queryParams.roleKey" placeholder="请输入权限字符" clearable style="width: 240px" @keyup.enter="handleQuery" />
<el-form-item label="状态" prop="status"> </el-form-item>
<el-select v-model="queryParams.status" placeholder="角色状态" clearable style="width: 240px"> <el-form-item label="状态" prop="status">
<el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" /> <el-select v-model="queryParams.status" placeholder="角色状态" clearable style="width: 240px">
</el-select> <el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-form-item> </el-select>
<el-form-item label="创建时间" style="width: 308px"> </el-form-item>
<el-date-picker <el-form-item label="创建时间" style="width: 308px">
v-model="dateRange" <el-date-picker
value-format="YYYY-MM-DD" v-model="dateRange"
type="daterange" value-format="YYYY-MM-DD"
range-separator="-" type="daterange"
start-placeholder="开始日期" range-separator="-"
end-placeholder="结束日期" start-placeholder="开始日期"
:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]" end-placeholder="结束日期"
></el-date-picker> :default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
</el-form-item> ></el-date-picker>
</el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" @click="handleQuery" icon="Search">搜索</el-button> <el-button type="primary" @click="handleQuery" icon="Search">搜索</el-button>
<el-button @click="resetQuery" icon="Refresh">重置</el-button> <el-button @click="resetQuery" icon="Refresh">重置</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
</el-card>
</div> </div>
</transition> </transition>
<el-card shadow="never"> <el-card shadow="hover">
<template #header> <template #header>
<el-row :gutter="10"> <el-row :gutter="10">
<el-col :span="1.5"> <el-col :span="1.5">
@ -119,7 +121,7 @@
<el-form-item label="状态"> <el-form-item label="状态">
<el-radio-group v-model="form.status"> <el-radio-group v-model="form.status">
<el-radio v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.value">{{ <el-radio v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.value">{{
dict.label dict.label
}}</el-radio> }}</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
@ -196,8 +198,6 @@ import { addRole, changeRoleStatus, dataScope, delRole, getRole, listRole, updat
import { roleMenuTreeselect, treeselect as menuTreeselect } from '@/api/system/menu/index'; import { roleMenuTreeselect, treeselect as menuTreeselect } from '@/api/system/menu/index';
import { RoleVO, RoleForm, RoleQuery, DeptTreeOption } from '@/api/system/role/types'; import { RoleVO, RoleForm, RoleQuery, DeptTreeOption } from '@/api/system/role/types';
import { MenuTreeOption, RoleMenuTree } from '@/api/system/menu/types'; import { MenuTreeOption, RoleMenuTree } from '@/api/system/menu/types';
import { ComponentInternalInstance } from 'vue';
import { ElTree, ElForm, DateModelType } from 'element-plus';
const router = useRouter(); const router = useRouter();
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@ -221,288 +221,282 @@ const openDataScope = ref(false)
/** 数据范围选项*/ /** 数据范围选项*/
const dataScopeOptions = ref([ const dataScopeOptions = ref([
{ value: "1", label: "全部数据权限" }, { value: "1", label: "全部数据权限" },
{ value: "2", label: "自定数据权限" }, { value: "2", label: "自定数据权限" },
{ value: "3", label: "本部门数据权限" }, { value: "3", label: "本部门数据权限" },
{ value: "4", label: "本部门及以下数据权限" }, { value: "4", label: "本部门及以下数据权限" },
{ value: "5", label: "仅本人数据权限" } { value: "5", label: "仅本人数据权限" }
]) ])
const queryFormRef = ref(ElForm); const queryFormRef = ref<ElFormInstance>();
const roleFormRef = ref(ElForm); const roleFormRef = ref<ElFormInstance>();
const dataScopeRef = ref(ElForm); const dataScopeRef = ref<ElFormInstance>();
const menuRef = ref(ElTree); const menuRef = ref<ElTreeInstance>();
const deptRef = ref(ElTree); const deptRef = ref<ElTreeInstance>();
const initForm: RoleForm = { const initForm: RoleForm = {
roleId: undefined, roleId: undefined,
roleSort: 1, roleSort: 1,
status: '0', status: '0',
roleName: '', roleName: '',
roleKey: '', roleKey: '',
menuCheckStrictly: true, menuCheckStrictly: true,
deptCheckStrictly: true, deptCheckStrictly: true,
remark: '', remark: '',
dataScope: '1', dataScope: '1',
menuIds: [], menuIds: [],
deptIds: [], deptIds: [],
} }
const data = reactive<PageData<RoleForm, RoleQuery>>({ const data = reactive<PageData<RoleForm, RoleQuery>>({
form: {...initForm}, form: { ...initForm },
queryParams: { queryParams: {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
roleName: '', roleName: '',
roleKey: '', roleKey: '',
status: '', status: '',
}, },
rules: { rules: {
roleName: [{ required: true, message: "角色名称不能为空", trigger: "blur" }], roleName: [{ required: true, message: "角色名称不能为空", trigger: "blur" }],
roleKey: [{ required: true, message: "权限字符不能为空", trigger: "blur" }], roleKey: [{ required: true, message: "权限字符不能为空", trigger: "blur" }],
roleSort: [{ required: true, message: "角色顺序不能为空", trigger: "blur" }] roleSort: [{ required: true, message: "角色顺序不能为空", trigger: "blur" }]
} }
}) })
const { form, queryParams, rules } = toRefs(data) const { form, queryParams, rules } = toRefs(data)
const dialog = reactive<DialogOption>({ const dialog = reactive<DialogOption>({
visible: false, visible: false,
title: '' title: ''
}); });
/** /**
* 查询角色列表 * 查询角色列表
*/ */
const getList = () => { const getList = () => {
loading.value = true loading.value = true
listRole(proxy?.addDateRange(queryParams.value, dateRange.value)).then(res => { listRole(proxy?.addDateRange(queryParams.value, dateRange.value)).then(res => {
roleList.value = res.rows roleList.value = res.rows
total.value = res.total total.value = res.total
loading.value = false loading.value = false
}) })
} }
/** /**
* 搜索按钮操作 * 搜索按钮操作
*/ */
const handleQuery = () => { const handleQuery = () => {
queryParams.value.pageNum = 1; queryParams.value.pageNum = 1;
getList(); getList();
} }
/** 重置 */ /** 重置 */
const resetQuery = () => { const resetQuery = () => {
dateRange.value = ['', ''] dateRange.value = ['', '']
queryFormRef.value.resetFields(); queryFormRef.value?.resetFields();
handleQuery(); handleQuery();
} }
/**删除按钮操作 */ /**删除按钮操作 */
const handleDelete = async (row?: RoleVO) => { const handleDelete = async (row?: RoleVO) => {
const roleids = row?.roleId || ids.value; const roleids = row?.roleId || ids.value;
await proxy?.$modal.confirm('是否确认删除角色编号为' + roleids + '数据项目'); await proxy?.$modal.confirm('是否确认删除角色编号为' + roleids + '数据项目');
await delRole(roleids); await delRole(roleids);
getList(); getList();
proxy?.$modal.msgSuccess('删除成功'); proxy?.$modal.msgSuccess('删除成功');
} }
/** 导出按钮操作 */ /** 导出按钮操作 */
const handleExport = () => { const handleExport = () => {
proxy?.download("system/role/export", { proxy?.download("system/role/export", {
...queryParams.value, ...queryParams.value,
}, `role_${new Date().getTime()}.xlsx`) }, `role_${new Date().getTime()}.xlsx`)
} }
/** 多选框选中数据 */ /** 多选框选中数据 */
const handleSelectionChange = (selection: RoleVO[]) => { const handleSelectionChange = (selection: RoleVO[]) => {
ids.value = selection.map((item: RoleVO) => item.roleId); ids.value = selection.map((item: RoleVO) => item.roleId);
single.value = selection.length != 1; single.value = selection.length != 1;
multiple.value = !selection.length; multiple.value = !selection.length;
} }
/** 角色状态修改 */ /** 角色状态修改 */
const handleStatusChange = async (row: RoleVO) => { const handleStatusChange = async (row: RoleVO) => {
let text = row.status === "0" ? "启用" : "停用"; let text = row.status === "0" ? "启用" : "停用";
try { try {
await proxy?.$modal.confirm('确认要"' + text + '""' + row.roleName + '"角色吗?'); await proxy?.$modal.confirm('确认要"' + text + '""' + row.roleName + '"角色吗?');
await changeRoleStatus(row.roleId, row.status); await changeRoleStatus(row.roleId, row.status);
proxy?.$modal.msgSuccess(text + "成功"); proxy?.$modal.msgSuccess(text + "成功");
} catch { } catch {
row.status = row.status === "0" ? "1" : "0"; row.status = row.status === "0" ? "1" : "0";
} }
} }
/** 分配用户 */ /** 分配用户 */
const handleAuthUser = (row: RoleVO) => { const handleAuthUser = (row: RoleVO) => {
router.push("/system/role-auth/user/" + row.roleId); router.push("/system/role-auth/user/" + row.roleId);
} }
/** 查询菜单树结构 */ /** 查询菜单树结构 */
const getMenuTreeselect = async () => { const getMenuTreeselect = async () => {
const res = await menuTreeselect(); const res = await menuTreeselect();
menuOptions.value = res.data; menuOptions.value = res.data;
} }
/** 所有部门节点数据 */ /** 所有部门节点数据 */
const getDeptAllCheckedKeys = () => { const getDeptAllCheckedKeys = (): any => {
// //
let checkedKeys = deptRef.value.getCheckedKeys(); let checkedKeys = deptRef.value?.getCheckedKeys();
// //
let halfCheckedKeys = deptRef.value.getHalfCheckedKeys(); let halfCheckedKeys = deptRef.value?.getHalfCheckedKeys();
checkedKeys.unshift.apply(checkedKeys, halfCheckedKeys); if (halfCheckedKeys) {
return checkedKeys checkedKeys?.unshift.apply(checkedKeys, halfCheckedKeys);
}
return checkedKeys
} }
/** 重置新增的表单以及其他数据 */ /** 重置新增的表单以及其他数据 */
const reset = () => { const reset = () => {
menuRef.value.setCheckedKeys([]); menuRef.value?.setCheckedKeys([]);
menuExpand.value = false menuExpand.value = false
menuNodeAll.value = false menuNodeAll.value = false
deptExpand.value = true deptExpand.value = true
deptNodeAll.value = false deptNodeAll.value = false
form.value = { ...initForm }; form.value = { ...initForm };
roleFormRef.value.resetFields(); roleFormRef.value?.resetFields();
} }
/** 添加角色 */ /** 添加角色 */
const handleAdd = () => { const handleAdd = () => {
dialog.visible = true; reset();
dialog.title = "添加角色"; getMenuTreeselect();
nextTick(() => { dialog.visible = true;
reset(); dialog.title = "添加角色";
getMenuTreeselect();
})
} }
/** 修改角色 */ /** 修改角色 */
const handleUpdate = async (row?: RoleVO) => { const handleUpdate = async (row?: RoleVO) => {
const roleId = row?.roleId || ids.value[0] reset();
const roleMenu = getRoleMenuTreeselect(roleId) const roleId = row?.roleId || ids.value[0]
const { data } = await getRole(roleId); const { data } = await getRole(roleId);
dialog.visible = true; Object.assign(form.value, data);
dialog.title = "修改角色"; form.value.roleSort = Number(form.value.roleSort);
const res = await getRoleMenuTreeselect(roleId);
dialog.title = "修改角色";
dialog.visible = true;
res.checkedKeys.forEach((v) => {
nextTick(() => { nextTick(() => {
reset(); menuRef.value?.setChecked(v, true, false);
Object.assign(form.value, data);
form.value.roleSort = Number(form.value.roleSort);
nextTick(async () => {
const res = await roleMenu;
let checkedKeys = res.checkedKeys;
checkedKeys.forEach((v) => {
nextTick(() => {
menuRef.value.setChecked(v, true, false);
})
})
})
}) })
})
} }
/** 根据角色ID查询菜单树结构 */ /** 根据角色ID查询菜单树结构 */
const getRoleMenuTreeselect = (roleId: string | number) => { const getRoleMenuTreeselect = (roleId: string | number) => {
return roleMenuTreeselect(roleId).then((res): RoleMenuTree => { return roleMenuTreeselect(roleId).then((res): RoleMenuTree => {
menuOptions.value = res.data.menus; menuOptions.value = res.data.menus;
return res.data; return res.data;
}) })
} }
/** 根据角色ID查询部门树结构 */ /** 根据角色ID查询部门树结构 */
const getRoleDeptTreeSelect = async (roleId: string | number) => { const getRoleDeptTreeSelect = async (roleId: string | number) => {
const res = await deptTreeSelect(roleId); const res = await deptTreeSelect(roleId);
deptOptions.value = res.data.depts; deptOptions.value = res.data.depts;
return res.data; return res.data;
} }
/** 树权限(展开/折叠)*/ /** 树权限(展开/折叠)*/
const handleCheckedTreeExpand = (value: any, type: string) => { const handleCheckedTreeExpand = (value: boolean, type: string) => {
if (type == "menu") { if (type == "menu") {
let treeList = menuOptions.value; let treeList = menuOptions.value;
for (let i = 0; i < treeList.length; i++) { for (let i = 0; i < treeList.length; i++) {
menuRef.value.store.nodesMap[treeList[i].id].expanded = value; if (menuRef.value) {
} menuRef.value.store.nodesMap[treeList[i].id].expanded = value;
} else if (type == "dept") { }
let treeList = deptOptions.value;
for (let i = 0; i < treeList.length; i++) {
deptRef.value.store.nodesMap[treeList[i].id].expanded = value;
}
} }
} else if (type == "dept") {
let treeList = deptOptions.value;
for (let i = 0; i < treeList.length; i++) {
if (deptRef.value) {
deptRef.value.store.nodesMap[treeList[i].id].expanded = value;
}
}
}
} }
/** 树权限(全选/全不选) */ /** 树权限(全选/全不选) */
const handleCheckedTreeNodeAll = (value: any, type: string) => { const handleCheckedTreeNodeAll = (value: any, type: string) => {
if (type == "menu") { if (type == "menu") {
menuRef.value.setCheckedNodes(value ? menuOptions.value : []); menuRef.value?.setCheckedNodes(value ? menuOptions.value as any : []);
} else if (type == "dept") { } else if (type == "dept") {
deptRef.value.setCheckedNodes(value ? deptOptions.value : []); deptRef.value?.setCheckedNodes(value ? deptOptions.value as any : []);
} }
} }
/** 树权限(父子联动) */ /** 树权限(父子联动) */
const handleCheckedTreeConnect = (value: any, type: string) => { const handleCheckedTreeConnect = (value: any, type: string) => {
if (type == "menu") { if (type == "menu") {
form.value.menuCheckStrictly = value; form.value.menuCheckStrictly = value;
} else if (type == "dept") { } else if (type == "dept") {
form.value.deptCheckStrictly = value; form.value.deptCheckStrictly = value;
} }
} }
/** 所有菜单节点数据 */ /** 所有菜单节点数据 */
const getMenuAllCheckedKeys = () => { const getMenuAllCheckedKeys = (): any => {
// //
let checkedKeys = menuRef.value.getCheckedKeys(); let checkedKeys = menuRef.value?.getCheckedKeys();
// //
let halfCheckedKeys = menuRef.value.getHalfCheckedKeys(); let halfCheckedKeys = menuRef.value?.getHalfCheckedKeys();
checkedKeys.unshift.apply(checkedKeys, halfCheckedKeys); if (halfCheckedKeys) {
return checkedKeys; checkedKeys?.unshift.apply(checkedKeys, halfCheckedKeys);
}
return checkedKeys;
} }
/** 提交按钮 */ /** 提交按钮 */
const submitForm = () => { const submitForm = () => {
roleFormRef.value.validate(async (valid: boolean) => { roleFormRef.value?.validate(async (valid: boolean) => {
if (valid) { if (valid) {
form.value.menuIds = getMenuAllCheckedKeys() form.value.menuIds = getMenuAllCheckedKeys()
form.value.roleId ? await updateRole(form.value) : await addRole(form.value); form.value.roleId ? await updateRole(form.value) : await addRole(form.value);
proxy?.$modal.msgSuccess("操作成功") proxy?.$modal.msgSuccess("操作成功")
dialog.visible = false dialog.visible = false
getList() getList()
} }
}) })
} }
/** 取消按钮 */ /** 取消按钮 */
const cancel = () => { const cancel = () => {
reset() reset()
dialog.visible = false; dialog.visible = false;
} }
/** 选择角色权限范围触发 */ /** 选择角色权限范围触发 */
const dataScopeSelectChange = (value: string) => { const dataScopeSelectChange = (value: string) => {
if (value !== "2") { if (value !== "2") {
deptRef.value.setCheckedKeys([]) deptRef.value?.setCheckedKeys([])
} }
} }
/** 分配数据权限操作 */ /** 分配数据权限操作 */
const handleDataScope = async (row: RoleVO) => { const handleDataScope = async (row: RoleVO) => {
const roleDeptTreeselect = getRoleDeptTreeSelect(row.roleId); const response = await getRole(row.roleId);
const response = await getRole(row.roleId); Object.assign(form.value, response.data);
Object.assign(form.value, response.data); const res = await getRoleDeptTreeSelect(row.roleId);
openDataScope.value = true; openDataScope.value = true;
dialog.title = "分配数据权限"; dialog.title = "分配数据权限";
nextTick(async () => { await nextTick(() => {
const res = await roleDeptTreeselect; deptRef.value?.setCheckedKeys(res.checkedKeys);
nextTick(() => { })
if (deptRef.value) {
deptRef.value.setCheckedKeys(res.checkedKeys);
}
})
})
} }
/** 提交按钮(数据权限) */ /** 提交按钮(数据权限) */
const submitDataScope = async () => { const submitDataScope = async () => {
if (form.value.roleId) { if (form.value.roleId) {
form.value.deptIds = getDeptAllCheckedKeys(); form.value.deptIds = getDeptAllCheckedKeys();
await dataScope(form.value); await dataScope(form.value);
proxy?.$modal.msgSuccess("修改成功"); proxy?.$modal.msgSuccess("修改成功");
openDataScope.value = false; openDataScope.value = false;
getList(); getList();
} }
} }
/** 取消按钮(数据权限)*/ /** 取消按钮(数据权限)*/
const cancelDataScope = () => { const cancelDataScope = () => {
dataScopeRef.value.resetFields(); dataScopeRef.value?.resetFields();
form.value = {...initForm}; form.value = { ...initForm };
openDataScope.value = false; openDataScope.value = false;
} }
onMounted(() => { onMounted(() => {
getList(); getList();
}); });
</script> </script>

View File

@ -47,14 +47,12 @@
import { authUserSelectAll, unallocatedUserList } from "@/api/system/role"; import { authUserSelectAll, unallocatedUserList } from "@/api/system/role";
import { UserVO } from '@/api/system/user/types'; import { UserVO } from '@/api/system/user/types';
import { UserQuery } from '@/api/system/user/types'; import { UserQuery } from '@/api/system/user/types';
import { ComponentInternalInstance } from 'vue';
import { ElForm, ElTable } from 'element-plus';
const props = defineProps({ const props = defineProps({
roleId: { roleId: {
type: [Number, String] type: [Number, String]
} }
}) })
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@ -66,67 +64,68 @@ const total = ref(0);
const userIds = ref<Array<string | number>>([]); const userIds = ref<Array<string | number>>([]);
const queryParams = reactive<UserQuery>({ const queryParams = reactive<UserQuery>({
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
roleId: undefined, roleId: undefined,
userName: undefined, userName: undefined,
phonenumber: undefined phonenumber: undefined
}) })
const tableRef = ref(ElTable); const tableRef = ref<ElTableInstance>();
const queryFormRef = ref(ElForm); const queryFormRef = ref<ElFormInstance>();
const show = () => { const show = () => {
queryParams.roleId = props.roleId; queryParams.roleId = props.roleId;
getList(); getList();
visible.value = true; visible.value = true;
} }
/** /**
* 选择行 * 选择行
*/ */
const clickRow = (row: any) => { const clickRow = (row: any) => {
tableRef.value.toggleRowSelection(row); // elebug
tableRef.value?.toggleRowSelection(row, false);
} }
/** 多选框选中数据 */ /** 多选框选中数据 */
const handleSelectionChange = (selection: UserVO[]) => { const handleSelectionChange = (selection: UserVO[]) => {
userIds.value = selection.map((item: UserVO) => item.userId); userIds.value = selection.map((item: UserVO) => item.userId);
} }
/** 查询数据 */ /** 查询数据 */
const getList = async () => { const getList = async () => {
const res = await unallocatedUserList(queryParams); const res = await unallocatedUserList(queryParams);
userList.value = res.rows; userList.value = res.rows;
total.value = res.total; total.value = res.total;
} }
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
queryParams.pageNum = 1; queryParams.pageNum = 1;
getList(); getList();
} }
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
queryFormRef.value.resetFields(); queryFormRef.value?.resetFields();
getList(); getList();
} }
const emit = defineEmits(["ok"]); const emit = defineEmits(["ok"]);
/**选择授权用户操作 */ /**选择授权用户操作 */
const handleSelectUser = async () => { const handleSelectUser = async () => {
const roleId = queryParams.roleId; const roleId = queryParams.roleId;
const ids = userIds.value.join(','); const ids = userIds.value.join(',');
if (ids == "") { if (ids == "") {
proxy?.$modal.msgError('请选择要分配的用户'); proxy?.$modal.msgError('请选择要分配的用户');
return; return;
} }
await authUserSelectAll({ roleId, userIds: ids }); await authUserSelectAll({ roleId, userIds: ids });
proxy?.$modal.msgSuccess('分配成功'); proxy?.$modal.msgSuccess('分配成功');
emit('ok'); emit('ok');
visible.value = false; visible.value = false;
} }
// //
defineExpose({ defineExpose({
show, show,
}); });
</script> </script>

View File

@ -1,29 +1,31 @@
<template> <template>
<div class="p-2"> <div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="search" v-show="showSearch"> <div class="mb-[10px]" v-show="showSearch">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px"> <el-card shadow="hover">
<el-form-item label="租户编号" prop="tenantId"> <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
<el-input v-model="queryParams.tenantId" placeholder="请输入租户编号" clearable @keyup.enter="handleQuery" /> <el-form-item label="租户编号" prop="tenantId">
</el-form-item> <el-input v-model="queryParams.tenantId" placeholder="请输入租户编号" clearable @keyup.enter="handleQuery" />
<el-form-item label="联系人" prop="contactUserName"> </el-form-item>
<el-input v-model="queryParams.contactUserName" placeholder="请输入联系人" clearable @keyup.enter="handleQuery" /> <el-form-item label="联系人" prop="contactUserName">
</el-form-item> <el-input v-model="queryParams.contactUserName" placeholder="请输入联系人" clearable @keyup.enter="handleQuery" />
<el-form-item label="联系电话" prop="contactPhone"> </el-form-item>
<el-input v-model="queryParams.contactPhone" placeholder="请输入联系电话" clearable @keyup.enter="handleQuery" /> <el-form-item label="联系电话" prop="contactPhone">
</el-form-item> <el-input v-model="queryParams.contactPhone" placeholder="请输入联系电话" clearable @keyup.enter="handleQuery" />
<el-form-item label="企业名称" prop="companyName"> </el-form-item>
<el-input v-model="queryParams.companyName" placeholder="请输入企业名称" clearable @keyup.enter="handleQuery" /> <el-form-item label="企业名称" prop="companyName">
</el-form-item> <el-input v-model="queryParams.companyName" placeholder="请输入企业名称" clearable @keyup.enter="handleQuery" />
<el-form-item> </el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button> <el-form-item>
<el-button icon="Refresh" @click="resetQuery">重置</el-button> <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
</el-form-item> <el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form> </el-form-item>
</el-form>
</el-card>
</div> </div>
</transition> </transition>
<el-card shadow="never"> <el-card shadow="hover">
<template #header> <template #header>
<el-row :gutter="10" class="mb8"> <el-row :gutter="10" class="mb8">
<el-col :span="1.5"> <el-col :span="1.5">
@ -64,7 +66,7 @@
<el-switch v-model="scope.row.status" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)"></el-switch> <el-switch v-model="scope.row.status" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)"></el-switch>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width"> <el-table-column width="150" label="操作" align="center" fixed="right" class-name="small-padding fixed-width">
<template #default="scope"> <template #default="scope">
<el-tooltip content="修改" placement="top"> <el-tooltip content="修改" placement="top">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:tenant:edit']"></el-button> <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:tenant:edit']"></el-button>
@ -80,7 +82,7 @@
</el-table-column> </el-table-column>
</el-table> </el-table>
<pagination v-show="total>0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card> </el-card>
<!-- 添加或修改租户对话框 --> <!-- 添加或修改租户对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body> <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
@ -143,8 +145,6 @@ import { listTenant, getTenant, delTenant, addTenant, updateTenant, changeTenant
import { selectTenantPackage } from '@/api/system/tenantPackage'; import { selectTenantPackage } from '@/api/system/tenantPackage';
import { TenantForm, TenantQuery, TenantVO } from '@/api/system/tenant/types'; import { TenantForm, TenantQuery, TenantVO } from '@/api/system/tenant/types';
import { TenantPkgVO } from '@/api/system/tenantPackage/types'; import { TenantPkgVO } from '@/api/system/tenantPackage/types';
import { ComponentInternalInstance } from 'vue';
import { ElForm } from 'element-plus';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@ -158,196 +158,190 @@ const single = ref(true);
const multiple = ref(true); const multiple = ref(true);
const total = ref(0); const total = ref(0);
const queryFormRef = ref(ElForm); const queryFormRef = ref<ElFormInstance>();
const tenantFormRef = ref(ElForm); const tenantFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({ const dialog = reactive<DialogOption>({
visible: false, visible: false,
title: '' title: ''
}); });
const initFormData: TenantForm = { const initFormData: TenantForm = {
id: undefined, id: undefined,
tenantId: undefined, tenantId: undefined,
contactUserName: '', contactUserName: '',
contactPhone: '', contactPhone: '',
username: '', username: '',
password: '', password: '',
companyName: '', companyName: '',
licenseNumber: '', licenseNumber: '',
domain: '', domain: '',
address: '', address: '',
intro: '', intro: '',
remark: '', remark: '',
packageId: '', packageId: '',
expireTime: '', expireTime: '',
accountCount: 0, accountCount: 0,
status: '0', status: '0',
} }
const data = reactive<PageData<TenantForm, TenantQuery>>({ const data = reactive<PageData<TenantForm, TenantQuery>>({
form: {...initFormData}, form: { ...initFormData },
queryParams: { queryParams: {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
tenantId: '', tenantId: '',
contactUserName: '', contactUserName: '',
contactPhone: '', contactPhone: '',
companyName: '' companyName: ''
}, },
rules: { rules: {
id: [{ required: true, message: "id不能为空", trigger: "blur" }], id: [{ required: true, message: "id不能为空", trigger: "blur" }],
tenantId: [{ required: true, message: "租户编号不能为空", trigger: "blur" }], tenantId: [{ required: true, message: "租户编号不能为空", trigger: "blur" }],
contactUserName: [{ required: true, message: "联系人不能为空", trigger: "blur" }], contactUserName: [{ required: true, message: "联系人不能为空", trigger: "blur" }],
contactPhone: [{ required: true, message: "联系电话不能为空", trigger: "blur" }], contactPhone: [{ required: true, message: "联系电话不能为空", trigger: "blur" }],
companyName: [{ required: true, message: "企业名称不能为空", trigger: "blur" }], companyName: [{ required: true, message: "企业名称不能为空", trigger: "blur" }],
username: [ username: [
{ required: true, message: "用户名不能为空", trigger: "blur" }, { required: true, message: "用户名不能为空", trigger: "blur" },
{ min: 2, max: 20, message: '用户名称长度必须介于 2 和 20 之间', trigger: 'blur' } { min: 2, max: 20, message: '用户名称长度必须介于 2 和 20 之间', trigger: 'blur' }
], ],
password: [ password: [
{ required: true, message: "密码不能为空", trigger: "blur" }, { required: true, message: "密码不能为空", trigger: "blur" },
{ min: 5, max: 20, message: '用户密码长度必须介于 5 和 20 之间', trigger: 'blur' } { min: 5, max: 20, message: '用户密码长度必须介于 5 和 20 之间', trigger: 'blur' }
] ]
} }
}); });
const { queryParams, form, rules } = toRefs(data); const { queryParams, form, rules } = toRefs(data);
/** 查询所有租户套餐 */ /** 查询所有租户套餐 */
const getTenantPackage = async () => { const getTenantPackage = async () => {
const res = await selectTenantPackage() const res = await selectTenantPackage()
packageList.value = res.data; packageList.value = res.data;
} }
/** 查询租户列表 */ /** 查询租户列表 */
const getList = async () => { const getList = async () => {
loading.value = true; loading.value = true;
const res = await listTenant(queryParams.value); const res = await listTenant(queryParams.value);
tenantList.value = res.rows; tenantList.value = res.rows;
total.value = res.total; total.value = res.total;
loading.value = false; loading.value = false;
} }
// //
const handleStatusChange = async (row: TenantVO) => { const handleStatusChange = async (row: TenantVO) => {
let text = row.status === "0" ? "启用" : "停用"; let text = row.status === "0" ? "启用" : "停用";
try { try {
await proxy?.$modal.confirm('确认要"' + text + '""' + row.companyName + '"租户吗?'); await proxy?.$modal.confirm('确认要"' + text + '""' + row.companyName + '"租户吗?');
await changeTenantStatus(row.id, row.tenantId, row.status); await changeTenantStatus(row.id, row.tenantId, row.status);
proxy?.$modal.msgSuccess(text + "成功"); proxy?.$modal.msgSuccess(text + "成功");
} catch { } catch {
row.status = row.status === "0" ? "1" : "0"; row.status = row.status === "0" ? "1" : "0";
} }
} }
// //
const cancel = () => { const cancel = () => {
reset(); reset();
dialog.visible = false; dialog.visible = false;
} }
// //
const reset = () => { const reset = () => {
form.value = {...initFormData}; form.value = { ...initFormData };
tenantFormRef.value.resetFields(); tenantFormRef.value?.resetFields();
} }
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
queryParams.value.pageNum = 1; queryParams.value.pageNum = 1;
getList(); getList();
} }
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
queryFormRef.value.resetFields(); queryFormRef.value?.resetFields();
handleQuery(); handleQuery();
} }
// //
const handleSelectionChange = (selection: TenantVO[]) => { const handleSelectionChange = (selection: TenantVO[]) => {
ids.value = selection.map(item => item.id); ids.value = selection.map(item => item.id);
single.value = selection.length != 1; single.value = selection.length != 1;
multiple.value = !selection.length; multiple.value = !selection.length;
} }
/** 新增按钮操作 */ /** 新增按钮操作 */
const handleAdd = () => { const handleAdd = () => {
dialog.visible = true; reset();
dialog.title = "添加租户"; getTenantPackage();
nextTick(() => { dialog.visible = true;
reset(); dialog.title = "添加租户";
getTenantPackage();
})
} }
/** 修改按钮操作 */ /** 修改按钮操作 */
const handleUpdate = (row?: TenantVO) => { const handleUpdate = async (row?: TenantVO) => {
loading.value = true; reset();
dialog.visible = true; await getTenantPackage();
dialog.title = "修改租户"; const _id = row?.id || ids.value[0];
nextTick(async () => { const res = await getTenant(_id);
reset(); Object.assign(form.value, res.data)
getTenantPackage(); dialog.visible = true;
const _id = row?.id || ids.value[0]; dialog.title = "修改租户";
const res = await getTenant(_id);
loading.value = false;
Object.assign(form.value, res.data)
})
} }
/** 提交按钮 */ /** 提交按钮 */
const submitForm = () => { const submitForm = () => {
tenantFormRef.value.validate(async (valid: boolean) => { tenantFormRef.value?.validate(async (valid: boolean) => {
if (valid) { if (valid) {
buttonLoading.value = true; buttonLoading.value = true;
if (form.value.id) { if (form.value.id) {
await updateTenant(form.value).finally(() => buttonLoading.value = false); await updateTenant(form.value).finally(() => buttonLoading.value = false);
} else { } else {
await addTenant(form.value).finally(() => buttonLoading.value = false); await addTenant(form.value).finally(() => buttonLoading.value = false);
} }
proxy?.$modal.msgSuccess("操作成功"); proxy?.$modal.msgSuccess("操作成功");
dialog.visible = false; dialog.visible = false;
getList(); await getList();
} }
}); });
} }
/** 删除按钮操作 */ /** 删除按钮操作 */
const handleDelete = async (row?: TenantVO) => { const handleDelete = async (row?: TenantVO) => {
const _ids = row?.id || ids.value; const _ids = row?.id || ids.value;
await proxy?.$modal.confirm('是否确认删除租户编号为"' + _ids + '"的数据项?') await proxy?.$modal.confirm('是否确认删除租户编号为"' + _ids + '"的数据项?')
loading.value = true; loading.value = true;
await delTenant(_ids).finally(() => loading.value = false); await delTenant(_ids).finally(() => loading.value = false);
getList(); await getList();
proxy?.$modal.msgSuccess("删除成功"); proxy?.$modal.msgSuccess("删除成功");
} }
/** 同步租户套餐按钮操作 */ /** 同步租户套餐按钮操作 */
const handleSyncTenantPackage = async (row: TenantVO) => { const handleSyncTenantPackage = async (row: TenantVO) => {
try { try {
await proxy?.$modal.confirm('是否确认同步租户套餐租户编号为"' + row.tenantId + '"的数据项?'); await proxy?.$modal.confirm('是否确认同步租户套餐租户编号为"' + row.tenantId + '"的数据项?');
loading.value = true; loading.value = true;
await syncTenantPackage(row.tenantId, row.packageId); await syncTenantPackage(row.tenantId, row.packageId);
getList(); await getList();
proxy?.$modal.msgSuccess("同步成功"); proxy?.$modal.msgSuccess("同步成功");
} catch {return} finally { } catch { return } finally {
loading.value = false; loading.value = false;
} }
} }
/** 导出按钮操作 */ /** 导出按钮操作 */
const handleExport = () => { const handleExport = () => {
proxy?.download('system/tenant/export', { proxy?.download('system/tenant/export', {
...queryParams.value ...queryParams.value
}, `tenant_${new Date().getTime()}.xlsx`) }, `tenant_${new Date().getTime()}.xlsx`)
} }
onMounted(() => { onMounted(() => {
getList(); getList();
}) })
</script> </script>

View File

@ -1,37 +1,39 @@
<template> <template>
<div class="p-2"> <div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="search" v-show="showSearch"> <div class="mb-[10px]" v-show="showSearch">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px"> <el-card shadow="hover">
<el-form-item label="套餐名称" prop="packageName"> <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
<el-input v-model="queryParams.packageName" placeholder="请输入套餐名称" clearable @keyup.enter="handleQuery" /> <el-form-item label="套餐名称" prop="packageName">
</el-form-item> <el-input v-model="queryParams.packageName" placeholder="请输入套餐名称" clearable @keyup.enter="handleQuery" />
<el-form-item> </el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button> <el-form-item>
<el-button icon="Refresh" @click="resetQuery">重置</el-button> <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
</el-form-item> <el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form> </el-form-item>
</el-form>
</el-card>
</div> </div>
</transition> </transition>
<el-card shadow="never"> <el-card shadow="hover">
<template #header> <template #header>
<el-row :gutter="10" class="mb8"> <el-row :gutter="10" class="mb8">
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:tenantPackage:add']">新增</el-button> <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:tenantPackage:add']"> 新增 </el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:tenantPackage:edit']" <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:tenantPackage:edit']">
>修改</el-button 修改
> </el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:tenantPackage:remove']" <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:tenantPackage:remove']">
>删除</el-button 删除
> </el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:tenantPackage:export']">导出</el-button> <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:tenantPackage:export']">导出 </el-button>
</el-col> </el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row> </el-row>
@ -53,13 +55,13 @@
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:tenantPackage:edit']"></el-button> <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:tenantPackage:edit']"></el-button>
</el-tooltip> </el-tooltip>
<el-tooltip content="删除" placement="top"> <el-tooltip content="删除" placement="top">
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:tenantPackage:remove']"> </el-button> <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:tenantPackage:remove']"></el-button>
</el-tooltip> </el-tooltip>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<pagination v-show="total>0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card> </el-card>
<!-- 添加或修改租户套餐对话框 --> <!-- 添加或修改租户套餐对话框 -->
@ -70,8 +72,8 @@
</el-form-item> </el-form-item>
<el-form-item label="关联菜单"> <el-form-item label="关联菜单">
<el-checkbox v-model="menuExpand" @change="handleCheckedTreeExpand($event, 'menu')">展开/折叠</el-checkbox> <el-checkbox v-model="menuExpand" @change="handleCheckedTreeExpand($event, 'menu')">展开/折叠</el-checkbox>
<el-checkbox v-model="menuNodeAll" @change="handleCheckedTreeNodeAll($event, 'menu')">全选/全不选</el-checkbox> <el-checkbox v-model="menuNodeAll" @change="handleCheckedTreeNodeAll($event, 'menu')">全选/全不选 </el-checkbox>
<el-checkbox v-model="form.menuCheckStrictly" @change="handleCheckedTreeConnect($event, 'menu')">父子联动</el-checkbox> <el-checkbox v-model="form.menuCheckStrictly" @change="handleCheckedTreeConnect($event, 'menu')">父子联动 </el-checkbox>
<el-tree <el-tree
class="tree-border" class="tree-border"
:data="menuOptions" :data="menuOptions"
@ -98,12 +100,17 @@
</template> </template>
<script setup name="TenantPackage" lang="ts"> <script setup name="TenantPackage" lang="ts">
import { listTenantPackage, getTenantPackage, delTenantPackage, addTenantPackage, updateTenantPackage, changePackageStatus } from "@/api/system/tenantPackage"; import {
listTenantPackage,
getTenantPackage,
delTenantPackage,
addTenantPackage,
updateTenantPackage,
changePackageStatus
} from "@/api/system/tenantPackage";
import { treeselect as menuTreeselect, tenantPackageMenuTreeselect } from "@/api/system/menu"; import { treeselect as menuTreeselect, tenantPackageMenuTreeselect } from "@/api/system/menu";
import { ComponentInternalInstance } from "vue";
import { TenantPkgForm, TenantPkgQuery, TenantPkgVO } from "@/api/system/tenantPackage/types"; import { TenantPkgForm, TenantPkgQuery, TenantPkgVO } from "@/api/system/tenantPackage/types";
import { MenuTreeOption } from "@/api/system/menu/types"; import { MenuTreeOption } from "@/api/system/menu/types";
import { CheckboxValueType, ElTree, ElForm } from 'element-plus';
import to from "await-to-js"; import to from "await-to-js";
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@ -120,212 +127,206 @@ const menuExpand = ref(false);
const menuNodeAll = ref(false); const menuNodeAll = ref(false);
const menuOptions = ref<MenuTreeOption[]>([]); const menuOptions = ref<MenuTreeOption[]>([]);
const menuTreeRef = ref(ElTree); const menuTreeRef = ref<ElTreeInstance>();
const queryFormRef = ref(ElForm); const queryFormRef = ref<ElFormInstance>();
const tenantPackageFormRef = ref(ElForm); const tenantPackageFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({ const dialog = reactive<DialogOption>({
visible: false, visible: false,
title: '' title: ""
}); });
const initFormData: TenantPkgForm = { const initFormData: TenantPkgForm = {
packageId: undefined, packageId: undefined,
packageName: '', packageName: "",
menuIds: '', menuIds: "",
remark: '', remark: "",
menuCheckStrictly: true menuCheckStrictly: true
}; };
const data = reactive<PageData<TenantPkgForm, TenantPkgQuery>>({ const data = reactive<PageData<TenantPkgForm, TenantPkgQuery>>({
form: {...initFormData}, form: { ...initFormData },
queryParams: { queryParams: {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
packageName: '' packageName: ""
}, },
rules: { rules: {
packageId: [{ required: true, message: "租户套餐id不能为空", trigger: "blur" }], packageId: [{ required: true, message: "租户套餐id不能为空", trigger: "blur" }],
packageName: [{ required: true, message: "套餐名称不能为空", trigger: "blur" }] packageName: [{ required: true, message: "套餐名称不能为空", trigger: "blur" }]
} }
}); });
const { queryParams, form, rules } = toRefs(data); const { queryParams, form, rules } = toRefs(data);
/** 查询菜单树结构 */ /** 查询菜单树结构 */
const getMenuTreeselect = async() => { const getMenuTreeselect = async () => {
const { data } = await menuTreeselect(); const { data } = await menuTreeselect();
menuOptions.value = data; menuOptions.value = data;
} };
// //
const getMenuAllCheckedKeys = () => { const getMenuAllCheckedKeys = (): any => {
// //
let checkedKeys = menuTreeRef.value.getCheckedKeys(); let checkedKeys = menuTreeRef.value?.getCheckedKeys();
// //
let halfCheckedKeys = menuTreeRef.value.getHalfCheckedKeys(); let halfCheckedKeys = menuTreeRef.value?.getHalfCheckedKeys();
checkedKeys.unshift.apply(checkedKeys, halfCheckedKeys); if (halfCheckedKeys) {
return checkedKeys; checkedKeys?.unshift.apply(checkedKeys, halfCheckedKeys);
} }
return checkedKeys;
};
/** 根据租户套餐ID查询菜单树结构 */ /** 根据租户套餐ID查询菜单树结构 */
const getPackageMenuTreeselect = async(packageId: string | number) => { const getPackageMenuTreeselect = async (packageId: string | number) => {
const res = await tenantPackageMenuTreeselect(packageId); const res = await tenantPackageMenuTreeselect(packageId);
menuOptions.value = res.data.menus; menuOptions.value = res.data.menus;
return Promise.resolve(res); return Promise.resolve(res);
} };
/** 查询租户套餐列表 */ /** 查询租户套餐列表 */
const getList = async () => { const getList = async () => {
loading.value = true; loading.value = true;
const res = await listTenantPackage(queryParams.value); const res = await listTenantPackage(queryParams.value);
tenantPackageList.value = res.rows; tenantPackageList.value = res.rows;
total.value = res.total; total.value = res.total;
loading.value = false; loading.value = false;
} };
// //
const handleStatusChange = async (row: TenantPkgVO) => { const handleStatusChange = async (row: TenantPkgVO) => {
let text = row.status === "0" ? "启用" : "停用"; let text = row.status === "0" ? "启用" : "停用";
const [err] = await to(proxy?.$modal.confirm('确认要"' + text + '""' + row.packageName + '"套餐吗?') as Promise<any>) const [err] = await to(proxy?.$modal.confirm("确认要\"" + text + "\"\"" + row.packageName + "\"套餐吗?") as Promise<any>);
if (err) { if (err) {
row.status = row.status === "0" ? "1" : "0"; row.status = row.status === "0" ? "1" : "0";
} else { } else {
await changePackageStatus(row.packageId, row.status); await changePackageStatus(row.packageId, row.status);
proxy?.$modal.msgSuccess(text + "成功"); proxy?.$modal.msgSuccess(text + "成功");
} }
} };
// //
const cancel = () => { const cancel = () => {
reset(); reset();
dialog.visible = false; dialog.visible = false;
} };
// //
const reset = () => { const reset = () => {
menuTreeRef.value.setCheckedKeys([]); menuTreeRef.value?.setCheckedKeys([]);
menuExpand.value = false; menuExpand.value = false;
menuNodeAll.value = false; menuNodeAll.value = false;
form.value = {...initFormData}; form.value = { ...initFormData };
tenantPackageFormRef.value.resetFields(); tenantPackageFormRef.value?.resetFields();
} };
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
queryParams.value.pageNum = 1; queryParams.value.pageNum = 1;
getList(); getList();
} };
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
queryFormRef.value.resetFields(); queryFormRef.value?.resetFields();
handleQuery(); handleQuery();
} };
// //
const handleSelectionChange = (selection: TenantPkgVO[]) => { const handleSelectionChange = (selection: TenantPkgVO[]) => {
ids.value = selection.map(item => item.packageId); ids.value = selection.map(item => item.packageId);
single.value = selection.length != 1; single.value = selection.length != 1;
multiple.value = !selection.length; multiple.value = !selection.length;
} };
// / // /
const handleCheckedTreeExpand = (value: CheckboxValueType, type: string) => { const handleCheckedTreeExpand = (value: CheckboxValueType, type: string) => {
if (type == 'menu') { if (type == "menu") {
let treeList = menuOptions.value; let treeList = menuOptions.value;
for (let i = 0; i < treeList.length; i++) { for (let i = 0; i < treeList.length; i++) {
menuTreeRef.value.store.nodesMap[treeList[i].id].expanded = value; if (menuTreeRef.value) {
} menuTreeRef.value.store.nodesMap[treeList[i].id].expanded = value as boolean;
}
} }
} }
};
// / // /
const handleCheckedTreeNodeAll = (value: CheckboxValueType, type: string) => { const handleCheckedTreeNodeAll = (value: CheckboxValueType, type: string) => {
if (type == 'menu') { if (type == "menu") {
menuTreeRef.value.setCheckedNodes(value ? menuOptions.value: []); menuTreeRef.value?.setCheckedNodes(value ? menuOptions.value as any : []);
} }
} };
// //
const handleCheckedTreeConnect = (value: CheckboxValueType, type: string) => { const handleCheckedTreeConnect = (value: CheckboxValueType, type: string) => {
if (type == 'menu') { if (type == "menu") {
form.value.menuCheckStrictly = value as boolean; form.value.menuCheckStrictly = value as boolean;
} }
} };
/** 新增按钮操作 */ /** 新增按钮操作 */
const handleAdd = () => { const handleAdd = () => {
dialog.visible = true; reset();
dialog.title = "添加租户套餐"; getMenuTreeselect();
nextTick(() => { dialog.visible = true;
reset(); dialog.title = "添加租户套餐";
getMenuTreeselect(); };
})
}
/** 修改按钮操作 */ /** 修改按钮操作 */
const handleUpdate = (row?: TenantPkgVO) => { const handleUpdate = async (row?: TenantPkgVO) => {
loading.value = true reset();
dialog.visible = true; const _packageId = row?.packageId || ids.value[0];
dialog.title = "修改租户套餐"; const response = await getTenantPackage(_packageId);
nextTick(async () => { form.value = response.data;
reset(); const res = await getPackageMenuTreeselect(_packageId);
const _packageId = row?.packageId || ids.value[0]; dialog.visible = true;
const packageMenu = getPackageMenuTreeselect(_packageId); dialog.title = "修改租户套餐";
const response = await getTenantPackage(_packageId); res.data.checkedKeys.forEach((v) => {
loading.value = false; nextTick(() => {
form.value = response.data; menuTreeRef.value?.setChecked(v, true, false);
nextTick(async () => { });
const res = await packageMenu; });
let checkedKeys = res.data.checkedKeys };
checkedKeys.forEach((v) => {
nextTick(() => {
menuTreeRef.value.setChecked(v, true ,false);
})
})
});
})
}
/** 提交按钮 */ /** 提交按钮 */
const submitForm = () => { const submitForm = () => {
tenantPackageFormRef.value.validate(async (valid: boolean) => { tenantPackageFormRef.value?.validate(async (valid: boolean) => {
if (valid) { if (valid) {
buttonLoading.value = true; buttonLoading.value = true;
form.value.menuIds = getMenuAllCheckedKeys(); form.value.menuIds = getMenuAllCheckedKeys();
if (form.value.packageId != null) { if (form.value.packageId != null) {
await updateTenantPackage(form.value).finally(() => buttonLoading.value = false); await updateTenantPackage(form.value).finally(() => buttonLoading.value = false);
} else { } else {
await addTenantPackage(form.value).finally(() => buttonLoading.value = false); await addTenantPackage(form.value).finally(() => buttonLoading.value = false);
} }
proxy?.$modal.msgSuccess("操作成功"); proxy?.$modal.msgSuccess("操作成功");
dialog.visible = false; dialog.visible = false;
getList(); await getList();
} }
}); });
} };
/** 删除按钮操作 */ /** 删除按钮操作 */
const handleDelete = async (row?: TenantPkgVO) => { const handleDelete = async (row?: TenantPkgVO) => {
const _packageIds = row?.packageId || ids.value; const _packageIds = row?.packageId || ids.value;
await proxy?.$modal.confirm('是否确认删除租户套餐编号为"' + _packageIds + '"的数据项?').finally(() => { await proxy?.$modal.confirm("是否确认删除租户套餐编号为\"" + _packageIds + "\"的数据项?").finally(() => {
loading.value = false; loading.value = false;
}); });
await delTenantPackage(_packageIds); await delTenantPackage(_packageIds);
loading.value = true; loading.value = true;
getList(); await getList();
proxy?.$modal.msgSuccess("删除成功"); proxy?.$modal.msgSuccess("删除成功");
} };
/** 导出按钮操作 */ /** 导出按钮操作 */
const handleExport = () => { const handleExport = () => {
proxy?.download('system/tenantPackage/export', { proxy?.download("system/tenantPackage/export", {
...queryParams.value ...queryParams.value
}, `tenantPackage_${new Date().getTime()}.xlsx`) }, `tenantPackage_${new Date().getTime()}.xlsx`);
} };
onMounted(() => { onMounted(() => {
getList(); getList();
}) });
</script> </script>

View File

@ -55,11 +55,10 @@
</template> </template>
<script setup name="AuthRole" lang="ts"> <script setup name="AuthRole" lang="ts">
import { RoleVO } from '@/api/system/role/types'; import { RoleVO } from "@/api/system/role/types";
import { getAuthRole, updateAuthRole } from '@/api/system/user'; import { getAuthRole, updateAuthRole } from "@/api/system/user";
import { UserForm } from '@/api/system/user/types'; import { UserForm } from "@/api/system/user/types";
import { ElTable } from "element-plus";
import { ComponentInternalInstance } from 'vue';
const route = useRoute(); const route = useRoute();
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@ -70,58 +69,59 @@ const pageSize = ref(10);
const roleIds = ref<Array<string | number>>([]); const roleIds = ref<Array<string | number>>([]);
const roles = ref<RoleVO[]>([]); const roles = ref<RoleVO[]>([]);
const form = ref<Partial<UserForm>>({ const form = ref<Partial<UserForm>>({
nickName: undefined, nickName: undefined,
userName: '', userName: "",
userId: undefined userId: undefined
}); });
const tableRef = ref(ElTable) const tableRef = ref<ElTableInstance>();
/** 单击选中行数据 */ /** 单击选中行数据 */
const clickRow = (row: RoleVO) => { const clickRow = (row: RoleVO) => {
tableRef.value.toggleRowSelection(row); // eleselected
tableRef.value?.toggleRowSelection(row, false);
}; };
/** 多选框选中数据 */ /** 多选框选中数据 */
const handleSelectionChange = (selection: RoleVO[]) => { const handleSelectionChange = (selection: RoleVO[]) => {
roleIds.value = selection.map(item => item.roleId); roleIds.value = selection.map(item => item.roleId);
}; };
/** 保存选中的数据编号 */ /** 保存选中的数据编号 */
const getRowKey = (row: RoleVO): string => { const getRowKey = (row: RoleVO): string => {
return String(row.roleId); return String(row.roleId);
}; };
/** 关闭按钮 */ /** 关闭按钮 */
const close = () => { const close = () => {
const obj = { path: "/system/user" }; const obj = { path: "/system/user" };
proxy?.$tab.closeOpenPage(obj); proxy?.$tab.closeOpenPage(obj);
}; };
/** 提交按钮 */ /** 提交按钮 */
const submitForm = async () => { const submitForm = async () => {
const userId = form.value.userId; const userId = form.value.userId;
const rIds = roleIds.value.join(","); const rIds = roleIds.value.join(",");
await updateAuthRole({ userId: userId as string, roleIds: rIds }) await updateAuthRole({ userId: userId as string, roleIds: rIds });
proxy?.$modal.msgSuccess("授权成功"); proxy?.$modal.msgSuccess("授权成功");
close(); close();
}; };
const getList = async() => { const getList = async () => {
const userId = route.params && route.params.userId; const userId = route.params && route.params.userId;
if (userId) { if (userId) {
loading.value = true; loading.value = true;
const res = await getAuthRole(userId as string); const res = await getAuthRole(userId as string);
Object.assign(form.value, res.data.user) Object.assign(form.value, res.data.user);
Object.assign(roles.value, res.data.roles) Object.assign(roles.value, res.data.roles);
total.value = roles.value.length; total.value = roles.value.length;
await nextTick(() => { await nextTick(() => {
roles.value.forEach(row => { roles.value.forEach(row => {
if (row?.flag) { if (row?.flag) {
tableRef.value.toggleRowSelection(row); tableRef.value?.toggleRowSelection(row, true);
} }
}); });
}); });
loading.value = false; loading.value = false;
} }
} };
onMounted(() => { onMounted(() => {
getList(); getList();
}) });
</script> </script>

View File

@ -16,40 +16,48 @@
highlight-current highlight-current
default-expand-all default-expand-all
@node-click="handleNodeClick" @node-click="handleNodeClick"
></el-tree> />
</el-card> </el-card>
</el-col> </el-col>
<el-col :lg="20" :xs="24"> <el-col :lg="20" :xs="24">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="search" v-show="showSearch"> <div class="mb-[10px]" v-show="showSearch">
<el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px"> <el-card shadow="hover">
<el-form-item label="用户名称" prop="userName"> <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px">
<el-input v-model="queryParams.userName" placeholder="请输入用户名称" clearable style="width: 240px" @keyup.enter="handleQuery" /> <el-form-item label="用户名称" prop="userName">
</el-form-item> <el-input v-model="queryParams.userName" placeholder="请输入用户名称" clearable style="width: 240px" @keyup.enter="handleQuery" />
<el-form-item label="手机号码" prop="phonenumber"> </el-form-item>
<el-input v-model="queryParams.phonenumber" placeholder="请输入手机号码" clearable style="width: 240px" @keyup.enter="handleQuery" /> <el-form-item label="手机号码" prop="phonenumber">
</el-form-item> <el-input
v-model="queryParams.phonenumber"
placeholder="请输入手机号码"
clearable
style="width: 240px"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="状态" prop="status"> <el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="用户状态" clearable style="width: 240px"> <el-select v-model="queryParams.status" placeholder="用户状态" clearable style="width: 240px">
<el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" /> <el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="创建时间" style="width: 308px;"> <el-form-item label="创建时间" style="width: 308px;">
<el-date-picker <el-date-picker
v-model="dateRange" v-model="dateRange"
value-format="YYYY-MM-DD" value-format="YYYY-MM-DD"
type="daterange" type="daterange"
range-separator="-" range-separator="-"
start-placeholder="开始日期" start-placeholder="开始日期"
end-placeholder="结束日期" end-placeholder="结束日期"
></el-date-picker> ></el-date-picker>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" @click="handleQuery" icon="Search">搜索</el-button> <el-button type="primary" @click="handleQuery" icon="Search">搜索</el-button>
<el-button @click="resetQuery" icon="Refresh">重置</el-button> <el-button @click="resetQuery" icon="Refresh">重置</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
</el-card>
</div> </div>
</transition> </transition>
@ -146,7 +154,7 @@
</el-row> </el-row>
<!-- 添加或修改用户配置对话框 --> <!-- 添加或修改用户配置对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="600px" append-to-body @close="closeDialog"> <el-dialog ref="formDialogRef" :title="dialog.title" v-model="dialog.visible" width="600px" append-to-body @close="closeDialog">
<el-form :model="form" :rules="rules" ref="userFormRef" label-width="80px"> <el-form :model="form" :rules="rules" ref="userFormRef" label-width="80px">
<el-row> <el-row>
<el-col :span="12"> <el-col :span="12">
@ -203,7 +211,7 @@
<el-form-item label="状态"> <el-form-item label="状态">
<el-radio-group v-model="form.status"> <el-radio-group v-model="form.status">
<el-radio v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.value">{{ <el-radio v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.value">{{
dict.label }}</el-radio> dict.label }}</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
</el-col> </el-col>
@ -289,30 +297,20 @@
</template> </template>
<script setup name="User" lang="ts"> <script setup name="User" lang="ts">
import { import api from "@/api/system/user"
changeUserStatus,
listUser,
resetUserPwd,
delUser,
getUser,
updateUser,
addUser,
deptTreeSelect
} from "@/api/system/user"
import { UserForm, UserQuery, UserVO } from '@/api/system/user/types'; import { UserForm, UserQuery, UserVO } from '@/api/system/user/types';
import { ComponentInternalInstance } from "vue";
import { getToken } from "@/utils/auth"; import { getToken } from "@/utils/auth";
import { treeselect } from "@/api/system/dept"; import { treeselect } from "@/api/system/dept";
import { DeptVO } from "@/api/system/dept/types"; import { DeptVO } from "@/api/system/dept/types";
import { RoleVO } from "@/api/system/role/types"; import { RoleVO } from "@/api/system/role/types";
import { PostVO } from "@/api/system/post/types"; import { PostVO } from "@/api/system/post/types";
import { DateModelType, ElTree, ElUpload, UploadFile, ElForm } from 'element-plus';
import { to } from "await-to-js"; import { to } from "await-to-js";
import { globalHeaders } from "@/utils/request";
const router = useRouter(); const router = useRouter();
const { proxy } = getCurrentInstance() as ComponentInternalInstance const { proxy } = getCurrentInstance() as ComponentInternalInstance
const { sys_normal_disable, sys_user_sex } = toRefs<any>(proxy?.useDict('sys_normal_disable', 'sys_user_sex')); const { sys_normal_disable, sys_user_sex } = toRefs<any>(proxy?.useDict('sys_normal_disable', 'sys_user_sex'));
const userList = ref<UserVO[]>(); const userList = ref<UserVO[]>();
const loading = ref(true); const loading = ref(true);
const showSearch = ref(true) const showSearch = ref(true)
@ -320,7 +318,7 @@ const ids = ref<Array<number | string>>([]);
const single = ref(true); const single = ref(true);
const multiple = ref(true); const multiple = ref(true);
const total = ref(0); const total = ref(0);
const dateRange = ref<[DateModelType, DateModelType]>(['','']); const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
const deptName = ref(''); const deptName = ref('');
const deptOptions = ref<DeptVO[]>([]); const deptOptions = ref<DeptVO[]>([]);
const initPassword = ref('123456'); const initPassword = ref('123456');
@ -328,274 +326,270 @@ const postOptions = ref<PostVO[]>([]);
const roleOptions = ref<RoleVO[]>([]); const roleOptions = ref<RoleVO[]>([]);
/*** 用户导入参数 */ /*** 用户导入参数 */
const upload = reactive<ImportOption>({ const upload = reactive<ImportOption>({
// //
open: false, open: false,
// //
title: "", title: "",
// //
isUploading: false, isUploading: false,
// //
updateSupport: 0, updateSupport: 0,
// //
headers: { Authorization: "Bearer " + getToken() }, headers: globalHeaders,
// //
url: import.meta.env.VITE_APP_BASE_API + "/system/user/importData" url: import.meta.env.VITE_APP_BASE_API + "/system/user/importData"
}) })
// //
const columns = ref<FieldOption[]>([ const columns = ref<FieldOption[]>([
{ key: 0, label: `用户编号`, visible: false }, { key: 0, label: `用户编号`, visible: false },
{ key: 1, label: `用户名称`, visible: true }, { key: 1, label: `用户名称`, visible: true },
{ key: 2, label: `用户昵称`, visible: true }, { key: 2, label: `用户昵称`, visible: true },
{ key: 3, label: `部门`, visible: true }, { key: 3, label: `部门`, visible: true },
{ key: 4, label: `手机号码`, visible: true }, { key: 4, label: `手机号码`, visible: true },
{ key: 5, label: `状态`, visible: true }, { key: 5, label: `状态`, visible: true },
{ key: 6, label: `创建时间`, visible: true } { key: 6, label: `创建时间`, visible: true }
]) ])
const deptTreeRef = ref(ElTree); const deptTreeRef = ref<ElTreeInstance>();
const queryFormRef = ref(ElForm); const queryFormRef = ref<ElFormInstance>();
const userFormRef = ref(ElForm); const userFormRef = ref<ElFormInstance>();
const uploadRef = ref(ElUpload); const uploadRef = ref<ElUploadInstance>();
const formDialogRef = ref<ElDialogInstance>();
const dialog = reactive<DialogOption>({ const dialog = reactive<DialogOption>({
visible: false, visible: false,
title: '' title: ''
}); });
const initFormData: UserForm = { const initFormData: UserForm = {
userId: undefined, userId: undefined,
deptId: undefined, deptId: undefined,
userName: '', userName: '',
nickName: undefined, nickName: undefined,
password: '', password: '',
phonenumber: undefined, phonenumber: undefined,
email: undefined, email: undefined,
sex: undefined, sex: undefined,
status: "0", status: "0",
remark: '', remark: '',
postIds: [], postIds: [],
roleIds: [] roleIds: []
} }
const data = reactive<PageData<UserForm, UserQuery>>({ const data = reactive<PageData<UserForm, UserQuery>>({
form: { ...initFormData }, form: { ...initFormData },
queryParams: { queryParams: {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
userName: '', userName: '',
phonenumber: '', phonenumber: '',
status: '', status: '',
deptId: '' deptId: ''
}, },
rules: { rules: {
userName: [{ required: true, message: "用户名称不能为空", trigger: "blur" }, { min: 2, max: 20, message: "用户名称长度必须介于 2 和 20 之间", trigger: "blur" }], userName: [{ required: true, message: "用户名称不能为空", trigger: "blur" }, { min: 2, max: 20, message: "用户名称长度必须介于 2 和 20 之间", trigger: "blur" }],
nickName: [{ required: true, message: "用户昵称不能为空", trigger: "blur" }], nickName: [{ required: true, message: "用户昵称不能为空", trigger: "blur" }],
password: [{ required: true, message: "用户密码不能为空", trigger: "blur" }, { min: 5, max: 20, message: "用户密码长度必须介于 5 和 20 之间", trigger: "blur" }], password: [{ required: true, message: "用户密码不能为空", trigger: "blur" }, { min: 5, max: 20, message: "用户密码长度必须介于 5 和 20 之间", trigger: "blur" }],
email: [{ type: "email", message: "请输入正确的邮箱地址", trigger: ["blur", "change"] }], email: [{ type: "email", message: "请输入正确的邮箱地址", trigger: ["blur", "change"] }],
phonenumber: [{ pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: "请输入正确的手机号码", trigger: "blur" }] phonenumber: [{ pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: "请输入正确的手机号码", trigger: "blur" }]
} }
}) })
const { queryParams, form, rules } = toRefs<PageData<UserForm, UserQuery>>(data) const { queryParams, form, rules } = toRefs<PageData<UserForm, UserQuery>>(data)
/** 通过条件过滤节点 */ /** 通过条件过滤节点 */
const filterNode = (value: string, data: any) => { const filterNode = (value: string, data: any) => {
if (!value) return true if (!value) return true
return data.label.indexOf(value) !== -1 return data.label.indexOf(value) !== -1
} }
/** 根据名称筛选部门树 */ /** 根据名称筛选部门树 */
watchEffect( watchEffect(
() => {deptTreeRef.value.filter(deptName.value);}, () => { deptTreeRef.value?.filter(deptName.value); },
{ {
flush: 'post' // watchEffectDOMDOM flush: 'post' // watchEffectDOMDOM
} }
); );
/** 查询部门下拉树结构 */ /** 查询部门下拉树结构 */
const getTreeSelect = async () => { const getTreeSelect = async () => {
const res = await deptTreeSelect(); const res = await api.deptTreeSelect();
deptOptions.value = res.data; deptOptions.value = res.data;
}; };
/** 查询用户列表 */ /** 查询用户列表 */
const getList = async () => { const getList = async () => {
loading.value = true; loading.value = true;
const res = await listUser(proxy?.addDateRange(queryParams.value, dateRange.value)); const res = await api.listUser(proxy?.addDateRange(queryParams.value, dateRange.value));
loading.value = false; loading.value = false;
userList.value = res.rows; userList.value = res.rows;
total.value = res.total; total.value = res.total;
} }
/** 节点单击事件 */ /** 节点单击事件 */
const handleNodeClick = (data: DeptVO) => { const handleNodeClick = (data: DeptVO) => {
queryParams.value.deptId = data.id; queryParams.value.deptId = data.id;
handleQuery() handleQuery()
} }
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
queryParams.value.pageNum = 1 queryParams.value.pageNum = 1
getList() getList()
} }
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
dateRange.value = ['',''] dateRange.value = ['', '']
queryFormRef.value.resetFields(); queryFormRef.value?.resetFields();
queryParams.value.pageNum = 1; queryParams.value.pageNum = 1;
queryParams.value.deptId = undefined; queryParams.value.deptId = undefined;
deptTreeRef.value.setCurrentKey(null); deptTreeRef.value?.setCurrentKey(undefined);
handleQuery(); handleQuery();
} }
/** 删除按钮操作 */ /** 删除按钮操作 */
const handleDelete = async (row?: UserVO) => { const handleDelete = async (row?: UserVO) => {
const userIds = row?.userId || ids.value; const userIds = row?.userId || ids.value;
const [err] = await to(proxy?.$modal.confirm('是否确认删除用户编号为"' + userIds + '"的数据项?') as any); const [err] = await to(proxy?.$modal.confirm('是否确认删除用户编号为"' + userIds + '"的数据项?') as any);
if (!err) { if (!err) {
await delUser(userIds); await api.delUser(userIds);
await getList(); await getList();
proxy?.$modal.msgSuccess("删除成功"); proxy?.$modal.msgSuccess("删除成功");
} }
} }
/** 用户状态修改 */ /** 用户状态修改 */
const handleStatusChange = async (row: UserVO) => { const handleStatusChange = async (row: UserVO) => {
let text = row.status === "0" ? "启用" : "停用" let text = row.status === "0" ? "启用" : "停用"
try { try {
await proxy?.$modal.confirm('确认要"' + text + '""' + row.userName + '"用户吗?'); await proxy?.$modal.confirm('确认要"' + text + '""' + row.userName + '"用户吗?');
await changeUserStatus(row.userId, row.status); await api.changeUserStatus(row.userId, row.status);
proxy?.$modal.msgSuccess(text + "成功"); proxy?.$modal.msgSuccess(text + "成功");
} catch (err) { } catch (err) {
row.status = row.status === "0" ? "1" : "0"; row.status = row.status === "0" ? "1" : "0";
} }
} }
/** 跳转角色分配 */ /** 跳转角色分配 */
const handleAuthRole = (row: UserVO) => { const handleAuthRole = (row: UserVO) => {
const userId = row.userId; const userId = row.userId;
router.push("/system/user-auth/role/" + userId); router.push("/system/user-auth/role/" + userId);
} }
/** 重置密码按钮操作 */ /** 重置密码按钮操作 */
const handleResetPwd = async (row: UserVO) => { const handleResetPwd = async (row: UserVO) => {
const [err, res] = await to(ElMessageBox.prompt('请输入"' + row.userName + '"的新密码', "提示", { const [err, res] = await to(ElMessageBox.prompt('请输入"' + row.userName + '"的新密码', "提示", {
confirmButtonText: "确定", confirmButtonText: "确定",
cancelButtonText: "取消", cancelButtonText: "取消",
closeOnClickModal: false, closeOnClickModal: false,
inputPattern: /^.{5,20}$/, inputPattern: /^.{5,20}$/,
inputErrorMessage: "用户密码长度必须介于 5 和 20 之间", inputErrorMessage: "用户密码长度必须介于 5 和 20 之间",
})) }))
if (!err) { if (!err) {
await resetUserPwd(row.userId, res.value); await api.resetUserPwd(row.userId, res.value);
proxy?.$modal.msgSuccess("修改成功,新密码是:" + res.value); proxy?.$modal.msgSuccess("修改成功,新密码是:" + res.value);
} }
} }
/** 选择条数 */ /** 选择条数 */
const handleSelectionChange = (selection: UserVO[]) => { const handleSelectionChange = (selection: UserVO[]) => {
ids.value = selection.map((item) => item.userId); ids.value = selection.map((item) => item.userId);
single.value = selection.length != 1; single.value = selection.length != 1;
multiple.value = !selection.length; multiple.value = !selection.length;
} }
/** 导入按钮操作 */ /** 导入按钮操作 */
const handleImport = () => { const handleImport = () => {
upload.title = "用户导入"; upload.title = "用户导入";
upload.open = true; upload.open = true;
} }
/** 导出按钮操作 */ /** 导出按钮操作 */
const handleExport = () => { const handleExport = () => {
proxy?.download("system/user/export", { proxy?.download("system/user/export", {
...queryParams.value, ...queryParams.value,
}, `user_${new Date().getTime()}.xlsx`); }, `user_${new Date().getTime()}.xlsx`);
}; };
/** 下载模板操作 */ /** 下载模板操作 */
const importTemplate = () => { const importTemplate = () => {
proxy?.download("system/user/importTemplate", { proxy?.download("system/user/importTemplate", {
}, `user_template_${new Date().getTime()}.xlsx`); }, `user_template_${new Date().getTime()}.xlsx`);
} }
/**文件上传中处理 */ /**文件上传中处理 */
const handleFileUploadProgress = () => { const handleFileUploadProgress = () => {
upload.isUploading = true; upload.isUploading = true;
} }
/** 文件上传成功处理 */ /** 文件上传成功处理 */
const handleFileSuccess = (response: any, file: UploadFile) => { const handleFileSuccess = (response: any, file: UploadFile) => {
upload.open = false; upload.open = false;
upload.isUploading = false; upload.isUploading = false;
uploadRef.value.handleRemove(file); uploadRef.value?.handleRemove(file);
ElMessageBox.alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + "</div>", "导入结果", { dangerouslyUseHTMLString: true }); ElMessageBox.alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + "</div>", "导入结果", { dangerouslyUseHTMLString: true });
getList(); getList();
} }
/** 提交上传文件 */ /** 提交上传文件 */
function submitFileForm() { function submitFileForm() {
uploadRef.value.submit(); uploadRef.value?.submit();
} }
/** 初始化部门数据 */ /** 初始化部门数据 */
const initTreeData = async () => { const initTreeData = async () => {
// //
if (deptOptions.value === undefined) { if (deptOptions.value === undefined) {
const { data } = await treeselect(); const { data } = await treeselect();
deptOptions.value = data; deptOptions.value = data;
} }
} }
/** 重置操作表单 */ /** 重置操作表单 */
const reset = () => { const reset = () => {
form.value = { ...initFormData }; form.value = { ...initFormData };
userFormRef.value.resetFields(); userFormRef.value?.resetFields();
} }
/** 取消按钮 */ /** 取消按钮 */
const cancel = () => { const cancel = () => {
reset(); dialog.visible = false;
dialog.visible = false; reset();
} }
/** 新增按钮操作 */ /** 新增按钮操作 */
const handleAdd = () => { const handleAdd = async () => {
dialog.visible = true; reset();
dialog.title = "新增用户"; const { data } = await api.getUser();
nextTick(async () => { dialog.visible = true;
reset(); dialog.title = "新增用户";
await initTreeData(); await initTreeData();
const { data } = await getUser(); postOptions.value = data.posts;
postOptions.value = data.posts; roleOptions.value = data.roles;
roleOptions.value = data.roles; form.value.password = initPassword.value;
form.value.password = initPassword.value;
})
} }
/** 修改按钮操作 */ /** 修改按钮操作 */
const handleUpdate = (row?: UserForm) => { const handleUpdate = async (row?: UserForm) => {
dialog.visible = true; reset();
dialog.title = "修改用户"; const userId = row?.userId || ids.value[0]
nextTick(async () => { const { data } = await api.getUser(userId)
reset(); dialog.visible = true;
await initTreeData(); dialog.title = "修改用户";
const userId = row?.userId || ids.value[0] await initTreeData();
const { data } = await getUser(userId) Object.assign(form.value, data.user);
Object.assign(form.value, data.user); postOptions.value = data.posts;
postOptions.value = data.posts; roleOptions.value = data.roles;
roleOptions.value = data.roles; form.value.postIds = data.postIds;
form.value.postIds = data.postIds; form.value.roleIds = data.roleIds;
form.value.roleIds = data.roleIds; form.value.password = "";
form.value.password = "";
})
} }
/** 提交按钮 */ /** 提交按钮 */
const submitForm = () => { const submitForm = () => {
userFormRef.value.validate(async (valid: boolean) => { userFormRef.value?.validate(async (valid: boolean) => {
if (valid) { if (valid) {
form.value.userId ? await updateUser(form.value) : await addUser(form.value); form.value.userId ? await api.updateUser(form.value) : await api.addUser(form.value);
proxy?.$modal.msgSuccess("操作成功"); proxy?.$modal.msgSuccess("操作成功");
dialog.visible = false; dialog.visible = false;
await getList(); await getList();
} }
}) })
} }
@ -603,23 +597,23 @@ const submitForm = () => {
* 关闭用户弹窗 * 关闭用户弹窗
*/ */
const closeDialog = () => { const closeDialog = () => {
dialog.visible = false; dialog.visible = false;
resetForm(); resetForm();
} }
/** /**
* 重置表单 * 重置表单
*/ */
const resetForm = () => { const resetForm = () => {
userFormRef.value.resetFields(); userFormRef.value?.resetFields();
userFormRef.value.clearValidate(); userFormRef.value?.clearValidate();
form.value.id = undefined; form.value.id = undefined;
form.value.status = '1'; form.value.status = '1';
} }
onMounted(() => { onMounted(() => {
getTreeSelect() // getTreeSelect() //
getList() // getList() //
}); });
</script> </script>

View File

@ -55,6 +55,9 @@
<el-tab-pane label="修改密码" name="resetPwd"> <el-tab-pane label="修改密码" name="resetPwd">
<resetPwd /> <resetPwd />
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="第三方应用" name="thirdParty">
<thirdParty :auths="state.auths" />
</el-tab-pane>
</el-tabs> </el-tabs>
</el-card> </el-card>
</el-col> </el-col>
@ -66,13 +69,16 @@
import userAvatar from "./userAvatar.vue"; import userAvatar from "./userAvatar.vue";
import userInfo from "./userInfo.vue"; import userInfo from "./userInfo.vue";
import resetPwd from "./resetPwd.vue"; import resetPwd from "./resetPwd.vue";
import thirdParty from "./thirdParty.vue";
import { getAuthList } from "@/api/system/social/auth";
import { getUserProfile } from "@/api/system/user"; import { getUserProfile } from "@/api/system/user";
const activeTab = ref("userinfo"); const activeTab = ref("userinfo");
const state = ref<{ user: any; roleGroup: string; postGroup: string}>({ const state = ref<Record<string, any>>({
user: {}, user: {},
roleGroup: '', roleGroup: '',
postGroup: '' postGroup: '',
auths: []
}); });
const userForm = ref({}); const userForm = ref({});
@ -85,7 +91,13 @@ const getUser = async () => {
state.value.postGroup = res.data.postGroup; state.value.postGroup = res.data.postGroup;
}; };
const getAuths = async () => {
const res = await getAuthList();
state.value.auths = res.data;
};
onMounted(() => { onMounted(() => {
getUser(); getUser();
getAuths();
}) })
</script> </script>

View File

@ -17,46 +17,50 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { updateUserPwd } from '@/api/system/user'; import { updateUserPwd } from "@/api/system/user";
import { ComponentInternalInstance } from 'vue'; import type { ResetPwdForm } from "@/api/system/user/types";
import { ResetPwdForm } from '@/api/system/user/types'
import { ElForm } from 'element-plus';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const pwdRef = ref<ElFormInstance>();
const pwdRef = ref(ElForm);
const user = ref<ResetPwdForm>({ const user = ref<ResetPwdForm>({
oldPassword: '', oldPassword: "",
newPassword: '', newPassword: "",
confirmPassword: '' confirmPassword: ""
}); });
const equalToPassword = (rule: any, value: string, callback: any) => { const equalToPassword = (rule: any, value: string, callback: any) => {
if (user.value.newPassword !== value) { if (user.value.newPassword !== value) {
callback(new Error("两次输入的密码不一致")); callback(new Error("两次输入的密码不一致"));
} else { } else {
callback(); callback();
} }
}; };
const rules = ref({ const rules = ref({
oldPassword: [{ required: true, message: "旧密码不能为空", trigger: "blur" }], oldPassword: [{ required: true, message: "旧密码不能为空", trigger: "blur" }],
newPassword: [{ required: true, message: "新密码不能为空", trigger: "blur" }, { min: 6, max: 20, message: "长度在 6 到 20 个字符", trigger: "blur" }], newPassword: [{ required: true, message: "新密码不能为空", trigger: "blur" }, {
confirmPassword: [{ required: true, message: "确认密码不能为空", trigger: "blur" }, { required: true, validator: equalToPassword, trigger: "blur" }] min: 6,
max: 20,
message: "长度在 6 到 20 个字符",
trigger: "blur"
}],
confirmPassword: [{ required: true, message: "确认密码不能为空", trigger: "blur" }, {
required: true,
validator: equalToPassword,
trigger: "blur"
}]
}); });
/** 提交按钮 */ /** 提交按钮 */
const submit = () => { const submit = () => {
pwdRef.value.validate(async (valid: boolean) => { pwdRef.value?.validate(async (valid: boolean) => {
if (valid) { if (valid) {
await updateUserPwd(user.value.oldPassword, user.value.newPassword) await updateUserPwd(user.value.oldPassword, user.value.newPassword);
proxy?.$modal.msgSuccess("修改成功"); proxy?.$modal.msgSuccess("修改成功");
} }
}); });
}; };
/** 关闭按钮 */ /** 关闭按钮 */
const close = () => { const close = () => {
proxy?.$tab.closePage(); proxy?.$tab.closePage();
}; };
</script> </script>

View File

@ -0,0 +1,140 @@
<template>
<div>
<el-table :data="auths" style="width: 100%; height: 100%; font-size: 10px">
<el-table-column label="序号" width="50" type="index"></el-table-column>
<el-table-column label="绑定账号平台" width="140" align="center" prop="source" show-overflow-tooltip />
<el-table-column label="头像" width="120" align="center" prop="avatar">
<template v-slot="scope">
<img :src="scope.row.avatar" style="width: 45px; height: 45px" />
</template>
</el-table-column>
<el-table-column label="系统账号" width="180" align="center" prop="userName" :show-overflow-tooltip="true" />
<el-table-column label="绑定时间" width="180" align="center" prop="createTime" />
<el-table-column label="操作" width="80" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="small" type="text" @click="unlockAuth(scope.row)">解绑</el-button>
</template>
</el-table-column>
</el-table>
<div id="git-user-binding">
<h4 class="provider-desc">你可以绑定以下第三方帐号</h4>
<div id="authlist" class="user-bind">
<a class="third-app" href="#" @click="authUrl('wechar');" title="使用 微信 账号授权登录">
<div class="git-other-login-icon">
<svg-icon icon-class="wechat" />
</div>
<span class="app-name">WeiXin</span>
</a>
<a class="third-app" href="#" @click="authUrl('maxkey');" title="使用 MaxKey 账号授权登录">
<div class="git-other-login-icon">
<svg-icon icon-class="maxkey" />
</div>
<span class="app-name">MaxKey</span>
</a>
<a class="third-app" href="#" @click="authUrl('gitee');" title="使用 Gitee 账号授权登录">
<div class="git-other-login-icon">
<svg-icon icon-class="gitee" />
</div>
<span class="app-name">Gitee</span>
</a>
<a class="third-app" href="#" @click="authUrl('github');" title="使用 GitHub 账号授权登录">
<div class="git-other-login-icon">
<svg-icon icon-class="github" />
</div>
<span class="app-name">Github</span>
</a>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { authUnlock, authBinding } from "@/api/system/social/auth";
import { PropType } from "vue";
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const props = defineProps({
auths: {
type: Object as PropType<any>,
}
});
const auths = computed(() => props.auths);
const unlockAuth = (row: any) => {
ElMessageBox.confirm('您确定要解除"' + row.source + '"的账号绑定吗?')
.then(() => {
return authUnlock(row.id);
}).then((res: any) => {
if (res.code === 200) {
proxy?.$modal.msgSuccess("解绑成功");
proxy?.$tab.refreshPage();
} else {
proxy?.$modal.msgError(res.msg);
}
}).catch(() => { });
};
const authUrl = (source: string) => {
authBinding(source).then((res: any) => {
if (res.code === 200) {
window.location.href = res.data;
} else {
proxy?.$modal.msgError(res.msg);
}
});
};
</script>
<style type="text/css">
.user-bind .third-app {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-ms-flex-direction: column;
flex-direction: column;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
min-width: 80px;
float: left;
}
.user-bind {
font-size: 1rem;
text-align: start;
height: 50px;
margin-top: 10px;
}
.git-other-login-icon>img {
height: 32px;
}
a {
text-decoration: none;
cursor: pointer;
color: #005980;
}
.provider-desc {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial,
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Liberation Sans",
"PingFang SC", "Microsoft YaHei", "Hiragino Sans GB", "Wenquanyi Micro Hei",
"WenQuanYi Zen Hei", "ST Heiti", SimHei, SimSun, "WenQuanYi Zen Hei Sharp",
sans-serif;
font-size: 1.071rem;
}
td>img {
height: 20px;
width: 20px;
display: inline-block;
border-radius: 50%;
margin-right: 5px;
}
</style>

View File

@ -29,7 +29,9 @@
<el-upload action="#" :http-request="requestUpload" :show-file-list="false" :before-upload="beforeUpload"> <el-upload action="#" :http-request="requestUpload" :show-file-list="false" :before-upload="beforeUpload">
<el-button> <el-button>
选择 选择
<el-icon class="el-icon--right"><Upload /></el-icon> <el-icon class="el-icon--right">
<Upload />
</el-icon>
</el-button> </el-button>
</el-upload> </el-upload>
</el-col> </el-col>
@ -58,18 +60,17 @@ import "vue-cropper/dist/index.css";
import { VueCropper } from "vue-cropper"; import { VueCropper } from "vue-cropper";
import { uploadAvatar } from "@/api/system/user"; import { uploadAvatar } from "@/api/system/user";
import useUserStore from "@/store/modules/user"; import useUserStore from "@/store/modules/user";
import { ComponentInternalInstance } from "vue";
interface Options { interface Options {
img: string | ArrayBuffer | null // img: string | ArrayBuffer | null; //
autoCrop: boolean // autoCrop: boolean; //
autoCropWidth: number // autoCropWidth: number; //
autoCropHeight: number // autoCropHeight: number; //
fixedBox: boolean // fixedBox: boolean; //
fileName: string fileName: string;
previews: any // previews: any; //
outputType: string outputType: string;
visible: boolean visible: boolean;
} }
@ -83,75 +84,76 @@ const title = ref("修改头像");
const cropper = ref<any>({}); const cropper = ref<any>({});
// //
const options = reactive<Options>({ const options = reactive<Options>({
img: userStore.avatar, img: userStore.avatar,
autoCrop: true, autoCrop: true,
autoCropWidth: 200, autoCropWidth: 200,
autoCropHeight: 200, autoCropHeight: 200,
fixedBox: true, fixedBox: true,
outputType: "png", outputType: "png",
fileName: '', fileName: "",
previews: {}, previews: {},
visible: false visible: false
}); });
/** 编辑头像 */ /** 编辑头像 */
const editCropper = () => { const editCropper = () => {
open.value = true; open.value = true;
} };
/** 打开弹出层结束时的回调 */ /** 打开弹出层结束时的回调 */
const modalOpened = () => { const modalOpened = () => {
visible.value = true; visible.value = true;
} };
/** 覆盖默认上传行为 */ /** 覆盖默认上传行为 */
const requestUpload = (): any => {} const requestUpload = (): any => {
};
/** 向左旋转 */ /** 向左旋转 */
const rotateLeft = () => { const rotateLeft = () => {
cropper.value.rotateLeft(); cropper.value.rotateLeft();
} };
/** 向右旋转 */ /** 向右旋转 */
const rotateRight = () => { const rotateRight = () => {
cropper.value.rotateRight(); cropper.value.rotateRight();
} };
/** 图片缩放 */ /** 图片缩放 */
const changeScale = (num: number) => { const changeScale = (num: number) => {
num = num || 1; num = num || 1;
cropper.value.changeScale(num); cropper.value.changeScale(num);
} };
/** 上传预处理 */ /** 上传预处理 */
const beforeUpload = (file: any) => { const beforeUpload = (file: any) => {
if (file.type.indexOf("image/") == -1) { if (file.type.indexOf("image/") == -1) {
proxy?.$modal.msgError("文件格式错误,请上传图片类型,如JPGPNG后缀的文件。"); proxy?.$modal.msgError("文件格式错误,请上传图片类型,如JPGPNG后缀的文件。");
} else { } else {
const reader = new FileReader(); const reader = new FileReader();
reader.readAsDataURL(file); reader.readAsDataURL(file);
reader.onload = () => { reader.onload = () => {
options.img = reader.result; options.img = reader.result;
options.fileName = file.name; options.fileName = file.name;
}; };
} }
} };
/** 上传图片 */ /** 上传图片 */
const uploadImg = async () => { const uploadImg = async () => {
cropper.value.getCropBlob(async (data: any) => { cropper.value.getCropBlob(async (data: any) => {
let formData = new FormData(); let formData = new FormData();
formData.append("avatarfile", data, options.fileName); formData.append("avatarfile", data, options.fileName);
const res = await uploadAvatar(formData); const res = await uploadAvatar(formData);
open.value = false; open.value = false;
options.img = res.data.imgUrl; options.img = res.data.imgUrl;
userStore.avatar = options.img as string; userStore.avatar = options.img as string
proxy?.$modal.msgSuccess("修改成功"); proxy?.$modal.msgSuccess("修改成功");
visible.value = false; visible.value = false;
}); });
} };
/** 实时预览 */ /** 实时预览 */
const realTime = (data: any) => { const realTime = (data: any) => {
options.previews = data; options.previews = data;
} };
/** 关闭窗口 */ /** 关闭窗口 */
const closeDialog = () => { const closeDialog = () => {
options.img = userStore.avatar; options.img = userStore.avatar;
options.visible = false; options.visible = false;
} };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -24,40 +24,42 @@
<script setup lang="ts"> <script setup lang="ts">
import { updateUserProfile } from "@/api/system/user"; import { updateUserProfile } from "@/api/system/user";
import { FormRules } from "element-plus";
import { ComponentInternalInstance } from "vue";
import { PropType } from "vue";
import { ElForm } from "element-plus";
const props = defineProps({ const props = defineProps({
user: { user: {
type: Object as PropType<any>, type: Object as PropType<any>,
} required: true
}
}); });
const userForm = computed(() => props.user); const userForm = computed(() => props.user);
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const userRef = ref<ElFormInstance>();
const userRef = ref(ElForm); const rules = ref<ElFormRules>({
nickName: [{ required: true, message: "用户昵称不能为空", trigger: "blur" }],
const rules = ref<FormRules>({ email: [{ required: true, message: "邮箱地址不能为空", trigger: "blur" }, {
nickName: [{ required: true, message: "用户昵称不能为空", trigger: "blur" }], type: "email",
email: [{ required: true, message: "邮箱地址不能为空", trigger: "blur" }, { type: "email", message: "请输入正确的邮箱地址", trigger: ["blur", "change"] }], message: "请输入正确的邮箱地址",
phonenumber: [{ required: true, message: "手机号码不能为空", trigger: "blur" }, { pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: "请输入正确的手机号码", trigger: "blur" }], trigger: ["blur", "change"]
}],
phonenumber: [{
required: true,
message: "手机号码不能为空",
trigger: "blur"
}, { pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: "请输入正确的手机号码", trigger: "blur" }]
}); });
/** 提交按钮 */ /** 提交按钮 */
const submit = () => { const submit = () => {
userRef.value.validate(async (valid: boolean) => { userRef.value?.validate(async (valid: boolean) => {
if (valid) { if (valid) {
await updateUserProfile(props.user) await updateUserProfile(props.user);
proxy?.$modal.msgSuccess("修改成功"); proxy?.$modal.msgSuccess("修改成功");
} }
}); });
}; };
/** 关闭按钮 */ /** 关闭按钮 */
const close = () => { const close = () => {
proxy?.$tab.closePage(); proxy?.$tab.closePage();
}; };
</script> </script>

View File

@ -31,24 +31,19 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { PropType } from 'vue'; import { propTypes } from "@/utils/propTypes";
const prop = defineProps({ const prop = defineProps({
info: { info: propTypes.any.def({})
type: Object as PropType<any>,
default: () => {
return {};
}
}
}); });
const infoForm = computed(() => prop.info) const infoForm = computed(() => prop.info)
// //
const rules = ref({ const rules = ref({
tableName: [{ required: true, message: "请输入表名称", trigger: "blur" }], tableName: [{ required: true, message: "请输入表名称", trigger: "blur" }],
tableComment: [{ required: true, message: "请输入表描述", trigger: "blur" }], tableComment: [{ required: true, message: "请输入表描述", trigger: "blur" }],
className: [{ required: true, message: "请输入实体类名称", trigger: "blur" }], className: [{ required: true, message: "请输入实体类名称", trigger: "blur" }],
functionAuthor: [{ required: true, message: "请输入作者", trigger: "blur" }] functionAuthor: [{ required: true, message: "请输入作者", trigger: "blur" }]
}); });
</script> </script>

View File

@ -117,9 +117,8 @@ import { getGenTable, updateGenTable } from '@/api/tool/gen';
import { DbColumnVO, DbTableVO } from '@/api/tool/gen/types'; import { DbColumnVO, DbTableVO } from '@/api/tool/gen/types';
import { optionselect as getDictOptionselect } from '@/api/system/dict/type'; import { optionselect as getDictOptionselect } from '@/api/system/dict/type';
import { DictTypeVO } from '@/api/system/dict/type/types'; import { DictTypeVO } from '@/api/system/dict/type/types';
import basicInfoForm from './basicInfoForm.vue'; import BasicInfoForm from './basicInfoForm.vue';
import genInfoForm from "./genInfoForm.vue"; import GenInfoForm from "./genInfoForm.vue";
import { ComponentInternalInstance } from "vue";
const route = useRoute(); const route = useRoute();
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@ -131,13 +130,13 @@ const columns = ref<DbColumnVO[]>([]);
const dictOptions = ref<DictTypeVO[]>([]); const dictOptions = ref<DictTypeVO[]>([]);
const info = ref<Partial<DbTableVO>>({}); const info = ref<Partial<DbTableVO>>({});
const basicInfo = ref(basicInfoForm); const basicInfo = ref<InstanceType<typeof BasicInfoForm>>();
const genInfo = ref(genInfoForm); const genInfo = ref<InstanceType<typeof GenInfoForm>>();
/** 提交按钮 */ /** 提交按钮 */
const submitForm = () => { const submitForm = () => {
const basicForm = basicInfo.value.$refs.basicInfoForm; const basicForm = basicInfo.value?.$refs.basicInfoForm;
const genForm = genInfo.value.$refs.genInfoForm; const genForm = genInfo.value?.$refs.genInfoForm;
Promise.all([basicForm, genForm].map(getFormPromise)).then(async res => { Promise.all([basicForm, genForm].map(getFormPromise)).then(async res => {
const validateResult = res.every(item => !!item); const validateResult = res.every(item => !!item);
@ -168,7 +167,7 @@ const getFormPromise = (form: any) => {
}); });
} }
const close = () => { const close = () => {
const obj = {path: "/tool/gen", query: {t: Date.now(), pageNum: route.query.pageNum}}; const obj = { path: "/tool/gen", query: { t: Date.now(), pageNum: route.query.pageNum } };
proxy?.$tab.closeOpenPage(obj); proxy?.$tab.closeOpenPage(obj);
} }

View File

@ -223,7 +223,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { listMenu } from '@/api/system/menu'; import { listMenu } from '@/api/system/menu';
import { ComponentInternalInstance, PropType } from 'vue'; import { propTypes } from "@/utils/propTypes";
interface MenuOptionsType { interface MenuOptionsType {
menuId: number | string; menuId: number | string;
@ -236,14 +236,8 @@ const menuOptions = ref<Array<MenuOptionsType>>([]);
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const props = defineProps({ const props = defineProps({
info: { info: propTypes.any.def(null),
type: Object as PropType<any>, tables: propTypes.any.def(null)
default: null
},
tables: {
type: Array as PropType<any[]>,
default: null
}
}); });
const infoForm = computed(() => props.info); const infoForm = computed(() => props.info);
@ -252,11 +246,11 @@ const table = computed(() => props.tables);
// //
const rules = ref({ const rules = ref({
tplCategory: [{required: true, message: "请选择生成模板", trigger: "blur"}], tplCategory: [{ required: true, message: "请选择生成模板", trigger: "blur" }],
packageName: [{required: true, message: "请输入生成包路径", trigger: "blur"}], packageName: [{ required: true, message: "请输入生成包路径", trigger: "blur" }],
moduleName: [{required: true, message: "请输入生成模块名", trigger: "blur"}], moduleName: [{ required: true, message: "请输入生成模块名", trigger: "blur" }],
businessName: [{required: true, message: "请输入生成业务名", trigger: "blur"}], businessName: [{ required: true, message: "请输入生成业务名", trigger: "blur" }],
functionName: [{required: true, message: "请输入生成功能名", trigger: "blur"}] functionName: [{ required: true, message: "请输入生成功能名", trigger: "blur" }]
}); });
const subSelectChange = () => { const subSelectChange = () => {
infoForm.value.subTableFkName = ""; infoForm.value.subTableFkName = "";
@ -268,7 +262,7 @@ const tplSelectChange = (value: string) => {
} }
} }
const setSubTableColumns = (value: string) => { const setSubTableColumns = (value: string) => {
table.value.forEach(item => { table.value.forEach((item: any) => {
const name = item.tableName; const name = item.tableName;
if (value === name) { if (value === name) {
subColumns.value = item.columns; subColumns.value = item.columns;

View File

@ -3,11 +3,11 @@
<el-dialog title="导入表" v-model="visible" width="1100px" top="5vh" append-to-body> <el-dialog title="导入表" v-model="visible" width="1100px" top="5vh" append-to-body>
<el-form :model="queryParams" ref="queryFormRef" :inline="true"> <el-form :model="queryParams" ref="queryFormRef" :inline="true">
<el-form-item label="数据源" prop="dataName"> <el-form-item label="数据源" prop="dataName">
<el-select v-model="queryParams.dataName" filterable placeholder="请选择/输入数据源名称" style="width: 200px"> <el-select v-model="queryParams.dataName" filterable placeholder="请选择/输入数据源名称" style="width: 200px">
<el-option v-for="item in dataNameList" :key="item" :label="item" :value="item"> </el-option> <el-option v-for="item in dataNameList" :key="item" :label="item" :value="item"> </el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="表名称" prop="tableName"> <el-form-item label="表名称" prop="tableName">
<el-input v-model="queryParams.tableName" placeholder="请输入表名称" clearable @keyup.enter="handleQuery" /> <el-input v-model="queryParams.tableName" placeholder="请输入表名称" clearable @keyup.enter="handleQuery" />
</el-form-item> </el-form-item>
<el-form-item label="表描述" prop="tableComment"> <el-form-item label="表描述" prop="tableComment">
@ -26,7 +26,7 @@
<el-table-column prop="createTime" label="创建时间"></el-table-column> <el-table-column prop="createTime" label="创建时间"></el-table-column>
<el-table-column prop="updateTime" label="更新时间"></el-table-column> <el-table-column prop="updateTime" label="更新时间"></el-table-column>
</el-table> </el-table>
<pagination v-show="total>0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-row> </el-row>
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">
@ -40,8 +40,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { listDbTable, importTable, getDataNames } from '@/api/tool/gen'; import { listDbTable, importTable, getDataNames } from '@/api/tool/gen';
import { DbTableQuery, DbTableVO } from '@/api/tool/gen/types'; import { DbTableQuery, DbTableVO } from '@/api/tool/gen/types';
import { ComponentInternalInstance } from 'vue';
import { ElTable, ElForm } from 'element-plus';
const total = ref(0); const total = ref(0);
const visible = ref(false); const visible = ref(false);
@ -49,15 +47,15 @@ const tables = ref<Array<string>>([]);
const dbTableList = ref<Array<DbTableVO>>([]); const dbTableList = ref<Array<DbTableVO>>([]);
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const tableRef = ref(ElTable); const tableRef = ref<ElTableInstance>();
const queryFormRef = ref(ElForm); const queryFormRef = ref<ElFormInstance>();
const queryParams = reactive<DbTableQuery>({ const queryParams = reactive<DbTableQuery>({
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
dataName: '', dataName: '',
tableName: '', tableName: '',
tableComment: '' tableComment: ''
}); });
const dataNameList = ref<Array<string>>([]); const dataNameList = ref<Array<string>>([]);
@ -65,52 +63,53 @@ const emit = defineEmits(["ok"]);
/** 查询参数列表 */ /** 查询参数列表 */
const show = (dataName: string) => { const show = (dataName: string) => {
getDataNameList(); getDataNameList();
if(dataName){ if (dataName) {
queryParams.dataName = dataName; queryParams.dataName = dataName;
} else { } else {
queryParams.dataName = 'master'; queryParams.dataName = 'master';
} }
getList(); getList();
visible.value = true; visible.value = true;
} }
/** 单击选择行 */ /** 单击选择行 */
const clickRow = (row: DbTableVO) => { const clickRow = (row: DbTableVO) => {
tableRef.value.toggleRowSelection(row); // ele bug
tableRef.value?.toggleRowSelection(row, false);
} }
/** 多选框选中数据 */ /** 多选框选中数据 */
const handleSelectionChange = (selection: DbTableVO[]) => { const handleSelectionChange = (selection: DbTableVO[]) => {
tables.value = selection.map(item => item.tableName); tables.value = selection.map(item => item.tableName);
} }
/** 查询表数据 */ /** 查询表数据 */
const getList = async () => { const getList = async () => {
const res = await listDbTable(queryParams); const res = await listDbTable(queryParams);
dbTableList.value = res.rows; dbTableList.value = res.rows;
total.value = res.total; total.value = res.total;
} }
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
queryParams.pageNum = 1; queryParams.pageNum = 1;
getList(); getList();
} }
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
queryFormRef.value.resetFields(); queryFormRef.value?.resetFields();
handleQuery(); handleQuery();
} }
/** 导入按钮操作 */ /** 导入按钮操作 */
const handleImportTable = async () => { const handleImportTable = async () => {
const tableNames = tables.value.join(","); const tableNames = tables.value.join(",");
if (tableNames == "") { if (tableNames == "") {
proxy?.$modal.msgError("请选择要导入的表"); proxy?.$modal.msgError("请选择要导入的表");
return; return;
} }
const res = await importTable({ tables: tableNames, dataName: queryParams.dataName }); const res = await importTable({ tables: tableNames, dataName: queryParams.dataName });
proxy?.$modal.msgSuccess(res.msg); proxy?.$modal.msgSuccess(res.msg);
if (res.code === 200) { if (res.code === 200) {
visible.value = false; visible.value = false;
emit("ok"); emit("ok");
} }
} }
/** 查询多数据源名称 */ /** 查询多数据源名称 */
const getDataNameList = async () => { const getDataNameList = async () => {
@ -119,6 +118,6 @@ const getDataNameList = async () => {
} }
defineExpose({ defineExpose({
show, show,
}); });
</script> </script>

View File

@ -1,39 +1,41 @@
<template> <template>
<div class="p-2"> <div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="search" v-show="showSearch"> <div class="mb-[10px]" v-show="showSearch">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px"> <el-card shadow="hover">
<el-form-item label="数据源" prop="dataName"> <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
<el-select v-model="queryParams.dataName" filterable clearable placeholder="请选择/输入数据源名称" style="width: 200px"> <el-form-item label="数据源" prop="dataName">
<el-option key="" label="全部" value="" /> <el-select v-model="queryParams.dataName" filterable clearable placeholder="请选择/输入数据源名称" style="width: 200px">
<el-option v-for="item in dataNameList" :key="item" :label="item" :value="item"> </el-option> <el-option key="" label="全部" value="" />
</el-select> <el-option v-for="item in dataNameList" :key="item" :label="item" :value="item"> </el-option>
</el-form-item> </el-select>
<el-form-item label="表名称" prop="tableName"> </el-form-item>
<el-input v-model="queryParams.tableName" placeholder="请输入表名称" clearable style="width: 200px" @keyup.enter="handleQuery" /> <el-form-item label="表名称" prop="tableName">
</el-form-item> <el-input v-model="queryParams.tableName" placeholder="请输入表名称" clearable style="width: 200px" @keyup.enter="handleQuery" />
<el-form-item label="表描述" prop="tableComment"> </el-form-item>
<el-input v-model="queryParams.tableComment" placeholder="请输入表描述" clearable style="width: 200px" @keyup.enter="handleQuery" /> <el-form-item label="表描述" prop="tableComment">
</el-form-item> <el-input v-model="queryParams.tableComment" placeholder="请输入表描述" clearable style="width: 200px" @keyup.enter="handleQuery" />
<el-form-item label="创建时间" style="width: 308px"> </el-form-item>
<el-date-picker <el-form-item label="创建时间" style="width: 308px">
v-model="dateRange" <el-date-picker
value-format="YYYY-MM-DD" v-model="dateRange"
type="daterange" value-format="YYYY-MM-DD"
range-separator="-" type="daterange"
start-placeholder="开始日期" range-separator="-"
end-placeholder="结束日期" start-placeholder="开始日期"
></el-date-picker> end-placeholder="结束日期"
</el-form-item> ></el-date-picker>
<el-form-item> </el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button> <el-form-item>
<el-button icon="Refresh" @click="resetQuery">重置</el-button> <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
</el-form-item> <el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form> </el-form-item>
</el-form>
</el-card>
</div> </div>
</transition> </transition>
<el-card shadow="never"> <el-card shadow="hover">
<template #header> <template #header>
<el-row :gutter="10" class="mb8"> <el-row :gutter="10" class="mb8">
<el-col :span="1.5"> <el-col :span="1.5">
@ -99,9 +101,9 @@
:name="(key as any).substring((key as any).lastIndexOf('/') + 1, (key as any).indexOf('.vm'))" :name="(key as any).substring((key as any).lastIndexOf('/') + 1, (key as any).indexOf('.vm'))"
:key="value" :key="value"
> >
<el-link :underline="false" icon="DocumentCopy" v-copyText="value" v-copyText:callback="copyTextSuccess" style="float:right" <el-link :underline="false" icon="DocumentCopy" v-copyText="value" v-copyText:callback="copyTextSuccess" style="float:right">
>&nbsp;复制</el-link &nbsp;复制
> </el-link>
<pre>{{ value }}</pre> <pre>{{ value }}</pre>
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
@ -114,9 +116,7 @@
import { listTable, previewTable, delTable, genCode, synchDb, getDataNames } from '@/api/tool/gen'; import { listTable, previewTable, delTable, genCode, synchDb, getDataNames } from '@/api/tool/gen';
import { TableQuery, TableVO } from '@/api/tool/gen/types'; import { TableQuery, TableVO } from '@/api/tool/gen/types';
import router from '@/router'; import router from '@/router';
import importTable from './importTable.vue'; import ImportTable from './importTable.vue';
import { ComponentInternalInstance } from 'vue';
import { ElForm, DateModelType } from 'element-plus';
const route = useRoute(); const route = useRoute();
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@ -132,35 +132,35 @@ const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
const uniqueId = ref(""); const uniqueId = ref("");
const dataNameList = ref<Array<string>>([]); const dataNameList = ref<Array<string>>([]);
const queryFormRef = ref(ElForm); const queryFormRef = ref<ElFormInstance>();
const importRef = ref(importTable); const importRef = ref<InstanceType<typeof ImportTable>>();
const queryParams = ref<TableQuery>({ const queryParams = ref<TableQuery>({
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
tableName: '', tableName: '',
tableComment: '', tableComment: '',
dataName: "" dataName: ""
}) })
const preview = ref <any>({ const preview = ref<any>({
data: {}, data: {},
activeName: 'domain.java' activeName: 'domain.java'
}) })
const dialog = reactive<DialogOption>({ const dialog = reactive<DialogOption>({
visible: false, visible: false,
title: '代码预览' title: '代码预览'
}); });
onActivated(() => { onActivated(() => {
const time = route.query.t; const time = route.query.t;
if (time != null && time != uniqueId.value) { if (time != null && time != uniqueId.value) {
uniqueId.value = time as string; uniqueId.value = time as string;
queryParams.value.pageNum = Number(route.query.pageNum); queryParams.value.pageNum = Number(route.query.pageNum);
dateRange.value = ['', '']; dateRange.value = ['', ''];
queryFormRef.value.resetFields(); queryFormRef.value?.resetFields();
getList(); getList();
} }
}) })
/** 查询多数据源名称 */ /** 查询多数据源名称 */
@ -171,81 +171,81 @@ const getDataNameList = async () => {
/** 查询表集合 */ /** 查询表集合 */
const getList = async () => { const getList = async () => {
loading.value = true; loading.value = true;
const res = await listTable(proxy?.addDateRange(queryParams.value, dateRange.value)); const res = await listTable(proxy?.addDateRange(queryParams.value, dateRange.value));
tableList.value = res.rows; tableList.value = res.rows;
total.value = res.total; total.value = res.total;
loading.value = false; loading.value = false;
} }
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
queryParams.value.pageNum = 1; queryParams.value.pageNum = 1;
getList(); getList();
} }
/** 生成代码操作 */ /** 生成代码操作 */
const handleGenTable = async (row?: TableVO) => { const handleGenTable = async (row?: TableVO) => {
const tbIds = row?.tableId || ids.value; const tbIds = row?.tableId || ids.value;
if (tbIds == "") { if (tbIds == "") {
proxy?.$modal.msgError('请选择要生成的数据'); proxy?.$modal.msgError('请选择要生成的数据');
return; return;
} }
if (row?.genType === "1") { if (row?.genType === "1") {
await genCode(row.tableId); await genCode(row.tableId);
proxy?.$modal.msgSuccess('成功生成到自定义路径:' + row.genPath); proxy?.$modal.msgSuccess('成功生成到自定义路径:' + row.genPath);
} else { } else {
proxy?.$download.zip('/tool/gen/batchGenCode?tableIdStr=' + tbIds, 'ruoyi.zip'); proxy?.$download.zip('/tool/gen/batchGenCode?tableIdStr=' + tbIds, 'ruoyi.zip');
} }
} }
/** 同步数据库操作 */ /** 同步数据库操作 */
const handleSynchDb = async (row: TableVO) => { const handleSynchDb = async (row: TableVO) => {
const tableId = row.tableId; const tableId = row.tableId;
await proxy?.$modal.confirm('确认要强制同步"' + row.tableName + '"表结构吗?'); await proxy?.$modal.confirm('确认要强制同步"' + row.tableName + '"表结构吗?');
await synchDb(tableId); await synchDb(tableId);
proxy?.$modal.msgSuccess('同步成功'); proxy?.$modal.msgSuccess('同步成功');
} }
/** 打开导入表弹窗 */ /** 打开导入表弹窗 */
const openImportTable = () => { const openImportTable = () => {
importRef.value.show(queryParams.value.dataName); importRef.value?.show(queryParams.value.dataName);
} }
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
dateRange.value = ['', '']; dateRange.value = ['', ''];
queryFormRef.value.resetFields(); queryFormRef.value?.resetFields();
handleQuery(); handleQuery();
} }
/** 预览按钮 */ /** 预览按钮 */
const handlePreview = async (row: TableVO) => { const handlePreview = async (row: TableVO) => {
const res = await previewTable(row.tableId); const res = await previewTable(row.tableId);
preview.value.data = res.data; preview.value.data = res.data;
dialog.visible = true; dialog.visible = true;
preview.value.activeName = 'domain.java'; preview.value.activeName = 'domain.java';
} }
/** 复制代码成功 */ /** 复制代码成功 */
const copyTextSuccess = () => { const copyTextSuccess = () => {
proxy?.$modal.msgSuccess('复制成功'); proxy?.$modal.msgSuccess('复制成功');
} }
// //
const handleSelectionChange = (selection: TableVO[]) => { const handleSelectionChange = (selection: TableVO[]) => {
ids.value = selection.map(item => item.tableId); ids.value = selection.map(item => item.tableId);
single.value = selection.length != 1; single.value = selection.length != 1;
multiple.value = !selection.length; multiple.value = !selection.length;
} }
/** 修改按钮操作 */ /** 修改按钮操作 */
const handleEditTable = (row?: TableVO) => { const handleEditTable = (row?: TableVO) => {
const tableId = row?.tableId || ids.value[0]; const tableId = row?.tableId || ids.value[0];
router.push({ path: '/tool/gen-edit/index/' + tableId, query: { pageNum: queryParams.value.pageNum } }); router.push({ path: '/tool/gen-edit/index/' + tableId, query: { pageNum: queryParams.value.pageNum } });
} }
/** 删除按钮操作 */ /** 删除按钮操作 */
const handleDelete = async (row?: TableVO) => { const handleDelete = async (row?: TableVO) => {
const tableIds = row?.tableId || ids.value; const tableIds = row?.tableId || ids.value;
await proxy?.$modal.confirm('是否确认删除表编号为"' + tableIds + '"的数据项?'); await proxy?.$modal.confirm('是否确认删除表编号为"' + tableIds + '"的数据项?');
await delTable(tableIds); await delTable(tableIds);
getList(); await getList();
proxy?.$modal.msgSuccess('删除成功'); proxy?.$modal.msgSuccess('删除成功');
} }
onMounted(() => { onMounted(() => {
getList(); getList();
getDataNameList(); getDataNameList();
}) })
</script> </script>