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

@ -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,45 +29,28 @@
<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: { height: propTypes.number.def(400),
type: Number,
default: null,
},
/* 最小高度 */ /* 最小高度 */
minHeight: { minHeight: propTypes.number.def(400),
type: Number,
default: null,
},
/* 只读 */ /* 只读 */
readOnly: { readOnly: propTypes.bool.def(false),
type: Boolean,
default: false,
},
/* 上传文件大小限制(MB) */ /* 上传文件大小限制(MB) */
fileSize: { fileSize: propTypes.number.def(5),
type: Number,
default: 5,
},
/* 类型base64格式、url格式 */ /* 类型base64格式、url格式 */
type: { type: propTypes.string.def('url')
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();
@ -82,7 +65,7 @@ const options = ref({
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] }], //
@ -166,16 +149,20 @@ const handleUploadError = (err: any) => {
</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) {
@ -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 = [];
@ -209,18 +196,21 @@ const listToString = (list: any[], separator?: string) => {
.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;
} }

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,11 +9,10 @@
</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'])

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) {

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,11 +9,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.def(''),
type: String,
default: ""
},
width: { width: {
type: [Number, String], type: [Number, String],
default: "" default: ""
@ -37,7 +36,7 @@ const realSrcList = computed(() => {
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);
}); });
@ -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,7 +29,6 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ElTreeSelect } from 'element-plus'
const props = defineProps({ const props = defineProps({
/* 配置项 */ /* 配置项 */
@ -68,7 +67,7 @@ const props = defineProps({
}) })
const selectTree = ref(ElTreeSelect); const selectTree = ref<ElTreeSelectInstance>();
const emit = defineEmits(['update:value']); const emit = defineEmits(['update:value']);
@ -81,14 +80,14 @@ const valueId = computed({
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 { } else {
@ -96,27 +95,27 @@ function initHandle() {
} }
}) })
} }
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,6 +95,12 @@ 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) => {
@ -104,7 +116,7 @@ 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('/');
} }
/** 租户列表 */ /** 租户列表 */
@ -121,7 +133,7 @@ defineExpose({
}) })
const toggleSideBar = () => { const toggleSideBar = () => {
appStore.toggleSideBar(false) appStore.toggleSideBar(false);
} }
const logout = async () => { const logout = async () => {
@ -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,14 +34,13 @@ export default {
}, },
async zip(url: string, name: string) { async zip(url: string, name: string) {
url = baseURL + url; url = baseURL + url;
downloadLoadingInstance = ElLoading.service({ text: '正在下载数据,请稍候', background: 'rgba(0, 0, 0, 0.7)' });
try {
const res = await axios({ const res = await axios({
method: 'get', method: 'get',
url: url, url: url,
responseType: 'blob', responseType: 'blob',
headers: { headers: globalHeaders,
Authorization: 'Bearer ' + getToken(),
datasource: localStorage.getItem('dataName')
}
}); });
const isBlob = blobValidate(res.data); const isBlob = blobValidate(res.data);
if (isBlob) { if (isBlob) {
@ -50,6 +49,12 @@ export default {
} else { } else {
this.printErrMsg(res.data); this.printErrMsg(res.data);
} }
downloadLoadingInstance.close();
} catch (r) {
console.error(r);
ElMessage.error('下载文件出现错误,请联系管理员!');
downloadLoadingInstance.close();
}
}, },
async printErrMsg(data: any) { async printErrMsg(data: any) {
const resText = await data.text(); const resText = await data.text();

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,7 +1,8 @@
<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-card shadow="hover">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px"> <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
<el-form-item label="部门id" prop="deptId"> <el-form-item label="部门id" prop="deptId">
<el-input v-model="queryParams.deptId" placeholder="请输入部门id" clearable @keyup.enter="handleQuery" /> <el-input v-model="queryParams.deptId" placeholder="请输入部门id" clearable @keyup.enter="handleQuery" />
@ -23,10 +24,11 @@
<el-button icon="Refresh" @click="resetQuery">重置</el-button> <el-button icon="Refresh" @click="resetQuery">重置</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" 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,30 +208,24 @@ 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
dialog.visible = true;
dialog.title = "修改测试单";
nextTick(async () => {
reset(); reset();
const _id = row?.id || ids.value[0] const _id = row?.id || ids.value[0]
const res = await getDemo(_id); const res = await getDemo(_id);
loading.value = false;
Object.assign(form.value, res.data); Object.assign(form.value, res.data);
}); dialog.visible = true;
dialog.title = "修改测试单";
} }
/** 提交按钮 */ /** 提交按钮 */
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) {

View File

@ -1,7 +1,8 @@
<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-card shadow="hover">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px"> <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
<el-form-item label="树节点名" prop="treeName"> <el-form-item label="树节点名" prop="treeName">
<el-input v-model="queryParams.treeName" placeholder="请输入树节点名" clearable @keyup.enter="handleQuery" /> <el-input v-model="queryParams.treeName" placeholder="请输入树节点名" clearable @keyup.enter="handleQuery" />
@ -11,10 +12,11 @@
<el-button icon="Refresh" @click="resetQuery">重置</el-button> <el-button icon="Refresh" @click="resetQuery">重置</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" 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) => {
dialog.visible = true;
dialog.title = "添加测试树";
nextTick(() => {
reset(); reset();
getTreeselect(); getTreeselect();
if (row != null && row.id) { if (row && row.id) {
form.value.parentId = row.id; form.value.parentId = row.id;
} else { } else {
form.value.parentId = 0; form.value.parentId = 0;
} }
}); dialog.visible = true;
dialog.title = "添加测试树";
} }
/** 展开/折叠操作 */ /** 展开/折叠操作 */
@ -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;
dialog.visible = true;
dialog.title = "修改测试树";
nextTick(async () => {
reset(); reset();
await getTreeselect(); await getTreeselect();
if (row != null) { if (row) {
form.value.parentId = row.id; form.value.parentId = row.id;
} }
const res = await getTree(row.id); const res = await getTree(row.id);
loading.value = false;
Object.assign(form.value, res.data); Object.assign(form.value, res.data);
}); dialog.visible = true;
dialog.title = "修改测试树";
} }
/** 提交按钮 */ /** 提交按钮 */
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,26 +60,26 @@
<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: '请输入您的密码' }],
@ -83,19 +97,19 @@ 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 {
// //
@ -105,7 +119,6 @@ const handleLogin = () => {
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 || '/' });
@ -141,11 +154,11 @@ const getCookie = () => {
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;
} }
@ -163,6 +176,28 @@ const initTenantList = async () => {
} }
} }
//
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();
@ -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();
@ -157,7 +156,6 @@ const getList = async () => {
} }
] ]
}); });
const usedmemoryInstance = echarts.init(usedmemory.value, "macarons"); const usedmemoryInstance = echarts.init(usedmemory.value, "macarons");
usedmemoryInstance.setOption({ usedmemoryInstance.setOption({
tooltip: { tooltip: {
@ -181,6 +179,10 @@ const getList = async () => {
} }
] ]
}) })
window.addEventListener("resize",()=>{
commandstatsIntance.resize()
usedmemoryInstance.resize()
});
} }
onMounted(() => { onMounted(() => {

View File

@ -1,7 +1,8 @@
<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-card shadow="hover">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px"> <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
<el-form-item label="登录地址" prop="ipaddr"> <el-form-item label="登录地址" prop="ipaddr">
<el-input v-model="queryParams.ipaddr" placeholder="请输入登录地址" clearable style="width: 240px;" @keyup.enter="handleQuery" /> <el-input v-model="queryParams.ipaddr" placeholder="请输入登录地址" clearable style="width: 240px;" @keyup.enter="handleQuery" />
@ -30,10 +31,11 @@
<el-button icon="Refresh" @click="resetQuery">重置</el-button> <el-button icon="Refresh" @click="resetQuery">重置</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" 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,6 +1,7 @@
<template> <template>
<div class="p-2"> <div class="p-2">
<div class="search"> <div class="mb-[10px]">
<el-card shadow="hover">
<el-form :model="queryParams" ref="queryFormRef" :inline="true"> <el-form :model="queryParams" ref="queryFormRef" :inline="true">
<el-form-item label="登录地址" prop="ipaddr"> <el-form-item label="登录地址" prop="ipaddr">
<el-input v-model="queryParams.ipaddr" placeholder="请输入登录地址" clearable style="width: 200px" @keyup.enter="handleQuery" /> <el-input v-model="queryParams.ipaddr" placeholder="请输入登录地址" clearable style="width: 200px" @keyup.enter="handleQuery" />
@ -13,8 +14,9 @@
<el-button icon="Refresh" @click="resetQuery">重置</el-button> <el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item> </el-form-item>
</el-form> </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,7 +64,7 @@ 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,
@ -87,14 +88,14 @@ const handleQuery = () => {
} }
/** 重置按钮操作 */ /** 重置按钮操作 */
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("删除成功");
} }

View File

@ -1,7 +1,8 @@
<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-card shadow="hover">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px"> <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
<el-form-item label="系统模块" prop="title"> <el-form-item label="系统模块" prop="title">
<el-input v-model="queryParams.title" placeholder="请输入系统模块" clearable style="width: 240px;" @keyup.enter="handleQuery" /> <el-input v-model="queryParams.title" placeholder="请输入系统模块" clearable style="width: 240px;" @keyup.enter="handleQuery" />
@ -35,10 +36,11 @@
<el-button icon="Refresh" @click="resetQuery">重置</el-button> <el-button icon="Refresh" @click="resetQuery">重置</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" 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,8 +181,8 @@ 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,
@ -247,9 +247,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;
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[]) => {
@ -272,7 +272,7 @@ 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("删除成功");
} }
@ -280,7 +280,7 @@ const handleDelete = async (row?: OperLogVO) => {
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("清空成功");
} }

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,7 +58,6 @@
<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();
@ -85,7 +84,7 @@ const equalToPassword = (rule: any, value: string, callback: any) => {
} }
}; };
const registerRules: FormRules = { const registerRules: ElFormRules = {
tenantId: [ tenantId: [
{ required: true, trigger: "blur", message: "请输入您的租户编号" } { required: true, trigger: "blur", message: "请输入您的租户编号" }
], ],

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,7 +1,8 @@
<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-card shadow="hover">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px"> <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
<el-form-item label="参数名称" prop="configName"> <el-form-item label="参数名称" prop="configName">
<el-input v-model="queryParams.configName" placeholder="请输入参数名称" clearable style="width: 240px" @keyup.enter="handleQuery" /> <el-input v-model="queryParams.configName" placeholder="请输入参数名称" clearable style="width: 240px" @keyup.enter="handleQuery" />
@ -30,9 +31,10 @@
<el-button icon="Refresh" @click="resetQuery">重置</el-button> <el-button icon="Refresh" @click="resetQuery">重置</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" 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,8 +138,8 @@ 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: ''
@ -153,7 +153,7 @@ const initFormData: ConfigForm = {
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,
@ -185,8 +185,8 @@ const cancel = () => {
} }
/** 表单重置 */ /** 表单重置 */
const reset = () => { const reset = () => {
form.value = {...initFormData}; form.value = { ...initFormData };
configFormRef.value.resetFields(); configFormRef.value?.resetFields();
} }
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
@ -196,7 +196,7 @@ const handleQuery = () => {
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
dateRange.value = ['', '']; dateRange.value = ['', ''];
queryFormRef.value.resetFields(); queryFormRef.value?.resetFields();
handleQuery(); handleQuery();
} }
/** 多选框选中数据 */ /** 多选框选中数据 */
@ -207,31 +207,27 @@ const handleSelectionChange = (selection: ConfigVO[]) => {
} }
/** 新增按钮操作 */ /** 新增按钮操作 */
const handleAdd = () => { const handleAdd = () => {
reset();
dialog.visible = true; dialog.visible = true;
dialog.title = "添加参数"; dialog.title = "添加参数";
nextTick(() => {
reset();
})
} }
/** 修改按钮操作 */ /** 修改按钮操作 */
const handleUpdate = (row?: ConfigVO) => { const handleUpdate = async (row?: ConfigVO) => {
reset();
const configId = row?.configId || ids.value[0];
const res = await getConfig(configId);
Object.assign(form.value, res.data);
dialog.visible = true; dialog.visible = true;
dialog.title = "修改参数"; dialog.title = "修改参数";
const configId = row?.configId || ids.value[0];
nextTick(async () => {
reset();
const res = await getConfig(configId);
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();
} }
}); });
} }
@ -240,7 +236,7 @@ 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("删除成功");
} }
/** 导出按钮操作 */ /** 导出按钮操作 */

