4 changed files with 706 additions and 105 deletions
@ -0,0 +1,508 @@ |
|||
<template> |
|||
<a-divider /> |
|||
<div class="list-flex"> |
|||
<div class="item-box" v-for="(item, index) in projects" :key="index"> |
|||
<div class="one-level h-70 cursor-pointer flex items-center"> |
|||
<!-- <div class="icon" @click.stop="showActionCard(item)"><img src="https://www.tall.wiki/staticrec/drag.svg" /></div> --> |
|||
<div class="flex-none"> |
|||
<a-popover placement="bottomLeft"> |
|||
<template #content> |
|||
<p class="cursor-pointer" @click="showImportCard(item)">导入</p> |
|||
<p class="cursor-pointer" @click="exportProject(item.id)">导出</p> |
|||
<p class="m-0 cursor-pointer" @click="showDelCard(item)">删除</p> |
|||
</template> |
|||
<div class="icon"><img src="https://www.tall.wiki/staticrec/drag.svg" /></div> |
|||
</a-popover> |
|||
</div> |
|||
|
|||
<div class="w-full flex items-center justify-between" @click="toDetail(item)"> |
|||
<div class="detail"> |
|||
<div class="name-box flex items-center" :class="{ 'mb-2': item.businessCode !== 'ZERO' }"> |
|||
<div class="name truncate">{{ item.name }}</div> |
|||
<div class="precent-num"> |
|||
{{ item.status === 1 ? '进行中' : item.status === 2 ? '已结束' : item.status === 0 ? '未开始' : '暂停' }} |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="time" v-if="item.businessCode !== 'ZERO'"> |
|||
{{ dayjs(Number(item.startTime)).format('MM-DD HH:mm') }} 至 |
|||
{{ dayjs(Number(item.endTime)).format('MM-DD HH:mm') }} |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="right flex justify-end items-center" @click.stop="openMenu"> |
|||
<a-button v-if="item.businessCode === 'ZERO'" class="mr-2" shape="round" type="primary" @click="toWorkbench(item)"> |
|||
工作台 |
|||
</a-button> |
|||
|
|||
<RightOutlined v-if="!item.show" @click="changeShow(item)" /> |
|||
<DownOutlined v-else @click="changeShow(item)" /> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="two-box" v-if="item.show"> |
|||
<div class="two-flex" v-for="(sonItem, sonIndex) in item.sonProjectList" :key="sonIndex"> |
|||
<div class="two-level h-70 cursor-pointer flex items-center"> |
|||
<!-- <div class="icon" @click.stop="showActionCard(sonItem)"><img src="https://www.tall.wiki/staticrec/drag.svg" /></div> --> |
|||
|
|||
<div class="flex-none"> |
|||
<a-popover placement="bottomLeft"> |
|||
<template #content> |
|||
<!-- <p class="cursor-pointer" @click="showImportCard(sonItem)">导入</p> --> |
|||
<p class="cursor-pointer" @click="exportProject(sonItem.id)">导出</p> |
|||
<p class="m-0 cursor-pointer" @click="showDelCard(sonItem)">删除</p> |
|||
</template> |
|||
<div class="icon"><img src="https://www.tall.wiki/staticrec/drag.svg" /></div> |
|||
</a-popover> |
|||
</div> |
|||
|
|||
<div class="flex items-center justify-between" style="width: calc(100% - 32px)" @click="toDetail(sonItem)"> |
|||
<div class="detail"> |
|||
<div class="name-box mb-2 flex items-center"> |
|||
<div class="name truncate">{{ sonItem.name }}</div> |
|||
<div class="precent-num"> |
|||
{{ item.status === 1 ? '进行中' : item.status === 2 ? '已结束' : item.status === 0 ? '未开始' : '暂停' }} |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="time"> |
|||
{{ dayjs(Number(sonItem.startTime)).format('MM-DD HH:mm') }} 至 |
|||
{{ dayjs(Number(sonItem.endTime)).format('MM-DD HH:mm') }} |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="right" @click.stop="openMenu"> |
|||
<RightOutlined v-if="!sonItem.show" @click="changeShow(sonItem)" /> |
|||
<DownOutlined v-else @click="changeShow(sonItem)" /> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<input class="import-wbs hidden" type="file" @change="toImport" /> |
|||
<a-modal v-model:visible="importVisible" title="导入" @ok="handleImport"> |
|||
<p>确定要导入到{{ importParent }}吗?</p> |
|||
</a-modal> |
|||
|
|||
<a-modal v-model:visible="visible" title="删除" @ok="handleOk"> |
|||
<p>确定要删除吗?</p> |
|||
</a-modal> |
|||
|
|||
<draggable |
|||
class="dragArea p-0 list-none" |
|||
tag="ul" |
|||
:list="projects" |
|||
:group="{ name: 'g1' }" |
|||
item-key="name" |
|||
:move="checkMove" |
|||
@end="onEnd" |
|||
> |
|||
<template #item="{ element }"> |
|||
<li class="item-box"> |
|||
<!-- <p>{{ element.name }}</p> --> |
|||
<div |
|||
class="one-level cursor-pointer h-60 flex items-center" |
|||
:class="{ 'h-70': element.sonProjectList && element.sonProjectList.length > 0 }" |
|||
:style="{ 'padding-top': element.sonProjectList && element.sonProjectList.length > 0 ? 0 : '5px' }" |
|||
> |
|||
<!-- <div class="icon" @click.stop="showActionCard(item)"><img src="https://www.tall.wiki/staticrec/drag.svg" /></div> --> |
|||
<div class="flex-none"> |
|||
<a-popover placement="bottomLeft"> |
|||
<template #content> |
|||
<p class="cursor-pointer" @click="showImportCard(element)">导入</p> |
|||
<p class="cursor-pointer" @click="exportProject(element.id)">导出</p> |
|||
<p class="m-0 cursor-pointer" @click="showDelCard(element)">删除</p> |
|||
</template> |
|||
<div class="icon"><img src="https://www.tall.wiki/staticrec/drag.svg" /></div> |
|||
</a-popover> |
|||
</div> |
|||
|
|||
<div class="w-full flex items-center justify-between" @click="toDetail(element)"> |
|||
<div class="detail"> |
|||
<div class="name-box flex items-center" :class="{ 'mb-2': element.businessCode !== 'ZERO' }"> |
|||
<div class="name truncate">{{ element.name }}</div> |
|||
<div class="precent-num"> |
|||
{{ element.status === 1 ? '进行中' : element.status === 2 ? '已结束' : element.status === 0 ? '未开始' : '暂停' }} |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="time" v-if="element.businessCode !== 'ZERO'"> |
|||
{{ dayjs(Number(element.startTime)).format('MM-DD HH:mm') }} 至 |
|||
{{ dayjs(Number(element.endTime)).format('MM-DD HH:mm') }} |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="right flex justify-end items-center" @click.stop="openMenu"> |
|||
<a-button v-if="element.businessCode === 'ZERO'" class="mr-2" shape="round" type="primary" @click="toWorkbench(element)"> |
|||
工作台 |
|||
</a-button> |
|||
|
|||
<RightOutlined v-if="!element.show" @click="changeShow(element)" /> |
|||
<DownOutlined v-else @click="changeShow(element)" /> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<draggable |
|||
class="dragArea p-0 list-none two-box" |
|||
tag="ul" |
|||
:list="element.sonProjectList" |
|||
:group="{ name: 'g1' }" |
|||
item-key="name" |
|||
:move="checkMove" |
|||
@end="onEnd" |
|||
> |
|||
<template #item="{ element }"> |
|||
<li class="two-flex"> |
|||
<!-- <p>{{ element.name }}</p> --> |
|||
<div class="two-level h-70 cursor-pointer flex items-center"> |
|||
<!-- <div class="icon" @click.stop="showActionCard(sonItem)"> |
|||
<img src="https://www.tall.wiki/staticrec/drag.svg" /> |
|||
</div> --> |
|||
|
|||
<div class="flex-none"> |
|||
<a-popover placement="bottomLeft"> |
|||
<template #content> |
|||
<!-- <p class="cursor-pointer" @click="showImportCard(sonItem)">导入</p> --> |
|||
<p class="cursor-pointer" @click="exportProject(element.id)">导出</p> |
|||
<p class="m-0 cursor-pointer" @click="showDelCard(element)">删除</p> |
|||
</template> |
|||
<div class="icon"><img src="https://www.tall.wiki/staticrec/drag.svg" /></div> |
|||
</a-popover> |
|||
</div> |
|||
|
|||
<div class="flex items-center justify-between" style="width: calc(100% - 32px)" @click="toDetail(element)"> |
|||
<div class="detail"> |
|||
<div class="name-box mb-2 flex items-center"> |
|||
<div class="name truncate">{{ element.name }}</div> |
|||
<div class="precent-num"> |
|||
{{ element.status === 1 ? '进行中' : element.status === 2 ? '已结束' : element.status === 0 ? '未开始' : '暂停' }} |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="time"> |
|||
{{ dayjs(Number(element.startTime)).format('MM-DD HH:mm') }} 至 |
|||
{{ dayjs(Number(element.endTime)).format('MM-DD HH:mm') }} |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="right" @click.stop="openMenu"> |
|||
<RightOutlined v-if="!element.show" @click="changeShow(element)" /> |
|||
<DownOutlined v-else @click="changeShow(element)" /> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</li> |
|||
</template> |
|||
</draggable> |
|||
</li> |
|||
</template> |
|||
</draggable> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import { ref, reactive, watch, computed } from 'vue'; |
|||
import { useStore } from 'vuex'; |
|||
import { useRouter } from 'vue-router'; |
|||
import dayjs from 'dayjs'; |
|||
import { getProjects, delProject, setProjectSort, exportWbs, importWbs } from 'apis'; |
|||
import { RightOutlined, DownOutlined } from '@ant-design/icons-vue'; |
|||
import { message } from 'ant-design-vue'; |
|||
import draggable from 'vuedraggable'; |
|||
|
|||
const store = useStore(); |
|||
const router = useRouter(); |
|||
const visible = ref(false); // 删除弹框 |
|||
const importVisible = ref(false); // 导入弹框 |
|||
const importParent = ref(null); // 导入的目标项目名称 |
|||
const importParentId = ref(null); // 导入的目标项目ID |
|||
const currUrl = ref(null); // 当前导入的url |
|||
const deleteId = ref(null); // 删除项目id |
|||
const sessionProject = sessionStorage.getItem('project'); // 项目信息缓存 |
|||
const projectInfo = computed(() => store.state.projects.project); // 当前选择项目信息 |
|||
const projects = computed(() => store.state.projects.projects); // 项目列表 |
|||
const startTime = computed(() => store.state.layout.startTime); // 当前选择时间 |
|||
const endTime = computed(() => store.state.layout.endTime); // 当前选择时间 |
|||
const refreshProjects = computed(() => store.state.layout.refreshProjects); // 刷新项目列表 |
|||
|
|||
const moveProjectId = ref(''); // 移动项目id |
|||
const moveBusinessCode = ref(''); // 移动项目所属服务 |
|||
const targetProjectId = ref(''); // 目标项目id |
|||
|
|||
if (sessionProject && !projectInfo.value.id) { |
|||
// 如果有缓存,而且store存储为空,则将缓存信息放到store中,一般页面刷新后store数据清空的清空 |
|||
const info = JSON.parse(sessionProject); |
|||
store.commit('projects/setProject', info); |
|||
} |
|||
|
|||
// 判断时间是否存在,不存在则设置 |
|||
if (!startTime.value) { |
|||
const data = { |
|||
startTime: dayjs().startOf('day').format('x'), |
|||
endTime: dayjs().endOf('day').format('x'), |
|||
}; |
|||
store.commit('layout/setSelectTime', data); |
|||
} |
|||
|
|||
getProjectsList(); |
|||
|
|||
watch([startTime, endTime, refreshProjects], () => { |
|||
getProjectsList(); |
|||
}); |
|||
|
|||
// 获取项目列表 |
|||
async function getProjectsList() { |
|||
try { |
|||
const data = await getProjects(startTime.value, endTime.value); |
|||
data.forEach(item => { |
|||
item.show = false; |
|||
if (item.sonProjectList.length > 0) { |
|||
item.show = true; |
|||
} |
|||
}); |
|||
|
|||
store.commit('projects/setProjects', data); |
|||
} catch (error) { |
|||
message.info(error); |
|||
throw new Error(error); |
|||
} |
|||
} |
|||
|
|||
// 点击打开项目详情 |
|||
function toDetail(item) { |
|||
clearRolesData(); |
|||
clearTasksData(); |
|||
store.commit('projects/setProject', item); |
|||
store.commit('task/setTaskDetailParams', ''); // 设置详情页参数 |
|||
store.commit('task/setTaskDetailUrl', ''); // 设置详情页链接 |
|||
store.commit('task/setTaskDetailShow', ''); |
|||
router.push({ path: '/tall/pc/home/test' }); |
|||
} |
|||
|
|||
// 父项目展开收起 |
|||
function changeShow(item) { |
|||
item.show = !item.show; |
|||
} |
|||
|
|||
// 打开导入确认弹框 |
|||
const showImportCard = item => { |
|||
importVisible.value = true; |
|||
importParent.value = item.name; |
|||
importParentId.value = item.id; |
|||
currUrl.value = item.url; |
|||
}; |
|||
|
|||
// 确认导入 |
|||
async function handleImport() { |
|||
importVisible.value = false; |
|||
await importProject(); |
|||
getProjectsList(); |
|||
} |
|||
|
|||
// 导入 |
|||
async function importProject() { |
|||
document.querySelector('.import-wbs').click(); |
|||
} |
|||
|
|||
async function toImport(e) { |
|||
try { |
|||
const file = e.target.files[0]; |
|||
const param = new FormData(); |
|||
param.append('file', file); |
|||
const config = { headers: { 'Content-Type': 'multipart/form-data' } }; |
|||
|
|||
const res = await importWbs(currUrl.value, param, importParentId.value); |
|||
} catch (error) { |
|||
message.info(error || '导入失败'); |
|||
} |
|||
} |
|||
|
|||
// 点击打开删除确认弹框 |
|||
const showDelCard = item => { |
|||
visible.value = true; |
|||
deleteId.value = item.id; |
|||
}; |
|||
|
|||
// 删除 |
|||
async function handleOk() { |
|||
visible.value = false; |
|||
await deleteProject(deleteId.value); |
|||
getProjectsList(); |
|||
} |
|||
|
|||
// 删除项目 |
|||
async function deleteProject(param) { |
|||
try { |
|||
await delProject(param); |
|||
} catch (error) { |
|||
message.info(error); |
|||
throw new Error(error); |
|||
} |
|||
} |
|||
|
|||
// 导出 |
|||
async function exportProject(id) { |
|||
try { |
|||
const params = { param: { projectId: id } }; |
|||
|
|||
const { url } = store.state.projects.project; |
|||
const data = await exportWbs(params, url); |
|||
window.open(data.url, '_blank'); |
|||
} catch (error) { |
|||
message.info(error); |
|||
throw new Error(error); |
|||
} |
|||
} |
|||
|
|||
// 清空角色数据 |
|||
function clearRolesData() { |
|||
store.commit('role/setInvisibleRoles', []); |
|||
store.commit('role/setVisibleRoles', []); |
|||
store.commit('role/setRoleId', ''); |
|||
} |
|||
|
|||
// 清空任务数据 |
|||
function clearTasksData() { |
|||
// 清空日常任务的数据 |
|||
store.commit('task/setPermanents', []); |
|||
store.commit('task/setDailyTasks', []); |
|||
// 清空定期任务数据 |
|||
store.commit('task/clearTasks'); |
|||
// 清空真实任务数据 |
|||
store.commit('task/clearRealTasks'); |
|||
store.commit('task/setUpNextPage', 1); |
|||
store.commit('task/setDownNextPage', 1); |
|||
} |
|||
|
|||
// 跳转到工作台 |
|||
function toWorkbench(item) { |
|||
toDetail(item); |
|||
store.commit('task/setTaskDetailShow', 'workbench'); // 设置内置组件关键字(根据关键字判断显示的详情页) |
|||
} |
|||
|
|||
function checkMove(e) { |
|||
moveProjectId.value = e.draggedContext.element.id; |
|||
moveBusinessCode.value = e.draggedContext.element.businessCode; |
|||
targetProjectId.value = ''; |
|||
} |
|||
|
|||
function onEnd(e) { |
|||
projects.value.forEach(item => { |
|||
if (item.sonProjectList) { |
|||
const index = item.sonProjectList.findIndex(sec => sec.id === moveProjectId.value); |
|||
if (index > -1) targetProjectId.value = item.id; |
|||
} |
|||
}); |
|||
|
|||
const params = { |
|||
moveProjectId: moveProjectId.value, |
|||
targetProjectId: targetProjectId.value, |
|||
businessCode: moveBusinessCode.value, |
|||
}; |
|||
|
|||
handleSort(params); |
|||
} |
|||
|
|||
// 排序 |
|||
async function handleSort(param) { |
|||
try { |
|||
const params = { param }; |
|||
await setProjectSort(params); |
|||
message.info('层级关系修改成功'); |
|||
} catch (error) { |
|||
message.info(error.msg || '层级关系修改失败'); |
|||
throw new Error(error); |
|||
} |
|||
|
|||
getProjectsList(); |
|||
} |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.list-flex { |
|||
height: calc(100vh - 48px - 272px - 56px - 16px - 2px); |
|||
overflow-y: auto; |
|||
} |
|||
|
|||
.list-flex::-webkit-scrollbar { |
|||
width: 0 !important; |
|||
} |
|||
|
|||
.ant-divider-horizontal { |
|||
height: 16px; |
|||
background: #eeeeee; |
|||
margin: 0; |
|||
} |
|||
.project-list { |
|||
padding: 16px 0; |
|||
} |
|||
|
|||
.h-60 { |
|||
height: 60px; |
|||
} |
|||
|
|||
.h-70 { |
|||
height: 70px; |
|||
} |
|||
|
|||
.one-level { |
|||
padding: 0 16px; |
|||
} |
|||
|
|||
.two-level { |
|||
padding: 0 16px 0 32px; |
|||
} |
|||
|
|||
.three-level { |
|||
padding: 0 16px 0 48px; |
|||
} |
|||
|
|||
.icon { |
|||
margin-right: 8px; |
|||
width: 24px; |
|||
height: 24px; |
|||
} |
|||
|
|||
.detail { |
|||
width: calc(100% - 46px); |
|||
} |
|||
|
|||
.name { |
|||
margin-right: 8px; |
|||
font-size: 14px; |
|||
line-height: 1; |
|||
font-weight: 600; |
|||
max-width: calc(100% - 56px); |
|||
color: #333333; |
|||
} |
|||
|
|||
.precent-num { |
|||
width: 48px; |
|||
height: 18px; |
|||
line-height: 18px; |
|||
text-align: center; |
|||
border-radius: 18px; |
|||
background-color: rgba(24, 144, 255, 0.2); |
|||
color: #1890ff; |
|||
font-size: 12px; |
|||
} |
|||
|
|||
.time { |
|||
font-size: 12px; |
|||
color: #999999; |
|||
} |
|||
|
|||
.right { |
|||
width: 14px; |
|||
margin-left: 30px; |
|||
} |
|||
|
|||
.dragArea { |
|||
min-height: 5px; |
|||
/* outline: 1px dashed; */ |
|||
} |
|||
</style> |
Loading…
Reference in new issue