17 changed files with 1498 additions and 187 deletions
@ -1,8 +1,77 @@ |
|||||
<template> |
<template> |
||||
|
<view class="m-2" v-show="globals && globals.length"> |
||||
|
<u-card |
||||
|
@click="openCard" |
||||
|
:show-foot="false" |
||||
|
:show-head="false" |
||||
|
:style="{ 'max-height': globalsHeight + 'px' }" |
||||
|
border-radius="25" |
||||
|
margin="0" |
||||
|
> |
||||
|
<view slot="body"> |
||||
|
<scroll-view :scrollY="true" :style="{ 'max-height': globalsHeight - 30 + 'px' }"> |
||||
|
<skeleton :banner="false" :loading="!globals.length" :row="4" animate class="u-line-2 skeleton"></skeleton> |
||||
|
<view class="grid gap-2"> |
||||
|
<block v-for="item in globals" :key="item.id"> |
||||
|
<template v-if="item.plugins"> |
||||
|
<block v-for="(pluginArr, i) in item.plugins" :key="i"> |
||||
|
<template class="p-0 u-col-between" v-if="pluginArr.length"> |
||||
|
<Plugin |
||||
|
:class="[`row-span-${plugin.row}`, `col-span-${plugin.col}`]" |
||||
|
:task="item" |
||||
|
:key="plugin.pluginTaskId" |
||||
|
:plugin-task-id="plugin.pluginTaskId" |
||||
|
:plugin-id="plugin.pluginId" |
||||
|
:param="plugin.param" |
||||
|
:style-type="plugin.styleType || 0" |
||||
|
v-for="plugin in pluginArr" |
||||
|
/> |
||||
|
</template> |
||||
|
</block> |
||||
|
</template> |
||||
|
</block> |
||||
|
</view> |
||||
|
</scroll-view> |
||||
|
</view> |
||||
|
</u-card> |
||||
|
</view> |
||||
</template> |
</template> |
||||
|
|
||||
<script> |
<script setup> |
||||
|
import { computed } from 'vue'; |
||||
|
import { useStore } from 'vuex'; |
||||
|
|
||||
|
const store = useStore(); |
||||
|
const isShrink = computed(() => store.state.task.isShrink); |
||||
|
const sysHeight = uni.getSystemInfoSync().screenHeight; |
||||
|
const globals = computed(() => store.getters['task/globals']); |
||||
|
const globalsHeight = computed(() => [((sysHeight.value - 44 - 30 - 10) / 5) * 4]); |
||||
|
|
||||
|
// 手动展开日常任务 |
||||
|
function openCard() { |
||||
|
if (isShrink.value) { |
||||
|
store.commit('task/setShrink', false); |
||||
|
} |
||||
|
} |
||||
</script> |
</script> |
||||
|
|
||||
<style> |
<style scoped lang="scss"> |
||||
|
.u-card-wrap { |
||||
|
background-color: $u-bg-color; |
||||
|
padding: 1px; |
||||
|
} |
||||
|
|
||||
|
.u-body-item { |
||||
|
font-size: 32rpx; |
||||
|
color: #333; |
||||
|
padding: 20rpx 10rpx; |
||||
|
} |
||||
|
|
||||
|
.u-body-item image { |
||||
|
width: 120rpx; |
||||
|
flex: 0 0 120rpx; |
||||
|
height: 120rpx; |
||||
|
border-radius: 8rpx; |
||||
|
margin-left: 12rpx; |
||||
|
} |
||||
</style> |
</style> |
||||
|
@ -0,0 +1,137 @@ |
|||||
|
<template> |
||||
|
<view class="u-font-14" style="height: 100%"> |
||||
|
<view v-if="data.pluginContent" @click="setStorage"> |
||||
|
<view |
||||
|
:data-did="task.detailId" |
||||
|
:data-param="param" |
||||
|
:data-pdu="task.planDuration" |
||||
|
:data-pid="projectId" |
||||
|
:data-pstart="task.planStart" |
||||
|
:data-rdu="task.realDuration" |
||||
|
:data-rid="roleId" |
||||
|
:data-tid="task.id" |
||||
|
:data-tname="task.name" |
||||
|
:data-token="token" |
||||
|
:data-rstart="task.realStart" |
||||
|
:data-uid="userId" |
||||
|
style="height: 100%" |
||||
|
v-html="data.pluginContent" |
||||
|
></view> |
||||
|
</view> |
||||
|
|
||||
|
<view v-else @click="setStorage"> |
||||
|
<!-- <plugin-default /> --> |
||||
|
<!-- <component :task="task" :is="pluginComponent"></component> --> |
||||
|
<!-- <p-task-title :task="task" v-if="pluginId === '1'" /> |
||||
|
<p-task-description :task="task" v-if="pluginId === '2'" /> |
||||
|
<p-task-duration-delay :task="task" v-if="pluginId === '3'" /> |
||||
|
<p-task-start-time-delay :task="task" v-if="pluginId === '4'" /> |
||||
|
<p-upload-deliverable :task="task" v-if="pluginId === '5' && isMine" /> |
||||
|
<p-delivery-history :task="task" v-if="pluginId === '5' && !isMine" /> |
||||
|
<p-subtasks :task="task" v-if="pluginId === '6'" /> |
||||
|
<p-subproject :task="task" v-if="pluginId === '7'" /> |
||||
|
<p-task-countdown :task="task" v-if="pluginId === '8'" /> |
||||
|
<p-manage-project :task="task" v-if="pluginId === '9'" /> |
||||
|
<p-manage-role :task="task" v-if="pluginId === '10'" /> |
||||
|
<p-manage-member :task="task" v-if="pluginId === '11'" /> |
||||
|
<p-manage-task :task="task" v-if="pluginId === '12'" /> |
||||
|
<p-wbs-import :task="task" v-if="pluginId === '13' || pluginId === '14'" /> |
||||
|
<p-deliver-check :task="task" v-if="pluginId === '15'" /> --> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { reactive, defineProps, nextTick, computed } from 'vue'; |
||||
|
import { useStore } from 'vuex'; |
||||
|
|
||||
|
const props = defineProps({ |
||||
|
task: { default: () => {}, type: Object }, |
||||
|
pluginId: { default: '1', type: String }, |
||||
|
styleType: { default: 0, type: Number }, |
||||
|
pluginTaskId: { default: '', type: String }, |
||||
|
param: { type: String, default: '' }, |
||||
|
}); |
||||
|
|
||||
|
const data = reactive({ pluginContent: null }); |
||||
|
|
||||
|
const store = useStore(); |
||||
|
const roleId = computed(() => store.state.role.roleId); |
||||
|
const token = computed(() => store.state.user.token); |
||||
|
const userId = computed(() => store.getters['user/userId']); |
||||
|
const projectId = computed(() => store.getters['project/projectId']); |
||||
|
const isMine = computed(() => store.getters['role/isMine']); |
||||
|
|
||||
|
// 插件名称 |
||||
|
// pluginComponent() { |
||||
|
// const target = this.$t.plugin.defaults.find(item => item.id === +this.pluginId); |
||||
|
// if (!target) return ''; |
||||
|
// return target.component; |
||||
|
// }, |
||||
|
|
||||
|
// 创建script dom |
||||
|
function handleDom(js) { |
||||
|
const domList = Array.from(document.getElementsByTagName('script')); |
||||
|
const index = domList.findIndex(item => item.id === `p${props.pluginTaskId}`); |
||||
|
if (index >= 0) { |
||||
|
document.body.removeChild(document.getElementById(`p${props.pluginTaskId}`)); |
||||
|
} |
||||
|
const scriptDom = document.createElement('script'); |
||||
|
scriptDom.id = `p${props.pluginTaskId}`; |
||||
|
scriptDom.setAttribute('data-type', 'plugin'); |
||||
|
scriptDom.innerHTML = js; |
||||
|
nextTick(() => { |
||||
|
document.body.append(scriptDom); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
// 获取插件信息 |
||||
|
async function getPlugin() { |
||||
|
const params = { pluginId: props.pluginId, styleType: props.styleType }; |
||||
|
uni.$catchReq.getOtherPlugin(params, (err, data) => { |
||||
|
if (err) { |
||||
|
console.error('err: ', err); |
||||
|
} else { |
||||
|
if (!data || !data.id) return; |
||||
|
const reg = /data-root=["|']?(\w+)["|']?/gi; |
||||
|
let uuid = ''; |
||||
|
// FIXME: 没有兼容 只有js, 没有html的情况 |
||||
|
if (data.html) { |
||||
|
// 查有没有data-root=“xxx” 有的话 将xxx替换为 pluginTaskId |
||||
|
|
||||
|
if (reg.test(data.html)) { |
||||
|
uuid = RegExp.$1; |
||||
|
const str = data.html.replace(new RegExp(uuid, 'g'), `p${props.pluginTaskId}`); |
||||
|
data.pluginContent = str; |
||||
|
} else { |
||||
|
data.pluginContent = data.html; |
||||
|
} |
||||
|
|
||||
|
const str = data.js.replace(new RegExp(uuid, 'g'), `p${props.pluginTaskId}`); |
||||
|
handleDom(str); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
// if (data.js) { |
||||
|
// if (reg.test(data.js)) { |
||||
|
// const uuid = RegExp.$1; |
||||
|
// const str = data.js.replace(new RegExp(uuid, 'g'), `p${this.pluginTaskId}`); |
||||
|
// this.handleDom(str); |
||||
|
// } else { |
||||
|
// this.handleDom(data.js); |
||||
|
// } |
||||
|
// } |
||||
|
} |
||||
|
|
||||
|
if (props.pluginId === '5') { |
||||
|
// 根据项目id获取成员列表 |
||||
|
store.dispatch('role/getAllMembers', { projectId: projectId.value }); |
||||
|
} |
||||
|
getPlugin(); |
||||
|
|
||||
|
// 点击时存储 storage |
||||
|
async function setStorage() { |
||||
|
uni.$storage.setStorageSync('roleId', roleId.value); |
||||
|
} |
||||
|
</script> |
@ -1,151 +1,134 @@ |
|||||
<template> |
<template> |
||||
<view class="w-full"> |
<view class="w-full"> |
||||
<!-- 有子项目 --> |
<!-- 有子项目 --> |
||||
<view class="flex items-center justify-between p-3"> |
<view class="flex items-center justify-between p-3"> |
||||
<u-icon @click="openMenu(item)" class="mover" name="https://www.tall.wiki/staticrec/drag.svg" size="48"></u-icon> |
<u-icon @click="openMenu(item)" class="mover" name="https://www.tall.wiki/staticrec/drag.svg" size="48"></u-icon> |
||||
|
|
||||
<view @click="openProject(item)" class="flex-1 px-3"> |
<view @click="openProject(item)" class="flex-1 px-3"> |
||||
<view class="flex items-center mb-1"> |
<view class="flex items-center mb-1"> |
||||
<view class="mr-2">{{ item.name }}</view> |
<view class="mr-2">{{ item.name }}</view> |
||||
<!-- 状态 TODO:--> |
<!-- 状态 TODO:--> |
||||
<view class="px-2 text-xs text-green-400 bg-green-100 rounded-full flex-shrink-0">进行中</view> |
<view class="px-2 text-xs text-green-400 bg-green-100 rounded-full flex-shrink-0">进行中</view> |
||||
</view> |
</view> |
||||
|
|
||||
<view class="flex items-center text-xs text-gray-400"> |
<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="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 class="pl-2">{{ dayjs(item.endTime).format('MM-DD HH:mm') }}</view> |
||||
</view> |
</view> |
||||
</view> |
</view> |
||||
|
|
||||
<!-- 箭头 --> |
<!-- 箭头 --> |
||||
<view v-if="item.sonProjectList && item.sonProjectList.length"> |
<view v-if="item.sonProjectList && item.sonProjectList.length"> |
||||
<u-icon |
<u-icon @click="$emit('openSubProject', item.sonProjectList.length, index)" class="text-gray-400" name="arrow-up" size="14px" v-if="item.show"></u-icon> |
||||
@click="$emit('openSubProject', item.sonProjectList.length, index)" |
<u-icon @click="$emit('openSubProject', item.sonProjectList.length, index)" class="text-gray-400" name="arrow-down" size="14px" v-else></u-icon> |
||||
class="text-gray-400" |
</view> |
||||
name="arrow-up" |
<u-icon @click="openProject(item)" class="text-gray-400" name="arrow-right" size="14px" v-else></u-icon> |
||||
size="14px" |
</view> |
||||
v-if="item.show" |
|
||||
></u-icon> |
<!-- 有子项目 --> |
||||
<u-icon |
<view class="ml-8" v-if="item.show"> |
||||
@click="$emit('openSubProject', item.sonProjectList.length, index)" |
<view :id="'cu-' + index + '-' + subIndex" :key="subIndex" class="cu-item flex-col" v-for="(subItem, subIndex) in item.sonProjectList"> |
||||
class="text-gray-400" |
<!-- <view :key="subItem.id" v-for="subItem in item.sonProjectList"> --> |
||||
name="arrow-down" |
<view class="flex items-center justify-between p-3"> |
||||
size="14px" |
<u-icon @click="openMenu(subItem)" class="mover" name="https://www.tall.wiki/staticrec/drag.svg" size="48"></u-icon> |
||||
v-else |
|
||||
></u-icon> |
<view @click="openProject(subItem)" class="flex-1 px-3"> |
||||
</view> |
<view class="flex items-center"> |
||||
<u-icon @click="openProject(item)" class="text-gray-400" name="arrow-right" size="14px" v-else></u-icon> |
<view class="mr-2">{{ subItem.name }}</view> |
||||
</view> |
<!-- 状态 --> |
||||
|
<view |
||||
<!-- 有子项目 --> |
:class=" |
||||
<view class="ml-8" v-if="item.show"> |
subItem.status === 0 |
||||
<view |
? 'text-blue-400 bg-blue-100' |
||||
:id="'cu-' + index + '-' + subIndex" |
: subItem.status === 1 |
||||
:key="subIndex" |
? 'text-green-400 bg-green-100' |
||||
class="cu-item flex-col" |
: subItem.status === 2 |
||||
v-for="(subItem, subIndex) in item.sonProjectList" |
? 'text-red-400 bg-red-100' |
||||
> |
: 'text-gray-400 bg-gray-100' |
||||
<!-- <view :key="subItem.id" v-for="subItem in item.sonProjectList"> --> |
" |
||||
<view class="flex items-center justify-between p-3"> |
class="px-2 text-xs text-gray-400 bg-gray-100 rounded-full flex-shrink-0" |
||||
<u-icon @click="openMenu(subItem)" class="mover" name="https://www.tall.wiki/staticrec/drag.svg" size="48"></u-icon> |
> |
||||
|
{{ subItem.status === 0 ? '未开始' : subItem.status === 1 ? '进行中' : subItem.status === 2 ? '暂停' : '已完成' }} |
||||
<view @click="openProject(subItem)" class="flex-1 px-3"> |
</view> |
||||
<view class="flex items-center"> |
</view> |
||||
<view class="mr-2">{{ subItem.name }}</view> |
</view> |
||||
<!-- 状态 --> |
|
||||
<view |
<!-- 箭头 --> |
||||
:class=" |
<u-icon @click="openProject(subItem)" class="text-gray-400" name="arrow-right" size="14px"></u-icon> |
||||
subItem.status === 0 |
</view> |
||||
? 'text-blue-400 bg-blue-100' |
</view> |
||||
: subItem.status === 1 |
</view> |
||||
? 'text-green-400 bg-green-100' |
</view> |
||||
: subItem.status === 2 |
</template> |
||||
? 'text-red-400 bg-red-100' |
|
||||
: 'text-gray-400 bg-gray-100' |
<script setup> |
||||
" |
import { ref, computed, defineProps, defineEmits } from 'vue'; |
||||
class="px-2 text-xs text-gray-400 bg-gray-100 rounded-full flex-shrink-0" |
import dayjs from 'dayjs'; |
||||
> |
import { useStore } from 'vuex'; |
||||
{{ subItem.status === 0 ? '未开始' : subItem.status === 1 ? '进行中' : subItem.status === 2 ? '暂停' : '已完成' }} |
import config from '@/common/js/config.js'; |
||||
</view> |
|
||||
</view> |
defineProps({ |
||||
</view> |
item: { |
||||
|
type: Object, |
||||
<!-- 箭头 --> |
default: () => {}, |
||||
<u-icon @click="openProject(subItem)" class="text-gray-400" name="arrow-right" size="14px"></u-icon> |
}, |
||||
</view> |
index: { |
||||
</view> |
type: Number, |
||||
</view> |
default: 0, |
||||
</view> |
}, |
||||
</template> |
// menuList: { |
||||
|
// type: Array, |
||||
<script setup> |
// default: () => [], |
||||
import { ref, computed } from 'vue'; |
// }, |
||||
import dayjs from 'dayjs'; |
}); |
||||
import { useStore } from 'vuex'; |
const emit = defineEmits(['setData']); |
||||
import config from '@/common/js/config.js'; |
|
||||
|
const store = useStore(); |
||||
defineProps({ |
const userId = computed(() => store.getters['user/userId']); |
||||
item: { |
|
||||
type: Object, |
const data = ref({ |
||||
default: () => {}, |
showMenu: false, |
||||
}, |
tips: { |
||||
index: { |
text: '', |
||||
type: Number, |
color: '#909399', |
||||
default: 0, |
fontSize: 28, |
||||
}, |
}, |
||||
menuList: { |
// show: false, |
||||
type: Array, |
// border: 'border border-blue-500 shadow rounded-md', |
||||
default: () => [], |
// showBorder: false, |
||||
}, |
projectId: 0, |
||||
}); |
}); |
||||
const emit = defineEmits(['setData']); |
|
||||
|
// 打开项目详情 |
||||
const store = useStore(); |
function openProject(project) { |
||||
const userId = computed(() => store.getters['user/userId']); |
const gateway = config.apiUrl; |
||||
|
const url = `${gateway}/defaultwbs`; |
||||
const data = ref({ |
const { name, id } = project; |
||||
showMenu: false, |
uni.navigateTo({ url: `/pages/project/project?u=${userId.value}&p=${id}&pname=${name}&url=${encodeURIComponent(url)}` }); |
||||
tips: { |
} |
||||
text: '', |
|
||||
color: '#909399', |
/** |
||||
fontSize: 28, |
* 弹出项目操作面板 |
||||
}, |
*/ |
||||
// show: false, |
function openMenu(project) { |
||||
// border: 'border border-blue-500 shadow rounded-md', |
data.showMenu = true; |
||||
// showBorder: false, |
data.projectId = project.id; |
||||
projectId: 0, |
data.tips.text = project.name; |
||||
}); |
|
||||
|
emit('setData', data.showMenu, data.projectId, data.tips); |
||||
// 打开项目详情 |
// this.$emit('setData', data.showMenu, data.projectId, data.tips); |
||||
function openProject(project) { |
} |
||||
const gateway = config.apiUrl; |
</script> |
||||
const url = `${gateway}/defaultwbs`; |
|
||||
const { name, id } = project; |
|
||||
uni.navigateTo({ url: `/pages/project/project?u=${userId.value}&p=${id}&pname=${name}&url=${encodeURIComponent(url)}` }); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 弹出项目操作面板 |
|
||||
*/ |
|
||||
function openMenu(project) { |
|
||||
data.value.showMenu = true; |
|
||||
data.value.projectId = project.id; |
|
||||
data.value.tips.text = project.name; |
|
||||
|
|
||||
emit('setData', data.value.showMenu, data.value.projectId, data.value.tips); |
|
||||
// this.$emit('setData', data.value.showMenu, data.value.projectId, data.value.tips); |
|
||||
} |
|
||||
</script> |
|
||||
|
|
||||
<style lang="scss" scoped> |
<style lang="scss" scoped> |
||||
.border-100 { |
.border-100 { |
||||
height: 4rpx; |
height: 4rpx; |
||||
margin: 0 20rpx; |
margin: 0 20rpx; |
||||
} |
} |
||||
|
|
||||
.border-80 { |
.border-80 { |
||||
height: 4rpx; |
height: 4rpx; |
||||
margin: 0 20rpx 0 90rpx; |
margin: 0 20rpx 0 90rpx; |
||||
} |
} |
||||
</style> |
</style> |
||||
|
@ -0,0 +1,84 @@ |
|||||
|
# skeleton组件 |
||||
|
|
||||
|
### 1.描述 |
||||
|
> 此组件用于加载数据时占位图显示,跟vant-ui骨架屏用法相似,但比vant-ui更灵活 |
||||
|
|
||||
|
|
||||
|
|
||||
|
### 2.用法 |
||||
|
|
||||
|
- 基本用法 |
||||
|
|
||||
|
代码: |
||||
|
```vue |
||||
|
//基本用法 |
||||
|
<skeleton :row="3" animate :loading="loading" > |
||||
|
<view> |
||||
|
content |
||||
|
</view> |
||||
|
</skeleton> |
||||
|
``` |
||||
|
|
||||
|
|
||||
|
- **显示 title ——通过 **title 属性显示title占位图 |
||||
|
|
||||
|
代码: |
||||
|
```vue |
||||
|
//显示 title——通过 title 属性显示title占位图 |
||||
|
<skeleton :row="3" title animate :loading="loading"> |
||||
|
<view> |
||||
|
content |
||||
|
</view> |
||||
|
</skeleton> |
||||
|
``` |
||||
|
|
||||
|
|
||||
|
- 显示头像(上面)——通过avatar=‘top’让头像的占位图上面显示 |
||||
|
|
||||
|
代码: |
||||
|
```vue |
||||
|
<skeleton :avatar="top" avatarAlign="left" :row="3" animate :loading="loading" style="margin-top:24rpx;"> |
||||
|
<view> |
||||
|
content |
||||
|
</view> |
||||
|
</skeleton> |
||||
|
``` |
||||
|
|
||||
|
|
||||
|
- 显示头像(左边)——通过avatar=‘left’让头像的占位图左边显示 |
||||
|
|
||||
|
代码: |
||||
|
```vue |
||||
|
<skeleton title :avatar="left" :row="3" animate :loading="loading" style="margin-top:24rpx;"> |
||||
|
<view> |
||||
|
content |
||||
|
</view> |
||||
|
</skeleton> |
||||
|
``` |
||||
|
|
||||
|
|
||||
|
- 显示banner**——通过 **banner属性显示banner占位图(只显示banner,不显示内容占位图时设置row="0") |
||||
|
|
||||
|
代码: |
||||
|
```vue |
||||
|
<skeleton banner :row="0" animate :loading="loading" style="margin-top:24rpx;"> |
||||
|
<view> |
||||
|
content |
||||
|
</view> |
||||
|
</skeleton> |
||||
|
``` |
||||
|
### |
||||
|
### 3. API |
||||
|
### Props |
||||
|
| **属性名** | **说明** | **类型** | **默认值** | 可取值 | |
||||
|
| --- | --- | --- | --- | --- | |
||||
|
| loading | 是否显示骨架屏 | Boolean | true | true/false | |
||||
|
| row | 段落行数 | Number | String | 3 | 0表示不展现 | |
||||
|
| rowWidth | 段落行宽度 | Boolean | Number | '100%' | | |
||||
|
| title | 是否显示标题 | Boolean | String | false | | |
||||
|
| banner | 是否显示banner | Boolean | String | false | | |
||||
|
| animate | 是否开启动画 | Boolean | String | false | | |
||||
|
| avatar | 头像位置 | Boolean | String | ''空 | left/top | |
||||
|
| avatarSize | 头像大小 | String | - | | |
||||
|
| avatarShape | 头像形状 | String | circle | circle/round | |
||||
|
|
@ -0,0 +1,173 @@ |
|||||
|
<template> |
||||
|
<view> |
||||
|
<view :class="[avatarClass, animationClass]" class="lx-skeleton" v-show="loading"> |
||||
|
<view :class="[avatarShapeClass, bannerClass]" :style="{ width: avatarSize, height: avatarSize }" class="avatar-class"></view> |
||||
|
<view :style="{ width: rowWidth }" class="row"> |
||||
|
<view class="row-class lx-skeleton_title" v-if="title"></view> |
||||
|
<view :key="index" class="row-class" v-for="(item, index) in row"></view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<slot v-if="!loading"></slot> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { computed, defineProps } from 'vue'; |
||||
|
/** |
||||
|
* skeleton 骨架屏 |
||||
|
* @description 用于加载数据时占位图显示,跟Vant-UI用法相似,但比Vant-UI更灵活 |
||||
|
* @property {Boolean} loading 是否显示骨架屏,默认为true |
||||
|
* @property {Number | String} row 段落行数,默认为3 |
||||
|
* @property {Boolean | Number} rowWidth 段落行宽度,默认为100% |
||||
|
* @property {Boolean | String} title 是否显示标题,默认为false |
||||
|
* @property {Boolean | String} banner 是否显示banner,默认为false |
||||
|
* @property {Boolean | String} animate 是否开启动画,默认为false |
||||
|
* @property {Boolean | String} avatar 头像位置 |
||||
|
* @property {String} avatarSize 头像大小 |
||||
|
* @property {String} avatarShape 头像形状,默认为circle |
||||
|
* |
||||
|
* */ |
||||
|
const props = defineProps({ |
||||
|
loading: { |
||||
|
type: Boolean, |
||||
|
default: true, |
||||
|
}, |
||||
|
row: { |
||||
|
type: Number, |
||||
|
default: 3, |
||||
|
}, |
||||
|
title: { |
||||
|
type: String, |
||||
|
default: '', |
||||
|
}, |
||||
|
avatar: { |
||||
|
type: String, |
||||
|
default: '', |
||||
|
}, |
||||
|
animate: { |
||||
|
type: Boolean, |
||||
|
default: false, |
||||
|
}, |
||||
|
avatarSize: { type: String }, |
||||
|
rowWidth: { |
||||
|
type: String, |
||||
|
default: '100%', |
||||
|
}, |
||||
|
avatarShape: { |
||||
|
type: String, |
||||
|
default: 'circle', |
||||
|
}, |
||||
|
banner: { |
||||
|
type: Boolean, |
||||
|
default: false, |
||||
|
}, |
||||
|
// avator-size:{ |
||||
|
// type: String, |
||||
|
// defualt: '32px' |
||||
|
// } |
||||
|
}); |
||||
|
|
||||
|
const avatarClass = computed(() => { |
||||
|
if (props.avatar === 'top') { |
||||
|
return ['lx-skeleton_avator__top']; |
||||
|
} |
||||
|
if (props.avatar === 'left') { |
||||
|
return ['lx-skeleton_avator__left']; |
||||
|
} return ''; |
||||
|
}); |
||||
|
const animationClass = computed(() => [props.animate ? 'lx-skeleton_animation' : '']); |
||||
|
const slotClass = computed(() => [!props.loading ? 'show' : 'hide']); |
||||
|
const avatarShapeClass = computed(() => [props.avatarShape == 'round' ? 'lx-skeleton_avator__round' : '']); |
||||
|
const bannerClass = computed(() => [props.banner ? 'lx-skeleton_banner' : '']); |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.lx-skeleton { |
||||
|
background-color: #fff; |
||||
|
padding: 12px; |
||||
|
} |
||||
|
|
||||
|
.lx-skeleton_avator__left { |
||||
|
display: flex; |
||||
|
width: 100%; |
||||
|
} |
||||
|
|
||||
|
.lx-skeleton_avator__left .avatar-class, |
||||
|
.lx-skeleton_avator__top .avatar-class { |
||||
|
background-color: #f2f3f5; |
||||
|
border-radius: 50%; |
||||
|
width: 32px; |
||||
|
height: 32px; |
||||
|
} |
||||
|
|
||||
|
.lx-skeleton_avator__left .avatar-class.lx-skeleton_avator__round, |
||||
|
.lx-skeleton_avator__top .avatar-class.lx-skeleton_avator__round { |
||||
|
border-radius: 0; |
||||
|
width: 32px; |
||||
|
height: 32px; |
||||
|
} |
||||
|
|
||||
|
.lx-skeleton_avator__left .avatar-class { |
||||
|
margin-right: 16px; |
||||
|
} |
||||
|
|
||||
|
.lx-skeleton_avator__top .avatar-class { |
||||
|
margin: 0 auto 12px auto; |
||||
|
} |
||||
|
|
||||
|
.row-class { |
||||
|
width: 100%; |
||||
|
height: 16px; |
||||
|
background-color: #f2f3f5; |
||||
|
margin-top: 12px; |
||||
|
} |
||||
|
|
||||
|
.row-class:first-child { |
||||
|
margin-top: 0; |
||||
|
} |
||||
|
|
||||
|
.row { |
||||
|
flex: 1; |
||||
|
} |
||||
|
|
||||
|
.lx-skeleton_avator__left .row { |
||||
|
width: calc(100% - 48px); |
||||
|
} |
||||
|
|
||||
|
.row-class:last-child { |
||||
|
width: 60%; |
||||
|
} |
||||
|
|
||||
|
.lx-skeleton_animation .row-class { |
||||
|
animation-duration: 1.5s; |
||||
|
animation-name: blink; |
||||
|
animation-timing-function: ease-in-out; |
||||
|
animation-iteration-count: infinite; |
||||
|
} |
||||
|
|
||||
|
@keyframes blink { |
||||
|
50% { |
||||
|
opacity: 0.6; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.lx-skeleton_title { |
||||
|
width: 40%; |
||||
|
} |
||||
|
|
||||
|
.show { |
||||
|
display: block; |
||||
|
} |
||||
|
|
||||
|
.hide { |
||||
|
display: none; |
||||
|
} |
||||
|
|
||||
|
.lx-skeleton .lx-skeleton_banner { |
||||
|
width: 92%; |
||||
|
margin: 10px auto; |
||||
|
height: 64px; |
||||
|
border-radius: 0; |
||||
|
background-color: #f2f3f5; |
||||
|
} |
||||
|
</style> |
@ -1,8 +0,0 @@ |
|||||
<template> |
|
||||
</template> |
|
||||
|
|
||||
<script> |
|
||||
</script> |
|
||||
|
|
||||
<style> |
|
||||
</style> |
|
@ -0,0 +1,81 @@ |
|||||
|
<template> |
||||
|
<view |
||||
|
class="fixed shadow-2xl" |
||||
|
style="z-index: 1000" |
||||
|
:style="{ |
||||
|
left: tip.left + 'px', |
||||
|
top: data.height - tip.top > 110 ? tip.top + 'px' : '', |
||||
|
bottom: data.height - tip.top > 110 ? '' : '10px', |
||||
|
}" |
||||
|
id="u-icard" |
||||
|
> |
||||
|
<u-card :title="title" style="width: 500rpx; margin: 0 !important" v-if="tip.show" titleSize="28" :headStyle="data.headStyle" :footStyle="data.footStyle"> |
||||
|
<view class="" slot="body">{{ tip.text }}</view> |
||||
|
<view class="flex justify-end" slot="foot"> |
||||
|
<u-button size="mini" @click="onCancel">取消</u-button> |
||||
|
<u-button v-if="tip.status === 1" size="mini" @click="onChangeStatus(1)">暂停</u-button> |
||||
|
<u-button v-if="tip.status === 2" size="mini" @click="onChangeStatus(2)">继续</u-button> |
||||
|
<u-button v-if="tip.status === 1 || tip.status === 2" size="mini" @click="onChangeStatus(0)">重新开始</u-button> |
||||
|
<u-button v-if="tip.status === 1 || tip.status === 2" type="primary" size="mini" @click="onChangeStatus(3)">结束</u-button> |
||||
|
<u-button v-if="tip.status === 0 || tip.status === 3" type="primary" size="mini" @click="onChangeStatus(0)">确定</u-button> |
||||
|
</view> |
||||
|
</u-card> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { useStore, defineProps, onMounted, computed, reactive } from 'vuex'; |
||||
|
|
||||
|
defineProps({ title: { default: '提示', type: String } }); |
||||
|
|
||||
|
const store = useStore(); |
||||
|
const tip = computed(() => store.state.task.tip); |
||||
|
|
||||
|
const data = reactive({ |
||||
|
footStyle: { padding: '4px 15px' }, |
||||
|
headStyle: { paddingTop: '8px', paddingBottom: '8px' }, |
||||
|
height: 0, |
||||
|
}); |
||||
|
|
||||
|
onMounted(() => { |
||||
|
const system = uni.getSystemInfoSync(); |
||||
|
data.height = system.windowHeight; |
||||
|
}); |
||||
|
|
||||
|
/** |
||||
|
* 执行修改任务状态的动作 |
||||
|
* @param {number} type 状态码 0开始 1暂停 2继续 3完成 默认0 |
||||
|
*/ |
||||
|
async function onChangeStatus(type) { |
||||
|
try { |
||||
|
const param = { id: data.tip.taskId, type }; |
||||
|
await uni.$u.api.updateTaskType(param); |
||||
|
if (type === 0) { |
||||
|
uni.$ui.showToast('项目已重新开始'); |
||||
|
} else if (type === 1) { |
||||
|
uni.$ui.showToast('项目已暂停'); |
||||
|
} else if (type === 2) { |
||||
|
uni.$ui.showToast('项目继续'); |
||||
|
} else if (type === 3) { |
||||
|
uni.$ui.showToast('项目结束'); |
||||
|
} |
||||
|
data.tip.show = false; |
||||
|
// TODO: 更新界面 不要整体刷新 |
||||
|
// location.reload(); |
||||
|
// this.$router.go(0); |
||||
|
} catch (error) { |
||||
|
console.error(error); |
||||
|
uni.$ui.showToast(error.msg || '操作失败'); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 点击了取消 |
||||
|
function onCancel() { |
||||
|
store.commit('task/setTipShow', false); |
||||
|
} |
||||
|
|
||||
|
// 点击了确认 |
||||
|
// function onConfirm() { |
||||
|
// onCancel(); |
||||
|
// } |
||||
|
</script> |
@ -0,0 +1,133 @@ |
|||||
|
import { computed, nextTick } from 'vue'; |
||||
|
import { useStore } from 'vuex'; |
||||
|
|
||||
|
export default function useGetTasks() { |
||||
|
const store = useStore(); |
||||
|
const showScrollTo = computed(() => store.state.task.showScrollTo); |
||||
|
const roleId = computed(() => store.state.role.roleId); |
||||
|
const projectId = computed(() => store.getters['project/projectId']); |
||||
|
const timeNode = computed(() => store.state.task.timeNode); |
||||
|
const timeUnit = computed(() => store.state.task.timeUnit); |
||||
|
|
||||
|
// 初始化 定期任务
|
||||
|
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);
|
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 生成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, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 根据时间基准点和角色查找定期任务 |
||||
|
* @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);
|
||||
|
console.log('根据时间基准点和角色查找定期任务') |
||||
|
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);
|
||||
|
// }
|
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
return { |
||||
|
initPlanTasks |
||||
|
} |
||||
|
} |
@ -0,0 +1,449 @@ |
|||||
|
import { |
||||
|
ref, |
||||
|
onMounted, |
||||
|
computed, |
||||
|
watch, |
||||
|
nextTick |
||||
|
} from 'vue'; |
||||
|
import { |
||||
|
onLoad |
||||
|
} from '@dcloudio/uni-app'; |
||||
|
import useGetUserIdFromLocal from '@/hooks/user/useGetUserIdFromLocal'; |
||||
|
import { |
||||
|
useStore |
||||
|
} from 'vuex'; |
||||
|
import { |
||||
|
flatten |
||||
|
} from 'lodash'; |
||||
|
|
||||
|
export default function useInit() { |
||||
|
const store = useStore(); |
||||
|
const token = computed(() => store.state.user.token); |
||||
|
const userId = useGetUserIdFromLocal(); |
||||
|
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 height = ref(null); |
||||
|
const timeLine = ref(null); |
||||
|
|
||||
|
onLoad(options => { |
||||
|
console.log('onLoad options: ', options); |
||||
|
if (options.share && options.share === '1') { |
||||
|
shareInit(options); |
||||
|
} else { |
||||
|
init(options); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
/** |
||||
|
* 当时间基准点发生变化时 |
||||
|
* 重新根据时间和角色查询普通日常任务 |
||||
|
* 永久日常任务不发生 改变 |
||||
|
*/ |
||||
|
watch(timeNode, newValue => { |
||||
|
if (newValue && roleId.value) { |
||||
|
console.log('当时间基准点发生变化时') |
||||
|
clearTasksData(); |
||||
|
getGlobalData(); // 查可变日常任务
|
||||
|
initPlanTasks(); // 处理定期任务
|
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
/** |
||||
|
* 当角色发生变化时 |
||||
|
* 重新查询永久日常任务和普通日常任务 |
||||
|
* 注意: 切换角色后 重新设置了时间基准点 时间基准点一定会变 |
||||
|
* 所以监听时间基准点获取 可变日常任务即可 这里不用获取 避免重复获取 |
||||
|
*/ |
||||
|
watch(roleId, newValue => { |
||||
|
if (newValue) { |
||||
|
console.log('当角色发生变化时') |
||||
|
store.commit('task/setTimeNode', Date.now()); |
||||
|
// 根据角色查找永久的日常任务
|
||||
|
const params = { |
||||
|
roleId: newValue.value, |
||||
|
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; |
||||
|
init(options); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
onMounted(() => { |
||||
|
const system = uni.getSystemInfoSync(); |
||||
|
height.value = `${system.windowHeight}px`; |
||||
|
}); |
||||
|
|
||||
|
/** |
||||
|
* 初始化 |
||||
|
* @param {object | null} options |
||||
|
*/ |
||||
|
function init(options) { |
||||
|
console.log('初始化init') |
||||
|
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', ''); |
||||
|
} |
||||
|
// 根据项目id获取项目信息
|
||||
|
const params = { |
||||
|
projectId: options.p, |
||||
|
num: 0 |
||||
|
} |
||||
|
getProjectById(params); |
||||
|
// 查询医院是否填写了调查问卷
|
||||
|
// this.handleQueryNotWrite(options.p);
|
||||
|
// 根据项目id获取成员列表
|
||||
|
store.dispatch('role/getAllMembers', { |
||||
|
projectId: options.p |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 初始化 定期任务
|
||||
|
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.value, data, 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; |
||||
|
} |
||||
|
|
||||
|
// 分享链接来的初始化
|
||||
|
async function shareInit(options) { |
||||
|
console.log('分享链接来的初始化init') |
||||
|
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 |
||||
|
}); |
||||
|
console.log('query',query) |
||||
|
init(query); |
||||
|
} |
||||
|
} else { |
||||
|
uni.$ui.showToast('缺少用户信息参数,请登录'); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 点击分享连接 |
||||
|
* @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 : []); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
// 设置 初始显示角色信息
|
||||
|
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', ''); |
||||
|
} |
||||
|
|
||||
|
// 获取可变全局任务
|
||||
|
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'); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
} |
Loading…
Reference in new issue