View File

@ -1,9 +1,10 @@
<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-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px"> <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px">
<el-form-item label="菜单名称" prop="menuName"> <el-form-item label="部门名称" prop="deptName">
<el-input v-model="queryParams.deptName" placeholder="请输入部门名称" clearable @keyup.enter="handleQuery" /> <el-input v-model="queryParams.deptName" placeholder="请输入部门名称" clearable @keyup.enter="handleQuery" />
</el-form-item> </el-form-item>
<el-form-item label="状态" prop="status"> <el-form-item label="状态" prop="status">
@ -16,10 +17,11 @@
<el-button icon="Refresh" @click="resetQuery">重置</el-button> <el-button icon="Refresh" @click="resetQuery">重置</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">
@ -130,7 +132,6 @@
<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 {
@ -155,9 +156,9 @@ const dialog = reactive<DialogOption>({
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,
@ -170,7 +171,7 @@ const initFormData: DeptForm = {
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,
@ -205,8 +206,8 @@ const cancel = () => {
} }
/** 表单重置 */ /** 表单重置 */
const reset = () => { const reset = () => {
form.value = {...initFormData}; form.value = { ...initFormData };
deptFormRef.value.resetFields(); deptFormRef.value?.resetFields();
} }
/** 搜索按钮操作 */ /** 搜索按钮操作 */
@ -215,26 +216,10 @@ const handleQuery = () => {
} }
/** 重置按钮操作 */ /** 重置按钮操作 */
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;
@ -243,38 +228,55 @@ const handleToggleExpandAll = () => {
/** 展开/折叠所有 */ /** 展开/折叠所有 */
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);
dialog.visible = true;
dialog.title = "修改部门";
nextTick(async () => {
reset(); reset();
const res = await getDept(row.deptId);
form.value = res.data form.value = res.data
const response = await listDeptExcludeChild(row.deptId); const response = await listDeptExcludeChild(row.deptId);
const data = proxy?.handleTree<DeptOptionsType>(response.data, "deptId") const data = proxy?.handleTree<DeptOptionsType>(response.data, "deptId")
if (data) { if (data) {
deptOptions.value = data; deptOptions.value = data;
if (data.length === 0) { if (data.length === 0) {
const noResultsOptions: DeptOptionsType = { deptId: res.data.parentId, deptName: res.data.parentName, children: [] }; const noResultsOptions: DeptOptionsType = {
deptId: res.data.parentId,
deptName: res.data.parentName,
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();
} }
}) })
} }
@ -282,7 +284,7 @@ const submitForm = () => {
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("删除成功");
} }

