Initial commit
This commit is contained in:
@@ -0,0 +1,302 @@
|
||||
<template>
|
||||
<div class="ele-body ele-body-card">
|
||||
<profile-card />
|
||||
<link-card ref="linkCardRef" />
|
||||
<a-row :gutter="16" ref="wrapRef">
|
||||
<a-col
|
||||
v-for="(item, index) in data"
|
||||
:key="item.name"
|
||||
:lg="item.lg"
|
||||
:md="item.md"
|
||||
:sm="item.sm"
|
||||
:xs="item.xs"
|
||||
>
|
||||
<component
|
||||
:is="item.name"
|
||||
:title="item.title"
|
||||
@remove="onRemove(index)"
|
||||
@edit="onEdit(index)"
|
||||
/>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-card :bordered="false" :body-style="{ padding: 0 }">
|
||||
<div class="ele-cell" style="line-height: 42px">
|
||||
<div
|
||||
class="ele-cell-content ele-text-primary workplace-bottom-btn"
|
||||
@click="add"
|
||||
>
|
||||
<PlusCircleOutlined /> 添加视图
|
||||
</div>
|
||||
<a-divider type="vertical" />
|
||||
<div
|
||||
class="ele-cell-content ele-text-primary workplace-bottom-btn"
|
||||
@click="reset"
|
||||
>
|
||||
<UndoOutlined /> 重置布局
|
||||
</div>
|
||||
</div>
|
||||
</a-card>
|
||||
<ele-modal
|
||||
:width="680"
|
||||
v-model:visible="visible"
|
||||
title="未添加的视图"
|
||||
:footer="null"
|
||||
>
|
||||
<a-row :gutter="16">
|
||||
<a-col
|
||||
v-for="item in notAddedData"
|
||||
:key="item.name"
|
||||
:md="8"
|
||||
:sm="12"
|
||||
:xs="24"
|
||||
>
|
||||
<div
|
||||
class="workplace-card-item ele-border-split"
|
||||
@click="addView(item)"
|
||||
>
|
||||
<div class="workplace-card-header ele-border-split">
|
||||
{{ item.title }}
|
||||
</div>
|
||||
<div class="workplace-card-body ele-text-placeholder">
|
||||
<plus-circle-outlined />
|
||||
</div>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-empty v-if="!notAddedData.length" description="已添加所有视图" />
|
||||
</ele-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted, onBeforeUnmount } from 'vue';
|
||||
import SortableJs from 'sortablejs';
|
||||
import type { Row as ARow } from 'ant-design-vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { PlusCircleOutlined, UndoOutlined } from '@ant-design/icons-vue';
|
||||
import ProfileCard from './components/profile-card.vue';
|
||||
import LinkCard from './components/link-card.vue';
|
||||
const CACHE_KEY = 'workplace-layout';
|
||||
|
||||
interface ViewItem {
|
||||
name: string;
|
||||
title: string;
|
||||
lg: number;
|
||||
md: number;
|
||||
sm: number;
|
||||
xs: number;
|
||||
}
|
||||
|
||||
// 默认布局
|
||||
const DEFAULT: ViewItem[] = [
|
||||
// {
|
||||
// name: 'project-card',
|
||||
// title: '项目管理',
|
||||
// lg: 16,
|
||||
// md: 24,
|
||||
// sm: 24,
|
||||
// xs: 24
|
||||
// },
|
||||
// {
|
||||
// name: 'user-list',
|
||||
// title: '小组成员',
|
||||
// lg: 8,
|
||||
// md: 24,
|
||||
// sm: 24,
|
||||
// xs: 24
|
||||
// },
|
||||
// {
|
||||
// name: 'activities-card',
|
||||
// title: '最新动态',
|
||||
// lg: 8,
|
||||
// md: 24,
|
||||
// sm: 24,
|
||||
// xs: 24
|
||||
// },
|
||||
// {
|
||||
// name: 'task-card',
|
||||
// title: '我的任务',
|
||||
// lg: 8,
|
||||
// md: 24,
|
||||
// sm: 24,
|
||||
// xs: 24
|
||||
// },
|
||||
// {
|
||||
// name: 'goal-card',
|
||||
// title: '本月目标',
|
||||
// lg: 8,
|
||||
// md: 24,
|
||||
// sm: 24,
|
||||
// xs: 24
|
||||
// },
|
||||
// {
|
||||
// name: 'docs',
|
||||
// title: '知识库',
|
||||
// lg: 8,
|
||||
// md: 24,
|
||||
// sm: 24,
|
||||
// xs: 24
|
||||
// }
|
||||
];
|
||||
|
||||
// 获取缓存的顺序
|
||||
const cache = (() => {
|
||||
const str = localStorage.getItem(CACHE_KEY);
|
||||
try {
|
||||
return str ? JSON.parse(str) : null;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
})();
|
||||
|
||||
const data = ref<ViewItem[]>([...(cache ?? DEFAULT)]);
|
||||
|
||||
const visible = ref(false);
|
||||
|
||||
const linkCardRef = ref<InstanceType<typeof LinkCard> | null>(null);
|
||||
|
||||
const wrapRef = ref<InstanceType<typeof ARow> | null>(null);
|
||||
|
||||
let sortableIns: SortableJs | null = null;
|
||||
|
||||
// 未添加的数据
|
||||
const notAddedData = computed(() => {
|
||||
return DEFAULT.filter((d) => !data.value.some((t) => t.name === d.name));
|
||||
});
|
||||
|
||||
/* 添加 */
|
||||
const add = () => {
|
||||
visible.value = true;
|
||||
};
|
||||
|
||||
/* 重置布局 */
|
||||
const reset = () => {
|
||||
data.value = [...DEFAULT];
|
||||
cacheData();
|
||||
linkCardRef.value?.reset();
|
||||
message.success('已重置');
|
||||
};
|
||||
|
||||
/* 缓存布局 */
|
||||
const cacheData = () => {
|
||||
localStorage.setItem(CACHE_KEY, JSON.stringify(data.value));
|
||||
};
|
||||
|
||||
/* 删除视图 */
|
||||
const onRemove = (index: number) => {
|
||||
data.value = data.value.filter((_d, i) => i !== index);
|
||||
cacheData();
|
||||
};
|
||||
|
||||
/* 编辑视图 */
|
||||
const onEdit = (index: number) => {
|
||||
data.value.map((d) => {
|
||||
if (d.name == 'user-list') {
|
||||
}
|
||||
});
|
||||
// message.info('点击了编辑');
|
||||
};
|
||||
|
||||
/* 添加视图 */
|
||||
const addView = (item) => {
|
||||
data.value.push(item);
|
||||
cacheData();
|
||||
message.success('已添加');
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
const isTouchDevice = 'ontouchstart' in document.documentElement;
|
||||
if (isTouchDevice) {
|
||||
return;
|
||||
}
|
||||
sortableIns = new SortableJs(wrapRef.value?.$el, {
|
||||
handle: '.ant-card-head',
|
||||
animation: 300,
|
||||
onUpdate: ({ oldIndex, newIndex }) => {
|
||||
if (typeof oldIndex === 'number' && typeof newIndex === 'number') {
|
||||
const temp = [...data.value];
|
||||
temp.splice(newIndex, 0, temp.splice(oldIndex, 1)[0]);
|
||||
data.value = temp;
|
||||
cacheData();
|
||||
}
|
||||
},
|
||||
setData: () => {}
|
||||
});
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (sortableIns) {
|
||||
sortableIns.destroy();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import ActivitiesCard from './components/activities-card.vue';
|
||||
import TaskCard from './components/task-card.vue';
|
||||
import GoalCard from './components/goal-card.vue';
|
||||
import ProjectCard from './components/project-card.vue';
|
||||
import UserList from './components/user-list.vue';
|
||||
import Docs from './components/docs.vue';
|
||||
|
||||
export default {
|
||||
name: 'DashboardWorkplace',
|
||||
components: {
|
||||
ProjectCard,
|
||||
UserList,
|
||||
ActivitiesCard,
|
||||
TaskCard,
|
||||
GoalCard,
|
||||
Docs
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.ele-body :deep(.ant-card-head) {
|
||||
cursor: move;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.ele-body :deep(.ant-row > .ant-col.sortable-chosen > .ant-card) {
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.workplace-bottom-btn {
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.workplace-bottom-btn:hover {
|
||||
background: hsla(0, 0%, 60%, 0.05);
|
||||
}
|
||||
|
||||
/* 添加弹窗 */
|
||||
.workplace-card-item {
|
||||
margin-bottom: 15px;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-radius: 4px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
transition: box-shadow 0.2s, background-color 0.2s;
|
||||
}
|
||||
|
||||
.workplace-card-item:hover {
|
||||
box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.1);
|
||||
background: hsla(0, 0%, 60%, 0.05);
|
||||
}
|
||||
|
||||
.workplace-card-item .workplace-card-header {
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-style: solid;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.workplace-card-body {
|
||||
font-size: 26px;
|
||||
padding: 24px 10px;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user