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 ## 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 ## Development languages and major frameworks

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -12,7 +12,7 @@
lazy lazy
> >
<template #tableHeader> <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> <el-button type="danger" icon="delete" :disabled="selectionData.length < 1" @click="deleteMongo" plain>{{ $t('common.delete') }}</el-button>
</template> </template>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -5,7 +5,7 @@
<div class="card !p-1 mr-1 flex justify-between"> <div class="card !p-1 mr-1 flex justify-between">
<div class="mb-1"> <div class="mb-1">
<el-input v-model="filterResource" clearable :placeholder="$t('system.menu.filterPlaceholder')" class="mr-2 !w-[200px]" /> <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>
<div> <div>
@@ -27,7 +27,7 @@
@node-expand="handleNodeExpand" @node-expand="handleNodeExpand"
@node-collapse="handleNodeCollapse" @node-collapse="handleNodeCollapse"
@node-contextmenu="nodeContextmenu" @node-contextmenu="nodeContextmenu"
@node-click="treeNodeClick" @node-click="onTreeNodeClick"
:default-expanded-keys="defaultExpandedKeys" :default-expanded-keys="defaultExpandedKeys"
:expand-on-click-node="false" :expand-on-click-node="false"
draggable draggable
@@ -136,7 +136,7 @@
:typeDisabled="dialogForm.typeDisabled" :typeDisabled="dialogForm.typeDisabled"
:departTree="data" :departTree="data"
:type="dialogForm.type" :type="dialogForm.type"
@val-change="valChange" @val-change="onValChange"
/> />
<contextmenu :dropdown="state.contextmenu.dropdown" :items="state.contextmenu.items" ref="contextmenuRef" /> <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') .withIcon('circle-plus')
.withPermission(perms.addResource) .withPermission(perms.addResource)
.withHideFunc((data: any) => data.type !== menuTypeValue) .withHideFunc((data: any) => data.type !== menuTypeValue)
.withOnClick((data: any) => addResource(data)); .withOnClick((data: any) => onAddResource(data));
const contextmenuEdit = new ContextmenuItem('edit', 'common.edit') const contextmenuEdit = new ContextmenuItem('edit', 'common.edit')
.withIcon('edit') .withIcon('edit')
.withPermission(perms.updateResource) .withPermission(perms.updateResource)
.withOnClick((data: any) => editResource(data)); .withOnClick((data: any) => onEditResource(data));
const contextmenuEnable = new ContextmenuItem('enable', 'system.menu.enable') const contextmenuEnable = new ContextmenuItem('enable', 'system.menu.enable')
.withIcon('circle-check') .withIcon('circle-check')
.withPermission(perms.updateResource) .withPermission(perms.updateResource)
.withHideFunc((data: any) => data.status === 1) .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') const contextmenuDisable = new ContextmenuItem('disable', 'system.menu.disable')
.withIcon('circle-close') .withIcon('circle-close')
.withPermission(perms.updateResource) .withPermission(perms.updateResource)
.withHideFunc((data: any) => data.status === -1) .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') const contextmenuDel = new ContextmenuItem('delete', 'common.delete')
.withIcon('delete') .withIcon('delete')
.withPermission(perms.delResource) .withPermission(perms.delResource)
.withOnClick((data: any) => deleteMenu(data)); .withOnClick((data: any) => onDeleteMenu(data));
const state = reactive({ const state = reactive({
contextmenu: { contextmenu: {
@@ -263,7 +263,7 @@ const nodeContextmenu = (event: any, data: any) => {
contextmenuRef.value.openContextmenu(data); contextmenuRef.value.openContextmenu(data);
}; };
const treeNodeClick = async (data: any) => { const onTreeNodeClick = async (data: any) => {
state.activeTabName = ResourceDetail; state.activeTabName = ResourceDetail;
// 关闭可能存在的右击菜单 // 关闭可能存在的右击菜单
contextmenuRef.value.closeContextmenu(); 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 useI18nDeleteConfirm(data.name);
await resourceApi.del.request({ await resourceApi.del.request({
id: data.id, id: data.id,
@@ -296,7 +296,7 @@ const deleteMenu = async (data: any) => {
search(); search();
}; };
const addResource = (data: any) => { const onAddResource = (data: any) => {
let dialog = state.dialogForm; let dialog = state.dialogForm;
dialog.data = { pid: 0, type: 1 }; dialog.data = { pid: 0, type: 1 };
// 添加顶级菜单情况 // 添加顶级菜单情况
@@ -333,7 +333,7 @@ const addResource = (data: any) => {
dialog.visible = true; dialog.visible = true;
}; };
const editResource = async (data: any) => { const onEditResource = async (data: any) => {
const res = await resourceApi.detail.request({ const res = await resourceApi.detail.request({
id: data.id, id: data.id,
}); });
@@ -347,12 +347,12 @@ const editResource = async (data: any) => {
state.dialogForm.visible = true; state.dialogForm.visible = true;
}; };
const valChange = () => { const onValChange = () => {
search(); search();
state.dialogForm.visible = false; state.dialogForm.visible = false;
}; };
const changeStatus = async (data: any, status: any) => { const onChangeStatus = async (data: any, status: any) => {
await resourceApi.changeStatus.request({ await resourceApi.changeStatus.request({
id: data.id, id: data.id,
status: status, 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> <page-table ref="pageTableRef" :page-api="roleApi.roleAccounts" :search-items="searchItems" v-model:query-form="query" :columns="columns" lazy>
<template #tableHeader> <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>
<template #action="{ data }"> <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') }} {{ $t('common.remove') }}
</el-button> </el-button>
</template> </template>
@@ -22,7 +22,7 @@
<el-dialog <el-dialog
width="400px" width="400px"
:title="$t('system.role.addAccount')" :title="$t('system.role.addAccount')"
:before-close="cancelAddAccount" :before-close="onCancelAddAccount"
v-model="addAccountDialog.visible" v-model="addAccountDialog.visible"
:destroy-on-close="true" :destroy-on-close="true"
> >
@@ -31,8 +31,8 @@
</el-form> </el-form>
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">
<el-button @click="cancelAddAccount()">{{ $t('common.cancel') }}</el-button> <el-button @click="onCancelAddAccount()">{{ $t('common.cancel') }}</el-button>
<el-button @click="relateAccount(1, addAccountDialog.accountId)" type="primary">{{ $t('common.confirm') }}</el-button> <el-button @click="onRelateAccount(1, addAccountDialog.accountId)" type="primary">{{ $t('common.confirm') }}</el-button>
</div> </div>
</template> </template>
</el-dialog> </el-dialog>
@@ -107,7 +107,7 @@ const searchRoleAccount = () => {
pageTableRef.value.search(); pageTableRef.value.search();
}; };
const relateAccount = async (relateType: number, accountId: number) => { const onRelateAccount = async (relateType: number, accountId: number) => {
await accountApi.saveRole.request({ await accountApi.saveRole.request({
id: accountId, id: accountId,
roleId: props.role?.id, roleId: props.role?.id,
@@ -116,16 +116,16 @@ const relateAccount = async (relateType: number, accountId: number) => {
useI18nOperateSuccessMsg(); useI18nOperateSuccessMsg();
// 如果是新增账号,则关闭新增账号弹窗 // 如果是新增账号,则关闭新增账号弹窗
if (relateType == 1) { if (relateType == 1) {
cancelAddAccount(); onCancelAddAccount();
} }
searchRoleAccount(); searchRoleAccount();
}; };
const showAddAccount = () => { const onShowAddAccount = () => {
state.addAccountDialog.visible = true; state.addAccountDialog.visible = true;
}; };
const cancelAddAccount = () => { const onCancelAddAccount = () => {
state.addAccountDialog.accountId = null; state.addAccountDialog.accountId = null;
state.addAccountDialog.accounts = []; state.addAccountDialog.accounts = [];
state.addAccountDialog.visible = false; state.addAccountDialog.visible = false;

View File

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

View File

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

View File

@@ -47,7 +47,7 @@ func (a *AccountLogin) ReqConfs() *req.Confs {
// @router /auth/accounts/login [post] // @router /auth/accounts/login [post]
func (a *AccountLogin) Login(rc *req.Ctx) { func (a *AccountLogin) Login(rc *req.Ctx) {
loginForm := req.BindJsonAndValid(rc, new(form.LoginForm)) loginForm := req.BindJsonAndValid[*form.LoginForm](rc)
ctx := rc.MetaCtx ctx := rc.MetaCtx
accountLoginSecurity := config.GetAccountLoginSecurity() accountLoginSecurity := config.GetAccountLoginSecurity()
@@ -96,8 +96,7 @@ type OtpVerifyInfo struct {
// OTP双因素校验 // OTP双因素校验
func (a *AccountLogin) OtpVerify(rc *req.Ctx) { func (a *AccountLogin) OtpVerify(rc *req.Ctx) {
otpVerify := new(form.OtpVerfiy) otpVerify := req.BindJsonAndValid[*form.OtpVerfiy](rc)
req.BindJsonAndValid(rc, otpVerify)
ctx := rc.MetaCtx ctx := rc.MetaCtx
tokenKey := fmt.Sprintf("otp:token:%s", otpVerify.OtpToken) 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] // @router /auth/ldap/login [post]
func (a *LdapLogin) Login(rc *req.Ctx) { func (a *LdapLogin) Login(rc *req.Ctx) {
loginForm := req.BindJsonAndValid(rc, new(form.LoginForm)) loginForm := req.BindJsonAndValid[*form.LoginForm](rc)
ctx := rc.MetaCtx ctx := rc.MetaCtx
accountLoginSecurity := config.GetAccountLoginSecurity() accountLoginSecurity := config.GetAccountLoginSecurity()
// 判断是否有开启登录验证码校验 // 判断是否有开启登录验证码校验

View File

@@ -77,7 +77,7 @@ func (d *Db) ReqConfs() *req.Confs {
// @router /api/dbs [get] // @router /api/dbs [get]
func (d *Db) Dbs(rc *req.Ctx) { func (d *Db) Dbs(rc *req.Ctx) {
queryCond := req.BindQuery[*entity.DbQuery](rc, new(entity.DbQuery)) queryCond := req.BindQuery[*entity.DbQuery](rc)
// 不存在可访问标签id即没有可操作数据 // 不存在可访问标签id即没有可操作数据
tags := d.tagApp.GetAccountTags(rc.GetLoginAccount().Id, &tagentity.TagTreeQuery{ 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) { func (d *Db) Save(rc *req.Ctx) {
form := &form.DbForm{} form, db := req.BindJsonAndCopyTo[*form.DbForm, *entity.Db](rc)
db := req.BindJsonAndCopyTo[*entity.Db](rc, form, new(entity.Db))
rc.ReqParam = form rc.ReqParam = form
biz.ErrIsNil(d.dbApp.SaveDb(rc.MetaCtx, db)) biz.ErrIsNil(d.dbApp.SaveDb(rc.MetaCtx, db))
@@ -137,7 +135,7 @@ func (d *Db) DeleteDb(rc *req.Ctx) {
/** 数据库操作相关、执行sql等 ***/ /** 数据库操作相关、执行sql等 ***/
func (d *Db) ExecSql(rc *req.Ctx) { 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) ctx, cancel := context.WithTimeout(rc.MetaCtx, time.Duration(config.GetDbms().SqlExecTl)*time.Second)
defer cancel() defer cancel()
@@ -351,8 +349,7 @@ func (d *Db) GetSchemas(rc *req.Ctx) {
} }
func (d *Db) CopyTable(rc *req.Ctx) { func (d *Db) CopyTable(rc *req.Ctx) {
form := &form.DbCopyTableForm{} form, copy := req.BindJsonAndCopyTo[*form.DbCopyTableForm, *dbi.DbCopyTable](rc)
copy := req.BindJsonAndCopyTo[*dbi.DbCopyTable](rc, form, new(dbi.DbCopyTable))
conn, err := d.dbApp.GetDbConn(rc.MetaCtx, form.Id, form.Db) conn, err := d.dbApp.GetDbConn(rc.MetaCtx, form.Id, form.Db)
biz.ErrIsNilAppendErr(err, "copy table error: %s") 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) { 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) res, err := d.dataSyncTaskApp.GetPageList(queryCond)
biz.ErrIsNil(err) biz.ErrIsNil(err)
rc.ResData = model.PageResultConv[*entity.DataSyncTask, *vo.DataSyncTaskListVO](res) rc.ResData = model.PageResultConv[*entity.DataSyncTask, *vo.DataSyncTaskListVO](res)
} }
func (d *DataSyncTask) Logs(rc *req.Ctx) { 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) res, err := d.dataSyncTaskApp.GetTaskLogList(queryCond)
biz.ErrIsNil(err) biz.ErrIsNil(err)
rc.ResData = model.PageResultConv[*entity.DataSyncLog, *vo.DataSyncLogListVO](res) rc.ResData = model.PageResultConv[*entity.DataSyncLog, *vo.DataSyncLogListVO](res)
} }
func (d *DataSyncTask) SaveTask(rc *req.Ctx) { func (d *DataSyncTask) SaveTask(rc *req.Ctx) {
form := &form.DataSyncTaskForm{} form, task := req.BindJsonAndCopyTo[*form.DataSyncTaskForm, *entity.DataSyncTask](rc)
task := req.BindJsonAndCopyTo[*entity.DataSyncTask](rc, form, new(entity.DataSyncTask))
// 解码base64 sql // 解码base64 sql
sqlStr, err := utils.AesDecryptByLa(task.DataSql, rc.GetLoginAccount()) 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) { func (d *DataSyncTask) ChangeStatus(rc *req.Ctx) {
form := &form.DataSyncTaskStatusForm{} form, task := req.BindJsonAndCopyTo[*form.DataSyncTaskStatusForm, *entity.DataSyncTask](rc)
task := req.BindJsonAndCopyTo[*entity.DataSyncTask](rc, form, new(entity.DataSyncTask))
_ = d.dataSyncTaskApp.UpdateById(rc.MetaCtx, task) _ = d.dataSyncTaskApp.UpdateById(rc.MetaCtx, task)
if task.Status == entity.DataSyncTaskStatusEnable { if task.Status == entity.DataSyncTaskStatusEnable {

View File

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

View File

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

View File

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

View File

@@ -61,7 +61,7 @@ func (d *DbTransferTask) ReqConfs() *req.Confs {
} }
func (d *DbTransferTask) Tasks(rc *req.Ctx) { 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) res, err := d.dbTransferTask.GetPageList(queryCond)
biz.ErrIsNil(err) biz.ErrIsNil(err)
@@ -78,8 +78,7 @@ func (d *DbTransferTask) Tasks(rc *req.Ctx) {
} }
func (d *DbTransferTask) SaveTask(rc *req.Ctx) { func (d *DbTransferTask) SaveTask(rc *req.Ctx) {
reqForm := &form.DbTransferTaskForm{} reqForm, task := req.BindJsonAndCopyTo[*form.DbTransferTaskForm, *entity.DbTransferTask](rc)
task := req.BindJsonAndCopyTo[*entity.DbTransferTask](rc, reqForm, new(entity.DbTransferTask))
rc.ReqParam = reqForm rc.ReqParam = reqForm
biz.ErrIsNil(d.dbTransferTask.Save(rc.MetaCtx, task)) 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) { func (d *DbTransferTask) ChangeStatus(rc *req.Ctx) {
form := &form.DbTransferTaskStatusForm{} form, task := req.BindJsonAndCopyTo[*form.DbTransferTaskStatusForm, *entity.DbTransferTask](rc)
task := req.BindJsonAndCopyTo[*entity.DbTransferTask](rc, form, new(entity.DbTransferTask))
_ = d.dbTransferTask.UpdateById(rc.MetaCtx, task) _ = d.dbTransferTask.UpdateById(rc.MetaCtx, task)
task, err := d.dbTransferTask.GetById(task.Id) 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) { 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) res, err := d.dbTransferFile.GetPageList(queryCond)
biz.ErrIsNil(err) biz.ErrIsNil(err)
@@ -142,7 +140,7 @@ func (d *DbTransferTask) FileDel(rc *req.Ctx) {
} }
func (d *DbTransferTask) FileRun(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 rc.ReqParam = fm

View File

@@ -277,7 +277,7 @@ func (d *dbSqlExecAppImpl) FlowBizHandle(ctx context.Context, bizHandleParam *fl
return nil, nil return nil, nil
} }
execSqlBizForm, err := jsonx.To(procinst.BizForm, new(FlowDbExecSqlBizForm)) execSqlBizForm, err := jsonx.To[*FlowDbExecSqlBizForm](procinst.BizForm)
if err != nil { if err != nil {
return nil, errorx.NewBiz("failed to parse the business form information: %s", err.Error()) 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) { 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{ 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) { func (d *Instance) TestConn(rc *req.Ctx) {
fm := &form.InstanceForm{} fm, instance := req.BindJsonAndCopyTo[*form.InstanceForm, *entity.EsInstance](rc)
instance := req.BindJsonAndCopyTo[*entity.EsInstance](rc, fm, new(entity.EsInstance))
var ac *tagentity.ResourceAuthCert var ac *tagentity.ResourceAuthCert
if len(fm.AuthCerts) > 0 { if len(fm.AuthCerts) > 0 {
@@ -105,8 +104,7 @@ func (d *Instance) TestConn(rc *req.Ctx) {
rc.ResData = res rc.ResData = res
} }
func (d *Instance) SaveInstance(rc *req.Ctx) { func (d *Instance) SaveInstance(rc *req.Ctx) {
fm := &form.InstanceForm{} fm, instance := req.BindJsonAndCopyTo[*form.InstanceForm, *entity.EsInstance](rc)
instance := req.BindJsonAndCopyTo[*entity.EsInstance](rc, fm, new(entity.EsInstance))
rc.ReqParam = fm rc.ReqParam = fm
id, err := d.inst.SaveInst(rc.MetaCtx, &dto.SaveEsInstance{ id, err := d.inst.SaveInst(rc.MetaCtx, &dto.SaveEsInstance{

View File

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

View File

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

View File

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

View File

@@ -48,7 +48,7 @@ func (p *Procdef) ReqConfs() *req.Confs {
} }
func (p *Procdef) GetProcdefPage(rc *req.Ctx) { 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) res, err := p.procdefApp.GetPageList(cond, page)
biz.ErrIsNil(err) biz.ErrIsNil(err)
@@ -87,8 +87,7 @@ func (p *Procdef) GetProcdef(rc *req.Ctx) {
} }
func (a *Procdef) Save(rc *req.Ctx) { func (a *Procdef) Save(rc *req.Ctx) {
form := &form.Procdef{} form, procdef := req.BindJsonAndCopyTo[*form.Procdef, *entity.Procdef](rc)
procdef := req.BindJsonAndCopyTo(rc, form, new(entity.Procdef))
rc.ReqParam = form rc.ReqParam = form
biz.ErrIsNil(a.procdefApp.SaveProcdef(rc.MetaCtx, &dto.SaveProcdef{ biz.ErrIsNil(a.procdefApp.SaveProcdef(rc.MetaCtx, &dto.SaveProcdef{
Procdef: procdef, Procdef: procdef,
@@ -98,7 +97,7 @@ func (a *Procdef) Save(rc *req.Ctx) {
} }
func (a *Procdef) SaveFlowDef(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 rc.ReqParam = form
biz.ErrIsNil(a.procdefApp.SaveFlowDef(rc.MetaCtx, &dto.SaveFlowDef{ 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) { 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 { if laId := rc.GetLoginAccount().Id; laId != consts.AdminId {
cond.CreatorId = laId cond.CreatorId = laId
@@ -47,8 +47,7 @@ func (p *Procinst) GetProcinstPage(rc *req.Ctx) {
} }
func (p *Procinst) ProcinstStart(rc *req.Ctx) { func (p *Procinst) ProcinstStart(rc *req.Ctx) {
startForm := new(form.ProcinstStart) startForm := req.BindJsonAndValid[*form.ProcinstStart](rc)
req.BindJsonAndValid(rc, startForm)
_, err := p.procinstApp.StartProc(rc.MetaCtx, startForm.ProcdefId, &dto.StarProc{ _, err := p.procinstApp.StartProc(rc.MetaCtx, startForm.ProcdefId, &dto.StarProc{
BizType: startForm.BizType, BizType: startForm.BizType,
BizForm: jsonx.ToStr(startForm.BizForm), BizForm: jsonx.ToStr(startForm.BizForm),

View File

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

View File

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

View File

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

View File

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

View File

@@ -76,7 +76,7 @@ func (m *Machine) ReqConfs() *req.Confs {
} }
func (m *Machine) Machines(rc *req.Ctx) { 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{ tags := m.tagTreeApp.GetAccountTags(rc.GetLoginAccount().Id, &tagentity.TagTreeQuery{
TypePaths: collx.AsArray(tagentity.NewTypePaths(tagentity.TagTypeMachine, tagentity.TagTypeAuthCert)), 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) { func (m *Machine) SaveMachine(rc *req.Ctx) {
machineForm := new(form.MachineForm) machineForm, me := req.BindJsonAndCopyTo[*form.MachineForm, *entity.Machine](rc)
me := req.BindJsonAndCopyTo(rc, machineForm, new(entity.Machine))
rc.ReqParam = machineForm rc.ReqParam = machineForm
@@ -156,8 +155,7 @@ func (m *Machine) SaveMachine(rc *req.Ctx) {
} }
func (m *Machine) TestConn(rc *req.Ctx) { func (m *Machine) TestConn(rc *req.Ctx) {
machineForm := new(form.MachineForm) machineForm, me := req.BindJsonAndCopyTo[*form.MachineForm, *entity.Machine](rc)
me := req.BindJsonAndCopyTo(rc, machineForm, new(entity.Machine))
// 测试连接 // 测试连接
biz.ErrIsNilAppendErr(m.machineApp.TestConn(rc.MetaCtx, me, machineForm.AuthCerts[0]), "connection error: %s") 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) { func (m *MachineCmdConf) MachineCmdConfs(rc *req.Ctx) {
cond := req.BindQuery(rc, new(entity.MachineCmdConf)) cond := req.BindQuery[*entity.MachineCmdConf](rc)
var vos []*vo.MachineCmdConfVO var vos []*vo.MachineCmdConfVO
err := m.machineCmdConfApp.ListByCondToAny(cond, &vos) err := m.machineCmdConfApp.ListByCondToAny(cond, &vos)
@@ -47,8 +47,7 @@ func (m *MachineCmdConf) MachineCmdConfs(rc *req.Ctx) {
} }
func (m *MachineCmdConf) Save(rc *req.Ctx) { func (m *MachineCmdConf) Save(rc *req.Ctx) {
cmdForm := new(form.MachineCmdConfForm) cmdForm, mcj := req.BindJsonAndCopyTo[*form.MachineCmdConfForm, *entity.MachineCmdConf](rc)
mcj := req.BindJsonAndCopyTo[*entity.MachineCmdConf](rc, cmdForm, new(entity.MachineCmdConf))
rc.ReqParam = cmdForm rc.ReqParam = cmdForm
err := m.machineCmdConfApp.SaveCmdConf(rc.MetaCtx, &dto.SaveMachineCmdConf{ 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) { 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) pageRes, err := m.machineCronJobApp.GetPageList(cond, pageParam)
biz.ErrIsNil(err) biz.ErrIsNil(err)
@@ -62,8 +62,7 @@ func (m *MachineCronJob) MachineCronJobs(rc *req.Ctx) {
} }
func (m *MachineCronJob) Save(rc *req.Ctx) { func (m *MachineCronJob) Save(rc *req.Ctx) {
jobForm := new(form.MachineCronJobForm) jobForm, mcj := req.BindJsonAndCopyTo[*form.MachineCronJobForm, *entity.MachineCronJob](rc)
mcj := req.BindJsonAndCopyTo[*entity.MachineCronJob](rc, jobForm, new(entity.MachineCronJob))
rc.ReqParam = jobForm rc.ReqParam = jobForm
err := m.machineCronJobApp.SaveMachineCronJob(rc.MetaCtx, &dto.SaveMachineCronJob{ 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) { 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) res, err := m.machineCronJobApp.GetExecPageList(cond, pageParam)
biz.ErrIsNil(err) biz.ErrIsNil(err)
rc.ResData = res rc.ResData = res

View File

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

View File

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

View File

@@ -20,5 +20,5 @@ func GetMachineStats(machineId uint64) (*mcm.Stats, error) {
if cacheStr == "" { if cacheStr == "" {
return nil, errors.New("不存在该值") 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) { func (m *Mongo) Mongos(rc *req.Ctx) {
queryCond := req.BindQuery(rc, new(entity.MongoQuery)) queryCond := req.BindQuery[*entity.MongoQuery](rc)
// 不存在可访问标签id即没有可操作数据 // 不存在可访问标签id即没有可操作数据
tags := m.tagTreeApp.GetAccountTags(rc.GetLoginAccount().Id, &tagentity.TagTreeQuery{ 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) { func (m *Mongo) TestConn(rc *req.Ctx) {
form := &form.Mongo{} _, mongo := req.BindJsonAndCopyTo[*form.Mongo, *entity.Mongo](rc)
mongo := req.BindJsonAndCopyTo[*entity.Mongo](rc, form, new(entity.Mongo))
biz.ErrIsNilAppendErr(m.mongoApp.TestConn(mongo), "connection error: %s") biz.ErrIsNilAppendErr(m.mongoApp.TestConn(mongo), "connection error: %s")
} }
func (m *Mongo) Save(rc *req.Ctx) { func (m *Mongo) Save(rc *req.Ctx) {
form := &form.Mongo{} form, mongo := req.BindJsonAndCopyTo[*form.Mongo, *entity.Mongo](rc)
mongo := req.BindJsonAndCopyTo[*entity.Mongo](rc, form, new(entity.Mongo))
// 密码脱敏记录日志 // 密码脱敏记录日志
form.Uri = func(str string) string { form.Uri = func(str string) string {
@@ -148,8 +146,7 @@ func (m *Mongo) Collections(rc *req.Ctx) {
} }
func (m *Mongo) RunCommand(rc *req.Ctx) { func (m *Mongo) RunCommand(rc *req.Ctx) {
commandForm := new(form.MongoRunCommand) commandForm := req.BindJsonAndValid[*form.MongoRunCommand](rc)
req.BindJsonAndValid(rc, commandForm)
conn, err := m.mongoApp.GetMongoConn(rc.MetaCtx, m.GetMongoId(rc)) conn, err := m.mongoApp.GetMongoConn(rc.MetaCtx, m.GetMongoId(rc))
biz.ErrIsNil(err) biz.ErrIsNil(err)
@@ -179,7 +176,7 @@ func (m *Mongo) RunCommand(rc *req.Ctx) {
} }
func (m *Mongo) FindCommand(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)) conn, err := m.mongoApp.GetMongoConn(rc.MetaCtx, m.GetMongoId(rc))
biz.ErrIsNil(err) biz.ErrIsNil(err)
@@ -214,7 +211,7 @@ func (m *Mongo) FindCommand(rc *req.Ctx) {
} }
func (m *Mongo) UpdateByIdCommand(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)) conn, err := m.mongoApp.GetMongoConn(rc.MetaCtx, m.GetMongoId(rc))
biz.ErrIsNil(err) biz.ErrIsNil(err)
@@ -238,7 +235,7 @@ func (m *Mongo) UpdateByIdCommand(rc *req.Ctx) {
} }
func (m *Mongo) DeleteByIdCommand(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)) conn, err := m.mongoApp.GetMongoConn(rc.MetaCtx, m.GetMongoId(rc))
biz.ErrIsNil(err) biz.ErrIsNil(err)
@@ -261,7 +258,7 @@ func (m *Mongo) DeleteByIdCommand(rc *req.Ctx) {
} }
func (m *Mongo) InsertOneCommand(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)) conn, err := m.mongoApp.GetMongoConn(rc.MetaCtx, m.GetMongoId(rc))
biz.ErrIsNil(err) biz.ErrIsNil(err)

View File

@@ -36,9 +36,8 @@ func (m *MsgChannel) GetMsgChannels(rc *req.Ctx) {
} }
func (m *MsgChannel) SaveMsgChannels(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 rc.ReqParam = form
channel := req.BindJsonAndCopyTo(rc, form, new(entity.MsgChannel))
err := m.msgChannelApp.SaveChannel(rc.MetaCtx, channel) err := m.msgChannelApp.SaveChannel(rc.MetaCtx, channel)
biz.ErrIsNil(err) biz.ErrIsNil(err)
} }

View File

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

View File

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

View File

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

View File

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

View File

@@ -253,7 +253,7 @@ func (r *redisAppImpl) FlowBizHandle(ctx context.Context, bizHandleParam *flowap
return nil, nil return nil, nil
} }
runCmdParam, err := jsonx.To(procinst.BizForm, new(FlowRedisRunCmdBizForm)) runCmdParam, err := jsonx.To[*FlowRedisRunCmdBizForm](procinst.BizForm)
if err != nil { if err != nil {
return nil, errorx.NewBiz("failed to parse the business form information: %s", err.Error()) 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) { func (a *Account) ChangePassword(rc *req.Ctx) {
ctx := rc.MetaCtx ctx := rc.MetaCtx
form := req.BindJsonAndValid(rc, new(form.AccountChangePasswordForm)) form := req.BindJsonAndValid[*form.AccountChangePasswordForm](rc)
originOldPwd, err := utils.DefaultRsaDecrypt(form.OldPassword, true) originOldPwd, err := utils.DefaultRsaDecrypt(form.OldPassword, true)
biz.ErrIsNilAppendErr(err, "Wrong to decrypt old password: %s") 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) { 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为登录者账号 // 账号id为登录者账号
updateAccount.Id = rc.GetLoginAccount().Id updateAccount.Id = rc.GetLoginAccount().Id
rc.ReqParam = form
ctx := rc.MetaCtx ctx := rc.MetaCtx
if updateAccount.Password != "" { if updateAccount.Password != "" {
@@ -210,8 +211,7 @@ func (a *Account) AccountDetail(rc *req.Ctx) {
// @router /accounts // @router /accounts
func (a *Account) SaveAccount(rc *req.Ctx) { func (a *Account) SaveAccount(rc *req.Ctx) {
form := &form.AccountCreateForm{} form, account := req.BindJsonAndCopyTo[*form.AccountCreateForm, *entity.Account](rc)
account := req.BindJsonAndCopyTo(rc, form, new(entity.Account))
form.Password = "*****" form.Password = "*****"
rc.ReqParam = form rc.ReqParam = form
@@ -307,7 +307,7 @@ func (a *Account) AccountResources(rc *req.Ctx) {
// 关联账号角色 // 关联账号角色
func (a *Account) RelateRole(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 rc.ReqParam = form
biz.ErrIsNil(a.roleApp.RelateAccountRole(rc.MetaCtx, form.Id, form.RoleId, consts.AccountRoleRelateType(form.RelateType))) 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) { func (c *Config) SaveConfig(rc *req.Ctx) {
form := &form.ConfigForm{} form, config := req.BindJsonAndCopyTo[*form.ConfigForm, *entity.Config](rc)
config := req.BindJsonAndCopyTo(rc, form, new(entity.Config))
rc.ReqParam = form rc.ReqParam = form
biz.ErrIsNil(c.configApp.Save(rc.MetaCtx, config)) 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) { func (r *Resource) SaveResource(rc *req.Ctx) {
form := new(form.ResourceForm) form, entity := req.BindJsonAndCopyTo[*form.ResourceForm, *entity.Resource](rc)
entity := req.BindJsonAndCopyTo(rc, form, new(entity.Resource))
rc.ReqParam = form rc.ReqParam = form

View File

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

View File

@@ -21,7 +21,7 @@ func (s *Syslog) ReqConfs() *req.Confs {
} }
func (r *Syslog) Syslogs(rc *req.Ctx) { 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") res, err := r.syslogApp.GetPageList(queryCond, "create_time DESC")
biz.ErrIsNil(err) biz.ErrIsNil(err)
rc.ResData = res rc.ResData = res

View File

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

View File

@@ -119,8 +119,7 @@ func (p *TagTree) ListByQuery(rc *req.Ctx) {
} }
func (p *TagTree) SaveTagTree(rc *req.Ctx) { func (p *TagTree) SaveTagTree(rc *req.Ctx) {
tagForm := &form.TagTree{} tagForm, tagTree := req.BindJsonAndCopyTo[*form.TagTree, *entity.TagTree](rc)
tagTree := req.BindJsonAndCopyTo(rc, tagForm, new(entity.TagTree))
rc.ReqParam = fmt.Sprintf("tagTreeId: %d, tagName: %s, code: %s", tagTree.Id, tagTree.Name, tagTree.Code) 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) { func (p *TagTree) MovingTag(rc *req.Ctx) {
movingForm := &form.MovingTag{} movingForm := req.BindJsonAndValid[*form.MovingTag](rc)
req.BindJsonAndValid(rc, movingForm)
rc.ReqParam = movingForm rc.ReqParam = movingForm
biz.ErrIsNil(p.tagTreeApp.MovingTag(rc.MetaCtx, movingForm.FromPath, movingForm.ToPath)) 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) { 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) res, err := p.teamApp.GetPageList(queryCond)
biz.ErrIsNil(err) biz.ErrIsNil(err)
@@ -60,7 +60,7 @@ func (p *Team) GetTeams(rc *req.Ctx) {
} }
func (p *Team) SaveTeam(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 rc.ReqParam = team
biz.ErrIsNil(p.teamApp.SaveTeam(rc.MetaCtx, 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) { func (p *Team) SaveTeamMember(rc *req.Ctx) {
teamMems := req.BindJsonAndValid(rc, new(form.TeamMember)) teamMems := req.BindJsonAndValid[*form.TeamMember](rc)
teamId := teamMems.TeamId teamId := teamMems.TeamId

View File

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

View File

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

View File

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

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 { if err := rc.BindJSON(data); err != nil {
panic(ConvBindValidationError(data, err)) panic(ConvBindValidationError(data, err))
} else { } else {
@@ -18,15 +19,15 @@ func BindJsonAndValid[T any](rc *Ctx, data T) T {
} }
} }
// 绑定请求体中的json至form结构体并拷贝至另一结构体 // 绑定请求体中的json至form结构体并拷贝至指定结构体
func BindJsonAndCopyTo[T any](rc *Ctx, form any, toStruct T) T { func BindJsonAndCopyTo[F, T any](rc *Ctx) (F, T) {
BindJsonAndValid(rc, form) f := BindJsonAndValid[F](rc)
structx.Copy(toStruct, form) return f, structx.CopyTo[T](f)
return toStruct
} }
// 绑定查询字符串到指定结构体 // 绑定查询字符串到指定结构体
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 { if err := rc.BindQuery(data); err != nil {
panic(ConvBindValidationError(data, err)) panic(ConvBindValidationError(data, err))
} else { } 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 { if err := rc.BindQuery(data); err != nil {
panic(ConvBindValidationError(data, err)) panic(ConvBindValidationError(data, err))
} else { } else {

View File

@@ -4,6 +4,7 @@ import (
"encoding/json" "encoding/json"
"mayfly-go/pkg/logx" "mayfly-go/pkg/logx"
"mayfly-go/pkg/utils/collx" "mayfly-go/pkg/utils/collx"
"mayfly-go/pkg/utils/structx"
"github.com/tidwall/gjson" "github.com/tidwall/gjson"
) )
@@ -16,9 +17,10 @@ func ToMap(jsonStr string) (collx.M, error) {
return ToMapByBytes([]byte(jsonStr)) return ToMapByBytes([]byte(jsonStr))
} }
// json字符串转结构体 // json字符串转结构体, T需为指针类型
func To[T any](jsonStr string, res T) (T, error) { func To[T any](jsonStr string) (T, error) {
return res, json.Unmarshal([]byte(jsonStr), &res) res := structx.NewInstance[T]()
return res, json.Unmarshal([]byte(jsonStr), res)
} }
// json字节数组转map // 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 package structx
import ( import (
"database/sql"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
@@ -11,126 +10,18 @@ import (
"strings" "strings"
) )
// Copy copy things引用至copier // CopyTo 将fromValue转为T类型并返回
func Copy(toValue any, fromValue any) (err error) { func CopyTo[T any](fromValue any) T {
var ( t := NewInstance[T]()
isSlice bool Copy(t, fromValue)
amount = 1 return t
from = Indirect(reflect.ValueOf(fromValue)) }
to = Indirect(reflect.ValueOf(toValue))
)
if !to.CanAddr() { // CopySliceTo 将fromValue转为[]T类型并返回
return errors.New("copy to value is unaddressable") func CopySliceTo[F, T any](fromValue []F) []T {
} var to []T
Copy(&to, fromValue)
// Return is from value is invalid return to
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
} }
// 对结构体的每个字段以及字段值执行doWith回调函数, 包括匿名属性的字段 // 对结构体的每个字段以及字段值执行doWith回调函数, 包括匿名属性的字段
@@ -158,23 +49,6 @@ func DoWithFields(str any, doWith func(fType reflect.StructField, fValue reflect
return nil 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 { func Indirect(reflectValue reflect.Value) reflect.Value {
for reflectValue.Kind() == reflect.Ptr { for reflectValue.Kind() == reflect.Ptr {
reflectValue = reflectValue.Elem() reflectValue = reflectValue.Elem()
@@ -189,35 +63,6 @@ func IndirectType(reflectType reflect.Type) reflect.Type {
return reflectType 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 { func Map2Struct(m map[string]any, s any) error {
toValue := Indirect(reflect.ValueOf(s)) toValue := Indirect(reflect.ValueOf(s))
if !toValue.CanAddr() { if !toValue.CanAddr() {