View File

@ -1,7 +1,8 @@
<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-card shadow="hover">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px"> <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
<el-form-item label="字典名称" prop="dictType"> <el-form-item label="字典名称" prop="dictType">
<el-select v-model="queryParams.dictType" style="width: 200px"> <el-select v-model="queryParams.dictType" style="width: 200px">
@ -21,9 +22,10 @@
<el-button icon="Refresh" @click="resetQuery">重置</el-button> <el-button icon="Refresh" @click="resetQuery">重置</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" 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,8 +154,8 @@ 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>({
@ -231,7 +231,7 @@ const cancel = () => {
/** 表单重置 */ /** 表单重置 */
const reset = () => { const reset = () => {
form.value = { ...initFormData }; form.value = { ...initFormData };
dataFormRef.value.resetFields(); dataFormRef.value?.resetFields();
} }
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
@ -245,18 +245,16 @@ const handleClose = () => {
} }
/** 重置按钮操作 */ /** 重置按钮操作 */
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;
dialog.title = "添加字典数据";
nextTick(() => {
reset(); reset();
form.value.dictType = queryParams.value.dictType; form.value.dictType = queryParams.value.dictType;
}) dialog.visible = true;
dialog.title = "添加字典数据";
} }
/** 多选框选中数据 */ /** 多选框选中数据 */
const handleSelectionChange = (selection: DictDataVO[]) => { const handleSelectionChange = (selection: DictDataVO[]) => {
@ -265,25 +263,23 @@ const handleSelectionChange = (selection: DictDataVO[]) => {
multiple.value = !selection.length; multiple.value = !selection.length;
} }
/** 修改按钮操作 */ /** 修改按钮操作 */
const handleUpdate = (row?: DictDataVO) => { const handleUpdate = async (row?: DictDataVO) => {
reset();
const dictCode = row?.dictCode || ids.value[0]; const dictCode = row?.dictCode || ids.value[0];
const res = await getData(dictCode);
Object.assign(form.value, res.data);
dialog.visible = true; dialog.visible = true;
dialog.title = "修改字典数据"; dialog.title = "修改字典数据";
nextTick(async () => {
const res = await getData(dictCode);
reset();
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();
} }
}); });
@ -293,7 +289,7 @@ 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);

View File

