Browse Source

feat: 时间轴接口

refact
song 4 years ago
parent
commit
a95d00559b
  1. 3
      App.vue
  2. 8
      CHANGELOG.md
  3. 4
      components/Calendar/Calendar.vue
  4. 73
      components/Globals/Globals.vue
  5. 137
      components/Plugin/Plugin.vue
  6. 279
      components/Projects/ProjectItem.vue
  7. 5
      components/Roles/Roles.vue
  8. 84
      components/Skeleton/READ_ME.md
  9. 173
      components/Skeleton/Skeleton.vue
  10. 8
      components/TimeLine/TimeLine.vue
  11. 81
      components/Tips/Tips.vue
  12. 133
      hooks/project/useGetTasks.js
  13. 449
      hooks/project/useInit - 副本.js
  14. 138
      hooks/project/useInit.js
  15. 9
      pages/index/index.vue
  16. 12
      pages/project/project A.vue
  17. 102
      pages/project/project.vue
  18. 2
      store/role/actions.js
  19. 2
      store/user/actions.js
  20. 4
      utils/task.js

3
App.vue

@ -8,7 +8,6 @@ export default {
},
async onLaunch(options) {
console.log('options: ', options);
this.checkNetwork(); //
this.getSystemInfo(); //
@ -21,7 +20,7 @@ export default {
// u (userId)
this.$ui.showToast('缺少用户信息参数');
} else {
const data = await store.dispatch('user/getTokenByUserId', options.query.u);
const data = await store.dispatch('user/getToken', options.query.u);
this.noPhone(data.phone);
}
}

8
CHANGELOG.md

@ -1,4 +1,4 @@
# 1.0.0 (2022-01-06)
# 1.0.0 (2022-01-07)
### 🌟 新功能
范围|描述|commitId
@ -21,6 +21,12 @@
- | calender格式及细节调整 | [db9602b](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/db9602b)
### 🔨 代码重构
范围|描述|commitId
--|--|--
- | 重构project init 部分 | [c7bf2df](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/c7bf2df)
### 🚀 性能优化
范围|描述|commitId
--|--|--

4
components/Calendar/Calendar.vue

