系统数据迁移、复制功能开发:增加导入类型“租户模式导入”

This commit is contained in:
suguangxu
2025-06-10 12:16:25 +08:00
parent 1f7e0e6763
commit d75c0042ed
4 changed files with 223 additions and 41 deletions

View File

@ -4,6 +4,8 @@ import { ErrorMessageMode } from '/#/axios';
enum Api {
ExportDatas= '/system/dataMigration/exportDatas',
DownloadDatas='/system/dataMigration/downloadDatas',
LogList='/system/dataMigration/logList',
LogDetails='/system/dataMigration/logDetails',
}
/**
@ -25,10 +27,7 @@ export async function exportDatas(params, mode: ErrorMessageMode = 'modal') {
/**
* @description: 根据uuid(目录名称)下载数据
*/
export async function downloadDatas(
params?: object,
mode: ErrorMessageMode = 'modal'
) {
export async function downloadDatas(params?: object, mode: ErrorMessageMode = 'modal') {
return defHttp.download(
{
url: Api.DownloadDatas+"/"+params.uuid,
@ -42,4 +41,39 @@ export async function downloadDatas(
}
/**
* @description: 获取日志列表
*/
export async function getLogList(mode: ErrorMessageMode = 'modal') {
return defHttp.get<[]>(
{
url: Api.LogList
},
{
errorMessageMode: mode,
},
);
}
/**
* @description: 获取日志详情
*/
export async function getLogDetails(fileName:String,mode: ErrorMessageMode = 'modal') {
return defHttp.get<[]>(
{
url: Api.LogDetails,
params:{
fileName
}
},
{
errorMessageMode: mode,
},
);
}

View File

@ -234,15 +234,17 @@
display:block;
margin-left:20px;
margin-bottom:20px;
color: rgba(0,0,255,0.8);
text-decoration:underline rgba(#0000ff,0.8);
font-weight: 500;
font-size: 16px;
cursor: pointer;
&:hover{
color: rgba(0,0,255,1);
text-decoration:underline #0000ff;
span{
color: rgba(0,0,255,0.8);
text-decoration:underline rgba(#0000ff,0.8);
font-weight: 500;
font-size: 16px;
cursor: pointer;
&:hover{
color: rgba(0,0,255,1);
text-decoration:underline #0000ff;
}
}
}

View File

@ -3,21 +3,31 @@
<slot></slot>
<a-modal
v-model:visible="data.visible"
:title="t(`导入数据[${coverType=='override'?'覆盖模式':'新增模式'}]`)"
:title="t(`导入数据[${importType=='overrideMode'?'覆盖模式':'租户模式'}]`)"
:maskClosable="false"
@ok="doUpload"
@cancel="close"
@click.stop=""
>
<div class="tenant-box" v-if="importType=='tenantMode'">
<Form ref="formRef" :model="formModel">
<FormItem
label="租户编码"
name="tenantCode"
:rules="[{ required: true, message: '请输入租户编码' }]"
>
<a-input v-model:value="formModel.tenantCode" style="width:300px;" placeholder="请输入租户编码" />
</FormItem>
</Form>
</div>
<div class="upload-box">
<a-upload
v-model:file-list="fileList"
class="upload-box"
name="file"
accept=".json,.zip"
:headers="data.headers"
:max-count="1"
@change="handleChange"
:before-upload="beforeUpload"
@remove="handleRemove"
>
@ -25,12 +35,14 @@
<div class="a-upload__text">{{ t('将文件拖到此处,或') }}<em>{{ t('点击上传') }}</em></div>
</a-upload>
</div>
</a-modal>
</span>
</template>
<script setup lang="ts">
import { reactive,ref} from 'vue';
import { Form, FormInstance} from 'ant-design-vue';
import BgImg from '../../assets/sysconfig_import.png';
import { useI18n } from '/@/hooks/web/useI18n';
import { defHttp } from '/@/utils/http/axios';
@ -38,11 +50,18 @@
import { useMessage } from '/@/hooks/web/useMessage';
const props = defineProps({
coverType: {
type: Boolean
importType: {
type:String,
},
});
const formRef = ref<FormInstance>();
const formModel=ref({
tenantCode:''
});
const FormItem = Form.Item;
const { t } = useI18n();
const globSetting = useGlobSetting();
const { notification } = useMessage();
@ -53,36 +72,38 @@
visible: false
});
const fileList=ref([]);
const fileList=ref<File[]>([]);
async function open() {
if(props.coverType!=='override'){
alert("新增模式导入暂时不可用");
return;
}
data.visible = true;
}
function close() {
data.visible = false;
fileList.value=[];
formModel.value.tenantCode='';
}
function handleRemove(file){
function handleRemove(file:File){
const index = fileList.value.indexOf(file);
const newFileList = fileList.value.slice();
newFileList.splice(index, 1);
fileList.value = newFileList;
};
function beforeUpload(file){
function beforeUpload(file:File){
fileList.value = [...(fileList.value || []), file];
return false;
};
function doUpload(){
async function doUpload(){
if(props.importType=='tenantMode'){
try{
await formRef.value!.validate();
}catch(error){
return;
}
}
if(fileList==undefined||fileList.value.length==0){
notification.error({
message: '提示',
@ -106,8 +127,8 @@
name: 'file',
file: files,
data:{
configType:props.configType,
cover:props.coverType=='override'?true:false
importType:props.importType,
tenantCode:formModel.value.tenantCode
}
}
).then((data) => {
@ -125,6 +146,12 @@
</script>
<style lang="less" scoped>
.tenant-box{
height:100px;
display: flex;
justify-content: center;
align-items: center;
}
.upload-box {
display: inline-block;
}

View File

@ -1,30 +1,116 @@
<template>
<div class="import">
<ImportSystemConfig coverType="new">
<span class="item-action">新增模式导入</span>
</ImportSystemConfig>
<ImportSystemConfig coverType="override">
<span class="item-action">覆盖模式导入</span>
</ImportSystemConfig>
<div class="import-log"><span @click.stop="showLogsDialog">查看导入日志</span></div>
<div class="import-type-wrapper">
<ImportSystemConfig importType="tenantMode">
<span class="item-action">租户模式导入</span>
</ImportSystemConfig>
<ImportSystemConfig importType="overrideMode">
<span class="item-action">覆盖模式导入</span>
</ImportSystemConfig>
</div>
</div>
<a-modal
v-model:visible="logsDialog.visible"
:title="t('导入日志')"
:maskClosable="false"
:width="700"
:footer="null"
>
<div class="logs-content">
<template v-for="item in logs" :key="item.index">
<div class="log">
日志时间<span class='log-time'>{{item.time}}</span>
执行结果<span :class="{'success':item.result=='success','fail':item.result!=='success'}">{{item.result=='success'?'成功':'失败'}}</span>
<span class="view-log-detail" v-if="item.result!=='success'" @click.stop="viewLogDetails(item.fileName)" >查看日志详情</span>
</div>
</template>
</div>
</a-modal>
<a-modal
v-model:visible="logDetailsDialog.visible"
:title="t('日志详情')"
:maskClosable="false"
:width="700"
:footer="null"
>
<div class="log-details">
<a-textarea v-model:value="logDetails" :style="{whiteSpace:'pre',overflowX: 'auto',color:'red'}" :rows="20" />
</div>
</a-modal>
</template>
<script lang="ts" setup>
import { reactive,ref} from 'vue';
import { useI18n } from '/@/hooks/web/useI18n';
import ImportSystemConfig from './ImportSystemConfig.vue';
import { getLogList,getLogDetails} from '/@/api/system/dataMigration';
const { t } = useI18n();
const logsDialog: {
visible: boolean;
} = reactive({
visible: false
});
const logDetailsDialog: {
visible: boolean;
} = reactive({
visible: false
});
const logs=ref([]);
const logDetails=ref([]);
async function showLogsDialog(){
logs.value=[];
logsDialog.visible=true;
let resList=await getLogList();
logs.value=resList;
}
async function viewLogDetails(fileName:String){
let logContent=await getLogDetails(fileName);
logDetailsDialog.visible=true;
logDetails.value=logContent;
}
</script>
<style lang="less" scoped>
.import{
height:100%;
width:100%;
border:1px solid #d6d6d6;
border-radius:8px;
padding:10px 10px;
}
.import-log{
height:100px;
width:100%;
span{
color: rgba(0,0,255,0.8);
text-decoration:underline rgba(#0000ff,0.8);
font-weight: 500;
font-size: 16px;
cursor: pointer;
&:hover{
color: rgba(0,0,255,1);
text-decoration:underline #0000ff;
}
}
}
.import-type-wrapper{
display:flex;
flex-direction:row;
align-items:center;
justify-content:center;
height:100%;
height:calc(100% - 100px);
width:100%;
gap:100px;
border:1px solid #d6d6d6;
border-radius:8px;
}
.item-action {
@ -40,4 +126,37 @@
}
.logs-content{
width:100%;
height:500px;
overflow-y:scroll;
padding:30px 30px;
.log{
padding:5px 0px;
.log-time{
color:rgba(22, 119, 224, 1);
margin-right:30px;
}
.success{
color:green;
}
.fail{
color:red;
}
.view-log-detail{
margin-left:30px;
color:rgba(22, 119, 224, 1);
&:hover{
text-decoration:underline;
cursor:pointer;
}
}
}
}
.log-details{
height:500px;
}
</style>