@ -1,7 +1,8 @@
<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-card shadow="hover">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px"> <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
<el-form-item label="字典名称" prop="dictName"> <el-form-item label="字典名称" prop="dictName">
<el-input v-model="queryParams.dictName" placeholder="请输入字典名称" clearable style="width: 240px" @keyup.enter="handleQuery" /> <el-input v-model="queryParams.dictName" placeholder="请输入字典名称" clearable style="width: 240px" @keyup.enter="handleQuery" />
@ -30,9 +31,10 @@
<el-button icon="Refresh" @click="resetQuery">重置</el-button> <el-button icon="Refresh" @click="resetQuery">重置</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" 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,8 +139,8 @@ 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>({
@ -156,7 +156,7 @@ const initFormData: DictTypeForm = {
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,
@ -188,8 +188,8 @@ const cancel = () => {
} }
/** 表单重置 */ /** 表单重置 */
const reset = () => { const reset = () => {
form.value = {...initFormData}; form.value = { ...initFormData };
dictFormRef.value.resetFields(); dictFormRef.value?.resetFields();
} }
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
@ -199,16 +199,14 @@ const handleQuery = () => {
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
dateRange.value = ['', '']; dateRange.value = ['', ''];
queryFormRef.value.resetFields(); queryFormRef.value?.resetFields();
handleQuery(); handleQuery();
} }
/** 新增按钮操作 */ /** 新增按钮操作 */
const handleAdd = () => { const handleAdd = () => {
reset();
dialog.visible = true; dialog.visible = true;
dialog.title = "添加字典类型"; dialog.title = "添加字典类型";
nextTick(() => {
reset();
})
} }
/** 多选框选中数据 */ /** 多选框选中数据 */
const handleSelectionChange = (selection: DictTypeVO[]) => { const handleSelectionChange = (selection: DictTypeVO[]) => {
@ -217,20 +215,17 @@ const handleSelectionChange = (selection: DictTypeVO[]) => {
multiple.value = !selection.length; multiple.value = !selection.length;
} }
/** 修改按钮操作 */ /** 修改按钮操作 */
const handleUpdate = (row?: DictTypeVO) => { const handleUpdate = async (row?: DictTypeVO) => {
reset();
const dictId = row?.dictId || ids.value[0];
const res = await getType(dictId);
Object.assign(form.value, res.data);
dialog.visible = true; dialog.visible = true;
dialog.title = "修改字典类型"; dialog.title = "修改字典类型";
const dictId = row?.dictId || ids.value[0];
nextTick(async () => {
reset();
const res = await getType(dictId);
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("操作成功");
@ -260,7 +255,7 @@ const handleRefreshCache = async () => {
useDictStore().cleanDict(); useDictStore().cleanDict();
} }
onMounted(()=>{ onMounted(() => {
getList(); getList();
}) })
</script> </script>

View File

@ -1,7 +1,8 @@
<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-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px"> <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px">
<el-form-item label="菜单名称" prop="menuName"> <el-form-item label="菜单名称" prop="menuName">
<el-input v-model="queryParams.menuName" placeholder="请输入菜单名称" clearable @keyup.enter="handleQuery" /> <el-input v-model="queryParams.menuName" placeholder="请输入菜单名称" clearable @keyup.enter="handleQuery" />
@ -16,10 +17,11 @@
<el-button icon="Refresh" @click="resetQuery">重置</el-button> <el-button icon="Refresh" @click="resetQuery">重置</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">
@ -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,9 +263,7 @@
<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;
@ -286,8 +285,8 @@ const dialog = reactive<DialogOption>({
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,
@ -314,7 +313,7 @@ const data = reactive<PageData<MenuForm, MenuQuery>>({
}, },
}) })
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)
/** 查询菜单列表 */ /** 查询菜单列表 */
@ -343,7 +342,7 @@ const cancel = () => {
/** 表单重置 */ /** 表单重置 */
const reset = () => { const reset = () => {
form.value = { ...initFormData }; form.value = { ...initFormData };
menuFormRef.value.resetFields(); menuFormRef.value?.resetFields();
} }
/** 搜索按钮操作 */ /** 搜索按钮操作 */
@ -352,19 +351,16 @@ const handleQuery = () => {
} }
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
queryFormRef.value.resetFields(); queryFormRef.value?.resetFields();
handleQuery(); handleQuery();
} }
/** 新增按钮操作 */ /** 新增按钮操作 */
const handleAdd = (row?: MenuVO) => { const handleAdd = (row?: MenuVO) => {
reset();
getTreeselect();
row && row.menuId ? form.value.parentId = row.menuId : form.value.parentId = 0;
dialog.visible = true; dialog.visible = true;
dialog.title = "添加菜单"; dialog.title = "添加菜单";
getTreeselect();
nextTick(() => {
reset();
row && row.menuId ? form.value.parentId = row.menuId : form.value.parentId = 0;
})
} }
/** 展开/折叠操作 */ /** 展开/折叠操作 */
const handleToggleExpandAll = () => { const handleToggleExpandAll = () => {
@ -374,32 +370,29 @@ const handleToggleExpandAll = () => {
/** 展开/折叠所有 */ /** 展开/折叠所有 */
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) => {
reset();
await getTreeselect(); await getTreeselect();
dialog.visible = true;
dialog.title = "修改菜单";
await nextTick(async () => {
if (row.menuId) { if (row.menuId) {
const { data } = await getMenu(row.menuId); const { data } = await getMenu(row.menuId);
reset();
form.value = data; form.value = data;
} }
}) dialog.visible = true;
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();
} }
}) })
} }
@ -407,7 +400,7 @@ const submitForm = () => {
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("删除成功");
} }

View File

@ -1,7 +1,8 @@
<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-card shadow="hover">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px"> <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
<el-form-item label="公告标题" prop="noticeTitle"> <el-form-item label="公告标题" prop="noticeTitle">
<el-input v-model="queryParams.noticeTitle" placeholder="请输入公告标题" clearable style="width: 200px" @keyup.enter="handleQuery" /> <el-input v-model="queryParams.noticeTitle" placeholder="请输入公告标题" clearable style="width: 200px" @keyup.enter="handleQuery" />
@ -19,10 +20,11 @@
<el-button icon="Refresh" @click="resetQuery">重置</el-button> <el-button icon="Refresh" @click="resetQuery">重置</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" 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,8 +134,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 noticeFormRef = ref(ElForm); const noticeFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({ const dialog = reactive<DialogOption>({
@ -186,7 +186,7 @@ const cancel = () => {
/** 表单重置 */ /** 表单重置 */
const reset = () => { const reset = () => {
form.value = { ...initFormData }; form.value = { ...initFormData };
noticeFormRef.value.resetFields(); noticeFormRef.value?.resetFields();
} }
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
@ -195,7 +195,7 @@ const handleQuery = () => {
} }
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
queryFormRef.value.resetFields(); queryFormRef.value?.resetFields();
handleQuery(); handleQuery();
} }
/** 多选框选中数据 */ /** 多选框选中数据 */
@ -206,31 +206,27 @@ const handleSelectionChange = (selection: NoticeVO[]) => {
} }
/** 新增按钮操作 */ /** 新增按钮操作 */
const handleAdd = () => { const handleAdd = () => {
reset();
dialog.visible = true; dialog.visible = true;
dialog.title = "添加公告"; dialog.title = "添加公告";
nextTick(() => {
reset();
})
} }
/**修改按钮操作 */ /**修改按钮操作 */
const handleUpdate = (row?: NoticeVO) => { const handleUpdate = async (row?: NoticeVO) => {
reset();
const noticeId = row?.noticeId || ids.value[0];
const { data } = await getNotice(noticeId);
Object.assign(form.value, data);
dialog.visible = true; dialog.visible = true;
dialog.title = "修改公告"; dialog.title = "修改公告";
nextTick(async () => {
const noticeId = row?.noticeId || ids.value[0];
reset();
const { data } = await getNotice(noticeId);
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();
} }
}); });
} }
@ -239,7 +235,7 @@ 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("删除成功");
} }

View File

