<template>
<div class="storage-content">
<div class="storage-num">
<div id="raid-card" class="raid-num">
<span>{{ $t('OTHER_RAID_CARD') }}</span>
<span>{{ raidNum }}</span>
</div>
<div id="volume-card" class="raid-num">
<span>{{ $t('HOME_LOGICAL_DISK') }}</span>
<span>{{ volumeNum }}</span>
</div>
<div id="driver-card" class="raid-num">
<span>{{ $t('HOME_PHYSICAL_DISK') }}</span>
<span>{{ driverNum }}</span>
</div>
</div>
<div class="storage-text">
<div class="storage-tree">
<el-tree
ref="treeElementNode"
v-reqKeepAlive.tree
:data="storageData.data.foliages"
:props="treeProps"
:accordion="false"
:highlight-current="true"
:show-checkbox="false"
:expand-on-click-node="false"
:node-key="treeProps.labelId"
:current-node-key="currentNode"
:default-expanded-keys="expanded.data"
@node-click="handleNodeClick"
@node-expand="handleNodeClick"
>
<template #default="{ node, data }">
<el-checkbox
v-if="data.componentName === 'VolumeNode' && showCheckbox"
@change="nodeCheck($event).option(data)"
></el-checkbox>
<span :id="data.id" class="custom-tree-node spanLabel" :class="data.className">
<span class="tree-node-row" :class="{ raidTitle: data.getRaidButton }">
<span
:class="{
activeWord: data.active,
notActiceWord: !data.active,
raidLabel: data.getRaidButton
}"
>
<LoadingIcon
v-if="['CreatingNode', 'DeletingNode'].includes(data.componentName)"
class="loading-icon"
></LoadingIcon>
<span>{{ node.label }}</span>
</span>
<span>
<span
v-if="data.getRaidButton && data.getRaidButton.isShow && isPrivileges"
class="raidBtn"
>
<template v-for="btn in data.getRaidButton.button" :key="btn.id">
<el-button
v-if="btn.isShow"
:id="btn.id"
class="volumn-create-btn"
:disabled="btn.disable || systemLocked || onTask || delDisable"
:class="{ disabledBtn: btn.disable }"
type="text"
@click="raidButtonClick(btn.id, data, $event)"
>
{{ $t(btn.label) }}
</el-button>
</template>
</span>
<span class="iconClass">
<a
v-if="isPrivileges && !systemLocked && !showCheckbox && !delDisable"
class="delete_img"
@click.stop="deleteOneVolumn(data, $event)"
>
<img src="/src/assets/common/image/license-delete.png" />
</a>
</span>
<span
v-if="data.getCryptoErase && isPrivileges && !systemLocked"
class="iconClass"
>
<div class="crypto_img" @click="entryDrive(data, $event)"></div>
</span>
</span>
</span>
</span>
</template>
</el-tree>
<div v-if="drawerBox" v-reqKeepAlive.drawer>
<el-drawer
ref="drawerRef"
v-model="drawerBox"
direction="rtl"
size="552px"
:show-close="false"
class="drawerBox"
:close-on-click-modal="false"
>
<template #title>{{ $t('STORE_ADD_VOLUME') }}</template>
<div class="drawer-box">
<DrawerComponent
:data="storageData"
:visibility="drawerBox"
@handle-close="handleClose"
/>
</div>
</el-drawer>
</div>
</div>
<div class="storage-info">
<div class="top-message">
<CustomAert :title="$t('STORE_TIP', { smsName: smsName })" :content="''" />
</div>
<div>
<component
:is="component"
v-if="componentRef"
ref="raidRef"
:data="showData"
:node="clickNode.data"
:collect-list="collectList"
:on-task="onTask"
@change-collect-list="changeCollectList"
></component>
</div>
</div>
</div>
<AdvancedSettings />
</div>
<Dialog ref="dialog" :config="diaginfo" @close="dialogCloseFun"></Dialog>
</template>
<script setup lang="ts">
import useStore from '@/stores';
import useStorageLogCollectStore from '@/stores/storage-log-collect';
import {
ref,
reactive,
onMounted,
computed,
shallowRef,
provide,
watch,
nextTick,
onUnmounted
} from 'vue';
import { storeToRefs } from 'pinia';
import { ElNotification as elNotification, ElLoading } from 'element-plus';
import {
factory,
entryDriveFun,
deleteBatchVolumnFun,
queryTaskProgress,
deleteVolumn
} from './storage.service';
import { TreeNode } from './models/storage-interface';
import {
StorageTree,
Raid,
RaidNode,
RaidEditModel,
VolumeNode,
VolumeStaticModel
} from './models';
import VolumesComponent from './components/volumes/Volumes.vue';
import SpanComponent from './components/span/Span.vue';
import RaidComponent from './components/raid/Raid.vue';
import DriversComponent from './components/drivers/Driver.vue';
import DrawerComponent from './components/volume-create/Drawer.vue';
import CustomAert from '@/components/CustAlert/CustAlert.vue';
import LoadingIcon from '@/components/LoadingIcon/LoadingIcon.vue';
import UploadingIcon from '@/components/UploadingIcon/UploadingIcon.vue';
import { loading, showFailedMessage, showSuccessMessage } from '@/utils/composition';
import { checkPrivil } from '@/apps/app-bmc/router/routes';
import { getErrorTranslate, traduction } from '@/utils/language';
import Dialog from '@/components/Dialog/Dialog.vue';
import { VolumeCreateService } from './components/volume-create/volume-creat.servics';
import { deepClone, showElMessage } from '@/utils/utils';
import { UserPrivil } from '@/model/base-enum';
import $http from '@/utils/http-service';
import AdvancedSettings from './components/advanced-settings/AdvancedSettings.vue';
const store = useStore();
const storageLogCollectStore = useStorageLogCollectStore();
let systemLocked = computed(() => {
return store.state.glob.systemLocked;
});
const selectedNode = ref(null);
const isPrivileges = checkPrivil(UserPrivil.baseConfig);
const storageTree = StorageTree.getInstance();
const componentRef = ref(false);
const treeProps = {
children: 'children',
label: 'label',
labelId: 'labelId'
};
const listPoin = reactive({ data: [] });
const treeElementNode = ref();
let checkedTreeList = {};
const showCheckbox = ref(false);
const expanded: any = reactive({ data: [] });
const currentNode = ref();
const smsName = useStore().state.loct.smsName;
const storageData: any = reactive({
data: { foliages: [], multipleChange: (node?: TreeNode) => {} }
});
const showData = reactive({ data: [] });
const clickNode = reactive({ data: [] });
const drawerBox = ref(false);
const dataDone = ref(false);
const delDialog = ref(false);
let dialogVisible = ref(false);
const raidRef = ref();
const dynamicComponent = {
VolumeNode: VolumesComponent,
SpanNode: SpanComponent,
RaidNode: RaidComponent,
DriverNode: DriversComponent
};
let component = shallowRef();
let componentName = ref('');
const { collectList } = storeToRefs(storageLogCollectStore);
const isAnyCollecting = computed(() => storageLogCollectStore.isAnyCollecting);
const trees = reactive({});
const dialog = ref();
type Config = {
id?: string;
type?: string;
title: string;
content: any;
param?: any;
};
const diaginfo = reactive({
id: 'driverCreatMessage',
type: 'warning',
title: traduction('COMMON_CONFIRM'),
content: traduction('COMMON_ASK_OK')
});
let deleteParam: any;
let entryParam: any;
let creatParam: any;
const volumeCreateService = new VolumeCreateService();
const driverNum = ref(0);
const volumeNum = ref(0);
const raidNum = ref(0);
const batchDeleteData = ref([]);
const batchDeleteSelectedData = ref([]);
const delDisable = ref(false);
const callFactoryFunNumber = ref(0);
const factoryFunPolling = ref<ReturnType<typeof setTimeout> | null>(null);
provide('clickNode', clickNode);
provide('treeRef', treeElementNode);
watch(
() => storageLogCollectStore.collectList,
() => {
const anyCollecting = storageLogCollectStore.isAnyCollecting;
const list = storageData.data.foliages;
list?.forEach((item: any) => {
item.raidButton?.button?.forEach((ele: any) => {
ele.disable = anyCollecting;
});
});
},
{ deep: true }
);
function doesVolumeLabelIdExistInData(data: any[], labelId: string): boolean {
for (const item of data) {
if (item?.componentName === 'VolumeNode' && item?.labelId === labelId) {
return true;
}
if (item?.children && doesVolumeLabelIdExistInData(item?.children, labelId)) {
return true;
}
}
return false;
}
function checkIdsInList(data: any[], list: any[]): boolean {
for (const item of list) {
const volumeLabelId = item?.labelId;
if (
item?.componentName === 'VolumeNode' &&
volumeLabelId &&
doesVolumeLabelIdExistInData(data, volumeLabelId)
) {
return false;
}
if (item?.children && !checkIdsInList(data, item?.children)) {
return false;
}
}
return true;
}
function factoryFun() {
loading(true);
factory()
.then((storageTreeData: any) => {
if (storageTreeData) {
storageData.data = storageTreeData;
driverNum.value = getNum(storageTreeData.foliages, 'DriverNode');
volumeNum.value = getNum(storageTreeData.foliages, 'VolumeNode');
raidNum.value = getNum(storageTreeData.foliages, 'RaidNode');
showData.data = storageTreeData.getFoliages;
componentRef.value = true;
displayComponent(showData.data[0]);
clickNode.data = showData.data[0];
listPoin.data = storageTreeData.getFoliages.map((item: any) => {
return item.id;
});
}
if (batchDeleteData.value.length && callFactoryFunNumber.value < 30) {
if (!checkIdsInList(storageTreeData.foliages, batchDeleteData.value)) {
showCheckbox.value = false;
checkedTreeList = {};
delDisable.value = true;
let raidNode = null;
for (let i = 0; i < selectedNode.value.length; i++) {
const node = selectedNode.value[i];
const volumeNode = node;
raidNode = StorageTree.getInstance().getParentRaid(volumeNode.getRaidIndex);
}
storageData.data.foliages?.forEach((r: any) => {
if (r.id === raidNode.id) {
r.children.unshift({
label: traduction('COMMON_DELETING'),
componentName: 'DeletingNode',
disabled: true
});
}
});
factoryFunPolling.value = setTimeout(() => {
callFactoryFunNumber.value++;
factoryFun();
}, 2000);
loading(false);
return;
} else {
if (batchDeleteSelectedData.value.length === batchDeleteData.value.length) {
showSuccessMessage();
}
if (factoryFunPolling.value) {
clearTimeout(factoryFunPolling.value);
}
factoryFunPolling.value = null;
showCheckbox.value = false;
checkedTreeList = {};
elNotification.closeAll();
batchDeleteData.value = [];
callFactoryFunNumber.value = 0;
}
} else {
clearTimeout(factoryFunPolling.value);
factoryFunPolling.value = null;
showCheckbox.value = false;
checkedTreeList = {};
elNotification.closeAll();
batchDeleteData.value = [];
callFactoryFunNumber.value = 0;
}
dataDone.value = true;
delDisable.value = false;
loading(false);
let createTask = JSON.parse(localStorage.getItem('createTask') || '{}');
if (localStorage.getItem('createTask') && !onTask.value && createTask?.url) {
storageData.data.foliages?.forEach((r: any) => {
if (r.id === createTask?.raidId) {
r.children.unshift({
label: traduction('COMMON_CREATING'),
componentName: 'CreatingNode',
disabled: true
});
}
});
taskRunning(createTask?.url);
} else {
localStorage.removeItem('createTask');
}
})
.catch((error: any) => {
loading(false);
if (batchDeleteData.value.length) {
delDisable.value = true;
if (callFactoryFunNumber.value < 30) {
if (factoryFunPolling.value) {
clearTimeout(factoryFunPolling.value);
}
factoryFunPolling.value = setTimeout(() => {
callFactoryFunNumber.value++;
factoryFun();
}, 2000);
return;
}
elNotification.closeAll();
batchDeleteData.value = [];
batchDeleteSelectedData.value = [];
callFactoryFunNumber.value = 0;
delDisable.value = false;
showFailedMessage();
return;
}
delDisable.value = false;
})
.finally(() => {
currentNode.value = storageData?.data?.foliages?.length
? storageData?.data?.foliages[0][treeProps.labelId]
: '';
});
}
function changeCollectList(id: string, value: boolean) {
storageLogCollectStore.setCollectStatus(id, value);
const anyCollecting = storageLogCollectStore.isAnyCollecting;
const list = storageData.data.foliages;
list?.forEach((item: any) => {
item.raidButton?.button?.forEach((ele: any) => {
ele.disable = anyCollecting;
});
});
}
onMounted(() => {
storageLogCollectStore.setupCrossTabSync();
factoryFun();
const anyCollecting = storageLogCollectStore.isAnyCollecting;
const list = storageTree.getFoliages;
list?.forEach((item: any) => {
item.raidButton?.button?.forEach((ele: any) => {
ele.disable = anyCollecting;
});
});
});
function getNum(data: any, type: string, diskSet: any = new Set()): any {
for (let item of data) {
if (item?.children?.length > 0) {
getNum(item.children, type, diskSet);
}
if (item.componentName === type) {
diskSet.add(item.labelId);
}
}
return diskSet.size;
}
function handleNodeClick(data: any, properties?: any) {
treeElementNode.value.setCurrentKey(data.labelId);
componentRef.value = false;
clickNode.data = data;
provide('clickNode', clickNode);
displayComponent(data);
$http.keepAlive('Activate');
}
function raidButtonClick(id: string, node: RaidNode, event?: any): void {
event.stopPropagation();
switch (id) {
case 'add':
addVolume(node);
break;
case 'edit':
editRaid(node);
break;
case 'delete':
deleteBatchVolumn();
break;
case 'cancel':
cancelEdit(node);
break;
default:
break;
}
}
function addVolume(node: any) {
handleNodeClick(node);
drawerBox.value = true;
}
function editRaid(node: RaidNode) {
storageData.data.multipleChange(node);
node.expanded = true;
node.setAddButShow = false;
node.setEditButShow = false;
node.setCancleButShow = true;
node.setDelButShow = true;
showCheckbox.value = true;
expanded.data = [node.labelId];
}
function deleteBatchVolumn() {
diaginfo.id = 'deleteBatchVolumnMessage';
diaginfo.title = traduction('COMMON_CONFIRM');
diaginfo.content = traduction('STORE_CONFIRM_CONTINUE');
dialog.value.show();
}
function cancelEdit(node: RaidNode) {
node.setMultiple = false;
node.setAddButShow = true;
node.setEditButShow = true;
node.setCancleButShow = false;
node.setDelButShow = false;
showCheckbox.value = false;
checkedTreeList = {};
}
function displayComponent(node: TreeNode, isEdit?: any) {
StorageTree.getInstance().checkedNode(node.labelId, node.getRaidIndex);
componentName.value = node.componentName;
component.value = dynamicComponent[componentName.value];
componentRef.value = true;
if (!isEdit) {
nextTick(() => {
if (Object.prototype.hasOwnProperty.call(raidRef.value, 'checkNode')) {
raidRef.value.checkNode(node);
} else {
raidRef.value?.factoryFun(node);
}
});
}
if (node.label && VolumeStaticModel.logicDriverList[node.label]) {
VolumeStaticModel.logicDriverList.maxCacheCount =
VolumeStaticModel.logicDriverList[node.label].maxCacheCount;
}
}
function deleteOneVolumn(node: VolumeNode, event?: any) {
diaginfo.id = 'deleteOneVolumeMessage';
diaginfo.title = traduction('COMMON_CONFIRM');
diaginfo.content = traduction('STORE_CONFIRM_CONTINUE');
deleteParam = node;
dialog.value.show();
}
function entryDrive(node: VolumeNode, event?: any) {
diaginfo.id = 'entryDriveMessage';
diaginfo.title = traduction('COMMON_CONFIRM');
diaginfo.content = traduction('STORE_CONFIRM_CONTINUE');
entryParam = node;
dialog.value.show();
}
function handleClose(val: boolean, param?: any) {
creatParam = param;
diaginfo.id = val ? 'creatOK' : 'creatCancel';
diaginfo.title = traduction('COMMON_CONFIRM');
diaginfo.content = traduction('COMMON_ASK_OK');
dialog.value.show();
}
function dialogCloseFun(val: boolean) {
if (val) {
if (diaginfo.id === 'driverCreatMessage') {
driverCreat();
drawerBox.value = false;
} else if (diaginfo.id === 'entryDriveMessage') {
entryDriveFun(entryParam)
.then(() => {
showSuccessMessage();
})
.catch(() => {
showFailedMessage();
});
} else if (diaginfo.id === 'deleteOneVolumeMessage') {
const selectedData = [deleteParam];
deleteVolumnFun(selectedData);
} else if (diaginfo.id === 'creatOK') {
driverCreat();
drawerBox.value = false;
} else if (diaginfo.id === 'deleteBatchVolumnMessage') {
const list = Object.values(checkedTreeList);
if (list.length > 0) {
deleteVolumnFun(list);
}
} else {
dialog.value.hide();
drawerBox.value = false;
}
} else {
if (diaginfo.id === 'creatCancel') {
dialog.value.hide();
return;
}
}
dialog.value.hide();
drawerBox.value = false;
}
function driverCreat() {
loading(true);
volumeCreateService
.createVolumn()
.then(res => {
loading(false);
const raidNode = StorageTree.getInstance().getCheckedNode as RaidNode;
if (res.data.url) {
storageData.data.foliages?.forEach((r: any) => {
if (r.id === raidNode.id) {
r.children.unshift({
label: traduction('COMMON_CREATING'),
componentName: 'CreatingNode',
disabled: true
});
}
});
localStorage.setItem(
'createTask',
JSON.stringify({
url: res.data.url,
raidId: raidNode.id
})
);
}
queryTask(res.data.url);
})
.catch(error => {
loading(false);
const errorCode = error.data.error[0].code;
showFailedMessage(getErrorTranslate(errorCode));
});
}
function nodeCheck(val: boolean) {
const option = (data: any) => {
if (val) {
if (!checkedTreeList[data.labelId]) {
checkedTreeList[data.labelId] = data;
}
} else {
delete checkedTreeList[data.labelId];
}
};
return { option };
}
function handleDeleteVolumn(nodeId, raidId) {
return new Promise((resolve, reject) => {
deleteVolumn(nodeId, raidId)
.then(res => {
resolve(res);
})
.catch(err => {
reject(err);
});
});
}
async function handleBatchDeleteVolumn(selectedData) {
batchDeleteSelectedData.value = selectedData;
const delErrorData = [];
for (let i = 0; i < selectedData.length; i++) {
const node = selectedData[i];
const volumeNode = node;
const raid = StorageTree.getInstance().getParentRaid(volumeNode.getRaidIndex);
try {
const result = await handleDeleteVolumn(node.id, raid.id);
batchDeleteData.value.push(selectedData[i]);
} catch (error) {
delErrorData.push(selectedData[i]);
}
}
if (delErrorData.length) {
showFailedMessage();
loading(false);
elNotification.closeAll();
}
factoryFun();
}
function deleteVolumnFun(selectedData: any) {
elNotification({
title: traduction('COMMON_DELETING'),
message: traduction('STORE_DELETE_VOLUME_TIPS'),
icon: UploadingIcon as any,
position: 'bottom-right',
duration: 0,
offset: 10
});
const raidNode = StorageTree.getInstance().getCheckedNode as RaidNode;
selectedNode.value = selectedData;
storageData.data.foliages?.forEach((r: any) => {
if (r.id === raidNode.id) {
r.children.unshift({
label: traduction('COMMON_DELETING'),
componentName: 'DeletingNode',
disabled: true
});
}
});
loading(true);
handleBatchDeleteVolumn(selectedData);
}
const progress = ref(0);
const onTask = ref(false);
let taskStateErrorAmount = 0;
let taskInterval: any = null;
function queryTask(url: string) {
queryTaskProgress(url)
.then(res => {
const taskData = res.data;
getInitProgress(url, taskData);
})
.catch(err => {
executeUpgradeFailed(err?.data.error[0].code);
});
}
function getInitProgress(url: string, taskData: any): void {
if (taskData.state === 'Completed' && taskData.prepare_progress === 100) {
taskCompleted(taskData);
localStorage.removeItem('createTask');
} else if (
taskData.state === 'Running' ||
taskData.state === 'Starting' ||
taskData.state === 'New'
) {
taskRunning(url);
} else if (taskData.state === 'Exception') {
exceptionHandler(taskData);
localStorage.removeItem('createTask');
} else {
taskOtherCases(taskData, url);
}
}
* 重新触发进度查询
* 查询间隔设置为2秒
*/
function reQueryProgress(url: string) {
taskInterval = window.setTimeout(() => {
queryTask(url);
}, 2000);
}
function clearQueryProgress() {
if (taskInterval) {
clearTimeout(taskInterval);
taskInterval = null;
}
}
function executeUpgradeFailed(messageId: string) {
onTask.value = false;
let msg = traduction('COMMON_FAILED');
if (messageId) {
msg = getErrorTranslate(messageId);
}
factoryFun();
displayUpgradeState('failed', msg);
localStorage.removeItem('createTask');
}
function displayUpgradeState(state: 'success' | 'failed' | 'clear', msg: string) {
elNotification.closeAll();
if (state === 'failed') {
showElMessage('error', msg);
} else if (state === 'success') {
showSuccessMessage();
} else {
}
}
function taskOtherCases(taskData: any, url: string) {
taskStateErrorAmount++;
if (taskStateErrorAmount > 10) {
executeUpgradeFailed(taskData.message_id);
return;
}
reQueryProgress(url);
}
function taskCompleted(taskData: any) {
clearQueryProgress();
progress.value = 100;
showSuccessMessage();
localStorage.removeItem('createTask');
factoryFun();
onTask.value = false;
elNotification.closeAll();
}
function taskRunning(url: string) {
if (!onTask.value) {
elNotification({
title: traduction('COMMON_CREATING'),
message: traduction('STORE_ADD_VOLUME_TIPS'),
icon: UploadingIcon as any,
position: 'bottom-right',
duration: 0,
offset: 10
});
}
onTask.value = true;
reQueryProgress(url);
}
function exceptionHandler(taskData: any) {
onTask.value = false;
executeUpgradeFailed(taskData.message_id);
}
onUnmounted(() => {
clearQueryProgress();
clearTimeout(factoryFunPolling.value);
factoryFunPolling.value = null;
elNotification.closeAll();
});
</script>
<style lang="scss" scoped>
#storageTitle {
height: auto;
font-size: 18px;
color: var(--o-text-color-primary);
line-height: 24px;
margin-top: 16px;
font-weight: bolder;
}
.storage-content {
position: relative;
height: 100%;
padding-bottom: 24px;
flex-direction: row !important;
.storage-text {
display: flex;
justify-content: space-between;
height: calc(100% - 72px);
flex-direction: row !important;
}
.storage-num {
display: flex;
justify-content: space-between;
padding-right: 24px;
.raid-num {
display: flex;
justify-content: space-between;
align-items: center;
height: 56px;
width: 33%;
font-size: 14px;
color: #000000;
font-weight: regular;
line-height: 22px;
border-radius: 4px;
background: #ffffff;
border: 1px soild #e6ebf5;
padding: 17px 24px;
margin-bottom: 16px;
}
}
}
.storage-tree {
background-color: var(--o-bg-color-base);
width: 332px;
border-radius: 4px;
margin-right: 8px;
height: 100%;
padding: 16px 8px;
max-height: calc(100vh - 159px);
overflow: scroll;
.drivers-num {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
margin-left: 8px;
.left-text {
font-size: 16px;
color: #000000;
font-weight: medium;
}
.label-content {
background: var(--o-border-color-light);
border-radius: 8px;
width: 28px;
height: 16px;
display: inline-block;
text-align: center;
margin: 0 0px 0 8px;
font-size: 12px;
font-weight: 900;
}
.label {
font-size: 12px;
color: $APP-COLOR-TEXT;
letter-spacing: 0;
line-height: 16px;
}
}
.spanLabel {
flex-shrink: 1;
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
}
.custom-tree-node {
padding-right: 16px;
position: relative;
}
:deep(
.el-tree-node.is-current > .el-tree-node__content .el-tree-node__expand-icon:not(.is-leaf)
) {
color: #fff;
}
.raidTitle {
width: 100%;
display: flex;
justify-content: space-between;
}
.el-button--text {
border: none;
min-width: auto;
color: var(--o-color-primary);
}
:deep(.el-tree--highlight-current .el-tree-node.is-current > .el-tree-node__content) {
background-color: var(--o-color-primary);
border-radius: 4px;
color: #fff;
.el-button--text > span {
color: #fff;
}
}
.delete_img {
height: 16px;
width: 16px;
cursor: pointer;
display: none;
overflow: hidden;
img {
margin-right: 8px;
position: relative;
}
}
.el-tree-node__label .el-checkbox {
margin-right: 8px;
}
.el-tree-node__content:hover {
.volumeClass .delete_img {
display: block;
img {
left: -20px;
filter: drop-shadow(20px 0 0 var(--o-color-danger));
}
}
}
}
.storage-info {
flex: 1;
padding-right: 24px;
}
:deep(.el-drawer__header) {
font-size: 16px;
font-weight: bold;
}
:deep(.el-drawer.rtl) {
height: calc(100% - 48px);
top: auto;
}
.volumn-create-btn {
font-weight: bolder;
}
.drawer-box {
height: 100%;
}
:deep(.el-drawer__body) {
padding-bottom: 8px;
padding-top: 16px;
padding-left: 24px;
}
:deep(.el-drawer__header) {
color: var(--o-text-color-primary);
margin-bottom: 0;
}
:deep(.el-button) {
&:not(.el-dropdown__caret-button) > span {
min-width: 0;
}
}
.storage-content {
:deep(.el-overlay) {
.el-drawer__body {
padding: 0;
}
}
}
:deep(.top-message) {
.bc-title {
font-weight: 100;
}
}
:deep(.storage-tree) {
.spanLabel {
&:hover {
color: var(--o-button-color_hover);
}
}
}
.tree-node-row {
display: flex;
flex: 1;
justify-content: space-between;
}
.loading-icon {
margin-right: 8px;
position: absolute;
width: 16px;
height: 16px;
top: 0;
left: -20px;
}
:deep(.ssdThreshold .el-dialog__body) {
height: 136px !important;
padding: 24px 24px 16px 24px;
flex-grow: 0 !important;
overflow: hidden !important;
}
:deep(.ssdThreshold .el-button) {
height: 24px !important;
}
</style>