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'
# xxl-job 控制台地址
VITE_APP_XXL_JOB_ADMIN = 'http://localhost:9100/xxl-job-admin'
# powerjob 控制台地址
VITE_APP_POWERJOB_ADMIN = 'http://localhost:7700/'
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_XXL_JOB_ADMIN = '/xxl-job-admin'
# powerjob 控制台地址
VITE_APP_POWERJOB_ADMIN = '/powerjob'
# 生产环境
VITE_APP_BASE_API = '/prod-api'
@ -20,3 +20,9 @@ VITE_APP_BASE_API = '/prod-api'
VITE_BUILD_COMPRESS = gzip
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": {
"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,
"useRoute": true,
"EffectScope": true,

View File

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

1
.gitignore vendored
View File

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

View File

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

View File

@ -3,22 +3,24 @@ import { AxiosPromise } from 'axios';
import { LoginData, LoginResult, VerifyCodeResult, TenantInfo } from './types';
import { UserInfo } from '@/api/system/user/types';
// pc端固定客户端授权id
const clientId = import.meta.env.VITE_APP_CLIENT_ID;
/**
* @param data {LoginData}
* @returns
*/
export function login(data: LoginData): AxiosPromise<LoginResult> {
const params = {
tenantId: data.tenantId,
username: data.username.trim(),
password: data.password,
code: data.code,
uuid: data.uuid
...data,
clientId: data.clientId || clientId,
grantType: data.grantType || 'password'
};
return request({
url: '/auth/login',
headers: {
isToken: false
isToken: false,
isEncrypt: true
},
method: 'post',
data: params
@ -52,7 +54,7 @@ export function logout() {
*/
export function getCodeImg(): AxiosPromise<VerifyCodeResult> {
return request({
url: '/code',
url: '/auth/code',
headers: {
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> {
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
*/
export function listUser(query: UserQuery): AxiosPromise<UserVO[]> {
export const listUser = (query: UserQuery): AxiosPromise<UserVO[]> => {
return request({
url: '/system/user/list',
method: 'get',
params: query
});
}
};
/**
*
* @param userId
*/
export function getUser(userId?: string | number): AxiosPromise<UserInfoVO> {
export const getUser = (userId?: string | number): AxiosPromise<UserInfoVO> => {
return request({
url: '/system/user/' + parseStrEmpty(userId),
method: 'get'
});
}
};
/**
*
*/
export function addUser(data: UserForm) {
export const addUser = (data: UserForm) => {
return request({
url: '/system/user',
method: 'post',
data: data
});
}
};
/**
*
*/
export function updateUser(data: UserForm) {
export const updateUser = (data: UserForm) => {
return request({
url: '/system/user',
method: 'put',
data: data
});
}
};
/**
*
* @param userId ID
*/
export function delUser(userId: Array<string | number> | string | number) {
export const delUser = (userId: Array<string | number> | string | number) => {
return request({
url: '/system/user/' + userId,
method: 'delete'
});
}
};
/**
*
* @param userId ID
* @param password
*/
export function resetUserPwd(userId: string | number, password: string) {
export const resetUserPwd = (userId: string | number, password: string) => {
const data = {
userId,
password
@ -76,14 +76,14 @@ export function resetUserPwd(userId: string | number, password: string) {
method: 'put',
data: data
});
}
};
/**
*
* @param userId ID
* @param status
*/
export function changeUserStatus(userId: number | string, status: string) {
export const changeUserStatus = (userId: number | string, status: string) => {
const data = {
userId,
status
@ -93,36 +93,36 @@ export function changeUserStatus(userId: number | string, status: string) {
method: 'put',
data: data
});
}
};
/**
*
*/
export function getUserProfile(): AxiosPromise<UserInfoVO> {
export const getUserProfile = (): AxiosPromise<UserInfoVO> => {
return request({
url: '/system/user/profile',
method: 'get'
});
}
};
/**
*
* @param data
*/
export function updateUserProfile(data: UserForm) {
export const updateUserProfile = (data: UserForm) => {
return request({
url: '/system/user/profile',
method: 'put',
data: data
});
}
};
/**
*
* @param oldPassword
* @param newPassword
*/
export function updateUserPwd(oldPassword: string, newPassword: string) {
export const updateUserPwd = (oldPassword: string, newPassword: string) => {
const data = {
oldPassword,
newPassword
@ -132,49 +132,66 @@ export function updateUserPwd(oldPassword: string, newPassword: string) {
method: 'put',
params: data
});
}
};
/**
*
* @param data
*/
export function uploadAvatar(data: FormData) {
export const uploadAvatar = (data: FormData) => {
return request({
url: '/system/user/profile/avatar',
method: 'post',
data: data
});
}
};
/**
*
* @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({
url: '/system/user/authRole/' + userId,
method: 'get'
});
}
};
/**
*
* @param data ID
*/
export function updateAuthRole(data: { userId: string; roleIds: string }) {
export const updateAuthRole = (data: { userId: string; roleIds: string }) => {
return request({
url: '/system/user/authRole',
method: 'put',
params: data
});
}
};
/**
*
*/
export function deptTreeSelect(): AxiosPromise<DeptVO[]> {
export const deptTreeSelect = (): AxiosPromise<DeptVO[]> => {
return request({
url: '/system/user/deptTree',
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 {
tenantId: string;
username: string;
password: string;
tenantId?: string;
username?: string;
password?: string;
rememberMe?: boolean;
socialCode?: string;
socialState?: string;
source?: string;
code?: string;
uuid?: string;
clientId: string;
grantType: string;
}
/**
*
*/
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;
}
html.dark .svg-icon, html.dark svg {
fill: var(--el-text-color-regular);
}
#app {
height: 100%;
}
@ -137,6 +141,7 @@ aside {
border: 1px solid var(--el-border-color-light);
background-color: var(--el-bg-color-overlay);
padding: 0.75rem;
transition: all ease 0.3s;
&:hover {
box-shadow: 0 2px 12px #0000001a;

View File

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

View File

@ -84,7 +84,7 @@
.sub-menu-title-noDropdown,
.el-sub-menu__title {
&: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 {
&:hover {
// 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
$blue: #324157;
$light-blue: #3a71a8;
@ -9,32 +46,23 @@ $yellow: #fec171;
$panGreen: #30b08f;
// 默认菜单主题风格
$base-menu-color: #bfcbd9;
$base-menu-color-active: #f4f4f5;
$base-menu-background: #304156;
$base-menu-color: var(--menuColor);
$base-menu-hover: var(--menuHover);
$base-menu-color-active: var(--menuActiveText);
$base-menu-background: var(--menuBg);
$base-logo-title-color: #ffffff;
$base-menu-light-color: rgba(0, 0, 0, 0.7);
$base-menu-light-background: #ffffff;
$base-logo-light-title-color: #001529;
$base-sub-menu-background: #1f2d3d;
$base-sub-menu-hover: #001528;
// 自定义暗色菜单风格
/**
$base-menu-color:hsla(0,0%,100%,.65);
$base-menu-color-active:#fff;
$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;
*/
$base-sub-menu-background: var(--subMenuBg);
$base-sub-menu-hover: var(--subMenuHover);
$base-sub-menu-title-hover: var(--subMenuTitleHover);
// 表单头背景色和标题颜色
$fixed-header-bg: var(--fixedHeaderBg);
$table-header-bg: var(--tableHeaderBg);
$table-header-text-color: var(--tableHeaderTextColor);
$--color-primary: #409eff;
$--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>
<script setup lang="ts">
import { PropType } from 'vue';
import { propTypes } from '@/utils/propTypes';
const props = defineProps({
@ -36,10 +36,7 @@ const props = defineProps({
//
value: [Number, String, Array] as PropType<number | string | Array<number | string>>,
// value
showValue: {
type: Boolean as PropType<boolean>,
default: true,
},
showValue: propTypes.bool.def(true),
});
const values = computed(() => {

View File

@ -29,45 +29,28 @@
<script setup lang="ts">
import { QuillEditor, Quill } from '@vueup/vue-quill';
import '@vueup/vue-quill/dist/vue-quill.snow.css';
import { getToken } from "@/utils/auth";
import { ComponentInternalInstance } from "vue";
import { propTypes } from '@/utils/propTypes';
import { globalHeaders } from "@/utils/request";
const props = defineProps({
/* 编辑器的内容 */
modelValue: {
type: String,
},
modelValue: propTypes.string,
/* 高度 */
height: {
type: Number,
default: null,
},
height: propTypes.number.def(400),
/* 最小高度 */
minHeight: {
type: Number,
default: null,
},
minHeight: propTypes.number.def(400),
/* 只读 */
readOnly: {
type: Boolean,
default: false,
},
readOnly: propTypes.bool.def(false),
/* 上传文件大小限制(MB) */
fileSize: {
type: Number,
default: 5,
},
fileSize: propTypes.number.def(5),
/* 类型base64格式、url格式 */
type: {
type: String,
default: "url",
}
type: propTypes.string.def('url')
});
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const upload = reactive<UploadOption>({
headers: { Authorization: "Bearer " + getToken() },
headers: globalHeaders,
url: import.meta.env.VITE_APP_BASE_API + '/resource/oss/upload'
})
const myQuillEditor = ref();
@ -82,7 +65,7 @@ const options = ref({
container: [
["bold", "italic", "underline", "strike"], // 线 线
["blockquote", "code-block"], //
[{ list: "ordered" }, { list: "bullet"} ], //
[{ list: "ordered" }, { list: "bullet" }], //
[{ indent: "-1" }, { indent: "+1" }], //
[{ size: ["small", false, "large", "huge"] }], //
[{ header: [1, 2, 3, 4, 5, 6, false] }], //
@ -166,16 +149,20 @@ const handleUploadError = (err: any) => {
</script>
<style>
.editor, .ql-toolbar {
.editor,
.ql-toolbar {
white-space: pre-wrap !important;
line-height: normal !important;
}
.quill-img {
display: none;
}
.ql-snow .ql-tooltip[data-mode="link"]::before {
content: "请输入链接地址:";
}
.ql-snow .ql-tooltip.ql-editing a.ql-action::after {
border-right: 0;
content: "保存";
@ -190,14 +177,17 @@ const handleUploadError = (err: any) => {
.ql-snow .ql-picker.ql-size .ql-picker-item::before {
content: "14px";
}
.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 {
content: "10px";
}
.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 {
content: "18px";
}
.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 {
content: "32px";
@ -207,26 +197,32 @@ const handleUploadError = (err: any) => {
.ql-snow .ql-picker.ql-header .ql-picker-item::before {
content: "文本";
}
.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 {
content: "标题1";
}
.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 {
content: "标题2";
}
.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 {
content: "标题3";
}
.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 {
content: "标题4";
}
.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 {
content: "标题5";
}
.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 {
content: "标题6";
@ -236,10 +232,12 @@ const handleUploadError = (err: any) => {
.ql-snow .ql-picker.ql-font .ql-picker-item::before {
content: "标准字体";
}
.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 {
content: "衬线字体";
}
.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 {
content: "等宽字体";

View File

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

View File

@ -1,6 +1,6 @@
<template>
<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
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>
<script setup lang="ts">
import { propTypes } from '@/utils/propTypes';
defineProps({
isActive: {
type: Boolean,
default: false
}
isActive: propTypes.bool.def(false)
})
const emit = defineEmits(['toggleClick'])

View File

@ -17,12 +17,12 @@
</div>
</template>
<script setup lang="ts">
import Fuse from 'fuse.js'
import { getNormalPath } from '@/utils/ruoyi'
import { isHttp } from '@/utils/validate'
import usePermissionStore from '@/store/modules/permission'
import { RouteOption } from 'vue-router'
<script setup lang="ts" name="HeaderSearch">
import Fuse from 'fuse.js';
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;
@ -34,7 +34,7 @@ const options = ref<any>([]);
const searchPool = ref<Router>([]);
const show = ref(false);
const fuse = ref();
const headerSearchSelectRef = ref(ElSelect);
const headerSearchSelectRef = ref<ElSelectInstance>();
const router = useRouter();
const routes = computed(() => usePermissionStore().routes);
@ -123,9 +123,9 @@ onMounted(() => {
searchPool.value = generateRoutes(routes.value);
})
watchEffect(() => {
searchPool.value = generateRoutes(routes.value)
})
// watchEffect(() => {
// searchPool.value = generateRoutes(routes.value)
// })
watch(show, (value) => {
if (value) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -29,7 +29,6 @@
</template>
<script setup lang="ts">
import { ElTreeSelect } from 'element-plus'
const props = defineProps({
/* 配置项 */
@ -68,7 +67,7 @@ const props = defineProps({
})
const selectTree = ref(ElTreeSelect);
const selectTree = ref<ElTreeSelectInstance>();
const emit = defineEmits(['update:value']);
@ -81,14 +80,14 @@ const valueId = computed({
const valueTitle = ref('');
const defaultExpandedKey = ref<any[]>([]);
function initHandle() {
const initHandle = () => {
nextTick(() => {
const selectedValue = valueId.value;
if(selectedValue !== null && typeof (selectedValue) !== 'undefined') {
const node = selectTree.value.getNode(selectedValue)
if (selectedValue !== null && typeof (selectedValue) !== 'undefined') {
const node = selectTree.value?.getNode(selectedValue)
if (node) {
valueTitle.value = node.data[props.objMap.label]
selectTree.value.setCurrentKey(selectedValue) //
selectTree.value?.setCurrentKey(selectedValue) //
defaultExpandedKey.value = [selectedValue] //
}
} else {
@ -96,27 +95,27 @@ function initHandle() {
}
})
}
function handleNodeClick(node: any) {
const handleNodeClick = (node: any) => {
valueTitle.value = node[props.objMap.label]
valueId.value = node[props.objMap.value];
defaultExpandedKey.value = [];
selectTree.value.blur()
selectTree.value?.blur()
selectFilterData('')
}
function selectFilterData(val: any) {
selectTree.value.filter(val)
const selectFilterData = (val: any) => {
selectTree.value?.filter(val)
}
function filterNode(value: any, data: any) {
const filterNode = (value: any, data: any) => {
if (!value) return true
return data[props.objMap['label']].indexOf(value) !== -1
}
function clearHandle() {
const clearHandle = () => {
valueTitle.value = ''
valueId.value = ''
defaultExpandedKey.value = [];
clearSelected()
}
function clearSelected() {
const clearSelected = () => {
const allNode = document.querySelectorAll('#tree-option .el-tree-node')
allNode.forEach((element) => element.classList.remove('is-current'))
}
@ -132,6 +131,7 @@ watch(valueId, () => {
<style lang="scss" scoped>
@import "@/assets/styles/variables.module.scss";
.el-scrollbar .el-scrollbar__view .el-select-dropdown__item {
padding: 0;
background-color: #fff;

View File

@ -5,11 +5,10 @@
</template>
<script setup lang="ts">
import { propTypes } from '@/utils/propTypes';
const props = defineProps({
src: {
type: String,
required: true
}
src: propTypes.string.isRequired
})
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>
</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">
<ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" />
</el-tooltip>
@ -68,17 +73,18 @@
</template>
<script setup lang="ts">
import useAppStore from '@/store/modules/app'
import useUserStore from '@/store/modules/user'
import useSettingsStore from '@/store/modules/settings'
import SearchMenu from './topBar/search.vue';
import useAppStore from '@/store/modules/app';
import useUserStore from '@/store/modules/user';
import useSettingsStore from '@/store/modules/settings';
import { getTenantList } from "@/api/login";
import { dynamicClear, dynamicTenant } from "@/api/system/tenant";
import { ComponentInternalInstance } from "vue";
import { TenantVO } from "@/api/types";
const appStore = useAppStore()
const userStore = useUserStore()
const settingsStore = useSettingsStore()
const appStore = useAppStore();
const userStore = useUserStore();
const settingsStore = useSettingsStore();
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@ -89,6 +95,12 @@ const tenantList = ref<TenantVO[]>([]);
const dynamic = ref(false);
//
const tenantEnabled = ref(true);
//
const searchMenuRef = ref<InstanceType<typeof SearchMenu>>();
const openSearchMenu = () => {
searchMenuRef.value?.openSearch();
}
//
const dynamicTenantEvent = async (tenantId: string) => {
@ -104,7 +116,7 @@ const dynamicClearEvent = async () => {
await dynamicClear();
dynamic.value = false;
proxy?.$tab.closeAllPage();
proxy?.$router.push('/')
proxy?.$router.push('/');
}
/** 租户列表 */
@ -121,7 +133,7 @@ defineExpose({
})
const toggleSideBar = () => {
appStore.toggleSideBar(false)
appStore.toggleSideBar(false);
}
const logout = async () => {
@ -169,7 +181,7 @@ const handleCommand = (command: string) => {
height: 50px;
overflow: hidden;
position: relative;
background: #fff;
//background: #fff;
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
.hamburger-container {

View File

@ -1,8 +1,7 @@
<template>
<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>
</div>
<div class="setting-drawer-block-checbox">
<div class="setting-drawer-block-checbox-item" @click="handleTheme('theme-dark')">
<img src="@/assets/images/dark.svg" alt="dark" />
@ -35,6 +34,13 @@
<el-color-picker v-model="theme" :predefine="predefineColors" @change="themeChange" />
</span>
</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 />
<h3 class="drawer-title">系统布局配置</h3>
@ -102,7 +108,15 @@ const sideTheme = ref(settingsStore.sideTheme);
const storeSettings = computed(() => settingsStore);
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({
get: () => storeSettings.value.topNav,
set: (val) => {
@ -234,7 +248,6 @@ defineExpose({
}
.drawer-item {
color: rgba(0, 0, 0, 0.65);
padding: 12px 0;
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">
import useTagsViewStore from '@/store/modules/tagsView'
import { ElScrollbar } from 'element-plus';
import { TagView } from 'vue-router'
const tagAndTagSpacing = ref(4);
const scrollContainerRef = ref(ElScrollbar)
const scrollWrapper = computed(() => scrollContainerRef.value.$refs.wrapRef);
const scrollContainerRef = ref<ElScrollbarInstance>()
const scrollWrapper = computed(() => scrollContainerRef.value?.$refs.wrapRef as any);
onMounted(() => {
scrollWrapper.value.addEventListener('scroll', emitScroll, true)
scrollWrapper.value?.addEventListener('scroll', emitScroll, true)
})
onBeforeUnmount(() => {
scrollWrapper.value.removeEventListener('scroll', emitScroll)
scrollWrapper.value?.removeEventListener('scroll', emitScroll)
})
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;
$scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4
}
@ -34,7 +33,7 @@ const tagsViewStore = useTagsViewStore()
const visitedViews = computed(() => tagsViewStore.visitedViews);
const moveToTarget = (currentTag: TagView) => {
const $container = scrollContainerRef.value.$el
const $container = scrollContainerRef.value?.$el
const $containerWidth = $container.offsetWidth
const $scrollWrapper = scrollWrapper.value;
@ -96,7 +95,7 @@ defineExpose({
bottom: 0px;
}
:deep(.el-scrollbar__wrap) {
height: 39px;
height: 49px;
}
}
</style>

View File

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

View File

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

View File

@ -1,9 +1,9 @@
import axios from 'axios';
import FileSaver from 'file-saver';
import { getToken } from '@/utils/auth';
import errorCode from '@/utils/errorCode';
import { blobValidate } from '@/utils/ruoyi';
import { LoadingInstance } from 'element-plus/es/components/loading/src/loading';
import { globalHeaders } from "@/utils/request";
const baseURL = import.meta.env.VITE_APP_BASE_API;
let downloadLoadingInstance: LoadingInstance;
@ -16,7 +16,7 @@ export default {
method: 'get',
url: url,
responseType: 'blob',
headers: { Authorization: 'Bearer ' + getToken() }
headers: globalHeaders,
});
const isBlob = blobValidate(res.data);
if (isBlob) {
@ -34,14 +34,13 @@ export default {
},
async zip(url: string, name: string) {
url = baseURL + url;
downloadLoadingInstance = ElLoading.service({ text: '正在下载数据,请稍候', background: 'rgba(0, 0, 0, 0.7)' });
try {
const res = await axios({
method: 'get',
url: url,
responseType: 'blob',
headers: {
Authorization: 'Bearer ' + getToken(),
datasource: localStorage.getItem('dataName')
}
headers: globalHeaders,
});
const isBlob = blobValidate(res.data);
if (isBlob) {
@ -50,6 +49,12 @@ export default {
} else {
this.printErrMsg(res.data);
}
downloadLoadingInstance.close();
} catch (r) {
console.error(r);
ElMessage.error('下载文件出现错误,请联系管理员!');
downloadLoadingInstance.close();
}
},
async printErrMsg(data: any) {
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';
let loadingInstance: LoadingInstance;
export default {
// 消息提示
msg(content: string) {
msg(content: any) {
ElMessage.info(content);
},
// 错误消息
msgError(content: string) {
msgError(content: any) {
ElMessage.error(content);
},
// 成功消息
msgSuccess(content: string) {
msgSuccess(content: any) {
ElMessage.success(content);
},
// 警告消息
msgWarning(content: string) {
msgWarning(content: any) {
ElMessage.warning(content);
},
// 弹出提示
alert(content: string) {
alert(content: any) {
ElMessageBox.alert(content, '系统提示');
},
// 错误提示
alertError(content: string) {
alertError(content: any) {
ElMessageBox.alert(content, '系统提示', { type: 'error' });
},
// 成功提示
alertSuccess(content: string, s: string, p: { dangerouslyUseHTMLString: boolean }) {
alertSuccess(content: any) {
ElMessageBox.alert(content, '系统提示', { type: 'success' });
},
// 警告提示
alertWarning(content: string) {
alertWarning(content: any) {
ElMessageBox.alert(content, '系统提示', { type: 'warning' });
},
// 通知提示
notify(content: string) {
notify(content: any) {
ElNotification.info(content);
},
// 错误通知
notifyError(content: string) {
notifyError(content: any) {
ElNotification.error(content);
},
// 成功通知
notifySuccess(content: string) {
notifySuccess(content: any) {
ElNotification.success(content);
},
// 警告通知
notifyWarning(content: string) {
notifyWarning(content: any) {
ElNotification.warning(content);
},
// 确认窗体
confirm(content: string): Promise<MessageBoxData> {
confirm(content: any): Promise<MessageBoxData> {
return ElMessageBox.confirm(content, '系统提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
@ -59,7 +59,7 @@ export default {
});
},
// 提交内容
prompt(content: string) {
prompt(content: any) {
return ElMessageBox.prompt(content, '系统提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',

View File

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

View File

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

View File

@ -23,8 +23,8 @@ export const useUserStore = defineStore('user', () => {
const [err, res] = await to(loginApi(userInfo));
if (res) {
const data = res.data;
setToken(data.token);
token.value = data.token;
setToken(data.access_token);
token.value = data.access_token;
return Promise.resolve();
}
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_CONTEXT_PATH: string;
VITE_APP_MONITRO_ADMIN: string;
VITE_APP_XXL_JOB_ADMIN: string;
VITE_APP_POWERJOB_ADMIN: string;
VITE_APP_ENV: string;
VITE_APP_RSA_PUBLIC_KEY: string;
VITE_APP_CLIENT_ID: string;
}
interface ImportMeta {
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 {
/** vue Instance */
declare type ComponentInternalInstance = ComponentInstance;
/**vue */
declare type PropType<T> = VuePropType<T>;
/**
*
*/
interface FieldOption {
declare interface FieldOption {
key: number;
label: string;
visible: boolean;
@ -12,7 +18,7 @@ declare global {
/**
*
*/
interface DialogOption {
declare interface DialogOption {
/**
*
*/
@ -23,7 +29,7 @@ declare global {
visible: boolean;
}
interface UploadOption {
declare interface UploadOption {
/** 设置上传的请求头部 */
headers: { [key: string]: any };
@ -34,7 +40,7 @@ declare global {
/**
*
*/
interface ImportOption extends UploadOption {
declare interface ImportOption extends UploadOption {
/** 是否显示弹出层 */
open: boolean;
/** 弹出层标题 */
@ -48,14 +54,14 @@ declare global {
/**
*
*/
interface DictDataOption {
declare interface DictDataOption {
label: string;
value: string;
elTagType?: ElTagType;
elTagClass?: string;
}
interface BaseEntity {
declare interface BaseEntity {
createBy?: any;
createDept?: any;
createTime?: string;
@ -68,15 +74,15 @@ declare global {
* T : 表单数据
* D : 查询参数
*/
interface PageData<T, D> {
declare interface PageData<T, D> {
form: T;
queryParams: D;
rules: FormRules;
rules: ElFormRules;
}
/**
*
*/
interface PageQuery {
declare interface PageQuery {
pageNum: number;
pageSize: number;
}

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

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

View File

@ -1,7 +1,7 @@
import { RouteRecordRaw } from 'vue-router';
declare module 'vue-router' {
type RouteOption = {
declare type RouteOption = {
hidden?: boolean;
permissions?: string[];
roles?: string[];
@ -16,15 +16,15 @@ declare module 'vue-router' {
query?: string;
} & RouteRecordRaw;
interface _RouteLocationBase {
declare interface _RouteLocationBase {
children?: RouteOption[];
}
interface RouteLocationOptions {
declare interface RouteLocationOptions {
fullPath?: string;
}
interface TagView extends Partial<_RouteLocationBase> {
declare interface TagView extends Partial<_RouteLocationBase> {
title?: string;
meta?: {
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 setToken = (token: string) => (tokenStorage.value = token);
export const setToken = (access_token: string) => (tokenStorage.value = access_token);
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';
// 密钥对生成 http://web.chacuo.net/netrsakeypair
const publicKey =
'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdH\n' + 'nzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==';
const publicKey = import.meta.env.VITE_APP_RSA_PUBLIC_KEY;
const privateKey =
'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY\n' +
'7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKN\n' +
'PuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gA\n' +
'kM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWow\n' +
'cSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99Ecv\n' +
'DQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthh\n' +
'YhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3\n' +
'UP8iWi1Qw0Y=';
// 前端不建议存放私钥 不建议解密数据 因为都是透明的意义不大
const privateKey = '**********';
// 加密
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 FileSaver from 'file-saver';
import { getLanguage } from '@/lang';
import { encryptBase64, encryptWithAes, generateAesKey } from '@/utils/crypto';
import { encrypt } from '@/utils/jsencrypt';
let downloadLoadingInstance: LoadingInstance;
// 是否显示重新登录
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['clientid'] = import.meta.env.VITE_APP_CLIENT_ID;
// 创建 axios 实例
const service = axios.create({
baseURL: import.meta.env.VITE_APP_BASE_API,
@ -29,6 +36,8 @@ service.interceptors.request.use(
const isToken = (config.headers || {}).isToken === false;
// 是否需要防止数据重复提交
const isRepeatSubmit = (config.headers || {}).repeatSubmit === false;
// 是否需要加密
const isEncrypt = (config.headers || {}).isEncrypt === 'true';
if (getToken() && !isToken) {
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
if (config.data instanceof FormData) {
delete config.headers['Content-Type'];

View File

@ -1,7 +1,8 @@
<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">
<div class="mb-[10px]" v-show="showSearch">
<el-card shadow="hover">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
<el-form-item label="部门id" prop="deptId">
<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-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="never">
<el-card shadow="hover">
<template #header>
<el-row :gutter="10" class="mb8">
<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-col>
<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 :span="1.5">
<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>
<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-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
@ -105,8 +103,6 @@
<script setup name="Demo" lang="ts">
import { listDemo, getDemo, delDemo, addDemo, updateDemo } from '@/api/demo/demo';
import { DemoVO, DemoQuery, DemoForm } from '@/api/demo/demo/types';
import { ComponentInternalInstance } from 'vue';
import { ElForm } from 'element-plus';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@ -119,8 +115,8 @@ const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const queryFormRef = ref(ElForm);
const demoFormRef = ref(ElForm);
const queryFormRef = ref<ElFormInstance>();
const demoFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({
visible: false,
@ -136,7 +132,7 @@ const initFormData: DemoForm = {
value: undefined,
}
const data = reactive<PageData<DemoForm, DemoQuery>>({
form: {...initFormData},
form: { ...initFormData },
queryParams: {
pageNum: 1,
pageSize: 10,
@ -187,8 +183,8 @@ const cancel = () => {
/** 表单重置 */
const reset = () => {
form.value = {...initFormData};
demoFormRef.value.resetFields();
form.value = { ...initFormData };
demoFormRef.value?.resetFields();
}
/** 搜索按钮操作 */
@ -199,7 +195,7 @@ const handleQuery = () => {
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields();
queryFormRef.value?.resetFields();
handleQuery();
}
@ -212,30 +208,24 @@ const handleSelectionChange = (selection: DemoVO[]) => {
/** 新增按钮操作 */
const handleAdd = () => {
reset();
dialog.visible = true;
dialog.title = "添加测试单";
nextTick(() => {
reset();
});
}
/** 修改按钮操作 */
const handleUpdate = (row?: DemoVO) => {
loading.value = true
dialog.visible = true;
dialog.title = "修改测试单";
nextTick(async () => {
const handleUpdate = async (row?: DemoVO) => {
reset();
const _id = row?.id || ids.value[0]
const res = await getDemo(_id);
loading.value = false;
Object.assign(form.value, res.data);
});
dialog.visible = true;
dialog.title = "修改测试单";
}
/** 提交按钮 */
const submitForm = () => {
demoFormRef.value.validate(async (valid: boolean) => {
demoFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
buttonLoading.value = true;
if (form.value.id) {

View File

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

View File

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

View File

@ -21,7 +21,7 @@
* 分布式锁 Lock4j 注解锁工具锁 多种多样<br />
* 分布式幂等 Lock4j 基于分布式锁实现<br />
* 分布式链路追踪 SkyWalking 支持链路追踪网格分析度量聚合可视化<br />
* 分布式任务调度 Xxl-Job 高性能 高可靠 易扩展<br />
* 分布式任务调度 PowerJob 高性能 高可靠 易扩展<br />
* 文件存储 Minio 本地存储<br />
* 文件存储 七牛阿里腾讯 云存储<br />
* 监控框架 SpringBoot-Admin 全方位服务监控<br />
@ -33,14 +33,14 @@
* 部署方式 Docker 容器编排 一键部署业务集群<br />
* 国际化 SpringMessage Spring标准国际化方案<br />
</p>
<p><b>当前版本:</b> <span>v5.0.0</span></p>
<p><b>当前版本:</b> <span>v5.1.0</span></p>
<p>
<el-tag type="danger">&yen;免费开源</el-tag>
</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://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
>
</p>
@ -78,14 +78,14 @@
* 分布式监控 PrometheusGrafana 全方位性能监控<br />
* 其余与 Vue 版本一致<br />
</p>
<p><b>当前版本:</b> <span>v2.0.0</span></p>
<p><b>当前版本:</b> <span>v2.1.0</span></p>
<p>
<el-tag type="danger">&yen;免费开源</el-tag>
</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://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
>
</p>

View File

@ -4,7 +4,7 @@
<h3 class="title">RuoYi-Vue-Plus多租户管理系统</h3>
<el-form-item prop="tenantId" v-if="tenantEnabled">
<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>
</el-select>
</el-form-item>
@ -27,6 +27,20 @@
</div>
</el-form-item>
<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-button :loading="loading" size="large" type="primary" style="width:100%;" @click.prevent="handleLogin">
<span v-if="!loading"> </span>
@ -46,26 +60,26 @@
<script setup lang="ts">
import { getCodeImg, getTenantList } from '@/api/login';
import { authBinding } from '@/api/system/social/auth';
import Cookies from 'js-cookie';
import { encrypt, decrypt } from '@/utils/jsencrypt';
import { useUserStore } from '@/store/modules/user';
import { LoginData, TenantVO } from '@/api/types';
import { FormRules } from 'element-plus';
import { to } from 'await-to-js';
import { HttpStatus } from "@/enums/RespEnum";
const userStore = useUserStore();
const router = useRouter();
const loginForm = ref<LoginData>({
tenantId: "000000",
tenantId: '000000',
username: 'admin',
password: 'admin123',
rememberMe: false,
code: '',
uuid: ''
});
} as LoginData);
const loginRules: FormRules = {
const loginRules: ElFormRules = {
tenantId: [{ required: true, trigger: "blur", message: "请输入您的租户编号" }],
username: [{ required: true, trigger: 'blur', message: '请输入您的账号' }],
password: [{ required: true, trigger: 'blur', message: '请输入您的密码' }],
@ -83,19 +97,19 @@ const tenantEnabled = ref(true);
//
const register = ref(false);
const redirect = ref(undefined);
const loginRef = ref(ElForm);
const loginRef = ref<ElFormInstance>();
//
const tenantList = ref<TenantVO[]>([]);
const handleLogin = () => {
loginRef.value.validate(async (valid:boolean, fields: any) => {
loginRef.value?.validate(async (valid: boolean, fields: any) => {
if (valid) {
loading.value = true;
// cookie
if (loginForm.value.rememberMe) {
Cookies.set("tenantId", loginForm.value.tenantId, { expires: 30 });
Cookies.set('username', loginForm.value.username, { expires: 30 });
Cookies.set('password', String(encrypt(loginForm.value.password)), { expires: 30 });
Cookies.set("tenantId", String(loginForm.value.tenantId), { expires: 30 });
Cookies.set('username', String(loginForm.value.username), { expires: 30 });
Cookies.set('password', String(loginForm.value.password), { expires: 30 });
Cookies.set('rememberMe', String(loginForm.value.rememberMe), { expires: 30 });
} else {
//
@ -105,7 +119,6 @@ const handleLogin = () => {
Cookies.remove('rememberMe');
}
// action
// prittier-ignore
const [err] = await to(userStore.login(loginForm.value));
if (!err) {
await router.push({ path: redirect.value || '/' });
@ -141,11 +154,11 @@ const getCookie = () => {
const password = Cookies.get('password');
const rememberMe = Cookies.get('rememberMe');
loginForm.value = {
tenantId: tenantId === undefined ? loginForm.value.tenantId : tenantId,
username: username === undefined ? loginForm.value.username : username,
password: password === undefined ? loginForm.value.password : (decrypt(password) as string),
tenantId: tenantId === undefined ? String(loginForm.value.tenantId) : tenantId,
username: username === undefined ? String(loginForm.value.username) : username,
password: password === undefined ? String(loginForm.value.password) : String(password),
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(() => {
getCode();
initTenantList();
@ -179,6 +214,7 @@ onMounted(() => {
background-image: url("../assets/images/login-background.jpg");
background-size: cover;
}
.title {
margin: 0px auto 30px auto;
text-align: center;
@ -190,32 +226,39 @@ onMounted(() => {
background: #ffffff;
width: 400px;
padding: 25px 25px 5px 25px;
.el-input {
height: 40px;
input {
height: 40px;
}
}
.input-icon {
height: 39px;
width: 14px;
margin-left: 0px;
}
}
.login-tip {
font-size: 13px;
text-align: center;
color: #bfbfbf;
}
.login-code {
width: 33%;
height: 40px;
float: right;
img {
cursor: pointer;
vertical-align: middle;
}
}
.el-login-footer {
height: 40px;
line-height: 40px;
@ -224,10 +267,11 @@ onMounted(() => {
width: 100%;
text-align: center;
color: #fff;
font-family: Arial,serif;
font-family: Arial, serif;
font-size: 12px;
letter-spacing: 1px;
}
.login-code-img {
height: 40px;
padding-left: 12px;

View File

@ -2,7 +2,7 @@
<div class="p-2">
<el-row>
<el-col :span="24" class="card-box">
<el-card>
<el-card shadow="hover">
<template #header>
<Monitor style="width: 1em; height: 1em; vertical-align: middle;" />
<span style="vertical-align: middle;">基本信息</span>
@ -98,7 +98,7 @@
</el-col>
<el-col :span="12" class="card-box">
<el-card>
<el-card shadow="hover">
<template #header>
<PieChart style="width: 1em; height: 1em; vertical-align: middle;" />
<span style="vertical-align: middle;">命令统计</span>
@ -110,7 +110,7 @@
</el-col>
<el-col :span="12" class="card-box">
<el-card>
<el-card shadow="hover">
<template #header>
<Odometer style="width: 1em; height: 1em; vertical-align: middle;" /> <span style="vertical-align: middle;">内存信息</span>
</template>
@ -126,7 +126,6 @@
<script setup name="Cache" lang="ts">
import { getCache } from '@/api/monitor/cache';
import * as echarts from 'echarts';
import { ComponentInternalInstance } from "vue";
const cache = ref<any>({});
const commandstats = ref();
@ -157,7 +156,6 @@ const getList = async () => {
}
]
});
const usedmemoryInstance = echarts.init(usedmemory.value, "macarons");
usedmemoryInstance.setOption({
tooltip: {
@ -181,6 +179,10 @@ const getList = async () => {
}
]
})
window.addEventListener("resize",()=>{
commandstatsIntance.resize()
usedmemoryInstance.resize()
});
}
onMounted(() => {

View File

@ -1,7 +1,8 @@
<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">
<div class="mb-[10px]" v-show="showSearch">
<el-card shadow="hover">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
<el-form-item label="登录地址" prop="ipaddr">
<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-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="never">
<el-card shadow="hover">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
@ -98,9 +100,7 @@
<script setup name="Logininfor" lang="ts">
import { list, delLoginInfo, cleanLoginInfo, unlockLoginInfo } from "@/api/monitor/loginInfo";
import { ComponentInternalInstance } from "vue";
import { LoginInfoQuery, LoginInfoVO } from "@/api/monitor/loginInfo/types";
import { DateModelType } from 'element-plus';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
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 defaultSort = ref<any>({ prop: "loginTime", order: "descending" });
const queryFormRef = ref(ElForm);
const loginInfoTableRef = ref(ElTable);
const queryFormRef = ref<ElFormInstance>();
const loginInfoTableRef = ref<ElTableInstance>();
//
const queryParams = ref<LoginInfoQuery>({
pageNum: 1,
@ -145,9 +145,9 @@ const handleQuery = () => {
/** 重置按钮操作 */
const resetQuery = () => {
dateRange.value = ['', ''];
queryFormRef.value.resetFields();
queryFormRef.value?.resetFields();
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[]) => {
@ -167,14 +167,14 @@ const handleDelete = async (row?: LoginInfoVO) => {
const infoIds = row?.infoId || ids.value;
await proxy?.$modal.confirm('是否确认删除访问编号为"' + infoIds + '"的数据项?');
await delLoginInfo(infoIds);
getList();
await getList();
proxy?.$modal.msgSuccess("删除成功");
}
/** 清空按钮操作 */
const handleClean = async () => {
await proxy?.$modal.confirm("是否确认清空所有登录日志数据项?");
await cleanLoginInfo();
getList();
await getList();
proxy?.$modal.msgSuccess("清空成功");
}
/** 解锁按钮操作 */

View File

@ -1,6 +1,7 @@
<template>
<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-item label="登录地址" prop="ipaddr">
<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-form-item>
</el-form>
</el-card>
</div>
<div class="panel">
<el-card shadow="hover">
<el-table
v-loading="loading"
:data="onlineList.slice((queryParams.pageNum - 1) * queryParams.pageSize, queryParams.pageNum * queryParams.pageSize)"
@ -48,13 +50,12 @@
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" />
</div>
</el-card>
</div>
</template>
<script setup name="Online" lang="ts">
import { forceLogout, list as initData } from "@/api/monitor/online";
import { ComponentInternalInstance } from "vue";
import { OnlineQuery, OnlineVO } from "@/api/monitor/online/types";
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@ -63,7 +64,7 @@ const onlineList = ref<OnlineVO[]>([]);
const loading = ref(true);
const total = ref(0);
const queryFormRef = ref(ElForm);
const queryFormRef = ref<ElFormInstance>();
const queryParams = ref<OnlineQuery>({
pageNum: 1,
@ -87,14 +88,14 @@ const handleQuery = () => {
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields();
queryFormRef.value?.resetFields();
handleQuery();
}
/** 强退按钮操作 */
const handleForceLogout = async (row: OnlineVO) => {
await proxy?.$modal.confirm('是否确认强退名称为"' + row.userName + '"的用户?');
await forceLogout(row.tokenId);
getList();
await getList();
proxy?.$modal.msgSuccess("删除成功");
}

View File

@ -1,7 +1,8 @@
<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">
<div class="mb-[10px]">
<el-card shadow="hover">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
<el-form-item label="系统模块" prop="title">
<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-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="never">
<el-card shadow="hover">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
@ -132,7 +134,7 @@
<el-form-item label="操作方法:">{{ form.method }}</el-form-item>
</el-col>
<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 :span="24">
<el-form-item label="返回参数:">{{ form.jsonResult }}</el-form-item>
@ -165,12 +167,10 @@
<script setup name="Operlog" lang="ts">
import { list, delOperlog, cleanOperlog } from '@/api/monitor/operlog';
import { ComponentInternalInstance } from 'vue';
import { OperLogForm, OperLogQuery, OperLogVO } from '@/api/monitor/operlog/types';
import { DateModelType } from 'element-plus';
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 loading = ref(true);
@ -181,8 +181,8 @@ const total = ref(0);
const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
const defaultSort = ref<any>({ prop: "operTime", order: "descending" });
const operLogTableRef = ref(ElTable);
const queryFormRef = ref(ElForm);
const operLogTableRef = ref<ElTableInstance>();
const queryFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({
visible: false,
@ -247,9 +247,9 @@ const handleQuery = () => {
/** 重置按钮操作 */
const resetQuery = () => {
dateRange.value = ['', ''];
queryFormRef.value.resetFields();
queryFormRef.value?.resetFields();
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[]) => {
@ -272,7 +272,7 @@ const handleDelete = async (row?: OperLogVO) => {
const operIds = row?.operId || ids.value;
await proxy?.$modal.confirm('是否确认删除日志编号为"' + operIds + '"的数据项?');
await delOperlog(operIds);
getList();
await getList();
proxy?.$modal.msgSuccess("删除成功");
}
@ -280,7 +280,7 @@ const handleDelete = async (row?: OperLogVO) => {
const handleClean = async () => {
await proxy?.$modal.confirm("是否确认清空所有操作日志数据项?");
await cleanOperlog();
getList();
await getList();
proxy?.$modal.msgSuccess("清空成功");
}

View File

@ -5,5 +5,5 @@
</template>
<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>

View File

@ -58,7 +58,6 @@
<script setup lang="ts">
import { getCodeImg, register, getTenantList } from '@/api/login';
import { RegisterForm, TenantVO } from '@/api/types';
import { FormRules } from 'element-plus';
import { to } from 'await-to-js';
const router = useRouter();
@ -85,7 +84,7 @@ const equalToPassword = (rule: any, value: string, callback: any) => {
}
};
const registerRules: FormRules = {
const registerRules: ElFormRules = {
tenantId: [
{ 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>
<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">
<div class="mb-[10px]" v-show="showSearch">
<el-card shadow="hover">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
<el-form-item label="参数名称" prop="configName">
<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-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="never">
<el-card shadow="hover">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
@ -123,8 +125,6 @@
<script setup name="Config" lang="ts">
import { listConfig, getConfig, delConfig, addConfig, updateConfig, refreshCache } from "@/api/system/config";
import { ConfigForm, ConfigQuery, ConfigVO } from "@/api/system/config/types";
import { ComponentInternalInstance } from "vue";
import { DateModelType } from 'element-plus';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { sys_yes_no } = toRefs<any>(proxy?.useDict("sys_yes_no"));
@ -138,8 +138,8 @@ const multiple = ref(true);
const total = ref(0);
const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
const queryFormRef = ref(ElForm);
const configFormRef = ref(ElForm);
const queryFormRef = ref<ElFormInstance>();
const configFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({
visible: false,
title: ''
@ -153,7 +153,7 @@ const initFormData: ConfigForm = {
remark: ''
}
const data = reactive<PageData<ConfigForm, ConfigQuery>>({
form: {...initFormData},
form: { ...initFormData },
queryParams: {
pageNum: 1,
pageSize: 10,
@ -185,8 +185,8 @@ const cancel = () => {
}
/** 表单重置 */
const reset = () => {
form.value = {...initFormData};
configFormRef.value.resetFields();
form.value = { ...initFormData };
configFormRef.value?.resetFields();
}
/** 搜索按钮操作 */
const handleQuery = () => {
@ -196,7 +196,7 @@ const handleQuery = () => {
/** 重置按钮操作 */
const resetQuery = () => {
dateRange.value = ['', ''];
queryFormRef.value.resetFields();
queryFormRef.value?.resetFields();
handleQuery();
}
/** 多选框选中数据 */
@ -207,31 +207,27 @@ const handleSelectionChange = (selection: ConfigVO[]) => {
}
/** 新增按钮操作 */
const handleAdd = () => {
reset();
dialog.visible = true;
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.title = "修改参数";
const configId = row?.configId || ids.value[0];
nextTick(async () => {
reset();
const res = await getConfig(configId);
form.value = res.data;
})
}
/** 提交按钮 */
const submitForm = () => {
configFormRef.value.validate(async (valid: boolean) => {
configFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
form.value.configId ? await updateConfig(form.value) : await addConfig(form.value);
proxy?.$modal.msgSuccess("操作成功");
dialog.visible = false;
getList();
await getList();
}
});
}
@ -240,7 +236,7 @@ const handleDelete = async (row?: ConfigVO) => {
const configIds = row?.configId || ids.value;
await proxy?.$modal.confirm('是否确认删除参数编号为"' + configIds + '"的数据项?');
await delConfig(configIds);
getList();
await getList();
proxy?.$modal.msgSuccess("删除成功");
}
/** 导出按钮操作 */

View File

@ -1,9 +1,10 @@
<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">
<div class="mb-[10px]" v-show="showSearch">
<el-card shadow="hover">
<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-form-item>
<el-form-item label="状态" prop="status">
@ -16,10 +17,11 @@
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="never">
<el-card shadow="hover">
<template #header>
<el-row :gutter="10">
<el-col :span="1.5">
@ -130,7 +132,6 @@
<script setup name="Dept" lang="ts">
import { listDept, getDept, delDept, addDept, updateDept, listDeptExcludeChild } from "@/api/system/dept"
import { ComponentInternalInstance } from 'vue';
import { DeptForm, DeptQuery, DeptVO } from "@/api/system/dept/types";
interface DeptOptionsType {
@ -155,9 +156,9 @@ const dialog = reactive<DialogOption>({
title: ''
});
const deptTableRef = ref(ElTable);
const queryFormRef = ref(ElForm);
const deptFormRef = ref(ElForm);
const deptTableRef = ref<ElTableInstance>();
const queryFormRef = ref<ElFormInstance>();
const deptFormRef = ref<ElFormInstance>();
const initFormData: DeptForm = {
deptId: undefined,
@ -170,7 +171,7 @@ const initFormData: DeptForm = {
status: "0"
}
const data = reactive<PageData<DeptForm, DeptQuery>>({
form: {...initFormData},
form: { ...initFormData },
queryParams: {
pageNum: 1,
pageSize: 10,
@ -205,8 +206,8 @@ const cancel = () => {
}
/** 表单重置 */
const reset = () => {
form.value = {...initFormData};
deptFormRef.value.resetFields();
form.value = { ...initFormData };
deptFormRef.value?.resetFields();
}
/** 搜索按钮操作 */
@ -215,26 +216,10 @@ const handleQuery = () => {
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields();
queryFormRef.value?.resetFields();
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 = () => {
isExpandAll.value = !isExpandAll.value;
@ -243,38 +228,55 @@ const handleToggleExpandAll = () => {
/** 展开/折叠所有 */
const toggleExpandAll = (data: DeptVO[], status: boolean) => {
data.forEach((item) => {
deptTableRef.value.toggleRowExpansion(item, status)
if(item.children && item.children.length > 0) toggleExpandAll(item.children, status)
deptTableRef.value?.toggleRowExpansion(item, 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 res = await getDept(row.deptId);
dialog.visible = true;
dialog.title = "修改部门";
nextTick(async () => {
reset();
const res = await getDept(row.deptId);
form.value = res.data
const response = await listDeptExcludeChild(row.deptId);
const data = proxy?.handleTree<DeptOptionsType>(response.data, "deptId")
if (data) {
deptOptions.value = data;
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);
}
}
})
dialog.visible = true;
dialog.title = "修改部门";
}
/** 提交按钮 */
const submitForm = () => {
deptFormRef.value.validate(async (valid: boolean) => {
deptFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
form.value.deptId ? await updateDept(form.value) : await addDept(form.value);
proxy?.$modal.msgSuccess("操作成功");
dialog.visible = false;
getList();
await getList();
}
})
}
@ -282,7 +284,7 @@ const submitForm = () => {
const handleDelete = async (row: DeptVO) => {
await proxy?.$modal.confirm('是否确认删除名称为"' + row.deptName + '"的数据项?');
await delDept(row.deptId);
getList();
await getList();
proxy?.$modal.msgSuccess("删除成功");
}

View File

@ -1,7 +1,8 @@
<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">
<div class="mb-[10px]" v-show="showSearch">
<el-card shadow="hover">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
<el-form-item label="字典名称" prop="dictType">
<el-select v-model="queryParams.dictType" style="width: 200px">
@ -21,9 +22,10 @@
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="never">
<el-card shadow="hover">
<template #header>
<el-row :gutter="10" class="mb8">
<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 { listData, getData, delData, addData, updateData } from "@/api/system/dict/data";
import { DictTypeVO } from '@/api/system/dict/type/types';
import { ComponentInternalInstance } from "vue";
import { DictDataForm, DictDataQuery, DictDataVO } from "@/api/system/dict/data/types";
import { ElForm } from 'element-plus';
const { proxy } = getCurrentInstance() as ComponentInternalInstance
const { sys_normal_disable } = toRefs<any>(proxy?.useDict("sys_normal_disable"));
@ -154,8 +154,8 @@ const total = ref(0);
const defaultDictType = ref("");
const typeOptions = ref<DictTypeVO[]>([]);
const dataFormRef = ref(ElForm);
const queryFormRef = ref(ElForm);
const dataFormRef = ref<ElFormInstance>();
const queryFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({
@ -231,7 +231,7 @@ const cancel = () => {
/** 表单重置 */
const reset = () => {
form.value = { ...initFormData };
dataFormRef.value.resetFields();
dataFormRef.value?.resetFields();
}
/** 搜索按钮操作 */
const handleQuery = () => {
@ -245,18 +245,16 @@ const handleClose = () => {
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields();
queryFormRef.value?.resetFields();
queryParams.value.dictType = defaultDictType.value;
handleQuery();
}
/** 新增按钮操作 */
const handleAdd = () => {
dialog.visible = true;
dialog.title = "添加字典数据";
nextTick(() => {
reset();
form.value.dictType = queryParams.value.dictType;
})
dialog.visible = true;
dialog.title = "添加字典数据";
}
/** 多选框选中数据 */
const handleSelectionChange = (selection: DictDataVO[]) => {
@ -265,25 +263,23 @@ const handleSelectionChange = (selection: DictDataVO[]) => {
multiple.value = !selection.length;
}
/** 修改按钮操作 */
const handleUpdate = (row?: DictDataVO) => {
const handleUpdate = async (row?: DictDataVO) => {
reset();
const dictCode = row?.dictCode || ids.value[0];
const res = await getData(dictCode);
Object.assign(form.value, res.data);
dialog.visible = true;
dialog.title = "修改字典数据";
nextTick(async () => {
const res = await getData(dictCode);
reset();
form.value = res.data;
})
}
/** 提交按钮 */
const submitForm = () => {
dataFormRef.value.validate(async (valid: boolean) => {
dataFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
form.value.dictCode ? await updateData(form.value) : await addData(form.value);
useDictStore().removeDict(queryParams.value.dictType);
proxy?.$modal.msgSuccess("操作成功");
dialog.visible = false;
getList();
await getList();
}
});
@ -293,7 +289,7 @@ const handleDelete = async (row?: DictDataVO) => {
const dictCodes = row?.dictCode || ids.value;
await proxy?.$modal.confirm('是否确认删除字典编码为"' + dictCodes + '"的数据项?');
await delData(dictCodes);
getList();
await getList();
proxy?.$modal.msgSuccess("删除成功");
useDictStore().removeDict(queryParams.value.dictType);

View File

@ -1,7 +1,8 @@
<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">
<div class="mb-[10px]" v-show="showSearch">
<el-card shadow="hover">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
<el-form-item label="字典名称" prop="dictName">
<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-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="never">
<el-card shadow="hover">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
@ -123,9 +125,7 @@
<script setup name="Dict" lang="ts">
import useDictStore from '@/store/modules/dict'
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 { DateModelType } from 'element-plus';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { sys_normal_disable } = toRefs<any>(proxy?.useDict("sys_normal_disable"))
@ -139,8 +139,8 @@ const multiple = ref(true);
const total = ref(0);
const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
const dictFormRef = ref(ElForm);
const queryFormRef = ref(ElForm);
const dictFormRef = ref<ElFormInstance>();
const queryFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({
@ -156,7 +156,7 @@ const initFormData: DictTypeForm = {
remark: ''
}
const data = reactive<PageData<DictTypeForm, DictTypeQuery>>({
form: {...initFormData},
form: { ...initFormData },
queryParams: {
pageNum: 1,
pageSize: 10,
@ -188,8 +188,8 @@ const cancel = () => {
}
/** 表单重置 */
const reset = () => {
form.value = {...initFormData};
dictFormRef.value.resetFields();
form.value = { ...initFormData };
dictFormRef.value?.resetFields();
}
/** 搜索按钮操作 */
const handleQuery = () => {
@ -199,16 +199,14 @@ const handleQuery = () => {
/** 重置按钮操作 */
const resetQuery = () => {
dateRange.value = ['', ''];
queryFormRef.value.resetFields();
queryFormRef.value?.resetFields();
handleQuery();
}
/** 新增按钮操作 */
const handleAdd = () => {
reset();
dialog.visible = true;
dialog.title = "添加字典类型";
nextTick(() => {
reset();
})
}
/** 多选框选中数据 */
const handleSelectionChange = (selection: DictTypeVO[]) => {
@ -217,20 +215,17 @@ const handleSelectionChange = (selection: DictTypeVO[]) => {
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.title = "修改字典类型";
const dictId = row?.dictId || ids.value[0];
nextTick(async () => {
reset();
const res = await getType(dictId);
form.value = res.data;
})
}
/** 提交按钮 */
const submitForm = () => {
dictFormRef.value.validate(async (valid: boolean) => {
dictFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
form.value.dictId ? await updateType(form.value) : await addType(form.value);
proxy?.$modal.msgSuccess("操作成功");
@ -260,7 +255,7 @@ const handleRefreshCache = async () => {
useDictStore().cleanDict();
}
onMounted(()=>{
onMounted(() => {
getList();
})
</script>

View File

@ -1,7 +1,8 @@
<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">
<div class="mb-[10px]" v-show="showSearch">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px">
<el-form-item label="菜单名称" prop="menuName">
<el-input v-model="queryParams.menuName" placeholder="请输入菜单名称" clearable @keyup.enter="handleQuery" />
@ -16,10 +17,11 @@
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="never">
<el-card shadow="hover">
<template #header>
<el-row :gutter="10">
<el-col :span="1.5">
@ -37,7 +39,6 @@
:data="menuList"
row-key="menuId"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
border
ref="menuTableRef"
:default-expand-all="isExpandAll"
>
@ -180,7 +181,7 @@
</el-col>
<el-col :span="12" v-if="form.menuType === 'C'">
<el-form-item>
<el-input v-model="form.query" placeholder="请输入路由参数" maxlength="255" />
<el-input v-model="form.queryParam" placeholder="请输入路由参数" maxlength="255" />
<template #label>
<span>
<el-tooltip content='访问路由的默认传递参数,如:`{"id": 1, "name": "ry"}`' placement="top">
@ -262,9 +263,7 @@
<script setup name="Menu" lang="ts">
import { addMenu, delMenu, getMenu, listMenu, updateMenu } from '@/api/system/menu';
import { MenuForm, MenuQuery, MenuVO } from '@/api/system/menu/types';
import { ComponentInternalInstance } from 'vue';
import { MenuTypeEnum } from '@/enums/MenuTypeEnum';
import { ElTable, ElForm } from 'element-plus';
interface MenuOptionsType {
menuId: number;
@ -286,8 +285,8 @@ const dialog = reactive<DialogOption>({
title: ''
});
const queryFormRef = ref(ElForm);
const menuFormRef = ref(ElForm);
const queryFormRef = ref<ElFormInstance>();
const menuFormRef = ref<ElFormInstance>();
const initFormData = {
path: '',
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)
/** 查询菜单列表 */
@ -343,7 +342,7 @@ const cancel = () => {
/** 表单重置 */
const reset = () => {
form.value = { ...initFormData };
menuFormRef.value.resetFields();
menuFormRef.value?.resetFields();
}
/** 搜索按钮操作 */
@ -352,19 +351,16 @@ const handleQuery = () => {
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields();
queryFormRef.value?.resetFields();
handleQuery();
}
/** 新增按钮操作 */
const handleAdd = (row?: MenuVO) => {
reset();
getTreeselect();
row && row.menuId ? form.value.parentId = row.menuId : form.value.parentId = 0;
dialog.visible = true;
dialog.title = "添加菜单";
getTreeselect();
nextTick(() => {
reset();
row && row.menuId ? form.value.parentId = row.menuId : form.value.parentId = 0;
})
}
/** 展开/折叠操作 */
const handleToggleExpandAll = () => {
@ -374,32 +370,29 @@ const handleToggleExpandAll = () => {
/** 展开/折叠所有 */
const toggleExpandAll = (data: MenuVO[], status: boolean) => {
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)
})
}
/** 修改按钮操作 */
const handleUpdate = async (row: MenuVO) => {
reset();
await getTreeselect();
dialog.visible = true;
dialog.title = "修改菜单";
await nextTick(async () => {
if (row.menuId) {
const { data } = await getMenu(row.menuId);
reset();
form.value = data;
}
})
dialog.visible = true;
dialog.title = "修改菜单";
}
/** 提交按钮 */
const submitForm = () => {
menuFormRef.value.validate(async (valid: boolean) => {
menuFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
form.value.menuId ? await updateMenu(form.value) : await addMenu(form.value);
proxy?.$modal.msgSuccess("操作成功");
dialog.visible = false;
getList();
await getList();
}
})
}
@ -407,7 +400,7 @@ const submitForm = () => {
const handleDelete = async (row: MenuVO) => {
await proxy?.$modal.confirm('是否确认删除名称为"' + row.menuName + '"的数据项?');
await delMenu(row.menuId);
getList();
await getList();
proxy?.$modal.msgSuccess("删除成功");
}

View File

@ -1,7 +1,8 @@
<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">
<div class="mb-[10px]" v-show="showSearch">
<el-card shadow="hover">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
<el-form-item label="公告标题" prop="noticeTitle">
<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-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="never">
<el-card shadow="hover">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
@ -119,9 +121,7 @@
<script setup name="Notice" lang="ts">
import { listNotice, getNotice, delNotice, addNotice, updateNotice } from "@/api/system/notice";
import { ComponentInternalInstance } from "vue";
import { NoticeForm, NoticeQuery, NoticeVO } from "@/api/system/notice/types";
import { ElForm } from 'element-plus';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
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 total = ref(0);
const queryFormRef = ref(ElForm);
const noticeFormRef = ref(ElForm);
const queryFormRef = ref<ElFormInstance>();
const noticeFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({
@ -186,7 +186,7 @@ const cancel = () => {
/** 表单重置 */
const reset = () => {
form.value = { ...initFormData };
noticeFormRef.value.resetFields();
noticeFormRef.value?.resetFields();
}
/** 搜索按钮操作 */
const handleQuery = () => {
@ -195,7 +195,7 @@ const handleQuery = () => {
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields();
queryFormRef.value?.resetFields();
handleQuery();
}
/** 多选框选中数据 */
@ -206,31 +206,27 @@ const handleSelectionChange = (selection: NoticeVO[]) => {
}
/** 新增按钮操作 */
const handleAdd = () => {
reset();
dialog.visible = true;
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.title = "修改公告";
nextTick(async () => {
const noticeId = row?.noticeId || ids.value[0];
reset();
const { data } = await getNotice(noticeId);
form.value = data;
})
}
/** 提交按钮 */
const submitForm = () => {
noticeFormRef.value.validate(async (valid: boolean) => {
noticeFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
form.value.noticeId ? await updateNotice(form.value) : await addNotice(form.value);
proxy?.$modal.msgSuccess("修改成功");
dialog.visible = false;
getList();
await getList();
}
});
}
@ -239,7 +235,7 @@ const handleDelete = async (row?: NoticeVO) => {
const noticeIds = row?.noticeId || ids.value
await proxy?.$modal.confirm('是否确认删除公告编号为"' + noticeIds + '"的数据项?');
await delNotice(noticeIds);
getList();
await getList();
proxy?.$modal.msgSuccess("删除成功");
}

View File

@ -1,7 +1,8 @@
<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">
<div class="mb-[10px]" v-show="showSearch">
<el-card shadow="hover">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
<el-form-item label="配置key" prop="configKey">
<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-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="never">
<el-card shadow="hover">
<template #header>
<el-row :gutter="10" class="mb8">
<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>
</template>
</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">
<el-tooltip content="修改" placement="top">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:oss:edit']"></el-button>
@ -138,9 +140,7 @@ import {
updateOssConfig,
changeOssConfigStatus
} from "@/api/system/ossConfig";
import { ComponentInternalInstance } from "vue";
import { OssConfigForm, OssConfigQuery, OssConfigVO } from "@/api/system/ossConfig/types";
import { ElForm } from 'element-plus';
const { proxy } = getCurrentInstance() as ComponentInternalInstance
@ -155,8 +155,8 @@ const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const queryFormRef = ref(ElForm);
const ossConfigFormRef = ref(ElForm);
const queryFormRef = ref<ElFormInstance>();
const ossConfigFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({
visible: false,
@ -262,7 +262,7 @@ const cancel = () => {
/** 表单重置 */
const reset = () => {
form.value = { ...initFormData };
ossConfigFormRef.value.resetFields();
ossConfigFormRef.value?.resetFields();
}
/** 搜索按钮操作 */
const handleQuery = () => {
@ -271,7 +271,7 @@ const handleQuery = () => {
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields();
queryFormRef.value?.resetFields();
handleQuery();
}
/** 选择条数 */
@ -282,28 +282,22 @@ const handleSelectionChange = (selection: OssConfigVO[]) => {
}
/** 新增按钮操作 */
const handleAdd = () => {
reset();
dialog.visible = true;
dialog.title = "添加对象存储配置";
nextTick(() => {
reset();
})
}
/** 修改按钮操作 */
const handleUpdate = (row?: OssConfigVO) => {
loading.value = true;
const handleUpdate = async (row?: OssConfigVO) => {
reset();
const ossConfigId = row?.ossConfigId || ids.value[0];
const res = await getOssConfig(ossConfigId);
Object.assign(form.value, res.data);
dialog.visible = true;
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 = () => {
ossConfigFormRef.value.validate(async (valid: boolean) => {
ossConfigFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
buttonLoading.value = true;
if (form.value.ossConfigId) {
@ -313,7 +307,7 @@ const submitForm = () => {
}
proxy?.$modal.msgSuccess("新增成功");
dialog.visible = false;
getList();
await getList();
}
});
}
@ -323,7 +317,7 @@ const handleStatusChange = async (row: OssConfigVO) => {
try {
await proxy?.$modal.confirm('确认要"' + text + '""' + row.configKey + '"配置吗?');
await changeOssConfigStatus(row.ossConfigId, row.status, row.configKey);
getList()
await getList()
proxy?.$modal.msgSuccess(text + "成功");
} catch { return } finally {
row.status = row.status === "0" ? "1" : "0";
@ -336,7 +330,7 @@ const handleDelete = async (row?: OssConfigVO) => {
await proxy?.$modal.confirm('是否确认删除OSS配置编号为"' + ossConfigIds + '"的数据项?');
loading.value = true;
await delOssConfig(ossConfigIds).finally(() => loading.value = false);
getList();
await getList();
proxy?.$modal.msgSuccess("删除成功");
}

View File

@ -1,7 +1,8 @@
<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">
<div class="mb-[10px]" v-show="showSearch">
<el-card shadow="hover">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
<el-form-item label="文件名" prop="fileName">
<el-input v-model="queryParams.fileName" placeholder="请输入文件名" clearable style="width: 200px" @keyup.enter="handleQuery" />
@ -14,7 +15,7 @@
</el-form-item>
<el-form-item label="创建时间">
<el-date-picker
v-model="daterangeCreateTime"
v-model="dateRangeCreateTime"
value-format="YYYY-MM-DD HH:mm:ss"
type="daterange"
range-separator="-"
@ -31,10 +32,11 @@
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="never">
<el-card shadow="hover">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
@ -133,9 +135,7 @@
<script setup name="Oss" lang="ts">
import { listOss, delOss } from "@/api/system/oss";
import ImagePreview from "@/components/ImagePreview/index.vue";
import { ComponentInternalInstance } from "vue";
import { OssForm, OssQuery, OssVO } from "@/api/system/oss/types";
import { DateModelType } from 'element-plus';
const router = useRouter();
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@ -151,7 +151,7 @@ const multiple = ref(true);
const total = ref(0);
const type = ref(0);
const previewListResource = ref(true);
const daterangeCreateTime = ref<[DateModelType, DateModelType]>(['', '']);
const dateRangeCreateTime = ref<[DateModelType, DateModelType]>(['', '']);
const dialog = reactive<DialogOption>({
visible: false,
@ -161,8 +161,8 @@ const dialog = reactive<DialogOption>({
//
const defaultSort = ref({ prop: 'createTime', order: 'ascending' });
const ossFormRef = ref(ElForm);
const queryFormRef = ref(ElForm);
const ossFormRef = ref<ElFormInstance>();
const queryFormRef = ref<ElFormInstance>();
const initFormData = {
file: undefined,
@ -195,7 +195,7 @@ const getList = async () => {
loading.value = true;
const res = await proxy?.getConfigKey("sys.oss.previewListResource");
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;
total.value = response.total;
loading.value = false;
@ -215,7 +215,7 @@ function cancel() {
/** 表单重置 */
function reset() {
form.value = { ...initFormData };
ossFormRef.value.resetFields();
ossFormRef.value?.resetFields();
}
/** 搜索按钮操作 */
function handleQuery() {
@ -225,8 +225,8 @@ function handleQuery() {
/** 重置按钮操作 */
function resetQuery() {
showTable.value = false;
daterangeCreateTime.value = ['', ''];
queryFormRef.value.resetFields();
dateRangeCreateTime.value = ['', ''];
queryFormRef.value?.resetFields();
queryParams.value.orderByColumn = defaultSort.value.prop;
queryParams.value.isAsc = defaultSort.value.order;
handleQuery();
@ -288,21 +288,17 @@ const handleOssConfig = () => {
}
/** 文件按钮操作 */
const handleFile = () => {
dialog.visible = true;
dialog.title = "上传文件";
nextTick(() => {
reset();
type.value = 0;
})
dialog.visible = true;
dialog.title = "上传文件";
}
/** 图片按钮操作 */
const handleImage = () => {
dialog.visible = true;
dialog.title = "上传图片";
nextTick(() => {
reset();
type.value = 1;
})
dialog.visible = true;
dialog.title = "上传图片";
}
/** 提交按钮 */
const submitForm = () => {
@ -319,7 +315,7 @@ const handlePreviewListResource = async (preview: boolean) => {
try {
await proxy?.$modal.confirm('确认要"' + text + '""预览列表图片"配置吗?');
await proxy?.updateConfigByKey("sys.oss.previewListResource", preview);
getList()
await getList()
proxy?.$modal.msgSuccess(text + "成功");
} catch { return }
}
@ -329,7 +325,7 @@ const handleDelete = async (row?: OssVO) => {
await proxy?.$modal.confirm('是否确认删除OSS对象存储编号为"' + ossIds + '"的数据项?');
loading.value = true;
await delOss(ossIds).finally(() => loading.value = false);
getList();
await getList();
proxy?.$modal.msgSuccess("删除成功");
}

View File

@ -1,7 +1,8 @@
<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">
<div class="mb-[10px]" v-show="showSearch">
<el-card shadow="hover">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="70">
<el-form-item label="岗位编码" prop="postCode">
<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-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="never">
<el-card shadow="hover">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
@ -107,7 +109,6 @@
<script setup name="Post" lang="ts">
import { listPost, addPost, delPost, getPost, updatePost } from "@/api/system/post";
import { PostForm, PostQuery, PostVO } from "@/api/system/post/types";
import { ComponentInternalInstance } from "vue";
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { sys_normal_disable } = toRefs<any>(proxy?.useDict("sys_normal_disable"));
@ -120,8 +121,8 @@ const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const postFormRef = ref(ElForm);
const queryFormRef = ref(ElForm);
const postFormRef = ref<ElFormInstance>();
const queryFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({
visible: false,
@ -138,7 +139,7 @@ const initFormData: PostForm = {
}
const data = reactive<PageData<PostForm, PostQuery>>({
form: {...initFormData},
form: { ...initFormData },
queryParams: {
pageNum: 1,
pageSize: 10,
@ -170,8 +171,8 @@ const cancel = () => {
}
/** 表单重置 */
const reset = () => {
form.value = {...initFormData};
postFormRef.value.resetFields();
form.value = { ...initFormData };
postFormRef.value?.resetFields();
}
/** 搜索按钮操作 */
const handleQuery = () => {
@ -180,7 +181,7 @@ const handleQuery = () => {
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields();
queryFormRef.value?.resetFields();
handleQuery();
}
/** 多选框选中数据 */
@ -191,31 +192,27 @@ const handleSelectionChange = (selection: PostVO[]) => {
}
/** 新增按钮操作 */
const handleAdd = () => {
reset();
dialog.visible = true;
dialog.title = "添加岗位";
nextTick(() => {
reset();
})
}
/** 修改按钮操作 */
const handleUpdate = (row?: PostVO) => {
dialog.visible = true;
dialog.title = "修改岗位";
nextTick(async () => {
const handleUpdate = async (row?: PostVO) => {
reset();
const postId = row?.postId || ids.value[0];
const res = await getPost(postId);
form.value = res.data;
})
Object.assign(form.value, res.data);
dialog.visible = true;
dialog.title = "修改岗位";
}
/** 提交按钮 */
const submitForm = () => {
postFormRef.value.validate(async (valid: boolean) => {
postFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
form.value.postId ? await updatePost(form.value) : await addPost(form.value);
proxy?.$modal.msgSuccess("操作成功");
dialog.visible = false;
getList();
await getList();
}
});
}
@ -224,7 +221,7 @@ const handleDelete = async (row?: PostVO) => {
const postIds = row?.postId || ids.value;
await proxy?.$modal.confirm('是否确认删除岗位编号为"' + postIds + '"的数据项?');
await delPost(postIds);
getList();
await getList();
proxy?.$modal.msgSuccess("删除成功");
}
/** 导出按钮操作 */

View File

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

View File

@ -1,7 +1,8 @@
<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">
<div class="mb-[10px]" v-show="showSearch">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px">
<el-form-item label="角色名称" prop="roleName">
<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-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="never">
<el-card shadow="hover">
<template #header>
<el-row :gutter="10">
<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 { RoleVO, RoleForm, RoleQuery, DeptTreeOption } from '@/api/system/role/types';
import { MenuTreeOption, RoleMenuTree } from '@/api/system/menu/types';
import { ComponentInternalInstance } from 'vue';
import { ElTree, ElForm, DateModelType } from 'element-plus';
const router = useRouter();
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@ -228,11 +228,11 @@ const dataScopeOptions = ref([
{ value: "5", label: "仅本人数据权限" }
])
const queryFormRef = ref(ElForm);
const roleFormRef = ref(ElForm);
const dataScopeRef = ref(ElForm);
const menuRef = ref(ElTree);
const deptRef = ref(ElTree);
const queryFormRef = ref<ElFormInstance>();
const roleFormRef = ref<ElFormInstance>();
const dataScopeRef = ref<ElFormInstance>();
const menuRef = ref<ElTreeInstance>();
const deptRef = ref<ElTreeInstance>();
const initForm: RoleForm = {
roleId: undefined,
@ -249,7 +249,7 @@ const initForm: RoleForm = {
}
const data = reactive<PageData<RoleForm, RoleQuery>>({
form: {...initForm},
form: { ...initForm },
queryParams: {
pageNum: 1,
pageSize: 10,
@ -265,13 +265,11 @@ const data = reactive<PageData<RoleForm, RoleQuery>>({
})
const { form, queryParams, rules } = toRefs(data)
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
/**
* 查询角色列表
*/
@ -295,7 +293,7 @@ const handleQuery = () => {
/** 重置 */
const resetQuery = () => {
dateRange.value = ['', '']
queryFormRef.value.resetFields();
queryFormRef.value?.resetFields();
handleQuery();
}
/**删除按钮操作 */
@ -343,56 +341,51 @@ const getMenuTreeselect = async () => {
menuOptions.value = res.data;
}
/** 所有部门节点数据 */
const getDeptAllCheckedKeys = () => {
const getDeptAllCheckedKeys = (): any => {
//
let checkedKeys = deptRef.value.getCheckedKeys();
let checkedKeys = deptRef.value?.getCheckedKeys();
//
let halfCheckedKeys = deptRef.value.getHalfCheckedKeys();
checkedKeys.unshift.apply(checkedKeys, halfCheckedKeys);
let halfCheckedKeys = deptRef.value?.getHalfCheckedKeys();
if (halfCheckedKeys) {
checkedKeys?.unshift.apply(checkedKeys, halfCheckedKeys);
}
return checkedKeys
}
/** 重置新增的表单以及其他数据 */
const reset = () => {
menuRef.value.setCheckedKeys([]);
menuRef.value?.setCheckedKeys([]);
menuExpand.value = false
menuNodeAll.value = false
deptExpand.value = true
deptNodeAll.value = false
form.value = { ...initForm };
roleFormRef.value.resetFields();
roleFormRef.value?.resetFields();
}
/** 添加角色 */
const handleAdd = () => {
dialog.visible = true;
dialog.title = "添加角色";
nextTick(() => {
reset();
getMenuTreeselect();
})
dialog.visible = true;
dialog.title = "添加角色";
}
/** 修改角色 */
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();
const roleId = row?.roleId || ids.value[0]
const { data } = await getRole(roleId);
Object.assign(form.value, data);
form.value.roleSort = Number(form.value.roleSort);
nextTick(async () => {
const res = await roleMenu;
let checkedKeys = res.checkedKeys;
checkedKeys.forEach((v) => {
const res = await getRoleMenuTreeselect(roleId);
dialog.title = "修改角色";
dialog.visible = true;
res.checkedKeys.forEach((v) => {
nextTick(() => {
menuRef.value.setChecked(v, true, false);
})
})
menuRef.value?.setChecked(v, true, false);
})
})
}
/** 根据角色ID查询菜单树结构 */
const getRoleMenuTreeselect = (roleId: string | number) => {
@ -408,25 +401,29 @@ const getRoleDeptTreeSelect = async (roleId: string | number) => {
return res.data;
}
/** 树权限(展开/折叠)*/
const handleCheckedTreeExpand = (value: any, type: string) => {
const handleCheckedTreeExpand = (value: boolean, type: string) => {
if (type == "menu") {
let treeList = menuOptions.value;
for (let i = 0; i < treeList.length; i++) {
if (menuRef.value) {
menuRef.value.store.nodesMap[treeList[i].id].expanded = value;
}
}
} else if (type == "dept") {
let treeList = deptOptions.value;
for (let i = 0; i < treeList.length; i++) {
if (deptRef.value) {
deptRef.value.store.nodesMap[treeList[i].id].expanded = value;
}
}
}
}
/** 树权限(全选/全不选) */
const handleCheckedTreeNodeAll = (value: any, type: string) => {
if (type == "menu") {
menuRef.value.setCheckedNodes(value ? menuOptions.value : []);
menuRef.value?.setCheckedNodes(value ? menuOptions.value as any : []);
} 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();
checkedKeys.unshift.apply(checkedKeys, halfCheckedKeys);
let halfCheckedKeys = menuRef.value?.getHalfCheckedKeys();
if (halfCheckedKeys) {
checkedKeys?.unshift.apply(checkedKeys, halfCheckedKeys);
}
return checkedKeys;
}
/** 提交按钮 */
const submitForm = () => {
roleFormRef.value.validate(async (valid: boolean) => {
roleFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
form.value.menuIds = getMenuAllCheckedKeys()
form.value.roleId ? await updateRole(form.value) : await addRole(form.value);
@ -466,23 +465,18 @@ const cancel = () => {
/** 选择角色权限范围触发 */
const dataScopeSelectChange = (value: string) => {
if (value !== "2") {
deptRef.value.setCheckedKeys([])
deptRef.value?.setCheckedKeys([])
}
}
/** 分配数据权限操作 */
const handleDataScope = async (row: RoleVO) => {
const roleDeptTreeselect = getRoleDeptTreeSelect(row.roleId);
const response = await getRole(row.roleId);
Object.assign(form.value, response.data);
const res = await getRoleDeptTreeSelect(row.roleId);
openDataScope.value = true;
dialog.title = "分配数据权限";
nextTick(async () => {
const res = await roleDeptTreeselect;
nextTick(() => {
if (deptRef.value) {
deptRef.value.setCheckedKeys(res.checkedKeys);
}
})
await nextTick(() => {
deptRef.value?.setCheckedKeys(res.checkedKeys);
})
}
/** 提交按钮(数据权限) */
@ -497,8 +491,8 @@ const submitDataScope = async () => {
}
/** 取消按钮(数据权限)*/
const cancelDataScope = () => {
dataScopeRef.value.resetFields();
form.value = {...initForm};
dataScopeRef.value?.resetFields();
form.value = { ...initForm };
openDataScope.value = false;
}

View File

@ -47,8 +47,6 @@
import { authUserSelectAll, unallocatedUserList } from "@/api/system/role";
import { UserVO } 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({
@ -73,8 +71,8 @@ const queryParams = reactive<UserQuery>({
phonenumber: undefined
})
const tableRef = ref(ElTable);
const queryFormRef = ref(ElForm);
const tableRef = ref<ElTableInstance>();
const queryFormRef = ref<ElFormInstance>();
const show = () => {
queryParams.roleId = props.roleId;
@ -86,7 +84,8 @@ const show = () => {
* 选择行
*/
const clickRow = (row: any) => {
tableRef.value.toggleRowSelection(row);
// elebug
tableRef.value?.toggleRowSelection(row, false);
}
/** 多选框选中数据 */
const handleSelectionChange = (selection: UserVO[]) => {
@ -106,7 +105,7 @@ const handleQuery = () => {
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields();
queryFormRef.value?.resetFields();
getList();
}

View File

@ -1,7 +1,8 @@
<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">
<div class="mb-[10px]" v-show="showSearch">
<el-card shadow="hover">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
<el-form-item label="租户编号" prop="tenantId">
<el-input v-model="queryParams.tenantId" placeholder="请输入租户编号" clearable @keyup.enter="handleQuery" />
@ -20,10 +21,11 @@
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="never">
<el-card shadow="hover">
<template #header>
<el-row :gutter="10" class="mb8">
<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>
</template>
</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">
<el-tooltip content="修改" placement="top">
<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>
<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-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 { TenantForm, TenantQuery, TenantVO } from '@/api/system/tenant/types';
import { TenantPkgVO } from '@/api/system/tenantPackage/types';
import { ComponentInternalInstance } from 'vue';
import { ElForm } from 'element-plus';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@ -158,8 +158,8 @@ const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const queryFormRef = ref(ElForm);
const tenantFormRef = ref(ElForm);
const queryFormRef = ref<ElFormInstance>();
const tenantFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({
visible: false,
@ -185,7 +185,7 @@ const initFormData: TenantForm = {
status: '0',
}
const data = reactive<PageData<TenantForm, TenantQuery>>({
form: {...initFormData},
form: { ...initFormData },
queryParams: {
pageNum: 1,
pageSize: 10,
@ -250,8 +250,8 @@ const cancel = () => {
//
const reset = () => {
form.value = {...initFormData};
tenantFormRef.value.resetFields();
form.value = { ...initFormData };
tenantFormRef.value?.resetFields();
}
/** 搜索按钮操作 */
@ -262,7 +262,7 @@ const handleQuery = () => {
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields();
queryFormRef.value?.resetFields();
handleQuery();
}
@ -275,32 +275,26 @@ const handleSelectionChange = (selection: TenantVO[]) => {
/** 新增按钮操作 */
const handleAdd = () => {
dialog.visible = true;
dialog.title = "添加租户";
nextTick(() => {
reset();
getTenantPackage();
})
dialog.visible = true;
dialog.title = "添加租户";
}
/** 修改按钮操作 */
const handleUpdate = (row?: TenantVO) => {
loading.value = true;
dialog.visible = true;
dialog.title = "修改租户";
nextTick(async () => {
const handleUpdate = async (row?: TenantVO) => {
reset();
getTenantPackage();
await getTenantPackage();
const _id = row?.id || ids.value[0];
const res = await getTenant(_id);
loading.value = false;
Object.assign(form.value, res.data)
})
dialog.visible = true;
dialog.title = "修改租户";
}
/** 提交按钮 */
const submitForm = () => {
tenantFormRef.value.validate(async (valid: boolean) => {
tenantFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
buttonLoading.value = true;
if (form.value.id) {
@ -310,7 +304,7 @@ const submitForm = () => {
}
proxy?.$modal.msgSuccess("操作成功");
dialog.visible = false;
getList();
await getList();
}
});
}
@ -321,7 +315,7 @@ const handleDelete = async (row?: TenantVO) => {
await proxy?.$modal.confirm('是否确认删除租户编号为"' + _ids + '"的数据项?')
loading.value = true;
await delTenant(_ids).finally(() => loading.value = false);
getList();
await getList();
proxy?.$modal.msgSuccess("删除成功");
@ -333,9 +327,9 @@ const handleSyncTenantPackage = async (row: TenantVO) => {
await proxy?.$modal.confirm('是否确认同步租户套餐租户编号为"' + row.tenantId + '"的数据项?');
loading.value = true;
await syncTenantPackage(row.tenantId, row.packageId);
getList();
await getList();
proxy?.$modal.msgSuccess("同步成功");
} catch {return} finally {
} catch { return } finally {
loading.value = false;
}
}

View File

@ -1,7 +1,8 @@
<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">
<div class="mb-[10px]" v-show="showSearch">
<el-card shadow="hover">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
<el-form-item label="套餐名称" prop="packageName">
<el-input v-model="queryParams.packageName" placeholder="请输入套餐名称" clearable @keyup.enter="handleQuery" />
@ -11,27 +12,28 @@
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="never">
<el-card shadow="hover">
<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:tenantPackage:add']">新增</el-button>
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:tenantPackage:add']"> 新增 </el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:tenantPackage:edit']"
>修改</el-button
>
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:tenantPackage:edit']">
修改
</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:tenantPackage:remove']"
>删除</el-button
>
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:tenantPackage:remove']">
删除
</el-button>
</el-col>
<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>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</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-tooltip>
<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>
</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" />
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card>
<!-- 添加或修改租户套餐对话框 -->
@ -70,8 +72,8 @@
</el-form-item>
<el-form-item label="关联菜单">
<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="form.menuCheckStrictly" @change="handleCheckedTreeConnect($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-tree
class="tree-border"
:data="menuOptions"
@ -98,12 +100,17 @@
</template>
<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 { ComponentInternalInstance } from "vue";
import { TenantPkgForm, TenantPkgQuery, TenantPkgVO } from "@/api/system/tenantPackage/types";
import { MenuTreeOption } from "@/api/system/menu/types";
import { CheckboxValueType, ElTree, ElForm } from 'element-plus';
import to from "await-to-js";
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@ -120,29 +127,29 @@ const menuExpand = ref(false);
const menuNodeAll = ref(false);
const menuOptions = ref<MenuTreeOption[]>([]);
const menuTreeRef = ref(ElTree);
const queryFormRef = ref(ElForm);
const tenantPackageFormRef = ref(ElForm);
const menuTreeRef = ref<ElTreeInstance>();
const queryFormRef = ref<ElFormInstance>();
const tenantPackageFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({
visible: false,
title: ''
title: ""
});
const initFormData: TenantPkgForm = {
packageId: undefined,
packageName: '',
menuIds: '',
remark: '',
packageName: "",
menuIds: "",
remark: "",
menuCheckStrictly: true
};
const data = reactive<PageData<TenantPkgForm, TenantPkgQuery>>({
form: {...initFormData},
form: { ...initFormData },
queryParams: {
pageNum: 1,
pageSize: 10,
packageName: ''
packageName: ""
},
rules: {
packageId: [{ required: true, message: "租户套餐id不能为空", trigger: "blur" }],
@ -153,27 +160,29 @@ const data = reactive<PageData<TenantPkgForm, TenantPkgQuery>>({
const { queryParams, form, rules } = toRefs(data);
/** 查询菜单树结构 */
const getMenuTreeselect = async() => {
const getMenuTreeselect = async () => {
const { data } = await menuTreeselect();
menuOptions.value = data;
}
};
//
const getMenuAllCheckedKeys = () => {
const getMenuAllCheckedKeys = (): any => {
//
let checkedKeys = menuTreeRef.value.getCheckedKeys();
let checkedKeys = menuTreeRef.value?.getCheckedKeys();
//
let halfCheckedKeys = menuTreeRef.value.getHalfCheckedKeys();
checkedKeys.unshift.apply(checkedKeys, halfCheckedKeys);
let halfCheckedKeys = menuTreeRef.value?.getHalfCheckedKeys();
if (halfCheckedKeys) {
checkedKeys?.unshift.apply(checkedKeys, halfCheckedKeys);
}
return checkedKeys;
}
};
/** 根据租户套餐ID查询菜单树结构 */
const getPackageMenuTreeselect = async(packageId: string | number) => {
const getPackageMenuTreeselect = async (packageId: string | number) => {
const res = await tenantPackageMenuTreeselect(packageId);
menuOptions.value = res.data.menus;
return Promise.resolve(res);
}
};
/** 查询租户套餐列表 */
const getList = async () => {
@ -182,115 +191,107 @@ const getList = async () => {
tenantPackageList.value = res.rows;
total.value = res.total;
loading.value = false;
}
};
//
const handleStatusChange = async (row: TenantPkgVO) => {
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) {
row.status = row.status === "0" ? "1" : "0";
} else {
await changePackageStatus(row.packageId, row.status);
proxy?.$modal.msgSuccess(text + "成功");
}
}
};
//
const cancel = () => {
reset();
dialog.visible = false;
}
};
//
const reset = () => {
menuTreeRef.value.setCheckedKeys([]);
menuTreeRef.value?.setCheckedKeys([]);
menuExpand.value = false;
menuNodeAll.value = false;
form.value = {...initFormData};
tenantPackageFormRef.value.resetFields();
}
form.value = { ...initFormData };
tenantPackageFormRef.value?.resetFields();
};
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
}
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields();
queryFormRef.value?.resetFields();
handleQuery();
}
};
//
const handleSelectionChange = (selection: TenantPkgVO[]) => {
ids.value = selection.map(item => item.packageId);
single.value = selection.length != 1;
multiple.value = !selection.length;
}
};
// /
const handleCheckedTreeExpand = (value: CheckboxValueType, type: string) => {
if (type == 'menu') {
if (type == "menu") {
let treeList = menuOptions.value;
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) => {
if (type == 'menu') {
menuTreeRef.value.setCheckedNodes(value ? menuOptions.value: []);
if (type == "menu") {
menuTreeRef.value?.setCheckedNodes(value ? menuOptions.value as any : []);
}
}
};
//
const handleCheckedTreeConnect = (value: CheckboxValueType, type: string) => {
if (type == 'menu') {
if (type == "menu") {
form.value.menuCheckStrictly = value as boolean;
}
}
};
/** 新增按钮操作 */
const handleAdd = () => {
dialog.visible = true;
dialog.title = "添加租户套餐";
nextTick(() => {
reset();
getMenuTreeselect();
})
}
dialog.visible = true;
dialog.title = "添加租户套餐";
};
/** 修改按钮操作 */
const handleUpdate = (row?: TenantPkgVO) => {
loading.value = true
dialog.visible = true;
dialog.title = "修改租户套餐";
nextTick(async () => {
const handleUpdate = async (row?: TenantPkgVO) => {
reset();
const _packageId = row?.packageId || ids.value[0];
const packageMenu = getPackageMenuTreeselect(_packageId);
const response = await getTenantPackage(_packageId);
loading.value = false;
form.value = response.data;
nextTick(async () => {
const res = await packageMenu;
let checkedKeys = res.data.checkedKeys
checkedKeys.forEach((v) => {
const res = await getPackageMenuTreeselect(_packageId);
dialog.visible = true;
dialog.title = "修改租户套餐";
res.data.checkedKeys.forEach((v) => {
nextTick(() => {
menuTreeRef.value.setChecked(v, true ,false);
})
})
menuTreeRef.value?.setChecked(v, true, false);
});
})
}
});
};
/** 提交按钮 */
const submitForm = () => {
tenantPackageFormRef.value.validate(async (valid: boolean) => {
tenantPackageFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
buttonLoading.value = true;
form.value.menuIds = getMenuAllCheckedKeys();
@ -301,31 +302,31 @@ const submitForm = () => {
}
proxy?.$modal.msgSuccess("操作成功");
dialog.visible = false;
getList();
await getList();
}
});
}
};
/** 删除按钮操作 */
const handleDelete = async (row?: TenantPkgVO) => {
const _packageIds = row?.packageId || ids.value;
await proxy?.$modal.confirm('是否确认删除租户套餐编号为"' + _packageIds + '"的数据项?').finally(() => {
await proxy?.$modal.confirm("是否确认删除租户套餐编号为\"" + _packageIds + "\"的数据项?").finally(() => {
loading.value = false;
});
await delTenantPackage(_packageIds);
loading.value = true;
getList();
await getList();
proxy?.$modal.msgSuccess("删除成功");
}
};
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download('system/tenantPackage/export', {
proxy?.download("system/tenantPackage/export", {
...queryParams.value
}, `tenantPackage_${new Date().getTime()}.xlsx`)
}
}, `tenantPackage_${new Date().getTime()}.xlsx`);
};
onMounted(() => {
getList();
})
});
</script>

View File

@ -55,11 +55,10 @@
</template>
<script setup name="AuthRole" lang="ts">
import { RoleVO } from '@/api/system/role/types';
import { getAuthRole, updateAuthRole } from '@/api/system/user';
import { UserForm } from '@/api/system/user/types';
import { ElTable } from "element-plus";
import { ComponentInternalInstance } from 'vue';
import { RoleVO } from "@/api/system/role/types";
import { getAuthRole, updateAuthRole } from "@/api/system/user";
import { UserForm } from "@/api/system/user/types";
const route = useRoute();
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@ -71,15 +70,16 @@ const roleIds = ref<Array<string | number>>([]);
const roles = ref<RoleVO[]>([]);
const form = ref<Partial<UserForm>>({
nickName: undefined,
userName: '',
userName: "",
userId: undefined
});
const tableRef = ref(ElTable)
const tableRef = ref<ElTableInstance>();
/** 单击选中行数据 */
const clickRow = (row: RoleVO) => {
tableRef.value.toggleRowSelection(row);
// eleselected
tableRef.value?.toggleRowSelection(row, false);
};
/** 多选框选中数据 */
const handleSelectionChange = (selection: RoleVO[]) => {
@ -98,30 +98,30 @@ const close = () => {
const submitForm = async () => {
const userId = form.value.userId;
const rIds = roleIds.value.join(",");
await updateAuthRole({ userId: userId as string, roleIds: rIds })
await updateAuthRole({ userId: userId as string, roleIds: rIds });
proxy?.$modal.msgSuccess("授权成功");
close();
};
const getList = async() => {
const getList = async () => {
const userId = route.params && route.params.userId;
if (userId) {
loading.value = true;
const res = await getAuthRole(userId as string);
Object.assign(form.value, res.data.user)
Object.assign(roles.value, res.data.roles)
Object.assign(form.value, res.data.user);
Object.assign(roles.value, res.data.roles);
total.value = roles.value.length;
await nextTick(() => {
roles.value.forEach(row => {
if (row?.flag) {
tableRef.value.toggleRowSelection(row);
tableRef.value?.toggleRowSelection(row, true);
}
});
});
loading.value = false;
}
}
};
onMounted(() => {
getList();
})
});
</script>

View File

@ -16,18 +16,25 @@
highlight-current
default-expand-all
@node-click="handleNodeClick"
></el-tree>
/>
</el-card>
</el-col>
<el-col :lg="20" :xs="24">
<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-item label="用户名称" prop="userName">
<el-input v-model="queryParams.userName" placeholder="请输入用户名称" clearable style="width: 240px" @keyup.enter="handleQuery" />
</el-form-item>
<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 label="状态" prop="status">
@ -50,6 +57,7 @@
<el-button @click="resetQuery" icon="Refresh">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
@ -146,7 +154,7 @@
</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-row>
<el-col :span="12">
@ -289,30 +297,20 @@
</template>
<script setup name="User" lang="ts">
import {
changeUserStatus,
listUser,
resetUserPwd,
delUser,
getUser,
updateUser,
addUser,
deptTreeSelect
} from "@/api/system/user"
import api from "@/api/system/user"
import { UserForm, UserQuery, UserVO } from '@/api/system/user/types';
import { ComponentInternalInstance } from "vue";
import { getToken } from "@/utils/auth";
import { treeselect } from "@/api/system/dept";
import { DeptVO } from "@/api/system/dept/types";
import { RoleVO } from "@/api/system/role/types";
import { PostVO } from "@/api/system/post/types";
import { DateModelType, ElTree, ElUpload, UploadFile, ElForm } from 'element-plus';
import { to } from "await-to-js";
import { globalHeaders } from "@/utils/request";
const router = useRouter();
const { proxy } = getCurrentInstance() as ComponentInternalInstance
const { sys_normal_disable, sys_user_sex } = toRefs<any>(proxy?.useDict('sys_normal_disable', 'sys_user_sex'));
const userList = ref<UserVO[]>();
const loading = ref(true);
const showSearch = ref(true)
@ -320,7 +318,7 @@ const ids = ref<Array<number | string>>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const dateRange = ref<[DateModelType, DateModelType]>(['','']);
const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
const deptName = ref('');
const deptOptions = ref<DeptVO[]>([]);
const initPassword = ref('123456');
@ -337,7 +335,7 @@ const upload = reactive<ImportOption>({
//
updateSupport: 0,
//
headers: { Authorization: "Bearer " + getToken() },
headers: globalHeaders,
//
url: import.meta.env.VITE_APP_BASE_API + "/system/user/importData"
})
@ -353,10 +351,11 @@ const columns = ref<FieldOption[]>([
])
const deptTreeRef = ref(ElTree);
const queryFormRef = ref(ElForm);
const userFormRef = ref(ElForm);
const uploadRef = ref(ElUpload);
const deptTreeRef = ref<ElTreeInstance>();
const queryFormRef = ref<ElFormInstance>();
const userFormRef = ref<ElFormInstance>();
const uploadRef = ref<ElUploadInstance>();
const formDialogRef = ref<ElDialogInstance>();
const dialog = reactive<DialogOption>({
visible: false,
@ -405,7 +404,7 @@ const filterNode = (value: string, data: any) => {
}
/** 根据名称筛选部门树 */
watchEffect(
() => {deptTreeRef.value.filter(deptName.value);},
() => { deptTreeRef.value?.filter(deptName.value); },
{
flush: 'post' // watchEffectDOMDOM
}
@ -413,14 +412,14 @@ watchEffect(
/** 查询部门下拉树结构 */
const getTreeSelect = async () => {
const res = await deptTreeSelect();
const res = await api.deptTreeSelect();
deptOptions.value = res.data;
};
/** 查询用户列表 */
const getList = async () => {
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;
userList.value = res.rows;
total.value = res.total;
@ -440,11 +439,11 @@ const handleQuery = () => {
}
/** 重置按钮操作 */
const resetQuery = () => {
dateRange.value = ['','']
queryFormRef.value.resetFields();
dateRange.value = ['', '']
queryFormRef.value?.resetFields();
queryParams.value.pageNum = 1;
queryParams.value.deptId = undefined;
deptTreeRef.value.setCurrentKey(null);
deptTreeRef.value?.setCurrentKey(undefined);
handleQuery();
}
@ -453,7 +452,7 @@ const handleDelete = async (row?: UserVO) => {
const userIds = row?.userId || ids.value;
const [err] = await to(proxy?.$modal.confirm('是否确认删除用户编号为"' + userIds + '"的数据项?') as any);
if (!err) {
await delUser(userIds);
await api.delUser(userIds);
await getList();
proxy?.$modal.msgSuccess("删除成功");
}
@ -464,7 +463,7 @@ const handleStatusChange = async (row: UserVO) => {
let text = row.status === "0" ? "启用" : "停用"
try {
await proxy?.$modal.confirm('确认要"' + text + '""' + row.userName + '"用户吗?');
await changeUserStatus(row.userId, row.status);
await api.changeUserStatus(row.userId, row.status);
proxy?.$modal.msgSuccess(text + "成功");
} catch (err) {
row.status = row.status === "0" ? "1" : "0";
@ -486,7 +485,7 @@ const handleResetPwd = async (row: UserVO) => {
inputErrorMessage: "用户密码长度必须介于 5 和 20 之间",
}))
if (!err) {
await resetUserPwd(row.userId, res.value);
await api.resetUserPwd(row.userId, res.value);
proxy?.$modal.msgSuccess("修改成功,新密码是:" + res.value);
}
}
@ -523,14 +522,14 @@ const handleFileUploadProgress = () => {
const handleFileSuccess = (response: any, file: UploadFile) => {
upload.open = 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 });
getList();
}
/** 提交上传文件 */
function submitFileForm() {
uploadRef.value.submit();
uploadRef.value?.submit();
}
/** 初始化部门数据 */
@ -546,51 +545,46 @@ const initTreeData = async () => {
/** 重置操作表单 */
const reset = () => {
form.value = { ...initFormData };
userFormRef.value.resetFields();
userFormRef.value?.resetFields();
}
/** 取消按钮 */
const cancel = () => {
reset();
dialog.visible = false;
reset();
}
/** 新增按钮操作 */
const handleAdd = () => {
const handleAdd = async () => {
reset();
const { data } = await api.getUser();
dialog.visible = true;
dialog.title = "新增用户";
nextTick(async () => {
reset();
await initTreeData();
const { data } = await getUser();
postOptions.value = data.posts;
roleOptions.value = data.roles;
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.title = "修改用户";
nextTick(async () => {
reset();
await initTreeData();
const userId = row?.userId || ids.value[0]
const { data } = await getUser(userId)
Object.assign(form.value, data.user);
postOptions.value = data.posts;
roleOptions.value = data.roles;
form.value.postIds = data.postIds;
form.value.roleIds = data.roleIds;
form.value.password = "";
})
}
/** 提交按钮 */
const submitForm = () => {
userFormRef.value.validate(async (valid: boolean) => {
userFormRef.value?.validate(async (valid: boolean) => {
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("操作成功");
dialog.visible = false;
await getList();
@ -611,8 +605,8 @@ const closeDialog = () => {
* 重置表单
*/
const resetForm = () => {
userFormRef.value.resetFields();
userFormRef.value.clearValidate();
userFormRef.value?.resetFields();
userFormRef.value?.clearValidate();
form.value.id = undefined;
form.value.status = '1';

View File

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

View File

@ -17,20 +17,15 @@
</template>
<script setup lang="ts">
import { updateUserPwd } from '@/api/system/user';
import { ComponentInternalInstance } from 'vue';
import { ResetPwdForm } from '@/api/system/user/types'
import { ElForm } from 'element-plus';
import { updateUserPwd } from "@/api/system/user";
import type { ResetPwdForm } from "@/api/system/user/types";
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const pwdRef = ref(ElForm);
const pwdRef = ref<ElFormInstance>();
const user = ref<ResetPwdForm>({
oldPassword: '',
newPassword: '',
confirmPassword: ''
oldPassword: "",
newPassword: "",
confirmPassword: ""
});
const equalToPassword = (rule: any, value: string, callback: any) => {
@ -42,15 +37,24 @@ const equalToPassword = (rule: any, value: string, callback: any) => {
};
const rules = ref({
oldPassword: [{ required: true, message: "旧密码不能为空", trigger: "blur" }],
newPassword: [{ required: true, message: "新密码不能为空", trigger: "blur" }, { min: 6, max: 20, message: "长度在 6 到 20 个字符", trigger: "blur" }],
confirmPassword: [{ required: true, message: "确认密码不能为空", trigger: "blur" }, { required: true, validator: equalToPassword, trigger: "blur" }]
newPassword: [{ required: true, message: "新密码不能为空", 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 = () => {
pwdRef.value.validate(async (valid: boolean) => {
pwdRef.value?.validate(async (valid: boolean) => {
if (valid) {
await updateUserPwd(user.value.oldPassword, user.value.newPassword)
await updateUserPwd(user.value.oldPassword, user.value.newPassword);
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-button>
选择
<el-icon class="el-icon--right"><Upload /></el-icon>
<el-icon class="el-icon--right">
<Upload />
</el-icon>
</el-button>
</el-upload>
</el-col>
@ -58,18 +60,17 @@ import "vue-cropper/dist/index.css";
import { VueCropper } from "vue-cropper";
import { uploadAvatar } from "@/api/system/user";
import useUserStore from "@/store/modules/user";
import { ComponentInternalInstance } from "vue";
interface Options {
img: string | ArrayBuffer | null //
autoCrop: boolean //
autoCropWidth: number //
autoCropHeight: number //
fixedBox: boolean //
fileName: string
previews: any //
outputType: string
visible: boolean
img: string | ArrayBuffer | null; //
autoCrop: boolean; //
autoCropWidth: number; //
autoCropHeight: number; //
fixedBox: boolean; //
fileName: string;
previews: any; //
outputType: string;
visible: boolean;
}
@ -89,7 +90,7 @@ const options = reactive<Options>({
autoCropHeight: 200,
fixedBox: true,
outputType: "png",
fileName: '',
fileName: "",
previews: {},
visible: false
});
@ -97,26 +98,27 @@ const options = reactive<Options>({
/** 编辑头像 */
const editCropper = () => {
open.value = true;
}
};
/** 打开弹出层结束时的回调 */
const modalOpened = () => {
visible.value = true;
}
};
/** 覆盖默认上传行为 */
const requestUpload = (): any => {}
const requestUpload = (): any => {
};
/** 向左旋转 */
const rotateLeft = () => {
cropper.value.rotateLeft();
}
};
/** 向右旋转 */
const rotateRight = () => {
cropper.value.rotateRight();
}
};
/** 图片缩放 */
const changeScale = (num: number) => {
num = num || 1;
cropper.value.changeScale(num);
}
};
/** 上传预处理 */
const beforeUpload = (file: any) => {
if (file.type.indexOf("image/") == -1) {
@ -129,7 +131,7 @@ const beforeUpload = (file: any) => {
options.fileName = file.name;
};
}
}
};
/** 上传图片 */
const uploadImg = async () => {
cropper.value.getCropBlob(async (data: any) => {
@ -138,20 +140,20 @@ const uploadImg = async () => {
const res = await uploadAvatar(formData);
open.value = false;
options.img = res.data.imgUrl;
userStore.avatar = options.img as string;
userStore.avatar = options.img as string
proxy?.$modal.msgSuccess("修改成功");
visible.value = false;
});
}
};
/** 实时预览 */
const realTime = (data: any) => {
options.previews = data;
}
};
/** 关闭窗口 */
const closeDialog = () => {
options.img = userStore.avatar;
options.visible = false;
}
};
</script>
<style lang="scss" scoped>

View File

@ -24,34 +24,36 @@
<script setup lang="ts">
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({
user: {
type: Object as PropType<any>,
required: true
}
});
const userForm = computed(() => props.user);
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const userRef = ref(ElForm);
const rules = ref<FormRules>({
const userRef = ref<ElFormInstance>();
const rules = ref<ElFormRules>({
nickName: [{ required: true, message: "用户昵称不能为空", trigger: "blur" }],
email: [{ required: true, 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" }],
email: [{ required: true, 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 = () => {
userRef.value.validate(async (valid: boolean) => {
userRef.value?.validate(async (valid: boolean) => {
if (valid) {
await updateUserProfile(props.user)
await updateUserProfile(props.user);
proxy?.$modal.msgSuccess("修改成功");
}
});

View File

@ -31,15 +31,10 @@
</template>
<script setup lang="ts">
import { PropType } from 'vue';
import { propTypes } from "@/utils/propTypes";
const prop = defineProps({
info: {
type: Object as PropType<any>,
default: () => {
return {};
}
}
info: propTypes.any.def({})
});
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 { optionselect as getDictOptionselect } from '@/api/system/dict/type';
import { DictTypeVO } from '@/api/system/dict/type/types';
import basicInfoForm from './basicInfoForm.vue';
import genInfoForm from "./genInfoForm.vue";
import { ComponentInternalInstance } from "vue";
import BasicInfoForm from './basicInfoForm.vue';
import GenInfoForm from "./genInfoForm.vue";
const route = useRoute();
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@ -131,13 +130,13 @@ const columns = ref<DbColumnVO[]>([]);
const dictOptions = ref<DictTypeVO[]>([]);
const info = ref<Partial<DbTableVO>>({});
const basicInfo = ref(basicInfoForm);
const genInfo = ref(genInfoForm);
const basicInfo = ref<InstanceType<typeof BasicInfoForm>>();
const genInfo = ref<InstanceType<typeof GenInfoForm>>();
/** 提交按钮 */
const submitForm = () => {
const basicForm = basicInfo.value.$refs.basicInfoForm;
const genForm = genInfo.value.$refs.genInfoForm;
const basicForm = basicInfo.value?.$refs.basicInfoForm;
const genForm = genInfo.value?.$refs.genInfoForm;
Promise.all([basicForm, genForm].map(getFormPromise)).then(async res => {
const validateResult = res.every(item => !!item);
@ -168,7 +167,7 @@ const getFormPromise = (form: any) => {
});
}
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);
}

View File

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

View File

@ -26,7 +26,7 @@
<el-table-column prop="createTime" label="创建时间"></el-table-column>
<el-table-column prop="updateTime" label="更新时间"></el-table-column>
</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>
<template #footer>
<div class="dialog-footer">
@ -40,8 +40,6 @@
<script setup lang="ts">
import { listDbTable, importTable, getDataNames } from '@/api/tool/gen';
import { DbTableQuery, DbTableVO } from '@/api/tool/gen/types';
import { ComponentInternalInstance } from 'vue';
import { ElTable, ElForm } from 'element-plus';
const total = ref(0);
const visible = ref(false);
@ -49,8 +47,8 @@ const tables = ref<Array<string>>([]);
const dbTableList = ref<Array<DbTableVO>>([]);
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const tableRef = ref(ElTable);
const queryFormRef = ref(ElForm);
const tableRef = ref<ElTableInstance>();
const queryFormRef = ref<ElFormInstance>();
const queryParams = reactive<DbTableQuery>({
pageNum: 1,
@ -66,7 +64,7 @@ const emit = defineEmits(["ok"]);
/** 查询参数列表 */
const show = (dataName: string) => {
getDataNameList();
if(dataName){
if (dataName) {
queryParams.dataName = dataName;
} else {
queryParams.dataName = 'master';
@ -76,7 +74,8 @@ const show = (dataName: string) => {
}
/** 单击选择行 */
const clickRow = (row: DbTableVO) => {
tableRef.value.toggleRowSelection(row);
// ele bug
tableRef.value?.toggleRowSelection(row, false);
}
/** 多选框选中数据 */
const handleSelectionChange = (selection: DbTableVO[]) => {
@ -95,7 +94,7 @@ const handleQuery = () => {
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields();
queryFormRef.value?.resetFields();
handleQuery();
}
/** 导入按钮操作 */

View File

@ -1,7 +1,8 @@
<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">
<div class="mb-[10px]" v-show="showSearch">
<el-card shadow="hover">
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
<el-form-item label="数据源" prop="dataName">
<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-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="never">
<el-card shadow="hover">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
@ -99,9 +101,9 @@
:name="(key as any).substring((key as any).lastIndexOf('/') + 1, (key as any).indexOf('.vm'))"
:key="value"
>
<el-link :underline="false" icon="DocumentCopy" v-copyText="value" v-copyText:callback="copyTextSuccess" style="float:right"
>&nbsp;复制</el-link
>
<el-link :underline="false" icon="DocumentCopy" v-copyText="value" v-copyText:callback="copyTextSuccess" style="float:right">
&nbsp;复制
</el-link>
<pre>{{ value }}</pre>
</el-tab-pane>
</el-tabs>
@ -114,9 +116,7 @@
import { listTable, previewTable, delTable, genCode, synchDb, getDataNames } from '@/api/tool/gen';
import { TableQuery, TableVO } from '@/api/tool/gen/types';
import router from '@/router';
import importTable from './importTable.vue';
import { ComponentInternalInstance } from 'vue';
import { ElForm, DateModelType } from 'element-plus';
import ImportTable from './importTable.vue';
const route = useRoute();
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@ -132,8 +132,8 @@ const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
const uniqueId = ref("");
const dataNameList = ref<Array<string>>([]);
const queryFormRef = ref(ElForm);
const importRef = ref(importTable);
const queryFormRef = ref<ElFormInstance>();
const importRef = ref<InstanceType<typeof ImportTable>>();
const queryParams = ref<TableQuery>({
pageNum: 1,
@ -143,7 +143,7 @@ const queryParams = ref<TableQuery>({
dataName: ""
})
const preview = ref <any>({
const preview = ref<any>({
data: {},
activeName: 'domain.java'
})
@ -158,7 +158,7 @@ onActivated(() => {
uniqueId.value = time as string;
queryParams.value.pageNum = Number(route.query.pageNum);
dateRange.value = ['', ''];
queryFormRef.value.resetFields();
queryFormRef.value?.resetFields();
getList();
}
})
@ -205,12 +205,12 @@ const handleSynchDb = async (row: TableVO) => {
}
/** 打开导入表弹窗 */
const openImportTable = () => {
importRef.value.show(queryParams.value.dataName);
importRef.value?.show(queryParams.value.dataName);
}
/** 重置按钮操作 */
const resetQuery = () => {
dateRange.value = ['', ''];
queryFormRef.value.resetFields();
queryFormRef.value?.resetFields();
handleQuery();
}
/** 预览按钮 */
@ -240,7 +240,7 @@ const handleDelete = async (row?: TableVO) => {
const tableIds = row?.tableId || ids.value;
await proxy?.$modal.confirm('是否确认删除表编号为"' + tableIds + '"的数据项?');
await delTable(tableIds);
getList();
await getList();
proxy?.$modal.msgSuccess('删除成功');
}