@ -1,7 +1,8 @@
<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-card shadow="hover">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px"> <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
<el-form-item label="配置key" prop="configKey"> <el-form-item label="配置key" prop="configKey">
<el-input v-model="queryParams.configKey" placeholder="配置key" clearable style="width: 200px" @keyup.enter="handleQuery" /> <el-input v-model="queryParams.configKey" placeholder="配置key" clearable style="width: 200px" @keyup.enter="handleQuery" />
@ -20,10 +21,11 @@
<el-button icon="Refresh" @click="resetQuery">重置</el-button> <el-button icon="Refresh" @click="resetQuery">重置</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" 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>
@ -138,9 +140,7 @@ import {
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,8 +155,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 ossConfigFormRef = ref(ElForm); const ossConfigFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({ const dialog = reactive<DialogOption>({
visible: false, visible: false,
@ -262,7 +262,7 @@ const cancel = () => {
/** 表单重置 */ /** 表单重置 */
const reset = () => { const reset = () => {
form.value = { ...initFormData }; form.value = { ...initFormData };
ossConfigFormRef.value.resetFields(); ossConfigFormRef.value?.resetFields();
} }
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
@ -271,7 +271,7 @@ const handleQuery = () => {
} }
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
queryFormRef.value.resetFields(); queryFormRef.value?.resetFields();
handleQuery(); handleQuery();
} }
/** 选择条数 */ /** 选择条数 */
@ -282,28 +282,22 @@ const handleSelectionChange = (selection: OssConfigVO[]) => {
} }
/** 新增按钮操作 */ /** 新增按钮操作 */
const handleAdd = () => { const handleAdd = () => {
reset();
dialog.visible = true; dialog.visible = true;
dialog.title = "添加对象存储配置"; dialog.title = "添加对象存储配置";
nextTick(() => {
reset();
})
} }
/** 修改按钮操作 */ /** 修改按钮操作 */
const handleUpdate = (row?: OssConfigVO) => { const handleUpdate = async (row?: OssConfigVO) => {
loading.value = true; reset();
const ossConfigId = row?.ossConfigId || ids.value[0];
const res = await getOssConfig(ossConfigId);
Object.assign(form.value, res.data);
dialog.visible = true; dialog.visible = true;
dialog.title = "修改对象存储配置"; dialog.title = "修改对象存储配置";
const ossConfigId = row?.ossConfigId || ids.value[0];
nextTick(async () => {
reset();
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) {
@ -313,7 +307,7 @@ const submitForm = () => {
} }
proxy?.$modal.msgSuccess("新增成功"); proxy?.$modal.msgSuccess("新增成功");
dialog.visible = false; dialog.visible = false;
getList(); await getList();
} }
}); });
} }
@ -323,7 +317,7 @@ const handleStatusChange = async (row: OssConfigVO) => {
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";
@ -336,7 +330,7 @@ const handleDelete = async (row?: OssConfigVO) => {
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("删除成功");
} }

View File

@ -1,7 +1,8 @@
<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-card shadow="hover">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px"> <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
<el-form-item label="文件名" prop="fileName"> <el-form-item label="文件名" prop="fileName">
<el-input v-model="queryParams.fileName" placeholder="请输入文件名" clearable style="width: 200px" @keyup.enter="handleQuery" /> <el-input v-model="queryParams.fileName" placeholder="请输入文件名" clearable style="width: 200px" @keyup.enter="handleQuery" />
@ -14,7 +15,7 @@
</el-form-item> </el-form-item>
<el-form-item label="创建时间"> <el-form-item label="创建时间">
<el-date-picker <el-date-picker
v-model="daterangeCreateTime" v-model="dateRangeCreateTime"
value-format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss"
type="daterange" type="daterange"
range-separator="-" range-separator="-"
@ -31,10 +32,11 @@
<el-button icon="Refresh" @click="resetQuery">重置</el-button> <el-button icon="Refresh" @click="resetQuery">重置</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" 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,7 +151,7 @@ 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,
@ -161,8 +161,8 @@ const dialog = reactive<DialogOption>({
// //
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,
@ -195,7 +195,7 @@ 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;
@ -215,7 +215,7 @@ function cancel() {
/** 表单重置 */ /** 表单重置 */
function reset() { function reset() {
form.value = { ...initFormData }; form.value = { ...initFormData };
ossFormRef.value.resetFields(); ossFormRef.value?.resetFields();
} }
/** 搜索按钮操作 */ /** 搜索按钮操作 */
function handleQuery() { function handleQuery() {
@ -225,8 +225,8 @@ function handleQuery() {
/** 重置按钮操作 */ /** 重置按钮操作 */
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();
@ -288,21 +288,17 @@ const handleOssConfig = () => {
} }
/** 文件按钮操作 */ /** 文件按钮操作 */
const handleFile = () => { const handleFile = () => {
dialog.visible = true;
dialog.title = "上传文件";
nextTick(() => {
reset(); reset();
type.value = 0; type.value = 0;
}) dialog.visible = true;
dialog.title = "上传文件";
} }
/** 图片按钮操作 */ /** 图片按钮操作 */
const handleImage = () => { const handleImage = () => {
dialog.visible = true;
dialog.title = "上传图片";
nextTick(() => {
reset(); reset();
type.value = 1; type.value = 1;
}) dialog.visible = true;
dialog.title = "上传图片";
} }
/** 提交按钮 */ /** 提交按钮 */
const submitForm = () => { const submitForm = () => {
@ -319,7 +315,7 @@ const handlePreviewListResource = async (preview: boolean) => {
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 }
} }
@ -329,7 +325,7 @@ const handleDelete = async (row?: OssVO) => {
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("删除成功");
} }

View File

@ -1,7 +1,8 @@
<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-card shadow="hover">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="70"> <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="70">
<el-form-item label="岗位编码" prop="postCode"> <el-form-item label="岗位编码" prop="postCode">
<el-input v-model="queryParams.postCode" placeholder="请输入岗位编码" clearable style="width: 200px" @keyup.enter="handleQuery" /> <el-input v-model="queryParams.postCode" placeholder="请输入岗位编码" clearable style="width: 200px" @keyup.enter="handleQuery" />
@ -19,9 +20,10 @@
<el-button icon="Refresh" @click="resetQuery">重置</el-button> <el-button icon="Refresh" @click="resetQuery">重置</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" 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,8 +121,8 @@ 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,
@ -138,7 +139,7 @@ const initFormData: PostForm = {
} }
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,
@ -170,8 +171,8 @@ const cancel = () => {
} }
/** 表单重置 */ /** 表单重置 */
const reset = () => { const reset = () => {
form.value = {...initFormData}; form.value = { ...initFormData };
postFormRef.value.resetFields(); postFormRef.value?.resetFields();
} }
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
@ -180,7 +181,7 @@ const handleQuery = () => {
} }
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
queryFormRef.value.resetFields(); queryFormRef.value?.resetFields();
handleQuery(); handleQuery();
} }
/** 多选框选中数据 */ /** 多选框选中数据 */
@ -191,31 +192,27 @@ const handleSelectionChange = (selection: PostVO[]) => {
} }
/** 新增按钮操作 */ /** 新增按钮操作 */
const handleAdd = () => { const handleAdd = () => {
reset();
dialog.visible = true; dialog.visible = true;
dialog.title = "添加岗位"; dialog.title = "添加岗位";
nextTick(() => {
reset();
})
} }
/** 修改按钮操作 */ /** 修改按钮操作 */
const handleUpdate = (row?: PostVO) => { const handleUpdate = async (row?: PostVO) => {
dialog.visible = true;
dialog.title = "修改岗位";
nextTick(async () => {
reset(); reset();
const postId = row?.postId || ids.value[0]; const postId = row?.postId || ids.value[0];
const res = await getPost(postId); const res = await getPost(postId);
form.value = res.data; Object.assign(form.value, res.data);
}) dialog.visible = true;
dialog.title = "修改岗位";
} }
/** 提交按钮 */ /** 提交按钮 */
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();
} }
}); });
} }
@ -224,7 +221,7 @@ 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("删除成功");
} }
/** 导出按钮操作 */ /** 导出按钮操作 */

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,8 +82,8 @@ 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,
@ -115,29 +107,29 @@ const handleClose = () => {
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("取消授权成功");
} }
/** 批量取消授权按钮操作 */ /** 批量取消授权按钮操作 */
@ -146,7 +138,7 @@ const cancelAuthUserAll = async () => {
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("取消授权成功");
} }