@ -87,12 +87,12 @@
</template>
<script setup>
import { reactive, computed, watchEffect } from 'vue';
import { reactive, computed, watchEffect, defineProps, defineEmits } from 'vue';
import { useStore } from 'vuex';
import dayjs from 'dayjs';
import { generateDates, formatDate } from './generateDates.js';
defineProps({
const props = defineProps({
//
dotStyle: {
type: Object,

73
components/Globals/Globals.vue

@ -1,8 +1,77 @@
<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>
<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>
<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>

137
components/Plugin/Plugin.vue

@ -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>

279
components/Projects/ProjectItem.vue

@ -1,151 +1,134 @@
<template>
<view class="w-full">
<!-- 有子项目 -->
<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>
<view @click="openProject(item)" class="flex-1 px-3">
<view class="flex items-center mb-1">
<view class="mr-2">{{ item.name }}</view>
<!-- 状态 TODO:-->
<view class="px-2 text-xs text-green-400 bg-green-100 rounded-full flex-shrink-0">进行中</view>
</view>
<view class="flex items-center text-xs text-gray-400">
<view class="pr-2">{{ dayjs(item.startTime).format('MM-DD HH:mm') }}</view>
<view class="pl-2">{{ dayjs(item.endTime).format('MM-DD HH:mm') }}</view>
</view>
</view>
<!-- 箭头 -->
<view v-if="item.sonProjectList && item.sonProjectList.length">
<u-icon
@click="$emit('openSubProject', item.sonProjectList.length, index)"
class="text-gray-400"
name="arrow-up"
size="14px"
v-if="item.show"
></u-icon>
<u-icon
@click="$emit('openSubProject', item.sonProjectList.length, index)"
class="text-gray-400"
name="arrow-down"
size="14px"
v-else
></u-icon>
</view>
<u-icon @click="openProject(item)" class="text-gray-400" name="arrow-right" size="14px" v-else></u-icon>
</view>
<!-- 有子项目 -->
<view class="ml-8" v-if="item.show">
<view
:id="'cu-' + index + '-' + subIndex"
:key="subIndex"
class="cu-item flex-col"
v-for="(subItem, subIndex) in item.sonProjectList"
>
<!-- <view :key="subItem.id" v-for="subItem in item.sonProjectList"> -->
<view class="flex items-center justify-between p-3">
<u-icon @click="openMenu(subItem)" class="mover" name="https://www.tall.wiki/staticrec/drag.svg" size="48"></u-icon>
<view @click="openProject(subItem)" class="flex-1 px-3">
<view class="flex items-center">
<view class="mr-2">{{ subItem.name }}</view>
<!-- 状态 -->
<view
:class="
subItem.status === 0
? 'text-blue-400 bg-blue-100'
: subItem.status === 1
? 'text-green-400 bg-green-100'
: subItem.status === 2
? 'text-red-400 bg-red-100'
: 'text-gray-400 bg-gray-100'
"
class="px-2 text-xs text-gray-400 bg-gray-100 rounded-full flex-shrink-0"
>
{{ subItem.status === 0 ? '未开始' : subItem.status === 1 ? '进行中' : subItem.status === 2 ? '暂停' : '已完成' }}
</view>
</view>
</view>
<!-- 箭头 -->
<u-icon @click="openProject(subItem)" class="text-gray-400" name="arrow-right" size="14px"></u-icon>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { ref, computed } from 'vue';
import dayjs from 'dayjs';
import { useStore } from 'vuex';
import config from '@/common/js/config.js';
defineProps({
item: {
type: Object,
default: () => {},
},
index: {
type: Number,
default: 0,
},
menuList: {
type: Array,
default: () => [],
},
});
const emit = defineEmits(['setData']);
const store = useStore();
const userId = computed(() => store.getters['user/userId']);
const data = ref({
showMenu: false,
tips: {
text: '',
color: '#909399',
fontSize: 28,
},
// show: false,
// border: 'border border-blue-500 shadow rounded-md',
// showBorder: false,
projectId: 0,
});
//
function openProject(project) {
const gateway = config.apiUrl;
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>
<template>
<view class="w-full">
<!-- 有子项目 -->
<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>
<view @click="openProject(item)" class="flex-1 px-3">
<view class="flex items-center mb-1">
<view class="mr-2">{{ item.name }}</view>
<!-- 状态 TODO:-->
<view class="px-2 text-xs text-green-400 bg-green-100 rounded-full flex-shrink-0">进行中</view>
</view>
<view class="flex items-center text-xs text-gray-400">
<view class="pr-2">{{ dayjs(item.startTime).format('MM-DD HH:mm') }}</view>
<view class="pl-2">{{ dayjs(item.endTime).format('MM-DD HH:mm') }}</view>
</view>
</view>
<!-- 箭头 -->
<view v-if="item.sonProjectList && item.sonProjectList.length">
<u-icon @click="$emit('openSubProject', item.sonProjectList.length, index)" class="text-gray-400" name="arrow-up" size="14px" v-if="item.show"></u-icon>
<u-icon @click="$emit('openSubProject', item.sonProjectList.length, index)" class="text-gray-400" name="arrow-down" size="14px" v-else></u-icon>
</view>
<u-icon @click="openProject(item)" class="text-gray-400" name="arrow-right" size="14px" v-else></u-icon>
</view>
<!-- 有子项目 -->
<view class="ml-8" v-if="item.show">
<view :id="'cu-' + index + '-' + subIndex" :key="subIndex" class="cu-item flex-col" v-for="(subItem, subIndex) in item.sonProjectList">
<!-- <view :key="subItem.id" v-for="subItem in item.sonProjectList"> -->
<view class="flex items-center justify-between p-3">
<u-icon @click="openMenu(subItem)" class="mover" name="https://www.tall.wiki/staticrec/drag.svg" size="48"></u-icon>
<view @click="openProject(subItem)" class="flex-1 px-3">
<view class="flex items-center">
<view class="mr-2">{{ subItem.name }}</view>
<!-- 状态 -->
<view
:class="
subItem.status === 0
? 'text-blue-400 bg-blue-100'
: subItem.status === 1
? 'text-green-400 bg-green-100'
: subItem.status === 2
? 'text-red-400 bg-red-100'
: 'text-gray-400 bg-gray-100'
"
class="px-2 text-xs text-gray-400 bg-gray-100 rounded-full flex-shrink-0"
>
{{ subItem.status === 0 ? '未开始' : subItem.status === 1 ? '进行中' : subItem.status === 2 ? '暂停' : '已完成' }}
</view>
</view>
</view>
<!-- 箭头 -->
<u-icon @click="openProject(subItem)" class="text-gray-400" name="arrow-right" size="14px"></u-icon>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { ref, computed, defineProps, defineEmits } from 'vue';
import dayjs from 'dayjs';
import { useStore } from 'vuex';
import config from '@/common/js/config.js';
defineProps({
item: {
type: Object,
default: () => {},
},
index: {
type: Number,
default: 0,
},
// menuList: {
// type: Array,
// default: () => [],
// },
});
const emit = defineEmits(['setData']);
const store = useStore();
const userId = computed(() => store.getters['user/userId']);
const data = ref({
showMenu: false,
tips: {
text: '',
color: '#909399',
fontSize: 28,
},
// show: false,
// border: 'border border-blue-500 shadow rounded-md',
// showBorder: false,
projectId: 0,
});
//
function openProject(project) {
const gateway = config.apiUrl;
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.showMenu = true;
data.projectId = project.id;
data.tips.text = project.name;
emit('setData', data.showMenu, data.projectId, data.tips);
// this.$emit('setData', data.showMenu, data.projectId, data.tips);
}
</script>
<style lang="scss" scoped>
.border-100 {
height: 4rpx;
margin: 0 20rpx;
}
.border-100 {
height: 4rpx;
margin: 0 20rpx;
}
.border-80 {
height: 4rpx;
margin: 0 20rpx 0 90rpx;
}
.border-80 {
height: 4rpx;
margin: 0 20rpx 0 90rpx;
}
</style>

5
components/Roles/Roles.vue

@ -30,7 +30,7 @@
</template>
<script setup>
import { reactive, ref, computed, watchEffect, onMounted, nextTick } from 'vue';
import { reactive, computed, watchEffect, onMounted, nextTick } from 'vue';
import { useStore } from 'vuex';
const data = reactive({
@ -51,8 +51,6 @@ watchEffect(() => {
if (visibleRoles.value && visibleRoles.value.length) {
data.roles = visibleRoles.value;
data.loading = false;
console.log('data.roles', data.roles);
}
});
@ -65,7 +63,6 @@ onMounted(() => {
nextTick(() => {
const query = uni.createSelectorQuery().in(this);
console.log('query', query);
query
.selectAll('.tab-children')
.boundingClientRect(data => {

84
components/Skeleton/READ_ME.md

@ -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 &#124; Number | '100%' | |
| title | 是否显示标题 | Boolean &#124; String | false | |
| banner | 是否显示banner | Boolean &#124; String | false | |
| animate | 是否开启动画 | Boolean &#124; String | false | |
| avatar | 头像位置 | Boolean &#124; String | ''空 | left/top |
| avatarSize | 头像大小 | String | - | |
| avatarShape | 头像形状 | String | circle | circle/round |

173
components/Skeleton/Skeleton.vue

@ -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>

8
components/TimeLine/TimeLine.vue

@ -1,8 +0,0 @@
<template>
</template>
<script>
</script>
<style>
</style>

81
components/Tips/Tips.vue

@ -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>

133
hooks/project/useGetTasks.js

@ -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
}
}

449
hooks/project/useInit - 副本.js

@ -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');
}
}

138
hooks/project/useInit.js

@ -1,24 +1,102 @@
import { computed } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
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 {
useStore
} from 'vuex';
// import {
// flatten
// } from 'lodash';
export default function useInit() {
const store = useStore();
const token = computed(() => store.state.user.token);
const userId = useGetUserIdFromLocal();
console.log('userId: ', userId);
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);
init(options);
if (options.share && options.share === '1') {
shareInit(options);
} else {
init(options);
}
});
// onMounted(() => {
// const system = uni.getSystemInfoSync();
// height.value = `${system.windowHeight}px`;
// });
// 设置 初始显示角色信息
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', '');
}
/**
* 通过项目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 : []);
}
});
}
/**
* 通过项目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 || '获取项目信息失败');
}
}
/**
* 初始化
* @param {object | null} options
*/
function init(options) {
console.log('初始化init')
if (!token.value) {
// 不论有没有token都直接从userId获取token
// token有过期时间 从本地获取可能是过期 干脆直接从userId获取
@ -39,12 +117,54 @@ export default function useInit() {
console.log('切项目了');
uni.$storage.setStorageSync('roleId', '');
}
// TODO
getProjectById({ projectId: options.p, num: 0 }); // 根据项目id获取项目信息
// 根据项目id获取项目信息
const params = {
projectId: options.p,
num: 0
}
getProjectById(params);
// 查询医院是否填写了调查问卷
// this.handleQueryNotWrite(options.p);
// 根据项目id获取成员列表
store.dispatch('role/getAllMembers', { projectId: options.p });
store.dispatch('role/getAllMembers', {
projectId: options.p
});
}
}
// 分享链接来的初始化
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('缺少用户信息参数,请登录');
}
}
return {
init
}
}

9
pages/index/index.vue

@ -19,12 +19,13 @@
</template>
<script setup>
import { reactive, computed, watchEffect } from 'vue';
import { reactive, computed, watchEffect, ref } from 'vue';
import { useStore } from 'vuex';
import dayjs from 'dayjs';
const store = useStore();
const token = computed(() => store.state.user.token);
const token = computed(() => store.state.user.token);
const uTips = ref(null);
const data = reactive({
calendar: null,
@ -76,7 +77,7 @@ const onDateChange = event => {
//
const onUploadSuccess = () => {
uni.$refs.uTips.show({
uTips.show({
title: '导入成功,即将打开新项目',
type: 'success',
duration: '3000',
@ -85,7 +86,7 @@ const onUploadSuccess = () => {
//
const onUploadError = error => {
uni.$refs.uTips.show({
uTips.show({
title: error || '导入失败',
type: 'error',
duration: '6000',

12
pages/project/project copy.vue → pages/project/project A.vue

@ -11,7 +11,7 @@
<Globals />
<!-- 定期任务面板 -->
<TimeLine @getTasks="getTasks" class="flex-1 overflow-hidden" ref="timeLine" />
<!-- <TimeLine @getTasks="getTasks" class="flex-1 overflow-hidden" ref="timeLine" /> -->
</view>
</view>
</template>
@ -24,9 +24,8 @@ import { flatten } from 'lodash';
export default defineComponent({
setup(options) {
const store = useStore();
const height = ref(null);
const timeLine = ref(null);
const token = computed(() => store.state.user.token);
const userId = computed(() => store.getters['user/userId']);
const roleId = computed(() => store.state.role.roleId);
const timeNode = computed(() => store.state.task.timeNode);
const timeUnit = computed(() => store.state.task.timeUnit);
@ -35,12 +34,13 @@ export default defineComponent({
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']);
const height = ref(null);
const timeLine = ref(null);
/**
* 当时间基准点发生变化时
* 重新根据时间和角色查询普通日常任务
* 永久日常任务不发生改变
* 永久日常任务不发生 改变
*/
watch(timeNode, newValue => {
if (newValue && roleId.value) {
@ -221,7 +221,7 @@ export default defineComponent({
* @param {number} type 0 -> 向上 1->向下
*/
function replacePrevData(data, type) {
let oldTasks = fillPlaceholderTask({ tasks: tasks.value, data, timeGranularity: timeGranularity.value }); //
let oldTasks = fillPlaceholderTask({ tasks.value, data, timeGranularity.value }); //
//
// TODO: tasks

102
pages/project/project.vue

@ -5,24 +5,114 @@
<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>
</template>
<script setup>
import { computed } from 'vue';
import {
computed,
watch,
} from 'vue';
import { useStore } from 'vuex';
import { onLoad } from '@dcloudio/uni-app';
import useInit from '@/hooks/project/useInit';
import useGetTasks from '@/hooks/project/useGetTasks';
const initHook = useInit();
const getTasksHook = useGetTasks();
const store = useStore();
const roleId = computed(() => store.state.role.roleId);
const timeNode = computed(() => store.state.task.timeNode);
const timeUnit = computed(() => store.state.task.timeUnit);
const projectId = computed(() => store.getters['project/projectId']);
const userId = computed(() => store.getters['user/userId']);
const newProjectInfo = computed(() => store.state.task.newProjectInfo);
/**
* 当时间基准点发生变化时
* 重新根据时间和角色查询普通日常任务
* 永久日常任务不发生 改变
*/
watch(timeNode, newValue => {
if (newValue && roleId.value) {
console.log('当时间基准点发生变化时');
clearTasksData();
getGlobalData(); //
// initPlanTasks(); //
getTasksHook.initPlanTasks(); //
}
});
/**
* 当角色发生变化时
* 重新查询永久日常任务和普通日常任务
* 注意: 切换角色后 重新设置了时间基准点 时间基准点一定会变
* 所以监听时间基准点获取 可变日常任务即可 这里不用获取 避免重复获取
*/
watch(roleId, newValue => {
if (newValue) {
console.log('当角色发生变化时', newValue);
store.commit('task/setTimeNode', Date.now());
//
const params = {
roleId: newValue,
projectId: projectId.value,
};
store.dispatch('task/getPermanent', params);
}
});
/**
* 当时间基准点发生变化时
* 重新根据时间和角色查询普通日常任务
* 永久日常任务不发生改变
*/
watch(newProjectInfo, newValue => {
console.log('当时间基准点发生变化时');
if (newValue && newValue.value.projectId && newValue.value.url) {
uni.$u.route('/', {
u: userId.value,
p: newValue.value.projectId,
url: newValue.value.url,
});
clearTasksData();
store.commit('role/setRoleId', '');
const options = uni.$route.query;
initHook.init(options);
}
});
//
function 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');
}
useInit();
</script>
<style lang="scss" scoped>

2
store/role/actions.js

@ -6,7 +6,7 @@ const actions = {
*/
async getAllMembers({ commit }, params) {
try {
const data = await uni.$catchReq.queryChecker(params);
const data = await uni.$u.api.queryChecker(params);
commit('setMembers', data);
} catch (error) {
uni.$ui.showToast(error.msg || '成员查询失败');

2
store/user/actions.js

@ -6,7 +6,7 @@ const actions = {
* @param {any} commit
* @param {string} userId 用户id
*/
async getTokenByUserId({ commit }, userId) {
async getToken({ commit }, userId) {
try {
const res = await uni.$u.api.getToken(userId);
commit('setToken', res.token);

4
utils/task.js

@ -34,7 +34,7 @@ const setPlaceholderTasks = (startTime, isUp, timeGranularity, pageCount) => {
* @param {string} option.timeGranularity 颗粒度
*/
const computeFillPlaceholderTaskCount = ({ tasks, data, timeGranularity }) => {
const result = { prev: 0, next: 0 };
let result = { prev: 0, next: 0 };
// 新数据的开始时间 < 旧数据的开始时间
// 超出了上限 补上限的时间刻度
// 补上 新数据开始时间 到 旧数据开始时间 的刻度
@ -55,4 +55,4 @@ const computeFillPlaceholderTaskCount = ({ tasks, data, timeGranularity }) => {
export default {
setPlaceholderTasks,
computeFillPlaceholderTaskCount
};
};

Loading…
Cancel
Save