24 changed files with 891 additions and 3882 deletions
@ -1,731 +0,0 @@ |
|||||
<template> |
|
||||
<view> |
|
||||
<scroll-view scroll-y="true"> |
|
||||
<view> |
|
||||
<view |
|
||||
:id="'cu-' + index" |
|
||||
class="cu-item flex-col" |
|
||||
v-for="(item, index) in data.itemList" |
|
||||
:key="item.id" |
|
||||
:style="{ 'background-color': item.color }" |
|
||||
@touchend="stops($event, index)" |
|
||||
@touchmove.stop.prevent="move" |
|
||||
@touchstart="start($event, index)" |
|
||||
> |
|
||||
|
|
||||
<!-- <view class="border-100 bg-blue-500" v-if="item.showTopBorder"></view> --> |
|
||||
|
|
||||
<view class="w-full" :style="{background: item.styleColor}"> |
|
||||
<!-- 有子项目 父项目 --> |
|
||||
<view class="flex items-center justify-between p-3"> |
|
||||
<u-icon @click="openMenu(item, index)" class="mover" name="https://www.tall.wiki/staticrec/drag.svg" size="48"></u-icon> |
|
||||
|
|
||||
<view @click="openProject(item)" class="flex-1 px-3"> |
|
||||
<view class="flex items-center" :class="{'mb-1': index > 0}"> |
|
||||
<view class="mr-2">{{ item.name }}</view> |
|
||||
<!-- 状态 TODO:--> |
|
||||
<view class="px-2 text-xs text-green-400 bg-green-100 rounded-full flex-shrink-0">{{item.status === 1 ? '进行中' : item.status === 2 ? '已结束' : item.status === 0 ? '未开始' : '暂停'}}</view> |
|
||||
</view> |
|
||||
|
|
||||
<view v-if="index > 0" class="flex items-center text-xs text-gray-400"> |
|
||||
<view class="pr-2">{{ dayjs(+item.startTime).format('MM-DD HH:mm') }}</view> |
|
||||
至 |
|
||||
<view class="pl-2">{{ dayjs(+item.endTime).format('MM-DD HH:mm') }}</view> |
|
||||
</view> |
|
||||
</view> |
|
||||
|
|
||||
<view class="workbench-btn" v-if="index === 0" @click="toWorkbench">工作台</view> |
|
||||
|
|
||||
<view class="remind-box bg-red-500 text-white text-xs" v-if="item.remindNum">{{ item.remindNum > 99 ? '99+' : item.remindNum }}</view> |
|
||||
|
|
||||
<!-- 箭头 --> |
|
||||
<view v-if="item.sonProjectList && item.sonProjectList.length"> |
|
||||
<u-icon |
|
||||
@click="openSubProject(item.sonProjectList.length, index)" |
|
||||
class="text-gray-400" |
|
||||
name="arrow-up" |
|
||||
size="14px" |
|
||||
v-if="item.show" |
|
||||
></u-icon> |
|
||||
<u-icon |
|
||||
@click="openSubProject(item.sonProjectList.length, index)" |
|
||||
class="text-gray-400" |
|
||||
name="arrow-down" |
|
||||
size="14px" |
|
||||
v-else |
|
||||
></u-icon> |
|
||||
</view> |
|
||||
<u-icon @click="openProject(item)" class="text-gray-400" name="arrow-right" size="14px" v-else></u-icon> |
|
||||
</view> |
|
||||
|
|
||||
<!-- 子项目 --> |
|
||||
<view class="ml-8" v-if="item.show"> |
|
||||
<view |
|
||||
:id="'cu-' + index + '-' + subIndex" |
|
||||
:key="subIndex" |
|
||||
@touchend.stop.prevent="stops($event, index + '-' + subIndex, item.sonProjectList.length)" |
|
||||
@touchmove.stop.prevent="move($event, item.sonProjectList.length)" |
|
||||
@longpress.stop.prevent="start($event, index + '-' + subIndex)" |
|
||||
class="cu-item flex-col" |
|
||||
v-for="(subItem, subIndex) in item.sonProjectList" |
|
||||
> |
|
||||
<!-- <view :key="subItem.id" v-for="subItem in item.sonProjectList"> --> |
|
||||
<view class="flex items-center justify-between p-3 w-full"> |
|
||||
<u-icon @click="openMenu(subItem)" class="mover" name="https://www.tall.wiki/staticrec/drag.svg" size="48"></u-icon> |
|
||||
|
|
||||
<view @click="openProject(subItem)" class="flex-1 px-3"> |
|
||||
<view class="flex items-center"> |
|
||||
<view class="mr-2">{{ subItem.name }}</view> |
|
||||
<!-- 状态 --> |
|
||||
<view |
|
||||
:class=" |
|
||||
subItem.status === 0 |
|
||||
? 'text-blue-400 bg-blue-100' |
|
||||
: subItem.status === 1 |
|
||||
? 'text-green-400 bg-green-100' |
|
||||
: subItem.status === 2 |
|
||||
? 'text-red-400 bg-red-100' |
|
||||
: 'text-gray-400 bg-gray-100' |
|
||||
" |
|
||||
class="px-2 text-xs text-gray-400 bg-gray-100 rounded-full flex-shrink-0" |
|
||||
> |
|
||||
{{ subItem.status === 0 ? '未开始' : subItem.status === 1 ? '进行中' : subItem.status === 2 ? '暂停' : '已完成' }} |
|
||||
</view> |
|
||||
</view> |
|
||||
</view> |
|
||||
|
|
||||
<!-- 箭头 --> |
|
||||
<u-icon @click="openProject(subItem)" class="text-gray-400" name="arrow-right" size="14px"></u-icon> |
|
||||
</view> |
|
||||
</view> |
|
||||
</view> |
|
||||
</view> |
|
||||
|
|
||||
<view class="border-100 bg-blue-500" v-if="item.showBorder"></view> |
|
||||
<view class="border-80 bg-blue-500" v-if="item.showSubBorder"></view> |
|
||||
</view> |
|
||||
</view> |
|
||||
</scroll-view> |
|
||||
|
|
||||
<!-- 移动悬浮 begin --> |
|
||||
<view v-if="data.showMoveImage"> |
|
||||
<view :style="{ left: data.moveLeft + 'px', top: data.moveTop + 'px' }" class="cu-item absolute"> |
|
||||
<ProjectItem class="w-full" :item="data.moveItem" /> |
|
||||
</view> |
|
||||
</view> |
|
||||
<!-- 移动悬浮 end --> |
|
||||
|
|
||||
<!-- 项目操作面板 --> |
|
||||
<u-action-sheet :list="data.menuList" :tips="data.tips" @click="chooseAction" v-model="data.showMenu" :cancel-btn="false"></u-action-sheet> |
|
||||
</view> |
|
||||
</template> |
|
||||
|
|
||||
<script setup> |
|
||||
import { reactive, onMounted, watchEffect, computed } from 'vue'; |
|
||||
import { useStore } from 'vuex'; |
|
||||
import dayjs from 'dayjs'; |
|
||||
import ProjectItem from '@/components/Projects/ProjectItem.vue'; |
|
||||
|
|
||||
const store = useStore(); |
|
||||
const projects = computed(() => store.state.project.projects); |
|
||||
const remindData = computed(() => store.state.socket.remindData); |
|
||||
const userId = computed(() => store.getters['user/userId']); |
|
||||
const data = reactive({ |
|
||||
// itemTop: 0, |
|
||||
// itemLeft: 0, |
|
||||
itemHeight: 0, // 移动元素的高度 |
|
||||
itemWidth: 0, // 移动元素的宽度 |
|
||||
subItemHeight: 0, // 移动子元素的高度 |
|
||||
showMoveImage: false, |
|
||||
moveItem: '', // 当前选中的项目信息 |
|
||||
moveLeft: 0, // 当前选中项目距左侧的距离 |
|
||||
moveTop: 0, // 当前选中项目距顶部的距离 |
|
||||
deltaLeft: 0, |
|
||||
deltaTop: 0, |
|
||||
beginleft: 0, // 项目列表中第一个项目初始时距离左侧的距离 |
|
||||
begintop: 0, // 项目列表中第一个项目初始时距离顶部的距离 |
|
||||
itemList: [], // 项目列表 |
|
||||
setSubItem: false, // 选中的是否是二级项目 |
|
||||
changeEvent: false, // 是否点击过操作面板 |
|
||||
showMenu: false, |
|
||||
tips: { text: '', color: '#909399', fontSize: 28 }, |
|
||||
clickProject: {}, |
|
||||
projectId: 0, |
|
||||
// menuList: [{ text: '复制' }, { text: '编辑' }, { text: '删除' }, { text: '置顶' }, { text: '排序' }], |
|
||||
menuList: [{ text: '导入' }, { text: '导出' }, { text: '删除' }], |
|
||||
|
|
||||
// show: false, |
|
||||
// border: 'border border-blue-500 shadow rounded-md', |
|
||||
// showBorder: false, // 一级项目底部边框是否显示 |
|
||||
showItemIndex: undefined, |
|
||||
isStartMove: 0, // 是否可以移动 |
|
||||
}); |
|
||||
|
|
||||
const emit = defineEmits(['changeHeight', 'change']); |
|
||||
|
|
||||
// 监听项目列表 |
|
||||
watchEffect(() => { |
|
||||
if (projects.value) { |
|
||||
data.itemList = projects.value; |
|
||||
data.itemList.forEach(item => { |
|
||||
item.showBorder = false; // 一级项目移动位置底部边框是否显示 |
|
||||
item.showSubBorder = false; // 二级项目移动位置底部边框是否显示 |
|
||||
item.showTopBorder = false; // 一级项目移动位置顶部边框是否显示 |
|
||||
item.remindNum = 0; |
|
||||
|
|
||||
if (remindData.value) { |
|
||||
remindData.value.forEach(remind => { |
|
||||
const remind_data = JSON.parse(remind.data); |
|
||||
if (remind_data.data.projectId === item.id) { |
|
||||
item.remindNum++; |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
onMounted(() => { |
|
||||
data.itemList = projects.value; |
|
||||
data.itemList.forEach(item => { |
|
||||
item.showBorder = false; // 一级项目底部边框是否显示 |
|
||||
item.showSubBorder = false; // 一级项目底部边框是否显示 |
|
||||
item.showTopBorder = false; // 一级项目顶部边框是否显示 |
|
||||
}); |
|
||||
}); |
|
||||
|
|
||||
// 展开子项目 |
|
||||
function openSubProject(length, index) { |
|
||||
store.commit('project/setProjectItemShow', { index, show: !data.itemList[index].show }); |
|
||||
if (length && index) { |
|
||||
emit('changeHeight', length, index); |
|
||||
} |
|
||||
data.showItemIndex = index; |
|
||||
} |
|
||||
|
|
||||
// 获取项目列表距离顶部的距离 |
|
||||
function getDate() { |
|
||||
const query = uni |
|
||||
.createSelectorQuery() |
|
||||
.select('#cu-0') |
|
||||
.fields( |
|
||||
{ |
|
||||
id: true, |
|
||||
dataset: true, |
|
||||
rect: true, |
|
||||
size: true, |
|
||||
}, |
|
||||
res => { |
|
||||
data.begintop = res.top; |
|
||||
data.beginleft = res.left; |
|
||||
}, |
|
||||
) |
|
||||
.exec(); |
|
||||
} |
|
||||
|
|
||||
// function setData(flag, project, tips) { |
|
||||
// data.showMenu = flag; |
|
||||
// data.projectId = project.id; |
|
||||
// data.tips = tips; |
|
||||
// data.clickProject = project; |
|
||||
// } |
|
||||
|
|
||||
function chooseAction(e) { |
|
||||
const obj = { |
|
||||
index: e, |
|
||||
projectId: data.projectId, |
|
||||
url: data.clickProject.url, |
|
||||
projectName: data.tips.text |
|
||||
}; |
|
||||
// emit('chooseAction', data); |
|
||||
actionFun(obj); |
|
||||
} |
|
||||
|
|
||||
// 操作 |
|
||||
function actionFun(obj) { |
|
||||
const action = data.menuList[obj.index].text; |
|
||||
// if (action === '排序') { |
|
||||
// data.changeEvent = true; |
|
||||
// uni.$ui.showToast('请移动进行排序'); |
|
||||
// } |
|
||||
|
|
||||
if (action === '删除') { |
|
||||
// data.changeEvent = false; |
|
||||
delProject(obj.projectId, obj.url); |
|
||||
} |
|
||||
|
|
||||
if (action === '导入') { |
|
||||
// data.changeEvent = false; |
|
||||
store.commit('setDomain', obj.url); |
|
||||
importProject(obj.projectId, obj.projectName); |
|
||||
} |
|
||||
|
|
||||
if (action === '导出') { |
|
||||
// data.changeEvent = false; |
|
||||
exportProject(obj.projectId, obj.url); |
|
||||
} |
|
||||
|
|
||||
if (data.showItemIndex !== undefined) { |
|
||||
store.commit('project/setProjectItemShow', { |
|
||||
index: data.showItemIndex, |
|
||||
show: true, |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
function isNumber(val) { |
|
||||
return val === +val; |
|
||||
} |
|
||||
|
|
||||
function start(e, index) { |
|
||||
console.log('开始', e); |
|
||||
data.isStartMove = 1; |
|
||||
|
|
||||
setTimeout(() => { |
|
||||
getDate(); |
|
||||
}, 300); |
|
||||
|
|
||||
if (isNumber(index)) { |
|
||||
// 选中一级项目 |
|
||||
data.setSubItem = false; |
|
||||
const query = uni |
|
||||
.createSelectorQuery() |
|
||||
.select(`#cu-${index}`) |
|
||||
.fields( |
|
||||
{ |
|
||||
id: true, |
|
||||
dataset: true, |
|
||||
rect: true, |
|
||||
size: true, |
|
||||
}, |
|
||||
res => { |
|
||||
data.moveTop = res.top; |
|
||||
data.moveLeft = res.left; |
|
||||
data.moveItem = data.itemList[index]; |
|
||||
data.itemWidth = res.width; |
|
||||
data.itemHeight = res.height; |
|
||||
}, |
|
||||
) |
|
||||
.exec(); |
|
||||
} else { |
|
||||
// 选中二级项目 |
|
||||
const arr = index.split('-'); |
|
||||
data.setSubItem = true; |
|
||||
|
|
||||
const query = uni.createSelectorQuery(); |
|
||||
query |
|
||||
.select(`#cu-${arr[0] - 0}`) |
|
||||
.fields( |
|
||||
{ |
|
||||
id: true, |
|
||||
dataset: true, |
|
||||
rect: true, |
|
||||
size: true, |
|
||||
}, |
|
||||
res => { |
|
||||
data.itemHeight = res.height; |
|
||||
}, |
|
||||
) |
|
||||
.exec(); |
|
||||
|
|
||||
query |
|
||||
.select(`#cu-${index}`) |
|
||||
.fields( |
|
||||
{ |
|
||||
id: true, |
|
||||
dataset: true, |
|
||||
rect: true, |
|
||||
size: true, |
|
||||
}, |
|
||||
res => { |
|
||||
console.log('res', res) |
|
||||
if (res) { |
|
||||
data.moveTop = res.top; |
|
||||
data.moveLeft = res.left; |
|
||||
data.itemWidth = res.width; |
|
||||
data.subItemHeight = res.height; |
|
||||
} |
|
||||
|
|
||||
data.moveItem = data.itemList[arr[0] - 0].sonProjectList[arr[1] - 0]; |
|
||||
}, |
|
||||
) |
|
||||
.exec(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
function move(e, length) { |
|
||||
if (!data.isStartMove) return false; |
|
||||
console.log('移动'); |
|
||||
data.showMoveImage = true; // 悬浮开始 |
|
||||
const touch = e.touches[0]; |
|
||||
if (data.deltaLeft == 0) { |
|
||||
// 获得本身的移动 |
|
||||
data.deltaLeft = touch.pageX - data.moveLeft; |
|
||||
data.deltaTop = touch.pageY - data.moveTop; |
|
||||
} |
|
||||
data.moveLeft = touch.pageX - data.deltaLeft; |
|
||||
data.moveTop = touch.pageY - data.deltaTop; |
|
||||
|
|
||||
const lastIndex = findOverIndex(touch.pageY, length); |
|
||||
// 显示下划线 |
|
||||
for (let i = 0; i < data.itemList.length; i++) { |
|
||||
if (data.moveLeft > 35) { |
|
||||
data.itemList[i].showBorder = false; |
|
||||
data.itemList[i].showTopBorder = false; |
|
||||
if (i === lastIndex) { |
|
||||
data.itemList[i].showSubBorder = true; |
|
||||
} else { |
|
||||
data.itemList[i].showSubBorder = false; |
|
||||
} |
|
||||
} else if (lastIndex === -1) { |
|
||||
data.itemList[0].showTopBorder = true; |
|
||||
data.itemList[i].showSubBorder = false; |
|
||||
data.itemList[i].showBorder = false; |
|
||||
} else { |
|
||||
data.itemList[i].showSubBorder = false; |
|
||||
data.itemList[i].showTopBorder = false; |
|
||||
if (i === lastIndex) { |
|
||||
data.itemList[i].showBorder = true; |
|
||||
} else { |
|
||||
data.itemList[i].showBorder = false; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
function stops(e, index, length) { |
|
||||
console.log('结束', e, index); |
|
||||
data.isStartMove = 0; |
|
||||
|
|
||||
const touch = e.changedTouches[0]; |
|
||||
const lastIndex = findOverIndex(touch.pageY, length); |
|
||||
|
|
||||
// 交换两个值 |
|
||||
for (let i = 0; i < data.itemList.length; i++) { |
|
||||
// 插入顶部 |
|
||||
if (data.itemList[i].showTopBorder) { |
|
||||
if (isNumber(index)) { |
|
||||
const Value = data.itemList[index]; |
|
||||
data.itemList.unshift(Value); |
|
||||
data.itemList.splice(index + 1, 1); |
|
||||
} else { |
|
||||
const arr = index.split('-'); |
|
||||
const Value = data.itemList[arr[0] - 0].sonProjectList[arr[1] - 0]; |
|
||||
data.itemList.unshift(Value); |
|
||||
data.itemList[arr[0] - 0].sonProjectList.splice([arr[1] - 0], 1); |
|
||||
// const options = { |
|
||||
// id: Value.id, |
|
||||
// parentId: 0, |
|
||||
// }; |
|
||||
const options = { |
|
||||
businessCode: Value.businessCode, |
|
||||
moveProjectId: Value.id, |
|
||||
targetProjectId: '', |
|
||||
}; |
|
||||
emit('change', options); |
|
||||
} |
|
||||
|
|
||||
// 清空 |
|
||||
clearSet(i); |
|
||||
emit('change', data.itemList); |
|
||||
return; |
|
||||
} |
|
||||
// 插入一级项目 |
|
||||
if (data.itemList[i].showBorder) { |
|
||||
if (isNumber(index)) { |
|
||||
const Value = data.itemList[index]; |
|
||||
data.itemList.splice(i + 1, 0, Value); |
|
||||
if (i < index) { |
|
||||
data.itemList.splice(index + 1, 1); |
|
||||
} else { |
|
||||
data.itemList.splice(index, 1); |
|
||||
} |
|
||||
} else { |
|
||||
const arr = index.split('-'); |
|
||||
const Value = data.itemList[arr[0] - 0].sonProjectList[arr[1] - 0]; |
|
||||
data.itemList[arr[0] - 0].sonProjectList.splice([arr[1] - 0], 1); |
|
||||
data.itemList.splice(i + 1, 0, Value); |
|
||||
// const options = { |
|
||||
// id: Value.id, |
|
||||
// parentId: 0, |
|
||||
// }; |
|
||||
const options = { |
|
||||
businessCode: Value.businessCode, |
|
||||
moveProjectId: Value.id, |
|
||||
targetProjectId: '', |
|
||||
}; |
|
||||
emit('change', options); |
|
||||
} |
|
||||
|
|
||||
// 清空 |
|
||||
clearSet(i); |
|
||||
emit('change', data.itemList); |
|
||||
return; |
|
||||
} |
|
||||
// 插入二级项目 |
|
||||
if (data.itemList[i].showSubBorder) { |
|
||||
if (isNumber(index)) { |
|
||||
const Value = data.itemList[index]; |
|
||||
if (data.itemList[lastIndex - 1].sonProjectList && data.itemList[lastIndex - 1].sonProjectList.length) { |
|
||||
data.itemList[lastIndex - 1].sonProjectList.push(Value); |
|
||||
} else { |
|
||||
data.itemList[lastIndex].sonProjectList = [Value]; |
|
||||
} |
|
||||
data.itemList.splice(index, 1); |
|
||||
// 清空 |
|
||||
clearSet(i); |
|
||||
// const options = { |
|
||||
// id: Value.id, |
|
||||
// parentId: data.itemList[lastIndex - 1].id, |
|
||||
// }; |
|
||||
const options = { |
|
||||
businessCode: Value.businessCode, |
|
||||
moveProjectId: Value.id, |
|
||||
targetProjectId: data.itemList[lastIndex - 1].id, |
|
||||
}; |
|
||||
emit('change', options); |
|
||||
} else { |
|
||||
const arr = index.split('-'); |
|
||||
const Value = data.itemList[arr[0] - 0].sonProjectList[arr[1] - 0]; |
|
||||
if (data.itemList[lastIndex].sonProjectList && data.itemList[lastIndex].sonProjectList.length) { |
|
||||
data.itemList[lastIndex].sonProjectList.push(Value); |
|
||||
} else { |
|
||||
data.itemList[lastIndex].sonProjectList = [Value]; |
|
||||
} |
|
||||
data.itemList[arr[0] - 0].sonProjectList.splice([arr[1] - 0], 1); |
|
||||
// 清空 |
|
||||
clearSet(i); |
|
||||
// const options = { |
|
||||
// id: Value.id, |
|
||||
// parentId: data.itemList[lastIndex].id, |
|
||||
// }; |
|
||||
const options = { |
|
||||
businessCode: Value.businessCode, |
|
||||
moveProjectId: Value.id, |
|
||||
targetProjectId: data.itemList[lastIndex].id, |
|
||||
}; |
|
||||
emit('change', options); |
|
||||
|
|
||||
// const options1 = { |
|
||||
// id: Value.id, |
|
||||
// parentId: 0, |
|
||||
// }; |
|
||||
const options1 = { |
|
||||
businessCode: Value.businessCode, |
|
||||
moveProjectId: Value.id, |
|
||||
targetProjectId: '', |
|
||||
}; |
|
||||
emit('change', options1); |
|
||||
} |
|
||||
return; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 还原初始数据 |
|
||||
function clearSet(i) { |
|
||||
if (i < data.itemList.length) { |
|
||||
data.itemList[i].showBorder = false; |
|
||||
data.itemList[i].showSubBorder = false; |
|
||||
data.itemList[i].showTopBorder = false; |
|
||||
} |
|
||||
|
|
||||
data.deltaLeft == 0; |
|
||||
data.showMoveImage = false; |
|
||||
data.setSubItem = false; |
|
||||
// data.changeEvent = false; |
|
||||
data.showItemIndex = undefined; |
|
||||
} |
|
||||
|
|
||||
// 找到停下的元素的下标 |
|
||||
function findOverIndex(posY) { |
|
||||
// 如果有子项目展开着 |
|
||||
const leng = data.itemList.length * data.itemHeight; // 最后一个元素距离顶部的距离 |
|
||||
if (posY < data.begintop) { |
|
||||
return -1; |
|
||||
} |
|
||||
for (let i = 0; i < data.itemList.length; i++) { |
|
||||
const begin = data.itemHeight * i + data.begintop; |
|
||||
const end = data.itemHeight * i + data.begintop + data.itemHeight; |
|
||||
if (begin <= posY && end >= posY) { |
|
||||
return i; |
|
||||
} |
|
||||
} |
|
||||
if (posY > leng) { |
|
||||
// 交换最后一个 |
|
||||
return data.itemList.length - 1; |
|
||||
} |
|
||||
if (posY < data.begintop) { |
|
||||
return 0; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 删除项目 |
|
||||
function delProject(id, url) { |
|
||||
uni.showModal({ |
|
||||
title: '', |
|
||||
content: '是否删除项目?', |
|
||||
showCancel: true, |
|
||||
success: async ({ confirm }) => { |
|
||||
if (confirm) { |
|
||||
await uni.$u.api.delProject(id, url); |
|
||||
let flag_index = 0; |
|
||||
data.itemList.forEach((item, index) => { |
|
||||
if (item.id == id) { |
|
||||
flag_index = index; |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
data.itemList.splice(flag_index, 1); |
|
||||
store.commit('project/setProjects', data.itemList); |
|
||||
} |
|
||||
}, |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
// 导入 |
|
||||
function importProject(id, name) { |
|
||||
uni.showModal({ |
|
||||
content: '是否导入到' + name, |
|
||||
showCancel: true, |
|
||||
success: async ({ confirm }) => { |
|
||||
if (confirm) { |
|
||||
try { |
|
||||
const res = await uni.$u.api.import(id); |
|
||||
// 导入WBS成功后 |
|
||||
// 直接打开导入的项目 |
|
||||
emit('success'); |
|
||||
// const { apiUrl } = Config; |
|
||||
// let defaultwbs = `${apiUrl}/defaultwbs`; |
|
||||
// res.url && (defaultwbs = res.url); |
|
||||
|
|
||||
store.commit('project/setIsRefresh', 1); |
|
||||
|
|
||||
setTimeout(() => { |
|
||||
uni.navigateTo({ url: `/pages/project/project?u=${user.value.id}&p=${res.id}&pname=${res.name}&url=${encodeURIComponent(res.url)}` }); |
|
||||
}, 2000); |
|
||||
} catch (error) { |
|
||||
console.error('error: ', error); |
|
||||
emit('error', error); |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
// 导出 |
|
||||
function exportProject(id, url) { |
|
||||
uni.showModal({ |
|
||||
title: '', |
|
||||
content: '是否导出项目?', |
|
||||
showCancel: true, |
|
||||
success: async ({ confirm }) => { |
|
||||
if (confirm) { |
|
||||
const data = await uni.$u.post(`${url}/tall/project/exportWbs`, { projectId: id }); |
|
||||
// #ifdef H5 |
|
||||
window.location.href = data.url; |
|
||||
// #endif |
|
||||
|
|
||||
// #ifdef APP-PLUS |
|
||||
uni.downloadFile({ |
|
||||
url: data.url, //仅为示例,并非真实的资源 |
|
||||
success: ({statusCode, tempFilePath}) => { |
|
||||
if (statusCode === 200) { |
|
||||
console.log('下载成功', tempFilePath); |
|
||||
|
|
||||
uni.saveFile({ |
|
||||
tempFilePath, |
|
||||
success:(res)=>{ |
|
||||
uni.$ui.showToast('文件保存路径:' + res.savedFilePath); |
|
||||
//res.savedFilePath文件的保存路径 |
|
||||
//保存成功并打开文件 |
|
||||
// uni.openDocument({ |
|
||||
// filePath: res.savedFilePath, |
|
||||
// success:(res)=>console.log('成功打开文档') |
|
||||
// }) |
|
||||
}, |
|
||||
fail:()=>console.log('下载失败') |
|
||||
}) |
|
||||
} |
|
||||
} |
|
||||
}); |
|
||||
// #endif |
|
||||
} |
|
||||
}, |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
// 跳转工作台 |
|
||||
function toWorkbench() { |
|
||||
uni.navigateTo({ url: '/pages/workbench/workbench' }); |
|
||||
} |
|
||||
|
|
||||
// 打开项目详情 |
|
||||
function openProject(project) { |
|
||||
store.commit('task/clearTasks'); // 清空定期任务 |
|
||||
store.commit('task/clearRealTasks'); // 清空真实任务数据 |
|
||||
store.commit('socket/setCurrLocationTaskId', ''); |
|
||||
store.commit('task/setAllTasks', []); // 清空定期任务 |
|
||||
store.commit('task/setUpNextPage', 1); |
|
||||
store.commit('task/setDownNextPage', 1); |
|
||||
store.commit('task/setTimeLineType', 1); |
|
||||
const { name, id, url, businessCode } = project; |
|
||||
uni.navigateTo({ url: `/pages/project/project?u=${userId.value}&p=${id}&pname=${name}&url=${encodeURIComponent(url)}&businessCode=${businessCode}` }); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 弹出项目操作面板 |
|
||||
*/ |
|
||||
function openMenu(project, index) { |
|
||||
if (index === 0) return; |
|
||||
data.showMenu = true; |
|
||||
data.projectId = project.id; |
|
||||
data.tips.text = project.name; |
|
||||
data.clickProject = project; |
|
||||
|
|
||||
// emit('setData', data.showMenu, project, data.tips); |
|
||||
} |
|
||||
</script> |
|
||||
|
|
||||
<style lang="scss" scoped> |
|
||||
.cu-item { |
|
||||
width: 100%; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
font-size: 14px; |
|
||||
} |
|
||||
|
|
||||
.border-100 { |
|
||||
height: 4rpx; |
|
||||
margin: 0 20rpx; |
|
||||
} |
|
||||
|
|
||||
.border-80 { |
|
||||
height: 4rpx; |
|
||||
margin: 0 20rpx 0 90rpx; |
|
||||
} |
|
||||
|
|
||||
.workbench-btn { |
|
||||
margin-right: 10px; |
|
||||
width: 80px; |
|
||||
height: 30px; |
|
||||
line-height: 30px; |
|
||||
border-radius: 15px; |
|
||||
overflow: hidden; |
|
||||
border: 1px solid #2b85e4; |
|
||||
background-color: #1890ff; |
|
||||
font-size: 12px; |
|
||||
color: #ffffff; |
|
||||
text-align: center; |
|
||||
} |
|
||||
|
|
||||
.remind-box { |
|
||||
padding: 0 3px; |
|
||||
min-width: 16px; |
|
||||
height: 16px; |
|
||||
text-align: center; |
|
||||
line-height: 16px; |
|
||||
border-radius: 8px; |
|
||||
font-weight: 100; |
|
||||
} |
|
||||
</style> |
|
@ -1,693 +0,0 @@ |
|||||
<template> |
|
||||
<view> |
|
||||
<scroll-view scroll-y="true"> |
|
||||
<view v-if="!data.changeEvent"> |
|
||||
<view :id="'cu-' + index" :key="item.id" class="cu-item flex-col" v-for="(item, index) in data.itemList"> |
|
||||
<ProjectItem |
|
||||
class="w-full" |
|
||||
:index="index" |
|
||||
:item="item" |
|
||||
:menuList="data.menuList" |
|
||||
@setData="setData" |
|
||||
@openSubProject="openSubProject" |
|
||||
/> |
|
||||
</view> |
|
||||
</view> |
|
||||
|
|
||||
<!-- 点击排序之后的效果 --> |
|
||||
<view v-else> |
|
||||
<view |
|
||||
:id="'cu-' + index" |
|
||||
:key="index" |
|
||||
:style="{ 'background-color': item.color }" |
|
||||
@touchend="stops($event, index)" |
|
||||
@touchmove.stop.prevent="move" |
|
||||
@touchstart="start($event, index)" |
|
||||
class="cu-item flex-col" |
|
||||
v-for="(item, index) in data.itemList" |
|
||||
> |
|
||||
<!-- <view class="border-100 bg-blue-500" v-if="item.showTopBorder"></view> --> |
|
||||
|
|
||||
<!-- 内容区 --> |
|
||||
<!-- 父项目 --> |
|
||||
<view class="w-full"> |
|
||||
<view class="flex items-center justify-between p-3"> |
|
||||
<u-icon class="mover" name="https://www.tall.wiki/staticrec/drag.svg" size="48"></u-icon> |
|
||||
|
|
||||
<view class="flex-1 px-3"> |
|
||||
<view class="flex items-center mb-1"> |
|
||||
<view class="mr-2">{{ item.name }}</view> |
|
||||
<!-- 状态 TODO:--> |
|
||||
<view class="px-2 text-xs text-green-400 bg-green-100 rounded-full flex-shrink-0">进行中</view> |
|
||||
</view> |
|
||||
|
|
||||
<view class="flex items-center text-xs text-gray-400"> |
|
||||
<view class="pr-2">{{ dayjs(+item.startTime).format('MM-DD HH:mm') }}</view> |
|
||||
至 |
|
||||
<view class="pl-2">{{ dayjs(+item.endTime).format('MM-DD HH:mm') }}</view> |
|
||||
</view> |
|
||||
</view> |
|
||||
|
|
||||
<!-- <view class="workbench-btn" v-if="index === 0" @click="toWorkbench">工作台</view> --> |
|
||||
|
|
||||
<!-- 箭头 --> |
|
||||
<view v-if="item.sonProjectList && item.sonProjectList.length"> |
|
||||
<u-icon |
|
||||
@click="openSubProject(item.sonProjectList.length, index)" |
|
||||
class="text-gray-400" |
|
||||
name="arrow-up" |
|
||||
size="14px" |
|
||||
v-if="item.show" |
|
||||
></u-icon> |
|
||||
<u-icon |
|
||||
@click="openSubProject(item.sonProjectList.length, index)" |
|
||||
class="text-gray-400" |
|
||||
name="arrow-down" |
|
||||
size="14px" |
|
||||
v-else |
|
||||
></u-icon> |
|
||||
</view> |
|
||||
<u-icon class="text-gray-400" name="arrow-right" size="14px" v-else></u-icon> |
|
||||
</view> |
|
||||
<!-- 父项目 end --> |
|
||||
|
|
||||
<!-- 子项目 --> |
|
||||
<view class="ml-8" v-if="item.show"> |
|
||||
<view |
|
||||
:id="'cu-' + index + '-' + subIndex" |
|
||||
:key="subIndex" |
|
||||
@touchend.stop.prevent="stops($event, index + '-' + subIndex, item.sonProjectList.length)" |
|
||||
@touchmove.stop.prevent="move($event, item.sonProjectList.length)" |
|
||||
@touchstart.stop.prevent="start($event, index + '-' + subIndex)" |
|
||||
class="cu-item flex-col" |
|
||||
v-for="(subItem, subIndex) in item.sonProjectList" |
|
||||
> |
|
||||
<view class="flex items-center justify-between p-3 w-full"> |
|
||||
<u-icon class="mover" name="https://www.tall.wiki/staticrec/drag.svg" size="48"></u-icon> |
|
||||
|
|
||||
<view class="flex-1 px-3"> |
|
||||
<view class="flex items-center"> |
|
||||
<view class="mr-2">{{ subItem.name }}</view> |
|
||||
<!-- 状态 --> |
|
||||
<view |
|
||||
:class=" |
|
||||
subItem.status === 0 |
|
||||
? 'text-blue-400 bg-blue-100' |
|
||||
: subItem.status === 1 |
|
||||
? 'text-green-400 bg-green-100' |
|
||||
: subItem.status === 2 |
|
||||
? 'text-red-400 bg-red-100' |
|
||||
: 'text-gray-400 bg-gray-100' |
|
||||
" |
|
||||
class="px-2 text-xs text-gray-400 bg-gray-100 rounded-full flex-shrink-0" |
|
||||
> |
|
||||
{{ subItem.status === 0 ? '未开始' : subItem.status === 1 ? '进行中' : subItem.status === 2 ? '暂停' : '已完成' }} |
|
||||
</view> |
|
||||
</view> |
|
||||
</view> |
|
||||
|
|
||||
<!-- 箭头 --> |
|
||||
<u-icon class="text-gray-400" name="arrow-right" size="14px"></u-icon> |
|
||||
</view> |
|
||||
</view> |
|
||||
</view> |
|
||||
</view> |
|
||||
<!-- 内容区 end --> |
|
||||
|
|
||||
<view class="border-100 bg-blue-500" v-if="item.showBorder"></view> |
|
||||
<view class="border-80 bg-blue-500" v-if="item.showSubBorder"></view> |
|
||||
</view> |
|
||||
</view> |
|
||||
</scroll-view> |
|
||||
|
|
||||
<!-- 移动悬浮 begin --> |
|
||||
<view v-if="data.showMoveImage"> |
|
||||
<view :style="{ left: data.moveLeft + 'px', top: data.moveTop + 'px' }" class="cu-item absolute"> |
|
||||
<ProjectItem class="w-full" :item="data.moveItem" /> |
|
||||
</view> |
|
||||
</view> |
|
||||
<!-- 移动悬浮 end --> |
|
||||
|
|
||||
<!-- 项目操作面板 --> |
|
||||
<u-action-sheet :list="data.menuList" :tips="data.tips" @click="chooseAction" v-model="data.showMenu"></u-action-sheet> |
|
||||
</view> |
|
||||
</template> |
|
||||
|
|
||||
<script setup> |
|
||||
import { reactive, onMounted, watchEffect, computed } from 'vue'; |
|
||||
import { useStore } from 'vuex'; |
|
||||
import dayjs from 'dayjs'; |
|
||||
import ProjectItem from '@/components/Projects/ProjectItem.vue'; |
|
||||
|
|
||||
const store = useStore(); |
|
||||
const projects = computed(() => store.state.project.projects); |
|
||||
const remindData = computed(() => store.state.socket.remindData); |
|
||||
const data = reactive({ |
|
||||
// itemTop: 0, |
|
||||
// itemLeft: 0, |
|
||||
itemHeight: 0, // 移动元素的高度 |
|
||||
itemWidth: 0, // 移动元素的宽度 |
|
||||
subItemHeight: 0, // 移动子元素的高度 |
|
||||
showMoveImage: false, |
|
||||
moveItem: '', // 当前选中的项目信息 |
|
||||
moveLeft: 0, // 当前选中项目距左侧的距离 |
|
||||
moveTop: 0, // 当前选中项目距顶部的距离 |
|
||||
deltaLeft: 0, |
|
||||
deltaTop: 0, |
|
||||
beginleft: 0, // 项目列表中第一个项目初始时距离左侧的距离 |
|
||||
begintop: 0, // 项目列表中第一个项目初始时距离顶部的距离 |
|
||||
itemList: [], // 项目列表 |
|
||||
setSubItem: false, // 选中的是否是二级项目 |
|
||||
changeEvent: false, // 是否点击过操作面板 |
|
||||
showMenu: false, |
|
||||
tips: { text: '', color: '#909399', fontSize: 28 }, |
|
||||
clickProject: {}, |
|
||||
projectId: 0, |
|
||||
// menuList: [{ text: '复制' }, { text: '编辑' }, { text: '删除' }, { text: '置顶' }, { text: '排序' }], |
|
||||
menuList: [{ text: '导入' }, { text: '导出' }, { text: '删除' }, { text: '排序' }], |
|
||||
|
|
||||
// show: false, |
|
||||
// border: 'border border-blue-500 shadow rounded-md', |
|
||||
// showBorder: false, // 一级项目底部边框是否显示 |
|
||||
showItemIndex: undefined, |
|
||||
}); |
|
||||
|
|
||||
const emit = defineEmits(['changeHeight', 'change']); |
|
||||
|
|
||||
// 监听项目列表 |
|
||||
watchEffect(() => { |
|
||||
if (projects.value) { |
|
||||
data.itemList = projects.value; |
|
||||
data.itemList.forEach(item => { |
|
||||
item.showBorder = false; // 一级项目移动位置底部边框是否显示 |
|
||||
item.showSubBorder = false; // 二级项目移动位置底部边框是否显示 |
|
||||
item.showTopBorder = false; // 一级项目移动位置顶部边框是否显示 |
|
||||
item.remindNum = 0; |
|
||||
|
|
||||
if (remindData.value) { |
|
||||
remindData.value.forEach(remind => { |
|
||||
const remind_data = JSON.parse(remind.data); |
|
||||
if (remind_data.data.projectId === item.id) { |
|
||||
item.remindNum++; |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
onMounted(() => { |
|
||||
data.itemList = projects.value; |
|
||||
data.itemList.forEach(item => { |
|
||||
item.showBorder = false; // 一级项目底部边框是否显示 |
|
||||
item.showSubBorder = false; // 一级项目底部边框是否显示 |
|
||||
item.showTopBorder = false; // 一级项目顶部边框是否显示 |
|
||||
}); |
|
||||
}); |
|
||||
|
|
||||
// 展开子项目 |
|
||||
function openSubProject(length, index) { |
|
||||
store.commit('project/setProjectItemShow', { index, show: !data.itemList[index].show }); |
|
||||
if (length && index) { |
|
||||
emit('changeHeight', length, index); |
|
||||
} |
|
||||
data.showItemIndex = index; |
|
||||
} |
|
||||
|
|
||||
// 获取项目列表距离顶部的距离 |
|
||||
function getDate() { |
|
||||
const query = uni |
|
||||
.createSelectorQuery() |
|
||||
.select('#cu-0') |
|
||||
.fields( |
|
||||
{ |
|
||||
id: true, |
|
||||
dataset: true, |
|
||||
rect: true, |
|
||||
size: true, |
|
||||
}, |
|
||||
res => { |
|
||||
data.begintop = res.top; |
|
||||
data.beginleft = res.left; |
|
||||
}, |
|
||||
) |
|
||||
.exec(); |
|
||||
} |
|
||||
|
|
||||
function setData(flag, project, tips) { |
|
||||
data.showMenu = flag; |
|
||||
data.projectId = project.id; |
|
||||
data.tips = tips; |
|
||||
data.clickProject = project; |
|
||||
} |
|
||||
|
|
||||
function chooseAction(e) { |
|
||||
const obj = { |
|
||||
index: e, |
|
||||
projectId: data.projectId, |
|
||||
url: data.clickProject.url, |
|
||||
projectName: data.tips.text |
|
||||
}; |
|
||||
// emit('chooseAction', data); |
|
||||
actionFun(obj); |
|
||||
} |
|
||||
|
|
||||
// 操作 |
|
||||
function actionFun(obj) { |
|
||||
const action = data.menuList[obj.index].text; |
|
||||
if (action === '排序') { |
|
||||
data.changeEvent = true; |
|
||||
uni.$ui.showToast('请移动进行排序'); |
|
||||
} |
|
||||
|
|
||||
if (action === '删除') { |
|
||||
data.changeEvent = false; |
|
||||
delProject(obj.projectId, obj.url); |
|
||||
} |
|
||||
|
|
||||
if (action === '导入') { |
|
||||
data.changeEvent = false; |
|
||||
store.commit('setDomain', obj.url); |
|
||||
importProject(obj.projectId, obj.projectName); |
|
||||
} |
|
||||
|
|
||||
if (action === '导出') { |
|
||||
data.changeEvent = false; |
|
||||
exportProject(obj.projectId, obj.url); |
|
||||
} |
|
||||
|
|
||||
if (data.showItemIndex !== undefined) { |
|
||||
store.commit('project/setProjectItemShow', { |
|
||||
index: data.showItemIndex, |
|
||||
show: true, |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
function isNumber(val) { |
|
||||
return val === +val; |
|
||||
} |
|
||||
|
|
||||
function start(e, index) { |
|
||||
console.log('开始'); |
|
||||
setTimeout(() => { |
|
||||
getDate(); |
|
||||
}, 300); |
|
||||
|
|
||||
if (isNumber(index)) { |
|
||||
// 选中一级项目 |
|
||||
data.setSubItem = false; |
|
||||
const query = uni |
|
||||
.createSelectorQuery() |
|
||||
.select(`#cu-${index}`) |
|
||||
.fields( |
|
||||
{ |
|
||||
id: true, |
|
||||
dataset: true, |
|
||||
rect: true, |
|
||||
size: true, |
|
||||
}, |
|
||||
res => { |
|
||||
data.moveTop = res.top; |
|
||||
data.moveLeft = res.left; |
|
||||
data.moveItem = data.itemList[index]; |
|
||||
data.itemWidth = res.width; |
|
||||
data.itemHeight = res.height; |
|
||||
}, |
|
||||
) |
|
||||
.exec(); |
|
||||
} else { |
|
||||
// 选中二级项目 |
|
||||
const arr = index.split('-'); |
|
||||
data.setSubItem = true; |
|
||||
|
|
||||
const query = uni.createSelectorQuery(); |
|
||||
query |
|
||||
.select(`#cu-${arr[0] - 0}`) |
|
||||
.fields( |
|
||||
{ |
|
||||
id: true, |
|
||||
dataset: true, |
|
||||
rect: true, |
|
||||
size: true, |
|
||||
}, |
|
||||
res => { |
|
||||
data.itemHeight = res.height; |
|
||||
}, |
|
||||
) |
|
||||
.exec(); |
|
||||
|
|
||||
query |
|
||||
.select(`#cu-${index}`) |
|
||||
.fields( |
|
||||
{ |
|
||||
id: true, |
|
||||
dataset: true, |
|
||||
rect: true, |
|
||||
size: true, |
|
||||
}, |
|
||||
res => { |
|
||||
data.moveTop = res.top; |
|
||||
data.moveLeft = res.left; |
|
||||
data.moveItem = data.itemList[arr[0] - 0].sonProjectList[arr[1] - 0]; |
|
||||
data.itemWidth = res.width; |
|
||||
data.subItemHeight = res.height; |
|
||||
}, |
|
||||
) |
|
||||
.exec(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
function move(e, length) { |
|
||||
console.log('移动'); |
|
||||
data.showMoveImage = true; // 悬浮开始 |
|
||||
const touch = e.touches[0]; |
|
||||
if (data.deltaLeft == 0) { |
|
||||
// 获得本身的移动 |
|
||||
data.deltaLeft = touch.pageX - data.moveLeft; |
|
||||
data.deltaTop = touch.pageY - data.moveTop; |
|
||||
} |
|
||||
data.moveLeft = touch.pageX - data.deltaLeft; |
|
||||
data.moveTop = touch.pageY - data.deltaTop; |
|
||||
|
|
||||
const lastIndex = findOverIndex(touch.pageY, length); |
|
||||
// 显示下划线 |
|
||||
for (let i = 0; i < data.itemList.length; i++) { |
|
||||
if (data.moveLeft > 35) { |
|
||||
data.itemList[i].showBorder = false; |
|
||||
data.itemList[i].showTopBorder = false; |
|
||||
if (i === lastIndex) { |
|
||||
data.itemList[i].showSubBorder = true; |
|
||||
} else { |
|
||||
data.itemList[i].showSubBorder = false; |
|
||||
} |
|
||||
} else if (lastIndex === -1) { |
|
||||
data.itemList[0].showTopBorder = true; |
|
||||
data.itemList[i].showSubBorder = false; |
|
||||
data.itemList[i].showBorder = false; |
|
||||
} else { |
|
||||
data.itemList[i].showSubBorder = false; |
|
||||
data.itemList[i].showTopBorder = false; |
|
||||
if (i === lastIndex) { |
|
||||
data.itemList[i].showBorder = true; |
|
||||
} else { |
|
||||
data.itemList[i].showBorder = false; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
function stops(e, index, length) { |
|
||||
console.log('结束', e, index, length); |
|
||||
const touch = e.changedTouches[0]; |
|
||||
const lastIndex = findOverIndex(touch.pageY, length); |
|
||||
console.log('11111111111', data.itemList) |
|
||||
// 交换两个值 |
|
||||
for (let i = 0; i < data.itemList.length; i++) { |
|
||||
// 插入顶部 |
|
||||
if (data.itemList[i].showTopBorder) { |
|
||||
if (isNumber(index)) { |
|
||||
const Value = data.itemList[index]; |
|
||||
data.itemList.unshift(Value); |
|
||||
data.itemList.splice(index + 1, 1); |
|
||||
} else { |
|
||||
const arr = index.split('-'); |
|
||||
const Value = data.itemList[arr[0] - 0].sonProjectList[arr[1] - 0]; |
|
||||
data.itemList.unshift(Value); |
|
||||
data.itemList[arr[0] - 0].sonProjectList.splice([arr[1] - 0], 1); |
|
||||
// const options = { |
|
||||
// id: Value.id, |
|
||||
// parentId: 0, |
|
||||
// }; |
|
||||
const options = { |
|
||||
businessCode: Value.businessCode, |
|
||||
moveProjectId: Value.id, |
|
||||
targetProjectId: '', |
|
||||
}; |
|
||||
emit('change', options); |
|
||||
} |
|
||||
// 清空 |
|
||||
clearSet(i); |
|
||||
emit('change', data.itemList); |
|
||||
return; |
|
||||
} |
|
||||
// 插入一级项目 |
|
||||
if (data.itemList[i].showBorder) { |
|
||||
if (isNumber(index)) { |
|
||||
const Value = data.itemList[index]; |
|
||||
data.itemList.splice(i + 1, 0, Value); |
|
||||
if (i < index) { |
|
||||
data.itemList.splice(index + 1, 1); |
|
||||
} else { |
|
||||
data.itemList.splice(index, 1); |
|
||||
} |
|
||||
} else { |
|
||||
const arr = index.split('-'); |
|
||||
const Value = data.itemList[arr[0] - 0].sonProjectList[arr[1] - 0]; |
|
||||
data.itemList.splice(i + 1, 0, Value); |
|
||||
data.itemList[arr[0] - 0].sonProjectList.splice([arr[1] - 0], 1); |
|
||||
// const options = { |
|
||||
// id: Value.id, |
|
||||
// parentId: 0, |
|
||||
// }; |
|
||||
const options = { |
|
||||
businessCode: Value.businessCode, |
|
||||
moveProjectId: Value.id, |
|
||||
targetProjectId: '', |
|
||||
}; |
|
||||
emit('change', options); |
|
||||
} |
|
||||
// 清空 |
|
||||
clearSet(i); |
|
||||
emit('change', data.itemList); |
|
||||
return; |
|
||||
} |
|
||||
// 插入二级项目 |
|
||||
if (data.itemList[i].showSubBorder) { |
|
||||
if (isNumber(index)) { |
|
||||
const Value = data.itemList[index]; |
|
||||
if (data.itemList[lastIndex - 1].sonProjectList && data.itemList[lastIndex - 1].sonProjectList.length) { |
|
||||
data.itemList[lastIndex - 1].sonProjectList.push(Value); |
|
||||
} else { |
|
||||
data.itemList[lastIndex].sonProjectList = [Value]; |
|
||||
} |
|
||||
data.itemList.splice(index, 1); |
|
||||
// 清空 |
|
||||
clearSet(i); |
|
||||
// const options = { |
|
||||
// id: Value.id, |
|
||||
// parentId: data.itemList[lastIndex - 1].id, |
|
||||
// }; |
|
||||
const options = { |
|
||||
businessCode: Value.businessCode, |
|
||||
moveProjectId: Value.id, |
|
||||
targetProjectId: data.itemList[lastIndex - 1].id, |
|
||||
}; |
|
||||
emit('change', options); |
|
||||
} else { |
|
||||
const arr = index.split('-'); |
|
||||
const Value = data.itemList[arr[0] - 0].sonProjectList[arr[1] - 0]; |
|
||||
if (data.itemList[lastIndex].sonProjectList && data.itemList[lastIndex].sonProjectList.length) { |
|
||||
data.itemList[lastIndex].sonProjectList.push(Value); |
|
||||
} else { |
|
||||
data.itemList[lastIndex].sonProjectList = [Value]; |
|
||||
} |
|
||||
data.itemList[arr[0] - 0].sonProjectList.splice([arr[1] - 0], 1); |
|
||||
// 清空 |
|
||||
clearSet(i); |
|
||||
// const options = { |
|
||||
// id: Value.id, |
|
||||
// parentId: data.itemList[lastIndex].id, |
|
||||
// }; |
|
||||
const options = { |
|
||||
businessCode: Value.businessCode, |
|
||||
moveProjectId: Value.id, |
|
||||
targetProjectId: data.itemList[lastIndex].id, |
|
||||
}; |
|
||||
emit('change', options); |
|
||||
|
|
||||
// const options1 = { |
|
||||
// id: Value.id, |
|
||||
// parentId: 0, |
|
||||
// }; |
|
||||
const options1 = { |
|
||||
businessCode: Value.businessCode, |
|
||||
moveProjectId: Value.id, |
|
||||
targetProjectId: '', |
|
||||
}; |
|
||||
emit('change', options1); |
|
||||
} |
|
||||
return; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 还原初始数据 |
|
||||
function clearSet(i) { |
|
||||
data.itemList[i].showBorder = false; |
|
||||
data.itemList[i].showSubBorder = false; |
|
||||
data.itemList[i].showTopBorder = false; |
|
||||
data.deltaLeft == 0; |
|
||||
data.showMoveImage = false; |
|
||||
data.setSubItem = false; |
|
||||
data.changeEvent = false; |
|
||||
data.showItemIndex = undefined; |
|
||||
} |
|
||||
|
|
||||
// 找到停下的元素的下标 |
|
||||
function findOverIndex(posY) { |
|
||||
// 如果有子项目展开着 |
|
||||
const leng = data.itemList.length * data.itemHeight; // 最后一个元素距离顶部的距离 |
|
||||
if (posY < data.begintop) { |
|
||||
return -1; |
|
||||
} |
|
||||
for (let i = 0; i < data.itemList.length; i++) { |
|
||||
const begin = data.itemHeight * i + data.begintop; |
|
||||
const end = data.itemHeight * i + data.begintop + data.itemHeight; |
|
||||
if (begin <= posY && end >= posY) { |
|
||||
return i; |
|
||||
} |
|
||||
} |
|
||||
if (posY > leng) { |
|
||||
// 交换最后一个 |
|
||||
return data.itemList.length - 1; |
|
||||
} |
|
||||
if (posY < data.begintop) { |
|
||||
return 0; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 删除项目 |
|
||||
function delProject(id, url) { |
|
||||
uni.showModal({ |
|
||||
title: '', |
|
||||
content: '是否删除项目?', |
|
||||
showCancel: true, |
|
||||
success: async ({ confirm }) => { |
|
||||
if (confirm) { |
|
||||
await uni.$u.api.delProject(id, url); |
|
||||
let flag_index = 0; |
|
||||
data.itemList.forEach((item, index) => { |
|
||||
if (item.id == id) { |
|
||||
flag_index = index; |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
data.itemList.splice(flag_index, 1); |
|
||||
store.commit('project/setProjects', data.itemList); |
|
||||
} |
|
||||
}, |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
// 导入 |
|
||||
function importProject(id, name) { |
|
||||
uni.showModal({ |
|
||||
content: '是否导入到' + name, |
|
||||
showCancel: true, |
|
||||
success: async ({ confirm }) => { |
|
||||
if (confirm) { |
|
||||
try { |
|
||||
const res = await uni.$u.api.import(id); |
|
||||
// 导入WBS成功后 |
|
||||
// 直接打开导入的项目 |
|
||||
emit('success'); |
|
||||
// const { apiUrl } = Config; |
|
||||
// let defaultwbs = `${apiUrl}/defaultwbs`; |
|
||||
// res.url && (defaultwbs = res.url); |
|
||||
|
|
||||
store.commit('project/setIsRefresh', 1); |
|
||||
|
|
||||
setTimeout(() => { |
|
||||
uni.navigateTo({ url: `/pages/project/project?u=${user.value.id}&p=${res.id}&pname=${res.name}&url=${encodeURIComponent(res.url)}` }); |
|
||||
}, 2000); |
|
||||
} catch (error) { |
|
||||
console.error('error: ', error); |
|
||||
emit('error', error); |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
// 导出 |
|
||||
function exportProject(id, url) { |
|
||||
uni.showModal({ |
|
||||
title: '', |
|
||||
content: '是否导出项目?', |
|
||||
showCancel: true, |
|
||||
success: async ({ confirm }) => { |
|
||||
if (confirm) { |
|
||||
const data = await uni.$u.post(`${url}/tall/project/exportWbs`, { projectId: id }); |
|
||||
// #ifdef H5 |
|
||||
window.location.href = data.url; |
|
||||
// #endif |
|
||||
|
|
||||
// #ifdef APP-PLUS |
|
||||
uni.downloadFile({ |
|
||||
url: data.url, //仅为示例,并非真实的资源 |
|
||||
success: ({statusCode, tempFilePath}) => { |
|
||||
if (statusCode === 200) { |
|
||||
console.log('下载成功', tempFilePath); |
|
||||
|
|
||||
uni.saveFile({ |
|
||||
tempFilePath, |
|
||||
success:(res)=>{ |
|
||||
uni.$ui.showToast('文件保存路径:' + res.savedFilePath); |
|
||||
//res.savedFilePath文件的保存路径 |
|
||||
//保存成功并打开文件 |
|
||||
// uni.openDocument({ |
|
||||
// filePath: res.savedFilePath, |
|
||||
// success:(res)=>console.log('成功打开文档') |
|
||||
// }) |
|
||||
}, |
|
||||
fail:()=>console.log('下载失败') |
|
||||
}) |
|
||||
} |
|
||||
} |
|
||||
}); |
|
||||
// #endif |
|
||||
} |
|
||||
}, |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
// 跳转工作台 |
|
||||
// function toWorkbench() { |
|
||||
// uni.navigateTo({ url: '/pages/workbench/workbench' }); |
|
||||
// } |
|
||||
</script> |
|
||||
|
|
||||
<style lang="scss" scoped> |
|
||||
.cu-item { |
|
||||
width: 100%; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
font-size: 14px; |
|
||||
} |
|
||||
|
|
||||
.border-100 { |
|
||||
width: 92%; |
|
||||
height: 4rpx; |
|
||||
} |
|
||||
|
|
||||
.border-80 { |
|
||||
width: 84%; |
|
||||
height: 2px; |
|
||||
margin-left: 30px; |
|
||||
} |
|
||||
|
|
||||
.workbench-btn { |
|
||||
margin-right: 10px; |
|
||||
width: 80px; |
|
||||
height: 36px; |
|
||||
line-height: 36px; |
|
||||
border-radius: 18px; |
|
||||
overflow: hidden; |
|
||||
border: 1px solid #2b85e4; |
|
||||
background-color: #1890ff; |
|
||||
font-size: 12px; |
|
||||
color: #ffffff; |
|
||||
text-align: center; |
|
||||
} |
|
||||
</style> |
|
@ -0,0 +1,234 @@ |
|||||
|
<template> |
||||
|
<view class="search-box fixed z-10 p-4 w-full bg-white shadow-lg"> |
||||
|
<view class="flex"> |
||||
|
<u-input class="mr-3" v-model="startTimeValue" type="text" :border="true" height="64" @focus="openCal" /> |
||||
|
<u-input v-model="endTimeValue" type="text" :border="true" height="64" @focus="openCal" /> |
||||
|
</view> |
||||
|
|
||||
|
<u-input class="mt-3" v-model="selectedMembers" type="text" :border="true" height="64" @focus="openMember" /> |
||||
|
|
||||
|
<view class="mt-3 text-center"> |
||||
|
<u-button class="mr-3" size="mini" @click="closePopup">取消</u-button> |
||||
|
<u-button class="mr-3" type="primary" size="mini" @click="submitSearch(1)">过滤</u-button> |
||||
|
<u-button type="primary" size="mini" @click="submitSearch(2)">导出</u-button> |
||||
|
</view> |
||||
|
|
||||
|
<u-popup title="请选择角色" mode="bottom" v-model="showMembers" :mask-close-able="false"> |
||||
|
<view class="multiple-select-box"> |
||||
|
<view class="multiple-select-title flex justify-between items-center"> |
||||
|
<view @click="handleCancel">取消</view> |
||||
|
<view @click="handleOK" class="text-blue-500">确认</view> |
||||
|
</view> |
||||
|
<scroll-view scroll-y="true" style="height: 400rpx;"> |
||||
|
<view v-for="(item, index) in members" :key="index" @click="changeItem(item)" |
||||
|
class="multiple-select-item flex items-center justify-between" |
||||
|
:class="[selectedMemberId.indexOf(item.value) > -1 ? 'selected' : '']" |
||||
|
> |
||||
|
<text>{{ item.label }} </text> |
||||
|
<u-icon name="checkbox-mark" class="icon"></u-icon> |
||||
|
</view> |
||||
|
</scroll-view> |
||||
|
</view> |
||||
|
</u-popup> |
||||
|
|
||||
|
<u-calendar v-if="!source" v-model="showCal" mode="range" @change="change"></u-calendar> |
||||
|
|
||||
|
<template v-if="source === 'checkWorkSummary'"> |
||||
|
<u-picker mode="time" v-model="showCal" :params="params" @confirm="selectedStartTime"></u-picker> |
||||
|
<u-picker mode="time" v-model="showEndCal" :params="params" @confirm="selectedEndTime"></u-picker> |
||||
|
</template> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { onMounted, ref } from 'vue'; |
||||
|
import { useStore } from 'vuex'; |
||||
|
import dayjs from 'dayjs'; |
||||
|
|
||||
|
const props = defineProps({ |
||||
|
// 打点日期的自定义样式 |
||||
|
members: { |
||||
|
type: Array, |
||||
|
default: () => [], |
||||
|
}, |
||||
|
show: { |
||||
|
type: Boolean, |
||||
|
default: false |
||||
|
}, |
||||
|
source: { |
||||
|
type: String, |
||||
|
default: "" |
||||
|
} |
||||
|
}); |
||||
|
const emit = defineEmits(['closePopup', 'getClockQuery']); |
||||
|
|
||||
|
const store = useStore(); |
||||
|
let startTimeValue = ref(null); // 当前选择的时间 |
||||
|
let endTimeValue = ref(null); // 当前选择的时间 |
||||
|
let showCal = ref(false); // 日历弹框是否显示 |
||||
|
let showEndCal = ref(false); // 结束时间弹框是否显示 |
||||
|
|
||||
|
let showMembers = ref(false); // 用户列表弹框是否显示 |
||||
|
let defaultMembers = ref([]); // 用户选择初始值 |
||||
|
let selectedMemberArr = ref([]); |
||||
|
let selectedMemberName = ref([]); // 被选中的用户 |
||||
|
let selectedMemberId = ref([]); // 被选中的用户 |
||||
|
let selectedMembers = ref(null); |
||||
|
|
||||
|
let selectedRoles = ref([]); |
||||
|
|
||||
|
const params = ref({ year: true, month: true, day: false, hour: false, minute: false, second: false }) |
||||
|
|
||||
|
// 打开日历弹框 |
||||
|
function openCal() { |
||||
|
showCal.value = true; |
||||
|
} |
||||
|
|
||||
|
// 选择时间 |
||||
|
function change(e) { |
||||
|
startTimeValue.value = e.startDate; |
||||
|
endTimeValue.value = e.endDate; |
||||
|
} |
||||
|
|
||||
|
// 打开月份弹框 |
||||
|
function selectedStartTime(e) { |
||||
|
showEndCal.value = true; |
||||
|
startTimeValue.value = e.year + '-' + e.month; |
||||
|
} |
||||
|
|
||||
|
// 打开结束月份弹框 |
||||
|
function selectedEndTime(e) { |
||||
|
endTimeValue.value = e.year + '-' + e.month; |
||||
|
} |
||||
|
|
||||
|
// 打开用户列表 |
||||
|
function openMember() { |
||||
|
showMembers.value = true; |
||||
|
|
||||
|
// 再次打开弹框,之前的数据显示问题处理(只显示选中且确认的,选中之后未确认但是关闭弹框,之后选中的数据不显示) |
||||
|
selectedMemberId.value = []; |
||||
|
let curr = (selectedMembers.value || "").split(","); |
||||
|
let arr = []; |
||||
|
selectedMemberArr.value.forEach(member => { |
||||
|
const index = curr.findIndex(item => item === member.label); |
||||
|
|
||||
|
if (index > -1) { |
||||
|
arr.push(member); |
||||
|
selectedMemberId.value.push(member.value); |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
selectedMemberArr.value = [...arr]; |
||||
|
} |
||||
|
|
||||
|
// 选择成员 |
||||
|
function changeItem(item) { |
||||
|
let index = selectedMemberArr.value.findIndex(member => member.value === item.value); |
||||
|
if (index === -1) { |
||||
|
selectedMemberArr.value.push(item); |
||||
|
} else { |
||||
|
selectedMemberArr.value.splice(index, 1); |
||||
|
} |
||||
|
|
||||
|
selectedMemberName.value = []; |
||||
|
selectedMemberId.value = []; |
||||
|
|
||||
|
selectedMemberArr.value.forEach(item => { |
||||
|
selectedMemberName.value.push(item.label); |
||||
|
selectedMemberId.value.push(item.value); |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
// 确认选中,关闭用户弹可 |
||||
|
function handleOK() { |
||||
|
showMembers.value = false; |
||||
|
selectedMembers.value = selectedMemberName.value.toString(); |
||||
|
} |
||||
|
|
||||
|
// 关闭用户弹框 |
||||
|
function handleCancel() { |
||||
|
showMembers.value = false; |
||||
|
} |
||||
|
|
||||
|
// 关闭过滤弹框 |
||||
|
function closePopup() { |
||||
|
emit('closePopup', false); |
||||
|
} |
||||
|
|
||||
|
// 提交筛选 |
||||
|
function submitSearch(type) { |
||||
|
if (!startTimeValue.value) { |
||||
|
uni.showToast({ |
||||
|
title: "请选择开始时间", |
||||
|
icon: 'none' |
||||
|
}); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
if (!endTimeValue.value) { |
||||
|
uni.showToast({ |
||||
|
title: "请选择结束时间", |
||||
|
icon: 'none' |
||||
|
}); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
const param = { |
||||
|
startTime: startTimeValue.value, |
||||
|
endTime: endTimeValue.value, |
||||
|
memberIdList: selectedMemberId.value |
||||
|
} |
||||
|
|
||||
|
if (type === 1) { |
||||
|
emit('getClockQuery', param) |
||||
|
} else { |
||||
|
emit('clockExport', param) |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.search-box { |
||||
|
top: 50px; |
||||
|
} |
||||
|
|
||||
|
.selected-member { |
||||
|
padding: 2px 10px; |
||||
|
min-height: 34px; |
||||
|
border: 1px solid #d9d9d9; |
||||
|
border-radius: 4px; |
||||
|
} |
||||
|
|
||||
|
.member-box { |
||||
|
padding: 0 10px; |
||||
|
background-color: #fafafa; |
||||
|
border: 1px solid #e8e8e8; |
||||
|
border-radius: 2px; |
||||
|
height: 24px; |
||||
|
margin: 2px 0; |
||||
|
margin-right: 4px; |
||||
|
} |
||||
|
|
||||
|
.multiple-select-box { |
||||
|
background-color: #fff; |
||||
|
} |
||||
|
|
||||
|
.multiple-select-title { |
||||
|
height: 45px; |
||||
|
line-height: 45; |
||||
|
border-bottom: 1px solid #f4f6f8; |
||||
|
|
||||
|
view { |
||||
|
padding: 0 12px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.multiple-select-item { |
||||
|
padding: 0 40rpx; |
||||
|
height: 32px; |
||||
|
} |
||||
|
|
||||
|
.multiple-select-item.selected { |
||||
|
color: #2979ff; |
||||
|
} |
||||
|
</style> |
@ -1,126 +0,0 @@ |
|||||
<template> |
|
||||
<!-- 时间间隔栏 --> |
|
||||
<!-- <Barrier /> --> |
|
||||
|
|
||||
<scroll-view |
|
||||
:lower-threshold="300" |
|
||||
scroll-y="true" |
|
||||
:upper-threshold="300" |
|
||||
:scroll-into-view="scrollToTaskId" |
|
||||
@scroll="scroll" |
|
||||
@scrolltolower="handleScrollBottom" |
|
||||
@scrolltoupper="handleScrollTop" |
|
||||
id="scroll" |
|
||||
> |
|
||||
<!-- 时间轴 --> |
|
||||
<!-- <u-divider bg-color="#f3f4f6" class="pt-5" fontSize="14px" v-if="topEnd">到顶啦</u-divider> --> |
|
||||
|
|
||||
<TimeBox /> |
|
||||
|
|
||||
<!-- <u-divider bg-color="#f3f4f6" class="pb-5" fontSize="14px" v-if="bottomEnd">到底啦</u-divider> --> |
|
||||
</scroll-view> |
|
||||
</template> |
|
||||
|
|
||||
<script setup> |
|
||||
import { reactive, computed } from 'vue'; |
|
||||
import { useStore } from 'vuex'; |
|
||||
// import Barrier from './component/Barrier.vue'; |
|
||||
import dayjs from 'dayjs'; |
|
||||
import TimeBox from './component/TimeBox.vue'; |
|
||||
|
|
||||
const store = useStore(); |
|
||||
// const visibleRoles = computed(() => store.state.role.visibleRoles); |
|
||||
// const scrollTop = computed(() => store.state.task.scrollTop); |
|
||||
const tasks = computed(() => store.state.task.tasks); |
|
||||
const topEnd = computed(() => store.state.task.topEnd); |
|
||||
const bottomEnd = computed(() => store.state.task.bottomEnd); |
|
||||
const showSkeleton = computed(() => store.state.task.showSkeleton); |
|
||||
const timeNode = computed(() => store.state.task.timeNode); |
|
||||
const scrollToTaskId = computed(() => store.state.task.scrollToTaskId); |
|
||||
const timeGranularity = computed(() => store.getters['task/timeGranularity']); |
|
||||
const emit = defineEmits(['getTasks']); |
|
||||
|
|
||||
const data = reactive({ top: 0 }); |
|
||||
|
|
||||
// 滚动 |
|
||||
function scroll(e) { |
|
||||
data.top = e.detail.scrollTop; |
|
||||
store.commit('task/setShrink', data.top > data.scrollTop); |
|
||||
store.commit('task/setScrollTop', data.top); |
|
||||
} |
|
||||
|
|
||||
// 滚动到顶部 |
|
||||
async function handleScrollTop() { |
|
||||
if (!tasks.value || !tasks.value.length || showSkeleton.value) return; |
|
||||
const startTime = tasks.value[0].planStart - 0; |
|
||||
if (topEnd.value) { |
|
||||
// 没有数据时 自动加载数据 |
|
||||
console.warn('滚动到顶部没有数据时: '); |
|
||||
const addTasks = uni.$task.setPlaceholderTasks(startTime, true, timeGranularity.value); |
|
||||
store.commit('task/setUpTasks', addTasks); |
|
||||
} else { |
|
||||
// 有数据时 |
|
||||
console.warn('滚动到顶部有数据时: '); |
|
||||
const detailId = tasks.value.findIndex(task => task.detailId); |
|
||||
const timeNodeValue = tasks.value[detailId].planStart - 0; |
|
||||
const upQuery = { |
|
||||
timeNode: timeNodeValue, |
|
||||
queryType: 0, |
|
||||
queryNum: 6, |
|
||||
}; |
|
||||
await emit('getTasks', upQuery); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 滚动到底部 |
|
||||
async function handleScrollBottom() { |
|
||||
if (!tasks.value || !tasks.value.length || showSkeleton.value) return; |
|
||||
const startTime = tasks.value[tasks.value.length - 1].planStart - 0; |
|
||||
if (bottomEnd.value) { |
|
||||
// 没有数据时 自动加载数据 |
|
||||
console.warn('滚动到底部没有数据时: '); |
|
||||
const addTasks = uni.$task.setPlaceholderTasks(startTime, false, timeGranularity.value); |
|
||||
store.commit('task/setDownTasks', addTasks); |
|
||||
} else { |
|
||||
// 时间基准点=最后一个任务的开始时间+当前时间颗粒度 |
|
||||
console.warn('滚动到底部有数据时: '); |
|
||||
const arr = []; |
|
||||
tasks.value.forEach(task => { |
|
||||
if (task.detailId) { |
|
||||
arr.push(task); |
|
||||
} |
|
||||
}); |
|
||||
const nextQueryTime = +uni.$time.add(+arr[arr.length - 1].planStart, 1, timeGranularity.value); |
|
||||
const downQuery = { |
|
||||
timeNode: nextQueryTime, |
|
||||
queryType: 1, |
|
||||
queryNum: 6, |
|
||||
}; |
|
||||
await emit('getTasks', downQuery); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 设置自动滚动位置 |
|
||||
function setScrollPosition() { |
|
||||
// 如果storage里有taskId 滚动到这个id的任务 |
|
||||
const taskId = uni.$storage.getStorageSync('taskId'); |
|
||||
if (taskId) { |
|
||||
store.commit('task/setScrollToTaskId', `a${taskId}`); |
|
||||
uni.$storage.setStorageSync('taskId', ''); // 记录后即刻清除本地存储 |
|
||||
} else { |
|
||||
const item = tasks.value.find(task => task.detailId); |
|
||||
if (item) { |
|
||||
store.commit('task/setScrollToTaskId', `a${item.id}`); |
|
||||
} else { |
|
||||
// 没有本地记录的taskId |
|
||||
// 找到当前时间基准线的任务id 记录 并滚动到当前时间基准线 |
|
||||
const task = tasks.value.find(item => dayjs(+item.planStart).isSame(timeNode.value, timeGranularity.value)); |
|
||||
task && store.commit('task/setScrollToTaskId', `a${task.id}`); // 有这个task 就记录他的id |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
defineExpose({ |
|
||||
setScrollPosition |
|
||||
}) |
|
||||
</script> |
|
@ -1,38 +0,0 @@ |
|||||
<!-- |
|
||||
* @Author: aBin |
|
||||
* @email: binbin0314@126.com |
|
||||
* @Date: 2021-07-19 14:22:54 |
|
||||
* @LastEditors: aBin |
|
||||
* @LastEditTime: 2021-07-20 11:46:04 |
|
||||
--> |
|
||||
<template> |
|
||||
<view class> |
|
||||
<!-- :class="{ active: cycleTasks.time.start === filter.startTime }" --> |
|
||||
<view class="cycle-time active"> |
|
||||
<!-- {{ $util.formatStartTimeToCycleTime(filter.time, cycleTasks.time.start) }} --> |
|
||||
2021年30周 |
|
||||
</view> |
|
||||
</view> |
|
||||
</template> |
|
||||
|
|
||||
<script setup> |
|
||||
|
|
||||
</script> |
|
||||
<style scoped lang="scss"> |
|
||||
.cycle-time { |
|
||||
padding: 8rpx 16rpx; |
|
||||
margin-bottom: 16rpx; |
|
||||
background: #fafafc; |
|
||||
color: $uni-text-color; |
|
||||
font-size: 28rpx; |
|
||||
position: sticky; |
|
||||
top: -1px; |
|
||||
left: 0; |
|
||||
z-index: 99; |
|
||||
|
|
||||
&.active { |
|
||||
background: $uni-color-primary; |
|
||||
color: $uni-text-color-inverse; |
|
||||
} |
|
||||
} |
|
||||
</style> |
|
@ -1,177 +0,0 @@ |
|||||
<template> |
|
||||
<view> |
|
||||
<view class="flex justify-between" style="min-width: 90px; position: relative"> |
|
||||
<u-icon custom-prefix="custom-icon" name="C-bxl-redux" size="17px"></u-icon> |
|
||||
<u-icon custom-prefix="custom-icon" name="attachment" size="21px"></u-icon> |
|
||||
<!-- <u-icon custom-prefix="custom-icon" name="moneycollect" size="20px"></u-icon> --> |
|
||||
<u-icon name="xuanxiang" custom-prefix="custom-icon" size="21px" @click="operation"></u-icon> |
|
||||
|
|
||||
<!-- 右上角 ... 弹窗 --> |
|
||||
<view class="popup border shadow-md" v-if="data.show"> |
|
||||
<!-- <view class="flex justify-center pb-3 border-b-1"> |
|
||||
<span>添加插件</span> |
|
||||
</view> --> |
|
||||
<view class="flex justify-center pb-3 border-b-1"> |
|
||||
<span @click="createTask">新建任务</span> |
|
||||
</view> |
|
||||
<view class="flex pt-3 justify-center"> |
|
||||
<span>克隆任务</span> |
|
||||
</view> |
|
||||
</view> |
|
||||
</view> |
|
||||
|
|
||||
<!-- 遮罩 --> |
|
||||
<view class="mask" v-if="data.maskShow" @click="closeMask"></view> |
|
||||
<!-- 新建任务弹窗 --> |
|
||||
<CreateTask |
|
||||
:startTime="data.startTime" |
|
||||
:endTime="data.endTime" |
|
||||
:task="task" |
|
||||
:source="'regular'" |
|
||||
@showTime="showTime" |
|
||||
@closeMask="closeMask" |
|
||||
class="thirdPopup flex transition-transform" |
|
||||
v-if="data.createTaskShow" |
|
||||
/> |
|
||||
|
|
||||
<u-picker title="开始时间" mode="time" v-model="data.showStart" :params="data.params" @confirm="confirmStartTime"></u-picker> |
|
||||
<u-picker title="结束时间" mode="time" v-model="data.showEnd" :params="data.params" @confirm="confirmEndTime"></u-picker> |
|
||||
</view> |
|
||||
</template> |
|
||||
|
|
||||
<script setup> |
|
||||
import { reactive } from 'vue'; |
|
||||
import CreateTask from '@/components/Title/components/CreateTask.vue'; |
|
||||
|
|
||||
defineProps({ task: { type: Object, default: () => {} } }); |
|
||||
|
|
||||
const data = reactive({ |
|
||||
show: false, // 右上角 ... 显示 |
|
||||
createTaskShow: false, // 新建项目显示 |
|
||||
secondShow: false, // 分享项目显示 |
|
||||
maskShow: false, // 遮罩显示 |
|
||||
showStart: false, |
|
||||
showEnd: false, |
|
||||
startTime: '', // 新建任务的开始时间 |
|
||||
endTime: '', // 新建任务的截止时间 |
|
||||
params: { |
|
||||
year: true, |
|
||||
month: true, |
|
||||
day: true, |
|
||||
hour: true, |
|
||||
minute: true, |
|
||||
second: true, |
|
||||
}, |
|
||||
}); |
|
||||
|
|
||||
// 操作 |
|
||||
function operation() { |
|
||||
// this.$t.ui.showToast('操作'); |
|
||||
data.show = !data.show; |
|
||||
} |
|
||||
|
|
||||
// 新建项目 |
|
||||
function createTask() { |
|
||||
// 关闭 ... 弹窗 |
|
||||
data.show = false; |
|
||||
// 打开遮罩 |
|
||||
data.maskShow = true; |
|
||||
// 打开新建项目弹窗 |
|
||||
data.createTaskShow = true; |
|
||||
} |
|
||||
|
|
||||
// 点击遮罩,关闭弹窗 |
|
||||
function closeMask() { |
|
||||
// 关闭遮罩 |
|
||||
data.maskShow = false; |
|
||||
// 关闭分享项目弹窗 |
|
||||
data.secondShow = false; |
|
||||
// 关闭新建项目弹窗 |
|
||||
data.createTaskShow = false; |
|
||||
} |
|
||||
|
|
||||
function showTime() { |
|
||||
data.showStart = !data.showStart; |
|
||||
} |
|
||||
|
|
||||
// 选择开始时间 |
|
||||
function confirmStartTime(e) { |
|
||||
data.startTime = `${e.year}-${e.month}-${e.day} ${e.hour}:${e.minute}:${e.second}`; |
|
||||
data.showEnd = true; |
|
||||
} |
|
||||
|
|
||||
// 选择结束时间 |
|
||||
function confirmEndTime(e) { |
|
||||
data.endTime = `${e.year}-${e.month}-${e.day} ${e.hour}:${e.minute}:${e.second}`; |
|
||||
} |
|
||||
</script> |
|
||||
|
|
||||
<style lang="scss" scoped> |
|
||||
.mask { |
|
||||
width: 100%; |
|
||||
height: 100vh; |
|
||||
z-index: 21; |
|
||||
position: fixed; |
|
||||
top: 0; |
|
||||
left: 0; |
|
||||
background: rgba(0, 0, 0, 0.3); |
|
||||
} |
|
||||
|
|
||||
.thirdPopup { |
|
||||
background: #ffffff; |
|
||||
position: fixed; |
|
||||
left: 50%; |
|
||||
top: 50%; |
|
||||
transform: translate(-50%, -50%); |
|
||||
z-index: 33; |
|
||||
border-radius: 5px; |
|
||||
width: 90%; |
|
||||
} |
|
||||
|
|
||||
.popup { |
|
||||
width: 110px; |
|
||||
background: #fff; |
|
||||
position: absolute; |
|
||||
right: 0; |
|
||||
top: 35px; |
|
||||
z-index: 99; |
|
||||
padding: 15px 0; |
|
||||
color: black; |
|
||||
animation: opacity 1s ease-in; |
|
||||
} |
|
||||
|
|
||||
@keyframes opacity { |
|
||||
0% { |
|
||||
opacity: 0; |
|
||||
} |
|
||||
50% { |
|
||||
opacity: 0.8; |
|
||||
} |
|
||||
100% { |
|
||||
opacity: 1; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
::v-deep .u-slot-content { |
|
||||
min-width: 0; |
|
||||
} |
|
||||
::v-deep .u-dropdown__content { |
|
||||
min-height: 120px !important; |
|
||||
height: auto !important; |
|
||||
overflow-y: auto; |
|
||||
background: #fff !important; |
|
||||
transition: none !important; |
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); |
|
||||
} |
|
||||
::v-deep .u-dropdown__menu__item .u-flex { |
|
||||
justify-content: space-between; |
|
||||
width: 100%; |
|
||||
height: 100%; |
|
||||
flex-wrap: nowrap; |
|
||||
border: 1px solid #afbed1; |
|
||||
padding: 0 8px; |
|
||||
} |
|
||||
::v-deep .u-dropdown__content__mask { |
|
||||
display: none; |
|
||||
} |
|
||||
</style> |
|
@ -1,132 +0,0 @@ |
|||||
<template> |
|
||||
<!-- v-finger:pinch="pinchHandler" --> |
|
||||
<view class="column"> |
|
||||
<view v-if="tasks && tasks.length"> |
|
||||
<view :key="task.id" v-for="task in tasks" :id="`a${task.id}`"> |
|
||||
<view class="flex"> |
|
||||
<TimeStatus :task="task" /> |
|
||||
<view class="flex items-center justify-between flex-1 ml-2 task-column"> |
|
||||
<view v-if="task.process !== 4">{{ $moment(+task.planStart).format(startTimeFormat) }}</view> |
|
||||
<view v-else>{{ $moment(+task.planStart).format('D日') }}</view> |
|
||||
|
|
||||
<!-- 任务功能菜单 --> |
|
||||
<TaskTools v-if="task.process !== 4" :task="task" /> |
|
||||
</view> |
|
||||
</view> |
|
||||
<view class="plugin"> |
|
||||
<view class="h-3" v-if="task.process === 4"></view> |
|
||||
<!-- <view class="ml-3 overflow-hidden shadow-lg task-box"> --> |
|
||||
<view class="ml-3"> |
|
||||
<u-card :show-foot="false" :show-head="false" :style="{ height: setHeight(task.panel) }" class="h-16" margin="0" v-if="showSkeleton"> |
|
||||
<view slot="body"> |
|
||||
<view><skeleton :banner="false" :loading="true" :row="4" animate class="mt-2 u-line-2 skeleton"></skeleton></view> |
|
||||
</view> |
|
||||
</u-card> |
|
||||
<!-- <u-card |
|
||||
@click="onClickTask(task.planStart - 0, task.id)" |
|
||||
:style="{ height: setHeight(task.panel) }" |
|
||||
:show-foot="false" |
|
||||
:show-head="false" |
|
||||
class="h-16" |
|
||||
margin="0" |
|
||||
v-if="tasks && tasks.length && task.process !== 4 && !showSkeleton" |
|
||||
> |
|
||||
<template v-slot:body> --> |
|
||||
<view class="h-16" v-if="tasks && tasks.length && task.process !== 4 && !showSkeleton" @click="onClickTask(task.planStart - 0, task.id)"> |
|
||||
<view class="p-0 u-col-between grid gap-3"> |
|
||||
<view :key="pIndex" v-for="(row, pIndex) in task.plugins"> |
|
||||
<view class="grid gap-2 grid-cols-1" v-if="row.length"> |
|
||||
<Plugin |
|
||||
:class="[`row-span-${plugin.row}`, `col-span-${plugin.col}`]" |
|
||||
:task="task" |
|
||||
:key="plugin.pluginTaskId" |
|
||||
:plugin-task-id="plugin.pluginTaskId" |
|
||||
:plugin-id="plugin.pluginId" |
|
||||
:param="plugin.param" |
|
||||
:style-type="data.styleType || 0" |
|
||||
v-for="plugin in row" |
|
||||
/> |
|
||||
</view> |
|
||||
</view> |
|
||||
</view> |
|
||||
</view> |
|
||||
<!-- </template> |
|
||||
</u-card> --> |
|
||||
</view> |
|
||||
</view> |
|
||||
</view> |
|
||||
</view> |
|
||||
<!-- 局部弹框操作栏 --> |
|
||||
<Tips /> |
|
||||
</view> |
|
||||
</template> |
|
||||
|
|
||||
<script setup> |
|
||||
import { useStore } from 'vuex'; |
|
||||
import { computed, reactive } from 'vue'; |
|
||||
import TimeStatus from './TimeStatus.vue'; |
|
||||
import TaskTools from './TaskTools.vue'; |
|
||||
import Skeleton from '@/components/Skeleton/Skeleton.vue'; |
|
||||
|
|
||||
const data = reactive({ |
|
||||
currentComponent: '', |
|
||||
styleType: 0, |
|
||||
}); |
|
||||
|
|
||||
const store = useStore(); |
|
||||
const roleId = computed(() => store.state.role.roleId); |
|
||||
const timeUnit = computed(() => store.state.task.timeUnit); |
|
||||
const tasks = computed(() => store.state.task.tasks); |
|
||||
const showSkeleton = computed(() => store.state.task.showSkeleton); |
|
||||
const startTimeFormat = computed(() => store.getters['task/startTimeFormat']); |
|
||||
|
|
||||
// 设置任务面板高度 |
|
||||
function setHeight(panel) { |
|
||||
if (panel && panel.height) { |
|
||||
return `${panel.height}px`; |
|
||||
} |
|
||||
return 'auto'; |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 点击了定期任务的面板 更新可变的日常任务 |
|
||||
* @param {number} planStart 任务计划开始时间 |
|
||||
* @param {string} taskId 任务id |
|
||||
*/ |
|
||||
function onClickTask(planStart, taskId) { |
|
||||
const param = { roleId: roleId.value, timeNode: planStart, timeUnit: timeUnit.value }; |
|
||||
store.dispatch('task/getGlobal', param); |
|
||||
uni.$storage.setStorageSync('taskId', taskId); |
|
||||
uni.$storage.setStorageSync('roleId', roleId.value); |
|
||||
} |
|
||||
|
|
||||
function pinchHandler(evt) { |
|
||||
// evt.scale代表两个手指缩放的比例 |
|
||||
console.log(`缩放:${evt.zoom}`); |
|
||||
} |
|
||||
</script> |
|
||||
|
|
||||
<style scoped lang="scss"> |
|
||||
.task-box { |
|
||||
border-radius: 24rpx; |
|
||||
} |
|
||||
.column { |
|
||||
padding: 24px 14px; |
|
||||
} |
|
||||
.task-column { |
|
||||
height: 33px; |
|
||||
} |
|
||||
.plugin { |
|
||||
margin-top: 8px; |
|
||||
margin-bottom: 8px; |
|
||||
margin-left: 15px; |
|
||||
border-left: 2px solid #d1d5db; |
|
||||
} |
|
||||
::v-deep .ml-2 { |
|
||||
margin-left: 16px; |
|
||||
} |
|
||||
|
|
||||
::v-deep .ml-3 { |
|
||||
margin-left: 20px; |
|
||||
} |
|
||||
</style> |
|
@ -1,317 +0,0 @@ |
|||||
<template> |
|
||||
<view class="u-font-14"> |
|
||||
<view |
|
||||
class="flex items-center justify-center rounded-full icon-column" |
|
||||
:style="{ color: orderStyle.color }" |
|
||||
@click="changeStatus(task.process, $event)" |
|
||||
> |
|
||||
<!-- 1进行中 2暂停中 3已完成 --> |
|
||||
<u-circle-progress |
|
||||
:percent="orderStyle.persent - 0" |
|
||||
:active-color="orderStyle.color" |
|
||||
bg-color="rgba(255,255,255,0)" |
|
||||
border-width="4" |
|
||||
:width="task.process !== 4 ? 66 : 50" |
|
||||
v-if="task.process === 1 || task.process === 2 || task.process === 3" |
|
||||
> |
|
||||
<view class="u-progress-content"> |
|
||||
<view class="u-progress-dot"></view> |
|
||||
<view class="u-progress-info"> |
|
||||
<u-icon :name="orderStyle.icon" v-if="orderStyle.icon" size="15px"></u-icon> |
|
||||
<template v-else>{{ data.durationText }}</template> |
|
||||
</view> |
|
||||
</view> |
|
||||
</u-circle-progress> |
|
||||
<!-- 0未开始 4添加任务 --> |
|
||||
<view class="flex items-center justify-center rounded-full progress-box" v-else :class="task.process === 4 ? 'progress-box-4' : ''"> |
|
||||
<view class="u-progress-content"> |
|
||||
<view class="u-progress-dot"></view> |
|
||||
<view class="u-progress-info"> |
|
||||
<span v-if="orderStyle.icon"> |
|
||||
<u-icon :name="orderStyle.icon" v-if="task.process !== 4" size="15px"></u-icon> |
|
||||
<u-icon :name="orderStyle.icon" v-else size="15px"></u-icon> |
|
||||
</span> |
|
||||
<template v-else>{{ data.durationText }}</template> |
|
||||
</view> |
|
||||
</view> |
|
||||
</view> |
|
||||
</view> |
|
||||
|
|
||||
<!-- 遮罩 --> |
|
||||
<view class="mask" v-if="data.maskShow" @click="closeMask"></view> |
|
||||
<!-- 新建任务弹窗 --> |
|
||||
<CreateTask |
|
||||
:startTime="data.startTime" |
|
||||
:endTime="data.endTime" |
|
||||
:task="task" |
|
||||
:source="'regular'" |
|
||||
@showTime="showTime" |
|
||||
@closeMask="closeMask" |
|
||||
class="thirdPopup flex transition-transform" |
|
||||
v-if="data.createTaskShow" |
|
||||
/> |
|
||||
|
|
||||
<u-picker title="开始时间" mode="time" v-model="data.showStart" :params="data.params" @confirm="confirmStartTime"></u-picker> |
|
||||
<u-picker title="结束时间" mode="time" v-model="data.showEnd" :params="data.params" @confirm="confirmEndTime"></u-picker> |
|
||||
</view> |
|
||||
</template> |
|
||||
|
|
||||
<script setup> |
|
||||
import { reactive, onMounted, computed } from 'vue'; |
|
||||
import { useStore } from 'vuex'; |
|
||||
import dayjs from 'dayjs'; |
|
||||
import CreateTask from '../../Title/components/CreateTask.vue'; |
|
||||
|
|
||||
const props = defineProps({ task: { type: Object, default: () => {} } }); |
|
||||
|
|
||||
const data = reactive({ |
|
||||
time: '', |
|
||||
start: [{ text: '确认开始任务', color: 'blue' }], |
|
||||
pause: [{ text: '继续' }, { text: '重新开始任务', color: 'blue' }, { text: '结束' }], |
|
||||
proceed: [{ text: '暂停' }, { text: '重新开始任务', color: 'blue' }, { text: '结束' }], |
|
||||
again: [{ text: '重新开始任务', color: 'blue' }], |
|
||||
timer: null, |
|
||||
durationText: 0, |
|
||||
maskShow: false, // 遮罩显示 |
|
||||
createTaskShow: false, // 新建项目显示 |
|
||||
startTime: '', // 新建任务的开始时间 |
|
||||
endTime: '', // 新建任务的截止时间 |
|
||||
showStart: false, |
|
||||
showEnd: false, |
|
||||
params: { |
|
||||
year: true, |
|
||||
month: true, |
|
||||
day: true, |
|
||||
hour: true, |
|
||||
minute: true, |
|
||||
second: true, |
|
||||
}, |
|
||||
}); |
|
||||
|
|
||||
const store = useStore(); |
|
||||
const tip = computed(() => store.state.task.tip); |
|
||||
const status = computed(() => props.task ? props.task.process : 0); |
|
||||
const taskName = computed(() => props.task ? props.task.name : ''); |
|
||||
const taskId = computed(() => props.task ? props.task.id : ''); |
|
||||
|
|
||||
// 计算圆环的弧度百分比 |
|
||||
function computeCyclePersent() { |
|
||||
if (!props.task || !props.task.realStart || !props.task.planDuration) return 100; |
|
||||
const { realStart, planDuration } = props.task; |
|
||||
return (((Date.now() - +realStart) * 100) / +planDuration).toFixed(2); |
|
||||
} |
|
||||
|
|
||||
const orderStyle = computed(() => { |
|
||||
// 图标文本颜色 |
|
||||
// 任务状态 0未开始 1进行中 2暂停中 3已完成 |
|
||||
let color = '#9CA3AF'; |
|
||||
let icon = 'play-right-fill'; |
|
||||
let persent = 100; |
|
||||
switch (status.value) { |
|
||||
case 1: // 进行中 |
|
||||
color = '#60A5FA'; |
|
||||
icon = ''; |
|
||||
if (+computeCyclePersent() > 100) { |
|
||||
persent = 96; |
|
||||
} else { |
|
||||
persent = computeCyclePersent(); |
|
||||
} |
|
||||
break; |
|
||||
case 2: // 暂停中 |
|
||||
color = '#F87171'; |
|
||||
icon = 'pause'; |
|
||||
persent = 50; // TODO: 暂时这样 暂停状态没有计算剩余多少时间 |
|
||||
break; |
|
||||
case 3: // 已结束 |
|
||||
color = '#34D399'; |
|
||||
icon = 'checkmark'; |
|
||||
persent = 100; |
|
||||
break; |
|
||||
case 4: // 添加任务 |
|
||||
color = '#60A5FA'; |
|
||||
icon = 'plus'; |
|
||||
persent = 100; |
|
||||
break; |
|
||||
default: |
|
||||
// 未开始 |
|
||||
color = '#9CA3AF'; |
|
||||
icon = 'play-right'; |
|
||||
persent = 100; |
|
||||
break; |
|
||||
} |
|
||||
return { color, icon, persent }; |
|
||||
}); |
|
||||
|
|
||||
// unMounted(() => { |
|
||||
// if (data.timer) { |
|
||||
// clearInterval(data.timer); |
|
||||
// data.timer = null; |
|
||||
// } |
|
||||
// }); |
|
||||
|
|
||||
/** |
|
||||
* 计算tip的标题内容 |
|
||||
*/ |
|
||||
function genetateTips(type, content) { |
|
||||
if (type === 0) { |
|
||||
return `确认开始任务"${content}"吗?`; |
|
||||
} |
|
||||
if (type === 3) { |
|
||||
return '是否要重新开始此任务'; |
|
||||
} |
|
||||
return '请选择要执行的操作'; |
|
||||
} |
|
||||
|
|
||||
// 新建任务 |
|
||||
function addTask() { |
|
||||
// uni.$ui.showToast('新建任务'); |
|
||||
// 打开遮罩 |
|
||||
data.maskShow = true; |
|
||||
// 打开新建项目弹窗 |
|
||||
data.createTaskShow = true; |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 点击了图标 修改任务状态 |
|
||||
* @param {object} event |
|
||||
*/ |
|
||||
function changeStatus(process, event) { |
|
||||
if (process === 4) { |
|
||||
addTask(); |
|
||||
return; |
|
||||
} |
|
||||
// return false; |
|
||||
tip.status = status; |
|
||||
tip.taskId = taskId; |
|
||||
tip.left = event.target.x; |
|
||||
tip.top = event.target.y; |
|
||||
tip.show = true; |
|
||||
tip.text = genetateTips(status, taskName); |
|
||||
store.commit('task/setTip', tip); |
|
||||
} |
|
||||
|
|
||||
// 点击遮罩,关闭弹窗 |
|
||||
function closeMask() { |
|
||||
// 关闭遮罩 |
|
||||
data.maskShow = false; |
|
||||
// 关闭新建项目弹窗 |
|
||||
data.createTaskShow = false; |
|
||||
} |
|
||||
|
|
||||
function showTime(type) { |
|
||||
if (type === 1) { |
|
||||
data.showStart = !data.showStart; |
|
||||
} else { |
|
||||
data.showEnd = !data.showEnd; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 选择开始时间 |
|
||||
function confirmStartTime(e) { |
|
||||
data.startTime = `${e.year}-${e.month}-${e.day} ${e.hour}:${e.minute}:${e.second}`; |
|
||||
data.showEnd = true; |
|
||||
} |
|
||||
|
|
||||
// 选择结束时间 |
|
||||
function confirmEndTime(e) { |
|
||||
data.endTime = `${e.year}-${e.month}-${e.day} ${e.hour}:${e.minute}:${e.second}`; |
|
||||
} |
|
||||
|
|
||||
// 计算进行中状态剩余时间 |
|
||||
// 预计结束时间 = realStart(实际开始) + planDuration(计划时长) |
|
||||
// 剩余时间 = 预计结束时间 - 当前时间 |
|
||||
// 剩余时间 = realStart + planDuration - Date.now() |
|
||||
function computeDurationText() { |
|
||||
const { realStart, planDuration } = props.task; |
|
||||
const leftTime = (realStart-0 || 0) + (planDuration-0 || 0) - Date.now(); // 剩余时间 |
|
||||
const { num, time } = uni.$time.computeDurationText(leftTime); |
|
||||
if (num <= 0) { |
|
||||
clearInterval(data.timer); |
|
||||
data.timer = null; |
|
||||
} |
|
||||
data.durationText = num; |
|
||||
return time; |
|
||||
} |
|
||||
|
|
||||
function updateDurationText(time) { |
|
||||
if (data.timer) { |
|
||||
clearInterval(data.timer); |
|
||||
data.timer = null; |
|
||||
} |
|
||||
if (!time) return; |
|
||||
setInterval(() => { |
|
||||
computeDurationText(); |
|
||||
}, time); |
|
||||
} |
|
||||
|
|
||||
onMounted(() => { |
|
||||
// TODO: 计算在不在窗口内显示 |
|
||||
const time = computeDurationText(); |
|
||||
updateDurationText(time); |
|
||||
}); |
|
||||
</script> |
|
||||
|
|
||||
<style scoped lang="scss"> |
|
||||
.icon-column { |
|
||||
height: 33px; |
|
||||
width: 33px; |
|
||||
} |
|
||||
.one { |
|
||||
height: 33px; |
|
||||
width: 33px; |
|
||||
} |
|
||||
|
|
||||
.progress-box { |
|
||||
background: rgba(255, 255, 255, 0); |
|
||||
width: 33px; |
|
||||
height: 33px; |
|
||||
border: 2px solid #9ca3af; |
|
||||
} |
|
||||
|
|
||||
.progress-box-4 { |
|
||||
width: 25px; |
|
||||
height: 25px; |
|
||||
border: 2px solid #60a5fa; |
|
||||
} |
|
||||
|
|
||||
.mask { |
|
||||
width: 100%; |
|
||||
height: 100vh; |
|
||||
z-index: 21; |
|
||||
position: fixed; |
|
||||
top: 0; |
|
||||
left: 0; |
|
||||
background: rgba(0, 0, 0, 0.3); |
|
||||
} |
|
||||
|
|
||||
.thirdPopup { |
|
||||
background: #ffffff; |
|
||||
position: fixed; |
|
||||
left: 50%; |
|
||||
top: 50%; |
|
||||
transform: translate(-50%, -50%); |
|
||||
z-index: 33; |
|
||||
border-radius: 5px; |
|
||||
width: 90%; |
|
||||
} |
|
||||
|
|
||||
::v-deep .u-dropdown__content { |
|
||||
min-height: 120px !important; |
|
||||
height: auto !important; |
|
||||
overflow-y: auto; |
|
||||
background: #fff !important; |
|
||||
transition: none !important; |
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); |
|
||||
} |
|
||||
::v-deep .u-dropdown__menu__item .u-flex { |
|
||||
justify-content: space-between; |
|
||||
width: 100%; |
|
||||
height: 100%; |
|
||||
flex-wrap: nowrap; |
|
||||
border: 1px solid #afbed1; |
|
||||
padding: 0 8px; |
|
||||
} |
|
||||
::v-deep .u-dropdown__content__mask { |
|
||||
display: none; |
|
||||
} |
|
||||
</style> |
|
@ -1,476 +0,0 @@ |
|||||
import { computed, nextTick, watch } from 'vue'; |
|
||||
import { useStore } from 'vuex'; |
|
||||
import { flatten } from 'lodash'; |
|
||||
import dayjs from 'dayjs'; |
|
||||
|
|
||||
export default function useGetTasks() { |
|
||||
const store = useStore(); |
|
||||
const tasks = computed(() => store.state.task.tasks); |
|
||||
const timeLineType = computed(() => store.state.task.timeLineType); // 时间轴模式
|
|
||||
const realTasks = computed(() => store.state.task.realTasks); // 真实任务
|
|
||||
const downNextPage = computed(() => store.state.task.downNextPage); // 下一页
|
|
||||
const upNextPage = computed(() => store.state.task.upNextPage); // 下一页
|
|
||||
const currUpTimeNode = computed(() => store.state.task.currUpTimeNode); // 当前查询的时间
|
|
||||
const currDownTimeNode = computed(() => store.state.task.currDownTimeNode); // 当前查询的时间
|
|
||||
const roleId = computed(() => store.state.role.roleId); |
|
||||
const timeNode = computed(() => store.state.task.timeNode); |
|
||||
const timeUnit = computed(() => store.state.task.timeUnit); |
|
||||
const visibleRoles = computed(() => store.state.role.visibleRoles); |
|
||||
const allTasks = computed(() => store.state.task.allTasks); |
|
||||
const roleIndex = computed(() => store.state.role.roleIndex); |
|
||||
|
|
||||
const projectId = computed(() => store.getters['project/projectId']); |
|
||||
const timeGranularity = computed(() => store.getters['task/timeGranularity']); |
|
||||
|
|
||||
const remindData = computed(() => store.state.socket.remindData); // 小红点
|
|
||||
|
|
||||
const currRoleRealTasks = computed(() => store.state.task.currRoleRealTasks); // 当前角色的真实任务数据
|
|
||||
|
|
||||
// 初始化 定期任务
|
|
||||
async function initPlanTasks() { |
|
||||
// timeLineType.value === 1 ? setNextPlaceholderTasks({}) : '';
|
|
||||
await getTasks({}); // 获取初始数据
|
|
||||
// await dataRender({});
|
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 生成getTasks所用的参数 |
|
||||
* @param {object} query getTasks传递的参数 |
|
||||
*/ |
|
||||
function generateGetTaskParam(query) { |
|
||||
return { |
|
||||
roleId: roleId.value, |
|
||||
timeNode: query.timeNode || timeNode.value, |
|
||||
timeUnit: query.timeUnit || timeUnit.value, |
|
||||
queryType: query.queryType === 0 ? 0 : 1, |
|
||||
pageNum: query.pageNum || 1, |
|
||||
pageSize: query.pageSize || uni.$taskConfig.pageCount, |
|
||||
taskId: query.taskId || '' |
|
||||
}; |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 根据时间基准点和角色查找定期任务 |
|
||||
* @param {object} query |
|
||||
* @param {string} query.roleId 角色id |
|
||||
* @param {string} query.timeNode 时间基准点 默认当前 |
|
||||
* @param {string} query.timeUnit 时间颗粒度 默认天 |
|
||||
* @param {string} query.queryNum 查找颗粒度数量 默认3个 |
|
||||
* @param {number} query.queryType 0向上查找 1向下查找(默认) 下查包含自己,上查不包含 |
|
||||
*/ |
|
||||
function getTasks(query) { |
|
||||
store.commit('task/setShowSkeleton', false); |
|
||||
|
|
||||
const params = generateGetTaskParam(query); |
|
||||
uni.$catchReq.getTaskByNum(params, (err, data) => { |
|
||||
store.commit('task/setShowSkeleton', false); |
|
||||
if (err) { |
|
||||
// TODO: 提示错误
|
|
||||
console.error('err: ', err); |
|
||||
} else { |
|
||||
store.commit('task/setShowScrollTo', true); |
|
||||
|
|
||||
params.queryType === 0 ? store.commit('task/setUpRealTasks', data.list) : store.commit('task/setDownRealTasks', data.list); |
|
||||
params.queryType === 0 ? store.commit('task/setUpNextPage', data.nextPage) : store.commit('task/setDownNextPage', data.nextPage); // 下一页
|
|
||||
|
|
||||
const index = visibleRoles.value.findIndex(role => role.id === roleId.value); |
|
||||
const arr = [...allTasks.value]; |
|
||||
arr[index].realTasks = [...realTasks.value]; |
|
||||
store.commit('task/setAllTasks', arr); |
|
||||
store.commit('task/setCurrRoleRealTasks', arr[index].realTasks); // 设置当前角色的真实任务数据
|
|
||||
|
|
||||
// 数据处理
|
|
||||
dataRender(params); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
function dataRender(params) { |
|
||||
timeLineType.value === 1 ? renderScaleTask(params) : renderConTask(params); |
|
||||
} |
|
||||
|
|
||||
// 任务模式
|
|
||||
async function renderConTask(params) { |
|
||||
let nextPage = params.queryType === 0 ? upNextPage.value : downNextPage.value; // 下一页的值
|
|
||||
let showTasks = tasks.value; |
|
||||
let centerData = await showTaskId(params, showTasks, realTasks.value) || []; |
|
||||
|
|
||||
if (centerData.length < 15 && nextPage > 0) { |
|
||||
getTasks({pageNum: nextPage, queryType: params.queryType}); |
|
||||
} else { |
|
||||
if (params.queryType === 0) { |
|
||||
showTasks = [...centerData, ...showTasks]; |
|
||||
} else { |
|
||||
showTasks = [...showTasks, ...centerData]; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
if (showTasks.length < 15 && nextPage === 0 && params.queryType === 1) { |
|
||||
getTasks({pageNum: 1, queryType: 0}); |
|
||||
} |
|
||||
|
|
||||
if (showTasks.length > 80) { |
|
||||
showTasks = params.queryType === 0 ? showTasks.slice(0, 80) : showTasks.slice(showTasks.length - 80); |
|
||||
} |
|
||||
|
|
||||
store.commit('task/clearTasks'); |
|
||||
params.queryType === 0 ? store.commit('task/setUpTasks', showTasks) : store.commit('task/setDownTasks', showTasks); |
|
||||
} |
|
||||
|
|
||||
// 刻度模式数据处理
|
|
||||
async function renderScaleTask(params) { |
|
||||
// params.queryType === 0 ? setPrevPlaceholderTasks(params) : setNextPlaceholderTasks(params);
|
|
||||
|
|
||||
// let centerData = await showTaskId(params, tasks.value, currRoleRealTasks.value) || [];
|
|
||||
let centerData = await showTaskTime(params, tasks.value, currRoleRealTasks.value) || []; |
|
||||
// tasksData(params, centerData, currRoleRealTasks.value);
|
|
||||
handleTasksData(params, centerData, currRoleRealTasks.value); |
|
||||
} |
|
||||
|
|
||||
// 已显示的任务第一个时间和最后一个时间
|
|
||||
async function showTaskTime(params, showTasks, realTasks) { |
|
||||
|
|
||||
// 初始值
|
|
||||
let centerData = params.queryType === 0 ? [] : realTasks.slice(0, params.pageSize); |
|
||||
|
|
||||
/** |
|
||||
* 1、判断显示任务中是否有真实任务 |
|
||||
* 1-1、有。根据id查找任务 |
|
||||
* 1-2、无。根据时间查找任务 |
|
||||
* 2、查找15个任务 |
|
||||
*/ |
|
||||
const firstDetailIndex = showTasks.findIndex(task => task.detailId); |
|
||||
|
|
||||
if (firstDetailIndex > -1) { |
|
||||
// 显示任务中有真实任务数据
|
|
||||
const firstId = showTasks[firstDetailIndex].id; |
|
||||
let lastDetailIndex = -1; |
|
||||
showTasks.forEach((item, index) => { |
|
||||
if (item.detailId) { |
|
||||
lastDetailIndex = index; |
|
||||
} |
|
||||
}) |
|
||||
const lastId = showTasks[lastDetailIndex].id; |
|
||||
|
|
||||
realTasks.forEach((item, index) => { |
|
||||
if (params.queryType === 1 && item.id === lastId) { |
|
||||
centerData = realTasks.slice(index + 1, index + 1 + params.pageSize) || []; |
|
||||
} else if (params.queryType === 0 && item.id === firstId) { |
|
||||
if (index >= params.pageSize) { |
|
||||
centerData = realTasks.slice(index - params.pageSize, index) || []; |
|
||||
} else { |
|
||||
centerData = realTasks.slice(0, index) || []; |
|
||||
} |
|
||||
} |
|
||||
}) |
|
||||
} else { |
|
||||
// 显示任务中没有真实任务数据
|
|
||||
// 已显示任务的第一个任务时间
|
|
||||
const firstTime = showTasks.length ? showTasks[0].planStart : ''; |
|
||||
// 已显示任务的最后一个任务时间
|
|
||||
const lastTime = showTasks.length ? showTasks[showTasks.length - 1].planStart : ''; |
|
||||
|
|
||||
try { |
|
||||
realTasks.forEach((item, index) => { |
|
||||
if (params.queryType === 1) { |
|
||||
if (dayjs(+lastTime).isSame(+item.planStart, timeGranularity.value) || dayjs(+lastTime).isBefore(+item.planStart, timeGranularity.value)) { |
|
||||
centerData = realTasks.slice(index, index + params.pageSize) || []; |
|
||||
throw Error(); |
|
||||
} |
|
||||
} else { |
|
||||
if (dayjs(+firstTime).isSame(+item.planStart, timeGranularity.value) || dayjs(+firstTime).isAfter(+item.planStart, timeGranularity.value)) { |
|
||||
if (index >= params.pageSize) { |
|
||||
centerData = realTasks.slice(index - params.pageSize, index) || []; |
|
||||
} else { |
|
||||
centerData = realTasks.slice(0, index) || []; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
}) |
|
||||
} catch (e) { |
|
||||
console.log('退出循环') |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
console.log('111111111', centerData); |
|
||||
return centerData; |
|
||||
} |
|
||||
|
|
||||
async function handleTasksData(params, centerData, realTasks) { |
|
||||
/** |
|
||||
* 3、查找的任务数量是否>=15 |
|
||||
* 3-1、是。 |
|
||||
* 判断时间跨度是否>=15 |
|
||||
* 3-1-1、是。显示时间刻度范围内的任务 |
|
||||
* 3-1-2、否。显示全部任务并删除多余的刻度 |
|
||||
* 3-2、否。 |
|
||||
* 判断时间跨度是否>=15 |
|
||||
* 3-2-1、是。显示时间刻度范围内的任务 |
|
||||
* 3-2-2、否。 |
|
||||
* 下一页是否为0 |
|
||||
* 3-2-2-1、是。无下一页,显示任务和刻度,之后继续展示刻度 |
|
||||
* 3-2-2-1、否。查找下一页数据并重复上述步骤 |
|
||||
*/ |
|
||||
const startTime = centerData.length ? centerData[0].planStart : ''; |
|
||||
const endTime = centerData.length ? centerData[centerData.length - 1].planStart : ''; |
|
||||
let centerTime = dayjs(+startTime).add(params.pageSize, timeGranularity.value);\ |
|
||||
// 时间跨度是否大于等于15
|
|
||||
let isExceed = dayjs(+centerTime).isBefore(+endTime, timeGranularity.value) || dayjs(+centerTime).isSame(+endTime, timeGranularity.value); |
|
||||
|
|
||||
let nextPage = params.queryType === 0 ? upNextPage.value : downNextPage.value; // 下一页的值
|
|
||||
if (isExceed || centerData.length >= params.pageSize || nextPage === 0) { |
|
||||
params.queryType === 0 ? setPrevPlaceholderTasks(params) : setNextPlaceholderTasks(params); |
|
||||
} |
|
||||
|
|
||||
let showTasks = tasks.value; // 显示的数据
|
|
||||
|
|
||||
const firstDetailIndex = showTasks.findIndex(task => task.detailId); // 显示任务中存在真实任务
|
|
||||
const firstId = showTasks[firstDetailIndex].id; |
|
||||
let lastDetailIndex = -1; |
|
||||
showTasks.forEach((item, index) => { |
|
||||
if (item.detailId) { |
|
||||
lastDetailIndex = index; |
|
||||
} |
|
||||
}) |
|
||||
const lastId = showTasks[lastDetailIndex].id; |
|
||||
|
|
||||
showTasks.forEach((task, index) => { |
|
||||
const arr = centerData.filter(item => dayjs(+item.planStart).isSame(+task.planStart, timeGranularity.value)); |
|
||||
if (arr && arr.length) { |
|
||||
if (firstDetailIndex > -1) { |
|
||||
if (params.queryType === 1 && task.id === lastId) { |
|
||||
showTasks.splice(index + 1, 0, [...arr]) |
|
||||
} else (params.queryType === 0 && task.id === firstId) { |
|
||||
showTasks.splice(index, 0, [...arr]) |
|
||||
} |
|
||||
} else { |
|
||||
showTasks.splice(index, 1, [...arr]); |
|
||||
} |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
showTasks = flatten(showTasks); // 1维拍平
|
|
||||
|
|
||||
if (!isExceed) { |
|
||||
if (centerData.length < params.pageSize && nextPage > 0) { |
|
||||
await getTasks({pageNum: nextPage, queryType: params.queryType}); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
let data = params.queryType === 0 ? centerData[0] : centerData[centerData.length - 1]; |
|
||||
showTasks.forEach((item, index) => { |
|
||||
if (item.id === data.id) { |
|
||||
len = index; |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
showTasks = params.queryType === 0 ? showTasks.slice(len) : showTasks.slice(0, len + 1); |
|
||||
} |
|
||||
|
|
||||
if (showTasks.length > 80) { |
|
||||
showTasks = param.queryType === 0 ? showTasks.slice(0, 80) : showTasks.slice(showTasks.length - 80); |
|
||||
} |
|
||||
|
|
||||
store.commit('task/clearTasks'); |
|
||||
param.queryType === 0 ? store.commit('task/setUpTasks', showTasks) : store.commit('task/setDownTasks', showTasks); |
|
||||
} |
|
||||
|
|
||||
// 已显示的任务第一个id和最后一个id
|
|
||||
async function showTaskId(params, showTasks, realTasks) { |
|
||||
const firstDetailIndex = showTasks.findIndex(task => task.detailId); |
|
||||
const firstId = firstDetailIndex === -1 ? 0 : showTasks[firstDetailIndex].id; |
|
||||
let lastDetailIndex = -1; |
|
||||
showTasks.forEach((item, index) => { |
|
||||
if (item.detailId) { |
|
||||
lastDetailIndex = index; |
|
||||
} |
|
||||
}) |
|
||||
const lastId = lastDetailIndex === -1 ? 0 : showTasks[lastDetailIndex].id; |
|
||||
|
|
||||
let centerData = params.queryType === 0 ? [] : realTasks.slice(0, params.pageSize); |
|
||||
let flag = false; |
|
||||
realTasks.forEach((item, index) => { |
|
||||
if (params.queryType === 1 && item.id === lastId) { |
|
||||
flag = true; |
|
||||
centerData = realTasks.slice(index + 1, index + 1 + params.pageSize) || []; |
|
||||
} else if (params.queryType === 0 && item.id === firstId) { |
|
||||
flag = true; |
|
||||
centerData = realTasks.slice(index - params.pageSize, index) || []; |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
if (!flag) { |
|
||||
centerData = 0 |
|
||||
} |
|
||||
|
|
||||
console.log('111111', centerData, realTasks) |
|
||||
|
|
||||
// 1、数量大于等于15 centerData.length >= 15
|
|
||||
// 2、时间跨度大于等于15
|
|
||||
const startTime = centerData.length ? centerData[0].planStart : ''; |
|
||||
const endTime = centerData.length ? centerData[centerData.length - 1].planStart : ''; |
|
||||
let centerTime = dayjs(+startTime).add(params.pageSize, timeGranularity.value); |
|
||||
let isExceed = dayjs(+centerTime).isBefore(+endTime, timeGranularity.value) || dayjs(+centerTime).isSame(+endTime, timeGranularity.value); |
|
||||
// 3、下一页不为0 nextPage != 0
|
|
||||
let nextPage = params.queryType === 0 ? upNextPage.value : downNextPage.value; // 下一页的值
|
|
||||
|
|
||||
if (centerData.length < 15 && !isExceed && nextPage > 0) { |
|
||||
await getTasks({pageNum: nextPage, queryType: params.queryType}); |
|
||||
// centerData = await showTaskId(params, tasks.value, currRoleRealTasks.value);
|
|
||||
} |
|
||||
|
|
||||
return centerData; |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 刻度模式数据处理 |
|
||||
* 大于等于15天,真实数据的时间跨度 |
|
||||
* 或大于等于15条,真实数据的数量 |
|
||||
* 不用重新加载数据 |
|
||||
* @param {Object} params |
|
||||
*/ |
|
||||
function tasksData(query, centerData, realTasks) { |
|
||||
let params = generateGetTaskParam(query); |
|
||||
|
|
||||
params.queryType === 0 ? setPrevPlaceholderTasks(params) : setNextPlaceholderTasks(params); |
|
||||
let showTasks = tasks.value; // 显示的数据
|
|
||||
|
|
||||
|
|
||||
|
|
||||
let nextPage = param.queryType === 0 ? upNextPage.value : downNextPage.value; // 下一页的值
|
|
||||
|
|
||||
// 判断条件
|
|
||||
let isAccordTerm1 = false, isAccordTerm2 = false; |
|
||||
if (centerData.length > 0) { |
|
||||
// 1.数据数量>=15条
|
|
||||
isAccordTerm1 = centerData.length >= param.pageSize; |
|
||||
|
|
||||
// 2.数据最后一条数据时间>=当前查询的时间
|
|
||||
if (param.queryType === 0) { |
|
||||
let firstData = dayjs(centerData[0].planStart).add(param.pageSize, timeGranularity.value); |
|
||||
isAccordTerm2 = dayjs(+firstData).isBefore(+currUpTimeNode.value, timeGranularity.value) || dayjs(+firstData).isSame(+currUpTimeNode.value, timeGranularity.value); |
|
||||
} else { |
|
||||
let lastData = dayjs(centerData[centerData.length - 1].planStart).subtract(param.pageSize, timeGranularity.value); |
|
||||
isAccordTerm2 = dayjs(+lastData).isAfter(+currDownTimeNode.value, timeGranularity.value) || dayjs(+lastData).isSame(+currDownTimeNode.value, timeGranularity.value); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 3.下一页===0
|
|
||||
// 不需要添加新数据
|
|
||||
if (!isAccordTerm1 && !isAccordTerm2 && nextPage > 0) { |
|
||||
// getTasks({pageNum: nextPage, queryType: param.queryType});
|
|
||||
} else { |
|
||||
let tasksArr = [], isReplace = false, firstIndex = -1, selctedIndex = -1, replaceTime = 0; |
|
||||
showTasks.forEach((task, index) => { |
|
||||
const arr = centerData.filter(item => dayjs(+item.planStart).isSame(+task.planStart, timeGranularity.value)); |
|
||||
if (arr && arr.length) { |
|
||||
if (task.detailId) { |
|
||||
// 新数据与旧数据时间有重叠
|
|
||||
if (param.queryType === 1) { |
|
||||
selctedIndex = index; |
|
||||
tasksArr = [...arr]; |
|
||||
} else { |
|
||||
firstIndex = showTasks.findIndex(data => dayjs(+data.planStart).isSame(+task.planStart, timeGranularity.value)); |
|
||||
} |
|
||||
} else { |
|
||||
showTasks.splice(index, 1, [...arr]); |
|
||||
} |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
if (selctedIndex) { |
|
||||
showTasks.splice(selctedIndex + 1, 0, [...tasksArr]) |
|
||||
} |
|
||||
|
|
||||
if (firstIndex) { |
|
||||
showTasks.splice(firstIndex, 0, [...tasksArr]) |
|
||||
} |
|
||||
|
|
||||
showTasks.forEach((task, index) => { |
|
||||
const arr = centerData.filter(item => dayjs(+item.planStart).isSame(+task.planStart, timeGranularity.value)); |
|
||||
if (arr && arr.length) { |
|
||||
if (!task.detailId) { // 新数据与旧数据时间有重叠
|
|
||||
showTasks.splice(index, 1, [...arr]); // 这里加入的数据是array类型的, [{},{},[],[],{}]
|
|
||||
} |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
showTasks = flatten(showTasks); // 1维拍平
|
|
||||
|
|
||||
if (isAccordTerm1 && !isAccordTerm2) { |
|
||||
let len = 0; |
|
||||
let data = param.queryType === 0 ? centerData[0] : centerData[centerData.length - 1]; |
|
||||
showTasks.forEach((item, index) => { |
|
||||
if (item.id === data.id) { |
|
||||
len = index; |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
showTasks = param.queryType === 0 ? showTasks.slice(len) : showTasks.slice(0, len + 1); |
|
||||
} |
|
||||
|
|
||||
if (showTasks.length > 80) { |
|
||||
showTasks = param.queryType === 0 ? showTasks.slice(0, 80) : showTasks.slice(showTasks.length - 80); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
store.commit('task/clearTasks'); |
|
||||
param.queryType === 0 ? store.commit('task/setUpTasks', showTasks) : store.commit('task/setDownTasks', showTasks); |
|
||||
} |
|
||||
|
|
||||
// 设置时间轴向上的空数据
|
|
||||
function setPrevPlaceholderTasks() { |
|
||||
store.commit('task/setTopEnd', true); |
|
||||
let startTime = ''; |
|
||||
if (!tasks.value || !tasks.value.length) { |
|
||||
startTime = Date.now(); // 没有任务就应该是时间基准点
|
|
||||
} else { |
|
||||
startTime = tasks.value[0].planStart - 0; // 有任务就是第一个任务的计划开始时间
|
|
||||
} |
|
||||
const placeholderTasks = uni.$task.setPlaceholderTasks(startTime, true, timeGranularity.value); |
|
||||
store.commit('task/setCurrUpTimeNode', startTime); |
|
||||
store.commit('task/setUpTasks', placeholderTasks); |
|
||||
} |
|
||||
|
|
||||
// 设置时间轴向下的空数据
|
|
||||
function setNextPlaceholderTasks(params) { |
|
||||
// store.commit('task/setBottomEnd', true);
|
|
||||
console.log('ddddddddd') |
|
||||
let startTime = ''; |
|
||||
if (!tasks.value || !tasks.value.length) { |
|
||||
startTime = Date.now(); |
|
||||
} else { |
|
||||
startTime = dayjs(+tasks.value[tasks.value.length - 1].planStart).add(1, timeGranularity.value).valueOf(); |
|
||||
} |
|
||||
|
|
||||
if (params.taskId) { |
|
||||
realTasks.value.forEach(item => { |
|
||||
if (item.id === params.taskId) { |
|
||||
startTime = Number(item.planStart); |
|
||||
} |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
const initData = uni.$task.setPlaceholderTasks(startTime, false, timeGranularity.value); |
|
||||
store.commit('task/setCurrDownTimeNode', startTime); |
|
||||
store.commit('task/setDownTasks', initData); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 当日常任务发生变化时 |
|
||||
* 将新获取到的日常任务放在allTasks里 |
|
||||
*/ |
|
||||
watch(tasks, newValue => { |
|
||||
// 添加到allTasks里
|
|
||||
const index = visibleRoles.value.findIndex(role => role.id === roleId.value); |
|
||||
const arr = [...allTasks.value]; |
|
||||
arr[index].task = [...newValue]; |
|
||||
store.commit('task/setAllTasks', arr); |
|
||||
}); |
|
||||
|
|
||||
return { |
|
||||
initPlanTasks, |
|
||||
getTasks, |
|
||||
dataRender |
|
||||
} |
|
||||
} |
|
@ -1,343 +0,0 @@ |
|||||
import { computed, nextTick, watch } from 'vue'; |
|
||||
import { useStore } from 'vuex'; |
|
||||
import { flatten } from 'lodash'; |
|
||||
import dayjs from 'dayjs'; |
|
||||
|
|
||||
export default function useGetTasks() { |
|
||||
const store = useStore(); |
|
||||
const tasks = computed(() => store.state.task.tasks); |
|
||||
const timeLineType = computed(() => store.state.task.timeLineType); // 时间轴模式
|
|
||||
const realTasks = computed(() => store.state.task.realTasks); // 真实任务
|
|
||||
const downNextPage = computed(() => store.state.task.downNextPage); // 下一页
|
|
||||
const upNextPage = computed(() => store.state.task.upNextPage); // 下一页
|
|
||||
const currUpTimeNode = computed(() => store.state.task.currUpTimeNode); // 当前查询的时间
|
|
||||
const currDownTimeNode = computed(() => store.state.task.currDownTimeNode); // 当前查询的时间
|
|
||||
const roleId = computed(() => store.state.role.roleId); |
|
||||
const timeNode = computed(() => store.state.task.timeNode); |
|
||||
const timeUnit = computed(() => store.state.task.timeUnit); |
|
||||
const visibleRoles = computed(() => store.state.role.visibleRoles); |
|
||||
const allTasks = computed(() => store.state.task.allTasks); |
|
||||
const roleIndex = computed(() => store.state.role.roleIndex); |
|
||||
|
|
||||
const projectId = computed(() => store.getters['project/projectId']); |
|
||||
const timeGranularity = computed(() => store.getters['task/timeGranularity']); |
|
||||
|
|
||||
const remindData = computed(() => store.state.socket.remindData); // 小红点
|
|
||||
|
|
||||
const currRoleRealTasks = computed(() => store.state.task.currRoleRealTasks); // 当前角色的真实任务数据
|
|
||||
|
|
||||
// 初始化 定期任务
|
|
||||
async function initPlanTasks() { |
|
||||
if (timeLineType.value === 1) setNextPlaceholderTasks({}); |
|
||||
await getTasks({}); // 获取初始数据
|
|
||||
// await dataRender({});
|
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 生成getTasks所用的参数 |
|
||||
* @param {object} query getTasks传递的参数 |
|
||||
*/ |
|
||||
function generateGetTaskParam(query) { |
|
||||
return { |
|
||||
roleId: roleId.value, |
|
||||
timeNode: query.timeNode || timeNode.value, |
|
||||
timeUnit: query.timeUnit || timeUnit.value, |
|
||||
queryType: query.queryType === 0 ? 0 : 1, |
|
||||
pageNum: query.pageNum || 1, |
|
||||
pageSize: query.pageSize || uni.$taskConfig.pageCount, |
|
||||
taskId: query.taskId || '' |
|
||||
}; |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 根据时间基准点和角色查找定期任务 |
|
||||
* @param {object} query |
|
||||
* @param {string} query.roleId 角色id |
|
||||
* @param {string} query.timeNode 时间基准点 默认当前 |
|
||||
* @param {string} query.timeUnit 时间颗粒度 默认天 |
|
||||
* @param {string} query.queryNum 查找颗粒度数量 默认3个 |
|
||||
* @param {number} query.queryType 0向上查找 1向下查找(默认) 下查包含自己,上查不包含 |
|
||||
*/ |
|
||||
function getTasks(query) { |
|
||||
store.commit('task/setShowSkeleton', false); |
|
||||
|
|
||||
const params = generateGetTaskParam(query); |
|
||||
uni.$catchReq.getTaskByNum(params, (err, data) => { |
|
||||
store.commit('task/setShowSkeleton', false); |
|
||||
if (err) { |
|
||||
// TODO: 提示错误
|
|
||||
console.error('err: ', err); |
|
||||
} else { |
|
||||
store.commit('task/setShowScrollTo', true); |
|
||||
|
|
||||
params.queryType === 0 ? store.commit('task/setUpRealTasks', data.list) : store.commit('task/setDownRealTasks', data.list); |
|
||||
params.queryType === 0 ? store.commit('task/setUpNextPage', data.nextPage) : store.commit('task/setDownNextPage', data.nextPage); // 下一页
|
|
||||
|
|
||||
const index = visibleRoles.value.findIndex(role => role.id === roleId.value); |
|
||||
const arr = [...allTasks.value]; |
|
||||
arr[index].realTasks = [...realTasks.value]; |
|
||||
store.commit('task/setAllTasks', arr); |
|
||||
store.commit('task/setCurrRoleRealTasks', arr[index].realTasks); // 设置当前角色的真实任务数据
|
|
||||
|
|
||||
// 数据处理
|
|
||||
dataRender(params); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
function dataRender(params) { |
|
||||
timeLineType.value === 1 ? renderScaleTask(params) : renderConTask(params); |
|
||||
} |
|
||||
|
|
||||
// 刻度模式数据处理
|
|
||||
async function renderScaleTask(query) { |
|
||||
const params = generateGetTaskParam(query); |
|
||||
let centerData = await showTaskTime(params, tasks.value, currRoleRealTasks.value) || []; |
|
||||
await handleTasksData(params, centerData, currRoleRealTasks.value); |
|
||||
} |
|
||||
|
|
||||
// 已显示的任务第一个时间和最后一个时间
|
|
||||
async function showTaskTime(params, showTasks, realTasks) { |
|
||||
|
|
||||
// 初始值
|
|
||||
let centerData = params.queryType === 0 ? [] : realTasks.slice(0, params.pageSize); |
|
||||
|
|
||||
/** |
|
||||
* 1、判断显示任务中是否有真实任务 |
|
||||
* 1-1、有。根据id查找任务 |
|
||||
* 1-2、无。根据时间查找任务 |
|
||||
* 2、查找15个任务 |
|
||||
*/ |
|
||||
const firstDetailIndex = showTasks.findIndex(task => task.detailId); |
|
||||
|
|
||||
if (firstDetailIndex > -1) { |
|
||||
// 显示任务中有真实任务数据
|
|
||||
const firstId = showTasks[firstDetailIndex].id; |
|
||||
let lastDetailIndex = -1; |
|
||||
showTasks.forEach((item, index) => { |
|
||||
if (item.detailId) { |
|
||||
lastDetailIndex = index; |
|
||||
} |
|
||||
}) |
|
||||
const lastId = showTasks[lastDetailIndex].id; |
|
||||
|
|
||||
realTasks.forEach((item, index) => { |
|
||||
if (params.queryType === 1 && item.id === lastId) { |
|
||||
centerData = realTasks.slice(index + 1, index + 1 + params.pageSize) || []; |
|
||||
} else if (params.queryType === 0 && item.id === firstId) { |
|
||||
if (index >= params.pageSize) { |
|
||||
centerData = realTasks.slice(index - params.pageSize, index) || []; |
|
||||
} else { |
|
||||
centerData = realTasks.slice(0, index) || []; |
|
||||
} |
|
||||
} |
|
||||
}) |
|
||||
} else { |
|
||||
// 显示任务中没有真实任务数据
|
|
||||
// 已显示任务的第一个任务时间
|
|
||||
const firstTime = showTasks.length ? showTasks[0].planStart : ''; |
|
||||
// 已显示任务的最后一个任务时间
|
|
||||
const lastTime = showTasks.length ? showTasks[showTasks.length - 1].planStart : ''; |
|
||||
|
|
||||
try { |
|
||||
realTasks.forEach((item, index) => { |
|
||||
if (params.queryType === 1) { |
|
||||
if (dayjs(+lastTime).isSame(+item.planStart, timeGranularity.value) || dayjs(+lastTime).isBefore(+item.planStart, timeGranularity.value)) { |
|
||||
centerData = realTasks.slice(index, index + params.pageSize) || []; |
|
||||
throw Error(); |
|
||||
} |
|
||||
} else { |
|
||||
if (dayjs(+firstTime).isSame(+item.planStart, timeGranularity.value) || dayjs(+firstTime).isAfter(+item.planStart, timeGranularity.value)) { |
|
||||
if (index >= params.pageSize) { |
|
||||
centerData = realTasks.slice(index - params.pageSize, index) || []; |
|
||||
} else { |
|
||||
centerData = realTasks.slice(0, index) || []; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
}) |
|
||||
} catch (e) { |
|
||||
console.log('退出循环') |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
console.log('111111111', centerData); |
|
||||
return centerData; |
|
||||
} |
|
||||
|
|
||||
async function handleTasksData(params, centerData, realTasks) { |
|
||||
/** |
|
||||
* 3、查找的任务数量是否>=15 |
|
||||
* 3-1、是。 |
|
||||
* 判断时间跨度是否>=15 |
|
||||
* 3-1-1、是。显示时间刻度范围内的任务 |
|
||||
* 3-1-2、否。显示全部任务并删除多余的刻度 |
|
||||
* 3-2、否。 |
|
||||
* 判断时间跨度是否>=15 |
|
||||
* 3-2-1、是。显示时间刻度范围内的任务 |
|
||||
* 3-2-2、否。 |
|
||||
* 下一页是否为0 |
|
||||
* 3-2-2-1、是。无下一页,显示任务和刻度,之后继续展示刻度 |
|
||||
* 3-2-2-1、否。查找下一页数据并重复上述步骤 |
|
||||
*/ |
|
||||
const startTime = centerData.length ? centerData[0].planStart : ''; |
|
||||
const endTime = centerData.length ? centerData[centerData.length - 1].planStart : ''; |
|
||||
let centerTime = dayjs(+startTime).add(params.pageSize, timeGranularity.value); |
|
||||
// 时间跨度是否大于等于15
|
|
||||
let isExceed = dayjs(+centerTime).isBefore(+endTime, timeGranularity.value) || dayjs(+centerTime).isSame(+endTime, timeGranularity.value); |
|
||||
|
|
||||
let showTasks = tasks.value; // 显示的数据
|
|
||||
const nextPage = params.queryType === 0 ? upNextPage.value : downNextPage.value; // 下一页的值
|
|
||||
console.log('下一页', nextPage, isExceed, params.pageSize) |
|
||||
if (isExceed || centerData.length >= params.pageSize || nextPage === 0) { |
|
||||
params.queryType === 0 ? setPrevPlaceholderTasks(params) : setNextPlaceholderTasks(params); |
|
||||
showTasks = tasks.value; |
|
||||
console.log('fffffff', showTasks); |
|
||||
} |
|
||||
|
|
||||
if (centerData.length === 0 && nextPage === 0) { |
|
||||
params.queryType === 0 ? setPrevPlaceholderTasks(params) : setNextPlaceholderTasks(params); |
|
||||
showTasks = tasks.value; |
|
||||
console.log('ccccc', showTasks); |
|
||||
} else { |
|
||||
console.log('ttttttttt', showTasks); |
|
||||
const firstDetailIndex = showTasks.findIndex(task => task.detailId); // 显示任务中存在真实任务
|
|
||||
const firstId = firstDetailIndex > -1 ? showTasks[firstDetailIndex].id : ''; |
|
||||
let lastDetailIndex = -1; |
|
||||
showTasks.forEach((item, index) => { |
|
||||
if (item.detailId) { |
|
||||
lastDetailIndex = index; |
|
||||
} |
|
||||
}) |
|
||||
const lastId = lastDetailIndex > -1 ? showTasks[lastDetailIndex].id : ''; |
|
||||
|
|
||||
showTasks.forEach((task, index) => { |
|
||||
const arr = centerData.filter(item => dayjs(+item.planStart).isSame(+task.planStart, timeGranularity.value)); |
|
||||
if (arr && arr.length) { |
|
||||
if (firstDetailIndex > -1) { |
|
||||
if (params.queryType === 1 && task.id === lastId) { |
|
||||
showTasks.splice(index + 1, 0, [...arr]) |
|
||||
} else if (params.queryType === 0 && task.id === firstId) { |
|
||||
showTasks.splice(index, 0, [...arr]) |
|
||||
} |
|
||||
} else { |
|
||||
showTasks.splice(index, 1, [...arr]); |
|
||||
} |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
showTasks = flatten(showTasks); // 1维拍平
|
|
||||
|
|
||||
if (!isExceed) { |
|
||||
if (centerData.length < params.pageSize && nextPage > 0) { |
|
||||
await getTasks({pageNum: nextPage, queryType: params.queryType}); |
|
||||
console.log('oooooooo', showTasks); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
if (centerData.length) { |
|
||||
let len = 0; |
|
||||
let data = params.queryType === 0 ? centerData[0] : centerData[centerData.length - 1]; |
|
||||
showTasks.forEach((item, index) => { |
|
||||
if (item.id === data.id) { |
|
||||
len = index; |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
showTasks = params.queryType === 0 ? showTasks.slice(len) : showTasks.slice(0, len + 1); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
if (showTasks.length > 80) { |
|
||||
showTasks = params.queryType === 0 ? showTasks.slice(0, 80) : showTasks.slice(showTasks.length - 80); |
|
||||
} |
|
||||
|
|
||||
store.commit('task/clearTasks'); |
|
||||
params.queryType === 0 ? store.commit('task/setUpTasks', showTasks) : store.commit('task/setDownTasks', showTasks); |
|
||||
} |
|
||||
|
|
||||
// 任务模式
|
|
||||
async function renderConTask(params) { |
|
||||
let nextPage = params.queryType === 0 ? upNextPage.value : downNextPage.value; // 下一页的值
|
|
||||
let showTasks = tasks.value; |
|
||||
let centerData = await showTaskId(params, showTasks, realTasks.value) || []; |
|
||||
|
|
||||
if (centerData.length < 15 && nextPage > 0) { |
|
||||
getTasks({pageNum: nextPage, queryType: params.queryType}); |
|
||||
} else { |
|
||||
if (params.queryType === 0) { |
|
||||
showTasks = [...centerData, ...showTasks]; |
|
||||
} else { |
|
||||
showTasks = [...showTasks, ...centerData]; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
if (showTasks.length < 15 && nextPage === 0 && params.queryType === 1) { |
|
||||
getTasks({pageNum: 1, queryType: 0}); |
|
||||
} |
|
||||
|
|
||||
if (showTasks.length > 80) { |
|
||||
showTasks = params.queryType === 0 ? showTasks.slice(0, 80) : showTasks.slice(showTasks.length - 80); |
|
||||
} |
|
||||
|
|
||||
store.commit('task/clearTasks'); |
|
||||
params.queryType === 0 ? store.commit('task/setUpTasks', showTasks) : store.commit('task/setDownTasks', showTasks); |
|
||||
} |
|
||||
|
|
||||
// 设置时间轴向上的空数据
|
|
||||
function setPrevPlaceholderTasks() { |
|
||||
store.commit('task/setTopEnd', true); |
|
||||
let startTime = ''; |
|
||||
if (!tasks.value || !tasks.value.length) { |
|
||||
startTime = Date.now(); // 没有任务就应该是时间基准点
|
|
||||
} else { |
|
||||
startTime = tasks.value[0].planStart - 0; // 有任务就是第一个任务的计划开始时间
|
|
||||
} |
|
||||
const placeholderTasks = uni.$task.setPlaceholderTasks(startTime, true, timeGranularity.value); |
|
||||
store.commit('task/setCurrUpTimeNode', startTime); |
|
||||
store.commit('task/setUpTasks', placeholderTasks); |
|
||||
} |
|
||||
|
|
||||
// 设置时间轴向下的空数据
|
|
||||
function setNextPlaceholderTasks(params) { |
|
||||
// store.commit('task/setBottomEnd', true);
|
|
||||
console.log('ddddddddd') |
|
||||
let startTime = ''; |
|
||||
if (!tasks.value || !tasks.value.length) { |
|
||||
startTime = Date.now(); |
|
||||
} else { |
|
||||
startTime = dayjs(+tasks.value[tasks.value.length - 1].planStart).add(1, timeGranularity.value).valueOf(); |
|
||||
} |
|
||||
|
|
||||
if (params.taskId) { |
|
||||
realTasks.value.forEach(item => { |
|
||||
if (item.id === params.taskId) { |
|
||||
startTime = Number(item.planStart); |
|
||||
} |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
const initData = uni.$task.setPlaceholderTasks(startTime, false, timeGranularity.value); |
|
||||
store.commit('task/setCurrDownTimeNode', startTime); |
|
||||
store.commit('task/setDownTasks', initData); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 当日常任务发生变化时 |
|
||||
* 将新获取到的日常任务放在allTasks里 |
|
||||
*/ |
|
||||
watch(tasks, newValue => { |
|
||||
// 添加到allTasks里
|
|
||||
const index = visibleRoles.value.findIndex(role => role.id === roleId.value); |
|
||||
const arr = [...allTasks.value]; |
|
||||
arr[index].task = [...newValue]; |
|
||||
store.commit('task/setAllTasks', arr); |
|
||||
}); |
|
||||
|
|
||||
return { |
|
||||
initPlanTasks, |
|
||||
getTasks, |
|
||||
dataRender |
|
||||
} |
|
||||
} |
|
@ -1,263 +0,0 @@ |
|||||
import { computed, nextTick, watch } from 'vue'; |
|
||||
import { useStore } from 'vuex'; |
|
||||
import { flatten } from 'lodash'; |
|
||||
import dayjs from 'dayjs'; |
|
||||
export default function useGetTasks() { |
|
||||
const store = useStore(); |
|
||||
const tasks = computed(() => store.state.task.tasks); |
|
||||
const basicTasks = computed(() => store.state.task.basicTasks); |
|
||||
const upBasicTasks = computed(() => store.state.task.upBasicTasks); |
|
||||
const nextPage = computed(() => store.state.task.nextPage); // 下一页
|
|
||||
const upNextPage = computed(() => store.state.task.upNextPage); // 下一页
|
|
||||
// const lastPage = computed(() => store.state.task.lastPage); // 最后一页
|
|
||||
const roleId = computed(() => store.state.role.roleId); |
|
||||
const timeNode = computed(() => store.state.task.timeNode); |
|
||||
const timeUnit = computed(() => store.state.task.timeUnit); |
|
||||
const visibleRoles = computed(() => store.state.role.visibleRoles); |
|
||||
const allTasks = computed(() => store.state.task.allTasks); |
|
||||
const roleIndex = computed(() => store.state.role.roleIndex); |
|
||||
|
|
||||
const projectId = computed(() => store.getters['project/projectId']); |
|
||||
const timeGranularity = computed(() => store.getters['task/timeGranularity']); |
|
||||
|
|
||||
// 初始化 定期任务
|
|
||||
async function initPlanTasks() { |
|
||||
await getTasks({}); // 获取初始数据
|
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 根据时间基准点和角色查找定期任务 |
|
||||
* @param {object} query |
|
||||
* @param {string} query.roleId 角色id |
|
||||
* @param {string} query.timeNode 时间基准点 默认当前 |
|
||||
* @param {string} query.timeUnit 时间颗粒度 默认天 |
|
||||
* @param {string} query.queryNum 查找颗粒度数量 默认3个 |
|
||||
* @param {number} query.queryType 0向上查找 1向下查找(默认) 下查包含自己,上查不包含 |
|
||||
*/ |
|
||||
function getTasks(query) { |
|
||||
store.commit('task/setShowSkeleton', false); |
|
||||
|
|
||||
const params = { |
|
||||
roleId: roleId.value, |
|
||||
timeUnit: query.timeUnit || timeUnit.value, |
|
||||
queryType: query.queryType === 0 ? 0 : 1, |
|
||||
pageNum: query.pageNum || 1, |
|
||||
pageSize: query.pageSize || uni.$taskConfig.pageCount |
|
||||
} |
|
||||
|
|
||||
uni.$catchReq.getTaskByNum(params, (err, data) => { |
|
||||
store.commit('task/setShowSkeleton', false); |
|
||||
if (err) { |
|
||||
// TODO: 提示错误
|
|
||||
console.error('err: ', err); |
|
||||
} else { |
|
||||
store.commit('task/setShowScrollTo', true); |
|
||||
|
|
||||
if (params.queryType === 0) { |
|
||||
// 将接口返回的数据存储到store
|
|
||||
let lists = [...upBasicTasks.value, ...data.list]; |
|
||||
store.commit('task/setUpBasicTask', lists); |
|
||||
store.commit('task/setUpNextPage', data.nextPage); // 下一页
|
|
||||
} else { |
|
||||
let lists = [...basicTasks.value, ...data.list]; |
|
||||
store.commit('task/setBasicTask', lists); |
|
||||
store.commit('task/setNextPage', data.nextPage); // 下一页
|
|
||||
} |
|
||||
|
|
||||
// 刻度模式数据处理
|
|
||||
renderTask(params); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
// 刻度模式数据处理
|
|
||||
function renderTask(params) { |
|
||||
params.queryType === 0 ? setPrevPlaceholderTasks() : setNextPlaceholderTasks(); |
|
||||
tasksData(params); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 刻度模式数据处理 |
|
||||
* 大于等于15天,真实数据的时间跨度 |
|
||||
* 或大于等于15条,真实数据的数量 |
|
||||
* 不用重新加载数据 |
|
||||
* @param {Object} params |
|
||||
*/ |
|
||||
function tasksData(params) { |
|
||||
let oldTasks = tasks.value; |
|
||||
let realTasks = params.queryType === 0 ? upBasicTasks.value : basicTasks.value; |
|
||||
// 下一页的值
|
|
||||
let next = params.queryType === 0 ? upNextPage.value : nextPage.value; |
|
||||
|
|
||||
// 真实数据是否>=15条 || 是否>=15天
|
|
||||
let term = tremFilter(params); |
|
||||
|
|
||||
if (term || next === 0) { |
|
||||
// 过滤真实数据中在tasks时间段中的数据,并将最后的数据存储到tasksArr数组中
|
|
||||
let tasksArr = [], taskLen = 0, lastIndex = 0; |
|
||||
oldTasks.forEach((task, index) => { |
|
||||
const oldArr = oldTasks.filter(item => dayjs(+item.planStart).isSame(+task.planStart, timeGranularity.value)); |
|
||||
const arr = realTasks.filter(item => dayjs(+item.planStart).isSame(+task.planStart, timeGranularity.value)); |
|
||||
|
|
||||
if (arr && arr.length) { |
|
||||
if (task.detailId) { |
|
||||
tasksArr = [...arr]; |
|
||||
lastIndex = index; |
|
||||
} else { |
|
||||
taskLen += arr.length; // 在时间刻度内的数据量
|
|
||||
oldTasks.splice(index, 1, [...arr]); // 这里加入的数据是array类型的, [{},{},[],[],{}]
|
|
||||
} |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
// 数据与上一页最后一个数据时间节点相同
|
|
||||
if (lastIndex) { |
|
||||
taskLen += tasksArr.length; |
|
||||
oldTasks.splice(lastIndex, 0, [...tasksArr]); |
|
||||
} |
|
||||
|
|
||||
oldTasks = flatten(oldTasks); // 1维拍平
|
|
||||
|
|
||||
if (taskLen === params.pageSize) { |
|
||||
// 如果真实任务所有数据都在tasks时间段中,则将所有任务显示,并且从最后一个真实任务的时间截止,存储到tasks中
|
|
||||
let len = 0; |
|
||||
oldTasks.forEach((item, index) => { |
|
||||
if (item.id === realTasks[realTasks.length - 1].id) { |
|
||||
len = index; |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
oldTasks = oldTasks.slice(0, len + 1); |
|
||||
params.queryType === 0 ? store.commit('task/setUpBasicTask', []) : store.commit('task/setBasicTask', []); |
|
||||
} else { |
|
||||
// 只有一部分真实数据展示,展示tasks时间段加时间段内的真实数据
|
|
||||
params.queryType === 0 ? store.commit('task/setUpBasicTask', realTasks.slice(taskLen)) : store.commit('task/setBasicTask', realTasks.slice(taskLen)); |
|
||||
} |
|
||||
|
|
||||
store.commit('task/clearTasks'); |
|
||||
params.queryType === 0 ? store.commit('task/setUpTasks', oldTasks) : store.commit('task/setDownTasks', oldTasks); |
|
||||
} else { |
|
||||
if (next > 0) { |
|
||||
getTasks({pageNum: params.pageNum, queryType: params.queryType}); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 数据筛选条件 |
|
||||
* 大于等于15天,真实数据的时间跨度,且大于等于15条,真实数据的数量,不用重新加载数据 |
|
||||
*/ |
|
||||
function tremFilter(params) { |
|
||||
let time1 = 0, time2 = 0; |
|
||||
let realTasks = basicTasks.value; |
|
||||
|
|
||||
if (realTasks.length > 0) { |
|
||||
// 真实数据第一条数据加上15天后的时间
|
|
||||
time1 = dayjs(+realTasks[0].planStart).add(params.pageSize, timeGranularity.value); |
|
||||
// 真实数据最后一条数据的时间
|
|
||||
time2 = dayjs(realTasks[realTasks.length - 1].planStart, timeGranularity.value); |
|
||||
} |
|
||||
|
|
||||
let result1 = false, result2 = false; |
|
||||
// 判断真实任务的时间跨度是否在15天之内
|
|
||||
if (time1 > 0 && time2 > 0) { |
|
||||
// 两个时间是否相等
|
|
||||
result1 = dayjs(time1).isSame(time2, timeGranularity.value); |
|
||||
// time1是否大于time2
|
|
||||
result2 = dayjs(time1).isAfter(time2, timeGranularity.value); |
|
||||
} |
|
||||
|
|
||||
let term1 = realTasks.length >= params.pageSize; // 大于等于15条,真实数据的数量
|
|
||||
// 大于等于15天,真实数据的时间跨度
|
|
||||
let term2 = result1 || result2; |
|
||||
|
|
||||
return term1 || term2; |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 用拿到的新数据 替换 时间刻度/旧数据 |
|
||||
* 先对比 新旧数据的 始末时间 补齐刻度 |
|
||||
* 再遍历对比 用任务替换刻度 |
|
||||
* @param {array} data 服务端返回的新数据 上边已经处理过空值 |
|
||||
* @param {number} type 0 -> 向上 1->向下 |
|
||||
*/ |
|
||||
// function replacePrevData(data, type) {
|
|
||||
// const obj = { tasks: tasks.value, data, timeGranularity: timeGranularity.value };
|
|
||||
// let oldTasks = fillPlaceholderTask(obj); // 已经上下补齐时间刻度的
|
|
||||
// console.log('oldTasks', oldTasks)
|
|
||||
// // 遍历对比 用任务替换刻度
|
|
||||
// // TODO: tasks越来越多 遍历越来越多 需要优化
|
|
||||
// oldTasks.forEach((taskItem, index) => {
|
|
||||
// const arr = data.filter(dataItem => dayjs(+dataItem.planStart).isSame(+taskItem.planStart, timeGranularity.value));
|
|
||||
// if (arr && arr.length) {
|
|
||||
// oldTasks.splice(index, 1, [...arr]); // 这里加入的数据是array类型的, [{},{},[],[],{}]
|
|
||||
// }
|
|
||||
// });
|
|
||||
|
|
||||
// oldTasks = flatten(oldTasks); // 1维拍平
|
|
||||
|
|
||||
// store.commit('task/clearTasks');
|
|
||||
// type === 0 ? store.commit('task/setUpTasks', oldTasks) : store.commit('task/setDownTasks', oldTasks);
|
|
||||
// }
|
|
||||
|
|
||||
/** |
|
||||
* 超出旧数据上、下限 补齐时间刻度到新数据的起始时间颗粒度 |
|
||||
*/ |
|
||||
// function fillPlaceholderTask(obj) {
|
|
||||
// const { prev, next } = uni.$task.computeFillPlaceholderTaskCount(obj);
|
|
||||
// if (prev) {
|
|
||||
// const newTasks = uni.$task.setPlaceholderTasks(+obj.tasks[0].planStart, true, obj.timeGranularity, prev);
|
|
||||
// store.commit('task/setUpTasks', newTasks);
|
|
||||
// }
|
|
||||
// if (next) {
|
|
||||
// const newTasks = uni.$task.setPlaceholderTasks(+obj.tasks[obj.tasks.length - 1].planStart, false, obj.timeGranularity, next);
|
|
||||
// store.commit('task/setDownTasks', newTasks);
|
|
||||
// }
|
|
||||
// return tasks.value;
|
|
||||
// }
|
|
||||
|
|
||||
// 设置时间轴向上的空数据
|
|
||||
function setPrevPlaceholderTasks() { |
|
||||
store.commit('task/setTopEnd', true); |
|
||||
let startTime = ''; |
|
||||
if (!tasks.value || !tasks.value.length) { |
|
||||
startTime = Date.now(); // 没有任务就应该是时间基准点
|
|
||||
} else { |
|
||||
startTime = tasks.value[0].planStart - 0; // 有任务就是第一个任务的计划开始时间
|
|
||||
} |
|
||||
const placeholderTasks = uni.$task.setPlaceholderTasks(startTime, true, timeGranularity.value); |
|
||||
store.commit('task/setUpTasks', placeholderTasks); |
|
||||
} |
|
||||
|
|
||||
// 设置时间轴向下的空数据
|
|
||||
function setNextPlaceholderTasks() { |
|
||||
// store.commit('task/setBottomEnd', true);
|
|
||||
let startTime = ''; |
|
||||
if (!tasks.value || !tasks.value.length) { |
|
||||
startTime = Date.now(); |
|
||||
} else { |
|
||||
startTime = dayjs(+tasks.value[tasks.value.length - 1].planStart).add(1, timeGranularity.value); |
|
||||
} |
|
||||
const initData = uni.$task.setPlaceholderTasks(startTime, false, timeGranularity.value); |
|
||||
store.commit('task/setDownTasks', initData); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 当日常任务发生变化时 |
|
||||
* 将新获取到的日常任务放在allTasks里 |
|
||||
*/ |
|
||||
watch(tasks, newValue => { |
|
||||
// console.log('newValue----->tasks: ', tasks.value);
|
|
||||
// 添加到allTasks里
|
|
||||
const index = visibleRoles.value.findIndex(role => role.id === roleId.value); |
|
||||
const arr = [...allTasks.value]; |
|
||||
arr[index].task = [...newValue]; |
|
||||
store.commit('task/setAllTasks', arr); |
|
||||
}); |
|
||||
|
|
||||
return { |
|
||||
initPlanTasks, |
|
||||
getTasks, |
|
||||
renderTask |
|
||||
}; |
|
||||
} |
|
@ -1,336 +0,0 @@ |
|||||
import { computed, nextTick, watch } from 'vue'; |
|
||||
import { useStore } from 'vuex'; |
|
||||
import { flatten } from 'lodash'; |
|
||||
import dayjs from 'dayjs'; |
|
||||
|
|
||||
export default function useGetTasks() { |
|
||||
const store = useStore(); |
|
||||
const tasks = computed(() => store.state.task.tasks); |
|
||||
const timeLineType = computed(() => store.state.task.timeLineType); // 时间轴模式
|
|
||||
const realTasks = computed(() => store.state.task.realTasks); // 真实任务
|
|
||||
const downNextPage = computed(() => store.state.task.downNextPage); // 下一页
|
|
||||
const upNextPage = computed(() => store.state.task.upNextPage); // 下一页
|
|
||||
const currUpTimeNode = computed(() => store.state.task.currUpTimeNode); // 当前查询的时间
|
|
||||
const currDownTimeNode = computed(() => store.state.task.currDownTimeNode); // 当前查询的时间
|
|
||||
const roleId = computed(() => store.state.role.roleId); |
|
||||
const timeNode = computed(() => store.state.task.timeNode); |
|
||||
const timeUnit = computed(() => store.state.task.timeUnit); |
|
||||
const visibleRoles = computed(() => store.state.role.visibleRoles); |
|
||||
const allTasks = computed(() => store.state.task.allTasks); |
|
||||
const roleIndex = computed(() => store.state.role.roleIndex); |
|
||||
|
|
||||
const projectId = computed(() => store.getters['project/projectId']); |
|
||||
const timeGranularity = computed(() => store.getters['task/timeGranularity']); |
|
||||
|
|
||||
const remindData = computed(() => store.state.socket.remindData); // 小红点
|
|
||||
|
|
||||
const currRoleRealTasks = computed(() => store.state.task.currRoleRealTasks); // 当前角色的真实任务数据
|
|
||||
const currRoleShowTasks = computed(() => store.state.task.currRoleShowTasks); // 当前角色的展示任务数据
|
|
||||
const currLocationTaskId = computed(() => store.state.socket.currLocationTaskId); |
|
||||
|
|
||||
// 初始化 定期任务
|
|
||||
async function initPlanTasks() { |
|
||||
if (timeLineType.value === 1) setNextPlaceholderTasks({}); |
|
||||
console.log('查询定期任务'); |
|
||||
await getTasks({}); // 获取初始数据
|
|
||||
// await dataRender({});
|
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 生成getTasks所用的参数 |
|
||||
* @param {object} query getTasks传递的参数 |
|
||||
*/ |
|
||||
function generateGetTaskParam(query) { |
|
||||
return { |
|
||||
roleId: roleId.value, |
|
||||
timeNode: query.timeNode || timeNode.value, |
|
||||
timeUnit: query.timeUnit || timeUnit.value, |
|
||||
queryType: query.queryType === 0 ? 0 : 1, |
|
||||
pageNum: query.pageNum || 1, |
|
||||
pageSize: query.pageSize || uni.$taskConfig.pageCount, |
|
||||
taskId: query.taskId || currLocationTaskId.value |
|
||||
}; |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 根据时间基准点和角色查找定期任务 |
|
||||
* @param {object} query |
|
||||
* @param {string} query.roleId 角色id |
|
||||
* @param {string} query.timeNode 时间基准点 默认当前 |
|
||||
* @param {string} query.timeUnit 时间颗粒度 默认天 |
|
||||
* @param {string} query.queryNum 查找颗粒度数量 默认3个 |
|
||||
* @param {number} query.queryType 0向上查找 1向下查找(默认) 下查包含自己,上查不包含 |
|
||||
*/ |
|
||||
function getTasks(query) { |
|
||||
store.commit('task/setShowSkeleton', false); |
|
||||
|
|
||||
const params = generateGetTaskParam(query); |
|
||||
console.log('查询定期任务api', params); |
|
||||
uni.$catchReq.getTaskByNum(params, (err, data) => { |
|
||||
store.commit('task/setShowSkeleton', false); |
|
||||
if (err) { |
|
||||
// TODO: 提示错误
|
|
||||
console.error('err: ', err); |
|
||||
} else { |
|
||||
store.commit('task/setShowScrollTo', true); |
|
||||
|
|
||||
params.queryType === 0 ? store.commit('task/setUpRealTasks', data.list) : store.commit('task/setDownRealTasks', data.list); |
|
||||
|
|
||||
console.log('查询到的真实任务', data.list); |
|
||||
const index = visibleRoles.value.findIndex(role => role.id === roleId.value); |
|
||||
console.log('api当前角色id', roleId.value, index); |
|
||||
const arr = [...allTasks.value]; |
|
||||
arr[index].realTasks = [...realTasks.value]; |
|
||||
arr[index].upNextPage = params.queryType === 0 ? data.nextPage : 1; |
|
||||
arr[index].downNextPage = params.queryType === 1 ? data.nextPage : 1; |
|
||||
store.commit('task/setAllTasks', arr); |
|
||||
console.log('设置当前真实任务', arr[index].realTasks); |
|
||||
store.commit('task/setCurrRoleRealTasks', arr[index].realTasks); // 设置当前角色的真实任务数据
|
|
||||
params.queryType === 0 ? store.commit('task/setUpNextPage', arr[index].upNextPage) : store.commit('task/setDownNextPage', arr[index].downNextPage); // 下一页
|
|
||||
|
|
||||
// 如果第一次渲染但没有空数据则加载空数据
|
|
||||
if (!currRoleShowTasks.value || !currRoleShowTasks.value.length && timeLineType.value === 1) { |
|
||||
setNextPlaceholderTasks(params); |
|
||||
} |
|
||||
|
|
||||
// 数据处理
|
|
||||
dataRender(params); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
function dataRender(params) { |
|
||||
timeLineType.value === 1 ? renderScaleTask(params) : renderConTask(params); |
|
||||
} |
|
||||
|
|
||||
// 刻度模式数据处理
|
|
||||
async function renderScaleTask(query) { |
|
||||
const params = generateGetTaskParam(query); |
|
||||
console.log('当前角色id', roleId.value); |
|
||||
console.log('当前角色的真实任务', currRoleRealTasks.value); |
|
||||
let centerData = await showTaskTime(params, currRoleShowTasks.value, currRoleRealTasks.value) || []; |
|
||||
console.log('需要显示的真实任务', centerData); |
|
||||
await handleTasksData(params, centerData, currRoleRealTasks.value); |
|
||||
} |
|
||||
|
|
||||
// 已显示的任务第一个时间和最后一个时间
|
|
||||
async function showTaskTime(params, showTasks, realTasks) { |
|
||||
/** |
|
||||
* 1、判断显示任务中是否有真实任务 |
|
||||
* 1-1、有。根据id查找任务 |
|
||||
* 1-2、无。根据时间查找任务 |
|
||||
* 2、查找15个任务 |
|
||||
*/ |
|
||||
|
|
||||
// 初始值
|
|
||||
// 显示任务中没有真实任务数据
|
|
||||
let centerData = []; |
|
||||
if (realTasks.length > params.pageSize && params.queryType === 0) { |
|
||||
centerData = realTasks.slice(realTasks.length - params.pageSize); |
|
||||
} else { |
|
||||
centerData = realTasks.slice(0, params.pageSize); |
|
||||
} |
|
||||
|
|
||||
const firstDetailIndex = showTasks.findIndex(task => task.detailId); |
|
||||
if (firstDetailIndex > -1) { |
|
||||
// 显示任务中有真实任务数据
|
|
||||
const firstId = showTasks[firstDetailIndex].id; |
|
||||
let lastDetailIndex = -1; |
|
||||
showTasks.forEach((item, index) => { |
|
||||
if (item.detailId) { |
|
||||
lastDetailIndex = index; |
|
||||
} |
|
||||
}) |
|
||||
const lastId = showTasks[lastDetailIndex].id; |
|
||||
|
|
||||
realTasks.forEach((item, index) => { |
|
||||
if (params.queryType === 1 && item.id === lastId) { |
|
||||
centerData = realTasks.slice(index + 1, index + 1 + params.pageSize); |
|
||||
} else if (params.queryType === 0 && item.id === firstId) { |
|
||||
centerData = index >= params.pageSize ? realTasks.slice(index - params.pageSize, index) : realTasks.slice(0, index); |
|
||||
} |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
return centerData; |
|
||||
} |
|
||||
|
|
||||
async function handleTasksData(params, centerData, realTasks) { |
|
||||
/** |
|
||||
* 3、查找的任务数量是否>=15 |
|
||||
* 3-1、是。 |
|
||||
* 判断时间跨度是否>=15 |
|
||||
* 3-1-1、是。显示时间刻度范围内的任务 |
|
||||
* 3-1-2、否。显示全部任务并删除多余的刻度 |
|
||||
* 3-2、否。 |
|
||||
* 判断时间跨度是否>=15 |
|
||||
* 3-2-1、是。显示时间刻度范围内的任务 |
|
||||
* 3-2-2、否。 |
|
||||
* 下一页是否为0 |
|
||||
* 3-2-2-1、是。无下一页,显示任务和刻度,之后继续展示刻度 |
|
||||
* 3-2-2-1、否。查找下一页数据并重复上述步骤 |
|
||||
*/ |
|
||||
let showTasks = currRoleShowTasks.value; // 显示的数据
|
|
||||
const nextPage = params.queryType === 0 ? upNextPage.value : downNextPage.value; // 下一页的值
|
|
||||
if (centerData.length) { |
|
||||
let centerDataTime = '', // 中间数据的时间
|
|
||||
scaleTime = '', // 空时间节点的开始时间
|
|
||||
centerTime = '', // 中间数据+/-15后的数据
|
|
||||
isExceed = false; // 时间跨度是否大于15
|
|
||||
|
|
||||
if (params.queryType) { |
|
||||
centerDataTime = centerData[centerData.length - 1].planStart; |
|
||||
scaleTime = currDownTimeNode.value; |
|
||||
centerTime = dayjs(+centerDataTime).subtract(params.pageSize, timeGranularity.value); |
|
||||
isExceed = dayjs(+centerTime).isSame(+scaleTime, timeGranularity.value) || dayjs(+centerTime).isAfter(+scaleTime, timeGranularity.value) |
|
||||
} else { |
|
||||
centerDataTime = centerData[0].planStart; |
|
||||
scaleTime = currUpTimeNode.value; |
|
||||
centerTime = dayjs(+centerDataTime).add(params.pageSize, timeGranularity.value); |
|
||||
isExceed = dayjs(+centerTime).isSame(+scaleTime, timeGranularity.value) || dayjs(+centerTime).isBefore(+scaleTime, timeGranularity.value) |
|
||||
} |
|
||||
|
|
||||
const firstDetailIndex = showTasks.findIndex(task => task.detailId); // 显示任务中存在真实任务
|
|
||||
const firstId = firstDetailIndex > -1 ? showTasks[firstDetailIndex].id : ''; |
|
||||
let lastDetailIndex = -1; |
|
||||
showTasks.forEach((item, index) => { |
|
||||
if (item.detailId) { |
|
||||
lastDetailIndex = index; |
|
||||
} |
|
||||
}) |
|
||||
const lastId = lastDetailIndex > -1 ? showTasks[lastDetailIndex].id : ''; |
|
||||
|
|
||||
showTasks.forEach((task, index) => { |
|
||||
const arr = centerData.filter(item => dayjs(+item.planStart).isSame(+task.planStart, timeGranularity.value)); |
|
||||
if (arr.length) { |
|
||||
if (params.queryType === 1 && task.id === lastId) { |
|
||||
showTasks.splice(index + 1, 0, [...arr]) |
|
||||
} else if (params.queryType === 0 && task.id === firstId) { |
|
||||
showTasks.splice(index, 0, [...arr]) |
|
||||
} else { |
|
||||
showTasks.splice(index, 1, [...arr]) |
|
||||
} |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
showTasks = flatten(showTasks); // 1维拍平
|
|
||||
|
|
||||
if (!isExceed) { |
|
||||
if (centerData.length >= params.pageSize) { |
|
||||
let len = -1; |
|
||||
let data = params.queryType === 0 ? centerData[0] : centerData[centerData.length - 1]; |
|
||||
showTasks.forEach((item, index) => { |
|
||||
if (item.id === data.id) { |
|
||||
len = index; |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
if (len > -1) { |
|
||||
showTasks = params.queryType === 0 ? showTasks.slice(len) : showTasks.slice(0, len + 1); |
|
||||
} |
|
||||
} else if (nextPage > 0) { |
|
||||
console.log('数据不为空,时间跨度小于15') |
|
||||
getTasks({pageNum: nextPage, queryType: params.queryType}); |
|
||||
} |
|
||||
} |
|
||||
} else { |
|
||||
if (nextPage > 0) { |
|
||||
console.log('数据为空') |
|
||||
getTasks({pageNum: nextPage, queryType: params.queryType}); |
|
||||
} else { |
|
||||
params.queryType === 0 ? setPrevPlaceholderTasks(params) : setNextPlaceholderTasks(params); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// if (showTasks.length > 30) {
|
|
||||
// showTasks = params.queryType === 0 ? showTasks.slice(0, 80) : showTasks.slice(showTasks.length - 80);
|
|
||||
// }
|
|
||||
|
|
||||
store.commit('task/clearTasks'); |
|
||||
params.queryType === 0 ? store.commit('task/setUpTasks', showTasks) : store.commit('task/setDownTasks', showTasks); |
|
||||
} |
|
||||
|
|
||||
// 任务模式
|
|
||||
async function renderConTask(params) { |
|
||||
let nextPage = params.queryType === 0 ? upNextPage.value : downNextPage.value; // 下一页的值
|
|
||||
let showTasks = currRoleShowTasks.value; |
|
||||
let centerData = await showTaskTime(params, showTasks, currRoleRealTasks.value) || []; |
|
||||
|
|
||||
if (centerData.length < 15 && nextPage > 0) { |
|
||||
getTasks({pageNum: nextPage, queryType: params.queryType}); |
|
||||
} else { |
|
||||
if (params.queryType === 0) { |
|
||||
showTasks = [...centerData, ...showTasks]; |
|
||||
} else { |
|
||||
showTasks = [...showTasks, ...centerData]; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
if (showTasks.length < 15 && nextPage === 0 && params.queryType === 1) { |
|
||||
getTasks({pageNum: 1, queryType: 0}); |
|
||||
} |
|
||||
|
|
||||
if (showTasks.length > 80) { |
|
||||
showTasks = params.queryType === 0 ? showTasks.slice(0, 80) : showTasks.slice(showTasks.length - 80); |
|
||||
} |
|
||||
|
|
||||
store.commit('task/clearTasks'); |
|
||||
params.queryType === 0 ? store.commit('task/setUpTasks', showTasks) : store.commit('task/setDownTasks', showTasks); |
|
||||
} |
|
||||
|
|
||||
// 设置时间轴向上的空数据
|
|
||||
function setPrevPlaceholderTasks() { |
|
||||
store.commit('task/setTopEnd', true); |
|
||||
let startTime = ''; |
|
||||
if (!currRoleShowTasks.value || !currRoleShowTasks.value.length) { |
|
||||
startTime = Date.now(); // 没有任务就应该是时间基准点
|
|
||||
} else { |
|
||||
startTime = currRoleShowTasks.value[0].planStart - 0; // 有任务就是第一个任务的计划开始时间
|
|
||||
} |
|
||||
const placeholderTasks = uni.$task.setPlaceholderTasks(startTime, true, timeGranularity.value); |
|
||||
store.commit('task/setCurrUpTimeNode', startTime); |
|
||||
store.commit('task/setUpTasks', placeholderTasks); |
|
||||
} |
|
||||
|
|
||||
// 设置时间轴向下的空数据
|
|
||||
function setNextPlaceholderTasks(params) { |
|
||||
// store.commit('task/setBottomEnd', true);
|
|
||||
let startTime = ''; |
|
||||
if (!currRoleShowTasks.value || !currRoleShowTasks.value.length) { |
|
||||
startTime = Date.now(); |
|
||||
} else { |
|
||||
startTime = dayjs(+currRoleShowTasks.value[currRoleShowTasks.value.length - 1].planStart).add(1, timeGranularity.value).valueOf(); |
|
||||
} |
|
||||
|
|
||||
if (params.taskId) { |
|
||||
currRoleRealTasks.value.forEach(item => { |
|
||||
if (item.id === params.taskId) { |
|
||||
startTime = Number(item.planStart); |
|
||||
} |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
const initData = uni.$task.setPlaceholderTasks(startTime, false, timeGranularity.value); |
|
||||
store.commit('task/setCurrDownTimeNode', startTime); |
|
||||
store.commit('task/setDownTasks', initData); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 当日常任务发生变化时 |
|
||||
* 将新获取到的日常任务放在allTasks里 |
|
||||
*/ |
|
||||
watch(tasks, () => { |
|
||||
// 添加到allTasks里
|
|
||||
const index = visibleRoles.value.findIndex(role => role.id === roleId.value); |
|
||||
const arr = [...allTasks.value]; |
|
||||
arr[index].task = [...tasks.value]; |
|
||||
store.commit('task/setAllTasks', arr); |
|
||||
store.commit('task/setCurrRoleShowTasks', arr[index].task); // 设置当前角色的展示任务数据
|
|
||||
}); |
|
||||
|
|
||||
return { |
|
||||
initPlanTasks, |
|
||||
getTasks, |
|
||||
dataRender |
|
||||
} |
|
||||
} |
|
@ -0,0 +1,26 @@ |
|||||
|
<template> |
||||
|
<!-- 考勤详情页 --> |
||||
|
<p-check-work-detail v-if="key === 'checkWork'" :clockParams="params"></p-check-work-detail> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { ref, onMounted } from 'vue'; |
||||
|
import pCheckWorkDetail from "@/plugins/p-check-work/detail"; |
||||
|
|
||||
|
let key = ref(null); |
||||
|
let params = ref({}); |
||||
|
|
||||
|
onMounted(() => { |
||||
|
key.value = uni.$storage.getStorageSync('pluginKey'); |
||||
|
}) |
||||
|
|
||||
|
// 查看当前员工的打卡记录 |
||||
|
function checkClock(data) { |
||||
|
params.value = data; |
||||
|
// uni.$storage.setStorageSync('pluginKey', 'checkWork'); |
||||
|
key.value = 'checkWork'; |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
</style> |
@ -1,142 +0,0 @@ |
|||||
<template> |
|
||||
<theme :style="{ height: height }" class="flex flex-col overflow-hidden u-font-14"> |
|
||||
<!-- 标题栏 --> |
|
||||
<Title /> |
|
||||
|
|
||||
<view class="container flex flex-col flex-1 mx-auto overflow-hidden bg-gray-100"> |
|
||||
<!-- 角色栏 --> |
|
||||
<Roles /> |
|
||||
|
|
||||
<!-- 日常任务面板 --> |
|
||||
<Globals /> |
|
||||
|
|
||||
<!-- 定期任务面板 --> |
|
||||
<TimeLine @getTasks="getTasks" class="flex-1 overflow-hidden" ref="timeLine" /> |
|
||||
|
|
||||
<!-- TODO: DEBUG: --> |
|
||||
<u-button @click="$store.commit('setTheme', 'theme-test')">测试切换主题</u-button> |
|
||||
</view> |
|
||||
</theme> |
|
||||
</template> |
|
||||
|
|
||||
<script setup> |
|
||||
import { ref, computed, watch, onMounted } from 'vue'; |
|
||||
import { useStore } from 'vuex'; |
|
||||
import useInit from '@/hooks/project/useInit'; |
|
||||
import useGetTasks from '@/hooks/project/useGetTasks'; |
|
||||
|
|
||||
const initHook = useInit(); |
|
||||
const getTasksHook = useGetTasks(); |
|
||||
const store = useStore(); |
|
||||
const roleId = computed(() => store.state.role.roleId); |
|
||||
const timeNode = computed(() => store.state.task.timeNode); |
|
||||
const timeUnit = computed(() => store.state.task.timeUnit); |
|
||||
const projectId = computed(() => store.getters['project/projectId']); |
|
||||
const userId = computed(() => store.getters['user/userId']); |
|
||||
const newProjectInfo = computed(() => store.state.task.newProjectInfo); |
|
||||
const showScrollTo = computed(() => store.state.task.showScrollTo); |
|
||||
const height = ref(null); |
|
||||
const timeLine = ref(null); |
|
||||
|
|
||||
onMounted(() => { |
|
||||
const system = uni.getSystemInfoSync(); |
|
||||
height.value = `${system.windowHeight}px`; |
|
||||
}); |
|
||||
|
|
||||
// 获取可变全局任务 |
|
||||
function getGlobalData() { |
|
||||
const param = { |
|
||||
roleId: roleId.value, |
|
||||
timeNode: timeNode.value, |
|
||||
timeUnit: timeUnit.value, |
|
||||
projectId: projectId.value, |
|
||||
}; |
|
||||
store.dispatch('task/getGlobal', param); |
|
||||
} |
|
||||
|
|
||||
// 清除已有的任务数据 |
|
||||
function clearTasksData() { |
|
||||
// 清空日常任务的数据 |
|
||||
store.commit('task/setPermanents', []); |
|
||||
store.commit('task/setDailyTasks', []); |
|
||||
// 清空定期任务数据 |
|
||||
store.commit('task/clearTasks'); |
|
||||
// 到顶的标志复位 |
|
||||
// 到底的标志复位 |
|
||||
store.commit('task/clearEndFlag'); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 当时间基准点发生变化时 |
|
||||
* 重新根据时间和角色查询普通日常任务 |
|
||||
* 永久日常任务不发生 改变 |
|
||||
*/ |
|
||||
watch(timeNode, newValue => { |
|
||||
if (newValue && roleId.value) { |
|
||||
console.log('当时间基准点发生变化时'); |
|
||||
clearTasksData(); |
|
||||
getGlobalData(); // 查可变日常任务 |
|
||||
getTasksHook.initPlanTasks(); // 处理定期任务 |
|
||||
|
|
||||
// 滚动到对应位置 |
|
||||
let timer = null; |
|
||||
timer = setInterval(() => { |
|
||||
if (showScrollTo.value) { |
|
||||
clearInterval(timer); |
|
||||
console.log('timeLine: ', timeLine); |
|
||||
timeLine.value.setScrollPosition(); |
|
||||
} |
|
||||
}, 500); |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
/** |
|
||||
* 当角色发生变化时 |
|
||||
* 重新查询永久日常任务和普通日常任务 |
|
||||
* 注意: 切换角色后 重新设置了时间基准点 时间基准点一定会变 |
|
||||
* 所以监听时间基准点获取 可变日常任务即可 这里不用获取 避免重复获取 |
|
||||
*/ |
|
||||
watch(roleId, newValue => { |
|
||||
if (newValue) { |
|
||||
console.log('当角色发生变化时', newValue); |
|
||||
store.commit('task/setTimeNode', Date.now()); |
|
||||
// 根据角色查找永久的日常任务 |
|
||||
const params = { |
|
||||
roleId: newValue, |
|
||||
projectId: projectId.value, |
|
||||
}; |
|
||||
store.dispatch('task/getPermanent', params); |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
/** |
|
||||
* 当时间基准点发生变化时 |
|
||||
* 重新根据时间和角色查询普通日常任务 |
|
||||
* 永久日常任务不发生改变 |
|
||||
*/ |
|
||||
watch(newProjectInfo, newValue => { |
|
||||
console.log('当时间基准点发生变化时'); |
|
||||
if (newValue && newValue.value.projectId && newValue.value.url) { |
|
||||
uni.$u.route('/', { |
|
||||
u: userId.value, |
|
||||
p: newValue.value.projectId, |
|
||||
url: newValue.value.url, |
|
||||
}); |
|
||||
clearTasksData(); |
|
||||
store.commit('role/setRoleId', ''); |
|
||||
const options = uni.$route.query; |
|
||||
|
|
||||
initHook.init(options); |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
function getTasks(params) { |
|
||||
getTasksHook.initPlanTasks(params); // 处理定期任务 |
|
||||
} |
|
||||
</script> |
|
||||
|
|
||||
<style lang="scss" scoped> |
|
||||
.border-b { |
|
||||
border-bottom: 1px solid #e4e7ed; |
|
||||
} |
|
||||
</style> |
|
@ -1,66 +0,0 @@ |
|||||
<template> |
|
||||
<theme class="min-h-full"> |
|
||||
<view class="px-3 pt-1" v-if="listRef && listRef.length"> |
|
||||
<view class="bg-white my-2 rounded-md p-3 text-gray-400" v-for="item in listRef"> |
|
||||
<!-- 插件名称和提交时间显示 --> |
|
||||
<view class="flex justify-between mb-2"> |
|
||||
<view class="text-gray-800">{{ deliverName }}</view> |
|
||||
<view class="text-xs">{{ dayjs(+item.submitTime).format('MM-DD HH:mm') }}</view> |
|
||||
</view> |
|
||||
<!-- 提交的链接 --> |
|
||||
<DeliverLink v-if="item.details[0]" :link="item.details[0]" /> |
|
||||
<!-- 该插件物的审核人 --> |
|
||||
<view class="mb-1 mt-3">审核人</view> |
|
||||
<view class="flex justify-between mb-2" v-for="checkItem in item.checkerList"> |
|
||||
<view> |
|
||||
<view class="mb-1 text-gray-800 font-semibold"> |
|
||||
{{ checkItem.checkerName }} |
|
||||
</view> |
|
||||
<view class="mb-1 text-xs"> |
|
||||
{{ checkItem.remark }} |
|
||||
</view> |
|
||||
<view class="mb-1 text-xs" v-if="+checkItem.checkTime > 0"> |
|
||||
{{ dayjs(+checkItem.checkTime).format('MM-DD HH:mm') }} |
|
||||
</view> |
|
||||
</view> |
|
||||
<view class="text-center text-xs"> |
|
||||
<view v-if="checkItem.status == null" class="text-gray-400">待审核</view> |
|
||||
<view v-else-if="checkItem.status === 1"> |
|
||||
<view class="text-green-600 mb-1">已通过</view> |
|
||||
<zwp-ring-timing mode="chart" :value="checkItem.score" active-color="#F59E0B" :radius="30" :bar-width="4"> |
|
||||
<text class="text-yellow-500 font-medium">{{ checkItem.score }}</text> |
|
||||
</zwp-ring-timing> |
|
||||
</view> |
|
||||
<view class="text-red-600" v-else>已驳回</view> |
|
||||
</view> |
|
||||
</view> |
|
||||
</view> |
|
||||
</view> |
|
||||
|
|
||||
<u-empty text="暂无记录" mode="history" style="padding-top: 120rpx" v-else></u-empty> |
|
||||
</theme> |
|
||||
</template> |
|
||||
|
|
||||
<script setup> |
|
||||
import { ref } from 'vue'; |
|
||||
import { onLoad } from '@dcloudio/uni-app'; |
|
||||
import dayjs from 'dayjs'; |
|
||||
|
|
||||
const listRef = ref([]); |
|
||||
const deliverName = ref(''); |
|
||||
|
|
||||
onLoad(options => { |
|
||||
// 根据交付物id获取上传记录 |
|
||||
(async function getHistory() { |
|
||||
try { |
|
||||
const param = { deliverId: options.deliverId }; |
|
||||
const data = await uni.$u.api.getDeliverHistory(param); |
|
||||
deliverName.value = data.deliverName; |
|
||||
listRef.value = data.deliverRecordList; |
|
||||
} catch (error) { |
|
||||
console.log('error: ', error); |
|
||||
uni.$ui.showToast('获取交付物历史失败'); |
|
||||
} |
|
||||
})(); |
|
||||
}); |
|
||||
</script> |
|
@ -0,0 +1,395 @@ |
|||||
|
<template> |
||||
|
<view> |
||||
|
<!-- 标题 + 筛选 --> |
||||
|
<view class="check-work-title px-3 fixed top-0 z-10 w-full flex justify-between items-center bg-white"> |
||||
|
<text>考勤管理</text> |
||||
|
|
||||
|
<view> |
||||
|
<u-button class="mr-3" size="mini" type="primary" @click="isShow = !isShow">过滤</u-button> |
||||
|
<u-button size="mini" type="primary" @click="isShow = !isShow">导出</u-button> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 列表 --> |
||||
|
<view class="check-work-list"> |
||||
|
<u-table class="table-head"> |
||||
|
<u-tr> |
||||
|
<u-th>姓名</u-th> |
||||
|
<u-th>早</u-th> |
||||
|
<u-th>晚</u-th> |
||||
|
<u-th width="30%">审核人</u-th> |
||||
|
</u-tr> |
||||
|
</u-table> |
||||
|
|
||||
|
<view v-if="clockInfos.length" v-for="(list, listIndex) in clockInfos" :key="listIndex"> |
||||
|
<view class="table-time px-2">{{ list.dateTime }}</view> |
||||
|
|
||||
|
<u-table class="table-body"> |
||||
|
<u-tr v-if="list.recordList.length" v-for="(item, index) in list.recordList" :key="index"> |
||||
|
<u-td>{{ item.memberName }}</u-td> |
||||
|
|
||||
|
<u-td> |
||||
|
<template v-if="!item.morningStatus"> |
||||
|
<view v-if="item.isMine === 1"> |
||||
|
<u-button class="m-0" size="mini" type="primary" @click="showTimeSelect(0, item)">早打卡</u-button> |
||||
|
</view> |
||||
|
|
||||
|
<template v-else> |
||||
|
<view>未打卡</view> |
||||
|
</template> |
||||
|
</template> |
||||
|
|
||||
|
<view v-else class="flex justify-center"> |
||||
|
<view :class="{'relative': item.morningStatus === 1}"> |
||||
|
<text :class="{'font-semibold': item.isMine === 1 || item.isChecker === 1, |
||||
|
'text-green-500': item.isChecker === 1 && (item.morningStatus === 1 || item.morningStatus === 3), |
||||
|
'line-through': item.morningStatus === 2, |
||||
|
'text-gray-400': item.morningStatus === 2 && (item.isMine === 1 || item.isChecker === 1), |
||||
|
'text-cyan': item.morningStatus === 3 && item.isMine === 1}" |
||||
|
@click="showActionPanel(item.morningStatus, 'morning', item.morning, item)" |
||||
|
> |
||||
|
{{ dayjs(+item.morning).format("HH:mm") }} |
||||
|
</text> |
||||
|
|
||||
|
<!-- 小红点 --> |
||||
|
<template v-if="item.morningStatus === 1"> |
||||
|
<u-badge v-if="item.isMine === 1" :is-dot="true" |
||||
|
type="error" size="mini" style="top: -2px; right: -10px;"></u-badge> |
||||
|
|
||||
|
<!-- 小红点 --> |
||||
|
<template v-else-if="item.isChecker === 1"> |
||||
|
<u-badge class="animate-ping" :is-dot="true" type="error" size="mini" style="top: -2px; right: -10px;"></u-badge> |
||||
|
<u-badge :is-dot="true" type="error" size="mini" style="top: -2px; right: -10px;"></u-badge> |
||||
|
</template> |
||||
|
|
||||
|
<!-- 小红点 --> |
||||
|
<u-badge v-else :is-dot="true" |
||||
|
type="warning" size="mini" style="top: -2px; right: -10px;"></u-badge> |
||||
|
</template> |
||||
|
</view> |
||||
|
</view> |
||||
|
</u-td> |
||||
|
|
||||
|
<u-td> |
||||
|
<template v-if="!item.nightStatus"> |
||||
|
<view v-if="item.isMine === 1"> |
||||
|
<u-button class="m-0" size="mini" type="primary" @click="showTimeSelect(1, item)">晚打卡</u-button> |
||||
|
</view> |
||||
|
|
||||
|
<template v-else> |
||||
|
<view>未打卡</view> |
||||
|
</template> |
||||
|
</template> |
||||
|
|
||||
|
<view v-else class="flex justify-center"> |
||||
|
<view :class="{'relative': item.nightStatus === 1}"> |
||||
|
<text :class="{'font-semibold': item.isMine === 1 || item.isChecker === 1, |
||||
|
'text-green-500': item.isChecker === 1 && (item.nightStatus === 1 || item.nightStatus === 3), |
||||
|
'line-through': item.nightStatus === 2, |
||||
|
'text-gray-400': item.nightStatus === 2 && (item.isMine === 1 || item.isChecker === 1), |
||||
|
'text-cyan': item.nightStatus === 3 && item.isMine === 1}" |
||||
|
@click="showActionPanel(item.nightStatus, 'night', item.night, item)" |
||||
|
> |
||||
|
{{ dayjs(+item.night).format("HH:mm") }} |
||||
|
</text> |
||||
|
|
||||
|
<!-- 小红点 --> |
||||
|
<template v-if="item.nightStatus === 1"> |
||||
|
<u-badge v-if="item.isMine === 1" :is-dot="true" |
||||
|
type="error" size="mini" style="top: -2px; right: -10px;"></u-badge> |
||||
|
|
||||
|
<!-- 小红点 --> |
||||
|
<template v-else-if="item.isChecker === 1"> |
||||
|
<u-badge class="animate-ping" :is-dot="true" type="error" size="mini" style="top: -2px; right: -10px;"></u-badge> |
||||
|
<u-badge :is-dot="true" type="error" size="mini" style="top: -2px; right: -10px;"></u-badge> |
||||
|
</template> |
||||
|
|
||||
|
<!-- 小红点 --> |
||||
|
<u-badge v-else :is-dot="true" |
||||
|
type="warning" size="mini" style="top: -2px; right: -10px;"></u-badge> |
||||
|
</template> |
||||
|
</view> |
||||
|
</view> |
||||
|
</u-td> |
||||
|
|
||||
|
<u-td width="30%"> |
||||
|
<view class="flex justify-center"> |
||||
|
<view v-if="item.isMine === 1 && (!item.morningStatus && !item.nightStatus)" class="px-2 py-1 flex items-center border border-solid rounded border-gray-200" @click="show = true"> |
||||
|
<text class="mr-3 whitespace-nowrap">{{ item.lastCheckerName ? item.lastCheckerName : item.checkerName ? item.checkerName : checkerName }}</text> |
||||
|
<u-icon size="24" name="arrow-down"></u-icon> |
||||
|
</view> |
||||
|
|
||||
|
<view v-else>{{ item.lastCheckerName ? item.lastCheckerName : item.checkerName ? item.checkerName : '周勇' }}</view> |
||||
|
</view> |
||||
|
</u-td> |
||||
|
</u-tr> |
||||
|
</u-table> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 审核人列表 --> |
||||
|
<u-select v-model="show" :list="list" @confirm="confirm"></u-select> |
||||
|
|
||||
|
<!-- 审核操作按钮列表 --> |
||||
|
<u-action-sheet :list="actions" v-model="actionShow" @click="clickAction"></u-action-sheet> |
||||
|
|
||||
|
<!-- 时间选择框 --> |
||||
|
<u-picker mode="time" v-model="showModal" :params="params" @confirm="changeTime"></u-picker> |
||||
|
|
||||
|
<!-- 筛选框 --> |
||||
|
<SearchPopup v-if="isShow" :members="list" :show="isShow" @closePopup="closePopup" @getClockQuery="getClockQuery" @clockExport="clockExport"></SearchPopup> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { computed, onMounted, ref } from 'vue'; |
||||
|
import { useStore } from 'vuex'; |
||||
|
import dayjs from 'dayjs'; |
||||
|
import SearchPopup from '@/components/SearchPopup/SearchPopup.vue'; |
||||
|
|
||||
|
const props = defineProps({ |
||||
|
clockParams: { |
||||
|
type: Object, |
||||
|
default: () => {} |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
const store = useStore(); |
||||
|
const projectId = uni.$storage.getStorageSync('projectId'); |
||||
|
const roleId = uni.$storage.getStorageSync('roleId'); |
||||
|
const checkers = uni.$storage.getStorageSync('checkers'); // 检查人列表 |
||||
|
const url = uni.$storage.getStorageSync('domain'); |
||||
|
|
||||
|
let clockInfos = ref([]); // 打卡列表 |
||||
|
const show = ref(false); // 是否展示审核人列表 |
||||
|
let list = ref([]); // 审核人列表 |
||||
|
let actionShow = ref(false); // 审核操作按钮列表 |
||||
|
const actions = ref([{ text: '修改' }, { text: '驳回' }, { text: '确认' }]); // 操作按钮列表 |
||||
|
let examineParams = ref({}); // 审核参数 |
||||
|
let showModal = ref(false); // 修改时间模态框 |
||||
|
const params = ref({year: false, month: false, day: false, hour: true, minute: true, timestamp: true}) |
||||
|
let checkerId = ref(null); |
||||
|
let checkerName = ref(null); |
||||
|
let isShow = ref(false); |
||||
|
let timeType = ref(null); // 时间类型 0 早打卡 1 晚打卡 |
||||
|
let actionType = ref(0); // 操作类型:0 打卡 1 审核 |
||||
|
let currRecord = ref({}); // 当前点击的打卡记录 |
||||
|
let selectedTime = ref(null); // 选中的时间 |
||||
|
let startTime = dayjs().startOf('day').valueOf(); |
||||
|
let endTime = dayjs().endOf('day').valueOf(); |
||||
|
let memberIdList = ref([]); |
||||
|
|
||||
|
onMounted(() => { |
||||
|
list.value = []; |
||||
|
|
||||
|
let checker_arr = JSON.parse(checkers); |
||||
|
|
||||
|
checker_arr.forEach(item => { |
||||
|
list.value.push({ |
||||
|
value: item.memberId, |
||||
|
label: item.name |
||||
|
}) |
||||
|
|
||||
|
if (item.name === '周勇') { |
||||
|
checkerId.value = item.memberId; |
||||
|
checkerName.value = item.name; |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
getClockQuery(props.clockParams); |
||||
|
}) |
||||
|
|
||||
|
/** |
||||
|
* 批量查询打卡信息 |
||||
|
*/ |
||||
|
async function getClockQuery(data) { |
||||
|
closePopup(); |
||||
|
|
||||
|
startTime = data ? dayjs(data.startTime).startOf('day').valueOf() : startTime; |
||||
|
endTime = data ? dayjs(data.endTime).endOf('day').valueOf() : endTime; |
||||
|
memberIdList.value = data ? data.memberIdList : memberIdList.value; |
||||
|
|
||||
|
try { |
||||
|
const params = { projectId, roleId, memberIdList: memberIdList.value, startTime, endTime } |
||||
|
const data = await uni.$u.api.clockQuery(params, url); |
||||
|
|
||||
|
data.forEach(list => { |
||||
|
let arr = []; |
||||
|
list.recordList.forEach(item => { |
||||
|
if (item.isMine === 1) { |
||||
|
arr.push(item); |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
list.recordList.forEach(item => { |
||||
|
if (item.isChecker === 1) { |
||||
|
arr.push(item); |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
list.recordList.forEach(item => { |
||||
|
if (!item.isMine && !item.isChecker) { |
||||
|
arr.push(item); |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
list.recordList = arr; |
||||
|
}) |
||||
|
|
||||
|
clockInfos.value = data; |
||||
|
} catch (error) { |
||||
|
console.log('error: ', error); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 选择审核人结果 |
||||
|
function confirm(e) { |
||||
|
checkerId.value = e[0].value; |
||||
|
checkerName.value = e[0].label; |
||||
|
} |
||||
|
|
||||
|
// 打卡选择时间 |
||||
|
function showTimeSelect(clockType, record) { |
||||
|
showModal.value = true; |
||||
|
actionType.value = 0; |
||||
|
timeType.value = clockType; |
||||
|
currRecord.value = record; |
||||
|
} |
||||
|
|
||||
|
// 打卡 |
||||
|
async function punch() { |
||||
|
checkerId.value = currRecord.value.lastCheckerId ? currRecord.value.lastCheckerId : currRecord.value.checkerId ? currRecord.value.checkerId : checkerId.value; |
||||
|
|
||||
|
try { |
||||
|
const params = { |
||||
|
checkerId: checkerId.value, |
||||
|
id: !currRecord.value.id ? '' : currRecord.value.id, |
||||
|
memberId: currRecord.value.memberId, |
||||
|
dateTime: selectedTime.value, |
||||
|
clockType: timeType.value, |
||||
|
remark: '' |
||||
|
} |
||||
|
|
||||
|
const data = await uni.$u.api.clockPunch(params, url); |
||||
|
|
||||
|
getClockQuery(); // 打卡成功重新加载打卡信息 |
||||
|
} catch (error) { |
||||
|
console.log('error: ', error); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 点击待审核时间打开操作面板 |
||||
|
async function showActionPanel(status, type, time, data) { |
||||
|
if (status !== 1) return; |
||||
|
actionShow.value = true; |
||||
|
timeType.value = type; |
||||
|
examineParams.value = { id: data.id, [type]: time, projectId, type: 0 }; |
||||
|
} |
||||
|
|
||||
|
// 选择的操作下班 0 修改 1 驳回 2 确认 |
||||
|
function clickAction(index) { |
||||
|
// if (timeType.value === 'morning') { |
||||
|
// if (index === 0 || index === 2) { |
||||
|
// examineParams.value.morningStatus = 3; |
||||
|
// } else if (index === 1) { |
||||
|
// examineParams.value.morningStatus = 2; |
||||
|
// } |
||||
|
// } else { |
||||
|
// if (index === 0 || index === 2) { |
||||
|
// examineParams.value.nightStatus = 3; |
||||
|
// } else if (index === 1) { |
||||
|
// examineParams.value.nightStatus = 2; |
||||
|
// } |
||||
|
// } |
||||
|
if (index === 1) { // 驳回 |
||||
|
examineParams.value.type = 1; |
||||
|
} else if (index === 2) { // 确认 |
||||
|
examineParams.value.type = 2; |
||||
|
} |
||||
|
|
||||
|
if (index === 0) { |
||||
|
// 修改 |
||||
|
showModal.value = true; |
||||
|
actionType.value = 1; |
||||
|
} else { |
||||
|
handleClockAudit(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 时间改变确认事件 |
||||
|
function changeTime(e) { |
||||
|
selectedTime.value = e.timestamp * 1000; |
||||
|
examineParams.value[timeType.value] = e.timestamp * 1000; |
||||
|
|
||||
|
if (actionType.value === 0) { |
||||
|
punch(); |
||||
|
} else { |
||||
|
handleClockAudit(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 审核 |
||||
|
async function handleClockAudit() { |
||||
|
try { |
||||
|
const data = await uni.$u.api.clockAudit(examineParams.value, url); |
||||
|
|
||||
|
getClockQuery(); |
||||
|
} catch (error) { |
||||
|
console.log('error: ', error); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 关闭过滤弹框 |
||||
|
function closePopup(data) { |
||||
|
isShow.value = data; |
||||
|
} |
||||
|
|
||||
|
// 导出 |
||||
|
function clockExport() { |
||||
|
closePopup(); |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.whitespace-nowrap { |
||||
|
white-space: nowrap; |
||||
|
} |
||||
|
|
||||
|
.check-work-title { |
||||
|
height: 50px; |
||||
|
border-bottom: 1px solid #e8e8e8; |
||||
|
} |
||||
|
|
||||
|
.check-work-list { |
||||
|
padding-top: 50px; |
||||
|
} |
||||
|
|
||||
|
.u-table { |
||||
|
border-left: unset !important; |
||||
|
} |
||||
|
|
||||
|
.table-head { |
||||
|
border-top: unset !important; |
||||
|
} |
||||
|
|
||||
|
.u-th { |
||||
|
border-right: unset !important; |
||||
|
height: 36px; |
||||
|
background: #fafafa; |
||||
|
} |
||||
|
|
||||
|
.u-td { |
||||
|
border-right: unset !important; |
||||
|
height: 40px; |
||||
|
} |
||||
|
|
||||
|
.table-time { |
||||
|
height: 30px; |
||||
|
line-height: 30px; |
||||
|
background: #fafafa; |
||||
|
} |
||||
|
|
||||
|
.text-cyan { |
||||
|
color: #3B83F6; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,165 @@ |
|||||
|
<template> |
||||
|
<view class="p-4 flex justify-between items-center"> |
||||
|
<view> |
||||
|
<u-button class="m-0" size="mini" type="primary" @click="punch(0)" v-if="!currPunchInfo.morningStatus">早打卡</u-button> |
||||
|
|
||||
|
<template v-else> |
||||
|
<!-- 1 待审核 2 已驳回 3 已通过 --> |
||||
|
<view :class="{'relative': currPunchInfo.morningStatus === 1}"> |
||||
|
<text class="font-semibold" |
||||
|
:class="{'line-through text-red-500': currPunchInfo.morningStatus === 2, 'text-cyan': currPunchInfo.morningStatus === 3}" |
||||
|
> |
||||
|
{{ dayjs(+currPunchInfo.morning).format("HH:mm") }} |
||||
|
</text> |
||||
|
|
||||
|
<!-- 小红点 --> |
||||
|
<u-badge v-if="currPunchInfo.morningStatus === 1" |
||||
|
:is-dot="true" type="error" size="mini" style="top: -2px; right: -10px;" |
||||
|
></u-badge> |
||||
|
</view> |
||||
|
</template> |
||||
|
</view> |
||||
|
|
||||
|
<view> |
||||
|
<u-button class="m-0" size="mini" type="primary" @click="punch(1)" v-if="!currPunchInfo.nightStatus">晚打卡</u-button> |
||||
|
|
||||
|
<template v-else> |
||||
|
<!-- 1 待审核 2 已驳回 3 已通过 --> |
||||
|
<view :class="{'relative': currPunchInfo.nightStatus === 1}"> |
||||
|
<text class="font-semibold" |
||||
|
:class="{'line-through text-red-500': currPunchInfo.nightStatus === 2, 'text-cyan': currPunchInfo.nightStatus === 3}" |
||||
|
> |
||||
|
{{ dayjs(+currPunchInfo.night).format("HH:mm") }} |
||||
|
</text> |
||||
|
|
||||
|
<!-- 小红点 --> |
||||
|
<u-badge v-if="currPunchInfo.nightStatus === 1" |
||||
|
:is-dot="true" type="error" size="mini" style="top: -2px; right: -10px;" |
||||
|
></u-badge> |
||||
|
</view> |
||||
|
</template> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 审核人 --> |
||||
|
<view class="flex items-center"> |
||||
|
<view class="mr-2 flex items-center rounded-md border-gray-200" |
||||
|
:class="{'px-2 py-1 border border-solid': !(currPunchInfo.morning && currPunchInfo.night)}" |
||||
|
@click="showCheckerList"> |
||||
|
<text>{{ checkerName ? checkerName : members[0].name }}</text> |
||||
|
<u-icon v-if="!(currPunchInfo.morning && currPunchInfo.night)" class="ml-3" size="24" name="arrow-down"></u-icon> |
||||
|
</view> |
||||
|
|
||||
|
<u-icon size="30" name="arrow-right" @click="toDetail"></u-icon> |
||||
|
|
||||
|
<u-select v-model="show" :list="list" @confirm="confirm"></u-select> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { computed, onMounted, ref, watch } from 'vue'; |
||||
|
import { useStore } from 'vuex'; |
||||
|
import dayjs from 'dayjs'; |
||||
|
|
||||
|
const store = useStore(); |
||||
|
const projectId = computed(() => store.getters['project/projectId']); |
||||
|
const roleId = computed(() => store.state.role.roleId); |
||||
|
const members = computed(() => store.state.role.members); // 检查人列表 |
||||
|
const businessCode = computed(() => store.state.task.businessCode); // 服务名 |
||||
|
|
||||
|
let currPunchInfo = ref({}); // 当前用户打卡信息 |
||||
|
const show = ref(false); // 是否展示审核人列表 |
||||
|
let list = ref([]); // 审核人列表 |
||||
|
let checkerId = ref(null); // 选择的审核人id |
||||
|
let checkerName = ref(null); // 选择的审核人姓名 |
||||
|
|
||||
|
onMounted(async () => { |
||||
|
// 用户数据处理 |
||||
|
await handleUser(); |
||||
|
await getClockQuery(); |
||||
|
}) |
||||
|
|
||||
|
// 数据 |
||||
|
async function handleUser() { |
||||
|
// 当前登录用户 |
||||
|
list.value = []; |
||||
|
members.value.forEach(item => { |
||||
|
list.value.push({ |
||||
|
value: item.memberId, |
||||
|
label: item.name |
||||
|
}) |
||||
|
|
||||
|
if (item.name === '周勇') { |
||||
|
checkerId.value = item.memberId; |
||||
|
checkerName.value = item.name; |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 批量查询打卡信息 |
||||
|
*/ |
||||
|
async function getClockQuery() { |
||||
|
let startTime = dayjs().startOf('day').valueOf().toString(); |
||||
|
let endTime = dayjs().endOf('day').valueOf().toString(); |
||||
|
|
||||
|
try { |
||||
|
const params = { projectId: projectId.value, roleId: roleId.value, memberIdList: [], startTime, endTime } |
||||
|
const data = await uni.$u.api.clockQuery(params); |
||||
|
|
||||
|
currPunchInfo.value = data[0].recordList[0]; |
||||
|
checkerId.value = currPunchInfo.value.lastCheckerId ? currPunchInfo.value.lastCheckerId : currPunchInfo.value.checkerId ? currPunchInfo.value.checkerId : checkerId.value; |
||||
|
checkerName.value = currPunchInfo.value.lastCheckerName ? currPunchInfo.value.lastCheckerName : currPunchInfo.value.checkerName ? currPunchInfo.value.checkerName : checkerName.value; |
||||
|
} catch (error) { |
||||
|
console.log('error: ', error); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function showCheckerList() { |
||||
|
if (!(currPunchInfo.morning && currPunchInfo.night)) { |
||||
|
show.value = true |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 选择审核人结果 |
||||
|
function confirm(e) { |
||||
|
checkerId.value = e[0].value; |
||||
|
checkerName.value = e[0].label; |
||||
|
} |
||||
|
|
||||
|
// 打卡 |
||||
|
async function punch(clockType) { |
||||
|
try { |
||||
|
const params = { |
||||
|
checkerId: checkerId.value, |
||||
|
id: currPunchInfo.value.id, |
||||
|
memberId: currPunchInfo.value.memberId, |
||||
|
dateTime: dayjs().valueOf(), |
||||
|
clockType, |
||||
|
longitude: "", |
||||
|
latitude: "" |
||||
|
} |
||||
|
|
||||
|
const data = await uni.$u.api.clockPunch(params); |
||||
|
|
||||
|
getClockQuery(); // 打卡成功重新加载打卡信息 |
||||
|
} catch (error) { |
||||
|
console.log('error: ', error); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function toDetail() { |
||||
|
uni.$storage.setStorageSync('pluginKey', 'checkWork'); |
||||
|
|
||||
|
uni.navigateTo({ |
||||
|
url: "/pages/detail/detail" |
||||
|
}) |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.text-cyan { |
||||
|
color: #13acc4; |
||||
|
} |
||||
|
</style> |
Loading…
Reference in new issue