View File

@ -1,7 +1,8 @@
<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-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px"> <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px">
<el-form-item label="角色名称" prop="roleName"> <el-form-item label="角色名称" prop="roleName">
<el-input v-model="queryParams.roleName" placeholder="请输入角色名称" clearable style="width: 240px" @keyup.enter="handleQuery" /> <el-input v-model="queryParams.roleName" placeholder="请输入角色名称" clearable style="width: 240px" @keyup.enter="handleQuery" />
@ -31,10 +32,11 @@
<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">
@ -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;
@ -228,11 +228,11 @@ const dataScopeOptions = ref([
{ 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,
@ -249,7 +249,7 @@ const initForm: RoleForm = {
} }
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,
@ -265,13 +265,11 @@ const data = reactive<PageData<RoleForm, RoleQuery>>({
}) })
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: ''
}); });
/** /**
* 查询角色列表 * 查询角色列表
*/ */
@ -295,7 +293,7 @@ const handleQuery = () => {
/** 重置 */ /** 重置 */
const resetQuery = () => { const resetQuery = () => {
dateRange.value = ['', ''] dateRange.value = ['', '']
queryFormRef.value.resetFields(); queryFormRef.value?.resetFields();
handleQuery(); handleQuery();
} }
/**删除按钮操作 */ /**删除按钮操作 */
@ -343,56 +341,51 @@ const getMenuTreeselect = async () => {
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) {
checkedKeys?.unshift.apply(checkedKeys, halfCheckedKeys);
}
return checkedKeys 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;
dialog.title = "添加角色";
nextTick(() => {
reset(); reset();
getMenuTreeselect(); getMenuTreeselect();
}) dialog.visible = true;
dialog.title = "添加角色";
} }
/** 修改角色 */ /** 修改角色 */
const handleUpdate = async (row?: RoleVO) => { const handleUpdate = async (row?: RoleVO) => {
const roleId = row?.roleId || ids.value[0]
const roleMenu = getRoleMenuTreeselect(roleId)
const { data } = await getRole(roleId);
dialog.visible = true;
dialog.title = "修改角色";
nextTick(() => {
reset(); reset();
const roleId = row?.roleId || ids.value[0]
const { data } = await getRole(roleId);
Object.assign(form.value, data); Object.assign(form.value, data);
form.value.roleSort = Number(form.value.roleSort); form.value.roleSort = Number(form.value.roleSort);
nextTick(async () => { const res = await getRoleMenuTreeselect(roleId);
const res = await roleMenu; dialog.title = "修改角色";
let checkedKeys = res.checkedKeys; dialog.visible = true;
checkedKeys.forEach((v) => { res.checkedKeys.forEach((v) => {
nextTick(() => { nextTick(() => {
menuRef.value.setChecked(v, true, false); menuRef.value?.setChecked(v, true, false);
})
})
}) })
}) })
} }
/** 根据角色ID查询菜单树结构 */ /** 根据角色ID查询菜单树结构 */
const getRoleMenuTreeselect = (roleId: string | number) => { const getRoleMenuTreeselect = (roleId: string | number) => {
@ -408,25 +401,29 @@ const getRoleDeptTreeSelect = async (roleId: string | number) => {
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++) {
if (menuRef.value) {
menuRef.value.store.nodesMap[treeList[i].id].expanded = value; menuRef.value.store.nodesMap[treeList[i].id].expanded = value;
} }
}
} else if (type == "dept") { } else if (type == "dept") {
let treeList = deptOptions.value; let treeList = deptOptions.value;
for (let i = 0; i < treeList.length; i++) { for (let i = 0; i < treeList.length; i++) {
if (deptRef.value) {
deptRef.value.store.nodesMap[treeList[i].id].expanded = 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 : []);
} }
} }
/** 树权限(父子联动) */ /** 树权限(父子联动) */
@ -438,17 +435,19 @@ const handleCheckedTreeConnect = (value: any, type: string) => {
} }
} }
/** 所有菜单节点数据 */ /** 所有菜单节点数据 */
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) {
checkedKeys?.unshift.apply(checkedKeys, halfCheckedKeys);
}
return checkedKeys; 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);
@ -466,23 +465,18 @@ const cancel = () => {
/** 选择角色权限范围触发 */ /** 选择角色权限范围触发 */
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);
}
})
}) })
} }
/** 提交按钮(数据权限) */ /** 提交按钮(数据权限) */
@ -497,8 +491,8 @@ const submitDataScope = async () => {
} }
/** 取消按钮(数据权限)*/ /** 取消按钮(数据权限)*/
const cancelDataScope = () => { const cancelDataScope = () => {
dataScopeRef.value.resetFields(); dataScopeRef.value?.resetFields();
form.value = {...initForm}; form.value = { ...initForm };
openDataScope.value = false; openDataScope.value = false;
} }

