34 changed files with 3193 additions and 314 deletions
@ -0,0 +1,17 @@ |
|||||
|
import Config from '@/common/js/config.js' |
||||
|
|
||||
|
const apiUrl = Config.apiUrl; |
||||
|
const defaultwbs = `${apiUrl}/defaultwbs`; |
||||
|
|
||||
|
export function setupProject(app) { |
||||
|
uni.$u.api = { ...uni.$u.api } || {}; |
||||
|
//根据id获取项目信息
|
||||
|
uni.$u.api.findProjectById = param => uni.$u.post(`${defaultwbs}/project/findProjectById`, param); |
||||
|
|
||||
|
//创建分享连接
|
||||
|
uni.$u.api.createShare = param => uni.$u.post(`${defaultwbs}/share/create`, param); |
||||
|
|
||||
|
//点击分享连接
|
||||
|
uni.$u.api.clickShare = param => uni.$u.post(`${defaultwbs}/share/click`, param); |
||||
|
}; |
||||
|
|
@ -0,0 +1,12 @@ |
|||||
|
import Config from '@/common/js/config.js' |
||||
|
|
||||
|
const apiUrl = Config.apiUrl; |
||||
|
const defaultwbs = `${apiUrl}/defaultwbs`; |
||||
|
|
||||
|
export function setupRole(app) { |
||||
|
uni.$u.api = { ...uni.$u.api } || {}; |
||||
|
//根据项目id查找角色
|
||||
|
uni.$u.api.findShowRole = param => uni.$u.post(`${defaultwbs}/role/show`, param); |
||||
|
//根据项目id查找所有成员
|
||||
|
uni.$u.api.queryChecker = param => uni.$u.post(`${defaultwbs}/deliver/queryChecker`, param); |
||||
|
}; |
@ -0,0 +1,20 @@ |
|||||
|
import Config from '@/common/js/config.js' |
||||
|
|
||||
|
const apiUrl = Config.apiUrl; |
||||
|
const defaultwbs = `${apiUrl}/defaultwbs`; |
||||
|
|
||||
|
export function setupTask(app) { |
||||
|
uni.$u.api = { ...uni.$u.api } || {}; |
||||
|
uni.$u.api.getGlobal = param => uni.$u.post(`${defaultwbs}/task/global`, param); |
||||
|
uni.$u.api.getPermanent = param => uni.$u.post(`${defaultwbs}/task/permanent`, param); |
||||
|
//根据时间基准点和角色查找定期任务
|
||||
|
uni.$u.api.getRegularTask = param => uni.$u.post(`${defaultwbs}/task/regular`, param); |
||||
|
//修改任务状态
|
||||
|
uni.$u.api.updateTaskType = param => uni.$u.post(`${defaultwbs}/task/type`, param); |
||||
|
//新建任务
|
||||
|
uni.$u.api.saveTask = param => uni.$u.post(`${defaultwbs}/task/save`, param); |
||||
|
//克隆任务
|
||||
|
uni.$u.api.cloneTask = param => uni.$u.post(`${defaultwbs}/task/clone`, param); |
||||
|
//模糊查询 查找项目下的任务
|
||||
|
uni.$u.api.queryTaskOfProject = param => uni.$u.post(`${defaultwbs}/task/queryTaskOfProject`, param); |
||||
|
}; |
@ -1,8 +1,240 @@ |
|||||
<template> |
<template> |
||||
|
<view class="px-2 bg-white wrap"> |
||||
|
<view class="home-box u-skeleton"> |
||||
|
<scroll-view :enable-flex="true" :scroll-left="data.scrollLeft" :throttle="false" scroll-with-animation scroll-x @scroll="scroll"> |
||||
|
<view class="tab-box"> |
||||
|
<!-- 角色项 |
||||
|
default-tab-choice 是 我的角色 && 当前展示 |
||||
|
default-tab-item 是 我的角色 && 当前不展示 |
||||
|
tab-choice 不是我的 && 当前展示 |
||||
|
--> |
||||
|
<view |
||||
|
:class="{ |
||||
|
'default-tab-choice': item.mine == 1 && roleId === item.id, |
||||
|
'default-tab-item': item.mine == 1 && roleId !== item.id, |
||||
|
'tab-choice': item.mine == 0 && roleId === item.id, |
||||
|
}" |
||||
|
:key="index" |
||||
|
@click="changeRole(item.id, index)" |
||||
|
class="tab-item" |
||||
|
v-for="(item, index) in data.roles" |
||||
|
> |
||||
|
<view class="tab-children u-skeleton-fillet u-font-14">{{ item.name }}</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</scroll-view> |
||||
|
</view> |
||||
|
<!-- 骨架屏 --> |
||||
|
<u-skeleton :animation="true" :loading="data.loading" bg-color="#fff"></u-skeleton> |
||||
|
</view> |
||||
</template> |
</template> |
||||
|
|
||||
<script> |
<script setup> |
||||
|
import { reactive, ref, computed, watchEffect, onMounted, nextTick } from 'vue'; |
||||
|
import { useStore } from 'vuex'; |
||||
|
|
||||
|
const data = reactive({ |
||||
|
tabIndex: 0, // 当前访问的 index 默认为0 |
||||
|
tabList: [], // tab dom节点集合 |
||||
|
scrollLeft: 0, // scrollview需要滚动的距离 |
||||
|
loading: false, // 是否显示骨架屏组件 |
||||
|
roles: [{ id: 1, name: '项目经理', mine: 0, pm: 1, sequence: 1 }, { id: 2, name: '运维', mine: 0, pm: 0, sequence: 2 }], |
||||
|
roleLeft: 0, |
||||
|
}); |
||||
|
|
||||
|
const store = useStore(); |
||||
|
const visibleRoles = computed(() => store.state.role.visibleRoles); |
||||
|
const roleId = computed(() => store.state.role.roleId); |
||||
|
const tasks = computed(() => store.state.task.tasks); |
||||
|
|
||||
|
watchEffect(() => { |
||||
|
if (visibleRoles.value && visibleRoles.value.length) { |
||||
|
data.roles = visibleRoles.value; |
||||
|
data.loading = false; |
||||
|
|
||||
|
console.log('data.roles', data.roles); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
onMounted(() => { |
||||
|
if (!visibleRoles.value || !visibleRoles.value.length) { |
||||
|
data.loading = true; |
||||
|
} else { |
||||
|
data.roles = visibleRoles.value; |
||||
|
} |
||||
|
|
||||
|
nextTick(() => { |
||||
|
const query = uni.createSelectorQuery().in(this); |
||||
|
console.log('query', query); |
||||
|
query |
||||
|
.selectAll('.tab-children') |
||||
|
.boundingClientRect(data => { |
||||
|
console.log('data', data); |
||||
|
if (data.length) { |
||||
|
data.roleLeft = data[0].left; |
||||
|
} |
||||
|
}) |
||||
|
.exec(); |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
function scroll(e) { |
||||
|
data.scrollLeft = e.detail.scrollLeft; |
||||
|
} |
||||
|
|
||||
|
// 设置滚动位置 |
||||
|
function setCurrentRole(index) { |
||||
|
const query = uni.createSelectorQuery().in(this); |
||||
|
query |
||||
|
.selectAll('.tab-children') |
||||
|
.boundingClientRect(data => { |
||||
|
data.forEach(item => { |
||||
|
data.tabList.push({ width: item.width }); |
||||
|
}); |
||||
|
}) |
||||
|
.exec(); |
||||
|
|
||||
|
const system = uni.getSystemInfoSync(); // 获取系统信息 |
||||
|
// 屏幕宽度 |
||||
|
const screenWidth = system.windowWidth; |
||||
|
// 当前滚动的位置 |
||||
|
let left = 0; |
||||
|
for (let i = 0; i < index; i++) { |
||||
|
left += data.tabList[i].width + (data.roleLeft - 8) * 2; |
||||
|
} |
||||
|
|
||||
|
left += (data.tabList[index].width + (data.roleLeft - 8) * 2) / 2; |
||||
|
if (left > screenWidth) { |
||||
|
data.scrollLeft = left - screenWidth + screenWidth / 2; |
||||
|
} else if (left > screenWidth / 2) { |
||||
|
data.scrollLeft = left - screenWidth / 2; |
||||
|
} else if (left < screenWidth / 2) { |
||||
|
data.scrollLeft = 0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 清除插件script |
||||
|
function clearPluginScript() { |
||||
|
try { |
||||
|
const scripts = document.querySelectorAll('script[data-type=plugin]'); |
||||
|
for (let i = 0; i < scripts.length; i++) { |
||||
|
document.body.removeChild(scripts[i]); |
||||
|
} |
||||
|
} catch (error) { |
||||
|
console.error('clearPluginScript error: ', error); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 切换角色 |
||||
|
// 查任务这里不用管 project监听了roleId的变化 |
||||
|
// 时间基准点不用管 project监听了roleId 里处理了 |
||||
|
function changeRole(id, index) { |
||||
|
try { |
||||
|
// 清除多余的script |
||||
|
clearPluginScript(); |
||||
|
nextTick(() => { |
||||
|
store.commit('role/setRoleId', id); |
||||
|
// 改变index 即手动点击切换 我在此时将当前元素赋值给左边距 实现自动滚动 |
||||
|
setCurrentRole(index); |
||||
|
}); |
||||
|
} catch (error) { |
||||
|
console.error('role.vue changeRole error: ', error); |
||||
|
} |
||||
|
} |
||||
</script> |
</script> |
||||
|
|
||||
<style> |
<style lang="scss" scoped> |
||||
|
.home-box { |
||||
|
// 对此盒子进行 sticky 粘性定位 |
||||
|
position: sticky; |
||||
|
top: 0; |
||||
|
background: #fff; // 设置背景 否则会透明 |
||||
|
|
||||
|
/* #ifdef H5 */ |
||||
|
//粘性定位 在h5下 加 原生头部高度 44px |
||||
|
top: 88rpx; |
||||
|
/* #endif */ |
||||
|
|
||||
|
.tab-box { |
||||
|
position: relative; |
||||
|
white-space: nowrap; |
||||
|
// height: 84rpx; |
||||
|
|
||||
|
.tab-item { |
||||
|
// width: 20%; |
||||
|
// height: 84rpx; |
||||
|
padding: 20rpx 24rpx; |
||||
|
position: relative; |
||||
|
display: inline-block; |
||||
|
text-align: center; |
||||
|
font-size: 30rpx; |
||||
|
transition-property: background-color, width; |
||||
|
} |
||||
|
|
||||
|
.default-tab-item { |
||||
|
color: $roleChoiceColor; |
||||
|
} |
||||
|
|
||||
|
.default-tab-choice { |
||||
|
//当前选中 基于此类名给与底部选中框 |
||||
|
position: relative; |
||||
|
color: $roleChoiceColor; |
||||
|
font-weight: 600; |
||||
|
} |
||||
|
|
||||
|
.default-tab-choice:before { |
||||
|
content: ''; |
||||
|
position: absolute; |
||||
|
left: 0; |
||||
|
bottom: -20rpx; |
||||
|
width: 100%; |
||||
|
height: 6rpx; |
||||
|
border-radius: 2rpx; |
||||
|
background: $roleChoiceColor; |
||||
|
} |
||||
|
|
||||
|
.tab-choice { |
||||
|
//当前选中 基于此类名给与底部选中框 |
||||
|
position: relative; |
||||
|
color: $uni-color-primary; |
||||
|
font-weight: 600; |
||||
|
} |
||||
|
|
||||
|
.tab-choice:before { |
||||
|
content: ''; |
||||
|
position: absolute; |
||||
|
left: 0; |
||||
|
bottom: -20rpx; |
||||
|
width: 100%; |
||||
|
height: 6rpx; |
||||
|
border-radius: 2rpx; |
||||
|
background: $uni-color-primary; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// // 删除 底部滚动条 |
||||
|
/* #ifndef APP-NVUE */ |
||||
|
::-webkit-scrollbar, |
||||
|
::-webkit-scrollbar, |
||||
|
::-webkit-scrollbar { |
||||
|
display: none; |
||||
|
width: 0 !important; |
||||
|
height: 0 !important; |
||||
|
-webkit-appearance: none; |
||||
|
background: transparent; |
||||
|
} |
||||
|
|
||||
|
/* #endif */ |
||||
|
/* #ifdef H5 */ |
||||
|
// 通过样式穿透,隐藏H5下,scroll-view下的滚动条 |
||||
|
scroll-view ::v-deep ::-webkit-scrollbar { |
||||
|
display: none; |
||||
|
} |
||||
|
|
||||
|
/* #endif */ |
||||
|
|
||||
|
.skeleton { |
||||
|
height: 44rpx; |
||||
|
} |
||||
</style> |
</style> |
||||
|
@ -1,8 +1,220 @@ |
|||||
<template> |
<template> |
||||
|
<view> |
||||
|
<!-- :is-back="false" --> |
||||
|
<u-navbar :custom-back="onBack" class="overflow-hidden"> |
||||
|
<view class="flex justify-start flex-1 px-3 font-bold min-0"> |
||||
|
<view class="truncate">{{ project.name }}</view> |
||||
|
</view> |
||||
|
<view class="mr-2" slot="right"> |
||||
|
<u-icon class="m-1" name="xuanzhong2" custom-prefix="custom-icon" size="20px" @click="lwbs"></u-icon> |
||||
|
<u-icon class="m-1" name="shuaxin1" custom-prefix="custom-icon" size="20px" @click="projectOverview"></u-icon> |
||||
|
<u-icon class="m-1" name="home" custom-prefix="custom-icon" size="20px" @click="openIndex"></u-icon> |
||||
|
<u-icon class="m-1" name="xuanxiang" custom-prefix="custom-icon" size="20px" @click="operation"></u-icon> |
||||
|
</view> |
||||
|
</u-navbar> |
||||
|
<view class="mask" v-if="data.maskShow" @click="closeMask" style="width: 100%; height: 100vh; z-index: 21; position: fixed; background: rgba(0, 0, 0, 0.3)"></view> |
||||
|
<!-- 右上角 ... 弹窗 --> |
||||
|
<view class="popup border shadow-md" v-if="data.show"> |
||||
|
<view class="flex pb-3 border-b-1"> |
||||
|
<u-icon name="plus-circle" size="36" style="margin: 0 15px 3px 0"></u-icon> |
||||
|
<view @click="createTask">新建任务</view> |
||||
|
<!-- <view>新建任务</view> --> |
||||
|
</view> |
||||
|
<view class="flex pt-3"> |
||||
|
<u-icon name="share" size="32" style="margin: 0 15px 3px 0"></u-icon> |
||||
|
<view @click="share">分享项目</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<!-- 分享项目弹窗 --> |
||||
|
<ShareProject v-if="data.secondShow" class="second-popup" /> |
||||
|
<!-- 新建任务弹窗 --> |
||||
|
<CreateTask |
||||
|
:startTime="data.startTime" |
||||
|
:endTime="data.endTime" |
||||
|
@showTime="showTime" |
||||
|
@closeMask="closeMask" |
||||
|
class="third-popup 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> |
</template> |
||||
|
|
||||
<script> |
<script setup> |
||||
|
import { reactive, computed } from 'vue'; |
||||
|
import { useStore } from 'vuex'; |
||||
|
import CreateTask from './components/CreateTask.vue'; |
||||
|
import ShareProject from './components/ShareProject.vue'; |
||||
|
|
||||
|
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, |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
const store = useStore(); |
||||
|
const project = computed(() => store.state.project.project); |
||||
|
const userId = computed(() => store.getters['user/userId']); |
||||
|
|
||||
|
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}`; |
||||
|
} |
||||
|
|
||||
|
// 点击返回按钮 |
||||
|
function onBack() { |
||||
|
// eslint-disable-next-line no-undef |
||||
|
const pages = getCurrentPages(); // 获取页面栈数组 |
||||
|
console.log('历史pages: ', pages.length); |
||||
|
if (pages.length > 1) { |
||||
|
uni.webView.navigateBack(); |
||||
|
} else { |
||||
|
// this.$u.route('/', { u: this.userId }); |
||||
|
uni.webView.reLaunch({ url: `/pages/index/index?u=${userId.value}` }); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// LWBS提示 |
||||
|
function lwbs() { |
||||
|
// uni.$ui.showToast('LWBS'); |
||||
|
} |
||||
|
// 项目概览 |
||||
|
function projectOverview() { |
||||
|
// uni.$ui.showToast('项目概览'); |
||||
|
} |
||||
|
// 回到首页 |
||||
|
function openIndex() { |
||||
|
uni.webView.reLaunch({ url: `/pages/index/index?u=${this.userId}` }); |
||||
|
} |
||||
|
|
||||
|
// 操作 |
||||
|
function operation() { |
||||
|
// uni.$ui.showToast('操作'); |
||||
|
data.show = !data.show; |
||||
|
} |
||||
|
|
||||
|
// 新建项目 |
||||
|
function createTask() { |
||||
|
// 关闭 ... 弹窗 |
||||
|
data.show = false; |
||||
|
// 打开遮罩 |
||||
|
data.maskShow = true; |
||||
|
// 打开新建项目弹窗 |
||||
|
data.createTaskShow = true; |
||||
|
} |
||||
|
|
||||
|
// 分享项目 |
||||
|
function share() { |
||||
|
// 关闭 ... 弹窗 |
||||
|
data.show = false; |
||||
|
// 打开遮罩 |
||||
|
data.maskShow = true; |
||||
|
// 打开分享项目弹窗 |
||||
|
data.secondShow = true; |
||||
|
} |
||||
|
|
||||
|
// 点击遮罩,关闭弹窗 |
||||
|
function closeMask() { |
||||
|
// 关闭遮罩 |
||||
|
data.maskShow = false; |
||||
|
// 关闭分享项目弹窗 |
||||
|
data.secondShow = false; |
||||
|
// 关闭新建项目弹窗 |
||||
|
data.createTaskShow = false; |
||||
|
} |
||||
</script> |
</script> |
||||
|
|
||||
<style> |
<style lang="scss" scoped> |
||||
|
.second-popup { |
||||
|
background: #ffffff; |
||||
|
position: fixed; |
||||
|
left: 50%; |
||||
|
top: 50%; |
||||
|
transform: translate(-50%, -50%); |
||||
|
z-index: 33; |
||||
|
border-radius: 5px; |
||||
|
width: 90%; |
||||
|
} |
||||
|
|
||||
|
.third-popup { |
||||
|
background: #ffffff; |
||||
|
position: fixed; |
||||
|
left: 50%; |
||||
|
top: 50%; |
||||
|
transform: translate(-50%, -50%); |
||||
|
z-index: 33; |
||||
|
border-radius: 5px; |
||||
|
width: 90%; |
||||
|
} |
||||
|
|
||||
|
.popup { |
||||
|
width: 40%; |
||||
|
background: #fff; |
||||
|
position: absolute; |
||||
|
right: 0; |
||||
|
z-index: 99; |
||||
|
padding: 15px; |
||||
|
color: black; |
||||
|
animation: opacity 0.5s 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> |
</style> |
||||
|
@ -0,0 +1,466 @@ |
|||||
|
<template> |
||||
|
<div class="new-projects-box"> |
||||
|
<div class="form"> |
||||
|
<!-- 项目名称 --> |
||||
|
<view class="mb-3 font-bold text-base flex justify-center text-black">新建任务</view> |
||||
|
<div class="flex items-center mb-2"> |
||||
|
<div>名称<span class="text-red-500">*</span>:</div> |
||||
|
<u-input max-length="5" v-model="name" :type="type" :border="border" /> |
||||
|
</div> |
||||
|
<!-- 起止时间 --> |
||||
|
<div class="mb-2"> |
||||
|
<div>起止时间:</div> |
||||
|
<u-input placeholder="请选择起止时间" v-model="timeValue" :type="type" :border="border" @click="$emit('showTime')" /> |
||||
|
</div> |
||||
|
<!-- 多选框 --> |
||||
|
<div class="flex justify-between items-center"> |
||||
|
<div>负责人<span class="text-red-500">*</span>:</div> |
||||
|
<div class="flex-1" v-if="hasRole">{{ roleName }}</div> |
||||
|
<div label="负责人" class="flex-1" v-else> |
||||
|
<u-dropdown disabled ref="uDropdown" placeholder="请选择负责人"> |
||||
|
<u-dropdown-item :title="roleList"> |
||||
|
<view class="slot-content bg-white"> |
||||
|
<div |
||||
|
class="flex flex-row justify-between mb-1 drop-item" |
||||
|
v-for="(role, roleIndex) in roleOptions" |
||||
|
:key="roleIndex" |
||||
|
@click="change(roleIndex)" |
||||
|
> |
||||
|
<view v-model="role.id">{{ role.name }}</view> |
||||
|
<u-icon v-if="role.dropdownShow" name="checkbox-mark" color="#2979ff" size="28"></u-icon> |
||||
|
</div> |
||||
|
</view> |
||||
|
</u-dropdown-item> |
||||
|
</u-dropdown> |
||||
|
</div> |
||||
|
</div> |
||||
|
<!-- 下拉图标 --> |
||||
|
<div class="flex justify-center my-6"> |
||||
|
<u-icon v-if="arrow" name="arrow-down" size="28" @click="openDropdown"></u-icon> |
||||
|
<u-icon v-else name="arrow-up" size="28" @click="closeSecondDropdown"></u-icon> |
||||
|
</div> |
||||
|
<!-- 下拉框的内容 --> |
||||
|
<div v-if="show" class="mb-6"> |
||||
|
<!-- 描述 --> |
||||
|
<div class="flex items-center mb-2"> |
||||
|
<div>描述:</div> |
||||
|
<u-input v-model="description" max-length="48" type="textarea" height="36" auto-height :border="border" /> |
||||
|
</div> |
||||
|
<!-- 所属项目 --> |
||||
|
<div class="w flex items-center mb-2"> |
||||
|
<div>所属项目<span class="text-red-500">*</span>:</div> |
||||
|
<div>{{ project.name }}</div> |
||||
|
</div> |
||||
|
<!-- 所属任务 --> |
||||
|
<div class="w flex items-center mb-2" v-if="task && task.id"> |
||||
|
<div>所属任务:</div> |
||||
|
<div>{{ task.name }}</div> |
||||
|
</div> |
||||
|
<!-- 上道工序 --> |
||||
|
<div class="flex items-center mb-2"> |
||||
|
<div>上道工序:</div> |
||||
|
<InputSearch |
||||
|
@searchPrevTask="searchPrevTask" |
||||
|
:dataSource="allTasks" |
||||
|
@select="handleChange" |
||||
|
@clearAllTasks="clearAllTasks" |
||||
|
placeholder="请输入上道工序" |
||||
|
/> |
||||
|
</div> |
||||
|
<!-- 检查人多选框 --> |
||||
|
<div class="flex justify-between items-center"> |
||||
|
<div>检查人<span class="text-red-500">*</span>:</div> |
||||
|
<div label="检查人" class="flex-1"> |
||||
|
<u-dropdown ref="dropdown"> |
||||
|
<u-dropdown-item :title="checkerList"> |
||||
|
<view class="slot-content bg-white"> |
||||
|
<div |
||||
|
class="flex flex-row justify-between mb-1 drop-item" |
||||
|
v-for="(checkoutOption, Index) in checkoutOptions" |
||||
|
:key="Index" |
||||
|
@click="choose(Index)" |
||||
|
> |
||||
|
<view v-model="checkoutOption.value">{{ checkoutOption.name }}</view> |
||||
|
<u-icon v-if="checkoutOption.dropdownShow" name="checkbox-mark" color="#2979ff" size="28"></u-icon> |
||||
|
</div> |
||||
|
</view> |
||||
|
</u-dropdown-item> |
||||
|
</u-dropdown> |
||||
|
</div> |
||||
|
</div> |
||||
|
<!-- 是否是日常任务 --> |
||||
|
<div class="flex justify-between items-center mt-6"> |
||||
|
是否是日常任务: |
||||
|
<u-switch v-model="isGlobal" size="28"></u-switch> |
||||
|
</div> |
||||
|
<div class="mt-6"> |
||||
|
<div>交付物:</div> |
||||
|
<div v-for="(sort, sortIndex) in deliverSort" :key="sortIndex"> |
||||
|
<u-input |
||||
|
@blur="addDeliverInput" |
||||
|
v-model="sort.name" |
||||
|
:placeholder="`交付物名称${sortIndex + 1}`" |
||||
|
:type="type" |
||||
|
:border="border" |
||||
|
/> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="flex items-center mb-6"> |
||||
|
<u-button type="primary" size="medium" @click="setParameters">提交</u-button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import { mapState, mapGetters, mapMutations, mapActions } from 'vuex'; |
||||
|
|
||||
|
export default { |
||||
|
props: { |
||||
|
startTime: { |
||||
|
type: String, |
||||
|
default: '', |
||||
|
}, |
||||
|
endTime: { |
||||
|
type: String, |
||||
|
default: '', |
||||
|
}, |
||||
|
task: { |
||||
|
type: Object, |
||||
|
default: null, |
||||
|
}, |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
arrow: true, |
||||
|
show: false, |
||||
|
isGlobal: false, //是否日常任务 |
||||
|
name: '', //名称 |
||||
|
showChooseTime: false, |
||||
|
timeValue: '', //起止时间 |
||||
|
description: '', //描述 |
||||
|
projectShow: false, //所属项目模糊搜索展示 |
||||
|
processTaskId: '', //上道工序 |
||||
|
type: 'text', |
||||
|
border: true, |
||||
|
roleList: undefined, //负责人默认多选 |
||||
|
checkerList: undefined, //检查人默认多选 |
||||
|
roleOptions: [], // 负责人下拉多选列表 |
||||
|
checkoutOptions: [], // 检查人下拉多选列表 |
||||
|
roleIdList: [], // 选中的负责人id |
||||
|
checkerIdList: [], // 选中的检查人id |
||||
|
deliverables: [], // 交付物 |
||||
|
deliverSort: [{ name: '' }], // 交付物排序 |
||||
|
allTasks: [], |
||||
|
roleName: '', // 负责人名字 |
||||
|
hasRole: false, // 有没有负责人 |
||||
|
}; |
||||
|
}, |
||||
|
|
||||
|
computed: { |
||||
|
...mapState('role', ['visibleRoles', 'roleId']), |
||||
|
...mapState('project', ['project']), |
||||
|
...mapState('task', ['tasks']), |
||||
|
...mapGetters('project', ['projectId']), |
||||
|
}, |
||||
|
|
||||
|
watch: { |
||||
|
endTime(val) { |
||||
|
if (val) { |
||||
|
this.timeValue = this.startTime + ' 至 ' + val; |
||||
|
} |
||||
|
}, |
||||
|
}, |
||||
|
|
||||
|
mounted() { |
||||
|
// 获取负责人和检查人列表 |
||||
|
if (this.visibleRoles.length) { |
||||
|
this.visibleRoles.forEach(role => { |
||||
|
role.dropdownShow = false; |
||||
|
role.status = false; |
||||
|
}); |
||||
|
} |
||||
|
this.roleOptions = this.$u.deepClone(this.visibleRoles); |
||||
|
this.checkoutOptions = this.$u.deepClone(this.visibleRoles); |
||||
|
|
||||
|
// 判断有没有负责人 是不是添加子任务 |
||||
|
if (this.roleId) { |
||||
|
const item = this.visibleRoles.find(r => r.id === this.roleId); |
||||
|
if (item) { |
||||
|
this.roleName = item.name; |
||||
|
this.hasRole = true; |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
methods: { |
||||
|
...mapMutations('task', ['updateTasks']), |
||||
|
...mapActions('task', ['getPermanent']), |
||||
|
|
||||
|
// 负责人下拉多选选中 |
||||
|
change(index) { |
||||
|
let arr = [...this.roleOptions]; |
||||
|
// 选择多选项图标的展示 |
||||
|
arr[index].dropdownShow = !arr[index].dropdownShow; |
||||
|
// 多选展示的改变 |
||||
|
this.roleList = arr[index].name; |
||||
|
let shows = ''; |
||||
|
// 遍历arr,如果选中,添加到多选展示框上 |
||||
|
arr.map(val => { |
||||
|
if (val.dropdownShow === true) { |
||||
|
shows += val.name + ','; |
||||
|
this.roleIdList.push(val.id); |
||||
|
} |
||||
|
}); |
||||
|
this.roleOptions = [...arr]; |
||||
|
// 删除最后的',' |
||||
|
this.roleList = shows.slice(0, shows.length - 1); |
||||
|
}, |
||||
|
|
||||
|
// 检查人下拉多选选中 |
||||
|
choose(index) { |
||||
|
let arr = [...this.checkoutOptions]; |
||||
|
// 选择多选项图标的展示 |
||||
|
arr[index].dropdownShow = !arr[index].dropdownShow; |
||||
|
// 多选展示的改变 |
||||
|
this.checkerList = arr[index].name; |
||||
|
let shows = ''; |
||||
|
// 遍历arr,如果选中,添加到多选展示框上 |
||||
|
arr.map(val => { |
||||
|
if (val.dropdownShow === true) { |
||||
|
shows += val.name + ','; |
||||
|
this.checkerIdList.push(val.id); |
||||
|
} |
||||
|
}); |
||||
|
this.checkoutOptions = [...arr]; |
||||
|
// 删除最后的',' |
||||
|
this.checkerList = shows.slice(0, shows.length - 1); |
||||
|
// this.roleList = arr[value - 1].name; |
||||
|
}, |
||||
|
|
||||
|
// 打开下拉框 |
||||
|
openDropdown() { |
||||
|
this.arrow = !this.arrow; |
||||
|
this.show = true; |
||||
|
}, |
||||
|
|
||||
|
// 关闭下拉框 |
||||
|
closeSecondDropdown() { |
||||
|
this.arrow = !this.arrow; |
||||
|
this.show = false; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 模糊查询 查找项目下的任务 |
||||
|
* @param name 任务名 |
||||
|
* @param projectId 项目id |
||||
|
*/ |
||||
|
async searchPrevTask(val) { |
||||
|
try { |
||||
|
const params = { name: val, projectId: this.projectId }; |
||||
|
const data = await this.$u.api.queryTaskOfProject(params); |
||||
|
this.allTasks = data; |
||||
|
return data; |
||||
|
} catch (error) { |
||||
|
console.error('error: ', error); |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
//用户点击获取的数据 |
||||
|
handleChange(data) { |
||||
|
console.log('data', data); |
||||
|
this.processTaskId = data.detailId; |
||||
|
}, |
||||
|
// 清空模糊查询信息 |
||||
|
clearAllTasks() { |
||||
|
this.allTasks = []; |
||||
|
}, |
||||
|
|
||||
|
// 数组最后一项有值 添加一条交付物输入框 |
||||
|
addDeliverInput() { |
||||
|
if (this.deliverSort[this.deliverSort.length - 1].name) { |
||||
|
this.deliverSort.push({ name: '' }); |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
// 设置提交参数 |
||||
|
async setParameters() { |
||||
|
const { |
||||
|
projectId, |
||||
|
task, |
||||
|
name, |
||||
|
startTime, |
||||
|
endTime, |
||||
|
hasRole, |
||||
|
roleIdList, |
||||
|
roleId, |
||||
|
description, |
||||
|
processTaskId, |
||||
|
checkerIdList, |
||||
|
isGlobal, |
||||
|
} = this; |
||||
|
if (!name) { |
||||
|
uni.$uiowToast('请输入名称'); |
||||
|
return; |
||||
|
} |
||||
|
if ((!roleIdList || !roleIdList.length) && !hasRole) { |
||||
|
uni.$uiowToast('请选择负责人'); |
||||
|
return; |
||||
|
} |
||||
|
if (!checkerIdList || !checkerIdList.length) { |
||||
|
uni.$uiowToast('请选择检查人'); |
||||
|
return; |
||||
|
} |
||||
|
const deliverList = []; |
||||
|
this.deliverSort.forEach(item => { |
||||
|
if (item.name) { |
||||
|
deliverList.push(item.name); |
||||
|
} |
||||
|
}); |
||||
|
const params = { |
||||
|
name, |
||||
|
startTime: startTime ? this.$moment(startTime).format('x') - 0 : '', |
||||
|
endTime: endTime ? this.$moment(endTime).format('x') - 0 : '', |
||||
|
roleIdList: hasRole ? [roleId] : roleIdList, |
||||
|
description, |
||||
|
projectId, |
||||
|
parentTaskId: task && task.id ? task.id : '', // 父任务 |
||||
|
processTaskId, // 上道工序 TODO |
||||
|
checkerIdList, |
||||
|
global: isGlobal ? 1 : 0, |
||||
|
deliverList, |
||||
|
}; |
||||
|
await this.handleSubmit(params); |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 新建任务 |
||||
|
* @param name 任务名 |
||||
|
* @param startTime 开始时间 |
||||
|
* @param endTime 结束时间 |
||||
|
* @param roleIdList 负责人id(数组) |
||||
|
* @param description 描述 |
||||
|
* @param projectId 所属项目id |
||||
|
* @param parentTaskId 所属任务id |
||||
|
* @param processTaskId 上道工序(任务id) |
||||
|
* @param checkerIdList 检查人id(数组) |
||||
|
* @param global 是否日常任务 0否 1是 |
||||
|
* @param deliverList 交付物名字(数组) |
||||
|
*/ |
||||
|
async handleSubmit(params) { |
||||
|
try { |
||||
|
const data = await this.$u.api.saveTask(params); |
||||
|
// TODO 任务新建成功 继续 or 取消 |
||||
|
this.$emit('closeMask'); |
||||
|
const newTasks = { |
||||
|
data: data[0], |
||||
|
processTaskId: params.processTaskId, |
||||
|
}; |
||||
|
// 将新加的任务加到store |
||||
|
// 判断不是子任务 |
||||
|
if (!this.task || !this.task.id) { |
||||
|
this.addNewTasks(newTasks); |
||||
|
} |
||||
|
} catch (error) { |
||||
|
this.$emit('closeMask'); |
||||
|
console.error('error: ', error); |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
// 添加任务后更新tasks |
||||
|
addNewTasks(data) { |
||||
|
const oldTasks = this.$u.deepClone(this.tasks); |
||||
|
let res = data.data; |
||||
|
console.log('添加任务后更新tasks', data); |
||||
|
// 判断有没有选择上道工序 |
||||
|
if (data.processTaskId) { |
||||
|
const index = oldTasks.find(item => item.detailId === data.processTaskId); |
||||
|
if (index) { |
||||
|
oldTasks.splice(index + 1, 0, res); |
||||
|
} |
||||
|
} else { |
||||
|
this.setAddPosition(res, oldTasks); |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
// 设置添加位置 |
||||
|
setAddPosition(res, oldTasks) { |
||||
|
console.log('设置添加位置', res, oldTasks); |
||||
|
|
||||
|
if (res.planStart - 0 < oldTasks[0].planStart - 0) { |
||||
|
// 开始时间小于列表的第一个 插入最前面 |
||||
|
oldTasks.splice(0, 0, res); |
||||
|
} else if (res.planStart - 0 === oldTasks[0].planStart - 0) { |
||||
|
// 开始时间等于列表的第一个 插入第二 |
||||
|
oldTasks.splice(1, 0, res); |
||||
|
} else if (res.planStart - 0 >= oldTasks[oldTasks.length - 1].planStart - 0) { |
||||
|
// 开始时间大等于列表的最后一个 插入最后 |
||||
|
oldTasks.splice(-1, 0, res); |
||||
|
} else { |
||||
|
// 判断开始时间在列表中间的哪个位置 |
||||
|
for (let i = 0; i < oldTasks.length; i++) { |
||||
|
const item = oldTasks[i]; |
||||
|
if (res.planStart - 0 > item.planStart - 0) { |
||||
|
if (res.planStart - 0 <= oldTasks[i + 1].planStart - 0) { |
||||
|
oldTasks.splice(i + 1, 0, res); |
||||
|
// console.log('res: ', res); |
||||
|
// return; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
// TODO: 不能全更新 |
||||
|
console.log('oldTasks: ', oldTasks); |
||||
|
this.updateTasks([...oldTasks]); |
||||
|
const params = { roleId: this.roleId, projectId: this.projectId }; |
||||
|
this.getPermanent(params); |
||||
|
}, |
||||
|
}, |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.form { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
width: 100%; |
||||
|
max-height: 400px; |
||||
|
overflow-y: scroll; |
||||
|
} |
||||
|
|
||||
|
.drop-item { |
||||
|
border-bottom: 1px solid #f1f1f1; |
||||
|
padding: 16rpx; |
||||
|
} |
||||
|
|
||||
|
::v-deep.u-input--border { |
||||
|
border: none; |
||||
|
border-radius: 0; |
||||
|
} |
||||
|
::v-deep.u-dropdown__menu__item > uni-view { |
||||
|
border: none !important; |
||||
|
padding: 5px; |
||||
|
} |
||||
|
.u-input { |
||||
|
border-bottom: 1px solid #dcdfe6; |
||||
|
} |
||||
|
|
||||
|
.new-projects-box { |
||||
|
margin-top: 20px; |
||||
|
padding: 15px; |
||||
|
width: 100%; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
|
||||
|
.w { |
||||
|
width: 300px; |
||||
|
height: 39px; |
||||
|
} |
||||
|
|
||||
|
::v-deep .u-dropdown__menu__item .u-flex { |
||||
|
border: 0 !important; |
||||
|
border-bottom: 1px solid #dcdfe6 !important; |
||||
|
padding: 0 20rpx; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,583 @@ |
|||||
|
<template> |
||||
|
<div class="new-projects-box"> |
||||
|
<div class="form"> |
||||
|
<!-- 项目名称 --> |
||||
|
<view class="new-projects-title font-bold text-base flex justify-center items-center text-black">新建任务</view> |
||||
|
|
||||
|
<div class="form-item flex items-center"> |
||||
|
<div class="mr-4">名称<span class="text-red-500">*</span></div> |
||||
|
<u-input :maxlength="8" v-model="name" type="text" :inputAlign="'right'" :clearable="false" placeholder="请输入任务名称" /> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 开始时间 --> |
||||
|
<div class="form-item flex items-center"> |
||||
|
<div class="mr-4">开始时间<span class="text-red-500" v-if="source === 'regular'">*</span></div> |
||||
|
<div class="flex justify-end items-center flex-1"> |
||||
|
<u-input |
||||
|
placeholder="请选择开始时间" |
||||
|
v-model="startTime" |
||||
|
type="text" |
||||
|
:inputAlign="'right'" |
||||
|
:clearable="false" |
||||
|
@click="$emit('showTime', 1)" |
||||
|
/> |
||||
|
<u-icon class="ml-1" name="arrow-right" color="#969799" size="28"></u-icon> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 结束时间 --> |
||||
|
<div class="form-item flex items-center"> |
||||
|
<div class="mr-4">结束时间<span class="text-red-500" v-if="source === 'regular'">*</span></div> |
||||
|
<div class="flex justify-end items-center flex-1"> |
||||
|
<u-input |
||||
|
placeholder="请选择结束时间" |
||||
|
v-model="endTime" |
||||
|
type="text" |
||||
|
:inputAlign="'right'" |
||||
|
:clearable="false" |
||||
|
@click="$emit('showTime', 2)" |
||||
|
/> |
||||
|
<u-icon class="ml-1" name="arrow-right" color="#969799" size="28"></u-icon> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 多选框 --> |
||||
|
<div class="form-item flex justify-between items-center"> |
||||
|
<div class="mr-4">负责人<span class="text-red-500">*</span></div> |
||||
|
<div class="flex-1 text-right" v-if="hasRole">{{ roleName }}</div> |
||||
|
<div label="负责人" class="flex-1" v-else> |
||||
|
<u-dropdown disabled ref="uDropdown" placeholder="请选择负责人"> |
||||
|
<u-dropdown-item :title="roleList"> |
||||
|
<view class="slot-content bg-white"> |
||||
|
<div |
||||
|
class="flex flex-row justify-between mb-1 drop-item" |
||||
|
v-for="(role, roleIndex) in roleOptions" |
||||
|
:key="roleIndex" |
||||
|
@click="change(roleIndex)" |
||||
|
> |
||||
|
<view v-model="role.id">{{ role.name }}</view> |
||||
|
<u-icon v-if="role.dropdownShow" name="checkbox-mark" color="#2979ff" size="28"></u-icon> |
||||
|
</div> |
||||
|
</view> |
||||
|
</u-dropdown-item> |
||||
|
</u-dropdown> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 描述 --> |
||||
|
<div class="form-item flex items-center"> |
||||
|
<div class="mr-4">描述</div> |
||||
|
<u-input v-model="description" :maxlength="48" type="text" :inputAlign="'right'" :clearable="false" /> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 所属项目 --> |
||||
|
<div class="form-item flex items-center"> |
||||
|
<div class="mr-4">所属项目<span class="text-red-500">*</span></div> |
||||
|
<div class="flex-1 text-right">{{ project.name }}</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 所属任务 --> |
||||
|
<div class="form-item flex items-center" v-if="task && task.id"> |
||||
|
<div class="mr-4">所属任务</div> |
||||
|
<div class="flex-1 text-right">{{ task.name }}</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 上道工序 --> |
||||
|
<div class="form-item flex items-center"> |
||||
|
<div class="mr-4">上道工序</div> |
||||
|
<InputSearch |
||||
|
@searchPrevTask="searchPrevTask" |
||||
|
:dataSource="allTasks" |
||||
|
@select="handleChange" |
||||
|
@clearAllTasks="clearAllTasks" |
||||
|
placeholder="请输入上道工序" |
||||
|
/> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 检查人多选框 --> |
||||
|
<div class="form-item flex justify-between items-center"> |
||||
|
<div class="mr-4 flex-shrink-0">检查人<span class="text-red-500">*</span></div> |
||||
|
<div label="检查人" class="flex-1" style="width: calc(100% - 65px)"> |
||||
|
<u-dropdown ref="dropdown"> |
||||
|
<u-dropdown-item :title="checkerList"> |
||||
|
<view class="slot-content bg-white"> |
||||
|
<div |
||||
|
class="flex flex-row justify-between mb-1 drop-item" |
||||
|
v-for="(checkoutOption, Index) in checkoutOptions" |
||||
|
:key="Index" |
||||
|
@click="choose(Index)" |
||||
|
> |
||||
|
<view v-model="checkoutOption.value">{{ checkoutOption.name }}</view> |
||||
|
<u-icon v-if="checkoutOption.dropdownShow" name="checkbox-mark" color="#2979ff" size="28"></u-icon> |
||||
|
</div> |
||||
|
</view> |
||||
|
</u-dropdown-item> |
||||
|
</u-dropdown> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 是否是日常任务 --> |
||||
|
<div class="form-item flex justify-between items-center" v-if="!source"> |
||||
|
是否是日常任务 |
||||
|
<u-switch v-model="isGlobal" size="28"></u-switch> |
||||
|
</div> |
||||
|
|
||||
|
<div class="form-item flex justify-between items-center" v-for="(sort, sortIndex) in deliverSort" :key="sortIndex"> |
||||
|
<div class="mr-4">{{ `交付物${sortIndex + 1}` }}</div> |
||||
|
<div class="flex-1"> |
||||
|
<u-input |
||||
|
@blur="addDeliverInput" |
||||
|
v-model="sort.name" |
||||
|
:placeholder="`交付物名称${sortIndex + 1}`" |
||||
|
type="text" |
||||
|
:inputAlign="'right'" |
||||
|
:clearable="false" |
||||
|
/> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- <div class="form-item flex items-center"> |
||||
|
<div class="mr-4">添加插件</div> |
||||
|
<u-input :maxlength="8" v-model="name" type="text" :inputAlign="'right'" :clearable="false" placeholder="选择插件" /> |
||||
|
</div> --> |
||||
|
|
||||
|
<div class="form-btn flex items-center mt-4"> |
||||
|
<u-button type="primary" size="medium" @click="setParameters">提交</u-button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import { mapState, mapGetters, mapMutations, mapActions } from 'vuex'; |
||||
|
|
||||
|
export default { |
||||
|
props: { |
||||
|
startTime: { |
||||
|
type: String, |
||||
|
default: '', |
||||
|
}, |
||||
|
endTime: { |
||||
|
type: String, |
||||
|
default: '', |
||||
|
}, |
||||
|
task: { |
||||
|
type: Object, |
||||
|
default: null, |
||||
|
}, |
||||
|
source: { |
||||
|
type: String, |
||||
|
default: '', |
||||
|
}, |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
// arrow: true, |
||||
|
// show: false, |
||||
|
isGlobal: false, //是否日常任务 |
||||
|
name: '', //名称 |
||||
|
// showChooseTime: false, |
||||
|
// timeValue: '', //起止时间 |
||||
|
// taskStartTime: '', // 新建任务开始时间 |
||||
|
// taskEndTime: '', // 新建任务结束时间 |
||||
|
description: '', //描述 |
||||
|
// projectShow: false, //所属项目模糊搜索展示 |
||||
|
processTaskId: '', //上道工序 |
||||
|
// type: 'text', |
||||
|
// border: true, |
||||
|
roleList: undefined, //负责人默认多选 |
||||
|
checkerList: undefined, //检查人默认多选 |
||||
|
roleOptions: [], // 负责人下拉多选列表 |
||||
|
checkoutOptions: [], // 检查人下拉多选列表 |
||||
|
roleIdList: [], // 选中的负责人id |
||||
|
checkerIdList: [], // 选中的检查人id |
||||
|
deliverables: [], // 交付物 |
||||
|
deliverSort: [{ name: '' }], // 交付物排序 |
||||
|
allTasks: [], |
||||
|
roleName: '', // 负责人名字 |
||||
|
hasRole: false, // 有没有负责人 |
||||
|
}; |
||||
|
}, |
||||
|
|
||||
|
computed: { |
||||
|
...mapState('role', ['visibleRoles', 'roleId']), |
||||
|
...mapState('project', ['project']), |
||||
|
...mapState('task', ['tasks']), |
||||
|
...mapGetters('project', ['projectId']), |
||||
|
}, |
||||
|
|
||||
|
// watch: { |
||||
|
// endTime(val) { |
||||
|
// if (val) { |
||||
|
// this.timeValue = this.startTime + ' 至 ' + val; |
||||
|
// } |
||||
|
// }, |
||||
|
// }, |
||||
|
|
||||
|
mounted() { |
||||
|
// 获取负责人和检查人列表 |
||||
|
if (this.visibleRoles.length) { |
||||
|
this.visibleRoles.forEach(role => { |
||||
|
role.dropdownShow = false; |
||||
|
role.status = false; |
||||
|
}); |
||||
|
} |
||||
|
this.roleOptions = this.$u.deepClone(this.visibleRoles); |
||||
|
this.checkoutOptions = this.$u.deepClone(this.visibleRoles); |
||||
|
|
||||
|
// 判断有没有负责人 是不是添加子任务 |
||||
|
if (this.roleId) { |
||||
|
const item = this.visibleRoles.find(r => r.id === this.roleId); |
||||
|
if (item) { |
||||
|
this.roleName = item.name; |
||||
|
this.hasRole = true; |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
methods: { |
||||
|
...mapMutations('task', ['updateTasks']), |
||||
|
...mapActions('task', ['getPermanent']), |
||||
|
|
||||
|
// 负责人下拉多选选中 |
||||
|
change(index) { |
||||
|
let arr = [...this.roleOptions]; |
||||
|
// 选择多选项图标的展示 |
||||
|
arr[index].dropdownShow = !arr[index].dropdownShow; |
||||
|
// 多选展示的改变 |
||||
|
this.roleList = arr[index].name; |
||||
|
let shows = ''; |
||||
|
// 遍历arr,如果选中,添加到多选展示框上 |
||||
|
arr.map(val => { |
||||
|
if (val.dropdownShow === true) { |
||||
|
shows += val.name + ','; |
||||
|
this.roleIdList.push(val.id); |
||||
|
} |
||||
|
}); |
||||
|
this.roleOptions = [...arr]; |
||||
|
// 删除最后的',' |
||||
|
this.roleList = shows.slice(0, shows.length - 1); |
||||
|
}, |
||||
|
|
||||
|
// 检查人下拉多选选中 |
||||
|
choose(index) { |
||||
|
let arr = [...this.checkoutOptions]; |
||||
|
// 选择多选项图标的展示 |
||||
|
arr[index].dropdownShow = !arr[index].dropdownShow; |
||||
|
// 多选展示的改变 |
||||
|
this.checkerList = arr[index].name; |
||||
|
let shows = ''; |
||||
|
// 遍历arr,如果选中,添加到多选展示框上 |
||||
|
arr.map(val => { |
||||
|
if (val.dropdownShow === true) { |
||||
|
shows += val.name + ','; |
||||
|
this.checkerIdList.push(val.id); |
||||
|
} |
||||
|
}); |
||||
|
this.checkoutOptions = [...arr]; |
||||
|
// 删除最后的',' |
||||
|
this.checkerList = shows.slice(0, shows.length - 1); |
||||
|
// this.roleList = arr[value - 1].name; |
||||
|
}, |
||||
|
|
||||
|
// 打开下拉框 |
||||
|
// openDropdown() { |
||||
|
// this.arrow = !this.arrow; |
||||
|
// this.show = true; |
||||
|
// }, |
||||
|
|
||||
|
// // 关闭下拉框 |
||||
|
// closeSecondDropdown() { |
||||
|
// this.arrow = !this.arrow; |
||||
|
// this.show = false; |
||||
|
// }, |
||||
|
|
||||
|
/** |
||||
|
* 模糊查询 查找项目下的任务 |
||||
|
* @param name 任务名 |
||||
|
* @param projectId 项目id |
||||
|
*/ |
||||
|
async searchPrevTask(val) { |
||||
|
try { |
||||
|
const params = { name: val, projectId: this.projectId }; |
||||
|
const data = await this.$u.api.queryTaskOfProject(params); |
||||
|
this.allTasks = data; |
||||
|
return data; |
||||
|
} catch (error) { |
||||
|
console.error('error: ', error); |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
//用户点击获取的数据 |
||||
|
handleChange(data) { |
||||
|
console.log('data', data); |
||||
|
this.processTaskId = data.detailId; |
||||
|
}, |
||||
|
// 清空模糊查询信息 |
||||
|
clearAllTasks() { |
||||
|
this.allTasks = []; |
||||
|
}, |
||||
|
|
||||
|
// 数组最后一项有值 添加一条交付物输入框 |
||||
|
addDeliverInput() { |
||||
|
if (this.deliverSort[this.deliverSort.length - 1].name) { |
||||
|
this.deliverSort.push({ name: '' }); |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
// 设置提交参数 |
||||
|
async setParameters() { |
||||
|
if (this.source) { |
||||
|
this.isGlobal = 0; |
||||
|
} |
||||
|
|
||||
|
const { |
||||
|
projectId, |
||||
|
task, |
||||
|
name, |
||||
|
startTime, |
||||
|
endTime, |
||||
|
hasRole, |
||||
|
roleIdList, |
||||
|
roleId, |
||||
|
description, |
||||
|
processTaskId, |
||||
|
checkerIdList, |
||||
|
isGlobal, |
||||
|
} = this; |
||||
|
if (!name) { |
||||
|
uni.$ui.showToast('请输入任务名称'); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (!isGlobal) { |
||||
|
if (!startTime) { |
||||
|
uni.$ui.showToast('定期任务时间不能为空'); |
||||
|
return; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (startTime && endTime) { |
||||
|
let start = startTime ? this.$moment(startTime).format('x') - 0 : ''; |
||||
|
let end = endTime ? this.$moment(endTime).format('x') - 0 : ''; |
||||
|
if (start > end) { |
||||
|
uni.$ui.showToast('结束时间不能小于开始时间'); |
||||
|
return; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if ((!roleIdList || !roleIdList.length) && !hasRole) { |
||||
|
uni.$ui.showToast('请选择负责人'); |
||||
|
return; |
||||
|
} |
||||
|
if (!checkerIdList || !checkerIdList.length) { |
||||
|
uni.$ui.showToast('请选择检查人'); |
||||
|
return; |
||||
|
} |
||||
|
const deliverList = []; |
||||
|
this.deliverSort.forEach(item => { |
||||
|
if (item.name) { |
||||
|
deliverList.push(item.name); |
||||
|
} |
||||
|
}); |
||||
|
const params = { |
||||
|
name, |
||||
|
startTime: startTime ? this.$moment(startTime).format('x') - 0 : '', |
||||
|
endTime: endTime ? this.$moment(endTime).format('x') - 0 : '', |
||||
|
roleIdList: hasRole ? [roleId] : roleIdList, |
||||
|
description, |
||||
|
projectId, |
||||
|
parentTaskId: task && task.process !== 4 && task.id ? task.id : '', // 父任务 |
||||
|
processTaskId, // 上道工序 TODO |
||||
|
checkerIdList, |
||||
|
global: isGlobal ? 1 : 0, |
||||
|
deliverList, |
||||
|
}; |
||||
|
await this.handleSubmit(params); |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 新建任务 |
||||
|
* @param name 任务名 |
||||
|
* @param startTime 开始时间 |
||||
|
* @param endTime 结束时间 |
||||
|
* @param roleIdList 负责人id(数组) |
||||
|
* @param description 描述 |
||||
|
* @param projectId 所属项目id |
||||
|
* @param parentTaskId 所属任务id |
||||
|
* @param processTaskId 上道工序(任务id) |
||||
|
* @param checkerIdList 检查人id(数组) |
||||
|
* @param global 是否日常任务 0否 1是 |
||||
|
* @param deliverList 交付物名字(数组) |
||||
|
*/ |
||||
|
async handleSubmit(params) { |
||||
|
try { |
||||
|
const data = await this.$u.api.saveTask(params); |
||||
|
// TODO 任务新建成功 继续 or 取消 |
||||
|
this.$emit('closeMask'); |
||||
|
const newTasks = { |
||||
|
data: data[0], |
||||
|
processTaskId: params.processTaskId, |
||||
|
}; |
||||
|
// 将新加的任务加到store |
||||
|
// 判断不是子任务 -- 暂无子任务,暂不判断 |
||||
|
// if (!this.task || !this.task.id || this.task.process === 4) { |
||||
|
// this.addNewTasks(newTasks); |
||||
|
// } |
||||
|
|
||||
|
this.addNewTasks(newTasks); |
||||
|
} catch (error) { |
||||
|
this.$emit('closeMask'); |
||||
|
console.error('error: ', error); |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
// 添加任务后更新tasks |
||||
|
addNewTasks(data) { |
||||
|
const oldTasks = this.$u.deepClone(this.tasks); |
||||
|
let res = data.data; |
||||
|
|
||||
|
// 判断有没有选择上道工序 |
||||
|
if (data.processTaskId) { |
||||
|
const index = oldTasks.find(item => item.detailId === data.processTaskId); |
||||
|
if (index) { |
||||
|
oldTasks.splice(index + 1, 0, res); |
||||
|
} |
||||
|
} else { |
||||
|
this.setAddPosition(res, oldTasks); |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
// 设置添加位置 |
||||
|
setAddPosition(res, oldTasks) { |
||||
|
if (res.planStart - 0 < oldTasks[0].planStart - 0) { |
||||
|
// 开始时间小于列表的第一个 插入最前面 |
||||
|
oldTasks.splice(0, 0, res); |
||||
|
} else if (res.planStart - 0 === oldTasks[0].planStart - 0) { |
||||
|
// 开始时间等于列表的第一个 插入第二 |
||||
|
oldTasks.splice(1, 0, res); |
||||
|
} else if (res.planStart - 0 >= oldTasks[oldTasks.length - 1].planStart - 0) { |
||||
|
// 开始时间大等于列表的最后一个 插入最后 |
||||
|
oldTasks.splice(-1, 0, res); |
||||
|
} else { |
||||
|
// 判断开始时间在列表中间的哪个位置 |
||||
|
for (let i = 0; i < oldTasks.length; i++) { |
||||
|
const item = oldTasks[i]; |
||||
|
if (res.planStart - 0 > item.planStart - 0) { |
||||
|
if (res.planStart - 0 <= oldTasks[i + 1].planStart - 0) { |
||||
|
oldTasks.splice(i + 1, 0, res); |
||||
|
// console.log('res: ', res); |
||||
|
// return; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
// TODO: 不能全更新 |
||||
|
console.log('oldTasks: ', oldTasks); |
||||
|
this.updateTasks([...oldTasks]); |
||||
|
const params = { roleId: this.roleId, projectId: this.projectId }; |
||||
|
this.getPermanent(params); |
||||
|
}, |
||||
|
}, |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.form { |
||||
|
// display: flex; |
||||
|
// flex-direction: column; |
||||
|
width: 100%; |
||||
|
max-height: 400px; |
||||
|
overflow-y: scroll; |
||||
|
} |
||||
|
|
||||
|
.drop-item { |
||||
|
border-bottom: 1px solid #f1f1f1; |
||||
|
padding: 16rpx 32rpx; |
||||
|
} |
||||
|
|
||||
|
// ::v-deep.u-input--border { |
||||
|
// border: none; |
||||
|
// border-radius: 0; |
||||
|
// padding: 0 !important; |
||||
|
// } |
||||
|
::v-deep.u-dropdown__menu__item > uni-view { |
||||
|
border: none !important; |
||||
|
padding: 5px; |
||||
|
} |
||||
|
// .u-input { |
||||
|
// border-bottom: 1px solid #dcdfe6; |
||||
|
// } |
||||
|
|
||||
|
.new-projects-box { |
||||
|
margin-top: 20px; |
||||
|
// padding: 15px; |
||||
|
width: 100%; |
||||
|
overflow: hidden; |
||||
|
color: #595959; |
||||
|
} |
||||
|
|
||||
|
.new-projects-title { |
||||
|
height: 60px; |
||||
|
} |
||||
|
|
||||
|
.form-item { |
||||
|
padding: 0 16px; |
||||
|
height: 48px; |
||||
|
border-bottom: 1px solid rgba(0, 0, 0, 0.1); |
||||
|
} |
||||
|
|
||||
|
.form-item ::v-deep .input-group uni-input { |
||||
|
border: 0; |
||||
|
font-size: 14px; |
||||
|
text-align: right; |
||||
|
} |
||||
|
|
||||
|
.form-btn { |
||||
|
height: 60px; |
||||
|
} |
||||
|
|
||||
|
.form-btn .u-btn { |
||||
|
width: 156px; |
||||
|
height: 32px; |
||||
|
font-size: 16px; |
||||
|
border-radius: 4px; |
||||
|
} |
||||
|
|
||||
|
.w { |
||||
|
width: 300px; |
||||
|
height: 39px; |
||||
|
} |
||||
|
|
||||
|
::v-deep .u-dropdown__menu { |
||||
|
height: 48px !important; |
||||
|
} |
||||
|
|
||||
|
::v-deep .u-dropdown__menu__item { |
||||
|
width: 100%; |
||||
|
} |
||||
|
|
||||
|
::v-deep .u-dropdown__menu__item .u-flex { |
||||
|
border: 0 !important; |
||||
|
// border-bottom: 1px solid #dcdfe6 !important; |
||||
|
padding: 0 !important; |
||||
|
} |
||||
|
|
||||
|
::v-deep .u-dropdown__menu__item__arrow { |
||||
|
margin-left: 10px; |
||||
|
flex-shrink: 0; |
||||
|
} |
||||
|
|
||||
|
::v-deep .u-dropdown__menu__item__text { |
||||
|
width: calc(100% - 23px); |
||||
|
text-align: right; |
||||
|
overflow: hidden; |
||||
|
text-overflow: ellipsis; |
||||
|
white-space: nowrap; |
||||
|
} |
||||
|
|
||||
|
::v-deep .u-dropdown__content { |
||||
|
top: 48px !important; |
||||
|
box-shadow: 0 4px 6px 1px rgba(0, 0, 0, 0.1) !important; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,210 @@ |
|||||
|
<template> |
||||
|
<view class="flex justify-center"> |
||||
|
<view class="content p-3 pb-8"> |
||||
|
<view class="mb-3 font-bold text-base flex justify-center">创建分享链接</view> |
||||
|
<view class="flex flex-col"> |
||||
|
<view class="mb-1">用户以什么角色加入项目</view> |
||||
|
<!-- 下拉多选 --> |
||||
|
<view class="uni-list"> |
||||
|
<view class="uni-list-cell"> |
||||
|
<view class="uni-list-cell-db ml-2" v-if="rolesArray.length"> |
||||
|
<picker @change="changeRole" :value="index" :range="rolesArray"> |
||||
|
<view class="uni-input">{{ allRolesName[index].name }}</view> |
||||
|
</picker> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 复制链接 --> |
||||
|
<view class="link flex items-center mt-4"> |
||||
|
<view class="link-url">{{ links }}</view> |
||||
|
<u-button |
||||
|
style="border-radius: 0; height: 100%" |
||||
|
type="primary" |
||||
|
v-clipboard:copy="copyText" |
||||
|
v-clipboard:success="copySuccess" |
||||
|
v-clipboard:error="copyError" |
||||
|
> |
||||
|
复制链接 |
||||
|
</u-button> |
||||
|
</view> |
||||
|
<view @click="select"> |
||||
|
<!-- 全选按钮 --> |
||||
|
<!-- <view class="flex mt-4"> |
||||
|
<view> |
||||
|
<u-checkbox-group> |
||||
|
<u-checkbox v-model="checked" @change="checkedAll"></u-checkbox> |
||||
|
</u-checkbox-group> |
||||
|
</view> |
||||
|
<view>已选择({{ this.quantity }})</view> |
||||
|
<view style="color: #f37378; margin-left: 20px">批量删除</view> |
||||
|
</view> --> |
||||
|
<!-- 多选框 --> |
||||
|
<!-- <view> |
||||
|
<u-checkbox-group class="checkboxs flex flex-1 items-center mt-4" v-for="(item, index) in list" :key="index"> |
||||
|
<div class="flex-1 flex items-center"> |
||||
|
<u-checkbox v-model="item.checked"></u-checkbox> |
||||
|
<u-avatar :src="item.src" size="55" style="background: #d8dce0; margin-right: 10px"></u-avatar> |
||||
|
<div style="width: 60%; font-size: 12px"> |
||||
|
<div style="color: gray">{{ item.name }}</div> |
||||
|
<div style="color: #c4d0e1">{{ item.joinMethod }}</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</u-checkbox-group> |
||||
|
</view> --> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import { mapGetters, mapState } from 'vuex'; |
||||
|
|
||||
|
export default { |
||||
|
data() { |
||||
|
return { |
||||
|
rolesArray: [], |
||||
|
allRolesName: [], |
||||
|
index: 0, |
||||
|
links: '', //复制的链接 |
||||
|
copyText: '', |
||||
|
checked: false, //全选按钮是否选中 |
||||
|
roleName: '观众', |
||||
|
// 多选框列表 |
||||
|
list: [ |
||||
|
{ |
||||
|
name: '冯老师', |
||||
|
src: '', |
||||
|
joinMethod: '文件创建者', |
||||
|
role: '观众', |
||||
|
checked: false, |
||||
|
disabled: false, |
||||
|
}, |
||||
|
{ |
||||
|
name: '马壮', |
||||
|
src: '', |
||||
|
joinMethod: '通过链接加入', |
||||
|
role: '干系人', |
||||
|
checked: false, |
||||
|
disabled: false, |
||||
|
}, |
||||
|
{ |
||||
|
name: '张野', |
||||
|
src: '', |
||||
|
joinMethod: '通过链接加入', |
||||
|
role: '观众', |
||||
|
checked: false, |
||||
|
disabled: false, |
||||
|
}, |
||||
|
], |
||||
|
quantity: 0, //多选里面已选择的数量 |
||||
|
path: '', |
||||
|
}; |
||||
|
}, |
||||
|
|
||||
|
computed: { |
||||
|
...mapState('role', ['visibleRoles', 'invisibleRoles']), |
||||
|
...mapState('project', ['project']), |
||||
|
...mapGetters('project', ['projectId']), |
||||
|
}, |
||||
|
|
||||
|
mounted() { |
||||
|
this.$nextTick(() => { |
||||
|
this.path = window.location.href.split('?')[0]; |
||||
|
const { path, projectId } = this; |
||||
|
const params = { path: `${path}`, projectId, roleId: '0' }; |
||||
|
this.creatShare(params); |
||||
|
}); |
||||
|
|
||||
|
if (this.visibleRoles.length || this.invisibleRoles.length) { |
||||
|
const arr = this.visibleRoles.concat(this.invisibleRoles); |
||||
|
arr.forEach(role => { |
||||
|
let item = { id: '', name: '' }; |
||||
|
item.id = role.id; |
||||
|
item.name = role.name; |
||||
|
this.allRolesName.push(item); |
||||
|
this.rolesArray.push(role.name); |
||||
|
}); |
||||
|
const firstItem = { id: '0', name: '观众' }; |
||||
|
this.allRolesName.unshift(firstItem); |
||||
|
this.rolesArray.unshift('观众'); |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
methods: { |
||||
|
// 选择角色 |
||||
|
async changeRole(e) { |
||||
|
this.index = e.target.value; |
||||
|
this.roleName = this.allRolesName[this.index].name; |
||||
|
const { path, projectId } = this; |
||||
|
const params = { path, projectId, roleId: this.allRolesName[this.index].id }; |
||||
|
await this.creatShare(params); |
||||
|
}, |
||||
|
|
||||
|
// 复制成功 |
||||
|
copySuccess() { |
||||
|
uni.$uiowToast('复制成功'); |
||||
|
}, |
||||
|
// 复制失败 |
||||
|
copyError() { |
||||
|
uni.$uiowToast('复制失败,请稍后重试'); |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 创建分享链接 |
||||
|
* @param path 路径前缀 |
||||
|
* @param projectId 项目id |
||||
|
* @param roleId 角色id |
||||
|
*/ |
||||
|
async creatShare(params) { |
||||
|
try { |
||||
|
const data = await this.$u.api.createShare(params); |
||||
|
this.links = data.path; |
||||
|
this.copyText = `邀请您加入${this.project.name}的项目,角色为${this.roleName},链接为${data.path}&url=${this.$t.domain}`; |
||||
|
} catch (error) { |
||||
|
console.error('error: ', error); |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
//已选择 |
||||
|
select() { |
||||
|
this.quantity = 0; |
||||
|
this.list.forEach(val => { |
||||
|
if (val.checked == true) { |
||||
|
this.quantity++; |
||||
|
} |
||||
|
}); |
||||
|
}, |
||||
|
|
||||
|
// 全选 |
||||
|
checkedAll() { |
||||
|
this.list.map(val => { |
||||
|
val.checked = !this.checked; |
||||
|
}); |
||||
|
}, |
||||
|
}, |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.content { |
||||
|
width: 100%; |
||||
|
max-height: 400px; |
||||
|
} |
||||
|
|
||||
|
.link { |
||||
|
height: 40px; |
||||
|
border: 1px solid #afbed1; |
||||
|
} |
||||
|
|
||||
|
.link-url { |
||||
|
color: #afbed1; |
||||
|
width: 80%; |
||||
|
line-height: 40px; |
||||
|
margin-left: 5px; |
||||
|
overflow: hidden; |
||||
|
white-space: nowrap; |
||||
|
text-overflow: ellipsis; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,17 @@ |
|||||
|
export default { |
||||
|
timeUnits: [ |
||||
|
// 时间颗粒度
|
||||
|
{ id: 0, value: '毫秒', format: 'x', cycle: 'YY-M-D HH:mm:ss', granularity: 'millisecond' }, |
||||
|
{ id: 1, value: '秒', format: 'x', cycle: 'YY-M-D HH:mm:ss', granularity: 'second' }, |
||||
|
{ id: 2, value: '分', format: 'ss', cycle: 'YY-M-D HH:mm', granularity: 'minute' }, |
||||
|
{ id: 3, value: '时', format: 'mm', cycle: 'YY-M-D HH时', granularity: 'hour' }, |
||||
|
{ id: 4, value: '天', format: 'D日 HH:mm', cycle: 'YY-M-D', granularity: 'day' }, |
||||
|
{ id: 5, value: '周', format: 'D日 HH:mm', cycle: '', granularity: 'week' }, |
||||
|
{ id: 6, value: '月', format: 'D日 H:m', cycle: 'YYYY年', granularity: 'month' }, |
||||
|
{ id: 7, value: '季度', format: '', cycle: 'YYYY年', granularity: 'quarter' }, |
||||
|
{ id: 8, value: '年', format: 'YYYY', cycle: '', granularity: 'year' }, |
||||
|
{ id: 9, value: '年代', format: '', cycle: '', granularity: '' }, |
||||
|
{ id: 10, value: '世纪', format: '', cycle: '', granularity: '' }, |
||||
|
{ id: 11, value: '千年', format: '', cycle: '', granularity: '' }, |
||||
|
], |
||||
|
}; |
@ -0,0 +1,8 @@ |
|||||
|
import qs from 'qs'; |
||||
|
|
||||
|
export default function useGetOptions(){ |
||||
|
const options = getCurrentPages(); |
||||
|
const param = options[0].$page.fullPath.split('?')[1]; |
||||
|
const prefixed = qs.parse(param, { ignoreQueryPrefix: true }); |
||||
|
return prefixed |
||||
|
} |
@ -1,64 +1,436 @@ |
|||||
<template> |
<template> |
||||
<view :style="{ height: height }" class="flex flex-col overflow-hidden u-font-14"> |
<view :style="{ height: height }" class="flex flex-col overflow-hidden u-font-14"> |
||||
<!-- 标题栏 --> |
<!-- 标题栏 --> |
||||
<Title /> |
<Title /> |
||||
|
|
||||
<view class="container flex flex-col flex-1 mx-auto overflow-hidden bg-gray-100"> |
<view class="container flex flex-col flex-1 mx-auto overflow-hidden bg-gray-100"> |
||||
<!-- 角色栏 --> |
<!-- 角色栏 --> |
||||
<Roles /> |
<Roles /> |
||||
|
|
||||
<!-- 日常任务面板 --> |
<!-- 日常任务面板 --> |
||||
<Globals /> |
<Globals /> |
||||
|
|
||||
<!-- 定期任务面板 --> |
<!-- 定期任务面板 --> |
||||
<TimeLine @getTasks="getTasks" class="flex-1 overflow-hidden" ref="timeLine" /> |
<TimeLine @getTasks="getTasks" class="flex-1 overflow-hidden" ref="timeLine" /> |
||||
</view> |
</view> |
||||
</view> |
</view> |
||||
</template> |
</template> |
||||
|
|
||||
<script setup> |
<script> |
||||
import { ref, onMounted } from 'vue'; |
import { defineComponent, ref, onMounted, computed, watch, nextTick } from 'vue'; |
||||
import Navbar from '@/components/Title/Title.vue'; |
import { useStore } from 'vuex'; |
||||
import Roles from '@/components/Roles/Roles.vue'; |
import { flatten } from 'lodash'; |
||||
import Globals from '@/components/Globals/Globals.vue'; |
import useGetOptions from '@/hooks/useGetOptions'; |
||||
import TimeLine from '@/components/TimeLine/TimeLine.vue'; |
|
||||
|
export default defineComponent({ |
||||
|
setup(options) { |
||||
|
const store = useStore(); |
||||
|
const height = ref(null); |
||||
|
const timeLine = ref(null); |
||||
|
const token = computed(() => store.state.user.token); |
||||
|
const roleId = computed(() => store.state.role.roleId); |
||||
|
const timeNode = computed(() => store.state.task.timeNode); |
||||
|
const timeUnit = computed(() => store.state.task.timeUnit); |
||||
|
const tasks = computed(() => store.state.task.tasks); |
||||
|
const newProjectInfo = computed(() => store.state.task.newProjectInfo); |
||||
|
const showScrollTo = computed(() => store.state.task.showScrollTo); |
||||
|
const timeGranularity = computed(() => store.getters['task/timeGranularity']); |
||||
|
const projectId = computed(() => store.getters['project/projectId']); |
||||
|
const userId = computed(() => store.getters['user/userId']); |
||||
|
|
||||
|
/** |
||||
|
* 当时间基准点发生变化时 |
||||
|
* 重新根据时间和角色查询普通日常任务 |
||||
|
* 永久日常任务不发生改变 |
||||
|
*/ |
||||
|
watch(timeNode, newValue => { |
||||
|
if (newValue && roleId.value) { |
||||
|
clearTasksData(); |
||||
|
getGlobalData(); // 查可变日常任务 |
||||
|
initPlanTasks(); // 处理定期任务 |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
/** |
||||
|
* 当角色发生变化时 |
||||
|
* 重新查询永久日常任务和普通日常任务 |
||||
|
* 注意: 切换角色后 重新设置了时间基准点 时间基准点一定会变 |
||||
|
* 所以监听时间基准点获取 可变日常任务即可 这里不用获取 避免重复获取 |
||||
|
*/ |
||||
|
watch(roleId, newValue => { |
||||
|
if (newValue) { |
||||
|
store.commit('task/setTimeNode', Date.now()); |
||||
|
// 根据角色查找永久的日常任务 |
||||
|
const params = { roleId: newValue, projectId: projectId.value }; |
||||
|
store.dispatch('task/getPermanent', params); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
/** |
||||
|
* 当时间基准点发生变化时 |
||||
|
* 重新根据时间和角色查询普通日常任务 |
||||
|
* 永久日常任务不发生改变 |
||||
|
*/ |
||||
|
watch(newProjectInfo, newValue => { |
||||
|
if (newValue && newValue.projectId && newValue.url) { |
||||
|
uni.$u.route('/', { u: userId.value, p: newValue.projectId, url: newValue.url }); |
||||
|
clearTasksData(); |
||||
|
store.commit('role/setRoleId', ''); |
||||
|
const options = uni.$route.query; |
||||
|
init(options); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
onMounted(() => { |
||||
|
const system = uni.getSystemInfoSync(); |
||||
|
height.value = `${system.windowHeight}px`; |
||||
|
}); |
||||
|
|
||||
|
// 初始化 定期任务 |
||||
|
async function initPlanTasks() { |
||||
|
setPrevPlaceholderTasks(); // 向上加载空数据 |
||||
|
setNextPlaceholderTasks(); // 向下加载空数据 |
||||
|
await getInitTasks(); // 获取初始数据 |
||||
|
|
||||
|
// 滚动到对应位置 |
||||
|
let timer = null; |
||||
|
timer = setInterval(() => { |
||||
|
if (showScrollTo.value) { |
||||
|
clearInterval(timer); |
||||
|
// nextTick(() => timeLine.setScrollPosition()); |
||||
|
} |
||||
|
}, 500); |
||||
|
} |
||||
|
|
||||
|
// 切换了 颗粒度 || 角色时候 获取初始定期任务 |
||||
|
function getInitTasks() { |
||||
|
// 预加载 上下的定期任务 |
||||
|
function preloadFn(that) { |
||||
|
const detailId = tasks.value.findIndex(task => task.detailId); |
||||
|
const arr = []; |
||||
|
tasks.value.forEach(task => { |
||||
|
if (task.detailId) { |
||||
|
arr.push(task); |
||||
|
} |
||||
|
}); |
||||
|
if (detailId !== -1) { |
||||
|
// 只要有1个真实的任务 就预加载上下周期的任务 |
||||
|
const { pageCount } = uni.$task; |
||||
|
nextTick(() => { |
||||
|
// 向上拿数据 |
||||
|
getTasks({ timeNode: +tasks.value[detailId].planStart, queryType: 0, queryNum: pageCount }); |
||||
|
// 向下拿数据 |
||||
|
const nextQueryTime = +uni.$time.add(+arr[arr.length - 1].planStart, 1, timeGranularity.value); |
||||
|
getTasks({ timeNode: nextQueryTime, queryType: 1, queryNum: pageCount }); |
||||
|
}); |
||||
|
} else { |
||||
|
// 没有任务 上下显示时间刻度 |
||||
|
// 向上加载 |
||||
|
setPrevPlaceholderTasks(); |
||||
|
// // 向下加载 |
||||
|
setNextPlaceholderTasks(); |
||||
|
} |
||||
|
} |
||||
|
// 根据时间基准点和角色查找定期任务 |
||||
|
getTasks({ queryType: 0 }); // 向上获取定期任务数据 |
||||
|
|
||||
|
// 根据项目id获取角色列表 |
||||
|
getTasks({ queryType: 1 }, preloadFn); // 向下获取定期任务数据 |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 根据时间基准点和角色查找定期任务 |
||||
|
* @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, fn) { |
||||
|
store.commit('task/setShowSkeleton', false); |
||||
|
const params = generateGetTaskParam(query); |
||||
|
uni.$catchReq.getRegularTask(params, (err, data) => { |
||||
|
store.commit('task/setShowSkeleton', false); |
||||
|
if (err) { |
||||
|
// TODO: 提示错误 |
||||
|
console.error('err: ', err); |
||||
|
} else { |
||||
|
store.commit('task/setShowScrollTo', true); |
||||
|
// 有数据用数据替换刻度 |
||||
|
// 没有数据 继续加载刻度 |
||||
|
if (data && data.length) { |
||||
|
replacePrevData(data, params.queryType); |
||||
|
params.queryType === 0 ? store.commit('task/setTopEnd', false) : store.commit('task/setBottomEnd', false); |
||||
|
} else { |
||||
|
// TODO: 0 -> 向上 1 -> 向下 |
||||
|
params.queryType === 0 ? setPrevPlaceholderTasks() : setNextPlaceholderTasks(); |
||||
|
} |
||||
|
if (tasks.value.length && fn) { |
||||
|
fn(this); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 生成getTasks所用的参数 |
||||
|
* @param {object} query getTasks传递的参数 |
||||
|
*/ |
||||
|
function generateGetTaskParam(query) { |
||||
|
return { |
||||
|
roleId: roleId.value, |
||||
|
timeNode: query.timeNode || timeNode.value, |
||||
|
timeUnit: query.timeUnit || timeUnit.value, |
||||
|
queryNum: query.queryNum || 3, |
||||
|
queryType: query.queryType, |
||||
|
projectId: projectId.value, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
// 设置时间轴向上的空数据 |
||||
|
function setPrevPlaceholderTasks() { |
||||
|
store.commit('task/setTopEnd', true); |
||||
|
let startTime = ''; |
||||
|
if (!tasks.value || !tasks.value.length) { |
||||
|
startTime = Date.now(); // 没有任务就应该是时间基准点 |
||||
|
} else { |
||||
|
startTime = tasks[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 = +tasks.value[tasks.value.length - 1].planStart; |
||||
|
} |
||||
|
const initData = uni.$task.setPlaceholderTasks(startTime, false, timeGranularity.value); |
||||
|
store.commit('task/setDownTasks', initData); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 用拿到的新数据 替换 时间刻度/旧数据 |
||||
|
* 先对比 新旧数据的 始末时间 补齐刻度 |
||||
|
* 再遍历对比 用任务替换刻度 |
||||
|
* @param {array} data 服务端返回的新数据 上边已经处理过空值 |
||||
|
* @param {number} type 0 -> 向上 1->向下 |
||||
|
*/ |
||||
|
function replacePrevData(data, type) { |
||||
|
let oldTasks = fillPlaceholderTask({ tasks: tasks.value, data, timeGranularity: timeGranularity.value }); // 已经上下补齐时间刻度的 |
||||
|
|
||||
|
// 遍历对比 用任务替换刻度 |
||||
|
// 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({ tasks, data, timeGranularity }) { |
||||
|
const { prev, next } = uni.$task.computeFillPlaceholderTaskCount({ tasks, data, timeGranularity }); |
||||
|
if (prev) { |
||||
|
const newTasks = uni.$task.setPlaceholderTasks(+tasks[0].planStart, true, timeGranularity, prev); |
||||
|
store.commit('task/setUpTasks', newTasks); |
||||
|
} |
||||
|
if (next) { |
||||
|
const newTasks = uni.$task.setPlaceholderTasks(+tasks[tasks.length - 1].planStart, false, timeGranularity, next); |
||||
|
store.commit('task/setDownTasks', newTasks); |
||||
|
} |
||||
|
return tasks.value; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 初始化 |
||||
|
* @param {object | null} options |
||||
|
*/ |
||||
|
function init(options) { |
||||
|
if (!token.value) { |
||||
|
// 不论有没有token都直接从userId获取token |
||||
|
// token有过期时间 从本地获取可能是过期 干脆直接从userId获取 |
||||
|
if (!options || !options.u) { |
||||
|
uni.$ui.showToast('缺少用户信息参数'); // 参数里没有u (userId)提示 |
||||
|
} else { |
||||
|
store.dispatch('user/getToken', options.u); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 参数里有项目名称 就设置标题里的项目名称 |
||||
|
options && options.pname && store.commit('project/setProjectName', options.pname); |
||||
|
|
||||
|
if (!options || !options.p) { |
||||
|
uni.$ui.showToast('缺少项目信息参数'); // 没有项目id参数 |
||||
|
} else { |
||||
|
if (options.p !== uni.$storage.getStorageSync('projectId')) { |
||||
|
console.log('切项目了'); |
||||
|
uni.$storage.setStorageSync('roleId', ''); |
||||
|
} |
||||
|
// TODO |
||||
|
getProjectById({ projectId: options.p, num: 0 }); // 根据项目id获取项目信息 |
||||
|
// 查询医院是否填写了调查问卷 |
||||
|
// this.handleQueryNotWrite(options.p); |
||||
|
// 根据项目id获取成员列表 |
||||
|
store.dispatch('role/getAllMembers', { projectId: options.p }); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 分享链接来的初始化 |
||||
|
async function shareInit(options) { |
||||
|
const storageUser = uni.$storage.getStorageSync('user'); |
||||
|
const user = storageUser ? JSON.parse(storageUser) : null; |
||||
|
if (user && user.id) { |
||||
|
await store.dispatch('user/getToken', user.id); |
||||
|
const res = await clickShare({ code: options.shareId }); |
||||
|
if (res && res.projectId) { |
||||
|
let query = { ...uni.$route.query }; |
||||
|
query = { |
||||
|
u: user.id, |
||||
|
p: res.projectId, |
||||
|
}; |
||||
|
uni.$router.push({ path: uni.$route.path, query }); |
||||
|
init(query); |
||||
|
} |
||||
|
} else { |
||||
|
uni.$ui.showToast('缺少用户信息参数,请登录'); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function setOptions(options) { |
||||
|
if (options.share && options.share === '1') { |
||||
|
shareInit(options); |
||||
|
} else { |
||||
|
init(options); |
||||
|
} |
||||
|
} |
||||
|
setOptions(options); |
||||
|
|
||||
|
/** |
||||
|
* 点击分享连接 |
||||
|
* @param {any} commit |
||||
|
* @param {object} param 请求参数 |
||||
|
*/ |
||||
|
async function clickShare(param) { |
||||
|
try { |
||||
|
const data = await uni.$catchReq.clickShare(param); |
||||
|
return data; |
||||
|
} catch (error) { |
||||
|
uni.$ui.showToast(error.msg || '获取失败'); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 通过项目id获取项目信息 |
||||
|
* @param {object} params 提交的参数 |
||||
|
*/ |
||||
|
async function getProjectById(params) { |
||||
|
try { |
||||
|
const data = await uni.$u.api.findProjectById(params); |
||||
|
store.commit('project/setProject', data); |
||||
|
// 根据项目id获取角色列表 |
||||
|
getRoles(params); |
||||
|
} catch (error) { |
||||
|
console.log('error: ', error || '获取项目信息失败'); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 通过项目id获取角色信息 |
||||
|
* @param {string} projectId |
||||
|
* @param {object} params 提交的参数 |
||||
|
*/ |
||||
|
function getRoles(params) { |
||||
|
uni.$catchReq.findShowRole(params, (err, data) => { |
||||
|
if (err) { |
||||
|
console.error('err: ', err || '获取角色信息失败'); |
||||
|
} else { |
||||
|
store.commit('role/setInvisibleRoles', data ? data.invisibleList : []); |
||||
|
store.commit('role/setVisibleRoles', data ? data.visibleList : []); |
||||
|
setInitialRoleId(data ? data.visibleList : []); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
let height = ref(null); |
// 设置 初始显示角色信息 |
||||
|
function setInitialRoleId(visibleList) { |
||||
|
if (!visibleList || !visibleList.length) return; |
||||
|
const index = visibleList.findIndex(item => +item.mine === 1); |
||||
|
const currentRole = index > 0 ? visibleList[index] : visibleList[0]; |
||||
|
const storageRoleId = uni.$storage.getStorageSync('roleId'); |
||||
|
const currentRoleId = storageRoleId || (currentRole ? currentRole.id : ''); |
||||
|
store.commit('role/setRoleId', currentRoleId); |
||||
|
// 清空storage |
||||
|
uni.$storage.setStorageSync('roleId', ''); |
||||
|
} |
||||
|
|
||||
onMounted(() => { |
// 获取可变全局任务 |
||||
const system = uni.getSystemInfoSync(); |
function getGlobalData() { |
||||
height.value = system.windowHeight + 'px'; |
const param = { roleId: roleId.value, timeNode: timeNode.value, timeUnit: timeUnit.value, projectId: projectId.value }; |
||||
}); |
store.dispatch('task/getGlobal', param); |
||||
|
} |
||||
|
|
||||
function getTasks() { |
// 清除已有的任务数据 |
||||
|
function clearTasksData() { |
||||
|
// 清空日常任务的数据 |
||||
|
store.commit('task/setPermanents', []); |
||||
|
store.commit('task/setDailyTasks', []); |
||||
|
// 清空定期任务数据 |
||||
|
store.commit('task/clearTasks'); |
||||
|
// 到顶的标志复位 |
||||
|
// 到底的标志复位 |
||||
|
store.commit('task/clearEndFlag'); |
||||
|
} |
||||
|
return { |
||||
|
height, |
||||
|
timeLine, |
||||
|
roleId, |
||||
|
timeNode, |
||||
|
timeUnit, |
||||
|
tasks, |
||||
|
newProjectInfo, |
||||
|
showScrollTo, |
||||
|
timeGranularity, |
||||
|
projectId, |
||||
|
userId, |
||||
|
initPlanTasks, |
||||
|
getInitTasks, |
||||
|
getTasks, |
||||
|
generateGetTaskParam, |
||||
|
setPrevPlaceholderTasks, |
||||
|
setNextPlaceholderTasks, |
||||
|
replacePrevData, |
||||
|
fillPlaceholderTask, |
||||
|
init, |
||||
|
shareInit, |
||||
|
setOptions, |
||||
|
clickShare, |
||||
|
getProjectById, |
||||
|
getRoles, |
||||
|
setInitialRoleId, |
||||
|
getGlobalData, |
||||
|
clearTasksData, |
||||
|
}; |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
} |
|
||||
</script> |
</script> |
||||
|
|
||||
<style> |
<style lang="scss" scoped> |
||||
.content { |
.border-b { |
||||
display: flex; |
border-bottom: 1px solid #e4e7ed; |
||||
flex-direction: column; |
} |
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
} |
|
||||
|
|
||||
.logo { |
|
||||
height: 200rpx; |
|
||||
width: 200rpx; |
|
||||
margin-top: 200rpx; |
|
||||
margin-left: auto; |
|
||||
margin-right: auto; |
|
||||
margin-bottom: 50rpx; |
|
||||
} |
|
||||
|
|
||||
.text-area { |
|
||||
display: flex; |
|
||||
justify-content: center; |
|
||||
} |
|
||||
|
|
||||
.title { |
|
||||
font-size: 36rpx; |
|
||||
color: #8f8f94; |
|
||||
} |
|
||||
</style> |
</style> |
||||
|
@ -0,0 +1,17 @@ |
|||||
|
const actions = { |
||||
|
/** |
||||
|
* 根据项目id查找所有成员信息 |
||||
|
* @param {*} commit |
||||
|
* @param {object} params |
||||
|
*/ |
||||
|
async getAllMembers({ commit }, params) { |
||||
|
try { |
||||
|
const data = await uni.$catchReq.queryChecker(params); |
||||
|
commit('setMembers', data); |
||||
|
} catch (error) { |
||||
|
uni.$ui.showToast(error.msg || '成员查询失败'); |
||||
|
} |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
export default actions; |
@ -0,0 +1,13 @@ |
|||||
|
const getters = { |
||||
|
// 是不是负责人
|
||||
|
isMine({ roleId, invisibleRoles, visibleRoles }) { |
||||
|
if (!visibleRoles || !visibleRoles.length) return false; |
||||
|
const visible = visibleRoles.find(visible => visible.id === roleId); |
||||
|
if (visible) return visible.mine; |
||||
|
const invisible = invisibleRoles.find(invisible => invisible.id === roleId); |
||||
|
if (invisible) return visible.mine; |
||||
|
return false; |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
export default getters; |
@ -0,0 +1,12 @@ |
|||||
|
import state from './state'; |
||||
|
import getters from './getters'; |
||||
|
import mutations from './mutations'; |
||||
|
import actions from './actions'; |
||||
|
|
||||
|
export default { |
||||
|
namespaced: true, |
||||
|
state, |
||||
|
getters, |
||||
|
mutations, |
||||
|
actions, |
||||
|
}; |
@ -0,0 +1,39 @@ |
|||||
|
const mutations = { |
||||
|
/** |
||||
|
* 设置不展示的角色信息 |
||||
|
* @param {Object} state |
||||
|
* @param {Array} data 服务端返回的模板数组 |
||||
|
*/ |
||||
|
setInvisibleRoles(state, data) { |
||||
|
state.invisibleRoles = data || []; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 设置展示的角色信息 |
||||
|
* @param {Object} state |
||||
|
* @param {Array} data 服务端返回的模板数组 |
||||
|
*/ |
||||
|
setVisibleRoles(state, data) { |
||||
|
state.visibleRoles = data || []; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 设置当前角色信息 |
||||
|
* @param {Object} state |
||||
|
* @param {string} roleId 当前正在展示的角色的id |
||||
|
*/ |
||||
|
setRoleId(state, roleId) { |
||||
|
state.roleId = roleId; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 设置项目下所有成员信息 |
||||
|
* @param {Object} state |
||||
|
* @param {Array} data 服务端返回的模板数组 |
||||
|
*/ |
||||
|
setMembers(state, data) { |
||||
|
state.members = data || []; |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
export default mutations; |
@ -0,0 +1,8 @@ |
|||||
|
const state = { |
||||
|
invisibleRoles: [], // 不展示的角色信息
|
||||
|
visibleRoles: [], // 展示的角色信息
|
||||
|
roleId: '', // 当前展示查看的角色id
|
||||
|
members: [], // 项目下所有成员
|
||||
|
}; |
||||
|
|
||||
|
export default state; |
@ -0,0 +1,33 @@ |
|||||
|
const actions = { |
||||
|
/** |
||||
|
* 根据角色查找永久的日常任务 |
||||
|
* @param {*} commit |
||||
|
* @param {string} roleId 角色id |
||||
|
*/ |
||||
|
getPermanent({ commit }, param) { |
||||
|
uni.$catchReq.getPermanent(param, (err, data) => { |
||||
|
if (err) { |
||||
|
console.error('err: ', err); |
||||
|
} else { |
||||
|
commit('setPermanents', data); |
||||
|
} |
||||
|
}); |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 根据时间和角色查找日常任务 |
||||
|
* @param {*} commit |
||||
|
* @param {object} param 请求参数 roleId, timeNode, timeUnit |
||||
|
*/ |
||||
|
getGlobal({ commit }, param) { |
||||
|
uni.$catchReq.getGlobal(param, (err, data) => { |
||||
|
if (err) { |
||||
|
console.error('err: ', err); |
||||
|
} else { |
||||
|
commit('setDailyTasks', data); |
||||
|
} |
||||
|
}); |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
export default actions; |
@ -0,0 +1,23 @@ |
|||||
|
const getters = { |
||||
|
// 所有的日常任务 永久 + 可变 日常任务
|
||||
|
globals({ dailyTasks, permanents }) { |
||||
|
return [...permanents, ...dailyTasks]; |
||||
|
}, |
||||
|
|
||||
|
unitConfig({ timeUnit }) { |
||||
|
const target = uni.$timeConfig.timeUnits.find(item => item.id === timeUnit); |
||||
|
return target; |
||||
|
}, |
||||
|
|
||||
|
// 计算任务开始时间的格式
|
||||
|
startTimeFormat(state, { unitConfig }) { |
||||
|
return unitConfig.format || 'D日 HH:mm'; |
||||
|
}, |
||||
|
|
||||
|
// 计算颗粒度 对应的 dayjs add 的单位
|
||||
|
timeGranularity(state, { unitConfig }) { |
||||
|
return unitConfig.granularity; |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
export default getters; |
@ -0,0 +1,12 @@ |
|||||
|
import state from './state'; |
||||
|
import getters from './getters'; |
||||
|
import mutations from './mutations'; |
||||
|
import actions from './actions'; |
||||
|
|
||||
|
export default { |
||||
|
namespaced: true, |
||||
|
state, |
||||
|
getters, |
||||
|
mutations, |
||||
|
actions, |
||||
|
}; |
@ -0,0 +1,238 @@ |
|||||
|
const mutations = { |
||||
|
/** |
||||
|
* 记录时间轴向上滚动的距离 |
||||
|
* @param { object } state |
||||
|
* @param { number } num |
||||
|
*/ |
||||
|
setScrollTop(state, num) { |
||||
|
state.scrollTop = num; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 记录时间轴向上滚动的距离 |
||||
|
* @param { object } state |
||||
|
* @param {string} taskId |
||||
|
*/ |
||||
|
setScrollToTaskId(state, taskId) { |
||||
|
state.scrollToTaskId = taskId; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 设置日常任务当前是否应该处于收缩状态 |
||||
|
* @param { object } state |
||||
|
* @param { boolean } data |
||||
|
*/ |
||||
|
setShrink(state, data) { |
||||
|
state.isShrink = data; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 设置tip的值 |
||||
|
* @param {object} state |
||||
|
* @param {object} data |
||||
|
*/ |
||||
|
setTip(state, data) { |
||||
|
if (!data) return; |
||||
|
state.tip = { ...data }; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 是否显示tips |
||||
|
* @param { object } state |
||||
|
* @param { boolean } show |
||||
|
*/ |
||||
|
setTipShow(state, show) { |
||||
|
state.tip.show = show; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 是否显示tips |
||||
|
* @param { object } state |
||||
|
* @param { number } status |
||||
|
*/ |
||||
|
setStatus(state, status) { |
||||
|
state.tip.status = status; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 设置时间基准点 |
||||
|
* @param { object } state |
||||
|
* @param { number } data |
||||
|
*/ |
||||
|
setTimeNode(state, data) { |
||||
|
state.timeNode = data; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 设置时间颗粒度 |
||||
|
* @param { object } state |
||||
|
* @param { number } data |
||||
|
*/ |
||||
|
setTimeUnit(state, data) { |
||||
|
state.timeUnit = data; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 设置向上查到的定期任务数据 |
||||
|
* @param {Object} state |
||||
|
* @param {Array} data 服务端返回的模板数组 |
||||
|
*/ |
||||
|
setUpTasks(state, data) { |
||||
|
if (!state.tasks.length) { |
||||
|
state.tasks = [...data]; // 原来没有数据
|
||||
|
} else { |
||||
|
state.tasks = [...data, ...state.tasks]; |
||||
|
|
||||
|
let arr = [], |
||||
|
flag = false; |
||||
|
state.tasks.forEach(task => { |
||||
|
arr.forEach(item => { |
||||
|
if (task.id == item.id) { |
||||
|
flag = true; |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
if (!flag) { |
||||
|
arr.push(task); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
state.tasks = [...arr]; |
||||
|
// state.tasks = [...data.concat(state.tasks)];
|
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 设置向下查到的定期任务数据 |
||||
|
* @param {Object} state |
||||
|
* @param {Array} data 服务端返回的模板数组 |
||||
|
*/ |
||||
|
setDownTasks(state, data) { |
||||
|
if (!state.tasks && !state.tasks.length) { |
||||
|
state.tasks = [...data]; |
||||
|
} else { |
||||
|
state.tasks = [...state.tasks, ...data]; |
||||
|
|
||||
|
let arr = [], |
||||
|
flag = false; |
||||
|
state.tasks.forEach(task => { |
||||
|
arr.forEach(item => { |
||||
|
if (task.id == item.id) { |
||||
|
flag = true; |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
if (!flag) { |
||||
|
arr.push(task); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
state.tasks = [...arr]; |
||||
|
// state.tasks = [...state.tasks.concat(data)];
|
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 添加任务后更新tasks |
||||
|
* @param {Object} state |
||||
|
* @param {Array} data 新添加的task |
||||
|
*/ |
||||
|
updateTasks(state, data) { |
||||
|
state.tasks = [...data]; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 设置添加任务的位置 |
||||
|
* @param {*} state |
||||
|
* @param {*} data |
||||
|
*/ |
||||
|
setAddPosition(state, data) { |
||||
|
console.log('data: ', data); |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 设置日常任务数据 |
||||
|
* @param {Object} state |
||||
|
* @param {Array} data 服务端返回的模板数组 |
||||
|
*/ |
||||
|
setDailyTasks(state, data) { |
||||
|
state.dailyTasks = data || []; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 设置永久固定任务 |
||||
|
* @param {object} state |
||||
|
* @param {array} tasks 服务端查询到的永久日常任务书籍 |
||||
|
*/ |
||||
|
setPermanents(state, tasks) { |
||||
|
state.permanents = tasks || []; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 设置时间轴是否继续向上查任务 |
||||
|
* @param {Object} state |
||||
|
* @param {Boolean} show |
||||
|
*/ |
||||
|
setTopEnd(state, show) { |
||||
|
state.topEnd = show; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 设置时间轴是否继续向下查任务 |
||||
|
* @param {Object} state |
||||
|
* @param {Boolean} show |
||||
|
*/ |
||||
|
setBottomEnd(state, show) { |
||||
|
state.bottomEnd = show; |
||||
|
}, |
||||
|
|
||||
|
// 清空标志位 如切换角色等使用
|
||||
|
clearEndFlag(state) { |
||||
|
state.topEnd = false; |
||||
|
state.bottomEnd = false; |
||||
|
}, |
||||
|
|
||||
|
// 清空定期任务
|
||||
|
clearTasks(state) { |
||||
|
state.tasks = []; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 收到消息设置任务状态 |
||||
|
* @param {Object} state |
||||
|
* @param {Array} data 服务端返回的模板数组 |
||||
|
*/ |
||||
|
setTaskStatus(state, data) { |
||||
|
const item = state.tasks.find(i => i.id === data.id); |
||||
|
item.process = data.taskStatus; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 收到打开新项目消息状态 |
||||
|
* @param {Object} state |
||||
|
* @param {Array} data 服务端返回的模板数组 |
||||
|
*/ |
||||
|
setNewProjectInfo(state, data) { |
||||
|
state.newProjectInfo = data; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 设置骨架屏是否显示 |
||||
|
* @param {Object} state |
||||
|
* @param {Boolean} show |
||||
|
*/ |
||||
|
setShowSkeleton(state, show) { |
||||
|
state.showSkeleton = show; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 是否设置时间轴自动滚动的位置 |
||||
|
* @param {Object} state |
||||
|
* @param {Boolean} show |
||||
|
*/ |
||||
|
setShowScrollTo(state, show) { |
||||
|
state.showScrollTo = show; |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
export default mutations; |
@ -0,0 +1,25 @@ |
|||||
|
const state = { |
||||
|
scrollTop: 0, |
||||
|
scrollToTaskId: '', // 时间轴自动滚动的位置
|
||||
|
isShrink: false, // true: 收起, false:展开
|
||||
|
tip: { |
||||
|
taskId: '', // 当前正在修改状态的任务的id
|
||||
|
show: false, |
||||
|
status: 0, // 所点击任务的当前状态码
|
||||
|
text: '', |
||||
|
left: 0, // 鼠标点击位置距离左边的距离
|
||||
|
top: 0, // 鼠标点击位置距离上边的距离
|
||||
|
}, |
||||
|
timeNode: new Date().getTime(), // 时间基准点
|
||||
|
timeUnit: 4, // 时间颗粒度
|
||||
|
topEnd: false, // 时间轴向上查任务到顶了
|
||||
|
bottomEnd: false, // 时间轴向下查任务到底了
|
||||
|
permanents: [], // 永久日常任务
|
||||
|
dailyTasks: [], // 日常任务
|
||||
|
tasks: [], // 所有的定期任务
|
||||
|
showSkeleton: false, // 定期任务骨架屏
|
||||
|
newProjectInfo: {}, |
||||
|
showScrollTo: false, // 是否可以设置时间轴自动滚动的位置
|
||||
|
}; |
||||
|
|
||||
|
export default state; |
@ -0,0 +1,58 @@ |
|||||
|
import dayjs from 'dayjs'; |
||||
|
|
||||
|
/** |
||||
|
* 设置时间轴空数据 |
||||
|
* @param {number} startTime |
||||
|
* @param {boolean} isUp true 向上加载,false 向下加载 |
||||
|
* @param {string} timeGranularity 颗粒度 |
||||
|
* @param {number} pageCount 加载的颗粒度数量 默认值是10 |
||||
|
*/ |
||||
|
const setPlaceholderTasks = (startTime, isUp, timeGranularity, pageCount) => { |
||||
|
let result = []; |
||||
|
pageCount = pageCount || uni.$task.pageCount; |
||||
|
for (let i = 0; i < pageCount; i++) { |
||||
|
const delta = isUp ? `-${i + 1}` - 0 : i + 1; |
||||
|
let item = { |
||||
|
id: uni.$u.guid(20, false, 10), |
||||
|
panel: {}, |
||||
|
plugins: [], |
||||
|
process: 4, |
||||
|
planStart: uni.$moment(startTime).add(delta, timeGranularity).valueOf(), |
||||
|
}; |
||||
|
// console.log('isup: ', isUp, 'result:', new Date(item.planStart).toLocaleDateString());
|
||||
|
|
||||
|
isUp ? result.unshift(item) : result.push(item); |
||||
|
} |
||||
|
return result; |
||||
|
}; |
||||
|
|
||||
|
/** |
||||
|
* 超出旧数据上、下限 补齐时间刻度到新数据的起始时间颗粒度 |
||||
|
* @param {object} option |
||||
|
* @param {array} option.tasks 旧的已有的任务书籍 |
||||
|
* @param {array} option.data 新拿到的任务数据 空值已经过滤过了 |
||||
|
* @param {string} option.timeGranularity 颗粒度 |
||||
|
*/ |
||||
|
const computeFillPlaceholderTaskCount = ({ tasks, data, timeGranularity }) => { |
||||
|
const result = { prev: 0, next: 0 }; |
||||
|
// 新数据的开始时间 < 旧数据的开始时间
|
||||
|
// 超出了上限 补上限的时间刻度
|
||||
|
// 补上 新数据开始时间 到 旧数据开始时间 的刻度
|
||||
|
if (+data[0].planStart < +tasks[0].planStart) { |
||||
|
// 找出来需要补几组颗粒度
|
||||
|
result.prev = dayjs(+tasks[0].planStart).diff(+data[0].planStart, timeGranularity) + 1; |
||||
|
} |
||||
|
|
||||
|
// 新数据的结束时间 > 旧数据的结束时间
|
||||
|
// 超出了下线 补下限的时间刻度
|
||||
|
// 补上 旧数据截止时间 到 新数据截止时间 的刻度
|
||||
|
if (+data[data.length - 1].planStart > +tasks[tasks.length - 1].planStart) { |
||||
|
result.next = dayjs(+data[data.length - 1].planStart).diff(+tasks[tasks.length - 1].planStart, timeGranularity) + 1; |
||||
|
} |
||||
|
return result; |
||||
|
}; |
||||
|
|
||||
|
export default { |
||||
|
setPlaceholderTasks, |
||||
|
computeFillPlaceholderTaskCount |
||||
|
}; |
Loading…
Reference in new issue