Files
CID/src/views/home/widgets/index.vue

651 lines
17 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!--
基于 SCUI 重构适配 vite 加载和适配相关业务页面https://gitee.com/lolicode/scui/tree/master/src/views/home
-->
<template>
<div ref="main" :class="['widgets-home', customizing ? 'customizing' : '']">
<div class="widgets-content">
<!-- <TopNav /> -->
<TopNavSpdm />
<div class="widgets-top">
<div class="flex justify-end custom_btn">
<el-button v-if="customizing" type="primary" round @click="save">{{ $t('widgets.index.0910663-0') }}</el-button>
<el-button v-else type="primary" round @click="custom">{{ $t('Crontab.index.7634731-6') }}</el-button>
</div>
</div>
<div ref="widgets" class="widgets">
<div class="widgets-wrapper">
<div v-if="nowComponentList.length <= 0" class="no-widgets">
<el-empty :description="$t('widgets.index.0910663-2')" :image-size="280"></el-empty>
</div>
<!-- 默认布局 SPDM CODE -->
<el-row v-if="isDefaultLayout" :gutter="16">
<!-- 第一列 -->
<el-col :md="8" :xs="24">
<draggable
v-model="grid.copmsList[0]"
animation="200"
handle=".customize-overlay"
group="people"
item-key="com"
dragClass="aaaaa"
force-fallback
fallbackOnBody
class="draggable-box"
>
<template #item="{ element }">
<div class="widgets-item" :class="{ 'inline-flex': allComps[element]?.isInline }">
<component :is="allComps[element]"></component>
<div v-if="customizing" class="customize-overlay">
<el-button class="close" type="danger" plain icon="Close" size="small" @click="remove(element)"></el-button>
<label>
<el-icon>
<component :is="allComps[element].icon" />
</el-icon>
{{ $t(allComps[element].title) }}
</label>
</div>
</div>
</template>
</draggable>
</el-col>
<!-- 第二列 要分两块-->
<el-col :md="16" :xs="24">
<!-- 第一行 分成左右两块 -->
<el-row :gutter="16">
<el-col :md="16" :xs="24">
<draggable
v-model="grid.copmsList[1]"
animation="200"
handle=".customize-overlay"
group="people"
item-key="com"
dragClass="aaaaa"
force-fallback
fallbackOnBody
class="draggable-box"
>
<template #item="{ element }">
<div class="widgets-item" :class="{ 'inline-flex': allComps[element]?.isInline }">
<component :is="allComps[element]"></component>
<div v-if="customizing" class="customize-overlay">
<el-button class="close" type="danger" plain icon="Close" size="small" @click="remove(element)"></el-button>
<label>
<el-icon>
<component :is="allComps[element].icon" />
</el-icon>
{{ $t(allComps[element].title) }}
</label>
</div>
</div>
</template>
</draggable>
</el-col>
<el-col :md="8" :xs="24">
<draggable
v-model="grid.copmsList[2]"
animation="200"
handle=".customize-overlay"
group="people"
item-key="com"
dragClass="aaaaa"
force-fallback
fallbackOnBody
class="draggable-box"
>
<template #item="{ element }">
<div class="widgets-item" :class="{ 'inline-flex': allComps[element]?.isInline }">
<component :is="allComps[element]"></component>
<div v-if="customizing" class="customize-overlay">
<el-button class="close" type="danger" plain icon="Close" size="small" @click="remove(element)"></el-button>
<label>
<el-icon>
<component :is="allComps[element].icon" />
</el-icon>
{{ $t(allComps[element].title) }}
</label>
</div>
</div>
</template>
</draggable>
</el-col>
</el-row>
<!-- 第二行 -->
<el-row :gutter="16">
<el-col :md="24" :xs="24">
<draggable
v-model="grid.copmsList[3]"
animation="200"
handle=".customize-overlay"
group="people"
item-key="com"
dragClass="aaaaa"
force-fallback
fallbackOnBody
class="draggable-box"
>
<template #item="{ element }">
<div class="widgets-item" :class="{ 'inline-flex': allComps[element]?.isInline }">
<component :is="allComps[element]"></component>
<div v-if="customizing" class="customize-overlay">
<el-button class="close" type="danger" plain icon="Close" size="small" @click="remove(element)"></el-button>
<label>
<el-icon>
<component :is="allComps[element].icon" />
</el-icon>
{{ $t(allComps[element].title) }}
</label>
</div>
</div>
</template>
</draggable>
</el-col>
</el-row>
</el-col>
</el-row>
<!-- 自定义布局 -->
<el-row v-else :gutter="16">
<el-col v-for="(item, index) in grid.layout" v-bind:key="index" :gutter="16" :md="item" :xs="24">
<draggable
v-model="grid.copmsList[index]"
animation="200"
handle=".customize-overlay"
group="people"
item-key="com"
dragClass="aaaaa"
force-fallback
fallbackOnBody
class="draggable-box"
>
<template #item="{ element }">
<div class="widgets-item" :class="{ 'inline-flex': allComps[element]?.isInline }">
<component :is="allComps[element]"></component>
<div v-if="customizing" class="customize-overlay">
<el-button class="close" type="danger" plain icon="Close" size="small" @click="remove(element)"></el-button>
<label>
<el-icon>
<component :is="allComps[element].icon" />
</el-icon>
{{ $t(allComps[element].title) }}</label
>
</div>
</div>
</template>
</draggable>
</el-col>
</el-row>
</div>
</div>
</div>
<div v-if="customizing" class="widgets-aside">
<el-container>
<el-header>
<div class="widgets-aside-title">{{ $t('widgets.index.0910663-3') }}</div>
<div class="widgets-aside-close" @click="close()">
<el-icon><Close /></el-icon>
</div>
</el-header>
<el-header style="height: auto">
<div class="selectLayout">
<div class="selectLayout-item item01" :class="{ active: grid.layout.join(',') === '12,6,6' }" @click="setLayout([12, 6, 6])">
<el-row :gutter="2">
<el-col :span="7"><span></span></el-col>
<el-col :span="7"><span></span></el-col>
<el-col :span="10"><span></span></el-col>
</el-row>
</div>
<div class="selectLayout-item item02" :class="{ active: grid.layout.join(',') === '24,16,8' }" @click="setLayout([24, 16, 8])">
<el-row :gutter="2">
<el-col :span="24"><span></span></el-col>
<el-col :span="16"><span></span></el-col>
<el-col :span="8"><span></span></el-col>
</el-row>
</div>
<!-- <div class="selectLayout-item item03" :class="{ active: grid.layout.join(',') === '24' }" @click="setLayout([24])">
<el-row :gutter="2">
<el-col :span="24"><span></span></el-col>
<el-col :span="24"><span></span></el-col>
<el-col :span="24"><span></span></el-col>
</el-row>
</div> -->
</div>
</el-header>
<el-main class="nopadding">
<div class="widgets-list">
<div v-if="myCompsList.length <= 0" class="widgets-list-nodata">
<el-empty :description="$t('widgets.index.0910663-2')" :image-size="60"></el-empty>
</div>
<div v-for="item in myCompsList" :key="item.title" class="widgets-list-item">
<div class="item-logo">
<el-icon>
<component :is="item.icon" />
</el-icon>
</div>
<div class="item-info">
<h2>{{ $t(item.title) }}</h2>
<p>{{ $t(item.description) }}</p>
</div>
<div class="item-actions">
<el-button type="primary" icon="el-icon-plus" size="small" @click="push(item)"></el-button>
</div>
</div>
</div>
</el-main>
<el-footer style="height: 51px">
<el-button size="small" @click="backDefault()">{{ $t('widgets.index.0910663-4') }}</el-button>
</el-footer>
</el-container>
</div>
</div>
</template>
<script lang="ts" name="widgets" setup>
const TopNav = defineAsyncComponent(() => import('./components/TopNav.vue'));
// SPDM CODE
const TopNavSpdm = defineAsyncComponent(() => import('../../../spdm/views/home/components/TopNavSpdm.vue'));
import draggable from 'vuedraggable';
import allComps from './components/index';
import { Local } from '/@/utils/storage';
import { useUserInfo } from '/@/stores/userInfo';
import { cloneDeep, flatten } from 'lodash';
// todo 默认布局设置
const defaultGrid = ref({
// layout: [8, 16],
layout: [8, 8, 8],
// SPDM CODE
// copmsList: [['ToDoCalendar', 'favorite-menu'], ['TaskMessage'], ['TaskList']],
copmsList: [
['ToDoCalendar'], // 第一列占8
['TaskMessage'], // 第二列占8
['favorite-menu'], // 第三列占8
['TaskList'], // 第四列占8
],
});
const customizing = ref(false);
const widgets = ref();
const widgetsKey = ref('widgets');
const grid = ref(cloneDeep(toValue(defaultGrid)));
// 默认布局是4组
// const isDefaultLayout = ref(true);
const isDefaultLayout = ref(grid.value.copmsList.length === 4);
const allComponentList = computed(() => {
const list = [];
for (const [key, { title, icon, description, isInline }] of Object.entries(allComps)) {
list.push({ key, title, icon, description, isInline });
}
const myComponentList = grid.value.copmsList.flat();
list.forEach((comp) => {
const existingItem = myComponentList.find((item) => item === comp.key);
comp.disabled = !!existingItem;
});
return list;
});
const myCompsList = computed(() => {
// 支持列表 SPDM CODE
const myGrid = [
'docking-tianyu',
'docking-zw-3d',
'docking-intesim-cae',
'calendar',
'current-user',
'news',
'audit-log',
'sys-log',
'flow-data',
'favorite-menu',
'favorite-flow',
'sys-log-line',
'demo-chart1',
'demo-chart2',
'task-info',
'task-list',
'task-compare-chart',
'task-trend-chart',
'TaskMessage',
'TaskList',
'ToDoCalendar',
];
return allComponentList.value.filter((item) => !item.disabled && myGrid.includes(item.key));
});
// setTimeout(() => {
// console.log('-----------myCompsList>>>>>>>>>', myCompsList.value);
// console.log('-----------allComponentList>>>>>>>>>', allComponentList.value);
// console.log('allComps>>>>>>', allComps) // 对象形式
// }, 5000)
const nowComponentList = computed(() => flatten(grid.value.copmsList));
const custom = () => {
customizing.value = true;
nextTick(() => {
const oldWidth = widgets.value.offsetWidth;
const scale = widgets.value.offsetWidth / oldWidth;
widgets.value.style.transform = `scale(${scale})`;
});
};
const setLayout = (layout: Array<number>) => {
grid.value.layout = layout;
// 默认布局是4组非默认布局是3组所以要合并不然会丢组件
if (isDefaultLayout.value && grid.value.copmsList.length === 4) {
grid.value.copmsList[1] = [...grid.value.copmsList[1], ...grid.value.copmsList[3]];
grid.value.copmsList.splice(3, 1);
}
isDefaultLayout.value = false;
// if (layout.join(',') === '24') {
// if (grid.value.copmsList[1]) {
// grid.value.copmsList[0].push(...grid.value.copmsList[1]);
// }
// if (grid.value.copmsList[2]) {
// grid.value.copmsList[0].push(...grid.value.copmsList[2]);
// }
// grid.value.copmsList.splice(1, 2);
// }
};
const push = (item: any) => {
grid.value.copmsList[0].push(item.key);
};
const remove = (item: any) => {
grid.value.copmsList = grid.value.copmsList.map((obj) => obj.filter((o) => o !== item));
};
const save = () => {
customizing.value = false;
widgets.value.style.removeProperty('transform');
Local.set(widgetsKey.value, JSON.stringify(grid.value));
};
const backDefault = () => {
isDefaultLayout.value = true;
customizing.value = false;
widgets.value.style.removeProperty('transform');
grid.value = cloneDeep(toValue(defaultGrid));
Local.remove(widgetsKey.value);
};
const close = () => {
customizing.value = false;
widgets.value.style.removeProperty('transform');
};
onMounted(() => {
// 初始化key
const data = useUserInfo().userInfos;
widgetsKey.value = `${window.location.host}_${data.user.userId}_widgets`;
let widgets = Local.get(widgetsKey.value);
grid.value = widgets ? JSON.parse(widgets) : cloneDeep(toValue(defaultGrid));
});
</script>
<style scoped lang="scss">
$rowHeight: 192px;
$rowSpace: 16px;
.custom_btn {
position: absolute;
top: 7px;
right: 5px;
z-index: 9;
color: white;
}
.widgets-home {
display: flex;
flex-direction: row;
flex: 1;
height: 100%;
}
.widgets-content {
flex: 1;
overflow: auto;
overflow-x: hidden;
padding: $rowSpace;
}
.widgets-aside {
width: 360px;
background: #fff;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
position: relative;
overflow: auto;
}
.widgets-aside-title {
font-size: 14px;
display: flex;
align-items: center;
justify-content: center;
}
.widgets-aside-title i {
margin-right: 10px;
font-size: 18px;
}
.widgets-aside-close {
font-size: 18px;
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 3px;
cursor: pointer;
}
.widgets-aside-close:hover {
background: rgba(180, 180, 180, 0.1);
}
.widgets-top {
display: flex;
justify-content: space-between;
align-items: center;
}
.widgets-top-title {
font-size: 18px;
font-weight: bold;
}
.widgets {
transform-origin: top left;
transition: transform 0.15s;
}
.draggable-box {
height: 100%;
}
.customizing .widgets-wrapper {
margin-right: -360px;
}
.customizing .widgets-wrapper .el-col {
padding-bottom: 15px;
}
.customizing .widgets-wrapper .draggable-box {
border: 1px dashed var(--el-color-primary);
padding: 15px;
}
.customizing .widgets-wrapper .no-widgets {
display: none;
}
.widgets-item {
position: relative;
margin-bottom: $rowSpace;
&-rows-1 {
height: $rowHeight;
}
&-rows-2 {
height: $rowHeight * 2 + $rowSpace;
}
}
.customize-overlay {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: rgba(255, 255, 255, 0.9);
cursor: move;
}
.customize-overlay label {
background: var(--el-color-primary);
color: #fff;
height: 40px;
padding: 0 30px;
border-radius: 40px;
font-size: 18px;
display: flex;
align-items: center;
justify-content: center;
cursor: move;
}
.customize-overlay label i {
margin-right: 15px;
font-size: 24px;
}
.customize-overlay .close {
position: absolute;
top: 15px;
left: 15px;
}
.widgets-list-item {
display: flex;
flex-direction: row;
padding: 15px;
align-items: center;
}
.widgets-list-item .item-logo {
width: 40px;
height: 40px;
border-radius: 50%;
background: rgba(180, 180, 180, 0.1);
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
margin-right: 15px;
color: #6a8bad;
}
.widgets-list-item .item-info {
flex: 1;
}
.widgets-list-item .item-info h2 {
font-size: 16px;
font-weight: normal;
cursor: default;
}
.widgets-list-item .item-info p {
font-size: 12px;
color: #999;
cursor: default;
}
.widgets-list-item:hover {
background: rgba(180, 180, 180, 0.1);
}
.widgets-wrapper .sortable-ghost {
opacity: 0.5;
}
.selectLayout {
width: 100%;
display: flex;
}
.selectLayout-item {
width: 60px;
height: 60px;
border: 2px solid var(--el-border-color-light);
padding: 5px;
cursor: pointer;
margin-right: 15px;
}
.selectLayout-item span {
display: block;
background: var(--el-border-color-light);
height: 46px;
}
.selectLayout-item.item02 span {
height: 30px;
}
.selectLayout-item.item02 .el-col:nth-child(1) span {
height: 14px;
margin-bottom: 2px;
}
.selectLayout-item.item03 span {
height: 14px;
margin-bottom: 2px;
}
.selectLayout-item:hover {
border-color: var(--el-color-primary);
}
.selectLayout-item.active {
border-color: var(--el-color-primary);
}
.selectLayout-item.active span {
background: var(--el-color-primary);
}
.dark {
.widgets-aside {
background: #2b2b2b;
}
.customize-overlay {
background: rgba(43, 43, 43, 0.9);
}
}
@media (max-width: 992px) {
.customizing .widgets {
transform: scale(1) !important;
}
.customizing .widgets-aside {
width: 100%;
position: absolute;
top: 50%;
right: 0;
bottom: 0;
}
.customizing .widgets-wrapper {
margin-right: 0;
}
}
</style>