View File

@ -47,8 +47,6 @@
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({
@ -73,8 +71,8 @@ const queryParams = reactive<UserQuery>({
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;
@ -86,7 +84,8 @@ const show = () => {
* 选择行 * 选择行
*/ */
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[]) => {
@ -106,7 +105,7 @@ const handleQuery = () => {
} }
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
queryFormRef.value.resetFields(); queryFormRef.value?.resetFields();
getList(); getList();
} }

View File

@ -1,7 +1,8 @@
<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-card shadow="hover">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px"> <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
<el-form-item label="租户编号" prop="tenantId"> <el-form-item label="租户编号" prop="tenantId">
<el-input v-model="queryParams.tenantId" placeholder="请输入租户编号" clearable @keyup.enter="handleQuery" /> <el-input v-model="queryParams.tenantId" placeholder="请输入租户编号" clearable @keyup.enter="handleQuery" />
@ -20,10 +21,11 @@
<el-button icon="Refresh" @click="resetQuery">重置</el-button> <el-button icon="Refresh" @click="resetQuery">重置</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" 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,8 +158,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 tenantFormRef = ref(ElForm); const tenantFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({ const dialog = reactive<DialogOption>({
visible: false, visible: false,
@ -185,7 +185,7 @@ const initFormData: TenantForm = {
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,
@ -250,8 +250,8 @@ const cancel = () => {
// //
const reset = () => { const reset = () => {
form.value = {...initFormData}; form.value = { ...initFormData };
tenantFormRef.value.resetFields(); tenantFormRef.value?.resetFields();
} }
/** 搜索按钮操作 */ /** 搜索按钮操作 */
@ -262,7 +262,7 @@ const handleQuery = () => {
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
queryFormRef.value.resetFields(); queryFormRef.value?.resetFields();
handleQuery(); handleQuery();
} }
@ -275,32 +275,26 @@ const handleSelectionChange = (selection: TenantVO[]) => {
/** 新增按钮操作 */ /** 新增按钮操作 */
const handleAdd = () => { const handleAdd = () => {
dialog.visible = true;
dialog.title = "添加租户";
nextTick(() => {
reset(); reset();
getTenantPackage(); getTenantPackage();
}) dialog.visible = true;
dialog.title = "添加租户";
} }
/** 修改按钮操作 */ /** 修改按钮操作 */
const handleUpdate = (row?: TenantVO) => { const handleUpdate = async (row?: TenantVO) => {
loading.value = true;
dialog.visible = true;
dialog.title = "修改租户";
nextTick(async () => {
reset(); reset();
getTenantPackage(); await getTenantPackage();
const _id = row?.id || ids.value[0]; const _id = row?.id || ids.value[0];
const res = await getTenant(_id); const res = await getTenant(_id);
loading.value = false;
Object.assign(form.value, res.data) Object.assign(form.value, res.data)
}) dialog.visible = true;
dialog.title = "修改租户";
} }
/** 提交按钮 */ /** 提交按钮 */
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) {
@ -310,7 +304,7 @@ const submitForm = () => {
} }
proxy?.$modal.msgSuccess("操作成功"); proxy?.$modal.msgSuccess("操作成功");
dialog.visible = false; dialog.visible = false;
getList(); await getList();
} }
}); });
} }
@ -321,7 +315,7 @@ const handleDelete = async (row?: TenantVO) => {
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("删除成功");
@ -333,9 +327,9 @@ const handleSyncTenantPackage = async (row: TenantVO) => {
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;
} }
} }

View File

