refactor: 使用泛型重构参数绑定等

This commit is contained in:
meilin.huang
2025-05-24 16:22:54 +08:00
parent 666b191b6c
commit d6280ea280
75 changed files with 1340 additions and 732 deletions

View File

@@ -31,7 +31,7 @@
## 前言
Web版 **统一管理操作平台**集成了对Linux系统的全面操作支持包括终端管理[终端回放、命令过滤]、文件管理、脚本执行、进程监控及计划任务设置),同时提供了多种数据库(如 MySQL、PostgreSQL、Oracle、SQL Server、达梦、高斯、SQLite 等)的数据操作、数据同步与数据迁移功能。此外,还支持 Redis单机、哨兵、集群模式以及 MongoDB 的操作管理,并结合工单流程审批功能,为企业提供一站式的运维与管理解决方案。
Web **统一管理操作平台**,集成了对 Linux 系统的全面操作支持(包括终端管理[终端回放、命令过滤]、文件管理、脚本执行、进程监控及计划任务设置),同时提供了多种数据库(如 MySQL、PostgreSQL、Oracle、SQL Server、达梦、高斯、SQLite 等)的数据操作、数据同步与数据迁移功能。此外,还支持 Redis单机、哨兵、集群模式 MongoDB 、Es 的操作管理,并结合工单流程审批功能,为企业提供一站式的运维与管理解决方案。
## 开发语言与主要框架

View File

@@ -28,7 +28,7 @@
## Preface
Web-based **Unified Management and Operation Platform**, integrating comprehensive operation support for Linux systems (including terminal management [terminal playback, command filtering], file management, script execution, process monitoring, and cronjob settings). It also provides data operation, data synchronization, and data migration for multiple databases (such as MySQL, PostgreSQL, Oracle, SQL Server, Dameng, Gauss, SQLite, etc.). Additionally, it supports Redis operations (standalone, sentinel, and cluster modes) and MongoDB management, combined with work order process approval functionality to offer enterprises an all-in-one solution for operations and management.
Web-based **Unified Management and Operation Platform**, integrating comprehensive operation support for Linux systems (including terminal management [terminal playback, command filtering], file management, script execution, process monitoring, and cronjob settings). It also provides data operation, data synchronization, and data migration for multiple databases (such as MySQL, PostgreSQL, Oracle, SQL Server, Dameng, Gauss, SQLite, etc.). Additionally, it supports Redis operations (standalone, sentinel, and cluster modes) and MongoDB、Es management, combined with work order process approval functionality to offer enterprises an all-in-one solution for operations and management.
## Development languages and major frameworks

View File

@@ -24,7 +24,7 @@
"crypto-js": "^4.2.0",
"dayjs": "^1.11.13",
"echarts": "^5.6.0",
"element-plus": "^2.9.10",
"element-plus": "^2.9.11",
"js-base64": "^3.7.7",
"jsencrypt": "^3.3.2",
"mitt": "^3.0.1",

View File

@@ -16,7 +16,7 @@ export const ResourceTypeEnum = {
Redis: EnumValue.of(3, 'redis').setExtra({ icon: 'icon redis/redis', iconColor: 'var(--el-color-danger)' }).tagTypeInfo(),
Mongo: EnumValue.of(4, 'mongo').setExtra({ icon: 'icon mongo/mongo', iconColor: 'var(--el-color-success)' }).tagTypeDanger(),
AuthCert: EnumValue.of(5, '授权凭证').setExtra({ icon: 'Ticket', iconColor: 'var(--el-color-success)' }),
Es: EnumValue.of(6, 'ES实例').setExtra({ icon: 'Coin', iconColor: 'var(--el-color-warning)' }).tagTypeWarning(),
Es: EnumValue.of(6, 'ES实例').setExtra({ icon: 'icon es/es-color', iconColor: 'var(--el-color-warning)' }).tagTypeWarning(),
};
// 标签关联的资源类型

View File

