initdata timeout
This commit is contained in:
@ -33,7 +33,8 @@
|
|||||||
"v3-infinite-loading": "^1.2.2",
|
"v3-infinite-loading": "^1.2.2",
|
||||||
"vue": "3.2.45",
|
"vue": "3.2.45",
|
||||||
"vue-cropper": "1.0.3",
|
"vue-cropper": "1.0.3",
|
||||||
"vue-router": "4.1.4"
|
"vue-router": "4.1.4",
|
||||||
|
"vue-spinner": "^1.0.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "3.1.0",
|
"@vitejs/plugin-vue": "3.1.0",
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
import request from "@/utils/request";
|
import request from "@/utils/request";
|
||||||
|
|
||||||
export const check = (tenantId) => {
|
export const check = (tenantId) => {
|
||||||
return request({
|
return request({
|
||||||
url: `/tenant/init/check/${tenantId}`,
|
url: `/tenant/init/check/${tenantId}`,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// /tenant/init/createTables/{tenantId}
|
// /tenant/init/createTables/{tenantId}
|
||||||
export const createTables = (tenantId) => {
|
export const createTables = (tenantId) => {
|
||||||
return request({
|
return request({
|
||||||
url: `/tenant/init/createTables/${tenantId}`,
|
url: `/tenant/init/createTables/${tenantId}`,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// /tenant/init/initData/{tenantId}
|
// /tenant/init/initData/{tenantId}
|
||||||
export const initData = (tenantId) => {
|
export const initData = (tenantId) => {
|
||||||
return request({
|
return request({
|
||||||
url: `/tenant/init/initData/${tenantId}`,
|
url: `/tenant/init/initData/${tenantId}`,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
timeout: 0,
|
timeout: 0,
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
@ -1,24 +1,33 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="app-container">
|
<div class="app-container">
|
||||||
<el-steps
|
<el-steps
|
||||||
:active="activeStep"
|
:active="activeStep"
|
||||||
:process-status="
|
:process-status="
|
||||||
currentStepStatus === 0
|
currentStepStatus === 0
|
||||||
? 'error'
|
? 'error'
|
||||||
: currentStepStatus === 1
|
: currentStepStatus === 1
|
||||||
? 'process'
|
? 'process'
|
||||||
: 'success'
|
: 'success'
|
||||||
"
|
"
|
||||||
align-center
|
align-center
|
||||||
class="init-steps"
|
class="init-steps"
|
||||||
finish-status="success"
|
finish-status="success"
|
||||||
>
|
>
|
||||||
<el-step description="初始化开始" title="开始"/>
|
<el-step description="初始化开始" title="开始" />
|
||||||
<el-step description="步骤描述" title="检查"/>
|
<el-step description="步骤描述" title="检查" />
|
||||||
<el-step description="步骤描述" title="步骤2"/>
|
<el-step description="步骤描述" title="步骤2" />
|
||||||
<el-step description="步骤描述" title="步骤3"/>
|
<el-step description="步骤描述" title="步骤3" />
|
||||||
</el-steps>
|
</el-steps>
|
||||||
<div class="steps-content">
|
<el-row v-if="loading" justify="center">
|
||||||
|
<el-col :span="1.5">
|
||||||
|
<div class="btn-loading">
|
||||||
|
<el-icon size="80">
|
||||||
|
<refresh />
|
||||||
|
</el-icon>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<div v-else class="steps-content">
|
||||||
<div v-if="activeStep === 0" class="start-step">
|
<div v-if="activeStep === 0" class="start-step">
|
||||||
<el-row justify="center">
|
<el-row justify="center">
|
||||||
<el-col :span="1.5">
|
<el-col :span="1.5">
|
||||||
@ -28,17 +37,17 @@
|
|||||||
</div>
|
</div>
|
||||||
<div v-else-if="activeStep === 1">
|
<div v-else-if="activeStep === 1">
|
||||||
<el-alert
|
<el-alert
|
||||||
v-if="currentStepStatus === 0"
|
v-if="currentStepStatus === 0"
|
||||||
:closable="false"
|
:closable="false"
|
||||||
:type="
|
:type="
|
||||||
currentStepStatus === 0
|
currentStepStatus === 0
|
||||||
? 'error'
|
? 'error'
|
||||||
: currentStepStatus === 1
|
: currentStepStatus === 1
|
||||||
? 'info'
|
? 'info'
|
||||||
: 'success'
|
: 'success'
|
||||||
"
|
"
|
||||||
show-icon
|
show-icon
|
||||||
>错误码: {{ tenantInitCheckResult.result }}, 错误信息:
|
>错误码: {{ tenantInitCheckResult.result }}, 错误信息:
|
||||||
{{ tenantInitCheckResult.msg }}
|
{{ tenantInitCheckResult.msg }}
|
||||||
</el-alert>
|
</el-alert>
|
||||||
<el-row style="margin-top: 36px">
|
<el-row style="margin-top: 36px">
|
||||||
@ -53,26 +62,26 @@
|
|||||||
<div class="card-content-item">
|
<div class="card-content-item">
|
||||||
<span class="card-content-item-title">租户名称:</span>
|
<span class="card-content-item-title">租户名称:</span>
|
||||||
<span class="card-content-item-value">{{
|
<span class="card-content-item-value">{{
|
||||||
tenantInitCheckResult.tenantName
|
tenantInitCheckResult.tenantName
|
||||||
}}</span>
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
<!--模式-->
|
<!--模式-->
|
||||||
<div class="card-content-item">
|
<div class="card-content-item">
|
||||||
<span class="card-content-item-title">模式:</span>
|
<span class="card-content-item-title">模式:</span>
|
||||||
<span class="card-content-item-value">{{
|
<span class="card-content-item-value">{{
|
||||||
tenantModeDict.find(
|
tenantModeDict.find(
|
||||||
(item) => item.value === tenantInitCheckResult.mode
|
(item) => item.value === tenantInitCheckResult.mode
|
||||||
)?.label
|
)?.label
|
||||||
}}</span>
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<div
|
<div
|
||||||
v-if="tenantInitCheckResult.datasource"
|
v-if="tenantInitCheckResult.datasource"
|
||||||
class="data-card init-check-base-data"
|
class="data-card init-check-base-data"
|
||||||
style="margin-left: 24px"
|
style="margin-left: 24px"
|
||||||
>
|
>
|
||||||
<div class="card-title">
|
<div class="card-title">
|
||||||
<span>数据源数据</span>
|
<span>数据源数据</span>
|
||||||
@ -81,43 +90,43 @@
|
|||||||
<div class="card-content-item">
|
<div class="card-content-item">
|
||||||
<span class="card-content-item-title">数据库IP:</span>
|
<span class="card-content-item-title">数据库IP:</span>
|
||||||
<span class="card-content-item-value">{{
|
<span class="card-content-item-value">{{
|
||||||
tenantInitCheckResult.datasource.ip
|
tenantInitCheckResult.datasource.ip
|
||||||
}}</span>
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
<!-- 端口-->
|
<!-- 端口-->
|
||||||
<div class="card-content-item">
|
<div class="card-content-item">
|
||||||
<span class="card-content-item-title">端口:</span>
|
<span class="card-content-item-title">端口:</span>
|
||||||
<span class="card-content-item-value">{{
|
<span class="card-content-item-value">{{
|
||||||
tenantInitCheckResult.datasource.port
|
tenantInitCheckResult.datasource.port
|
||||||
}}</span>
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
<!--模式-->
|
<!--模式-->
|
||||||
<div class="card-content-item">
|
<div class="card-content-item">
|
||||||
<span class="card-content-item-title">类型:</span>
|
<span class="card-content-item-title">类型:</span>
|
||||||
<span class="card-content-item-value">{{
|
<span class="card-content-item-value">{{
|
||||||
tenantInitCheckResult.datasource.type
|
tenantInitCheckResult.datasource.type
|
||||||
}}</span>
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
<!-- 数据库名称-->
|
<!-- 数据库名称-->
|
||||||
<div class="card-content-item">
|
<div class="card-content-item">
|
||||||
<span class="card-content-item-title">数据库名称:</span>
|
<span class="card-content-item-title">数据库名称:</span>
|
||||||
<span class="card-content-item-value">{{
|
<span class="card-content-item-value">{{
|
||||||
tenantInitCheckResult.datasource.dbName
|
tenantInitCheckResult.datasource.dbName
|
||||||
}}</span>
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
<!-- 用户名-->
|
<!-- 用户名-->
|
||||||
<div class="card-content-item">
|
<div class="card-content-item">
|
||||||
<span class="card-content-item-title">用户名:</span>
|
<span class="card-content-item-title">用户名:</span>
|
||||||
<span class="card-content-item-value">{{
|
<span class="card-content-item-value">{{
|
||||||
tenantInitCheckResult.datasource.username
|
tenantInitCheckResult.datasource.username
|
||||||
}}</span>
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
<!-- 密码-->
|
<!-- 密码-->
|
||||||
<div class="card-content-item">
|
<div class="card-content-item">
|
||||||
<span class="card-content-item-title">密码:</span>
|
<span class="card-content-item-title">密码:</span>
|
||||||
<span class="card-content-item-value">{{
|
<span class="card-content-item-value">{{
|
||||||
tenantInitCheckResult.datasource.password
|
tenantInitCheckResult.datasource.password
|
||||||
}}</span>
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -126,16 +135,16 @@
|
|||||||
<el-row justify="center" style="margin-top: 24px">
|
<el-row justify="center" style="margin-top: 24px">
|
||||||
<el-col :span="1.5">
|
<el-col :span="1.5">
|
||||||
<div
|
<div
|
||||||
v-if="currentStepStatus === 0"
|
v-if="currentStepStatus === 0"
|
||||||
class="circle-button error"
|
class="circle-button error"
|
||||||
@click="router.push({ path: '/tenant/tenant' })"
|
@click="router.push({ path: '/tenant/tenant' })"
|
||||||
>
|
>
|
||||||
关闭
|
关闭
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-else-if="currentStepStatus === 2"
|
v-else-if="currentStepStatus === 2"
|
||||||
class="circle-button"
|
class="circle-button"
|
||||||
@click="createDatabase"
|
@click="createDatabase"
|
||||||
>
|
>
|
||||||
创建库
|
创建库
|
||||||
</div>
|
</div>
|
||||||
@ -143,41 +152,57 @@
|
|||||||
</el-row>
|
</el-row>
|
||||||
</div>
|
</div>
|
||||||
<template v-else-if="activeStep === 2">
|
<template v-else-if="activeStep === 2">
|
||||||
<el-alert v-if="currentStepStatus === 0" :closable="false" show-icon type="error"
|
<el-alert
|
||||||
>错误码: {{ createTablesResult.result }}, 错误信息:
|
v-if="currentStepStatus === 0"
|
||||||
|
:closable="false"
|
||||||
|
show-icon
|
||||||
|
type="error"
|
||||||
|
>错误码: {{ createTablesResult.result }}, 错误信息:
|
||||||
{{ createTablesResult.msg }}
|
{{ createTablesResult.msg }}
|
||||||
</el-alert
|
</el-alert>
|
||||||
>
|
|
||||||
<el-row justify="center" style="margin-top: 24px">
|
<el-row justify="center" style="margin-top: 24px">
|
||||||
<el-col :span="1.5">
|
<el-col :span="1.5">
|
||||||
<div
|
<div
|
||||||
v-if="currentStepStatus === 0"
|
v-if="currentStepStatus === 0"
|
||||||
class="circle-button error"
|
class="circle-button error"
|
||||||
@click="router.push({ path: '/tenant/tenant' })"
|
@click="router.push({ path: '/tenant/tenant' })"
|
||||||
>
|
>
|
||||||
关闭
|
关闭
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="currentStepStatus === 2" class="circle-button" @click="importData">导入数据</div>
|
<div
|
||||||
|
v-else-if="currentStepStatus === 2"
|
||||||
|
class="circle-button"
|
||||||
|
@click="importData"
|
||||||
|
>
|
||||||
|
<span>导入数据</span>
|
||||||
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="activeStep === 3">
|
<template v-else-if="activeStep === 3">
|
||||||
<el-alert v-if="currentStepStatus === 0" :closable="false" show-icon type="error"
|
<el-alert
|
||||||
>错误码: {{ initDataResult.result }}, 错误信息:
|
v-if="currentStepStatus === 0"
|
||||||
|
:closable="false"
|
||||||
|
show-icon
|
||||||
|
type="error"
|
||||||
|
>错误码: {{ initDataResult.result }}, 错误信息:
|
||||||
{{ initDataResult.msg }}
|
{{ initDataResult.msg }}
|
||||||
</el-alert
|
</el-alert>
|
||||||
>
|
|
||||||
<el-row justify="center" style="margin-top: 24px">
|
<el-row justify="center" style="margin-top: 24px">
|
||||||
<el-col :span="1.5">
|
<el-col :span="1.5">
|
||||||
<div
|
<div
|
||||||
v-if="currentStepStatus === 0"
|
v-if="currentStepStatus === 0"
|
||||||
class="circle-button error"
|
class="circle-button error"
|
||||||
@click="router.push({ path: '/tenant/tenant' })"
|
@click="router.push({ path: '/tenant/tenant' })"
|
||||||
>
|
>
|
||||||
关闭
|
关闭
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="currentStepStatus===2" class="circle-button finish"
|
<div
|
||||||
@click="router.push({ path: '/tenant/tenant' })">完成
|
v-else-if="currentStepStatus === 2"
|
||||||
|
class="circle-button finish"
|
||||||
|
@click="router.push({ path: '/tenant/tenant' })"
|
||||||
|
>
|
||||||
|
完成
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
@ -186,21 +211,22 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import {onMounted, reactive, ref, toRefs, watchEffect} from "vue";
|
import { onMounted, reactive, ref, toRefs, watchEffect } from "vue";
|
||||||
import {check, createTables, initData} from "@/api/tenant/init";
|
import { check, createTables, initData } from "@/api/tenant/init";
|
||||||
import {useRoute, useRouter} from "vue-router";
|
import { useRoute, useRouter } from "vue-router";
|
||||||
import {tenantModeDict} from "@/constant/dict";
|
import { tenantModeDict } from "@/constant/dict";
|
||||||
|
import { RefreshRight, Refresh } from "@element-plus/icons-vue";
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const activeStep = ref(0);
|
const activeStep = ref(0);
|
||||||
const currentStepStatus = ref(1); // 0: error 1: process 2: finish
|
const currentStepStatus = ref(1); // 0: error 1: process 2: finish
|
||||||
const tenantId = ref(undefined);
|
const tenantId = ref(undefined);
|
||||||
|
const loading = ref(false);
|
||||||
if (route.query.id) {
|
if (route.query.id) {
|
||||||
tenantId.value = route.query.id;
|
tenantId.value = route.query.id;
|
||||||
} else {
|
} else {
|
||||||
router.push({path: "/tenant/tenant"});
|
router.push({ path: "/tenant/tenant" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = reactive({
|
const data = reactive({
|
||||||
@ -209,64 +235,82 @@ const data = reactive({
|
|||||||
initDataResult: {},
|
initDataResult: {},
|
||||||
});
|
});
|
||||||
|
|
||||||
const {tenantInitCheckResult, createTablesResult, initDataResult} = toRefs(data);
|
const { tenantInitCheckResult, createTablesResult, initDataResult } =
|
||||||
|
toRefs(data);
|
||||||
const tenantInitCheck = () => {
|
const tenantInitCheck = () => {
|
||||||
activeStep.value = 1;
|
activeStep.value = 1;
|
||||||
currentStepStatus.value = 1;
|
currentStepStatus.value = 1;
|
||||||
check(tenantId.value).then((resp) => {
|
check(tenantId.value)
|
||||||
tenantInitCheckResult.value = resp;
|
.then((resp) => {
|
||||||
if (resp.result === 200) {
|
tenantInitCheckResult.value = resp;
|
||||||
|
if (resp.result === 200) {
|
||||||
|
currentStepStatus.value = 2;
|
||||||
|
} else {
|
||||||
|
currentStepStatus.value = 0;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
currentStepStatus.value = 2;
|
currentStepStatus.value = 2;
|
||||||
} else {
|
activeStep.value = 2;
|
||||||
currentStepStatus.value = 0;
|
})
|
||||||
}
|
.finally(() => {
|
||||||
}).catch(() => {
|
loading.value = false;
|
||||||
currentStepStatus.value = 2;
|
});
|
||||||
activeStep.value = 2;
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 第二步: 创建数据库
|
// 第二步: 创建数据库
|
||||||
const createDatabase = () => {
|
const createDatabase = () => {
|
||||||
|
loading.value = true;
|
||||||
activeStep.value = 2;
|
activeStep.value = 2;
|
||||||
currentStepStatus.value = 1;
|
currentStepStatus.value = 1;
|
||||||
createTables(tenantId.value).then((resp) => {
|
createTables(tenantId.value)
|
||||||
createTablesResult.value = resp;
|
.then((resp) => {
|
||||||
if (resp.result === 200) {
|
createTablesResult.value = resp;
|
||||||
|
if (resp.result === 200) {
|
||||||
|
currentStepStatus.value = 2;
|
||||||
|
} else {
|
||||||
|
currentStepStatus.value = 0;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
currentStepStatus.value = 2;
|
currentStepStatus.value = 2;
|
||||||
} else {
|
activeStep.value = 2;
|
||||||
currentStepStatus.value = 0;
|
})
|
||||||
}
|
.finally(() => {
|
||||||
}).catch(() => {
|
loading.value = false;
|
||||||
currentStepStatus.value = 2;
|
});
|
||||||
activeStep.value = 2;
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 第三步,导入数据
|
// 第三步,导入数据
|
||||||
const importData = () => {
|
const importData = () => {
|
||||||
|
loading.value = true;
|
||||||
activeStep.value = 3;
|
activeStep.value = 3;
|
||||||
currentStepStatus.value = 1;
|
currentStepStatus.value = 1;
|
||||||
initData(tenantId.value).then((resp) => {
|
initData(tenantId.value)
|
||||||
initDataResult.value = resp;
|
.then((resp) => {
|
||||||
if (resp.result === 200) {
|
initDataResult.value = resp;
|
||||||
|
if (resp.result === 200) {
|
||||||
|
currentStepStatus.value = 2;
|
||||||
|
} else {
|
||||||
|
currentStepStatus.value = 0;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
currentStepStatus.value = 2;
|
currentStepStatus.value = 2;
|
||||||
} else {
|
activeStep.value = 2;
|
||||||
currentStepStatus.value = 0;
|
})
|
||||||
}
|
.finally(() => {
|
||||||
}).catch(() => {
|
loading.value = false;
|
||||||
currentStepStatus.value = 2;
|
});
|
||||||
activeStep.value = 2;
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
let progressLineWidth =
|
let progressLineWidth =
|
||||||
((activeStep.value - 1) * 100 +
|
(activeStep.value - 1) * 100 +
|
||||||
Math.abs(currentStepStatus.value - 1) * 30 +
|
Math.abs(currentStepStatus.value - 1) * 30 +
|
||||||
70)
|
70;
|
||||||
progressLineWidth = progressLineWidth < 100 ? 0 : progressLineWidth
|
progressLineWidth = progressLineWidth < 100 ? 0 : progressLineWidth;
|
||||||
let progressLineColor;
|
let progressLineColor;
|
||||||
if (currentStepStatus.value === 0) {
|
if (currentStepStatus.value === 0) {
|
||||||
const percent = ((activeStep.value - 1) / activeStep.value) * 100;
|
const percent = ((activeStep.value - 1) / activeStep.value) * 100;
|
||||||
@ -276,11 +320,11 @@ onMounted(() => {
|
|||||||
progressLineColor = `linear-gradient(to right, #67c23a ${100}%, #f56c6c ${100}%)`;
|
progressLineColor = `linear-gradient(to right, #67c23a ${100}%, #f56c6c ${100}%)`;
|
||||||
}
|
}
|
||||||
document
|
document
|
||||||
.querySelector(".app-container")
|
.querySelector(".app-container")
|
||||||
.style.setProperty("--progress-line-color", progressLineColor);
|
.style.setProperty("--progress-line-color", progressLineColor);
|
||||||
document
|
document
|
||||||
.querySelector(".app-container")
|
.querySelector(".app-container")
|
||||||
.style.setProperty("--progress-line-width", progressLineWidth + "%");
|
.style.setProperty("--progress-line-width", progressLineWidth + "%");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@ -337,6 +381,22 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn-loading {
|
||||||
|
margin-top: 80px;
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #409eff;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.el-icon {
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.circle-button {
|
.circle-button {
|
||||||
width: 100px;
|
width: 100px;
|
||||||
height: 100px;
|
height: 100px;
|
||||||
@ -438,4 +498,13 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
from {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
Reference in New Issue
Block a user