@ -1,7 +1,8 @@
<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-card shadow="hover">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px"> <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
<el-form-item label="套餐名称" prop="packageName"> <el-form-item label="套餐名称" prop="packageName">
<el-input v-model="queryParams.packageName" placeholder="请输入套餐名称" clearable @keyup.enter="handleQuery" /> <el-input v-model="queryParams.packageName" placeholder="请输入套餐名称" clearable @keyup.enter="handleQuery" />
@ -11,27 +12,28 @@
<el-button icon="Refresh" @click="resetQuery">重置</el-button> <el-button icon="Refresh" @click="resetQuery">重置</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" 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,29 +127,29 @@ 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" }],
@ -153,27 +160,29 @@ const data = reactive<PageData<TenantPkgForm, TenantPkgQuery>>({
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) {
checkedKeys?.unshift.apply(checkedKeys, halfCheckedKeys);
}
return checkedKeys; 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 () => {
@ -182,115 +191,107 @@ const getList = async () => {
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;
dialog.title = "添加租户套餐";
nextTick(() => {
reset(); reset();
getMenuTreeselect(); getMenuTreeselect();
}) dialog.visible = true;
} dialog.title = "添加租户套餐";
};
/** 修改按钮操作 */ /** 修改按钮操作 */
const handleUpdate = (row?: TenantPkgVO) => { const handleUpdate = async (row?: TenantPkgVO) => {
loading.value = true
dialog.visible = true;
dialog.title = "修改租户套餐";
nextTick(async () => {
reset(); reset();
const _packageId = row?.packageId || ids.value[0]; const _packageId = row?.packageId || ids.value[0];
const packageMenu = getPackageMenuTreeselect(_packageId);
const response = await getTenantPackage(_packageId); const response = await getTenantPackage(_packageId);
loading.value = false;
form.value = response.data; form.value = response.data;
nextTick(async () => { const res = await getPackageMenuTreeselect(_packageId);
const res = await packageMenu; dialog.visible = true;
let checkedKeys = res.data.checkedKeys dialog.title = "修改租户套餐";
checkedKeys.forEach((v) => { res.data.checkedKeys.forEach((v) => {
nextTick(() => { nextTick(() => {
menuTreeRef.value.setChecked(v, true ,false); 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();
@ -301,31 +302,31 @@ const submitForm = () => {
} }
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;
@ -71,15 +70,16 @@ 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[]) => {
@ -98,30 +98,30 @@ const close = () => {
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,18 +16,25 @@
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-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px"> <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px">
<el-form-item label="用户名称" prop="userName"> <el-form-item label="用户名称" prop="userName">
<el-input v-model="queryParams.userName" placeholder="请输入用户名称" clearable style="width: 240px" @keyup.enter="handleQuery" /> <el-input v-model="queryParams.userName" placeholder="请输入用户名称" clearable style="width: 240px" @keyup.enter="handleQuery" />
</el-form-item> </el-form-item>
<el-form-item label="手机号码" prop="phonenumber"> <el-form-item label="手机号码" prop="phonenumber">
<el-input v-model="queryParams.phonenumber" placeholder="请输入手机号码" clearable style="width: 240px" @keyup.enter="handleQuery" /> <el-input
v-model="queryParams.phonenumber"
placeholder="请输入手机号码"
clearable
style="width: 240px"
@keyup.enter="handleQuery"
/>
</el-form-item> </el-form-item>
<el-form-item label="状态" prop="status"> <el-form-item label="状态" prop="status">
@ -50,6 +57,7 @@
<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">
@ -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');
@ -337,7 +335,7 @@ const upload = reactive<ImportOption>({
// //
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"
}) })
@ -353,10 +351,11 @@ const columns = ref<FieldOption[]>([
]) ])
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,
@ -405,7 +404,7 @@ const filterNode = (value: string, data: any) => {
} }
/** 根据名称筛选部门树 */ /** 根据名称筛选部门树 */
watchEffect( watchEffect(
() => {deptTreeRef.value.filter(deptName.value);}, () => { deptTreeRef.value?.filter(deptName.value); },
{ {
flush: 'post' // watchEffectDOMDOM flush: 'post' // watchEffectDOMDOM
} }
@ -413,14 +412,14 @@ watchEffect(
/** 查询部门下拉树结构 */ /** 查询部门下拉树结构 */
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;
@ -440,11 +439,11 @@ const handleQuery = () => {
} }
/** 重置按钮操作 */ /** 重置按钮操作 */
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();
} }
@ -453,7 +452,7 @@ 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("删除成功");
} }
@ -464,7 +463,7 @@ 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";
@ -486,7 +485,7 @@ const handleResetPwd = async (row: UserVO) => {
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);
} }
} }
@ -523,14 +522,14 @@ const handleFileUploadProgress = () => {
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();
} }
/** 初始化部门数据 */ /** 初始化部门数据 */
@ -546,51 +545,46 @@ const initTreeData = async () => {
/** 重置操作表单 */ /** 重置操作表单 */
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 () => {
reset();
const { data } = await api.getUser();
dialog.visible = true; dialog.visible = true;
dialog.title = "新增用户"; dialog.title = "新增用户";
nextTick(async () => {
reset();
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) => {
reset();
const userId = row?.userId || ids.value[0]
const { data } = await api.getUser(userId)
dialog.visible = true; dialog.visible = true;
dialog.title = "修改用户"; dialog.title = "修改用户";
nextTick(async () => {
reset();
await initTreeData(); await initTreeData();
const userId = row?.userId || ids.value[0]
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();
@ -611,8 +605,8 @@ const closeDialog = () => {
* 重置表单 * 重置表单
*/ */
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';

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,20 +17,15 @@
</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) => {
@ -42,15 +37,24 @@ const equalToPassword = (rule: any, value: string, callback: any) => {
}; };
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("修改成功");
} }
}); });

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;
} }
@ -89,7 +90,7 @@ const options = reactive<Options>({
autoCropHeight: 200, autoCropHeight: 200,
fixedBox: true, fixedBox: true,
outputType: "png", outputType: "png",
fileName: '', fileName: "",
previews: {}, previews: {},
visible: false visible: false
}); });
@ -97,26 +98,27 @@ const options = reactive<Options>({
/** 编辑头像 */ /** 编辑头像 */
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) {
@ -129,7 +131,7 @@ const beforeUpload = (file: any) => {
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) => {
@ -138,20 +140,20 @@ const uploadImg = async () => {
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,34 +24,36 @@
<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>({
const rules = ref<FormRules>({
nickName: [{ required: true, message: "用户昵称不能为空", trigger: "blur" }], nickName: [{ required: true, message: "用户昵称不能为空", trigger: "blur" }],
email: [{ required: true, message: "邮箱地址不能为空", trigger: "blur" }, { type: "email", message: "请输入正确的邮箱地址", trigger: ["blur", "change"] }], email: [{ required: true, message: "邮箱地址不能为空", trigger: "blur" }, {
phonenumber: [{ required: true, message: "手机号码不能为空", trigger: "blur" }, { pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: "请输入正确的手机号码", trigger: "blur" }], type: "email",
message: "请输入正确的邮箱地址",
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("修改成功");
} }
}); });

View File

@ -31,15 +31,10 @@
</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)

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

@ -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,8 +47,8 @@ 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,
@ -66,7 +64,7 @@ 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';
@ -76,7 +74,8 @@ const show = (dataName: string) => {
} }
/** 单击选择行 */ /** 单击选择行 */
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[]) => {
@ -95,7 +94,7 @@ const handleQuery = () => {
} }
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
queryFormRef.value.resetFields(); queryFormRef.value?.resetFields();
handleQuery(); handleQuery();
} }
/** 导入按钮操作 */ /** 导入按钮操作 */

View File

@ -1,7 +1,8 @@
<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-card shadow="hover">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px"> <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
<el-form-item label="数据源" prop="dataName"> <el-form-item label="数据源" prop="dataName">
<el-select v-model="queryParams.dataName" filterable clearable placeholder="请选择/输入数据源名称" style="width: 200px"> <el-select v-model="queryParams.dataName" filterable clearable placeholder="请选择/输入数据源名称" style="width: 200px">
@ -30,10 +31,11 @@
<el-button icon="Refresh" @click="resetQuery">重置</el-button> <el-button icon="Refresh" @click="resetQuery">重置</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" 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,8 +132,8 @@ 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,
@ -143,7 +143,7 @@ const queryParams = ref<TableQuery>({
dataName: "" dataName: ""
}) })
const preview = ref <any>({ const preview = ref<any>({
data: {}, data: {},
activeName: 'domain.java' activeName: 'domain.java'
}) })
@ -158,7 +158,7 @@ onActivated(() => {
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();
} }
}) })
@ -205,12 +205,12 @@ const handleSynchDb = async (row: TableVO) => {
} }
/** 打开导入表弹窗 */ /** 打开导入表弹窗 */
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();
} }
/** 预览按钮 */ /** 预览按钮 */
@ -240,7 +240,7 @@ 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('删除成功');
} }