@@ -1,8 +1,7 @@
<template>
<el-tree-select
v-bind="$attrs"
v-model="state.selectTags"
@change="changeTag"
v-model="modelValue"
:data="tags"
:placeholder="$t('tag.selectTagPlaceholder')"
:default-expanded-keys="defaultExpandedKeys"
@@ -35,44 +34,33 @@ import { tagApi } from '../tag/api';
import { TagResourceTypeEnum } from '@/common/commonEnum';
import EnumValue from '@/common/Enum';
//定义事件
const emit = defineEmits(['update:modelValue', 'changeTag', 'input']);
const props = defineProps({
selectTags: {
type: [Array<any>, Object],
},
tagType: {
type: Number,
default: TagResourceTypeEnum.Tag.value,
},
});
const modelValue = defineModel<Array<any> | Object>('modelValue');
const state = reactive({
tags: [],
// 单选则为codePath多选为codePath数组
selectTags: [] as any,
});
const { tags } = toRefs(state);
const defaultExpandedKeys = computed(() => {
if (Array.isArray(state.selectTags)) {
// 如果 state.selectTags 是数组,直接返回
return state.selectTags;
if (Array.isArray(modelValue.value)) {
// 如果 modelValue 是数组,直接返回
return modelValue.value;
}
// 如果 state.selectTags 不是数组,转换为包含 state.selectTags 的数组
return [state.selectTags];
// 如果 modelValue 不是数组,转换为包含 state.selectTags 的数组
return [modelValue.value];
});
onMounted(async () => {
state.selectTags = props.selectTags;
state.tags = await tagApi.getTagTrees.request({ type: props.tagType });
});
const changeTag = () => {
emit('changeTag', state.selectTags);
};
</script>
<style lang="scss"></style>

View File

@@ -4,7 +4,7 @@
:title="title"
v-model="dialogVisible"
@open="open"
:before-close="cancel"
:before-close="onCancel"
:close-on-click-modal="false"
:destroy-on-close="true"
width="38%"
@@ -51,7 +51,7 @@
:loading="state.loadingDbNames"
>
<template #header>
<el-checkbox v-model="checkAllDbNames" :indeterminate="indeterminateDbNames" @change="handleCheckAll">
<el-checkbox v-model="checkAllDbNames" :indeterminate="indeterminateDbNames" @change="onCheckAll">
{{ $t('db.allSelect') }}
</el-checkbox>
</template>
@@ -65,8 +65,8 @@
</el-form>
<template #footer>
<el-button @click="cancel()">{{ $t('common.cancel') }}</el-button>
<el-button type="primary" @click="btnOk">{{ $t('common.confirm') }}</el-button>
<el-button @click="onCancel()">{{ $t('common.cancel') }}</el-button>
<el-button type="primary" @click="onConfirm">{{ $t('common.confirm') }}</el-button>
</template>
</el-dialog>
</div>
@@ -198,7 +198,7 @@ const open = async () => {
}
};
const btnOk = async () => {
const onConfirm = async () => {
await useI18nFormValidate(dbForm);
emit('confirm', state.form);
};
@@ -209,7 +209,7 @@ const resetInputDb = () => {
state.instances = [];
};
const cancel = () => {
const onCancel = () => {
dialogVisible.value = false;
emit('cancel');
setTimeout(() => {
@@ -243,7 +243,7 @@ watch(allDatabases, (val: string[]) => {
state.dbNamesFiltered = val.map((dbName: string) => dbName);
});
const handleCheckAll = (val: CheckboxValueType) => {
const onCheckAll = (val: CheckboxValueType) => {
const otherSelected = state.dbNamesSelected.filter((dbName: string) => {
return !state.dbNamesFiltered.includes(dbName);
});

View File

@@ -8,17 +8,8 @@
<el-form :model="form" ref="dbFormRef" :rules="rules" label-width="auto">
<el-divider content-position="left">{{ $t('common.basic') }}</el-divider>
<el-form-item ref="tagSelectRef" prop="tagCodePaths" :label="$t('tag.relateTag')">
<tag-tree-select
multiple
@change-tag="
(paths: any) => {
form.tagCodePaths = paths;
tagSelectRef.validate();
}
"
:select-tags="form.tagCodePaths"
/>
<el-form-item prop="tagCodePaths" :label="$t('tag.relateTag')">
<tag-tree-select multiple v-model="form.tagCodePaths" />
</el-form-item>
<el-form-item prop="name" :label="$t('common.name')" required>
@@ -114,7 +105,7 @@
</template>
<script lang="ts" setup>
import { computed, reactive, ref, toRefs, watchEffect } from 'vue';
import { computed, reactive, toRefs, useTemplateRef, watchEffect } from 'vue';
import { dbApi } from './api';
import { ElMessage } from 'element-plus';
import SshTunnelSelect from '../component/SshTunnelSelect.vue';
@@ -153,8 +144,7 @@ const rules = {
host: [Rules.requiredInput('Host:Port')],
};
const dbFormRef: any = ref(null);
const tagSelectRef: any = ref(null);
const dbFormRef: any = useTemplateRef('dbFormRef');
const DefaultForm = {
id: null,

View File

@@ -1,24 +1,15 @@
<template>
<div>
<el-drawer :title="title" v-model="dialogVisible" :before-close="cancel" :destroy-on-close="true" :close-on-click-modal="false" size="40%">
<el-drawer :title="title" v-model="dialogVisible" :before-close="onCancel" :destroy-on-close="true" :close-on-click-modal="false" size="40%">
<template #header>
<DrawerHeader :header="title" :back="cancel" />
<DrawerHeader :header="title" :back="onCancel" />
</template>
<el-form :model="form" ref="dbForm" :rules="rules" label-width="auto">
<el-form :model="form" ref="dbFormRef" :rules="rules" label-width="auto">
<el-divider content-position="left">{{ t('common.basic') }}</el-divider>
<el-form-item ref="tagSelectRef" prop="tagCodePaths" :label="t('tag.relateTag')">
<tag-tree-select
multiple
@change-tag="
(paths: any) => {
form.tagCodePaths = paths;
tagSelectRef.validate();
}
"
:select-tags="form.tagCodePaths"
/>
<el-form-item prop="tagCodePaths" :label="t('tag.relateTag')">
<tag-tree-select multiple v-model="form.tagCodePaths" />
</el-form-item>
<el-form-item prop="name" :label="t('common.name')" required>
@@ -50,7 +41,7 @@
:resource-code="form.code"
:resource-type="TagResourceTypeEnum.EsInstance.value"
:test-conn-btn-loading="testConnBtnLoading"
@test-conn="testConn"
@test-conn="onTestConn"
:disable-ciphertext-type="[AuthCertCiphertextTypeEnum.PrivateKey.value]"
/>
</div>
@@ -63,16 +54,16 @@
</el-form>
<template #footer>
<el-button @click="testConn(null)" type="success" v-if="form.authCerts?.length <= 0">{{ t('ac.testConn') }}</el-button>
<el-button @click="cancel()">{{ t('common.cancel') }}</el-button>
<el-button type="primary" :loading="saveBtnLoading" @click="btnOk">{{ t('common.confirm') }}</el-button>
<el-button @click="onTestConn(null)" type="success" v-if="form.authCerts?.length <= 0">{{ t('ac.testConn') }}</el-button>
<el-button @click="onCancel()">{{ t('common.cancel') }}</el-button>
<el-button type="primary" :loading="saveBtnLoading" @click="onConfirm">{{ t('common.confirm') }}</el-button>
</template>
</el-drawer>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref, toRefs, watchEffect } from 'vue';
import { reactive, toRefs, useTemplateRef, watchEffect } from 'vue';
import { esApi } from './api';
import { ElMessage } from 'element-plus';
import SshTunnelSelect from '../component/SshTunnelSelect.vue';
@@ -88,9 +79,6 @@ import { Rules } from '@/common/rule';
const { t } = useI18n();
const props = defineProps({
visible: {
type: Boolean,
},
data: {
type: [Boolean, Object],
},
@@ -99,6 +87,8 @@ const props = defineProps({
},
});
const dialogVisible = defineModel<boolean>('visible', { default: false });
//定义事件
const emit = defineEmits(['update:visible', 'cancel', 'val-change']);
@@ -109,8 +99,7 @@ const rules = {
host: [Rules.requiredInput('Host:Port')],
};
const dbForm: any = ref(null);
const tagSelectRef: any = ref(null);
const dbFormRef: any = useTemplateRef('dbFormRef');
const DefaultForm = {
id: null,
@@ -126,19 +115,17 @@ const DefaultForm = {
};
const state = reactive({
dialogVisible: false,
form: DefaultForm,
submitForm: {} as any,
});
const { dialogVisible, form, submitForm } = toRefs(state);
const { form, submitForm } = toRefs(state);
const { isFetching: saveBtnLoading, execute: saveInstanceExec, data: saveInstanceRes } = esApi.saveInstance.useApi(submitForm);
const { isFetching: testConnBtnLoading, execute: testConnExec, data: testConnRes } = esApi.testConn.useApi<any>(submitForm);
watchEffect(() => {
state.dialogVisible = props.visible;
if (!state.dialogVisible) {
if (!dialogVisible.value) {
return;
}
const dbInst: any = props.data;
@@ -161,8 +148,8 @@ const getReqForm = async () => {
return reqForm;
};
const testConn = async (authCert: any) => {
await useI18nFormValidate(dbForm);
const onTestConn = async (authCert: any) => {
await useI18nFormValidate(dbFormRef);
state.submitForm = await getReqForm();
if (authCert) {
state.submitForm.authCerts = [authCert];
@@ -172,23 +159,23 @@ const testConn = async (authCert: any) => {
ElMessage.success(t('es.connSuccess'));
};
const btnOk = async () => {
const onConfirm = async () => {
if (!state.form.version) {
ElMessage.warning(t('es.shouldTestConn'));
return;
}
await useI18nFormValidate(dbForm);
await useI18nFormValidate(dbFormRef);
state.submitForm = await getReqForm();
await saveInstanceExec();
useI18nSaveSuccessMsg();
state.form.id = saveInstanceRes as any;
emit('val-change', state.form);
cancel();
onCancel();
};
const cancel = () => {
emit('update:visible', false);
const onCancel = () => {
dialogVisible.value = false;
emit('cancel');
};
</script>

View File

@@ -1,23 +1,14 @@
<template>
<div>
<el-drawer :title="title" v-model="dialogVisible" :before-close="cancel" :destroy-on-close="true" :close-on-click-modal="false" size="40%">
<el-drawer :title="title" v-model="dialogVisible" :before-close="onCancel" :destroy-on-close="true" :close-on-click-modal="false" size="40%">
<template #header>
<DrawerHeader :header="title" :back="cancel" />
<DrawerHeader :header="title" :back="onCancel" />
</template>
<el-form :model="form" ref="machineForm" :rules="rules" label-width="auto">
<el-form :model="form" ref="machineFormRef" :rules="rules" label-width="auto">
<el-divider content-position="left">{{ $t('common.basic') }}</el-divider>
<el-form-item ref="tagSelectRef" prop="tagCodePaths" :label="$t('tag.relateTag')">
<tag-tree-select
multiple
@change-tag="
(paths) => {
form.tagCodePaths = paths;
tagSelectRef.validate();
}
"
:select-tags="form.tagCodePaths"
/>
<el-form-item prop="tagCodePaths" :label="$t('tag.relateTag')">
<tag-tree-select multiple v-model="form.tagCodePaths" />
</el-form-item>
<el-form-item prop="name" :label="$t('common.name')" required>
<el-input v-model.trim="form.name" auto-complete="off"></el-input>
@@ -48,7 +39,7 @@
:resource-code="form.code"
:resource-type="TagResourceTypeEnum.Machine.value"
:test-conn-btn-loading="testConnBtnLoading"
@test-conn="testConn"
@test-conn="onTestConn"
/>
</div>
@@ -71,8 +62,8 @@
<template #footer>
<div>
<el-button @click="cancel()">{{ $t('common.cancel') }}</el-button>
<el-button type="primary" :loading="saveBtnLoading" @click="btnOk">{{ $t('common.confirm') }}</el-button>
<el-button @click="onCancel()">{{ $t('common.cancel') }}</el-button>
<el-button type="primary" :loading="saveBtnLoading" @click="onConfirm">{{ $t('common.confirm') }}</el-button>
</div>
</template>
</el-drawer>
@@ -80,7 +71,7 @@
</template>
<script lang="ts" setup>
import { reactive, ref, toRefs, watchEffect } from 'vue';
import { reactive, toRefs, useTemplateRef, watchEffect } from 'vue';
import { machineApi } from './api';
import { ElMessage } from 'element-plus';
import TagTreeSelect from '../component/TagTreeSelect.vue';
@@ -119,8 +110,7 @@ const rules = {
ip: [Rules.requiredInput('machine.ipAndPort')],
};
const machineForm: any = ref(null);
const tagSelectRef: any = ref(null);
const machineFormRef: any = useTemplateRef('machineFormRef');
const defaultForm = {
id: null,
@@ -166,8 +156,8 @@ watchEffect(() => {
}
});
const testConn = async (authCert: any) => {
await useI18nFormValidate(machineForm);
const onTestConn = async (authCert: any) => {
await useI18nFormValidate(machineFormRef);
state.submitForm = getReqForm();
state.submitForm.authCerts = [authCert];
@@ -175,8 +165,8 @@ const testConn = async (authCert: any) => {
ElMessage.success(t('machine.connSuccess'));
};
const btnOk = async () => {
await useI18nFormValidate(machineForm);
const onConfirm = async () => {
await useI18nFormValidate(machineFormRef);
if (state.form.authCerts.length == 0) {
ElMessage.error(t('machine.noAcErrMsg'));
@@ -187,7 +177,7 @@ const btnOk = async () => {
await saveMachineExec();
useI18nSaveSuccessMsg();
emit('val-change', submitForm);
cancel();
onCancel();
};
const getReqForm = () => {
@@ -208,7 +198,7 @@ const handleChangeProtocol = (val: any) => {
}
};
const cancel = () => {
const onCancel = () => {
dialogVisible.value = false;
emit('cancel');
};

View File

@@ -20,11 +20,12 @@
<el-table-column :label="$t('common.operation')" min-width="120px">
<template #header>
<el-text tag="b">{{ $t('common.operation') }}</el-text>
<el-button v-auth="'cmdconf:save'" class="ml-1" type="primary" circle size="small" icon="Plus" @click="openFormDialog(false)"> </el-button>
<el-button v-auth="'cmdconf:save'" class="ml-1" type="primary" circle size="small" icon="Plus" @click="onOpenFormDialog(false)">
</el-button>
</template>
<template #default="scope">
<el-button v-auth="'cmdconf:save'" @click="openFormDialog(scope.row)" type="primary" link>{{ $t('common.edit') }}</el-button>
<el-button v-auth="'cmdconf:del'" @click="deleteCmdConf(scope.row)" type="danger" link>{{ $t('common.delete') }}</el-button>
<el-button v-auth="'cmdconf:save'" @click="onOpenFormDialog(scope.row)" type="primary" link>{{ $t('common.edit') }}</el-button>
<el-button v-auth="'cmdconf:del'" @click="onDeleteCmdConf(scope.row)" type="danger" link>{{ $t('common.delete') }}</el-button>
</template>
</el-table-column>
</el-table>
@@ -38,7 +39,7 @@
:close-on-click-modal="false"
>
<template #header>
<DrawerHeader :header="$t('machine.cmdConfig')" :back="cancelEdit" />
<DrawerHeader :header="$t('machine.cmdConfig')" :back="onCancelEdit" />
</template>
<el-form ref="formRef" :model="state.form" :rules="rules" label-width="auto">
@@ -46,7 +47,7 @@
<el-input v-model="form.name"></el-input>
</el-form-item>
<el-form-item prop="cmds" :label="$t('machine.filterCmds')" required>
<el-form-item prop="cmds" :label="$t('machine.filterCmds')">
<el-row>
<el-tag
class="ml-0.5 mt-0.5"
@@ -54,7 +55,7 @@
:key="tag"
closable
:disable-transitions="false"
@close="handleCmdClose(tag)"
@close="onCmdClose(tag)"
type="danger"
>
{{ tag }}
@@ -65,11 +66,11 @@
v-model="state.cmdInputValue"
class="mt-0.5"
size="small"
@keyup.enter="handleCmdInputConfirm"
@blur="handleCmdInputConfirm"
@keyup.enter="onCmdInputConfirm"
@blur="onCmdInputConfirm"
:placeholder="$t('machine.cmdPlaceholder')"
/>
<el-button v-else class="ml-0.5 mt-0.5" size="small" @click="showCmdInput"> + {{ $t('machine.newCmd') }} </el-button>
<el-button v-else class="ml-0.5 mt-0.5" size="small" @click="onShowCmdInput"> + {{ $t('machine.newCmd') }} </el-button>
</el-row>
</el-form-item>
@@ -87,8 +88,8 @@
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button :loading="submiting" @click="cancelEdit">{{ $t('common.cancel') }}</el-button>
<el-button v-auth="'cmdconf:save'" type="primary" :loading="submiting" @click="submitForm">{{ $t('common.confirm') }}</el-button>
<el-button :loading="submiting" @click="onCancelEdit">{{ $t('common.cancel') }}</el-button>
<el-button v-auth="'cmdconf:save'" type="primary" :loading="submiting" @click="onSubmitForm">{{ $t('common.confirm') }}</el-button>
</div>
</template>
</el-drawer>
@@ -143,18 +144,18 @@ const getCmdConfs = async () => {
state.cmdConfs = await cmdConfApi.list.request();
};
const handleCmdClose = (tag: string) => {
const onCmdClose = (tag: string) => {
state.form.cmds.splice(state.form.cmds.indexOf(tag), 1);
};
const showCmdInput = () => {
const onShowCmdInput = () => {
state.inputCmdVisible = true;
nextTick(() => {
cmdInputRef.value!.input!.focus();
});
};
const handleCmdInputConfirm = () => {
const onCmdInputConfirm = () => {
if (state.cmdInputValue) {
state.form.cmds.push(state.cmdInputValue);
}
@@ -162,24 +163,25 @@ const handleCmdInputConfirm = () => {
state.cmdInputValue = '';
};
const openFormDialog = (data: any) => {
const onOpenFormDialog = (data: any) => {
if (!data) {
state.form = { ...DefaultForm };
} else {
state.form = deepClone(data);
state.form.codePaths = data.tags?.map((tag: any) => tag.codePath);
state.form.cmds = data.cmds || [];
}
state.dialogVisible = true;
};
const deleteCmdConf = async (data: any) => {
const onDeleteCmdConf = async (data: any) => {
await useI18nDeleteConfirm(data.name);
await cmdConfApi.delete.request({ id: data.id });
useI18nDeleteSuccessMsg();
getCmdConfs();
};
const cancelEdit = () => {
const onCancelEdit = () => {
state.dialogVisible = false;
// 取消表单的校验
setTimeout(() => {
@@ -188,14 +190,14 @@ const cancelEdit = () => {
}, 200);
};
const submitForm = async () => {
const onSubmitForm = async () => {
try {
await useI18nFormValidate(formRef);
state.submiting = true;
await cmdConfApi.save.request(state.form);
useI18nSaveSuccessMsg();
cancelEdit();
onCancelEdit();
getCmdConfs();
} finally {
state.submiting = false;

View File

@@ -1,20 +1,11 @@
<template>
<div>
<el-dialog :title="title" v-model="dialogVisible" :before-close="cancel" :close-on-click-modal="false" width="38%" :destroy-on-close="true">
<el-form :model="form" ref="mongoForm" :rules="rules" label-width="auto">
<el-dialog :title="title" v-model="dialogVisible" :before-close="onCancel" :close-on-click-modal="false" width="38%" :destroy-on-close="true">
<el-form :model="form" ref="mongoFormRef" :rules="rules" label-width="auto">
<el-tabs v-model="tabActiveName">
<el-tab-pane :label="$t('common.basic')" name="basic">
<el-form-item ref="tagSelectRef" prop="tagCodePaths" :label="$t('tag.relateTag')" required>
<tag-tree-select
@change-tag="
(tagCodePaths) => {
form.tagCodePaths = tagCodePaths;
tagSelectRef.validate();
}
"
multiple
:select-tags="form.tagCodePaths"
/>
<el-form-item prop="tagCodePaths" :label="$t('tag.relateTag')" required>
<tag-tree-select multiple v-model="form.tagCodePaths" />
</el-form-item>
<el-form-item prop="name" :label="$t('common.name')" required>
@@ -41,9 +32,9 @@
<template #footer>
<div class="dialog-footer">
<el-button @click="testConn" :loading="testConnBtnLoading" type="success">{{ $t('ac.testConn') }}</el-button>
<el-button @click="cancel()">{{ $t('common.cancel') }}</el-button>
<el-button type="primary" :loading="saveBtnLoading" @click="btnOk">{{ $t('common.confirm') }}</el-button>
<el-button @click="onTestConn" :loading="testConnBtnLoading" type="success">{{ $t('ac.testConn') }}</el-button>
<el-button @click="onCancel()">{{ $t('common.cancel') }}</el-button>
<el-button type="primary" :loading="saveBtnLoading" @click="onConfirm">{{ $t('common.confirm') }}</el-button>
</div>
</template>
</el-dialog>
@@ -51,7 +42,7 @@
</template>
<script lang="ts" setup>
import { toRefs, reactive, ref, watchEffect } from 'vue';
import { toRefs, reactive, watchEffect, useTemplateRef } from 'vue';
import { mongoApi } from './api';
import { ElMessage } from 'element-plus';
import TagTreeSelect from '../component/TagTreeSelect.vue';
@@ -63,9 +54,6 @@ import { Rules } from '@/common/rule';
const { t } = useI18n();
const props = defineProps({
visible: {
type: Boolean,
},
mongo: {
type: [Boolean, Object],
},
@@ -74,8 +62,10 @@ const props = defineProps({
},
});
const dialogVisible = defineModel<boolean>('visible', { default: false });
//定义事件
const emit = defineEmits(['update:visible', 'cancel', 'val-change']);
const emit = defineEmits(['cancel', 'val-change']);
const rules = {
tagCodePaths: [Rules.requiredSelect('tag.relateTag')],
@@ -83,11 +73,9 @@ const rules = {
uri: [Rules.requiredInput('mongo.connUrl')],
};
const mongoForm: any = ref(null);
const tagSelectRef: any = ref(null);
const mongoFormRef: any = useTemplateRef('mongoFormRef');
const state = reactive({
dialogVisible: false,
tabActiveName: 'basic',
form: {
id: null,
@@ -100,14 +88,13 @@ const state = reactive({
submitForm: {},
});
const { dialogVisible, tabActiveName, form, submitForm } = toRefs(state);
const { tabActiveName, form, submitForm } = toRefs(state);
const { isFetching: testConnBtnLoading, execute: testConnExec } = mongoApi.testConn.useApi(submitForm);
const { isFetching: saveBtnLoading, execute: saveMongoExec } = mongoApi.saveMongo.useApi(submitForm);
watchEffect(() => {
state.dialogVisible = props.visible;
if (!state.dialogVisible) {
if (!dialogVisible.value) {
return;
}
state.tabActiveName = 'basic';
@@ -116,7 +103,7 @@ watchEffect(() => {
state.form = { ...mongo };
state.form.tagCodePaths = mongo.tags.map((t: any) => t.codePath);
} else {
state.form = { db: 0 } as any;
state.form = { db: 0, tagCodePaths: [] } as any;
}
});
@@ -128,24 +115,24 @@ const getReqForm = () => {
return reqForm;
};
const testConn = async () => {
await useI18nFormValidate(mongoForm);
const onTestConn = async () => {
await useI18nFormValidate(mongoFormRef);
state.submitForm = getReqForm();
await testConnExec();
ElMessage.success(t('ac.connSuccess'));
};
const btnOk = async () => {
await useI18nFormValidate(mongoForm);
const onConfirm = async () => {
await useI18nFormValidate(mongoFormRef);
state.submitForm = getReqForm();
await saveMongoExec();
useI18nSaveSuccessMsg();
emit('val-change', state.form);
cancel();
onCancel();
};
const cancel = () => {
emit('update:visible', false);
const onCancel = () => {
dialogVisible.value = false;
emit('cancel');
};
</script>

View File

@@ -12,7 +12,7 @@
lazy
>
<template #tableHeader>
<el-button type="primary" icon="plus" @click="editMongo(true)" plain>{{ $t('common.create') }}</el-button>
<el-button type="primary" icon="plus" @click="editMongo(false)" plain>{{ $t('common.create') }}</el-button>
<el-button type="danger" icon="delete" :disabled="selectionData.length < 1" @click="deleteMongo" plain>{{ $t('common.delete') }}</el-button>
</template>

View File

@@ -1,22 +1,13 @@
<template>
<div>
<el-drawer :title="title" v-model="dialogVisible" :before-close="cancel" :destroy-on-close="true" :close-on-click-modal="false" size="40%">
<el-drawer :title="title" v-model="dialogVisible" :before-close="onCancel" :destroy-on-close="true" :close-on-click-modal="false" size="40%">
<template #header>
<DrawerHeader :header="title" :back="cancel" />
<DrawerHeader :header="title" :back="onCancel" />
</template>
<el-form :model="form" ref="redisForm" :rules="rules" label-width="auto">
<el-form-item ref="tagSelectRef" prop="tagCodePaths" :label="$t('tag.relateTag')" required>
<tag-tree-select
@change-tag="
(tagCodePaths) => {
form.tagCodePaths = tagCodePaths;
tagSelectRef.validate();
}
"
multiple
:select-tags="form.tagCodePaths"
/>
<el-form :model="form" ref="redisFormRef" :rules="rules" label-width="auto">
<el-form-item prop="tagCodePaths" :label="$t('tag.relateTag')" required>
<tag-tree-select multiple v-model="form.tagCodePaths" />
</el-form-item>
<el-form-item prop="name" :label="$t('common.name')" required>
<el-input v-model.trim="form.name" auto-complete="off"></el-input>
@@ -55,9 +46,9 @@
<template #footer>
<div class="dialog-footer">
<el-button @click="testConn" :loading="testConnBtnLoading" type="success">{{ $t('ac.testConn') }}</el-button>
<el-button @click="cancel()">{{ $t('common.cancel') }}</el-button>
<el-button type="primary" :loading="saveBtnLoading" @click="btnOk">{{ $t('common.confirm') }}</el-button>
<el-button @click="onTestConn" :loading="testConnBtnLoading" type="success">{{ $t('ac.testConn') }}</el-button>
<el-button @click="onCancel()">{{ $t('common.cancel') }}</el-button>
<el-button type="primary" :loading="saveBtnLoading" @click="onConfirm">{{ $t('common.confirm') }}</el-button>
</div>
</template>
</el-drawer>
@@ -65,7 +56,7 @@
</template>
<script lang="ts" setup>
import { toRefs, reactive, ref, watch } from 'vue';
import { toRefs, reactive, watch, useTemplateRef } from 'vue';
import { redisApi } from './api';
import { ElMessage } from 'element-plus';
import TagTreeSelect from '../component/TagTreeSelect.vue';
@@ -78,9 +69,6 @@ import { Rules } from '@/common/rule';
const { t } = useI18n();
const props = defineProps({
visible: {
type: Boolean,
},
redis: {
type: [Boolean, Object],
},
@@ -89,7 +77,9 @@ const props = defineProps({
},
});
const emit = defineEmits(['update:visible', 'val-change', 'cancel']);
const dialogVisible = defineModel<boolean>('visible', { default: false });
const emit = defineEmits(['val-change', 'cancel']);
const rules = {
tagCodePaths: [Rules.requiredSelect('tag.relateTag')],
@@ -99,12 +89,9 @@ const rules = {
mode: [Rules.requiredSelect('mode')],
};
const redisForm: any = ref(null);
const tagSelectRef: any = ref(null);
const redisFormRef: any = useTemplateRef('redisFormRef');
const state = reactive({
dialogVisible: false,
tabActiveName: 'basic',
form: {
id: null,
code: '',
@@ -124,30 +111,26 @@ const state = reactive({
pwd: '',
});
const { dialogVisible, tabActiveName, form, submitForm, dbList } = toRefs(state);
const { form, submitForm, dbList } = toRefs(state);
const { isFetching: testConnBtnLoading, execute: testConnExec } = redisApi.testConn.useApi(submitForm);
const { isFetching: saveBtnLoading, execute: saveRedisExec } = redisApi.saveRedis.useApi(submitForm);
watch(
() => props.visible,
() => {
state.dialogVisible = props.visible;
if (!state.dialogVisible) {
return;
}
state.tabActiveName = 'basic';
const redis: any = props.redis;
if (redis) {
state.form = { ...redis };
state.form.tagCodePaths = redis.tags.map((t: any) => t.codePath);
convertDb(state.form.db);
} else {
state.form = { db: '0', tagCodePaths: [] } as any;
state.dbList = [0];
}
watch(dialogVisible, () => {
if (!dialogVisible.value) {
return;
}
);
const redis: any = props.redis;
if (redis) {
state.form = { ...redis };
state.form.tagCodePaths = redis.tags.map((t: any) => t.codePath);
convertDb(state.form.db);
} else {
state.form = { db: '0', tagCodePaths: [] } as any;
state.dbList = [0];
}
});
const convertDb = (db: string) => {
state.dbList = db.split(',').map((x) => Number.parseInt(x));
@@ -172,24 +155,24 @@ const getReqForm = async () => {
return reqForm;
};
const testConn = async () => {
await useI18nFormValidate(redisForm);
const onTestConn = async () => {
await useI18nFormValidate(redisFormRef);
state.submitForm = await getReqForm();
await testConnExec();
ElMessage.success(t('ac.connSuccess'));
};
const btnOk = async () => {
await useI18nFormValidate(redisForm);
const onConfirm = async () => {
await useI18nFormValidate(redisFormRef);
state.submitForm = await getReqForm();
await saveRedisExec();
useI18nSaveSuccessMsg();
emit('val-change', state.form);
cancel();
onCancel();
};
const cancel = () => {
emit('update:visible', false);
const onCancel = () => {
dialogVisible.value = false;
emit('cancel');
};
</script>

View File

@@ -8,7 +8,7 @@
:columns="state.columns"
>
<template #tableHeader>
<el-button v-auth="'authcert:save'" type="primary" icon="plus" @click="edit(false)">{{ $t('common.create') }}</el-button>
<el-button v-auth="'authcert:save'" type="primary" icon="plus" @click="onEdit(false)">{{ $t('common.create') }}</el-button>
</template>
<template #resourceCode="{ data }">
@@ -20,9 +20,9 @@
</template>
<template #action="{ data }">
<el-button v-auth="'authcert:save'" @click="edit(data)" type="primary" link>{{ $t('common.edit') }}</el-button>
<el-button v-auth="'authcert:save'" @click="onEdit(data)" type="primary" link>{{ $t('common.edit') }}</el-button>
<el-button v-auth="'authcert:del'" @click="deleteAc(data)" type="danger" link>{{ $t('common.delete') }}</el-button>
<el-button v-auth="'authcert:del'" @click="onDeleteAc(data)" type="danger" link>{{ $t('common.delete') }}</el-button>
</template>
</page-table>
@@ -30,7 +30,7 @@
:title="editor.title"
v-model:visible="editor.visible"
:auth-cert="editor.authcert"
@confirm="confirmSave"
@confirm="onConfirmSave"
@cancel="editor.authcert = {}"
:disable-type="state.disableAuthCertType"
:disable-ciphertext-type="state.disableAuthCertCiphertextType"
@@ -102,7 +102,7 @@ const search = async () => {
pageTableRef.value.search();
};
const edit = (data: any) => {
const onEdit = (data: any) => {
state.disableAuthCertType = [];
state.disableAuthCertCiphertextType = [];
if (data) {
@@ -128,14 +128,14 @@ const edit = (data: any) => {
state.editor.visible = true;
};
const confirmSave = async (authCert: any) => {
const onConfirmSave = async (authCert: any) => {
await resourceAuthCertApi.save.request(authCert);
useI18nSaveSuccessMsg();
state.editor.visible = false;
search();
};
const deleteAc = async (data: any) => {
const onDeleteAc = async (data: any) => {
try {
await useI18nDeleteConfirm(data.name);
await resourceAuthCertApi.delete.request({ id: data.id });

View File

@@ -10,7 +10,7 @@
v-auth="'tag:save'"
type="primary"
icon="plus"
@click="showSaveTagDialog(null)"
@click="onShowSaveTagDialog(null)"
></el-button>
</div>
<div>
@@ -33,15 +33,15 @@
highlight-current
:props="props"
:data="data"
@node-expand="handleNodeExpand"
@node-collapse="handleNodeCollapse"
@node-contextmenu="nodeContextmenu"
@node-click="treeNodeClick"
@node-expand="onNodeExpand"
@node-collapse="onNodeCollapse"
@node-contextmenu="onNodeContextmenu"
@node-click="onTreeNodeClick"
:default-expanded-keys="defaultExpandedKeys"
draggable
:allow-drop="allowDrop"
:allow-drag="allowDrag"
@node-drop="handleDrop"
@node-drop="onNodeDrop"
:expand-on-click-node="false"
:filter-node-method="filterNode"
>
@@ -67,7 +67,7 @@
<Pane min-size="40" size="70">
<div class="ml-2 h-full">
<el-tabs class="h-full" @tab-change="tabChange" v-model="state.activeTabName" v-if="currentTag">
<el-tabs class="h-full" @tab-change="onTabChange" v-model="state.activeTabName" v-if="currentTag">
<el-tab-pane :label="$t('common.detail')" :name="TagDetail">
<el-descriptions :column="2" border>
<el-descriptions-item :label="$t('common.type')">
@@ -137,7 +137,7 @@
</Pane>
</Splitpanes>
<el-dialog width="500px" :title="saveTabDialog.title" :before-close="cancelSaveTag" v-model="saveTabDialog.visible">
<el-dialog width="500px" :title="saveTabDialog.title" :before-close="onCancelSaveTag" v-model="saveTabDialog.visible">
<el-form ref="tagForm" :rules="rules" :model="saveTabDialog.form" label-width="auto">
<el-form-item prop="code" :label="$t('tag.code')" required>
<el-input :disabled="saveTabDialog.form.id ? true : false" v-model="saveTabDialog.form.code" auto-complete="off"></el-input>
@@ -151,8 +151,8 @@
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="cancelSaveTag()">{{ $t('common.cancel') }}</el-button>
<el-button @click="saveTag" type="primary">{{ $t('common.confirm') }}</el-button>
<el-button @click="onCancelSaveTag()">{{ $t('common.cancel') }}</el-button>
<el-button @click="onSaveTag" type="primary">{{ $t('common.confirm') }}</el-button>
</div>
</template>
</el-dialog>
@@ -223,7 +223,7 @@ const contextmenuAdd = new ContextmenuItem('addTag', 'tag.createSubTag')
// 非标签类型不可添加子标签
return data.type != TagResourceTypeEnum.Tag.value || (data.children && data.children?.[0].type != TagResourceTypeEnum.Tag.value);
})
.withOnClick((data: any) => showSaveTagDialog(data));
.withOnClick((data: any) => onShowSaveTagDialog(data));
const contextmenuEdit = new ContextmenuItem('edit', 'common.edit')
.withIcon('edit')
@@ -231,7 +231,7 @@ const contextmenuEdit = new ContextmenuItem('edit', 'common.edit')
.withHideFunc((data: any) => {
return data.type != TagResourceTypeEnum.Tag.value;
})
.withOnClick((data: any) => showEditTagDialog(data));
.withOnClick((data: any) => onShowEditTagDialog(data));
const contextmenuDel = new ContextmenuItem('delete', 'common.delete')
.withIcon('delete')
@@ -240,7 +240,7 @@ const contextmenuDel = new ContextmenuItem('delete', 'common.delete')
// 存在子标签,则不允许删除
return data.children || data.type != TagResourceTypeEnum.Tag.value;
})
.withOnClick((data: any) => deleteTag(data));
.withOnClick((data: any) => onDeleteTag(data));
const state = reactive({
data: [],
@@ -364,7 +364,7 @@ const allowDrag = (node: any) => {
);
};
const handleDrop = async (draggingNode: any, dropNode: any) => {
const onNodeDrop = async (draggingNode: any, dropNode: any) => {
const draggingData = draggingNode.data;
const dropData = dropNode.data;
@@ -378,7 +378,7 @@ const handleDrop = async (draggingNode: any, dropNode: any) => {
}
};
const tabChange = () => {
const onTabChange = () => {
setNowTabData();
};
@@ -420,20 +420,21 @@ const getDetail = async (id: number) => {
};
// 树节点右击事件
const nodeContextmenu = (event: any, data: any) => {
const onNodeContextmenu = (event: any, data: any) => {
const { clientX, clientY } = event;
state.contextmenu.dropdown.x = clientX;
state.contextmenu.dropdown.y = clientY;
contextmenuRef.value.openContextmenu(data);
};
const treeNodeClick = async (data: any) => {
const onTreeNodeClick = async (data: any) => {
state.currentTag = await getDetail(data.id);
state.activeTabName = TagDetail;
// 关闭可能存在的右击菜单
contextmenuRef.value.closeContextmenu();
};
const showSaveTagDialog = (data: any) => {
const onShowSaveTagDialog = (data: any) => {
if (data) {
state.saveTabDialog.form.pid = data.id;
state.saveTabDialog.title = t('tag.createSubTagTitle', { codePath: data.codePath });
@@ -443,7 +444,7 @@ const showSaveTagDialog = (data: any) => {
state.saveTabDialog.visible = true;
};
const showEditTagDialog = (data: any) => {
const onShowEditTagDialog = (data: any) => {
state.saveTabDialog.form.id = data.id;
state.saveTabDialog.form.code = data.code;
state.saveTabDialog.form.name = data.name;
@@ -452,23 +453,23 @@ const showEditTagDialog = (data: any) => {
state.saveTabDialog.visible = true;
};
const saveTag = async () => {
const onSaveTag = async () => {
await useI18nFormValidate(tagForm);
const form = state.saveTabDialog.form;
await tagApi.saveTagTree.request(form);
useI18nSaveSuccessMsg();
search();
cancelSaveTag();
onCancelSaveTag();
state.currentTag = null;
};
const cancelSaveTag = () => {
const onCancelSaveTag = () => {
state.saveTabDialog.visible = false;
state.saveTabDialog.form = {} as any;
tagForm.value.resetFields();
};
const deleteTag = async (data: any) => {
const onDeleteTag = async (data: any) => {
await useI18nDeleteConfirm(data.codePath);
await tagApi.delTagTree.request({ id: data.id });
useI18nDeleteSuccessMsg();
@@ -476,7 +477,7 @@ const deleteTag = async (data: any) => {
};
// 节点被展开时触发的事件
const handleNodeExpand = (data: any, node: any) => {
const onNodeExpand = (data: any, node: any) => {
const id: any = node.data.id;
if (!state.defaultExpandedKeys.includes(id)) {
state.defaultExpandedKeys.push(id);
@@ -484,7 +485,7 @@ const handleNodeExpand = (data: any, node: any) => {
};
// 关闭节点
const handleNodeCollapse = (data: any, node: any) => {
const onNodeCollapse = (data: any, node: any) => {
removeDeafultExpandId(node.data.id);
let childNodes = node.childNodes;
@@ -493,7 +494,7 @@ const handleNodeCollapse = (data: any, node: any) => {
removeDeafultExpandId(cn.data.id);
}
// 递归删除展开的子节点节点id
handleNodeCollapse(data, cn);
onNodeCollapse(data, cn);
}
};

View File

@@ -10,8 +10,8 @@
:columns="columns"
>
<template #tableHeader>
<el-button v-auth="'team:save'" type="primary" icon="plus" @click="showSaveTeamDialog(false)">{{ $t('common.create') }}</el-button>
<el-button v-auth="'team:del'" :disabled="selectionData.length < 1" @click="deleteTeam()" type="danger" icon="delete">
<el-button v-auth="'team:save'" type="primary" icon="plus" @click="onShowSaveTeamDialog(false)">{{ $t('common.create') }}</el-button>
<el-button v-auth="'team:del'" :disabled="selectionData.length < 1" @click="onDeleteTeam()" type="danger" icon="delete">
{{ $t('common.delete') }}
</el-button>
</template>
@@ -23,22 +23,22 @@
<template #validityDate="{ data }"> {{ formatDate(data.validityStartDate) }} ~ {{ formatDate(data.validityEndDate) }} </template>
<template #action="{ data }">
<el-button @click.prevent="showMembers(data)" link type="primary">{{ $t('team.member') }}</el-button>
<el-button @click.prevent="onShowMembers(data)" link type="primary">{{ $t('team.member') }}</el-button>
<el-button v-auth="'team:save'" @click.prevent="showSaveTeamDialog(data)" link type="warning">{{ $t('common.edit') }}</el-button>
<el-button v-auth="'team:save'" @click.prevent="onShowSaveTeamDialog(data)" link type="warning">{{ $t('common.edit') }}</el-button>
</template>
</page-table>
<el-drawer
:title="addTeamDialog.title"
v-model="addTeamDialog.visible"
:before-close="cancelSaveTeam"
:before-close="onCancelSaveTeam"
:destroy-on-close="true"
:close-on-click-modal="false"
size="40%"
>
<template #header>
<DrawerHeader :header="addTeamDialog.title" :back="cancelSaveTeam" />
<DrawerHeader :header="addTeamDialog.title" :back="onCancelSaveTeam" />
</template>
<el-form ref="teamForm" :model="addTeamDialog.form" :rules="teamFormRules" label-width="auto">
@@ -69,8 +69,8 @@
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="cancelSaveTeam()">{{ $t('common.cancel') }}</el-button>
<el-button @click="saveTeam" type="primary">{{ $t('common.confirm') }}</el-button>
<el-button @click="onCancelSaveTeam()">{{ $t('common.cancel') }}</el-button>
<el-button @click="onSaveTeam" type="primary">{{ $t('common.confirm') }}</el-button>
</div>
</template>
</el-drawer>
@@ -85,22 +85,22 @@
:columns="showMemDialog.columns"
>
<template #tableHeader>
<el-button v-auth="'team:member:save'" @click="showAddMemberDialog()" type="primary" icon="plus">{{ $t('common.add') }}</el-button>
<el-button v-auth="'team:member:save'" @click="onShowAddMemberDialog()" type="primary" icon="plus">{{ $t('common.add') }}</el-button>
</template>
<template #action="{ data }">
<el-button v-auth="'team:member:del'" @click="deleteMember(data)" type="danger" link icon="delete"></el-button>
<el-button v-auth="'team:member:del'" @click="onDeleteMember(data)" type="danger" link icon="delete"></el-button>
</template>
</page-table>
<el-dialog width="400px" :title="$t('team.addMember')" :before-close="cancelAddMember" v-model="showMemDialog.addVisible">
<el-dialog width="400px" :title="$t('team.addMember')" :before-close="onCancelAddMember" v-model="showMemDialog.addVisible">
<el-form :model="showMemDialog.memForm" label-width="auto">
<AccountSelectFormItem v-model="showMemDialog.memForm.accountIds" multiple focus />
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="cancelAddMember()">{{ $t('common.cancel') }}</el-button>
<el-button @click="addMember" type="primary">{{ $t('common.confirm') }}</el-button>
<el-button @click="onCancelAddMember()">{{ $t('common.cancel') }}</el-button>
<el-button @click="onAddMember" type="primary">{{ $t('common.confirm') }}</el-button>
</div>
</template>
</el-dialog>
@@ -207,7 +207,7 @@ const search = async () => {
pageTableRef.value.search();
};
const showSaveTeamDialog = async (data: any) => {
const onShowSaveTeamDialog = async (data: any) => {
if (data) {
state.addTeamDialog.title = useI18nEditTitle('team.team');
state.addTeamDialog.form.id = data.id;
@@ -225,7 +225,7 @@ const showSaveTeamDialog = async (data: any) => {
state.addTeamDialog.visible = true;
};
const saveTeam = async () => {
const onSaveTeam = async () => {
await useI18nFormValidate(teamForm);
const form = state.addTeamDialog.form;
form.validityStartDate = formatDate(form.validityDate[0]);
@@ -233,10 +233,10 @@ const saveTeam = async () => {
await tagApi.saveTeam.request(form);
useI18nSaveSuccessMsg();
search();
cancelSaveTeam();
onCancelSaveTeam();
};
const cancelSaveTeam = () => {
const onCancelSaveTeam = () => {
state.addTeamDialog.visible = false;
teamForm.value.resetFields();
setTimeout(() => {
@@ -244,7 +244,7 @@ const cancelSaveTeam = () => {
}, 500);
};
const deleteTeam = async () => {
const onDeleteTeam = async () => {
await useI18nDeleteConfirm(state.selectionData.map((x: any) => x.name).join('、'));
await tagApi.delTeam.request({ id: state.selectionData.map((x: any) => x.id).join(',') });
useI18nDeleteSuccessMsg();
@@ -253,13 +253,13 @@ const deleteTeam = async () => {
/********** 团队成员相关 ***********/
const showMembers = async (team: any) => {
const onShowMembers = async (team: any) => {
state.showMemDialog.query.teamId = team.id;
state.showMemDialog.visible = true;
state.showMemDialog.title = t('team.teamMember', { teamName: team.name });
};
const deleteMember = async (data: any) => {
const onDeleteMember = async (data: any) => {
await tagApi.delTeamMem.request(data);
useI18nOperateSuccessMsg();
// 重新赋值成员列表
@@ -273,11 +273,11 @@ const setMemebers = async () => {
showMemPageTableRef.value.search();
};
const showAddMemberDialog = () => {
const onShowAddMemberDialog = () => {
state.showMemDialog.addVisible = true;
};
const addMember = async () => {
const onAddMember = async () => {
const memForm = state.showMemDialog.memForm;
memForm.teamId = state.showMemDialog.query.teamId;
notBlank(memForm.accountIds, t('team.selectAccountTips'));
@@ -285,10 +285,10 @@ const addMember = async () => {
await tagApi.saveTeamMem.request(memForm);
useI18nSaveSuccessMsg();
setMemebers();
cancelAddMember();
onCancelAddMember();
};
const cancelAddMember = () => {
const onCancelAddMember = () => {
state.showMemDialog.memForm = {} as any;
state.showMemDialog.addVisible = false;
};

View File

@@ -1,7 +1,7 @@
<template>
<div>
<el-dialog :title="title" v-model="visible" :before-close="cancel" :show-close="false" width="600px" :destroy-on-close="true">
<el-form :model="form" ref="accountForm" :rules="rules" label-width="auto">
<el-dialog :title="title" v-model="visible" :before-close="onCancel" :show-close="false" width="600px" :destroy-on-close="true">
<el-form :model="form" ref="accountFormRef" :rules="rules" label-width="auto">
<el-form-item prop="name" :label="$t('system.account.name')">
<el-input v-model.trim="form.name" auto-complete="off" clearable></el-input>
</el-form-item>
@@ -49,15 +49,15 @@
</el-form>
<template #footer>
<el-button @click="cancel()">{{ $t('common.cancel') }}</el-button>
<el-button type="primary" :loading="saveBtnLoading" @click="btnOk">{{ $t('common.confirm') }}</el-button>
<el-button @click="onCancel()">{{ $t('common.cancel') }}</el-button>
<el-button type="primary" :loading="saveBtnLoading" @click="onConfirm">{{ $t('common.confirm') }}</el-button>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup>
import { toRefs, reactive, watch, ref } from 'vue';
import { toRefs, reactive, watch, useTemplateRef } from 'vue';
import { accountApi } from '../api';
import { randomPassword } from '@/common/utils/string';
import { useI18nFormValidate, useI18nSaveSuccessMsg } from '@/hooks/useI18n';
@@ -77,7 +77,7 @@ const emit = defineEmits(['cancel', 'val-change']);
const visible = defineModel<boolean>('visible', { default: false });
const accountForm: any = ref(null);
const accountFormRef: any = useTemplateRef('accountFormRef');
const rules = {
name: [Rules.requiredInput('system.account.name')],
@@ -123,16 +123,16 @@ watch(props, (newValue: any) => {
}
});
const btnOk = async () => {
await useI18nFormValidate(accountForm);
const onConfirm = async () => {
await useI18nFormValidate(accountFormRef);
await saveAccountExec();
useI18nSaveSuccessMsg();
emit('val-change', state.form);
//重置表单域
accountForm.value.resetFields();
accountFormRef.value.resetFields();
};
const cancel = () => {
const onCancel = () => {
visible.value = false;
emit('cancel');
};

View File

@@ -10,24 +10,24 @@
:columns="columns"
>
<template #tableHeader>
<el-button v-auth="perms.addAccount" type="primary" icon="plus" @click="editAccount(false)">{{ $t('common.create') }}</el-button>
<el-button v-auth="perms.delAccount" :disabled="state.selectionData.length < 1" @click="deleteAccount()" type="danger" icon="delete">
<el-button v-auth="perms.addAccount" type="primary" icon="plus" @click="onEditAccount(false)">{{ $t('common.create') }}</el-button>
<el-button v-auth="perms.delAccount" :disabled="state.selectionData.length < 1" @click="onDeleteAccount()" type="danger" icon="delete">
{{ $t('common.delete') }}
</el-button>
</template>
<template #action="{ data }">
<el-button link v-if="actionBtns[perms.addAccount]" @click="editAccount(data)" type="primary">{{ $t('common.edit') }}</el-button>
<el-button link v-if="actionBtns[perms.addAccount]" @click="onEditAccount(data)" type="primary">{{ $t('common.edit') }}</el-button>
<el-button link v-if="actionBtns[perms.saveAccountRole]" @click="showRoleEdit(data)" type="success">
<el-button link v-if="actionBtns[perms.saveAccountRole]" @click="onShowRoleEdit(data)" type="success">
{{ $t('system.account.roleAllocation') }}
</el-button>
<el-button link v-if="actionBtns[perms.changeAccountStatus] && data.status == 1" @click="changeStatus(data)" type="danger">
<el-button link v-if="actionBtns[perms.changeAccountStatus] && data.status == 1" @click="onChangeStatus(data)" type="danger">
{{ $t('common.disable') }}
</el-button>
<el-button link v-if="actionBtns[perms.changeAccountStatus] && data.status == -1" type="success" @click="changeStatus(data)">
<el-button link v-if="actionBtns[perms.changeAccountStatus] && data.status == -1" type="success" @click="onChangeStatus(data)">
{{ $t('common.enable') }}
</el-button>
@@ -35,7 +35,7 @@
link
v-if="actionBtns[perms.addAccount]"
:disabled="!data.otpSecret || data.otpSecret == '-'"
@click="resetOtpSecret(data)"
@click="onResetOtpSecret(data)"
type="warning"
>
{{ $t('system.account.resetOtp') }}
@@ -55,8 +55,8 @@
</el-table>
</el-dialog>
<role-allocation v-model:visible="roleDialog.visible" :account="roleDialog.account" @cancel="cancel()" />
<account-edit :title="accountDialog.title" v-model:visible="accountDialog.visible" v-model:account="accountDialog.data" @val-change="valChange()" />
<role-allocation v-model:visible="roleDialog.visible" :account="roleDialog.account" @cancel="onCancel()" />
<account-edit :title="accountDialog.title" v-model:visible="accountDialog.visible" v-model:account="accountDialog.data" @val-change="onValChange()" />
</div>
</template>
@@ -150,7 +150,7 @@ const search = async () => {
pageTableRef.value.search();
};
const changeStatus = async (row: any) => {
const onChangeStatus = async (row: any) => {
let id = row.id;
let status = row.status == AccountStatusEnum.Disable.value ? AccountStatusEnum.Enable.value : AccountStatusEnum.Disable.value;
await accountApi.changeStatus.request({
@@ -161,7 +161,7 @@ const changeStatus = async (row: any) => {
search();
};
const resetOtpSecret = async (row: any) => {
const onResetOtpSecret = async (row: any) => {
let id = row.id;
await accountApi.resetOtpSecret.request({
id,
@@ -170,7 +170,7 @@ const resetOtpSecret = async (row: any) => {
row.otpSecret = '-';
};
const editAccount = (data: any) => {
const onEditAccount = (data: any) => {
if (!data) {
state.accountDialog.title = useI18nCreateTitle('personal.accountInfo');
state.accountDialog.data = null;
@@ -181,22 +181,22 @@ const editAccount = (data: any) => {
state.accountDialog.visible = true;
};
const showRoleEdit = (data: any) => {
const onShowRoleEdit = (data: any) => {
state.roleDialog.visible = true;
state.roleDialog.account = data;
};
const cancel = () => {
const onCancel = () => {
state.roleDialog.visible = false;
state.roleDialog.account = null;
};
const valChange = () => {
const onValChange = () => {
state.accountDialog.visible = false;
search();
};
const deleteAccount = async () => {
const onDeleteAccount = async () => {
await useI18nDeleteConfirm(state.selectionData.map((x: any) => x.username).join('、'));
await accountApi.del.request({ id: state.selectionData.map((x: any) => x.id).join(',') });
useI18nDeleteSuccessMsg();

View File

@@ -4,7 +4,7 @@
@open="searchAccountRoles()"
:title="account == null ? '' : $t('system.account.allocateRoleTitle', { name: account.username })"
v-model="dialogVisible"
:before-close="cancel"
:before-close="onCancel"
:destroy-on-close="true"
width="55%"
>
@@ -20,11 +20,11 @@
lazy
>
<template #tableHeader>
<el-button @click="showResources" icon="view" type="primary" link>{{ $t('system.account.menuAndPermission') }}</el-button>
<el-button @click="onShowResources" icon="view" type="primary" link>{{ $t('system.account.menuAndPermission') }}</el-button>
</template>
<template #action="{ data }">
<el-button v-auth="'account:saveRoles'" type="danger" @click="relateRole(-1, data.roleId)" icon="delete" link plain>
<el-button v-auth="'account:saveRoles'" type="danger" @click="onRelateRole(-1, data.roleId)" icon="delete" link plain>
{{ $t('system.account.remove') }}
</el-button>
</template>
@@ -44,7 +44,7 @@
<template #action="{ data }">
<el-button
v-auth="'account:saveRoles'"
@click="relateRole(1, data.id)"
@click="onRelateRole(1, data.id)"
:disabled="data.code?.indexOf('COMMON') == 0 || data.status == RoleStatusEnum.Disable.value"
type="success"
icon="CirclePlus"
@@ -176,7 +176,7 @@ const onTabChange = () => {
searchAccountRoles();
};
const relateRole = async (relateType: number, roleId: number) => {
const onRelateRole = async (relateType: number, roleId: number) => {
await accountApi.saveRole.request({
id: props.account!.id,
roleId,
@@ -191,7 +191,7 @@ const relateRole = async (relateType: number, roleId: number) => {
}
};
const showResources = async () => {
const onShowResources = async () => {
let showResourceDialog = state.showResourceDialog;
showResourceDialog.title = t('system.account.userMenuTitle', { name: props.account?.username });
showResourceDialog.resources = [];
@@ -204,7 +204,7 @@ const showResources = async () => {
/**
* 取消
*/
const cancel = () => {
const onCancel = () => {
state.unRelatedQuery.pageNum = 1;
state.unRelatedQuery.name = null;
state.unRelatedQuery.code = null;

View File

@@ -1,11 +1,11 @@
<template>
<div>
<el-drawer :title="title" v-model="visible" :show-close="false" :before-close="cancel" size="1000px" :destroy-on-close="true">
<el-drawer :title="title" v-model="visible" :show-close="false" :before-close="onCancel" size="1000px" :destroy-on-close="true">
<template #header>
<DrawerHeader :header="title" :back="cancel" />
<DrawerHeader :header="title" :back="onCancel" />
</template>
<el-form ref="configForm" :model="form" :rules="rules" label-width="auto">
<el-form ref="configFormRef" :model="form" :rules="rules" label-width="auto">
<el-form-item prop="name" :label="$t('system.sysconf.confItem')" required>
<el-input v-model="form.name"></el-input>
</el-form-item>
@@ -35,8 +35,8 @@
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="cancel()">{{ $t('common.cancel') }}</el-button>
<el-button type="primary" :loading="saveBtnLoading" @click="btnOk">{{ $t('common.confirm') }}</el-button>
<el-button @click="onCancel()">{{ $t('common.cancel') }}</el-button>
<el-button type="primary" :loading="saveBtnLoading" @click="onConfirm">{{ $t('common.confirm') }}</el-button>
</div>
</template>
</el-drawer>
@@ -44,7 +44,7 @@
</template>
<script lang="ts" setup>
import { ref, toRefs, reactive, watch } from 'vue';
import { toRefs, reactive, watch, useTemplateRef } from 'vue';
import { configApi, accountApi } from '../api';
import { DynamicFormEdit } from '@/components/dynamic-form';
import DrawerHeader from '@/components/drawer-header/DrawerHeader.vue';
@@ -70,7 +70,7 @@ const visible = defineModel<boolean>('visible', { default: false });
//定义事件
const emit = defineEmits(['cancel', 'val-change']);
const configForm: any = ref(null);
const configFormRef: any = useTemplateRef('configFormRef');
const state = reactive({
params: [] as any,
@@ -116,7 +116,7 @@ watch(visible, () => {
}
});
const cancel = () => {
const onCancel = () => {
visible.value = false;
// 若父组件有取消事件,则调用
emit('cancel');
@@ -131,8 +131,8 @@ const getAccount = (username: any) => {
}
};
const btnOk = async () => {
await useI18nFormValidate(configForm);
const onConfirm = async () => {
await useI18nFormValidate(configFormRef);
if (state.params) {
state.form.params = JSON.stringify(state.params);
}
@@ -144,7 +144,7 @@ const btnOk = async () => {
await saveConfigExec();
emit('val-change', state.form);
cancel();
onCancel();
};
</script>
<style lang="scss"></style>

View File

@@ -9,7 +9,7 @@
:data-handler-fn="handleData"
>
<template #tableHeader>
<el-button v-auth="perms.saveConfig" type="primary" icon="plus" @click="editConfig(false)">{{ $t('common.create') }}</el-button>
<el-button v-auth="perms.saveConfig" type="primary" icon="plus" @click="onEditConfig(false)">{{ $t('common.create') }}</el-button>
</template>
<template #status="{ data }">
@@ -19,11 +19,11 @@
<template #action="{ data }">
<el-button :disabled="data.status == -1" type="warning" @click="showSetConfigDialog(data)" link>{{ $t('system.sysconf.conf') }}</el-button>
<el-button v-if="actionBtns[perms.saveConfig]" @click="editConfig(data)" type="primary" link>{{ $t('common.edit') }}</el-button>
<el-button v-if="actionBtns[perms.saveConfig]" @click="onEditConfig(data)" type="primary" link>{{ $t('common.edit') }}</el-button>
</template>
</page-table>
<el-dialog @close="closeSetConfigDialog" :title="$t('system.sysconf.confItemSetting')" v-model="paramsDialog.visible" width="700px">
<el-dialog @close="onCloseSetConfigDialog" :title="$t('system.sysconf.confItemSetting')" v-model="paramsDialog.visible" width="700px">
<dynamic-form
ref="paramsFormRef"
v-if="paramsDialog.paramsFormItem.length > 0"
@@ -39,13 +39,13 @@
<template #footer>
<span class="dialog-footer">
<el-button @click="closeSetConfigDialog()">{{ $t('common.cancel') }}</el-button>
<el-button @click="onCloseSetConfigDialog()">{{ $t('common.cancel') }}</el-button>
<el-button v-auth="'config:save'" type="primary" @click="setConfig()">{{ $t('common.confirm') }}</el-button>
</span>
</template>
</el-dialog>
<config-edit :title="$t(configEdit.title)" v-model:visible="configEdit.visible" :data="configEdit.config" @val-change="configEditChange" />
<config-edit :title="$t(configEdit.title)" v-model:visible="configEdit.visible" :data="configEdit.config" @val-change="onConfigEditChange" />
</div>
</template>
@@ -143,7 +143,7 @@ const showSetConfigDialog = (row: any) => {
state.paramsDialog.visible = true;
};
const closeSetConfigDialog = () => {
const onCloseSetConfigDialog = () => {
state.paramsDialog.visible = false;
setTimeout(() => {
state.paramsDialog.config = {};
@@ -182,7 +182,7 @@ const setConfig = async () => {
value: paramsValue,
});
useI18nSaveSuccessMsg();
closeSetConfigDialog();
onCloseSetConfigDialog();
search();
};
@@ -195,12 +195,12 @@ const hasParam = (paramKey: string, paramItems: any) => {
return false;
};
const configEditChange = () => {
const onConfigEditChange = () => {
useI18nSaveSuccessMsg();
search();
};
const editConfig = (data: any) => {
const onEditConfig = (data: any) => {
if (data) {
state.configEdit.title = 'common.edit';
state.configEdit.config = data;

View File

@@ -1,7 +1,7 @@
<template>
<div>
<el-dialog :title="title" :destroy-on-close="true" v-model="visible" width="800px">
<el-form :model="form" :inline="true" ref="menuForm" :rules="rules" label-width="auto">
<el-form :model="form" :inline="true" ref="menuFormRef" :rules="rules" label-width="auto">
<el-row :gutter="35">
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
<el-form-item class="!w-full" prop="type" :label="$t('common.type')" required>
@@ -71,7 +71,7 @@
prop="meta.linkType"
:tooltip="$t('system.menu.externalLinkTips')"
>
<el-select class="!w-full" @change="changeLinkType" v-model="form.meta.linkType">
<el-select class="!w-full" @change="onChangeLinkType" v-model="form.meta.linkType">
<el-option :key="0" :label="$t('system.menu.no')" :value="0"> </el-option>
<el-option :key="1" :label="$t('system.menu.inline')" :value="1"> </el-option>
<el-option :key="2" :label="$t('system.menu.externalLink')" :value="2"> </el-option>
@@ -87,17 +87,15 @@
</el-form>
<template #footer>
<div>
<el-button @click="cancel()">{{ $t('common.cancel') }}</el-button>
<el-button type="primary" :loading="saveBtnLoading" @click="btnOk">{{ $t('common.confirm') }}</el-button>
</div>
<el-button @click="onCancel()">{{ $t('common.cancel') }}</el-button>
<el-button type="primary" :loading="saveBtnLoading" @click="onConfirm">{{ $t('common.confirm') }}</el-button>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup>
import { ref, toRefs, reactive, watchEffect } from 'vue';
import { toRefs, reactive, watchEffect, useTemplateRef } from 'vue';
import { ElMessage } from 'element-plus';
import { resourceApi } from '../api';
import { ResourceTypeEnum } from '../enums';
@@ -107,6 +105,7 @@ import { useI18n } from 'vue-i18n';
import EnumSelect from '@/components/enumselect/EnumSelect.vue';
import FormItemTooltip from '@/components/form/FormItemTooltip.vue';
import { Rules } from '@/common/rule';
import { useI18nFormValidate } from '@/hooks/useI18n';
const { t } = useI18n();
@@ -127,7 +126,7 @@ const visible = defineModel<boolean>('visible', { default: false });
//定义事件
const emit = defineEmits(['cancel', 'val-change']);
const menuForm: any = ref(null);
const menuFormRef: any = useTemplateRef('menuFormRef');
const menuTypeValue = ResourceTypeEnum.Menu.value;
@@ -208,17 +207,12 @@ watchEffect(() => {
});
// 改变外链类型
const changeLinkType = () => {
const onChangeLinkType = () => {
state.form.meta.component = '';
};
const btnOk = async () => {
try {
await menuForm.value.validate();
} catch (e: any) {
ElMessage.error(t('common.formValidationError'));
return false;
}
const onConfirm = async () => {
await useI18nFormValidate(menuFormRef);
const submitForm = { ...state.form };
if (submitForm.type == 1) {
@@ -233,7 +227,7 @@ const btnOk = async () => {
emit('val-change', submitForm);
ElMessage.success(t('common.saveSuccess'));
cancel();
onCancel();
};
const parseMenuMeta = (meta: any) => {
@@ -270,7 +264,7 @@ const parseMenuMeta = (meta: any) => {
return metaForm;
};
const cancel = () => {
const onCancel = () => {
visible.value = false;
emit('cancel');
};

View File

@@ -5,7 +5,7 @@
<div class="card !p-1 mr-1 flex justify-between">
<div class="mb-1">
<el-input v-model="filterResource" clearable :placeholder="$t('system.menu.filterPlaceholder')" class="mr-2 !w-[200px]" />
<el-button v-auth="perms.addResource" type="primary" icon="plus" @click="addResource(false)"></el-button>
<el-button v-auth="perms.addResource" type="primary" icon="plus" @click="onAddResource(false)"></el-button>
</div>
<div>
@@ -27,7 +27,7 @@
@node-expand="handleNodeExpand"
@node-collapse="handleNodeCollapse"
@node-contextmenu="nodeContextmenu"
@node-click="treeNodeClick"
@node-click="onTreeNodeClick"
:default-expanded-keys="defaultExpandedKeys"
:expand-on-click-node="false"
draggable
@@ -136,7 +136,7 @@
:typeDisabled="dialogForm.typeDisabled"
:departTree="data"
:type="dialogForm.type"
@val-change="valChange"
@val-change="onValChange"
/>
<contextmenu :dropdown="state.contextmenu.dropdown" :items="state.contextmenu.items" ref="contextmenuRef" />
@@ -186,29 +186,29 @@ const contextmenuAdd = new ContextmenuItem('add', 'system.menu.addSubResource')
.withIcon('circle-plus')
.withPermission(perms.addResource)
.withHideFunc((data: any) => data.type !== menuTypeValue)
.withOnClick((data: any) => addResource(data));
.withOnClick((data: any) => onAddResource(data));
const contextmenuEdit = new ContextmenuItem('edit', 'common.edit')
.withIcon('edit')
.withPermission(perms.updateResource)
.withOnClick((data: any) => editResource(data));
.withOnClick((data: any) => onEditResource(data));
const contextmenuEnable = new ContextmenuItem('enable', 'system.menu.enable')
.withIcon('circle-check')
.withPermission(perms.updateResource)
.withHideFunc((data: any) => data.status === 1)
.withOnClick((data: any) => changeStatus(data, 1));
.withOnClick((data: any) => onChangeStatus(data, 1));
const contextmenuDisable = new ContextmenuItem('disable', 'system.menu.disable')
.withIcon('circle-close')
.withPermission(perms.updateResource)
.withHideFunc((data: any) => data.status === -1)
.withOnClick((data: any) => changeStatus(data, -1));
.withOnClick((data: any) => onChangeStatus(data, -1));
const contextmenuDel = new ContextmenuItem('delete', 'common.delete')
.withIcon('delete')
.withPermission(perms.delResource)
.withOnClick((data: any) => deleteMenu(data));
.withOnClick((data: any) => onDeleteMenu(data));
const state = reactive({
contextmenu: {
@@ -263,7 +263,7 @@ const nodeContextmenu = (event: any, data: any) => {
contextmenuRef.value.openContextmenu(data);
};
const treeNodeClick = async (data: any) => {
const onTreeNodeClick = async (data: any) => {
state.activeTabName = ResourceDetail;
// 关闭可能存在的右击菜单
contextmenuRef.value.closeContextmenu();
@@ -286,7 +286,7 @@ const onTabClick = async (activeTab: any) => {
}
};
const deleteMenu = async (data: any) => {
const onDeleteMenu = async (data: any) => {
await useI18nDeleteConfirm(data.name);
await resourceApi.del.request({
id: data.id,
@@ -296,7 +296,7 @@ const deleteMenu = async (data: any) => {
search();
};
const addResource = (data: any) => {
const onAddResource = (data: any) => {
let dialog = state.dialogForm;
dialog.data = { pid: 0, type: 1 };
// 添加顶级菜单情况
@@ -333,7 +333,7 @@ const addResource = (data: any) => {
dialog.visible = true;
};
const editResource = async (data: any) => {
const onEditResource = async (data: any) => {
const res = await resourceApi.detail.request({
id: data.id,
});
@@ -347,12 +347,12 @@ const editResource = async (data: any) => {
state.dialogForm.visible = true;
};
const valChange = () => {
const onValChange = () => {
search();
state.dialogForm.visible = false;
};
const changeStatus = async (data: any, status: any) => {
const onChangeStatus = async (data: any, status: any) => {
await resourceApi.changeStatus.request({
id: data.id,
status: status,

View File

@@ -9,11 +9,11 @@
>
<page-table ref="pageTableRef" :page-api="roleApi.roleAccounts" :search-items="searchItems" v-model:query-form="query" :columns="columns" lazy>
<template #tableHeader>
<el-button v-auth="perms.saveAccountRole" type="primary" icon="plus" @click="showAddAccount()">{{ $t('common.add') }}</el-button>
<el-button v-auth="perms.saveAccountRole" type="primary" icon="plus" @click="onShowAddAccount()">{{ $t('common.add') }}</el-button>
</template>
<template #action="{ data }">
<el-button link v-if="actionBtns[perms.saveAccountRole]" @click="relateAccount(-1, data.accountId)" icon="delete" type="danger">
<el-button link v-if="actionBtns[perms.saveAccountRole]" @click="onRelateAccount(-1, data.accountId)" icon="delete" type="danger">
{{ $t('common.remove') }}
</el-button>
</template>
@@ -22,7 +22,7 @@
<el-dialog
width="400px"
:title="$t('system.role.addAccount')"
:before-close="cancelAddAccount"
:before-close="onCancelAddAccount"
v-model="addAccountDialog.visible"
:destroy-on-close="true"
>
@@ -31,8 +31,8 @@
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="cancelAddAccount()">{{ $t('common.cancel') }}</el-button>
<el-button @click="relateAccount(1, addAccountDialog.accountId)" type="primary">{{ $t('common.confirm') }}</el-button>
<el-button @click="onCancelAddAccount()">{{ $t('common.cancel') }}</el-button>
<el-button @click="onRelateAccount(1, addAccountDialog.accountId)" type="primary">{{ $t('common.confirm') }}</el-button>
</div>
</template>
</el-dialog>
@@ -107,7 +107,7 @@ const searchRoleAccount = () => {
pageTableRef.value.search();
};
const relateAccount = async (relateType: number, accountId: number) => {
const onRelateAccount = async (relateType: number, accountId: number) => {
await accountApi.saveRole.request({
id: accountId,
roleId: props.role?.id,
@@ -116,16 +116,16 @@ const relateAccount = async (relateType: number, accountId: number) => {
useI18nOperateSuccessMsg();
// 如果是新增账号,则关闭新增账号弹窗
if (relateType == 1) {
cancelAddAccount();
onCancelAddAccount();
}
searchRoleAccount();
};
const showAddAccount = () => {
const onShowAddAccount = () => {
state.addAccountDialog.visible = true;
};
const cancelAddAccount = () => {
const onCancelAddAccount = () => {
state.addAccountDialog.accountId = null;
state.addAccountDialog.accounts = [];
state.addAccountDialog.visible = false;

View File

@@ -3,7 +3,7 @@
<el-dialog
:title="$t('system.role.allocateMenuTitle', { roleName: roleInfo?.name })"
v-model="visible"
:before-close="cancel"
:before-close="onCancel"
:show-close="false"
width="400px"
>
@@ -26,8 +26,8 @@
</el-tree>
<template #footer>
<div class="dialog-footer">
<el-button :loading="state.submiting" @click="cancel">{{ $t('common.cancel') }}</el-button>
<el-button :loading="state.submiting" type="primary" @click="btnOk">{{ $t('common.confirm') }}</el-button>
<el-button :loading="state.submiting" @click="onCancel">{{ $t('common.cancel') }}</el-button>
<el-button :loading="state.submiting" type="primary" @click="onConfirm">{{ $t('common.confirm') }}</el-button>
</div>
</template>
</el-dialog>
@@ -88,7 +88,7 @@ watch(
}
);
const btnOk = async () => {
const onConfirm = async () => {
let menuIds = menuTree.value.getCheckedKeys();
let halfMenuIds = menuTree.value.getHalfCheckedKeys();
let resources = [].concat(menuIds, halfMenuIds).join(',');
@@ -105,7 +105,7 @@ const btnOk = async () => {
}
};
const cancel = () => {
const onCancel = () => {
visible.value = false;
emit('cancel');
};

View File

@@ -1,7 +1,7 @@
<template>
<div>
<el-dialog :title="title" v-model="visible" :show-close="false" :before-close="cancel" width="600px" :destroy-on-close="true">
<el-form ref="roleForm" :model="form" :rules="rules" label-width="auto">
<el-dialog :title="title" v-model="visible" :show-close="false" :before-close="onCancel" width="600px" :destroy-on-close="true">
<el-form ref="roleFormRef" :model="form" :rules="rules" label-width="auto">
<el-form-item prop="name" :label="$t('system.role.roleName')" required>
<el-input v-model="form.name" auto-complete="off"></el-input>
</el-form-item>
@@ -22,8 +22,8 @@
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="cancel()">{{ $t('common.cancel') }}</el-button>
<el-button type="primary" :loading="saveBtnLoading" @click="btnOk">{{ $t('common.confirm') }}</el-button>
<el-button @click="onCancel()">{{ $t('common.cancel') }}</el-button>
<el-button type="primary" :loading="saveBtnLoading" @click="onConfirm">{{ $t('common.confirm') }}</el-button>
</div>
</template>
</el-dialog>
@@ -31,7 +31,7 @@
</template>
<script lang="ts" setup>
import { ref, toRefs, reactive, watchEffect } from 'vue';
import { toRefs, reactive, watchEffect, useTemplateRef } from 'vue';
import { roleApi } from '../api';
import { RoleStatusEnum } from '../enums';
import EnumSelect from '@/components/enumselect/EnumSelect.vue';
@@ -58,7 +58,8 @@ const visible = defineModel<boolean>('visible', { default: false });
//定义事件
const emit = defineEmits(['cancel', 'val-change']);
const roleForm: any = ref(null);
const roleFormRef: any = useTemplateRef('roleFormRef');
const state = reactive({
form: {
id: null,
@@ -84,17 +85,17 @@ watchEffect(() => {
}
});
const cancel = () => {
const onCancel = () => {
visible.value = false;
// 若父组件有取消事件,则调用
emit('cancel');
};
const btnOk = async () => {
await useI18nFormValidate(roleForm);
const onConfirm = async () => {
await useI18nFormValidate(roleFormRef);
await saveRoleExec();
emit('val-change', state.form);
cancel();
onCancel();
};
</script>
<style lang="scss"></style>

View File

@@ -47,7 +47,7 @@ func (a *AccountLogin) ReqConfs() *req.Confs {
// @router /auth/accounts/login [post]
func (a *AccountLogin) Login(rc *req.Ctx) {
loginForm := req.BindJsonAndValid(rc, new(form.LoginForm))
loginForm := req.BindJsonAndValid[*form.LoginForm](rc)
ctx := rc.MetaCtx
accountLoginSecurity := config.GetAccountLoginSecurity()
@@ -96,8 +96,7 @@ type OtpVerifyInfo struct {
// OTP双因素校验
func (a *AccountLogin) OtpVerify(rc *req.Ctx) {
otpVerify := new(form.OtpVerfiy)
req.BindJsonAndValid(rc, otpVerify)
otpVerify := req.BindJsonAndValid[*form.OtpVerfiy](rc)
ctx := rc.MetaCtx
tokenKey := fmt.Sprintf("otp:token:%s", otpVerify.OtpToken)

View File

@@ -47,7 +47,7 @@ func (a *LdapLogin) GetLdapEnabled(rc *req.Ctx) {
// @router /auth/ldap/login [post]
func (a *LdapLogin) Login(rc *req.Ctx) {
loginForm := req.BindJsonAndValid(rc, new(form.LoginForm))
loginForm := req.BindJsonAndValid[*form.LoginForm](rc)
ctx := rc.MetaCtx
accountLoginSecurity := config.GetAccountLoginSecurity()
// 判断是否有开启登录验证码校验

View File

@@ -77,7 +77,7 @@ func (d *Db) ReqConfs() *req.Confs {
// @router /api/dbs [get]
func (d *Db) Dbs(rc *req.Ctx) {
queryCond := req.BindQuery[*entity.DbQuery](rc, new(entity.DbQuery))
queryCond := req.BindQuery[*entity.DbQuery](rc)
// 不存在可访问标签id即没有可操作数据
tags := d.tagApp.GetAccountTags(rc.GetLoginAccount().Id, &tagentity.TagTreeQuery{
@@ -115,9 +115,7 @@ func (d *Db) Dbs(rc *req.Ctx) {
}
func (d *Db) Save(rc *req.Ctx) {
form := &form.DbForm{}
db := req.BindJsonAndCopyTo[*entity.Db](rc, form, new(entity.Db))
form, db := req.BindJsonAndCopyTo[*form.DbForm, *entity.Db](rc)
rc.ReqParam = form
biz.ErrIsNil(d.dbApp.SaveDb(rc.MetaCtx, db))
@@ -137,7 +135,7 @@ func (d *Db) DeleteDb(rc *req.Ctx) {
/** 数据库操作相关、执行sql等 ***/
func (d *Db) ExecSql(rc *req.Ctx) {
form := req.BindJsonAndValid(rc, new(form.DbSqlExecForm))
form := req.BindJsonAndValid[*form.DbSqlExecForm](rc)
ctx, cancel := context.WithTimeout(rc.MetaCtx, time.Duration(config.GetDbms().SqlExecTl)*time.Second)
defer cancel()
@@ -351,8 +349,7 @@ func (d *Db) GetSchemas(rc *req.Ctx) {
}
func (d *Db) CopyTable(rc *req.Ctx) {
form := &form.DbCopyTableForm{}
copy := req.BindJsonAndCopyTo[*dbi.DbCopyTable](rc, form, new(dbi.DbCopyTable))
form, copy := req.BindJsonAndCopyTo[*form.DbCopyTableForm, *dbi.DbCopyTable](rc)
conn, err := d.dbApp.GetDbConn(rc.MetaCtx, form.Id, form.Db)
biz.ErrIsNilAppendErr(err, "copy table error: %s")

View File

@@ -50,22 +50,21 @@ func (d *DataSyncTask) ReqConfs() *req.Confs {
}
func (d *DataSyncTask) Tasks(rc *req.Ctx) {
queryCond := req.BindQuery[*entity.DataSyncTaskQuery](rc, new(entity.DataSyncTaskQuery))
queryCond := req.BindQuery[*entity.DataSyncTaskQuery](rc)
res, err := d.dataSyncTaskApp.GetPageList(queryCond)
biz.ErrIsNil(err)
rc.ResData = model.PageResultConv[*entity.DataSyncTask, *vo.DataSyncTaskListVO](res)
}
func (d *DataSyncTask) Logs(rc *req.Ctx) {
queryCond := req.BindQuery(rc, new(entity.DataSyncLogQuery))
queryCond := req.BindQuery[*entity.DataSyncLogQuery](rc)
res, err := d.dataSyncTaskApp.GetTaskLogList(queryCond)
biz.ErrIsNil(err)
rc.ResData = model.PageResultConv[*entity.DataSyncLog, *vo.DataSyncLogListVO](res)
}
func (d *DataSyncTask) SaveTask(rc *req.Ctx) {
form := &form.DataSyncTaskForm{}
task := req.BindJsonAndCopyTo[*entity.DataSyncTask](rc, form, new(entity.DataSyncTask))
form, task := req.BindJsonAndCopyTo[*form.DataSyncTaskForm, *entity.DataSyncTask](rc)
// 解码base64 sql
sqlStr, err := utils.AesDecryptByLa(task.DataSql, rc.GetLoginAccount())
@@ -89,8 +88,7 @@ func (d *DataSyncTask) DeleteTask(rc *req.Ctx) {
}
func (d *DataSyncTask) ChangeStatus(rc *req.Ctx) {
form := &form.DataSyncTaskStatusForm{}
task := req.BindJsonAndCopyTo[*entity.DataSyncTask](rc, form, new(entity.DataSyncTask))
form, task := req.BindJsonAndCopyTo[*form.DataSyncTaskStatusForm, *entity.DataSyncTask](rc)
_ = d.dataSyncTaskApp.UpdateById(rc.MetaCtx, task)
if task.Status == entity.DataSyncTaskStatusEnable {

View File

@@ -55,7 +55,7 @@ func (d *Instance) ReqConfs() *req.Confs {
// Instances 获取数据库实例信息
// @router /api/instances [get]
func (d *Instance) Instances(rc *req.Ctx) {
queryCond := req.BindQuery(rc, new(entity.InstanceQuery))
queryCond := req.BindQuery[*entity.InstanceQuery](rc)
tags := d.tagApp.GetAccountTags(rc.GetLoginAccount().Id, &tagentity.TagTreeQuery{
TypePaths: collx.AsArray(tagentity.NewTypePaths(tagentity.TagTypeDbInstance, tagentity.TagTypeAuthCert)),
@@ -90,17 +90,14 @@ func (d *Instance) Instances(rc *req.Ctx) {
}
func (d *Instance) TestConn(rc *req.Ctx) {
form := &form.InstanceForm{}
instance := req.BindJsonAndCopyTo[*entity.DbInstance](rc, form, new(entity.DbInstance))
form, instance := req.BindJsonAndCopyTo[*form.InstanceForm, *entity.DbInstance](rc)
biz.ErrIsNil(d.instanceApp.TestConn(rc.MetaCtx, instance, form.AuthCerts[0]))
}
// SaveInstance 保存数据库实例信息
// @router /api/instances [post]
func (d *Instance) SaveInstance(rc *req.Ctx) {
form := &form.InstanceForm{}
instance := req.BindJsonAndCopyTo[*entity.DbInstance](rc, form, new(entity.DbInstance))
form, instance := req.BindJsonAndCopyTo[*form.InstanceForm, *entity.DbInstance](rc)
rc.ReqParam = form
id, err := d.instanceApp.SaveDbInstance(rc.MetaCtx, &dto.SaveDbInstance{
@@ -135,8 +132,7 @@ func (d *Instance) DeleteInstance(rc *req.Ctx) {
// 获取数据库实例的所有数据库名
func (d *Instance) GetDatabaseNames(rc *req.Ctx) {
form := &form.InstanceDbNamesForm{}
instance := req.BindJsonAndCopyTo[*entity.DbInstance](rc, form, new(entity.DbInstance))
form, instance := req.BindJsonAndCopyTo[*form.InstanceDbNamesForm, *entity.DbInstance](rc)
res, err := d.instanceApp.GetDatabases(rc.MetaCtx, instance, form.AuthCert)
biz.ErrIsNil(err)
rc.ResData = res

View File

@@ -30,8 +30,7 @@ func (d *DbSql) ReqConfs() *req.Confs {
// @router /api/db/:dbId/sql [post]
func (d *DbSql) SaveSql(rc *req.Ctx) {
dbSqlForm := &form.DbSqlSaveForm{}
req.BindJsonAndValid(rc, dbSqlForm)
dbSqlForm := req.BindJsonAndValid[*form.DbSqlSaveForm](rc)
rc.ReqParam = dbSqlForm
dbId := getDbId(rc)

View File

@@ -26,7 +26,7 @@ func (d *DbSqlExec) ReqConfs() *req.Confs {
}
func (d *DbSqlExec) DbSqlExecs(rc *req.Ctx) {
queryCond := req.BindQuery(rc, new(entity.DbSqlExecQuery))
queryCond := req.BindQuery[*entity.DbSqlExecQuery](rc)
if statusStr := rc.Query("status"); statusStr != "" {
queryCond.Status = collx.ArrayMap[string, int8](strings.Split(statusStr, ","), func(val string) int8 {
return cast.ToInt8(val)

View File

@@ -61,7 +61,7 @@ func (d *DbTransferTask) ReqConfs() *req.Confs {
}
func (d *DbTransferTask) Tasks(rc *req.Ctx) {
queryCond := req.BindQuery(rc, new(entity.DbTransferTaskQuery))
queryCond := req.BindQuery[*entity.DbTransferTaskQuery](rc)
res, err := d.dbTransferTask.GetPageList(queryCond)
biz.ErrIsNil(err)
@@ -78,8 +78,7 @@ func (d *DbTransferTask) Tasks(rc *req.Ctx) {
}
func (d *DbTransferTask) SaveTask(rc *req.Ctx) {
reqForm := &form.DbTransferTaskForm{}
task := req.BindJsonAndCopyTo[*entity.DbTransferTask](rc, reqForm, new(entity.DbTransferTask))
reqForm, task := req.BindJsonAndCopyTo[*form.DbTransferTaskForm, *entity.DbTransferTask](rc)
rc.ReqParam = reqForm
biz.ErrIsNil(d.dbTransferTask.Save(rc.MetaCtx, task))
@@ -98,8 +97,7 @@ func (d *DbTransferTask) DeleteTask(rc *req.Ctx) {
}
func (d *DbTransferTask) ChangeStatus(rc *req.Ctx) {
form := &form.DbTransferTaskStatusForm{}
task := req.BindJsonAndCopyTo[*entity.DbTransferTask](rc, form, new(entity.DbTransferTask))
form, task := req.BindJsonAndCopyTo[*form.DbTransferTaskStatusForm, *entity.DbTransferTask](rc)
_ = d.dbTransferTask.UpdateById(rc.MetaCtx, task)
task, err := d.dbTransferTask.GetById(task.Id)
@@ -122,7 +120,7 @@ func (d *DbTransferTask) Stop(rc *req.Ctx) {
}
func (d *DbTransferTask) Files(rc *req.Ctx) {
queryCond := req.BindQuery(rc, new(entity.DbTransferFileQuery))
queryCond := req.BindQuery[*entity.DbTransferFileQuery](rc)
res, err := d.dbTransferFile.GetPageList(queryCond)
biz.ErrIsNil(err)
@@ -142,7 +140,7 @@ func (d *DbTransferTask) FileDel(rc *req.Ctx) {
}
func (d *DbTransferTask) FileRun(rc *req.Ctx) {
fm := req.BindJsonAndValid(rc, &form.DbTransferFileRunForm{})
fm := req.BindJsonAndValid[*form.DbTransferFileRunForm](rc)
rc.ReqParam = fm

View File

@@ -277,7 +277,7 @@ func (d *dbSqlExecAppImpl) FlowBizHandle(ctx context.Context, bizHandleParam *fl
return nil, nil
}
execSqlBizForm, err := jsonx.To(procinst.BizForm, new(FlowDbExecSqlBizForm))
execSqlBizForm, err := jsonx.To[*FlowDbExecSqlBizForm](procinst.BizForm)
if err != nil {
return nil, errorx.NewBiz("failed to parse the business form information: %s", err.Error())
}

View File

@@ -51,7 +51,7 @@ func (d *Instance) ReqConfs() *req.Confs {
}
func (d *Instance) Instances(rc *req.Ctx) {
queryCond := req.BindQuery(rc, new(entity.InstanceQuery))
queryCond := req.BindQuery[*entity.InstanceQuery](rc)
// 只查询实例,兼容没有录入密码的实例
instTags := d.tagApp.GetAccountTags(rc.GetLoginAccount().Id, &tagentity.TagTreeQuery{
@@ -92,8 +92,7 @@ func (d *Instance) Instances(rc *req.Ctx) {
}
func (d *Instance) TestConn(rc *req.Ctx) {
fm := &form.InstanceForm{}
instance := req.BindJsonAndCopyTo[*entity.EsInstance](rc, fm, new(entity.EsInstance))
fm, instance := req.BindJsonAndCopyTo[*form.InstanceForm, *entity.EsInstance](rc)
var ac *tagentity.ResourceAuthCert
if len(fm.AuthCerts) > 0 {
@@ -105,8 +104,7 @@ func (d *Instance) TestConn(rc *req.Ctx) {
rc.ResData = res
}
func (d *Instance) SaveInstance(rc *req.Ctx) {
fm := &form.InstanceForm{}
instance := req.BindJsonAndCopyTo[*entity.EsInstance](rc, fm, new(entity.EsInstance))
fm, instance := req.BindJsonAndCopyTo[*form.InstanceForm, *entity.EsInstance](rc)
rc.ReqParam = fm
id, err := d.inst.SaveInst(rc.MetaCtx, &dto.SaveEsInstance{

View File

@@ -5,13 +5,13 @@ import (
)
type InstanceForm struct {
Id uint64 `json:"id"`
Name string `binding:"required" json:"name"`
Host string `binding:"required" json:"host"`
Port int `binding:"required" json:"port"`
Version string `json:"version"`
Remark string `json:"remark"`
SshTunnelMachineId int `json:"sshTunnelMachineId"`
Id uint64 `json:"id"`
Name string `binding:"required" json:"name"`
Host string `binding:"required" json:"host"`
Port int `binding:"required" json:"port"`
Version string `json:"version"`
Remark *string `json:"remark"`
SshTunnelMachineId int `json:"sshTunnelMachineId"`
AuthCerts []*tagentity.ResourceAuthCert `json:"authCerts"` // 资产授权凭证信息列表
TagCodePaths []string `binding:"required" json:"tagCodePaths"`

View File

@@ -9,16 +9,17 @@ type InstanceListVO struct {
tagentity.AuthCerts // 授权凭证信息
tagentity.ResourceTags
Id *int64 `json:"id"`
Code string `json:"code"`
Name *string `json:"name"`
Host *string `json:"host"`
Port *int `json:"port"`
Version *string `json:"version"`
Id *int64 `json:"id"`
Code string `json:"code"`
Name *string `json:"name"`
Host *string `json:"host"`
Port *int `json:"port"`
Version *string `json:"version"`
Remark *string `json:"remark"`
CreateTime *time.Time `json:"createTime"`
Creator *string `json:"creator"`
CreatorId *int64 `json:"creatorId"`
UpdateTime *time.Time `json:"updateTime"`
Modifier *string `json:"modifier"`
ModifierId *int64 `json:"modifierId"`

View File

@@ -8,14 +8,14 @@ import (
type EsInstance struct {
model.Model
Code string `json:"code" gorm:"size:32;not null;"`
Name string `json:"name" gorm:"size:32;not null;"`
Host string `json:"host" gorm:"size:255;not null;"`
Port int `json:"port"`
Network string `json:"network" gorm:"size:20;"`
Version string `json:"version" gorm:"size:50;"`
AuthCertName string `json:"authCertName" gorm:"size:255;"`
SshTunnelMachineId int `json:"sshTunnelMachineId"` // ssh隧道机器id
Code string `json:"code" gorm:"size:32;not null;"`
Name string `json:"name" gorm:"size:32;not null;"`
Host string `json:"host" gorm:"size:255;not null;"`
Port int `json:"port"`
Network string `json:"network" gorm:"size:20;"`
Version string `json:"version" gorm:"size:50;"`
Remark *string `json:"remark" gorm:"size:255;"`
SshTunnelMachineId int `json:"sshTunnelMachineId"` // ssh隧道机器id
}
func (d *EsInstance) TableName() string {

View File

@@ -48,7 +48,7 @@ func (p *Procdef) ReqConfs() *req.Confs {
}
func (p *Procdef) GetProcdefPage(rc *req.Ctx) {
cond, page := req.BindQueryAndPage(rc, new(entity.Procdef))
cond, page := req.BindQueryAndPage[*entity.Procdef](rc)
res, err := p.procdefApp.GetPageList(cond, page)
biz.ErrIsNil(err)
@@ -87,8 +87,7 @@ func (p *Procdef) GetProcdef(rc *req.Ctx) {
}
func (a *Procdef) Save(rc *req.Ctx) {
form := &form.Procdef{}
procdef := req.BindJsonAndCopyTo(rc, form, new(entity.Procdef))
form, procdef := req.BindJsonAndCopyTo[*form.Procdef, *entity.Procdef](rc)
rc.ReqParam = form
biz.ErrIsNil(a.procdefApp.SaveProcdef(rc.MetaCtx, &dto.SaveProcdef{
Procdef: procdef,
@@ -98,7 +97,7 @@ func (a *Procdef) Save(rc *req.Ctx) {
}
func (a *Procdef) SaveFlowDef(rc *req.Ctx) {
form := req.BindJsonAndValid(rc, &form.ProcdefFlow{})
form := req.BindJsonAndValid[*form.ProcdefFlow](rc)
rc.ReqParam = form
biz.ErrIsNil(a.procdefApp.SaveFlowDef(rc.MetaCtx, &dto.SaveFlowDef{

View File

@@ -35,7 +35,7 @@ func (p *Procinst) ReqConfs() *req.Confs {
}
func (p *Procinst) GetProcinstPage(rc *req.Ctx) {
cond := req.BindQuery(rc, new(entity.ProcinstQuery))
cond := req.BindQuery[*entity.ProcinstQuery](rc)
// 非管理员只能获取自己申请的流程
if laId := rc.GetLoginAccount().Id; laId != consts.AdminId {
cond.CreatorId = laId
@@ -47,8 +47,7 @@ func (p *Procinst) GetProcinstPage(rc *req.Ctx) {
}
func (p *Procinst) ProcinstStart(rc *req.Ctx) {
startForm := new(form.ProcinstStart)
req.BindJsonAndValid(rc, startForm)
startForm := req.BindJsonAndValid[*form.ProcinstStart](rc)
_, err := p.procinstApp.StartProc(rc.MetaCtx, startForm.ProcdefId, &dto.StarProc{
BizType: startForm.BizType,
BizForm: jsonx.ToStr(startForm.BizForm),

View File

@@ -41,7 +41,7 @@ func (p *ProcinstTask) ReqConfs() *req.Confs {
}
func (p *ProcinstTask) GetTasks(rc *req.Ctx) {
instTaskQuery := req.BindQuery(rc, new(entity.ProcinstTaskQuery))
instTaskQuery := req.BindQuery[*entity.ProcinstTaskQuery](rc)
if laId := rc.GetLoginAccount().Id; laId != consts.AdminId {
// 赋值操作人为当前登录账号
instTaskQuery.Assignee = fmt.Sprintf("%d", rc.GetLoginAccount().Id)
@@ -74,7 +74,7 @@ func (p *ProcinstTask) GetTasks(rc *req.Ctx) {
}
func (p *ProcinstTask) PassTask(rc *req.Ctx) {
auditForm := req.BindJsonAndValid(rc, new(form.ProcinstTaskAudit))
auditForm := req.BindJsonAndValid[*form.ProcinstTaskAudit](rc)
rc.ReqParam = auditForm
la := rc.GetLoginAccount()
@@ -84,7 +84,7 @@ func (p *ProcinstTask) PassTask(rc *req.Ctx) {
}
func (p *ProcinstTask) RejectTask(rc *req.Ctx) {
auditForm := req.BindJsonAndValid(rc, new(form.ProcinstTaskAudit))
auditForm := req.BindJsonAndValid[*form.ProcinstTaskAudit](rc)
rc.ReqParam = auditForm
la := rc.GetLoginAccount()
@@ -94,7 +94,7 @@ func (p *ProcinstTask) RejectTask(rc *req.Ctx) {
}
func (p *ProcinstTask) BackTask(rc *req.Ctx) {
auditForm := req.BindJsonAndValid(rc, new(form.ProcinstTaskAudit))
auditForm := req.BindJsonAndValid[*form.ProcinstTaskAudit](rc)
rc.ReqParam = auditForm
biz.ErrIsNil(p.procinstTaskApp.BackTask(rc.MetaCtx, dto.UserTaskOp{TaskId: auditForm.Id, Remark: auditForm.Remark}))
}

View File

@@ -104,7 +104,9 @@ func (e *executionAppImpl) MoveTo(ctx *ExecutionCtx, nextNode *entity.FlowNode)
}
// 记录当前节点结束
e.hisProcinstOpApp.RecordEnd(ctx, "copmpleted")
if err := e.hisProcinstOpApp.RecordEnd(ctx, "copmpleted"); err != nil {
return err
}
// 下一个节点为空,说明流程已结束
if nextNode == nil {
@@ -163,7 +165,9 @@ func (e *executionAppImpl) executeNode(ctx *ExecutionCtx) error {
}
// 节点开始操作记录
e.hisProcinstOpApp.RecordStart(ctx)
if err := e.hisProcinstOpApp.RecordStart(ctx); err != nil {
return err
}
// 执行节点逻辑
return node.Execute(ctx)

View File

@@ -61,7 +61,7 @@ func (p *Procdef) GetFlowDef() *FlowDef {
if p.FlowDef == "" {
return nil
}
flow, err := jsonx.To(p.FlowDef, new(FlowDef))
flow, err := jsonx.To[*FlowDef](p.FlowDef)
if err != nil {
logx.ErrorTrace("parse flow def failed", err)
return flow

View File

@@ -43,7 +43,7 @@ func (a *Procinst) SetEnd() {
// GetProcdefFlow 获取流程定义信息
func (p *Procinst) GetFlowDef() *FlowDef {
flow, err := jsonx.To(p.FlowDef, new(FlowDef))
flow, err := jsonx.To[*FlowDef](p.FlowDef)
if err != nil {
logx.ErrorTrace("parse procdef flow failed", err)
return flow

View File

@@ -50,12 +50,12 @@ type MachineCronJobForm struct {
}
type MachineCmdConfForm struct {
Id uint64 `json:"id"`
Name string `json:"name"`
Cmds []string `json:"cmds"` // 命令配置
Status int8 `json:"execCmds"` // 状态
Stratege string `json:"stratege"` // 策略,空禁用
Remark string `json:"remark"` // 备注
Id uint64 `json:"id"`
Name string `json:"name"`
Cmds model.Slice[string] `json:"cmds"` // 命令配置
Status int8 `json:"execCmds"` // 状态
Stratege string `json:"stratege"` // 策略,空禁用
Remark string `json:"remark"` // 备注
CodePaths []string `json:"codePaths"`
}

View File

@@ -76,7 +76,7 @@ func (m *Machine) ReqConfs() *req.Confs {
}
func (m *Machine) Machines(rc *req.Ctx) {
condition := req.BindQuery(rc, new(entity.MachineQuery))
condition := req.BindQuery[*entity.MachineQuery](rc)
tags := m.tagTreeApp.GetAccountTags(rc.GetLoginAccount().Id, &tagentity.TagTreeQuery{
TypePaths: collx.AsArray(tagentity.NewTypePaths(tagentity.TagTypeMachine, tagentity.TagTypeAuthCert)),
@@ -143,8 +143,7 @@ func (m *Machine) MachineStats(rc *req.Ctx) {
// 保存机器信息
func (m *Machine) SaveMachine(rc *req.Ctx) {
machineForm := new(form.MachineForm)
me := req.BindJsonAndCopyTo(rc, machineForm, new(entity.Machine))
machineForm, me := req.BindJsonAndCopyTo[*form.MachineForm, *entity.Machine](rc)
rc.ReqParam = machineForm
@@ -156,8 +155,7 @@ func (m *Machine) SaveMachine(rc *req.Ctx) {
}
func (m *Machine) TestConn(rc *req.Ctx) {
machineForm := new(form.MachineForm)
me := req.BindJsonAndCopyTo(rc, machineForm, new(entity.Machine))
machineForm, me := req.BindJsonAndCopyTo[*form.MachineForm, *entity.Machine](rc)
// 测试连接
biz.ErrIsNilAppendErr(m.machineApp.TestConn(rc.MetaCtx, me, machineForm.AuthCerts[0]), "connection error: %s")
}

View File

@@ -33,7 +33,7 @@ func (mcc *MachineCmdConf) ReqConfs() *req.Confs {
}
func (m *MachineCmdConf) MachineCmdConfs(rc *req.Ctx) {
cond := req.BindQuery(rc, new(entity.MachineCmdConf))
cond := req.BindQuery[*entity.MachineCmdConf](rc)
var vos []*vo.MachineCmdConfVO
err := m.machineCmdConfApp.ListByCondToAny(cond, &vos)
@@ -47,8 +47,7 @@ func (m *MachineCmdConf) MachineCmdConfs(rc *req.Ctx) {
}
func (m *MachineCmdConf) Save(rc *req.Ctx) {
cmdForm := new(form.MachineCmdConfForm)
mcj := req.BindJsonAndCopyTo[*entity.MachineCmdConf](rc, cmdForm, new(entity.MachineCmdConf))
cmdForm, mcj := req.BindJsonAndCopyTo[*form.MachineCmdConfForm, *entity.MachineCmdConf](rc)
rc.ReqParam = cmdForm
err := m.machineCmdConfApp.SaveCmdConf(rc.MetaCtx, &dto.SaveMachineCmdConf{

View File

@@ -43,7 +43,7 @@ func (mcj *MachineCronJob) ReqConfs() *req.Confs {
}
func (m *MachineCronJob) MachineCronJobs(rc *req.Ctx) {
cond, pageParam := req.BindQueryAndPage(rc, new(entity.MachineCronJob))
cond, pageParam := req.BindQueryAndPage[*entity.MachineCronJob](rc)
pageRes, err := m.machineCronJobApp.GetPageList(cond, pageParam)
biz.ErrIsNil(err)
@@ -62,8 +62,7 @@ func (m *MachineCronJob) MachineCronJobs(rc *req.Ctx) {
}
func (m *MachineCronJob) Save(rc *req.Ctx) {
jobForm := new(form.MachineCronJobForm)
mcj := req.BindJsonAndCopyTo[*entity.MachineCronJob](rc, jobForm, new(entity.MachineCronJob))
jobForm, mcj := req.BindJsonAndCopyTo[*form.MachineCronJobForm, *entity.MachineCronJob](rc)
rc.ReqParam = jobForm
err := m.machineCronJobApp.SaveMachineCronJob(rc.MetaCtx, &dto.SaveMachineCronJob{
@@ -90,7 +89,7 @@ func (m *MachineCronJob) RunCronJob(rc *req.Ctx) {
}
func (m *MachineCronJob) CronJobExecs(rc *req.Ctx) {
cond, pageParam := req.BindQueryAndPage[*entity.MachineCronJobExec](rc, new(entity.MachineCronJobExec))
cond, pageParam := req.BindQueryAndPage[*entity.MachineCronJobExec](rc)
res, err := m.machineCronJobApp.GetExecPageList(cond, pageParam)
biz.ErrIsNil(err)
rc.ResData = res

View File

@@ -93,8 +93,7 @@ func (m *MachineFile) MachineFiles(rc *req.Ctx) {
}
func (m *MachineFile) SaveMachineFiles(rc *req.Ctx) {
fileForm := new(form.MachineFileForm)
entity := req.BindJsonAndCopyTo[*entity.MachineFile](rc, fileForm, new(entity.MachineFile))
fileForm, entity := req.BindJsonAndCopyTo[*form.MachineFileForm, *entity.MachineFile](rc)
rc.ReqParam = fileForm
biz.ErrIsNil(m.machineFileApp.Save(rc.MetaCtx, entity))
@@ -107,7 +106,7 @@ func (m *MachineFile) DeleteFile(rc *req.Ctx) {
/*** sftp相关操作 */
func (m *MachineFile) CreateFile(rc *req.Ctx) {
opForm := req.BindJsonAndValid(rc, new(form.CreateFileForm))
opForm := req.BindJsonAndValid[*form.CreateFileForm](rc)
path := opForm.Path
attrs := collx.Kvs("path", path)
@@ -126,7 +125,7 @@ func (m *MachineFile) CreateFile(rc *req.Ctx) {
}
func (m *MachineFile) ReadFileContent(rc *req.Ctx) {
opForm := req.BindQuery(rc, new(dto.MachineFileOp))
opForm := req.BindQuery[*dto.MachineFileOp](rc)
readPath := opForm.Path
ctx := rc.MetaCtx
@@ -158,7 +157,7 @@ func (m *MachineFile) ReadFileContent(rc *req.Ctx) {
}
func (m *MachineFile) DownloadFile(rc *req.Ctx) {
opForm := req.BindQuery(rc, new(dto.MachineFileOp))
opForm := req.BindQuery[*dto.MachineFileOp](rc)
readPath := opForm.Path
@@ -186,7 +185,7 @@ func (m *MachineFile) DownloadFile(rc *req.Ctx) {
}
func (m *MachineFile) GetDirEntry(rc *req.Ctx) {
opForm := req.BindQuery(rc, new(dto.MachineFileOp))
opForm := req.BindQuery[*dto.MachineFileOp](rc)
readPath := opForm.Path
rc.ReqParam = fmt.Sprintf("path: %s", readPath)
@@ -225,7 +224,7 @@ func (m *MachineFile) GetDirEntry(rc *req.Ctx) {
}
func (m *MachineFile) GetDirSize(rc *req.Ctx) {
opForm := req.BindQuery(rc, new(dto.MachineFileOp))
opForm := req.BindQuery[*dto.MachineFileOp](rc)
size, err := m.machineFileApp.GetDirSize(rc.MetaCtx, opForm)
biz.ErrIsNil(err)
@@ -233,14 +232,14 @@ func (m *MachineFile) GetDirSize(rc *req.Ctx) {
}
func (m *MachineFile) GetFileStat(rc *req.Ctx) {
opForm := req.BindQuery(rc, new(dto.MachineFileOp))
opForm := req.BindQuery[*dto.MachineFileOp](rc)
res, err := m.machineFileApp.FileStat(rc.MetaCtx, opForm)
biz.ErrIsNil(err, res)
rc.ResData = res
}
func (m *MachineFile) WriteFileContent(rc *req.Ctx) {
opForm := req.BindJsonAndValid(rc, new(form.WriteFileContentForm))
opForm := req.BindJsonAndValid[*form.WriteFileContentForm](rc)
path := opForm.Path
mi, err := m.machineFileApp.WriteFileContent(rc.MetaCtx, opForm.MachineFileOp, []byte(opForm.Content))
@@ -401,7 +400,7 @@ func (m *MachineFile) UploadFolder(rc *req.Ctx) {
}
func (m *MachineFile) RemoveFile(rc *req.Ctx) {
opForm := req.BindJsonAndValid(rc, new(form.RemoveFileForm))
opForm := req.BindJsonAndValid[*form.RemoveFileForm](rc)
mi, err := m.machineFileApp.RemoveFile(rc.MetaCtx, opForm.MachineFileOp, opForm.Paths...)
rc.ReqParam = collx.Kvs("machine", mi, "path", opForm)
@@ -409,21 +408,21 @@ func (m *MachineFile) RemoveFile(rc *req.Ctx) {
}
func (m *MachineFile) CopyFile(rc *req.Ctx) {
opForm := req.BindJsonAndValid(rc, new(form.CopyFileForm))
opForm := req.BindJsonAndValid[*form.CopyFileForm](rc)
mi, err := m.machineFileApp.Copy(rc.MetaCtx, opForm.MachineFileOp, opForm.ToPath, opForm.Paths...)
biz.ErrIsNilAppendErr(err, "file copy error: %s")
rc.ReqParam = collx.Kvs("machine", mi, "cp", opForm)
}
func (m *MachineFile) MvFile(rc *req.Ctx) {
opForm := req.BindJsonAndValid(rc, new(form.CopyFileForm))
opForm := req.BindJsonAndValid[*form.CopyFileForm](rc)
mi, err := m.machineFileApp.Mv(rc.MetaCtx, opForm.MachineFileOp, opForm.ToPath, opForm.Paths...)
rc.ReqParam = collx.Kvs("machine", mi, "mv", opForm)
biz.ErrIsNilAppendErr(err, "file move error: %s")
}
func (m *MachineFile) Rename(rc *req.Ctx) {
renameForm := req.BindJsonAndValid(rc, new(form.RenameForm))
renameForm := req.BindJsonAndValid[*form.RenameForm](rc)
mi, err := m.machineFileApp.Rename(rc.MetaCtx, renameForm.MachineFileOp, renameForm.Newname)
rc.ReqParam = collx.Kvs("machine", mi, "rename", renameForm)
biz.ErrIsNilAppendErr(err, "file rename error: %s")

View File

@@ -46,8 +46,7 @@ func (m *MachineScript) MachineScripts(rc *req.Ctx) {
}
func (m *MachineScript) SaveMachineScript(rc *req.Ctx) {
form := new(form.MachineScriptForm)
machineScript := req.BindJsonAndCopyTo(rc, form, new(entity.MachineScript))
form, machineScript := req.BindJsonAndCopyTo[*form.MachineScriptForm, *entity.MachineScript](rc)
rc.ReqParam = form
biz.ErrIsNil(m.machineScriptApp.Save(rc.MetaCtx, machineScript))

View File

@@ -111,10 +111,10 @@ type MachineCmdConfVO struct {
model.Model
Name string `json:"name"`
Cmds model.Slice[string] `json:"cmds"` // 命令配置
Status int8 `json:"execCmds"` // 状态
Stratege string `json:"stratege"` // 策略,空禁用
Remark string `json:"remark"` // 备注
Cmds model.Slice[string] `json:"cmds" gorm:"type:varchar"` // 命令配置要加gorm标签才会正确解析model.Slice
Status int8 `json:"execCmds"` // 状态
Stratege string `json:"stratege"` // 策略,空禁用
Remark string `json:"remark"` // 备注
}
func (mcc *MachineCmdConfVO) GetRelateId() uint64 {

View File

@@ -20,5 +20,5 @@ func GetMachineStats(machineId uint64) (*mcm.Stats, error) {
if cacheStr == "" {
return nil, errors.New("不存在该值")
}
return jsonx.To(cacheStr, new(mcm.Stats))
return jsonx.To[*mcm.Stats](cacheStr)
}

View File

@@ -68,7 +68,7 @@ func (ma *Mongo) ReqConfs() *req.Confs {
}
func (m *Mongo) Mongos(rc *req.Ctx) {
queryCond := req.BindQuery(rc, new(entity.MongoQuery))
queryCond := req.BindQuery[*entity.MongoQuery](rc)
// 不存在可访问标签id即没有可操作数据
tags := m.tagTreeApp.GetAccountTags(rc.GetLoginAccount().Id, &tagentity.TagTreeQuery{
@@ -95,14 +95,12 @@ func (m *Mongo) Mongos(rc *req.Ctx) {
}
func (m *Mongo) TestConn(rc *req.Ctx) {
form := &form.Mongo{}
mongo := req.BindJsonAndCopyTo[*entity.Mongo](rc, form, new(entity.Mongo))
_, mongo := req.BindJsonAndCopyTo[*form.Mongo, *entity.Mongo](rc)
biz.ErrIsNilAppendErr(m.mongoApp.TestConn(mongo), "connection error: %s")
}
func (m *Mongo) Save(rc *req.Ctx) {
form := &form.Mongo{}
mongo := req.BindJsonAndCopyTo[*entity.Mongo](rc, form, new(entity.Mongo))
form, mongo := req.BindJsonAndCopyTo[*form.Mongo, *entity.Mongo](rc)
// 密码脱敏记录日志
form.Uri = func(str string) string {
@@ -148,8 +146,7 @@ func (m *Mongo) Collections(rc *req.Ctx) {
}
func (m *Mongo) RunCommand(rc *req.Ctx) {
commandForm := new(form.MongoRunCommand)
req.BindJsonAndValid(rc, commandForm)
commandForm := req.BindJsonAndValid[*form.MongoRunCommand](rc)
conn, err := m.mongoApp.GetMongoConn(rc.MetaCtx, m.GetMongoId(rc))
biz.ErrIsNil(err)
@@ -179,7 +176,7 @@ func (m *Mongo) RunCommand(rc *req.Ctx) {
}
func (m *Mongo) FindCommand(rc *req.Ctx) {
commandForm := req.BindJsonAndValid(rc, new(form.MongoFindCommand))
commandForm := req.BindJsonAndValid[*form.MongoFindCommand](rc)
conn, err := m.mongoApp.GetMongoConn(rc.MetaCtx, m.GetMongoId(rc))
biz.ErrIsNil(err)
@@ -214,7 +211,7 @@ func (m *Mongo) FindCommand(rc *req.Ctx) {
}
func (m *Mongo) UpdateByIdCommand(rc *req.Ctx) {
commandForm := req.BindJsonAndValid(rc, new(form.MongoUpdateByIdCommand))
commandForm := req.BindJsonAndValid[*form.MongoUpdateByIdCommand](rc)
conn, err := m.mongoApp.GetMongoConn(rc.MetaCtx, m.GetMongoId(rc))
biz.ErrIsNil(err)
@@ -238,7 +235,7 @@ func (m *Mongo) UpdateByIdCommand(rc *req.Ctx) {
}
func (m *Mongo) DeleteByIdCommand(rc *req.Ctx) {
commandForm := req.BindJsonAndValid(rc, new(form.MongoUpdateByIdCommand))
commandForm := req.BindJsonAndValid[*form.MongoUpdateByIdCommand](rc)
conn, err := m.mongoApp.GetMongoConn(rc.MetaCtx, m.GetMongoId(rc))
biz.ErrIsNil(err)
@@ -261,7 +258,7 @@ func (m *Mongo) DeleteByIdCommand(rc *req.Ctx) {
}
func (m *Mongo) InsertOneCommand(rc *req.Ctx) {
commandForm := req.BindJsonAndValid(rc, new(form.MongoInsertCommand))
commandForm := req.BindJsonAndValid[*form.MongoInsertCommand](rc)
conn, err := m.mongoApp.GetMongoConn(rc.MetaCtx, m.GetMongoId(rc))
biz.ErrIsNil(err)

View File

@@ -36,9 +36,8 @@ func (m *MsgChannel) GetMsgChannels(rc *req.Ctx) {
}
func (m *MsgChannel) SaveMsgChannels(rc *req.Ctx) {
form := &form.MsgChannel{}
form, channel := req.BindJsonAndCopyTo[*form.MsgChannel, *entity.MsgChannel](rc)
rc.ReqParam = form
channel := req.BindJsonAndCopyTo(rc, form, new(entity.MsgChannel))
err := m.msgChannelApp.SaveChannel(rc.MetaCtx, channel)
biz.ErrIsNil(err)
}

View File

@@ -57,9 +57,8 @@ func (m *MsgTmpl) GetMsgTmplChannels(rc *req.Ctx) {
}
func (m *MsgTmpl) SaveMsgTmpl(rc *req.Ctx) {
form := &form.MsgTmpl{}
form, channel := req.BindJsonAndCopyTo[*form.MsgTmpl, *dto.MsgTmplSave](rc)
rc.ReqParam = form
channel := req.BindJsonAndCopyTo(rc, form, new(dto.MsgTmplSave))
biz.ErrIsNil(m.msgTmplApp.SaveTmpl(rc.MetaCtx, channel))
}
@@ -75,7 +74,7 @@ func (m *MsgTmpl) DelMsgTmpls(rc *req.Ctx) {
func (m *MsgTmpl) SendMsg(rc *req.Ctx) {
code := rc.PathParam("code")
form := req.BindJsonAndValid(rc, new(form.SendMsg))
form := req.BindJsonAndValid[*form.SendMsg](rc)
rc.ReqParam = form

View File

@@ -11,8 +11,7 @@ import (
)
func (r *Redis) RunCmd(rc *req.Ctx) {
var cmdReq form.RunCmdForm
runCmdParam := req.BindJsonAndCopyTo(rc, &cmdReq, new(dto.RunCmd))
cmdReq, runCmdParam := req.BindJsonAndCopyTo[*form.RunCmdForm, *dto.RunCmd](rc)
biz.IsTrue(len(cmdReq.Cmd) > 0, "redis cmd cannot be empty")
redisConn := r.getRedisConn(rc)

View File

@@ -17,7 +17,7 @@ import (
func (r *Redis) ScanKeys(rc *req.Ctx) {
ri := r.getRedisConn(rc)
form := req.BindJsonAndValid(rc, new(form.RedisScanForm))
form := req.BindJsonAndValid[*form.RedisScanForm](rc)
cmd := ri.GetCmdable()
ctx := context.Background()

View File

@@ -60,7 +60,7 @@ func (rs *Redis) ReqConfs() *req.Confs {
}
func (r *Redis) RedisList(rc *req.Ctx) {
queryCond := req.BindQuery(rc, new(entity.RedisQuery))
queryCond := req.BindQuery[*entity.RedisQuery](rc)
// 不存在可访问标签id即没有可操作数据
tags := r.tagApp.GetAccountTags(rc.GetLoginAccount().Id, &tagentity.TagTreeQuery{
@@ -87,8 +87,7 @@ func (r *Redis) RedisList(rc *req.Ctx) {
}
func (r *Redis) TestConn(rc *req.Ctx) {
form := &form.Redis{}
redis := req.BindJsonAndCopyTo[*entity.Redis](rc, form, new(entity.Redis))
form, redis := req.BindJsonAndCopyTo[*form.Redis, *entity.Redis](rc)
authCert := &tagentity.ResourceAuthCert{
Username: form.Username,
@@ -110,8 +109,7 @@ func (r *Redis) TestConn(rc *req.Ctx) {
}
func (r *Redis) Save(rc *req.Ctx) {
form := &form.Redis{}
redis := req.BindJsonAndCopyTo[*entity.Redis](rc, form, new(entity.Redis))
form, redis := req.BindJsonAndCopyTo[*form.Redis, *entity.Redis](rc)
redisParam := &dto.SaveRedis{
Redis: redis,

View File

@@ -253,7 +253,7 @@ func (r *redisAppImpl) FlowBizHandle(ctx context.Context, bizHandleParam *flowap
return nil, nil
}
runCmdParam, err := jsonx.To(procinst.BizForm, new(FlowRedisRunCmdBizForm))
runCmdParam, err := jsonx.To[*FlowRedisRunCmdBizForm](procinst.BizForm)
if err != nil {
return nil, errorx.NewBiz("failed to parse the business form information: %s", err.Error())
}

View File

@@ -108,7 +108,7 @@ func (a *Account) GetPermissions(rc *req.Ctx) {
func (a *Account) ChangePassword(rc *req.Ctx) {
ctx := rc.MetaCtx
form := req.BindJsonAndValid(rc, new(form.AccountChangePasswordForm))
form := req.BindJsonAndValid[*form.AccountChangePasswordForm](rc)
originOldPwd, err := utils.DefaultRsaDecrypt(form.OldPassword, true)
biz.ErrIsNilAppendErr(err, "Wrong to decrypt old password: %s")
@@ -145,9 +145,10 @@ func (a *Account) AccountInfo(rc *req.Ctx) {
// 更新个人账号信息
func (a *Account) UpdateAccount(rc *req.Ctx) {
updateAccount := req.BindJsonAndCopyTo[*entity.Account](rc, new(form.AccountUpdateForm), new(entity.Account))
form, updateAccount := req.BindJsonAndCopyTo[*form.AccountUpdateForm, *entity.Account](rc)
// 账号id为登录者账号
updateAccount.Id = rc.GetLoginAccount().Id
rc.ReqParam = form
ctx := rc.MetaCtx
if updateAccount.Password != "" {
@@ -210,8 +211,7 @@ func (a *Account) AccountDetail(rc *req.Ctx) {
// @router /accounts
func (a *Account) SaveAccount(rc *req.Ctx) {
form := &form.AccountCreateForm{}
account := req.BindJsonAndCopyTo(rc, form, new(entity.Account))
form, account := req.BindJsonAndCopyTo[*form.AccountCreateForm, *entity.Account](rc)
form.Password = "*****"
rc.ReqParam = form
@@ -307,7 +307,7 @@ func (a *Account) AccountResources(rc *req.Ctx) {
// 关联账号角色
func (a *Account) RelateRole(rc *req.Ctx) {
form := req.BindJsonAndValid(rc, new(form.AccountRoleForm))
form := req.BindJsonAndValid[*form.AccountRoleForm](rc)
rc.ReqParam = form
biz.ErrIsNil(a.roleApp.RelateAccountRole(rc.MetaCtx, form.Id, form.RoleId, consts.AccountRoleRelateType(form.RelateType)))
}

View File

@@ -55,8 +55,7 @@ func (c *Config) GetConfigValueByKey(rc *req.Ctx) {
}
func (c *Config) SaveConfig(rc *req.Ctx) {
form := &form.ConfigForm{}
config := req.BindJsonAndCopyTo(rc, form, new(entity.Config))
form, config := req.BindJsonAndCopyTo[*form.ConfigForm, *entity.Config](rc)
rc.ReqParam = form
biz.ErrIsNil(c.configApp.Save(rc.MetaCtx, config))
}

View File

@@ -50,8 +50,7 @@ func (r *Resource) GetById(rc *req.Ctx) {
}
func (r *Resource) SaveResource(rc *req.Ctx) {
form := new(form.ResourceForm)
entity := req.BindJsonAndCopyTo(rc, form, new(entity.Resource))
form, entity := req.BindJsonAndCopyTo[*form.ResourceForm, *entity.Resource](rc)
rc.ReqParam = form

View File

@@ -39,7 +39,7 @@ func (r *Role) ReqConfs() *req.Confs {
}
func (r *Role) Roles(rc *req.Ctx) {
cond := req.BindQuery(rc, new(entity.RoleQuery))
cond := req.BindQuery[*entity.RoleQuery](rc)
notIdsStr := rc.Query("notIds")
if notIdsStr != "" {
@@ -61,8 +61,7 @@ func (r *Role) Roles(rc *req.Ctx) {
// 保存角色信息
func (r *Role) SaveRole(rc *req.Ctx) {
form := &form.RoleForm{}
role := req.BindJsonAndCopyTo(rc, form, new(entity.Role))
form, role := req.BindJsonAndCopyTo[*form.RoleForm, *entity.Role](rc)
rc.ReqParam = form
r.roleApp.SaveRole(rc.MetaCtx, role)
@@ -93,8 +92,7 @@ func (r *Role) RoleResource(rc *req.Ctx) {
// 保存角色资源
func (r *Role) SaveResource(rc *req.Ctx) {
var form form.RoleResourceForm
req.BindJsonAndValid(rc, &form)
form := req.BindJsonAndValid[*form.RoleResourceForm](rc)
rc.ReqParam = form
// 将,拼接的字符串进行切割并转换
@@ -107,7 +105,7 @@ func (r *Role) SaveResource(rc *req.Ctx) {
// 查看角色关联的用户
func (r *Role) RoleAccount(rc *req.Ctx) {
cond := req.BindQuery(rc, new(entity.RoleAccountQuery))
cond := req.BindQuery[*entity.RoleAccountQuery](rc)
cond.RoleId = uint64(rc.PathParamInt("id"))
res, err := r.roleApp.GetRoleAccountPage(cond)
biz.ErrIsNil(err)

View File

@@ -21,7 +21,7 @@ func (s *Syslog) ReqConfs() *req.Confs {
}
func (r *Syslog) Syslogs(rc *req.Ctx) {
queryCond := req.BindQuery(rc, new(entity.SysLogQuery))
queryCond := req.BindQuery[*entity.SysLogQuery](rc)
res, err := r.syslogApp.GetPageList(queryCond, "create_time DESC")
biz.ErrIsNil(err)
rc.ResData = res

View File

@@ -72,8 +72,7 @@ func (r *ResourceAuthCert) GetCompleteAuthCert(rc *req.Ctx) {
}
func (c *ResourceAuthCert) SaveAuthCert(rc *req.Ctx) {
acForm := &form.AuthCertForm{}
ac := req.BindJsonAndCopyTo(rc, acForm, new(entity.ResourceAuthCert))
acForm, ac := req.BindJsonAndCopyTo[*form.AuthCertForm, *entity.ResourceAuthCert](rc)
// 脱敏记录日志
acForm.Ciphertext = "***"

View File

@@ -119,8 +119,7 @@ func (p *TagTree) ListByQuery(rc *req.Ctx) {
}
func (p *TagTree) SaveTagTree(rc *req.Ctx) {
tagForm := &form.TagTree{}
tagTree := req.BindJsonAndCopyTo(rc, tagForm, new(entity.TagTree))
tagForm, tagTree := req.BindJsonAndCopyTo[*form.TagTree, *entity.TagTree](rc)
rc.ReqParam = fmt.Sprintf("tagTreeId: %d, tagName: %s, code: %s", tagTree.Id, tagTree.Name, tagTree.Code)
@@ -132,8 +131,7 @@ func (p *TagTree) DelTagTree(rc *req.Ctx) {
}
func (p *TagTree) MovingTag(rc *req.Ctx) {
movingForm := &form.MovingTag{}
req.BindJsonAndValid(rc, movingForm)
movingForm := req.BindJsonAndValid[*form.MovingTag](rc)
rc.ReqParam = movingForm
biz.ErrIsNil(p.tagTreeApp.MovingTag(rc.MetaCtx, movingForm.FromPath, movingForm.ToPath))
}

View File

@@ -46,7 +46,7 @@ func (t *Team) ReqConfs() *req.Confs {
}
func (p *Team) GetTeams(rc *req.Ctx) {
queryCond := req.BindQuery(rc, new(entity.TeamQuery))
queryCond := req.BindQuery[*entity.TeamQuery](rc)
res, err := p.teamApp.GetPageList(queryCond)
biz.ErrIsNil(err)
@@ -60,7 +60,7 @@ func (p *Team) GetTeams(rc *req.Ctx) {
}
func (p *Team) SaveTeam(rc *req.Ctx) {
team := req.BindJsonAndValid(rc, new(dto.SaveTeam))
team := req.BindJsonAndValid[*dto.SaveTeam](rc)
rc.ReqParam = team
biz.ErrIsNil(p.teamApp.SaveTeam(rc.MetaCtx, team))
}
@@ -87,7 +87,7 @@ func (p *Team) GetTeamMembers(rc *req.Ctx) {
// 保存团队信息
func (p *Team) SaveTeamMember(rc *req.Ctx) {
teamMems := req.BindJsonAndValid(rc, new(form.TeamMember))
teamMems := req.BindJsonAndValid[*form.TeamMember](rc)
teamId := teamMems.TeamId

View File

@@ -7,6 +7,8 @@ import (
)
type ResourceAuthCert struct {
model.ExtraData
Id uint64 `json:"id"`
Name string `json:"name"` // 名称
ResourceCode string `json:"resourceCode"` // 资源编号
@@ -14,7 +16,6 @@ type ResourceAuthCert struct {
Username string `json:"username"` // 用户名
Ciphertext string `json:"ciphertext"` // 密文
CiphertextType entity.AuthCertCiphertextType `json:"ciphertextType"` // 密文类型
Extra model.Map[string, any] `json:"extra"` // 账号需要的其他额外信息(如秘钥口令等)
Type entity.AuthCertType `json:"type"` // 凭证类型
Remark string `json:"remark"` // 备注

View File

@@ -1,8 +1,8 @@
package cache
import (
"encoding/json"
"mayfly-go/pkg/utils/anyx"
"mayfly-go/pkg/utils/jsonx"
"time"
"github.com/may-fly/cast"
@@ -65,7 +65,7 @@ func (dc *defaultCache) GetInt(k string) (int, bool) {
func (dc *defaultCache) GetJson(k string, valPtr any) bool {
if val, ok := dc.GetStr(k); ok {
jsonx.To(val, valPtr)
json.Unmarshal([]byte(val), valPtr)
return true
}
return false

View File

@@ -1,7 +1,6 @@
package model
import (
"mayfly-go/pkg/utils/collx"
"mayfly-go/pkg/utils/structx"
)
@@ -27,13 +26,8 @@ func PageResultConv[F any, T any](pageResult *PageResult[F]) *PageResult[T] {
if pageResult == nil {
return NewEmptyPageResult[T]()
}
return &PageResult[T]{
Total: pageResult.Total,
List: collx.ArrayMap(pageResult.List, func(item F) T {
t := structx.NewInstance[T]()
structx.Copy(t, item)
return t
}),
List: structx.CopySliceTo[F, T](pageResult.List),
}
}

View File

@@ -10,7 +10,8 @@ import (
)
// 绑定并校验请求结构体参数
func BindJsonAndValid[T any](rc *Ctx, data T) T {
func BindJsonAndValid[T any](rc *Ctx) T {
data := structx.NewInstance[T]()
if err := rc.BindJSON(data); err != nil {
panic(ConvBindValidationError(data, err))
} else {
@@ -18,15 +19,15 @@ func BindJsonAndValid[T any](rc *Ctx, data T) T {
}
}
// 绑定请求体中的json至form结构体并拷贝至另一结构体
func BindJsonAndCopyTo[T any](rc *Ctx, form any, toStruct T) T {
BindJsonAndValid(rc, form)
structx.Copy(toStruct, form)
return toStruct
// 绑定请求体中的json至form结构体并拷贝至指定结构体
func BindJsonAndCopyTo[F, T any](rc *Ctx) (F, T) {
f := BindJsonAndValid[F](rc)
return f, structx.CopyTo[T](f)
}
// 绑定查询字符串到指定结构体
func BindQuery[T any](rc *Ctx, data T) T {
func BindQuery[T any](rc *Ctx) T {
data := structx.NewInstance[T]()
if err := rc.BindQuery(data); err != nil {
panic(ConvBindValidationError(data, err))
} else {
@@ -35,7 +36,8 @@ func BindQuery[T any](rc *Ctx, data T) T {
}
// 绑定查询字符串到指定结构体,并将分页信息也返回
func BindQueryAndPage[T any](rc *Ctx, data T) (T, model.PageParam) {
func BindQueryAndPage[T any](rc *Ctx) (T, model.PageParam) {
data := structx.NewInstance[T]()
if err := rc.BindQuery(data); err != nil {
panic(ConvBindValidationError(data, err))
} else {

View File

@@ -4,6 +4,7 @@ import (
"encoding/json"
"mayfly-go/pkg/logx"
"mayfly-go/pkg/utils/collx"
"mayfly-go/pkg/utils/structx"
"github.com/tidwall/gjson"
)
@@ -16,9 +17,10 @@ func ToMap(jsonStr string) (collx.M, error) {
return ToMapByBytes([]byte(jsonStr))
}
// json字符串转结构体
func To[T any](jsonStr string, res T) (T, error) {
return res, json.Unmarshal([]byte(jsonStr), &res)
// json字符串转结构体, T需为指针类型
func To[T any](jsonStr string) (T, error) {
res := structx.NewInstance[T]()
return res, json.Unmarshal([]byte(jsonStr), res)
}
// json字节数组转map

View File

@@ -0,0 +1,874 @@
package structx
import (
"database/sql"
"database/sql/driver"
"errors"
"fmt"
"reflect"
"strings"
"sync"
"unicode"
)
// github.com/jinzhu/copier
var (
ErrInvalidCopyDestination = errors.New("copy destination must be non-nil and addressable")
ErrInvalidCopyFrom = errors.New("copy from must be non-nil and addressable")
ErrMapKeyNotMatch = errors.New("map's key type doesn't match")
ErrNotSupported = errors.New("not supported")
ErrFieldNameTagStartNotUpperCase = errors.New("copier field name tag must be start upper case")
)
// These flags define options for tag handling
const (
// Denotes that a destination field must be copied to. If copying fails then a panic will ensue.
tagMust uint8 = 1 << iota
// Denotes that the program should not panic when the must flag is on and
// value is not copied. The program will return an error instead.
tagNoPanic
// Ignore a destination field from being copied to.
tagIgnore
// Denotes the fact that the field should be overridden, no matter if the IgnoreEmpty is set
tagOverride
// Denotes that the value as been copied
hasCopied
// Some default converter types for a nicer syntax
String string = ""
Bool bool = false
Int int = 0
Float32 float32 = 0
Float64 float64 = 0
)
// Option sets copy options
type Option struct {
// setting this value to true will ignore copying zero values of all the fields, including bools, as well as a
// struct having all it's fields set to their zero values respectively (see IsZero() in reflect/value.go)
IgnoreEmpty bool
CaseSensitive bool
DeepCopy bool
Converters []TypeConverter
// Custom field name mappings to copy values with different names in `fromValue` and `toValue` types.
// Examples can be found in `copier_field_name_mapping_test.go`.
FieldNameMapping []FieldNameMapping
}
func (opt Option) converters() map[converterPair]TypeConverter {
var converters = map[converterPair]TypeConverter{}
// save converters into map for faster lookup
for i := range opt.Converters {
pair := converterPair{
SrcType: reflect.TypeOf(opt.Converters[i].SrcType),
DstType: reflect.TypeOf(opt.Converters[i].DstType),
}
converters[pair] = opt.Converters[i]
}
return converters
}
type TypeConverter struct {
SrcType interface{}
DstType interface{}
Fn func(src interface{}) (dst interface{}, err error)
}
type converterPair struct {
SrcType reflect.Type
DstType reflect.Type
}
func (opt Option) fieldNameMapping() map[converterPair]FieldNameMapping {
var mapping = map[converterPair]FieldNameMapping{}
for i := range opt.FieldNameMapping {
pair := converterPair{
SrcType: reflect.TypeOf(opt.FieldNameMapping[i].SrcType),
DstType: reflect.TypeOf(opt.FieldNameMapping[i].DstType),
}
mapping[pair] = opt.FieldNameMapping[i]
}
return mapping
}
type FieldNameMapping struct {
SrcType interface{}
DstType interface{}
Mapping map[string]string
}
// Tag Flags
type flags struct {
BitFlags map[string]uint8
SrcNames tagNameMapping
DestNames tagNameMapping
}
// Field Tag name mapping
type tagNameMapping struct {
FieldNameToTag map[string]string
TagToFieldName map[string]string
}
// Copy copy things
func Copy(toValue interface{}, fromValue interface{}) (err error) {
return copier(toValue, fromValue, Option{})
}
// CopyWithOption copy with option
func CopyWithOption(toValue interface{}, fromValue interface{}, opt Option) (err error) {
return copier(toValue, fromValue, opt)
}
func copier(toValue interface{}, fromValue interface{}, opt Option) (err error) {
var (
isSlice bool
amount = 1
from = indirect(reflect.ValueOf(fromValue))
to = indirect(reflect.ValueOf(toValue))
converters = opt.converters()
mappings = opt.fieldNameMapping()
)
if !to.CanAddr() {
return ErrInvalidCopyDestination
}
// Return is from value is invalid
if !from.IsValid() {
return ErrInvalidCopyFrom
}
fromType, isPtrFrom := indirectType(from.Type())
toType, _ := indirectType(to.Type())
if fromType.Kind() == reflect.Interface {
fromType = reflect.TypeOf(from.Interface())
}
if toType.Kind() == reflect.Interface {
toType, _ = indirectType(reflect.TypeOf(to.Interface()))
oldTo := to
to = reflect.New(reflect.TypeOf(to.Interface())).Elem()
defer func() {
oldTo.Set(to)
}()
}
// Just set it if possible to assign for normal types
if from.Kind() != reflect.Slice && from.Kind() != reflect.Struct && from.Kind() != reflect.Map && (from.Type().AssignableTo(to.Type()) || from.Type().ConvertibleTo(to.Type())) {
if !isPtrFrom || !opt.DeepCopy {
to.Set(from.Convert(to.Type()))
} else {
fromCopy := reflect.New(from.Type())
fromCopy.Set(from.Elem())
to.Set(fromCopy.Convert(to.Type()))
}
return
}
if from.Kind() != reflect.Slice && fromType.Kind() == reflect.Map && toType.Kind() == reflect.Map {
if !fromType.Key().ConvertibleTo(toType.Key()) {
return ErrMapKeyNotMatch
}
if to.IsNil() {
to.Set(reflect.MakeMapWithSize(toType, from.Len()))
}
for _, k := range from.MapKeys() {
toKey := indirect(reflect.New(toType.Key()))
isSet, err := set(toKey, k, opt.DeepCopy, converters)
if err != nil {
return err
}
if !isSet {
return fmt.Errorf("%w map, old key: %v, new key: %v", ErrNotSupported, k.Type(), toType.Key())
}
elemType := toType.Elem()
if elemType.Kind() != reflect.Slice {
elemType, _ = indirectType(elemType)
}
toValue := indirect(reflect.New(elemType))
isSet, err = set(toValue, from.MapIndex(k), opt.DeepCopy, converters)
if err != nil {
return err
}
if !isSet {
if err = copier(toValue.Addr().Interface(), from.MapIndex(k).Interface(), opt); err != nil {
return err
}
}
for {
if elemType == toType.Elem() {
to.SetMapIndex(toKey, toValue)
break
}
elemType = reflect.PointerTo(elemType)
toValue = toValue.Addr()
}
}
return
}
if from.Kind() == reflect.Slice && to.Kind() == reflect.Slice {
// Return directly if both slices are nil
if from.IsNil() && to.IsNil() {
return
}
if to.IsNil() {
slice := reflect.MakeSlice(reflect.SliceOf(to.Type().Elem()), from.Len(), from.Cap())
to.Set(slice)
}
if fromType.ConvertibleTo(toType) {
for i := 0; i < from.Len(); i++ {
if to.Len() < i+1 {
to.Set(reflect.Append(to, reflect.New(to.Type().Elem()).Elem()))
}
isSet, err := set(to.Index(i), from.Index(i), opt.DeepCopy, converters)
if err != nil {
return err
}
if !isSet {
// ignore error while copy slice element
err = copier(to.Index(i).Addr().Interface(), from.Index(i).Interface(), opt)
if err != nil {
continue
}
}
}
if to.Len() > from.Len() {
to.SetLen(from.Len())
}
return
}
}
if fromType.Kind() != reflect.Struct || toType.Kind() != reflect.Struct {
// skip not supported type
return
}
if len(converters) > 0 {
if ok, e := set(to, from, opt.DeepCopy, converters); e == nil && ok {
// converter supported
return
}
}
if from.Kind() == reflect.Slice || to.Kind() == reflect.Slice {
isSlice = true
if from.Kind() == reflect.Slice {
amount = from.Len()
}
}
for i := 0; i < amount; i++ {
var dest, source reflect.Value
if isSlice {
// source
if from.Kind() == reflect.Slice {
source = indirect(from.Index(i))
} else {
source = indirect(from)
}
// dest
dest = indirect(reflect.New(toType).Elem())
} else {
source = indirect(from)
dest = indirect(to)
}
if len(converters) > 0 {
if ok, e := set(dest, source, opt.DeepCopy, converters); e == nil && ok {
if isSlice {
// FIXME: maybe should check the other types?
if to.Type().Elem().Kind() == reflect.Ptr {
to.Index(i).Set(dest.Addr())
} else {
if to.Len() < i+1 {
reflect.Append(to, dest)
} else {
to.Index(i).Set(dest)
}
}
} else {
to.Set(dest)
}
continue
}
}
destKind := dest.Kind()
initDest := false
if destKind == reflect.Interface {
initDest = true
dest = indirect(reflect.New(toType))
}
// Get tag options
flgs, err := getFlags(dest, source, toType, fromType)
if err != nil {
return err
}
// check source
if source.IsValid() {
copyUnexportedStructFields(dest, source)
// Copy from source field to dest field or method
fromTypeFields := deepFields(fromType)
for _, field := range fromTypeFields {
name := field.Name
// Get bit flags for field
fieldFlags := flgs.BitFlags[name]
// Check if we should ignore copying
if (fieldFlags & tagIgnore) != 0 {
continue
}
fieldNamesMapping := getFieldNamesMapping(mappings, fromType, toType)
srcFieldName, destFieldName := getFieldName(name, flgs, fieldNamesMapping)
if fromField := fieldByNameOrZeroValue(source, srcFieldName); fromField.IsValid() && !shouldIgnore(fromField, fieldFlags, opt.IgnoreEmpty) {
// process for nested anonymous field
destFieldNotSet := false
if f, ok := dest.Type().FieldByName(destFieldName); ok {
// only initialize parent embedded struct pointer in the path
for idx := range f.Index[:len(f.Index)-1] {
destField := dest.FieldByIndex(f.Index[:idx+1])
if destField.Kind() != reflect.Ptr {
continue
}
if !destField.IsNil() {
continue
}
if !destField.CanSet() {
destFieldNotSet = true
break
}
// destField is a nil pointer that can be set
newValue := reflect.New(destField.Type().Elem())
destField.Set(newValue)
}
}
if destFieldNotSet {
break
}
toField := fieldByName(dest, destFieldName, opt.CaseSensitive)
if toField.IsValid() {
if toField.CanSet() {
isSet, err := set(toField, fromField, opt.DeepCopy, converters)
if err != nil {
return err
}
if !isSet {
if err := copier(toField.Addr().Interface(), fromField.Interface(), opt); err != nil {
return err
}
}
if fieldFlags != 0 {
// Note that a copy was made
flgs.BitFlags[name] = fieldFlags | hasCopied
}
}
} else {
// try to set to method
var toMethod reflect.Value
if dest.CanAddr() {
toMethod = dest.Addr().MethodByName(destFieldName)
} else {
toMethod = dest.MethodByName(destFieldName)
}
if toMethod.IsValid() && toMethod.Type().NumIn() == 1 && fromField.Type().AssignableTo(toMethod.Type().In(0)) {
toMethod.Call([]reflect.Value{fromField})
}
}
}
}
// Copy from from method to dest field
for _, field := range deepFields(toType) {
name := field.Name
srcFieldName, destFieldName := getFieldName(name, flgs, getFieldNamesMapping(mappings, fromType, toType))
var fromMethod reflect.Value
if source.CanAddr() {
fromMethod = source.Addr().MethodByName(srcFieldName)
} else {
fromMethod = source.MethodByName(srcFieldName)
}
if fromMethod.IsValid() && fromMethod.Type().NumIn() == 0 && fromMethod.Type().NumOut() == 1 && !shouldIgnore(fromMethod, flgs.BitFlags[name], opt.IgnoreEmpty) {
if toField := fieldByName(dest, destFieldName, opt.CaseSensitive); toField.IsValid() && toField.CanSet() {
values := fromMethod.Call([]reflect.Value{})
if len(values) >= 1 {
set(toField, values[0], opt.DeepCopy, converters)
}
}
}
}
}
if isSlice && to.Kind() == reflect.Slice {
if dest.Addr().Type().AssignableTo(to.Type().Elem()) {
if to.Len() < i+1 {
to.Set(reflect.Append(to, dest.Addr()))
} else {
isSet, err := set(to.Index(i), dest.Addr(), opt.DeepCopy, converters)
if err != nil {
return err
}
if !isSet {
// ignore error while copy slice element
err = copier(to.Index(i).Addr().Interface(), dest.Addr().Interface(), opt)
if err != nil {
continue
}
}
}
} else if dest.Type().AssignableTo(to.Type().Elem()) {
if to.Len() < i+1 {
to.Set(reflect.Append(to, dest))
} else {
isSet, err := set(to.Index(i), dest, opt.DeepCopy, converters)
if err != nil {
return err
}
if !isSet {
// ignore error while copy slice element
err = copier(to.Index(i).Addr().Interface(), dest.Interface(), opt)
if err != nil {
continue
}
}
}
}
} else if initDest {
to.Set(dest)
}
err = checkBitFlags(flgs.BitFlags)
}
return
}
func getFieldNamesMapping(mappings map[converterPair]FieldNameMapping, fromType reflect.Type, toType reflect.Type) map[string]string {
var fieldNamesMapping map[string]string
if len(mappings) > 0 {
pair := converterPair{
SrcType: fromType,
DstType: toType,
}
if v, ok := mappings[pair]; ok {
fieldNamesMapping = v.Mapping
}
}
return fieldNamesMapping
}
func fieldByNameOrZeroValue(source reflect.Value, fieldName string) (value reflect.Value) {
defer func() {
if err := recover(); err != nil {
value = reflect.Value{}
}
}()
return source.FieldByName(fieldName)
}
func copyUnexportedStructFields(to, from reflect.Value) {
if from.Kind() != reflect.Struct || to.Kind() != reflect.Struct || !from.Type().AssignableTo(to.Type()) {
return
}
// create a shallow copy of 'to' to get all fields
tmp := indirect(reflect.New(to.Type()))
tmp.Set(from)
// revert exported fields
for i := 0; i < to.NumField(); i++ {
if tmp.Field(i).CanSet() {
tmp.Field(i).Set(to.Field(i))
}
}
to.Set(tmp)
}
func shouldIgnore(v reflect.Value, bitFlags uint8, ignoreEmpty bool) bool {
return ignoreEmpty && bitFlags&tagOverride == 0 && v.IsZero()
}
var deepFieldsLock sync.RWMutex
var deepFieldsMap = make(map[reflect.Type][]reflect.StructField)
func deepFields(reflectType reflect.Type) []reflect.StructField {
deepFieldsLock.RLock()
cache, ok := deepFieldsMap[reflectType]
deepFieldsLock.RUnlock()
if ok {
return cache
}
var res []reflect.StructField
if reflectType, _ = indirectType(reflectType); reflectType.Kind() == reflect.Struct {
fields := make([]reflect.StructField, 0, reflectType.NumField())
for i := 0; i < reflectType.NumField(); i++ {
v := reflectType.Field(i)
// PkgPath is the package path that qualifies a lower case (unexported)
// field name. It is empty for upper case (exported) field names.
// See https://golang.org/ref/spec#Uniqueness_of_identifiers
if v.PkgPath == "" {
fields = append(fields, v)
if v.Anonymous {
// also consider fields of anonymous fields as fields of the root
fields = append(fields, deepFields(v.Type)...)
}
}
}
res = fields
}
deepFieldsLock.Lock()
deepFieldsMap[reflectType] = res
deepFieldsLock.Unlock()
return res
}
func indirect(reflectValue reflect.Value) reflect.Value {
for reflectValue.Kind() == reflect.Ptr {
reflectValue = reflectValue.Elem()
}
return reflectValue
}
func indirectType(reflectType reflect.Type) (_ reflect.Type, isPtr bool) {
for reflectType.Kind() == reflect.Ptr || reflectType.Kind() == reflect.Slice {
reflectType = reflectType.Elem()
isPtr = true
}
return reflectType, isPtr
}
func set(to, from reflect.Value, deepCopy bool, converters map[converterPair]TypeConverter) (bool, error) {
if !from.IsValid() {
return true, nil
}
if ok, err := lookupAndCopyWithConverter(to, from, converters); err != nil {
return false, err
} else if ok {
return true, nil
}
if to.Kind() == reflect.Ptr {
// set `to` to nil if from is nil
if from.Kind() == reflect.Ptr && from.IsNil() {
to.Set(reflect.Zero(to.Type()))
return true, nil
} else if to.IsNil() {
// `from` -> `to`
// sql.NullString -> *string
if fromValuer, ok := driverValuer(from); ok {
v, err := fromValuer.Value()
if err != nil {
return true, nil
}
// if `from` is not valid do nothing with `to`
if v == nil {
return true, nil
}
}
// allocate new `to` variable with default value (eg. *string -> new(string))
to.Set(reflect.New(to.Type().Elem()))
} else if from.Kind() != reflect.Ptr && from.IsZero() {
to.Set(reflect.Zero(to.Type()))
return true, nil
}
// depointer `to`
to = to.Elem()
}
if deepCopy {
toKind := to.Kind()
if toKind == reflect.Interface && to.IsNil() {
if reflect.TypeOf(from.Interface()) != nil {
to.Set(reflect.New(reflect.TypeOf(from.Interface())).Elem())
toKind = reflect.TypeOf(to.Interface()).Kind()
}
}
if from.Kind() == reflect.Ptr && from.IsNil() {
to.Set(reflect.Zero(to.Type()))
return true, nil
}
if _, ok := to.Addr().Interface().(sql.Scanner); !ok && (toKind == reflect.Struct || toKind == reflect.Map || toKind == reflect.Slice) {
return false, nil
}
}
// try convert directly
if from.Type().ConvertibleTo(to.Type()) {
to.Set(from.Convert(to.Type()))
return true, nil
}
// try Scanner
if toScanner, ok := to.Addr().Interface().(sql.Scanner); ok {
// `from` -> `to`
// *string -> sql.NullString
if from.Kind() == reflect.Ptr {
// if `from` is nil do nothing with `to`
if from.IsNil() {
return true, nil
}
// depointer `from`
from = indirect(from)
}
// `from` -> `to`
// string -> sql.NullString
// set `to` by invoking method Scan(`from`)
err := toScanner.Scan(from.Interface())
if err == nil {
return true, nil
}
}
// try Valuer
if fromValuer, ok := driverValuer(from); ok {
// `from` -> `to`
// sql.NullString -> string
v, err := fromValuer.Value()
if err != nil {
return false, nil
}
// if `from` is not valid do nothing with `to`
if v == nil {
return true, nil
}
rv := reflect.ValueOf(v)
if rv.Type().AssignableTo(to.Type()) {
to.Set(rv)
return true, nil
}
if to.CanSet() && rv.Type().ConvertibleTo(to.Type()) {
to.Set(rv.Convert(to.Type()))
return true, nil
}
return false, nil
}
// from is ptr
if from.Kind() == reflect.Ptr {
return set(to, from.Elem(), deepCopy, converters)
}
return false, nil
}
// lookupAndCopyWithConverter looks up the type pair, on success the TypeConverter Fn func is called to copy src to dst field.
func lookupAndCopyWithConverter(to, from reflect.Value, converters map[converterPair]TypeConverter) (copied bool, err error) {
pair := converterPair{
SrcType: from.Type(),
DstType: to.Type(),
}
if cnv, ok := converters[pair]; ok {
result, err := cnv.Fn(from.Interface())
if err != nil {
return false, err
}
if result != nil {
to.Set(reflect.ValueOf(result))
} else {
// in case we've got a nil value to copy
to.Set(reflect.Zero(to.Type()))
}
return true, nil
}
return false, nil
}
// parseTags Parses struct tags and returns uint8 bit flags.
func parseTags(tag string) (flg uint8, name string, err error) {
for _, t := range strings.Split(tag, ",") {
switch t {
case "-":
flg = tagIgnore
return
case "must":
flg = flg | tagMust
case "nopanic":
flg = flg | tagNoPanic
case "override":
flg = flg | tagOverride
default:
if unicode.IsUpper([]rune(t)[0]) {
name = strings.TrimSpace(t)
} else {
err = ErrFieldNameTagStartNotUpperCase
}
}
}
return
}
// getTagFlags Parses struct tags for bit flags, field name.
func getFlags(dest, src reflect.Value, toType, fromType reflect.Type) (flags, error) {
flgs := flags{
BitFlags: map[string]uint8{},
SrcNames: tagNameMapping{
FieldNameToTag: map[string]string{},
TagToFieldName: map[string]string{},
},
DestNames: tagNameMapping{
FieldNameToTag: map[string]string{},
TagToFieldName: map[string]string{},
},
}
var toTypeFields, fromTypeFields []reflect.StructField
if dest.IsValid() {
toTypeFields = deepFields(toType)
}
if src.IsValid() {
fromTypeFields = deepFields(fromType)
}
// Get a list dest of tags
for _, field := range toTypeFields {
tags := field.Tag.Get("copier")
if tags != "" {
var name string
var err error
if flgs.BitFlags[field.Name], name, err = parseTags(tags); err != nil {
return flags{}, err
} else if name != "" {
flgs.DestNames.FieldNameToTag[field.Name] = name
flgs.DestNames.TagToFieldName[name] = field.Name
}
}
}
// Get a list source of tags
for _, field := range fromTypeFields {
tags := field.Tag.Get("copier")
if tags != "" {
var name string
var err error
if _, name, err = parseTags(tags); err != nil {
return flags{}, err
} else if name != "" {
flgs.SrcNames.FieldNameToTag[field.Name] = name
flgs.SrcNames.TagToFieldName[name] = field.Name
}
}
}
return flgs, nil
}
// checkBitFlags Checks flags for error or panic conditions.
func checkBitFlags(flagsList map[string]uint8) (err error) {
// Check flag conditions were met
for name, flgs := range flagsList {
if flgs&hasCopied == 0 {
switch {
case flgs&tagMust != 0 && flgs&tagNoPanic != 0:
err = fmt.Errorf("field %s has must tag but was not copied", name)
return
case flgs&(tagMust) != 0:
panic(fmt.Sprintf("Field %s has must tag but was not copied", name))
}
}
}
return
}
func getFieldName(fieldName string, flgs flags, fieldNameMapping map[string]string) (srcFieldName string, destFieldName string) {
// get dest field name
if name, ok := fieldNameMapping[fieldName]; ok {
srcFieldName = fieldName
destFieldName = name
return
}
if srcTagName, ok := flgs.SrcNames.FieldNameToTag[fieldName]; ok {
destFieldName = srcTagName
if destTagName, ok := flgs.DestNames.TagToFieldName[srcTagName]; ok {
destFieldName = destTagName
}
} else {
if destTagName, ok := flgs.DestNames.TagToFieldName[fieldName]; ok {
destFieldName = destTagName
}
}
if destFieldName == "" {
destFieldName = fieldName
}
// get source field name
if destTagName, ok := flgs.DestNames.FieldNameToTag[fieldName]; ok {
srcFieldName = destTagName
if srcField, ok := flgs.SrcNames.TagToFieldName[destTagName]; ok {
srcFieldName = srcField
}
} else {
if srcField, ok := flgs.SrcNames.TagToFieldName[fieldName]; ok {
srcFieldName = srcField
}
}
if srcFieldName == "" {
srcFieldName = fieldName
}
return
}
func driverValuer(v reflect.Value) (i driver.Valuer, ok bool) {
if !v.CanAddr() {
i, ok = v.Interface().(driver.Valuer)
return
}
i, ok = v.Addr().Interface().(driver.Valuer)
return
}
func fieldByName(v reflect.Value, name string, caseSensitive bool) reflect.Value {
if caseSensitive {
return v.FieldByName(name)
}
return v.FieldByNameFunc(func(n string) bool { return strings.EqualFold(n, name) })
}

View File

@@ -1,7 +1,6 @@
package structx
import (
"database/sql"
"encoding/json"
"errors"
"fmt"
@@ -11,126 +10,18 @@ import (
"strings"
)
// Copy copy things引用至copier
func Copy(toValue any, fromValue any) (err error) {
var (
isSlice bool
amount = 1
from = Indirect(reflect.ValueOf(fromValue))
to = Indirect(reflect.ValueOf(toValue))
)
// CopyTo 将fromValue转为T类型并返回
func CopyTo[T any](fromValue any) T {
t := NewInstance[T]()
Copy(t, fromValue)
return t
}
if !to.CanAddr() {
return errors.New("copy to value is unaddressable")
}
// Return is from value is invalid
if !from.IsValid() {
return
}
fromType := IndirectType(from.Type())
toType := IndirectType(to.Type())
// Just set it if possible to assign
// And need to do copy anyway if the type is struct
if fromType.Kind() != reflect.Struct && from.Type().AssignableTo(to.Type()) {
to.Set(from)
return
}
if fromType.Kind() != reflect.Struct || toType.Kind() != reflect.Struct {
return
}
if to.Kind() == reflect.Slice {
isSlice = true
if from.Kind() == reflect.Slice {
amount = from.Len()
}
}
for i := 0; i < amount; i++ {
var dest, source reflect.Value
if isSlice {
// source
if from.Kind() == reflect.Slice {
source = Indirect(from.Index(i))
} else {
source = Indirect(from)
}
// dest
dest = Indirect(reflect.New(toType).Elem())
} else {
source = Indirect(from)
dest = Indirect(to)
}
// check source
if source.IsValid() {
fromTypeFields := deepFields(fromType)
//fmt.Printf("%#v", fromTypeFields)
// Copy from field to field or method
for _, field := range fromTypeFields {
name := field.Name
if fromField := source.FieldByName(name); fromField.IsValid() {
// has field
if toField := dest.FieldByName(name); toField.IsValid() {
if toField.CanSet() {
if !set(toField, fromField) {
if err := Copy(toField.Addr().Interface(), fromField.Interface()); err != nil {
return err
}
}
}
} else {
// try to set to method
var toMethod reflect.Value
if dest.CanAddr() {
toMethod = dest.Addr().MethodByName(name)
} else {
toMethod = dest.MethodByName(name)
}
if toMethod.IsValid() && toMethod.Type().NumIn() == 1 && fromField.Type().AssignableTo(toMethod.Type().In(0)) {
toMethod.Call([]reflect.Value{fromField})
}
}
}
}
// Copy from method to field
for _, field := range deepFields(toType) {
name := field.Name
var fromMethod reflect.Value
if source.CanAddr() {
fromMethod = source.Addr().MethodByName(name)
} else {
fromMethod = source.MethodByName(name)
}
if fromMethod.IsValid() && fromMethod.Type().NumIn() == 0 && fromMethod.Type().NumOut() == 1 {
if toField := dest.FieldByName(name); toField.IsValid() && toField.CanSet() {
values := fromMethod.Call([]reflect.Value{})
if len(values) >= 1 {
set(toField, values[0])
}
}
}
}
}
if isSlice {
if dest.Addr().Type().AssignableTo(to.Type().Elem()) {
to.Set(reflect.Append(to, dest.Addr()))
} else if dest.Type().AssignableTo(to.Type().Elem()) {
to.Set(reflect.Append(to, dest))
}
}
}
return
// CopySliceTo 将fromValue转为[]T类型并返回
func CopySliceTo[F, T any](fromValue []F) []T {
var to []T
Copy(&to, fromValue)
return to
}
// 对结构体的每个字段以及字段值执行doWith回调函数, 包括匿名属性的字段
@@ -158,23 +49,6 @@ func DoWithFields(str any, doWith func(fType reflect.StructField, fValue reflect
return nil
}
func deepFields(reflectType reflect.Type) []reflect.StructField {
var fields []reflect.StructField
if reflectType = IndirectType(reflectType); reflectType.Kind() == reflect.Struct {
for i := 0; i < reflectType.NumField(); i++ {
v := reflectType.Field(i)
if v.Anonymous {
fields = append(fields, deepFields(v.Type)...)
} else {
fields = append(fields, v)
}
}
}
return fields
}
func Indirect(reflectValue reflect.Value) reflect.Value {
for reflectValue.Kind() == reflect.Ptr {
reflectValue = reflectValue.Elem()
@@ -189,35 +63,6 @@ func IndirectType(reflectType reflect.Type) reflect.Type {
return reflectType
}
func set(to, from reflect.Value) bool {
if from.IsValid() {
if to.Kind() == reflect.Ptr {
//set `to` to nil if from is nil
if from.Kind() == reflect.Ptr && from.IsNil() {
to.Set(reflect.Zero(to.Type()))
return true
} else if to.IsNil() {
to.Set(reflect.New(to.Type().Elem()))
}
to = to.Elem()
}
if from.Type().ConvertibleTo(to.Type()) {
to.Set(from.Convert(to.Type()))
} else if scanner, ok := to.Addr().Interface().(sql.Scanner); ok {
err := scanner.Scan(from.Interface())
if err != nil {
return false
}
} else if from.Kind() == reflect.Ptr {
return set(to, from.Elem())
} else {
return false
}
}
return true
}
func Map2Struct(m map[string]any, s any) error {
toValue := Indirect(reflect.ValueOf(s))
if !toValue.CanAddr() {