mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-03 16:00:25 +08:00
refactor: code review
This commit is contained in:
@@ -33,7 +33,7 @@
|
||||
"splitpanes": "^3.1.5",
|
||||
"sql-formatter": "^14.0.0",
|
||||
"uuid": "^9.0.1",
|
||||
"vue": "^3.3.12",
|
||||
"vue": "^3.4.0",
|
||||
"vue-router": "^4.2.5",
|
||||
"xterm": "^5.3.0",
|
||||
"xterm-addon-fit": "^0.8.0",
|
||||
|
||||
@@ -24,7 +24,7 @@ export function dateStrFormat(fmt: string, dateStr: string) {
|
||||
}
|
||||
|
||||
export function dateFormat(dateStr: string) {
|
||||
if (dateStr.startsWith('0001-01-01', 0)) {
|
||||
if (dateStr?.startsWith('0001-01-01', 0)) {
|
||||
return '';
|
||||
}
|
||||
return dateFormat2('yyyy-MM-dd HH:mm:ss', new Date(dateStr));
|
||||
|
||||
@@ -28,17 +28,13 @@
|
||||
<script setup lang="ts" name="SearchFormItem">
|
||||
import { computed } from 'vue';
|
||||
import { SearchItem } from '../index';
|
||||
import { useVModel } from '@vueuse/core';
|
||||
|
||||
interface SearchFormItemProps {
|
||||
modelValue: any;
|
||||
item: SearchItem;
|
||||
}
|
||||
const props = defineProps<SearchFormItemProps>();
|
||||
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
|
||||
const itemValue = useVModel(props, 'modelValue', emit);
|
||||
const itemValue = defineModel('modelValue');
|
||||
|
||||
// 判断 fieldNames 设置 label && value && children 的 key 值
|
||||
const fieldNames = computed(() => {
|
||||
|
||||
@@ -44,11 +44,9 @@ import Grid from '@/components/Grid/index.vue';
|
||||
import GridItem from '@/components/Grid/components/GridItem.vue';
|
||||
import SvgIcon from '@/components/svgIcon/index.vue';
|
||||
import { SearchItem } from './index';
|
||||
import { useVModel } from '@vueuse/core';
|
||||
|
||||
interface ProTableProps {
|
||||
items: SearchItem[]; // 搜索配置项
|
||||
modelValue?: { [key: string]: any }; // 搜索参数
|
||||
searchCol: number | Record<BreakPoint, number>;
|
||||
search: (params: any) => void; // 搜索方法
|
||||
reset: (params: any) => void; // 重置方法
|
||||
@@ -60,9 +58,7 @@ const props = withDefaults(defineProps<ProTableProps>(), {
|
||||
modelValue: () => ({}),
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
|
||||
const searchParam = useVModel(props, 'modelValue', emit);
|
||||
const searchParam: any = defineModel('modelValue');
|
||||
|
||||
// 获取响应式设置
|
||||
const getResponsive = (item: SearchItem) => {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<div class="dynamic-form">
|
||||
<el-form v-bind="$attrs" ref="formRef" :model="formData" label-width="auto">
|
||||
<el-form-item v-for="item in formItems as any" :key="item.name" :prop="item.model" :label="item.name" required>
|
||||
<el-input v-if="!item.options" v-model="formData[item.model]" :placeholder="item.placeholder" autocomplete="off" clearable></el-input>
|
||||
<el-form v-bind="$attrs" ref="formRef" :model="modelValue" label-width="auto">
|
||||
<el-form-item v-for="item in props.formItems as any" :key="item.name" :prop="item.model" :label="item.name" :required="item.required ?? true">
|
||||
<el-input v-if="!item.options" v-model="modelValue[item.model]" :placeholder="item.placeholder" autocomplete="off" clearable></el-input>
|
||||
|
||||
<el-select v-else v-model="formData[item.model]" :placeholder="item.placeholder" filterable autocomplete="off" clearable style="width: 100%">
|
||||
<el-select v-else v-model="modelValue[item.model]" :placeholder="item.placeholder" filterable autocomplete="off" clearable style="width: 100%">
|
||||
<el-option v-for="option in item.options.split(',')" :key="option" :label="option" :value="option" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
@@ -13,19 +13,15 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useVModel } from '@vueuse/core';
|
||||
import { ref } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
formItems: { type: Array },
|
||||
modelValue: { type: Object },
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
|
||||
const formRef: any = ref();
|
||||
|
||||
const formData: any = useVModel(props, 'modelValue', emit);
|
||||
const modelValue: any = defineModel();
|
||||
|
||||
const validate = async (func: any) => {
|
||||
await formRef.value.validate(func);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="form-dialog">
|
||||
<el-dialog @close="close" v-bind="$attrs" :title="title" v-model="dialogVisible" :width="width">
|
||||
<dynamic-form ref="df" :form-items="formItems" v-model="formData" />
|
||||
<dynamic-form ref="df" :form-items="props.formItems" v-model="formData" />
|
||||
|
||||
<template #footer>
|
||||
<span>
|
||||
@@ -18,22 +18,19 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import DynamicForm from './DynamicForm.vue';
|
||||
import { useVModel } from '@vueuse/core';
|
||||
|
||||
const emit = defineEmits(['update:visible', 'update:modelValue', 'close', 'confirm']);
|
||||
const emit = defineEmits(['close', 'confirm']);
|
||||
|
||||
const props = defineProps({
|
||||
title: { type: String },
|
||||
visible: { type: Boolean },
|
||||
width: { type: [String, Number], default: '500px' },
|
||||
formItems: { type: Array },
|
||||
modelValue: { type: Object },
|
||||
});
|
||||
|
||||
const df: any = ref();
|
||||
|
||||
const formData: any = useVModel(props, 'modelValue', emit);
|
||||
const dialogVisible: any = useVModel(props, 'visible', emit);
|
||||
const formData: any = defineModel('modelValue');
|
||||
const dialogVisible = defineModel<boolean>('visible', { default: false });
|
||||
|
||||
const close = () => {
|
||||
emit('close');
|
||||
|
||||
@@ -29,6 +29,12 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="required" label="必填" min-width="40px">
|
||||
<template #default="scope">
|
||||
<el-checkbox v-model="scope.row['required']" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="操作" wdith="20px">
|
||||
<template #default="scope">
|
||||
<el-button type="danger" @click="deleteItem(scope.$index)" icon="delete" plain></el-button>
|
||||
@@ -39,15 +45,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useVModel } from '@vueuse/core';
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: { type: Array },
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
|
||||
const formItems: any = useVModel(props, 'modelValue', emit);
|
||||
const formItems: any = defineModel('modelValue');
|
||||
|
||||
const addItem = () => {
|
||||
formItems.value.push({});
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div>
|
||||
<transition name="el-zoom-in-top">
|
||||
<!-- 查询表单 -->
|
||||
<SearchForm v-if="isShowSearch" :items="tableSearchItems" v-model="queryForm_" :search="search" :reset="reset" :search-col="searchCol">
|
||||
<SearchForm v-if="isShowSearch" :items="tableSearchItems" v-model="queryForm" :search="search" :reset="reset" :search-col="searchCol">
|
||||
<!-- 遍历父组件传入的 solts 透传给子组件 -->
|
||||
<template v-for="(_, key) in useSlots()" v-slot:[key]>
|
||||
<slot :name="key"></slot>
|
||||
@@ -47,7 +47,7 @@
|
||||
@keyup.enter.native="searchFormItemKeyUpEnter"
|
||||
v-if="!nowSearchItem.slot"
|
||||
:item="nowSearchItem"
|
||||
v-model="queryForm_[nowSearchItem.prop]"
|
||||
v-model="queryForm[nowSearchItem.prop]"
|
||||
/>
|
||||
|
||||
<slot @keyup.enter.native="searchFormItemKeyUpEnter" v-else :name="nowSearchItem.slot"></slot>
|
||||
@@ -161,8 +161,8 @@
|
||||
style="text-align: right"
|
||||
layout="prev, pager, next, total, sizes, jumper"
|
||||
:total="total"
|
||||
v-model:current-page="queryForm_.pageNum"
|
||||
v-model:page-size="queryForm_.pageSize"
|
||||
v-model:current-page="queryForm.pageNum"
|
||||
v-model:page-size="queryForm.pageSize"
|
||||
:page-sizes="pageSizes"
|
||||
/>
|
||||
</el-row>
|
||||
@@ -176,7 +176,7 @@ import { TableColumn } from './index';
|
||||
import EnumTag from '@/components/enumtag/EnumTag.vue';
|
||||
import { useThemeConfig } from '@/store/themeConfig';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useVModel, useEventListener } from '@vueuse/core';
|
||||
import { useEventListener } from '@vueuse/core';
|
||||
import Api from '@/common/Api';
|
||||
import SearchForm from '@/components/SearchForm/index.vue';
|
||||
import { SearchItem } from '../SearchForm/index';
|
||||
@@ -200,7 +200,6 @@ export interface PageTableProps {
|
||||
beforeQueryFn?: (params: any) => any; // 执行查询时对查询参数进行处理,调整等
|
||||
dataHandlerFn?: (data: any) => any; // 数据处理回调函数,用于将请求回来的数据二次加工处理等
|
||||
searchItems?: SearchItem[];
|
||||
queryForm?: any; // 查询表单参数 ==> 非必传(默认为{pageNum:1, pageSize: 10})
|
||||
border?: boolean; // 是否带有纵向边框 ==> 非必传(默认为false)
|
||||
toolButton?: ('setting' | 'search')[] | boolean; // 是否显示表格功能按钮 ==> 非必传(默认为true)
|
||||
searchCol?: any; // 表格搜索项 每列占比配置 ==> 非必传 { xs: 1, sm: 2, md: 2, lg: 3, xl: 4 } | number 如 3
|
||||
@@ -212,10 +211,6 @@ const props = withDefaults(defineProps<PageTableProps>(), {
|
||||
pageable: true,
|
||||
showSelection: false,
|
||||
lazy: false,
|
||||
queryForm: {
|
||||
pageNum: 1,
|
||||
pageSize: 0,
|
||||
},
|
||||
border: false,
|
||||
toolButton: true,
|
||||
showSearch: false,
|
||||
@@ -223,6 +218,14 @@ const props = withDefaults(defineProps<PageTableProps>(), {
|
||||
searchCol: () => ({ xs: 1, sm: 3, md: 3, lg: 4, xl: 5 }),
|
||||
});
|
||||
|
||||
// 查询表单参数 ==> 非必传(默认为{pageNum:1, pageSize: 10})
|
||||
const queryForm: Ref<any> = defineModel('queryForm', {
|
||||
default: {
|
||||
pageNum: 1,
|
||||
pageSize: 0,
|
||||
},
|
||||
});
|
||||
|
||||
// table 实例
|
||||
const tableRef = ref<InstanceType<typeof ElTable>>();
|
||||
|
||||
@@ -250,16 +253,14 @@ const nowSearchItem: Ref<SearchItem> = ref(null) as any;
|
||||
*/
|
||||
const changeSimpleFormItem = (searchItem: SearchItem) => {
|
||||
// 将之前的值置为空,避免因为只显示一个搜索项却搜索多个条件
|
||||
queryForm_.value[nowSearchItem.value.prop] = null;
|
||||
queryForm.value[nowSearchItem.value.prop] = null;
|
||||
nowSearchItem.value = searchItem;
|
||||
};
|
||||
|
||||
const queryForm_: Ref<any> = useVModel(props, 'queryForm', emit);
|
||||
|
||||
const { tableData, total, loading, search, reset, getTableData, handlePageNumChange, handlePageSizeChange } = usePageTable(
|
||||
props.pageable,
|
||||
props.pageApi,
|
||||
queryForm_,
|
||||
queryForm,
|
||||
props.beforeQueryFn,
|
||||
props.dataHandlerFn
|
||||
);
|
||||
@@ -295,7 +296,7 @@ onMounted(async () => {
|
||||
nowSearchItem.value = props.searchItems[0];
|
||||
}
|
||||
|
||||
let pageSize = queryForm_.value.pageSize;
|
||||
let pageSize = queryForm.value.pageSize;
|
||||
// 如果pageSize设为0,则使用系统全局配置的pageSize
|
||||
if (!pageSize) {
|
||||
pageSize = themeConfig.value.defaultListPageSize;
|
||||
@@ -305,8 +306,8 @@ onMounted(async () => {
|
||||
}
|
||||
}
|
||||
|
||||
queryForm_.value.pageNum = 1;
|
||||
queryForm_.value.pageSize = pageSize;
|
||||
queryForm.value.pageNum = 1;
|
||||
queryForm.value.pageSize = pageSize;
|
||||
state.pageSizes = [pageSize, pageSize * 2, pageSize * 3, pageSize * 4, pageSize * 5];
|
||||
|
||||
if (!props.lazy) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Api from '@/common/Api';
|
||||
import { reactive, toRefs, toValue } from 'vue';
|
||||
import { isReactive, reactive, toRefs, toValue } from 'vue';
|
||||
|
||||
/**
|
||||
* @description table 页面操作方法封装
|
||||
@@ -41,7 +41,12 @@ export const usePageTable = (
|
||||
let sp = toValue(state.searchParams);
|
||||
if (beforeQueryFn) {
|
||||
sp = beforeQueryFn(sp);
|
||||
state.searchParams = sp;
|
||||
|
||||
if (isReactive(state.searchParams)) {
|
||||
state.searchParams.value = sp;
|
||||
} else {
|
||||
state.searchParams = sp;
|
||||
}
|
||||
}
|
||||
|
||||
let res = await api.request(sp);
|
||||
|
||||
@@ -46,9 +46,6 @@ import { dbApi } from './api';
|
||||
import { ElMessage } from 'element-plus';
|
||||
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
type: Boolean,
|
||||
},
|
||||
data: {
|
||||
type: [Boolean, Object],
|
||||
},
|
||||
@@ -61,8 +58,12 @@ const props = defineProps({
|
||||
},
|
||||
});
|
||||
|
||||
const visible = defineModel<boolean>('visible', {
|
||||
default: false,
|
||||
});
|
||||
|
||||
//定义事件
|
||||
const emit = defineEmits(['update:visible', 'cancel', 'val-change']);
|
||||
const emit = defineEmits(['cancel', 'val-change']);
|
||||
|
||||
const rules = {
|
||||
dbNames: [
|
||||
@@ -97,7 +98,7 @@ const state = reactive({
|
||||
dbId: 0,
|
||||
dbNames: String,
|
||||
name: null as any,
|
||||
intervalDay: 1,
|
||||
intervalDay: null,
|
||||
startTime: null as any,
|
||||
repeated: null as any,
|
||||
},
|
||||
@@ -107,9 +108,9 @@ const state = reactive({
|
||||
editOrCreate: false,
|
||||
});
|
||||
|
||||
watch(props, (newValue: any) => {
|
||||
if (newValue.visible) {
|
||||
init(newValue.data);
|
||||
watch(visible, (newValue: any) => {
|
||||
if (newValue) {
|
||||
init(props.data);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -135,7 +136,7 @@ const init = (data: any) => {
|
||||
} else {
|
||||
state.editOrCreate = false;
|
||||
state.form.name = '';
|
||||
state.form.intervalDay = 1;
|
||||
state.form.intervalDay = null;
|
||||
const now = new Date();
|
||||
state.form.startTime = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1);
|
||||
getDbNamesWithoutBackup();
|
||||
@@ -150,32 +151,32 @@ const getDbNamesWithoutBackup = async () => {
|
||||
|
||||
const btnOk = async () => {
|
||||
backupForm.value.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
state.form.repeated = true;
|
||||
const reqForm = { ...state.form };
|
||||
let api = dbApi.createDbBackup;
|
||||
if (props.data) {
|
||||
api = dbApi.saveDbBackup;
|
||||
}
|
||||
api.request(reqForm).then(() => {
|
||||
ElMessage.success('保存成功');
|
||||
emit('val-change', state.form);
|
||||
state.btnLoading = true;
|
||||
setTimeout(() => {
|
||||
state.btnLoading = false;
|
||||
}, 1000);
|
||||
|
||||
cancel();
|
||||
});
|
||||
} else {
|
||||
if (!valid) {
|
||||
ElMessage.error('请正确填写信息');
|
||||
return false;
|
||||
}
|
||||
|
||||
state.form.repeated = true;
|
||||
const reqForm = { ...state.form };
|
||||
let api = dbApi.createDbBackup;
|
||||
if (props.data) {
|
||||
api = dbApi.saveDbBackup;
|
||||
}
|
||||
|
||||
try {
|
||||
state.btnLoading = true;
|
||||
await api.request(reqForm);
|
||||
ElMessage.success('保存成功');
|
||||
emit('val-change', state.form);
|
||||
cancel();
|
||||
} finally {
|
||||
state.btnLoading = false;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const cancel = () => {
|
||||
emit('update:visible', false);
|
||||
visible.value = false;
|
||||
emit('cancel');
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -9,11 +9,7 @@
|
||||
:searchItems="searchItems"
|
||||
:before-query-fn="beforeQueryFn"
|
||||
v-model:query-form="query"
|
||||
:data="state.data"
|
||||
:columns="columns"
|
||||
:total="state.total"
|
||||
v-model:page-size="query.pageSize"
|
||||
v-model:page-num="query.pageNum"
|
||||
>
|
||||
<template #dbSelect>
|
||||
<el-select v-model="query.dbName" placeholder="请选择数据库" style="width: 200px" filterable clearable>
|
||||
@@ -29,8 +25,8 @@
|
||||
|
||||
<template #action="{ data }">
|
||||
<el-button @click="editDbBackup(data)" type="primary" link>编辑</el-button>
|
||||
<el-button @click="enableDbBackup(data)" type="primary" link>启用</el-button>
|
||||
<el-button @click="disableDbBackup(data)" type="primary" link>禁用</el-button>
|
||||
<el-button @click="enableDbBackup(data)" v-if="!data.enabled" type="success" link>启用</el-button>
|
||||
<el-button @click="disableDbBackup(data)" v-if="data.enabled" type="warning" link>禁用</el-button>
|
||||
</template>
|
||||
</page-table>
|
||||
|
||||
@@ -76,7 +72,7 @@ const columns = [
|
||||
TableColumn.new('enabled', '是否启用'),
|
||||
TableColumn.new('lastResult', '执行结果'),
|
||||
TableColumn.new('lastTime', '执行时间').isTime(),
|
||||
TableColumn.new('action', '操作').isSlot().setMinWidth(220).fixedRight().alignCenter(),
|
||||
TableColumn.new('action', '操作').isSlot().setMinWidth(160).fixedRight().alignCenter(),
|
||||
];
|
||||
|
||||
const emptyQuery = {
|
||||
|
||||
@@ -123,7 +123,7 @@
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog
|
||||
width="90%"
|
||||
width="80%"
|
||||
:title="`${dbBackupDialog.title} - 数据库备份`"
|
||||
:close-on-click-modal="false"
|
||||
:destroy-on-close="true"
|
||||
@@ -133,7 +133,7 @@
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog
|
||||
width="90%"
|
||||
width="80%"
|
||||
:title="`${dbRestoreDialog.title} - 数据库恢复`"
|
||||
:close-on-click-modal="false"
|
||||
:destroy-on-close="true"
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
:disabled="state.editOrCreate"
|
||||
@change="changeHistory"
|
||||
v-model="state.history"
|
||||
value-key="id"
|
||||
placeholder="数据库备份"
|
||||
filterable
|
||||
clearable
|
||||
@@ -58,9 +59,6 @@ import { dbApi } from './api';
|
||||
import { ElMessage } from 'element-plus';
|
||||
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
type: Boolean,
|
||||
},
|
||||
data: {
|
||||
type: [Boolean, Object],
|
||||
},
|
||||
@@ -78,7 +76,11 @@ const props = defineProps({
|
||||
});
|
||||
|
||||
//定义事件
|
||||
const emit = defineEmits(['update:visible', 'cancel', 'val-change']);
|
||||
const emit = defineEmits(['cancel', 'val-change']);
|
||||
|
||||
const visible = defineModel<boolean>('visible', {
|
||||
default: false,
|
||||
});
|
||||
|
||||
const validatePointInTime = (rule: any, value: any, callback: any) => {
|
||||
if (!state.histories || state.histories.length == 0) {
|
||||
@@ -161,9 +163,9 @@ onMounted(async () => {
|
||||
await init(props.data);
|
||||
});
|
||||
|
||||
watch(props, (newValue: any) => {
|
||||
if (newValue.visible) {
|
||||
init(newValue.data);
|
||||
watch(visible, (newValue: any) => {
|
||||
if (newValue) {
|
||||
init(props.data);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -230,38 +232,40 @@ const getDbNamesWithoutRestore = async () => {
|
||||
|
||||
const btnOk = async () => {
|
||||
restoreForm.value.validate(async (valid: any) => {
|
||||
if (valid) {
|
||||
if (state.restoreMode == 'point-in-time') {
|
||||
state.form.dbBackupId = 0;
|
||||
state.form.dbBackupHistoryId = 0;
|
||||
state.form.dbBackupHistoryName = '';
|
||||
} else {
|
||||
state.form.pointInTime = '0001-01-01T00:00:00Z';
|
||||
}
|
||||
state.form.repeated = false;
|
||||
const reqForm = { ...state.form };
|
||||
let api = dbApi.createDbRestore;
|
||||
if (props.data) {
|
||||
api = dbApi.saveDbRestore;
|
||||
}
|
||||
api.request(reqForm).then(() => {
|
||||
ElMessage.success('保存成功');
|
||||
emit('val-change', state.form);
|
||||
state.btnLoading = true;
|
||||
setTimeout(() => {
|
||||
state.btnLoading = false;
|
||||
}, 1000);
|
||||
cancel();
|
||||
});
|
||||
} else {
|
||||
if (!valid) {
|
||||
ElMessage.error('请正确填写信息');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (state.restoreMode == 'point-in-time') {
|
||||
state.form.dbBackupId = 0;
|
||||
state.form.dbBackupHistoryId = 0;
|
||||
state.form.dbBackupHistoryName = '';
|
||||
} else {
|
||||
state.form.pointInTime = '0001-01-01T00:00:00Z';
|
||||
}
|
||||
|
||||
state.form.repeated = false;
|
||||
const reqForm = { ...state.form };
|
||||
let api = dbApi.createDbRestore;
|
||||
if (props.data) {
|
||||
api = dbApi.saveDbRestore;
|
||||
}
|
||||
|
||||
try {
|
||||
state.btnLoading = true;
|
||||
await api.request(reqForm);
|
||||
ElMessage.success('保存成功');
|
||||
emit('val-change', state.form);
|
||||
cancel();
|
||||
} finally {
|
||||
state.btnLoading = false;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const cancel = () => {
|
||||
emit('update:visible', false);
|
||||
visible.value = false;
|
||||
emit('cancel');
|
||||
};
|
||||
|
||||
|
||||
@@ -9,11 +9,7 @@
|
||||
:searchItems="searchItems"
|
||||
:before-query-fn="beforeQueryFn"
|
||||
v-model:query-form="query"
|
||||
:data="state.data"
|
||||
:columns="columns"
|
||||
:total="state.total"
|
||||
v-model:page-size="query.pageSize"
|
||||
v-model:page-num="query.pageNum"
|
||||
>
|
||||
<template #dbSelect>
|
||||
<el-select v-model="query.dbName" placeholder="请选择数据库" style="width: 200px" filterable clearable>
|
||||
|
||||
@@ -396,7 +396,7 @@ const removeDeafultExpandId = (id: any) => {
|
||||
<style lang="scss">
|
||||
.tag-tree-list {
|
||||
.tag-tree-data {
|
||||
height: calc(100vh - 200px);
|
||||
height: calc(100vh - 202px);
|
||||
|
||||
.el-tree-node__content {
|
||||
height: 40px;
|
||||
|
||||
@@ -82,18 +82,14 @@ import { ElMessage } from 'element-plus';
|
||||
import PageTable from '@/components/pagetable/PageTable.vue';
|
||||
import { TableColumn } from '@/components/pagetable';
|
||||
import { SearchItem } from '@/components/SearchForm';
|
||||
import { useVModel } from '@vueuse/core';
|
||||
import { ResourceTypeEnum, RoleStatusEnum } from '../enums';
|
||||
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
type: Boolean,
|
||||
},
|
||||
account: Object,
|
||||
});
|
||||
|
||||
//定义事件
|
||||
const emit = defineEmits(['update:visible', 'cancel', 'val-change']);
|
||||
const emit = defineEmits(['cancel', 'val-change']);
|
||||
|
||||
const relatedColumns = [
|
||||
TableColumn.new('roleName', '角色名'),
|
||||
@@ -148,7 +144,7 @@ let relatedRoleIds: Number[] = []; // 用户已关联的角色ids
|
||||
|
||||
const { releateQuery, unRelatedQuery, showResourceDialog } = toRefs(state);
|
||||
|
||||
const dialogVisible = useVModel(props, 'visible', emit);
|
||||
const dialogVisible = defineModel<boolean>('visible', { default: false });
|
||||
|
||||
const searchAccountRoles = async () => {
|
||||
state.releateQuery.id = props.account?.id;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
filterable
|
||||
placeholder="请输入账号模糊搜索并选择"
|
||||
v-bind="$attrs"
|
||||
:ref="(el: any) => focus && el?.focus()"
|
||||
:ref="(el: any) => props.focus && el?.focus()"
|
||||
>
|
||||
<el-option v-for="item in accounts" :key="item.id" :label="`${item.username} [${item.name}]`" :value="item.id"> </el-option>
|
||||
</el-select>
|
||||
@@ -18,12 +18,8 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import { accountApi } from '../../api';
|
||||
import { useVModel } from '@vueuse/core';
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Object,
|
||||
},
|
||||
// 是否获取焦点
|
||||
focus: {
|
||||
type: Boolean,
|
||||
@@ -31,9 +27,7 @@ const props = defineProps({
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
|
||||
const accountId = useVModel(props, 'modelValue', emit);
|
||||
const accountId = defineModel('modelValue');
|
||||
|
||||
const accounts: any = ref([]);
|
||||
|
||||
|
||||
@@ -408,7 +408,7 @@ const info = async (data: any) => {
|
||||
}
|
||||
|
||||
.tree-data {
|
||||
height: calc(100vh - 200px);
|
||||
height: calc(100vh - 202px);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,18 +43,12 @@ import PageTable from '@/components/pagetable/PageTable.vue';
|
||||
import { TableColumn } from '@/components/pagetable';
|
||||
import { hasPerms } from '@/components/auth/auth';
|
||||
import { SearchItem } from '@/components/SearchForm';
|
||||
import { useVModel } from '@vueuse/core';
|
||||
import AccountSelectFormItem from '../account/components/AccountSelectFormItem.vue';
|
||||
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
type: Boolean,
|
||||
},
|
||||
role: Object,
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:visible', 'cancel', 'val-change']);
|
||||
|
||||
const perms = {
|
||||
saveAccountRole: 'account:saveRoles',
|
||||
};
|
||||
@@ -94,7 +88,7 @@ const state = reactive({
|
||||
|
||||
const { query, addAccountDialog } = toRefs(state);
|
||||
|
||||
const dialogVisible = useVModel(props, 'visible', emit);
|
||||
const dialogVisible = defineModel('visible', { default: false });
|
||||
|
||||
onMounted(() => {
|
||||
if (Object.keys(actionBtns).length > 0) {
|
||||
|
||||
@@ -7,6 +7,11 @@
|
||||
resolved "https://registry.npmmirror.com/@babel/parser/-/parser-7.23.5.tgz#37dee97c4752af148e1d38c34b856b2507660563"
|
||||
integrity sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ==
|
||||
|
||||
"@babel/parser@^7.23.6":
|
||||
version "7.23.6"
|
||||
resolved "https://registry.npmmirror.com/@babel/parser/-/parser-7.23.6.tgz#ba1c9e512bda72a47e285ae42aff9d2a635a9e3b"
|
||||
integrity sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==
|
||||
|
||||
"@babel/runtime@^7.21.0":
|
||||
version "7.21.5"
|
||||
resolved "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.21.5.tgz#8492dddda9644ae3bda3b45eabe87382caee7200"
|
||||
@@ -450,13 +455,14 @@
|
||||
estree-walker "^2.0.2"
|
||||
source-map-js "^1.0.2"
|
||||
|
||||
"@vue/compiler-core@3.3.12":
|
||||
version "3.3.12"
|
||||
resolved "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.3.12.tgz#3346c0f55ce0d59e17c21d9eef9154b70c19931b"
|
||||
integrity sha512-qAtjyG3GBLG0chzp5xGCyRLLe6wFCHmjI82aGzwuGKyznNP+GJJMxjc0wOYWDB2YKfho7niJFdoFpo0CZZQg9w==
|
||||
"@vue/compiler-core@3.4.0":
|
||||
version "3.4.0"
|
||||
resolved "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.4.0.tgz#2a35bc4b70afb5504ff62f916e22f9080d8149b6"
|
||||
integrity sha512-cw4S15PkNGTKkP9OFFl4wnQoJJk+HqaYBafgrpDnSukiQGpcYJeRpzmqnCVCIkl6V6Eqsv58E0OAdl6b592vuA==
|
||||
dependencies:
|
||||
"@babel/parser" "^7.23.5"
|
||||
"@vue/shared" "3.3.12"
|
||||
"@babel/parser" "^7.23.6"
|
||||
"@vue/shared" "3.4.0"
|
||||
entities "^4.5.0"
|
||||
estree-walker "^2.0.2"
|
||||
source-map-js "^1.0.2"
|
||||
|
||||
@@ -468,25 +474,24 @@
|
||||
"@vue/compiler-core" "3.3.11"
|
||||
"@vue/shared" "3.3.11"
|
||||
|
||||
"@vue/compiler-dom@3.3.12":
|
||||
version "3.3.12"
|
||||
resolved "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.3.12.tgz#267c54b388d58f30fc120ea496ebf27d4ea8368b"
|
||||
integrity sha512-RdJU9oEYaoPKUdGXCy0l+i4clesdDeLmbvRlszoc9iagsnBnMmQtYfCPVQ5BHB6o7K4SCucDdJM2Dh3oXB0D6g==
|
||||
"@vue/compiler-dom@3.4.0":
|
||||
version "3.4.0"
|
||||
resolved "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.4.0.tgz#25d4c1e24a2e37c4136cdc593cd533d371bc573b"
|
||||
integrity sha512-E957uOhpoE48YjZGWeAoLmNYd3UeU4oIP8kJi8Rcsb9l2tV8Z48Jn07Zgq1aW0v3vuhlmydEKkKKbhLpADHXEA==
|
||||
dependencies:
|
||||
"@vue/compiler-core" "3.3.12"
|
||||
"@vue/shared" "3.3.12"
|
||||
"@vue/compiler-core" "3.4.0"
|
||||
"@vue/shared" "3.4.0"
|
||||
|
||||
"@vue/compiler-sfc@3.3.12":
|
||||
version "3.3.12"
|
||||
resolved "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.3.12.tgz#6ec2c19858f264671457699c1f3a0a6fedf429fe"
|
||||
integrity sha512-yy5b9e7b79dsGbMmglCe/YnhCQgBkHO7Uf6JfjWPSf2/5XH+MKn18LhzhHyxbHdJgnA4lZCqtXzLaJz8Pd8lMw==
|
||||
"@vue/compiler-sfc@3.4.0":
|
||||
version "3.4.0"
|
||||
resolved "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.4.0.tgz#dab7b87a9a09e3860a4b5c9baf6b7d3ab8af0c0f"
|
||||
integrity sha512-PWE0mE2yW7bJS7PmaCrVDEG6KPaDJo0pb4AKnCxJ5lRRDO4IwL/fswBGhCpov+v/c+N/e+hQHpXNwvqU9BtUXg==
|
||||
dependencies:
|
||||
"@babel/parser" "^7.23.5"
|
||||
"@vue/compiler-core" "3.3.12"
|
||||
"@vue/compiler-dom" "3.3.12"
|
||||
"@vue/compiler-ssr" "3.3.12"
|
||||
"@vue/reactivity-transform" "3.3.12"
|
||||
"@vue/shared" "3.3.12"
|
||||
"@babel/parser" "^7.23.6"
|
||||
"@vue/compiler-core" "3.4.0"
|
||||
"@vue/compiler-dom" "3.4.0"
|
||||
"@vue/compiler-ssr" "3.4.0"
|
||||
"@vue/shared" "3.4.0"
|
||||
estree-walker "^2.0.2"
|
||||
magic-string "^0.30.5"
|
||||
postcss "^8.4.32"
|
||||
@@ -516,13 +521,13 @@
|
||||
"@vue/compiler-dom" "3.3.11"
|
||||
"@vue/shared" "3.3.11"
|
||||
|
||||
"@vue/compiler-ssr@3.3.12":
|
||||
version "3.3.12"
|
||||
resolved "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.3.12.tgz#e62499c6003ccd09acb7190167d08845e3a0eaa5"
|
||||
integrity sha512-adCiMJPznfWcQyk/9HSuXGja859IaMV+b8UNSVzDatqv7h0PvT9BEeS22+gjkWofDiSg5d78/ZLls3sLA+cn3A==
|
||||
"@vue/compiler-ssr@3.4.0":
|
||||
version "3.4.0"
|
||||
resolved "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.4.0.tgz#0e437c6e619ce4ec515d2d77a0a9e67e877cc2cd"
|
||||
integrity sha512-+oXKy105g9DIYQKDi3Gwung0xqQX5gJHr0GR+Vf7yK/WkNDM6q61ummcKmKAB85EIst8y3vj2PA9z9YU5Oc4DQ==
|
||||
dependencies:
|
||||
"@vue/compiler-dom" "3.3.12"
|
||||
"@vue/shared" "3.3.12"
|
||||
"@vue/compiler-dom" "3.4.0"
|
||||
"@vue/shared" "3.4.0"
|
||||
|
||||
"@vue/devtools-api@^6.5.0":
|
||||
version "6.5.0"
|
||||
@@ -540,58 +545,47 @@
|
||||
estree-walker "^2.0.2"
|
||||
magic-string "^0.30.5"
|
||||
|
||||
"@vue/reactivity-transform@3.3.12":
|
||||
version "3.3.12"
|
||||
resolved "https://registry.npmmirror.com/@vue/reactivity-transform/-/reactivity-transform-3.3.12.tgz#4cb871b597eb8b321577b4d7f1e93eaebca16128"
|
||||
integrity sha512-g5TijmML7FyKkLt6QnpqNmA4KD7K/T5SbXa88Bhq+hydNQEkzA8veVXWAQuNqg9rjaFYD0rPf0a9NofKA0ENgg==
|
||||
"@vue/reactivity@3.4.0":
|
||||
version "3.4.0"
|
||||
resolved "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.4.0.tgz#84e1d4196bed3fb471e3593d187680a35cfee23a"
|
||||
integrity sha512-X6BvQjNcgKKHWPQzlRJjZvIu72Kkn8xJSv6VNptqWh8dToMknD0Hch1l4N7llKgVt6Diq4lMeUnErbZFvuGlAA==
|
||||
dependencies:
|
||||
"@babel/parser" "^7.23.5"
|
||||
"@vue/compiler-core" "3.3.12"
|
||||
"@vue/shared" "3.3.12"
|
||||
estree-walker "^2.0.2"
|
||||
magic-string "^0.30.5"
|
||||
"@vue/shared" "3.4.0"
|
||||
|
||||
"@vue/reactivity@3.3.12":
|
||||
version "3.3.12"
|
||||
resolved "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.3.12.tgz#b4a62a7678ab20c1ef32f991342ddbb8532417da"
|
||||
integrity sha512-vOJORzO8DlIx88cgTnMLIf2GlLYpoXAKsuoQsK6SGdaqODjxO129pVPTd2s/N/Mb6KKZEFIHIEwWGmtN4YPs+g==
|
||||
"@vue/runtime-core@3.4.0":
|
||||
version "3.4.0"
|
||||
resolved "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.4.0.tgz#6c49cac6142d941954c68c2888160c8fbcdbfc11"
|
||||
integrity sha512-NYrj/JgMMqnSWcIud8lLzDQrBLu+EVEeQ56QE9DYJeKG2eFrnQy8o/h57R9nCprafHs0uImKL3xsdXjHseYVxw==
|
||||
dependencies:
|
||||
"@vue/shared" "3.3.12"
|
||||
"@vue/reactivity" "3.4.0"
|
||||
"@vue/shared" "3.4.0"
|
||||
|
||||
"@vue/runtime-core@3.3.12":
|
||||
version "3.3.12"
|
||||
resolved "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.3.12.tgz#67ee6cfc2e85d656946975239ea635ec42dde5f6"
|
||||
integrity sha512-5iL4w7MZrSGKEZU2wFAYhDZdZmgn+s//73EfgDXW1M+ZUOl36md7tlWp1QFK/ladiq4FvQ82shVjo0KiPDPr0A==
|
||||
"@vue/runtime-dom@3.4.0":
|
||||
version "3.4.0"
|
||||
resolved "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.4.0.tgz#9397aa9e74c6f44a03c4f4755d931a10dbf6ec44"
|
||||
integrity sha512-1ZoHEsA5l77qbx2F+SWo/hQdBksPuOmww1t/jznidDG+xMB/iidafEFvo2ZTtZii0JfTIrlDhjshfYUvQC17wQ==
|
||||
dependencies:
|
||||
"@vue/reactivity" "3.3.12"
|
||||
"@vue/shared" "3.3.12"
|
||||
|
||||
"@vue/runtime-dom@3.3.12":
|
||||
version "3.3.12"
|
||||
resolved "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.3.12.tgz#28a239496e589037774cba7c1b27057242eedb11"
|
||||
integrity sha512-8mMzqiIdl+IYa/OXwKwk6/4ebLq7cYV1pUcwCSwBK2KerUa6cwGosen5xrCL9f8o2DJ9TfPFwbPEvH7OXzUpoA==
|
||||
dependencies:
|
||||
"@vue/runtime-core" "3.3.12"
|
||||
"@vue/shared" "3.3.12"
|
||||
"@vue/runtime-core" "3.4.0"
|
||||
"@vue/shared" "3.4.0"
|
||||
csstype "^3.1.3"
|
||||
|
||||
"@vue/server-renderer@3.3.12":
|
||||
version "3.3.12"
|
||||
resolved "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.3.12.tgz#f0246aba5d5d6fdfa840ac9e4f32d76f03b20665"
|
||||
integrity sha512-OZ0IEK5TU5GXb5J8/wSplyxvGGdIcwEmS8EIO302Vz8K6fGSgSJTU54X0Sb6PaefzZdiN3vHsLXO8XIeF8crQQ==
|
||||
"@vue/server-renderer@3.4.0":
|
||||
version "3.4.0"
|
||||
resolved "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.4.0.tgz#7b0c5e71463140c64d05c314f46a07c9dcc62625"
|
||||
integrity sha512-GuOVCyLDlWPu8nKo5AUxb8B+iB/Ik4I1WwqAlBqf5+y48z6D6rvKshp7KR3cJea+pte1tdTsb0+Ja82KizMZOw==
|
||||
dependencies:
|
||||
"@vue/compiler-ssr" "3.3.12"
|
||||
"@vue/shared" "3.3.12"
|
||||
"@vue/compiler-ssr" "3.4.0"
|
||||
"@vue/shared" "3.4.0"
|
||||
|
||||
"@vue/shared@3.3.11":
|
||||
version "3.3.11"
|
||||
resolved "https://registry.npmmirror.com/@vue/shared/-/shared-3.3.11.tgz#f6a038e15237edefcc90dbfe7edb806dd355c7bd"
|
||||
integrity sha512-u2G8ZQ9IhMWTMXaWqZycnK4UthG1fA238CD+DP4Dm4WJi5hdUKKLg0RMRaRpDPNMdkTwIDkp7WtD0Rd9BH9fLw==
|
||||
|
||||
"@vue/shared@3.3.12":
|
||||
version "3.3.12"
|
||||
resolved "https://registry.npmmirror.com/@vue/shared/-/shared-3.3.12.tgz#7c030c4e2f1db8beb638b159cbb86d0ff78c3198"
|
||||
integrity sha512-6p0Yin0pclvnER7BLNOQuod9Z+cxSYh8pSh7CzHnWNjAIP6zrTlCdHRvSCb1aYEx6i3Q3kvfuWU7nG16CgG1ag==
|
||||
"@vue/shared@3.4.0":
|
||||
version "3.4.0"
|
||||
resolved "https://registry.npmmirror.com/@vue/shared/-/shared-3.4.0.tgz#b6e804a4742929f94f0159aafd0a2940621cd8de"
|
||||
integrity sha512-Nhh3ed3G1R6HDAWiG6YYFt0Zmq/To6u5vjzwa9TIquGheCXPY6nEdIAO8ZdlwXsWqC2yNLj700FOvShpYt5CEA==
|
||||
|
||||
"@vueuse/core@^10.7.0":
|
||||
version "10.7.0"
|
||||
@@ -944,6 +938,11 @@ element-plus@^2.4.4:
|
||||
memoize-one "^6.0.0"
|
||||
normalize-wheel-es "^1.2.0"
|
||||
|
||||
entities@^4.5.0:
|
||||
version "4.5.0"
|
||||
resolved "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48"
|
||||
integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==
|
||||
|
||||
esbuild@^0.19.3:
|
||||
version "0.19.8"
|
||||
resolved "https://registry.npmmirror.com/esbuild/-/esbuild-0.19.8.tgz#ad05b72281d84483fa6b5345bd246c27a207b8f1"
|
||||
@@ -2003,16 +2002,16 @@ vue-router@^4.2.5:
|
||||
dependencies:
|
||||
"@vue/devtools-api" "^6.5.0"
|
||||
|
||||
vue@^3.3.12:
|
||||
version "3.3.12"
|
||||
resolved "https://registry.npmmirror.com/vue/-/vue-3.3.12.tgz#4a3a39e79d22e9826ae7c058863316333c838b63"
|
||||
integrity sha512-jYNv2QmET2OTHsFzfWHMnqgCfqL4zfo97QwofdET+GBRCHhSCHuMTTvNIgeSn0/xF3JRT5OGah6MDwUFN7MPlg==
|
||||
vue@^3.4.0:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.npmmirror.com/vue/-/vue-3.4.0.tgz#0671a2607265c16ab56041174744b6bed8ac2baa"
|
||||
integrity sha512-iTE9Ve/7DO/H39+gXHrNkRdnh1jDwPe/fap4brbPKkp1APMkS03OiZ+UY0dwpqtRX0iPWQTkh8Fu3hKgLtaxfA==
|
||||
dependencies:
|
||||
"@vue/compiler-dom" "3.3.12"
|
||||
"@vue/compiler-sfc" "3.3.12"
|
||||
"@vue/runtime-dom" "3.3.12"
|
||||
"@vue/server-renderer" "3.3.12"
|
||||
"@vue/shared" "3.3.12"
|
||||
"@vue/compiler-dom" "3.4.0"
|
||||
"@vue/compiler-sfc" "3.4.0"
|
||||
"@vue/runtime-dom" "3.4.0"
|
||||
"@vue/server-renderer" "3.4.0"
|
||||
"@vue/shared" "3.4.0"
|
||||
|
||||
which@^2.0.1:
|
||||
version "2.0.2"
|
||||
|
||||
8
server/.gitignore
vendored
8
server/.gitignore
vendored
@@ -1,10 +1,4 @@
|
||||
static/static
|
||||
config.yml
|
||||
mayfly_rsa
|
||||
mayfly_rsa.pub
|
||||
# 数据库备份目录
|
||||
backup/
|
||||
# MySQL 可执行文件 (mysql mysqldump mysqlbinlog) 所在目录
|
||||
mysqlutil/
|
||||
# MariaDB 可执行文件 (mysql mysqldump mysqlbinlog) 所在目录
|
||||
mariadbutil/
|
||||
mayfly_rsa.pub
|
||||
@@ -15,6 +15,7 @@ require (
|
||||
github.com/go-playground/validator/v10 v10.14.0
|
||||
github.com/go-sql-driver/mysql v1.7.1
|
||||
github.com/golang-jwt/jwt/v5 v5.2.0
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/gorilla/websocket v1.5.1
|
||||
github.com/kanzihuang/vitess/go/vt/sqlparser v0.0.0-20231018071450-ac8d9f0167e9
|
||||
github.com/lionsoul2014/ip2region/binding/golang v0.0.0-20230712084735-068dc2aee82d
|
||||
@@ -28,17 +29,13 @@ require (
|
||||
go.mongodb.org/mongo-driver v1.13.1 // mongo
|
||||
golang.org/x/crypto v0.17.0 // ssh
|
||||
golang.org/x/oauth2 v0.15.0
|
||||
golang.org/x/sync v0.1.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
// gorm
|
||||
gorm.io/driver/mysql v1.5.2
|
||||
gorm.io/gorm v1.25.5
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/google/uuid v1.3.0
|
||||
golang.org/x/sync v0.1.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
|
||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
|
||||
|
||||
@@ -61,6 +61,7 @@ func (d *DbBackup) Create(rc *req.Ctx) {
|
||||
Enabled: true,
|
||||
Repeated: form.Repeated,
|
||||
DbInstanceId: db.InstanceId,
|
||||
LastTime: form.StartTime,
|
||||
}
|
||||
tasks = append(tasks, task)
|
||||
}
|
||||
@@ -78,6 +79,7 @@ func (d *DbBackup) Save(rc *req.Ctx) {
|
||||
Name: form.Name,
|
||||
StartTime: form.StartTime,
|
||||
Interval: form.Interval,
|
||||
LastTime: form.StartTime,
|
||||
}
|
||||
task.Id = form.Id
|
||||
biz.ErrIsNilAppendErr(d.DbBackupApp.Save(rc.MetaCtx, task), "保存数据库备份任务失败: %v")
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
package config
|
||||
|
||||
import sysapp "mayfly-go/internal/sys/application"
|
||||
import (
|
||||
sysapp "mayfly-go/internal/sys/application"
|
||||
)
|
||||
|
||||
const (
|
||||
ConfigKeyDbSaveQuerySQL string = "DbSaveQuerySQL" // 数据库是否记录查询相关sql
|
||||
ConfigKeyDbQueryMaxCount string = "DbQueryMaxCount" // 数据库查询的最大数量
|
||||
ConfigKeyDbBackupRestore string = "DbBackupRestore" // 数据库备份
|
||||
ConfigKeyDbMysqlBin string = "MysqlBin" // mysql可执行文件配置
|
||||
)
|
||||
|
||||
// 获取数据库最大查询数量配置
|
||||
@@ -16,3 +20,65 @@ func GetDbQueryMaxCount() int {
|
||||
func GetDbSaveQuerySql() bool {
|
||||
return sysapp.GetConfigApp().GetConfig(ConfigKeyDbSaveQuerySQL).BoolValue(false)
|
||||
}
|
||||
|
||||
type DbBackupRestore struct {
|
||||
BackupPath string // 备份文件路径呢
|
||||
}
|
||||
|
||||
// 获取数据库备份配置
|
||||
func GetDbBackupRestore() *DbBackupRestore {
|
||||
c := sysapp.GetConfigApp().GetConfig(ConfigKeyDbBackupRestore)
|
||||
jm := c.GetJsonMap()
|
||||
|
||||
dbrc := new(DbBackupRestore)
|
||||
|
||||
backupPath := jm["backupPath"]
|
||||
if backupPath == "" {
|
||||
backupPath = "./db/backup"
|
||||
}
|
||||
dbrc.BackupPath = backupPath
|
||||
|
||||
return dbrc
|
||||
}
|
||||
|
||||
// mysql客户端可执行文件配置
|
||||
type MysqlBin struct {
|
||||
Path string // 可执行文件路径
|
||||
MysqlPath string // mysql可执行文件路径
|
||||
MysqldumpPath string // mysqldump可执行文件路径
|
||||
MysqlbinlogPath string // mysqlbinlog可执行文件路径
|
||||
}
|
||||
|
||||
// 获取数据库备份配置
|
||||
func GetMysqlBin() *MysqlBin {
|
||||
c := sysapp.GetConfigApp().GetConfig(ConfigKeyDbMysqlBin)
|
||||
jm := c.GetJsonMap()
|
||||
|
||||
mbc := new(MysqlBin)
|
||||
|
||||
path := jm["path"]
|
||||
if path == "" {
|
||||
path = "./db/backup"
|
||||
}
|
||||
mbc.Path = path
|
||||
|
||||
mysqlPath := jm["mysql"]
|
||||
if mysqlPath == "" {
|
||||
mysqlPath = path + "mysql"
|
||||
}
|
||||
mbc.MysqlPath = mysqlPath
|
||||
|
||||
mysqldumpPath := jm["mysqldump"]
|
||||
if mysqldumpPath == "" {
|
||||
mysqldumpPath = path + "mysqldump"
|
||||
}
|
||||
mbc.MysqldumpPath = mysqldumpPath
|
||||
|
||||
mysqlbinlogPath := jm["mysqlbinlog"]
|
||||
if mysqlbinlogPath == "" {
|
||||
mysqlbinlogPath = path + "mysqlbinlog"
|
||||
}
|
||||
mbc.MysqlbinlogPath = mysqlbinlogPath
|
||||
|
||||
return mbc
|
||||
}
|
||||
|
||||
1
server/internal/db/dbm/dbbin.go
Normal file
1
server/internal/db/dbm/dbbin.go
Normal file
@@ -0,0 +1 @@
|
||||
package dbm
|
||||
@@ -29,6 +29,7 @@ func NewDbBinlog(history *DbBackupHistory) *DbBinlog {
|
||||
Enabled: true,
|
||||
Interval: BinlogDownloadInterval,
|
||||
DbInstanceId: history.DbInstanceId,
|
||||
LastTime: time.Now(),
|
||||
}
|
||||
binlogTask.Id = binlogTask.DbInstanceId
|
||||
return binlogTask
|
||||
|
||||
@@ -4,12 +4,13 @@ import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"github.com/google/uuid"
|
||||
"mayfly-go/internal/db/domain/entity"
|
||||
"mayfly-go/internal/db/domain/repository"
|
||||
"mayfly-go/internal/db/domain/service"
|
||||
"mayfly-go/pkg/model"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
var _ service.DbBackupSvc = (*DbBackupSvcImpl)(nil)
|
||||
|
||||
@@ -18,11 +18,12 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/sync/singleflight"
|
||||
|
||||
"mayfly-go/internal/db/config"
|
||||
"mayfly-go/internal/db/dbm"
|
||||
"mayfly-go/internal/db/domain/entity"
|
||||
"mayfly-go/internal/db/domain/repository"
|
||||
"mayfly-go/internal/db/domain/service"
|
||||
"mayfly-go/pkg/config"
|
||||
|
||||
"mayfly-go/pkg/logx"
|
||||
"mayfly-go/pkg/utils/structx"
|
||||
)
|
||||
@@ -145,13 +146,13 @@ func (svc *DbInstanceSvcImpl) Backup(ctx context.Context, backupHistory *entity.
|
||||
}
|
||||
|
||||
cmd := exec.CommandContext(ctx, mysqldumpPath(), args...)
|
||||
logx.Debug("backup database using mysqldump binary: ", cmd.String())
|
||||
logx.Debugf("backup database using mysqldump binary: %s", cmd.String())
|
||||
if err := runCmd(cmd); err != nil {
|
||||
logx.Errorf("运行 mysqldump 程序失败: %v", err)
|
||||
return nil, errors.Wrap(err, "运行 mysqldump 程序失败")
|
||||
}
|
||||
|
||||
logx.Debug("Checking dumped file stat", tmpFile)
|
||||
logx.Debugf("Checking dumped file stat: %s", tmpFile)
|
||||
if _, err := os.Stat(tmpFile); err != nil {
|
||||
logx.Errorf("未找到备份文件: %v", err)
|
||||
return nil, errors.Wrapf(err, "未找到备份文件")
|
||||
@@ -297,20 +298,20 @@ func parseLocalBinlogFirstEventTime(ctx context.Context, filePath string) (event
|
||||
// getBinlogDir gets the binlogDir.
|
||||
func getBinlogDir(instanceId uint64) string {
|
||||
return filepath.Join(
|
||||
config.Conf.Db.BackupPath,
|
||||
config.GetDbBackupRestore().BackupPath,
|
||||
fmt.Sprintf("instance-%d", instanceId),
|
||||
"binlog")
|
||||
}
|
||||
|
||||
func getDbInstanceBackupRoot(instanceId uint64) string {
|
||||
return filepath.Join(
|
||||
config.Conf.Db.BackupPath,
|
||||
config.GetDbBackupRestore().BackupPath,
|
||||
fmt.Sprintf("instance-%d", instanceId))
|
||||
}
|
||||
|
||||
func getDbBackupDir(instanceId, backupId uint64) string {
|
||||
return filepath.Join(
|
||||
config.Conf.Db.BackupPath,
|
||||
config.GetDbBackupRestore().BackupPath,
|
||||
fmt.Sprintf("instance-%d", instanceId),
|
||||
fmt.Sprintf("backup-%d", backupId))
|
||||
}
|
||||
@@ -885,13 +886,13 @@ func formatDateTime(t time.Time) string {
|
||||
}
|
||||
|
||||
func mysqlPath() string {
|
||||
return config.Conf.Db.MysqlUtil.Mysql
|
||||
return config.GetMysqlBin().MysqlPath
|
||||
}
|
||||
|
||||
func mysqldumpPath() string {
|
||||
return config.Conf.Db.MysqlUtil.MysqlDump
|
||||
return config.GetMysqlBin().MysqldumpPath
|
||||
}
|
||||
|
||||
func mysqlbinlogPath() string {
|
||||
return config.Conf.Db.MysqlUtil.MysqlBinlog
|
||||
return config.GetMysqlBin().MysqlbinlogPath
|
||||
}
|
||||
|
||||
@@ -55,7 +55,6 @@ type Config struct {
|
||||
Sqlite Sqlite `yaml:"sqlite"`
|
||||
Redis Redis `yaml:"redis"`
|
||||
Log Log `yaml:"log"`
|
||||
Db Db `yaml:"db"`
|
||||
}
|
||||
|
||||
func (c *Config) IfBlankDefaultValue() {
|
||||
@@ -73,7 +72,6 @@ func (c *Config) IfBlankDefaultValue() {
|
||||
c.Jwt.Default()
|
||||
c.Mysql.Default()
|
||||
c.Sqlite.Default()
|
||||
c.Db.Default()
|
||||
}
|
||||
|
||||
// 配置文件内容校验
|
||||
@@ -82,7 +80,7 @@ func (c *Config) Valid() {
|
||||
c.Aes.Valid()
|
||||
}
|
||||
|
||||
// 替换系统环境变量,如果环境变量中存在该值,则优秀使用环境变量设定的值
|
||||
// 替换系统环境变量,如果环境变量中存在该值,则优先使用环境变量设定的值
|
||||
func (c *Config) ReplaceOsEnv() {
|
||||
serverPort := os.Getenv("MAYFLY_SERVER_PORT")
|
||||
if serverPort != "" {
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
package config
|
||||
|
||||
type Db struct {
|
||||
BackupPath string `yaml:"backup-path"`
|
||||
MysqlUtil MysqlUtil `yaml:"mysqlutil-path"`
|
||||
MariadbUtil MysqlUtil `yaml:"mariadbutil-path"`
|
||||
}
|
||||
|
||||
type MysqlUtil struct {
|
||||
Mysql string `yaml:"mysql"`
|
||||
MysqlDump string `yaml:"mysqldump"`
|
||||
MysqlBinlog string `yaml:"mysqlbinlog"`
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package config
|
||||
|
||||
func (db *Db) Default() {
|
||||
if len(db.BackupPath) == 0 {
|
||||
db.BackupPath = "./backup"
|
||||
}
|
||||
|
||||
if len(db.MysqlUtil.Mysql) == 0 {
|
||||
db.MysqlUtil.Mysql = "./mysqlutil/bin/mysql"
|
||||
}
|
||||
if len(db.MysqlUtil.MysqlDump) == 0 {
|
||||
db.MysqlUtil.MysqlDump = "./mysqlutil/bin/mysqldump"
|
||||
}
|
||||
if len(db.MysqlUtil.Mysql) == 0 {
|
||||
db.MysqlUtil.MysqlBinlog = "./mysqlutil/bin/mysqlbinlog"
|
||||
}
|
||||
|
||||
if len(db.MariadbUtil.Mysql) == 0 {
|
||||
db.MariadbUtil.Mysql = "./mariadbutil/bin/mariadb"
|
||||
}
|
||||
if len(db.MariadbUtil.MysqlDump) == 0 {
|
||||
db.MariadbUtil.MysqlDump = "./mariadbutil/bin/mariadb-dump"
|
||||
}
|
||||
if len(db.MariadbUtil.MysqlBinlog) == 0 {
|
||||
db.MariadbUtil.MysqlBinlog = "./mariadbutil/bin/mariadb-binlog"
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package config
|
||||
|
||||
func (db *Db) Default() {
|
||||
if len(db.BackupPath) == 0 {
|
||||
db.BackupPath = "./backup"
|
||||
}
|
||||
|
||||
if len(db.MysqlUtil.Mysql) == 0 {
|
||||
db.MysqlUtil.Mysql = "./mysqlutil/bin/mysql"
|
||||
}
|
||||
if len(db.MysqlUtil.MysqlDump) == 0 {
|
||||
db.MysqlUtil.MysqlDump = "./mysqlutil/bin/mysqldump"
|
||||
}
|
||||
if len(db.MysqlUtil.MysqlBinlog) == 0 {
|
||||
db.MysqlUtil.MysqlBinlog = "./mysqlutil/bin/mysqlbinlog"
|
||||
}
|
||||
|
||||
if len(db.MariadbUtil.Mysql) == 0 {
|
||||
db.MariadbUtil.Mysql = "./mariadbutil/bin/mariadb"
|
||||
}
|
||||
if len(db.MariadbUtil.MysqlDump) == 0 {
|
||||
db.MariadbUtil.MysqlDump = "./mariadbutil/bin/mariadb-dump"
|
||||
}
|
||||
if len(db.MariadbUtil.MysqlBinlog) == 0 {
|
||||
db.MariadbUtil.MysqlBinlog = "./mariadbutil/bin/mariadb-binlog"
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package config
|
||||
|
||||
func (db *Db) Default() {
|
||||
if len(db.BackupPath) == 0 {
|
||||
db.BackupPath = "./backup"
|
||||
}
|
||||
|
||||
if len(db.MysqlUtil.Mysql) == 0 {
|
||||
db.MysqlUtil.Mysql = "./mysqlutil/bin/mysql.exe"
|
||||
}
|
||||
if len(db.MysqlUtil.MysqlDump) == 0 {
|
||||
db.MysqlUtil.MysqlDump = "./mysqlutil/bin/mysqldump.exe"
|
||||
}
|
||||
if len(db.MysqlUtil.Mysql) == 0 {
|
||||
db.MysqlUtil.MysqlBinlog = "./mysqlutil/bin/mysqlbinlog.exe"
|
||||
}
|
||||
|
||||
if len(db.MariadbUtil.Mysql) == 0 {
|
||||
db.MariadbUtil.Mysql = "./mariadbutil/bin/mariadb.exe"
|
||||
}
|
||||
if len(db.MariadbUtil.MysqlDump) == 0 {
|
||||
db.MariadbUtil.MysqlDump = "./mariadbutil/bin/mariadb-dump.exe"
|
||||
}
|
||||
if len(db.MariadbUtil.MysqlBinlog) == 0 {
|
||||
db.MariadbUtil.MysqlBinlog = "./mariadbutil/bin/mariadb-binlog.exe"
|
||||
}
|
||||
}
|
||||
57
server/pkg/model/json_time.go
Normal file
57
server/pkg/model/json_time.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
"mayfly-go/pkg/utils/timex"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type JsonTime struct {
|
||||
time.Time
|
||||
}
|
||||
|
||||
func NewJsonTime(t time.Time) JsonTime {
|
||||
return JsonTime{
|
||||
Time: t,
|
||||
}
|
||||
}
|
||||
|
||||
func NowJsonTime() JsonTime {
|
||||
return JsonTime{
|
||||
Time: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
func (j JsonTime) MarshalJSON() ([]byte, error) {
|
||||
var stamp = fmt.Sprintf("\"%s\"", j.Format(timex.DefaultDateTimeFormat))
|
||||
return []byte(stamp), nil
|
||||
}
|
||||
|
||||
func (j *JsonTime) UnmarshalJSON(b []byte) error {
|
||||
s := strings.ReplaceAll(string(b), "\"", "")
|
||||
t, err := time.Parse(timex.DefaultDateTimeFormat, s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*j = NewJsonTime(t)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (j JsonTime) Value() (driver.Value, error) {
|
||||
var zeroTime time.Time
|
||||
if j.Time.UnixNano() == zeroTime.UnixNano() {
|
||||
return nil, nil
|
||||
}
|
||||
return j.Time, nil
|
||||
}
|
||||
|
||||
func (j *JsonTime) Scan(v interface{}) error {
|
||||
value, ok := v.(time.Time)
|
||||
if ok {
|
||||
*j = JsonTime{Time: value}
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("can not convert %v to timestamp", v)
|
||||
}
|
||||
@@ -2,6 +2,8 @@ package timex
|
||||
|
||||
import "time"
|
||||
|
||||
const DefaultDateTimeFormat = "2006-01-02 15:04:05"
|
||||
|
||||
func DefaultFormat(time time.Time) string {
|
||||
return time.Format("2006-01-02 15:04:05")
|
||||
return time.Format(DefaultDateTimeFormat)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user