x-php-Admin/src/views/home/widgets/index.vue

253 lines
8.9 KiB
Vue
Raw Normal View History

2023-06-07 10:48:30 +00:00
<template>
<div :class="['widgets-home', customizing?'customizing':'']" ref="main">
<div class="widgets-content">
2023-06-27 11:02:30 +00:00
<div class="widgets-top-actions">
<el-button v-if="customizing" type="primary" icon="el-icon-check" round @click="save">完成</el-button>
<el-button v-else type="primary" icon="el-icon-edit" round @click="custom">自定义</el-button>
2023-06-07 10:48:30 +00:00
</div>
<div class="widgets" ref="widgets">
<div class="widgets-wrapper">
2023-08-16 15:22:19 +00:00
<div v-if="widgets.length<=0" class="no-widgets">
2023-06-27 11:02:30 +00:00
<el-empty description="没有部件啦" :image-size="280"></el-empty>
2023-06-07 10:48:30 +00:00
</div>
2023-08-16 15:22:19 +00:00
2023-06-07 10:48:30 +00:00
<el-row :gutter="15">
2023-08-16 15:22:19 +00:00
<draggable v-model="widgets" animation="100" handle=".customize-overlay" group="people" item-key="com" force-fallback fallbackOnBody class="draggable-box">
<template #item="{ element }">
<el-col :md="parseInt(element.layout) || 24" :xs="24">
2023-06-07 10:48:30 +00:00
<div class="widgets-item">
2023-08-16 15:22:19 +00:00
<component :is="allComps[element.name]"></component>
2023-06-07 10:48:30 +00:00
<div v-if="customizing" class="customize-overlay">
2023-08-16 15:22:19 +00:00
<el-button class="close" type="danger" plain icon="el-icon-close" size="small" @click="remove(element.name)"></el-button>
<label>
<el-icon><component :is="allComps[element.name].icon" /></el-icon>
{{ allComps[element.name].title }}
</label>
<el-input-number v-model="element.layout" :min="4" :max="24" />
<!-- <el-slider v-model="element.layout" :min="4" :max="24" :step="1" show-stops /> -->
2023-06-07 10:48:30 +00:00
</div>
</div>
2023-08-16 15:22:19 +00:00
</el-col>
</template>
</draggable>
2023-06-07 10:48:30 +00:00
</el-row>
</div>
</div>
</div>
<div v-if="customizing" class="widgets-aside">
<el-container>
<el-header>
<div class="widgets-aside-title"><el-icon><el-icon-circle-plus-filled/></el-icon></div>
<div class="widgets-aside-close" @click="close()"><el-icon><el-icon-close /></el-icon></div>
</el-header>
<el-main class="nopadding">
<div class="widgets-list">
<div v-if="myCompsList.length<=0" class="widgets-list-nodata">
<el-empty description="没有部件啦" :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>{{ item.title }}</h2>
<p>{{ 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="backDefaul()">恢复默认</el-button>
</el-footer>
</el-container>
</div>
</div>
</template>
<script>
import draggable from 'vuedraggable'
import allComps from './components'
export default {
components: {
draggable
},
data() {
return {
customizing: false,
allComps: allComps,
2023-06-27 11:02:30 +00:00
defaultGrid: [],
2023-08-16 15:22:19 +00:00
widgets: [],
2023-06-07 10:48:30 +00:00
}
},
created(){
2023-06-27 11:02:30 +00:00
this.defaultGrid = this.$tool.data.get("defaultgrid") || this.$config.DEFAULT_GRID
2023-08-16 15:22:19 +00:00
this.widgets = this.$tool.data.get('widgets');
if (this.widgets.length==0) {
this.widgets = JSON.parse(JSON.stringify(this.defaultGrid))
}
2023-06-07 10:48:30 +00:00
},
mounted() {
this.$emit('on-mounted')
},
computed: {
allCompsList(){
var allCompsList = []
for(var key in this.allComps){
allCompsList.push({
key: key,
title: allComps[key].title,
icon: allComps[key].icon,
description: allComps[key].description
})
}
2023-08-25 10:55:41 +00:00
2023-08-16 15:22:19 +00:00
var myCopmsList = this.widgets
2023-06-07 10:48:30 +00:00
for(let comp of allCompsList){
2023-08-16 15:22:19 +00:00
const _item = myCopmsList.find((item)=>{return item.name === comp.key})
2023-06-07 10:48:30 +00:00
if(_item){
comp.disabled = true
}
}
2023-08-25 10:55:41 +00:00
console.log(allCompsList)
2023-06-07 10:48:30 +00:00
return allCompsList
},
myCompsList(){
2023-06-13 07:50:06 +00:00
var myGrid = this.$tool.data.get("dashboardgrid")
2023-08-25 10:55:41 +00:00
console.log(this.$tool.data.get("dashboardgrid"))
2023-08-16 15:22:19 +00:00
if (myGrid.length>0) {
return this.allCompsList.filter(item => !item.disabled && myGrid.includes(item.key))
}
return this.allCompsList.filter(item => !item.disabled)
2023-06-07 10:48:30 +00:00
},
},
methods: {
//开启自定义
custom(){
this.customizing = true
const oldWidth = this.$refs.widgets.offsetWidth
this.$nextTick(() => {
const scale = this.$refs.widgets.offsetWidth / oldWidth
this.$refs.widgets.style.setProperty('transform', `scale(${scale})`)
})
},
//追加
push(item){
2023-08-16 15:22:19 +00:00
this.widgets.push({
name:item.key,
layout:item.layout || 24
})
2023-06-07 10:48:30 +00:00
},
//隐藏组件
remove(item){
2023-08-16 15:22:19 +00:00
var newCopmsList = this.widgets
newCopmsList.splice(newCopmsList.findIndex(o => o.name === item), 1);
2023-06-07 10:48:30 +00:00
},
//保存
save(){
this.customizing = false
this.$refs.widgets.style.removeProperty('transform')
2023-08-16 15:22:19 +00:00
this.$tool.data.set("widgets", this.widgets)
this.$http.post('widgets', {name:'widgets', event:'update', data:this.widgets }).then((res) => {
if (res.code == 200 ) {
return;
}
this.$alert(res.message, "提示", {type: 'error'});
});
2023-06-07 10:48:30 +00:00
},
//恢复默认
backDefaul(){
this.customizing = false
this.$refs.widgets.style.removeProperty('transform')
2023-08-16 15:22:19 +00:00
this.widgets = JSON.parse(JSON.stringify(this.defaultGrid))
this.$tool.data.remove("widgets")
2023-06-07 10:48:30 +00:00
},
//关闭
close(){
this.customizing = false
this.$refs.widgets.style.removeProperty('transform')
}
}
}
</script>
<style scoped lang="scss">
2023-08-16 15:22:19 +00:00
.el-input-number {
position: absolute;
top: 15px;
left: 10px;
width: 100px;
}
.el-slider {
position: absolute;
top: 12px;
left: 5px;
width: calc( 100% - 68px );
}
2023-06-07 10:48:30 +00:00
.widgets-home {display: flex;flex-direction: row;flex: 1;height: 100%;}
2023-06-27 11:02:30 +00:00
.widgets-content {flex: 1;overflow: auto;overflow-x:hidden;padding:10px 10px;}
2023-08-16 15:22:19 +00:00
.widgets-aside {z-index: 999; width: 380px;background: #fff;box-shadow: 0 0 10px rgba(0,0,0,.1);position: relative;overflow: auto;}
2023-06-07 10:48:30 +00:00
.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 {margin-bottom: 15px;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 .15s;}
2023-06-27 11:02:30 +00:00
.widgets-top-actions {
position: fixed;
right: 30px;
2023-06-27 15:07:38 +00:00
bottom: 10px;
2023-08-16 15:22:19 +00:00
z-index: 1000;
2023-06-27 11:02:30 +00:00
-webkit-animation-duration: 3s;
-o-animation-duration: 3s;
animation-duration: 3s;
}
2023-06-07 10:48:30 +00:00
2023-08-16 15:22:19 +00:00
.draggable-box {height: 100%;width: 100%; display: contents; }
2023-06-07 10:48:30 +00:00
.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;}
2023-08-16 15:22:19 +00:00
.customizing .widgets-item {
position: relative;margin-bottom: 15px;
border: 1px dashed var(--el-color-primary);
2023-08-17 00:43:29 +00:00
padding: 15px;
2023-08-16 15:22:19 +00:00
}
2023-06-07 10:48:30 +00:00
.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;right:15px;}
2023-08-16 15:22:19 +00:00
.customize-overlay .close:focus, .customize-overlay .close:hover {background: var(--el-button-hover-color);color: var(--el-color-danger);}
2023-06-07 10:48:30 +00:00
.widgets-list {}
.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;}
.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>