Browse Source
小程序主题与project界面分离,并传递参数给project 不兼容变更: project完全独立出去,目前项目只有小程序非project内容;tailwindcss进行了精简pull/45/head
66 changed files with 4701 additions and 2995 deletions
@ -1,14 +0,0 @@ |
|||
// 插件的地址是固定的
|
|||
const url = process.env.VUE_APP_API_URL; |
|||
|
|||
const install = (Vue, vm) => { |
|||
vm.$u.api = { ...vm.$u.api } || {}; |
|||
// 获取插件信息
|
|||
vm.$u.api.getOtherPlugin = param => vm.$u.post(`${url}/pluginshop/plugin/query`, param); |
|||
// 查询子任务
|
|||
vm.$u.api.findSonTask = param => vm.$u.post(`${uni.$t.domain}/task/findSonTask`, param); |
|||
// 查询子项目
|
|||
vm.$u.api.findSonProject = param => vm.$u.post(`${uni.$t.domain}/project/findSonProject`, param); |
|||
}; |
|||
|
|||
export default { install }; |
@ -1,7 +0,0 @@ |
|||
const install = (Vue, vm) => { |
|||
vm.$u.api = { ...vm.$u.api } || {}; |
|||
//根据id获取项目信息
|
|||
vm.$u.api.findProjectById = param => vm.$u.post(`${uni.$t.domain}/project/findProjectById`, param); |
|||
}; |
|||
|
|||
export default { install }; |
@ -1,7 +0,0 @@ |
|||
const install = (Vue, vm) => { |
|||
vm.$u.api = { ...vm.$u.api } || {}; |
|||
//根据时间基准点和角色查找定期任务
|
|||
vm.$u.api.findShowRole = param => vm.$u.post(`${uni.$t.domain}/role/show`, param); |
|||
}; |
|||
|
|||
export default { install }; |
@ -1,11 +0,0 @@ |
|||
const install = (Vue, vm) => { |
|||
vm.$u.api = { ...vm.$u.api } || {}; |
|||
vm.$u.api.getGlobal = param => vm.$u.post(`${uni.$t.domain}/task/global`, param); |
|||
vm.$u.api.getPermanent = param => vm.$u.post(`${uni.$t.domain}/task/permanent`, param); |
|||
//根据时间基准点和角色查找定期任务
|
|||
vm.$u.api.getRegularTask = param => vm.$u.post(`${uni.$t.domain}/task/regular`, param); |
|||
//修改任务状态
|
|||
vm.$u.api.updateTaskType = param => vm.$u.post(`${uni.$t.domain}/task/type`, param); |
|||
}; |
|||
|
|||
export default { install }; |
@ -1,6 +0,0 @@ |
|||
/* ./src/common/styles/index.css */ |
|||
|
|||
/*! @import */ |
|||
@tailwind base; |
|||
@tailwind components; |
|||
@tailwind utilities; |
File diff suppressed because it is too large
@ -1,91 +0,0 @@ |
|||
<template> |
|||
<view class="m-2" v-if="globals && globals.length"> |
|||
<u-card |
|||
@click="openCard" |
|||
:show-foot="false" |
|||
:show-head="false" |
|||
:style="{ 'max-height': isShrink ? '106rpx' : '340rpx' }" |
|||
border-radius="25" |
|||
margin="0" |
|||
> |
|||
<view slot="body"> |
|||
<scroll-view :scrollY="true" :style="{ 'max-height': isShrink ? '50rpx' : '280rpx' }"> |
|||
<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" |
|||
:style-type="plugin.styleType || 0" |
|||
v-for="plugin in pluginArr" |
|||
/> |
|||
</template> |
|||
</block> |
|||
</template> |
|||
</block> |
|||
</view> |
|||
</scroll-view> |
|||
</view> |
|||
</u-card> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import { mapGetters, mapMutations, mapState } from 'vuex'; |
|||
import Skeleton from '@/components/Skeleton/Skeleton'; |
|||
|
|||
export default { |
|||
name: 'Global', |
|||
components: { Skeleton }, |
|||
|
|||
data() { |
|||
return { |
|||
// loading: true, |
|||
task: null, |
|||
}; |
|||
}, |
|||
|
|||
computed: { |
|||
...mapState('task', ['isShrink']), |
|||
...mapGetters('task', ['globals']), |
|||
}, |
|||
|
|||
methods: { |
|||
...mapMutations('task', ['setShrink']), |
|||
|
|||
// 手动展开日常任务 |
|||
openCard() { |
|||
if (this.isShrink) { |
|||
this.setShrink(false); |
|||
} |
|||
}, |
|||
}, |
|||
}; |
|||
</script> |
|||
|
|||
<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> |
@ -1,139 +0,0 @@ |
|||
<template> |
|||
<view class="u-font-14" style="height: 100%"> |
|||
<view v-if="pluginContent"> |
|||
<view |
|||
style="height: 100%" |
|||
:data-uid="userId" |
|||
:data-token="token" |
|||
:data-pid="projectId" |
|||
:data-did="task.detailId" |
|||
:data-rid="roleId" |
|||
:data-tid="task.id" |
|||
:data-tname="task.name" |
|||
:data-pstart="task.planStart" |
|||
:data-rstart="task.realStart" |
|||
:data-pdu="task.planDuration" |
|||
:data-rdu="task.realDuration" |
|||
:data-param="param" |
|||
v-html="pluginContent" |
|||
></view> |
|||
</view> |
|||
|
|||
<view v-else> |
|||
<!-- <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-deliverable :task="task" v-if="pluginId === '5'" /> --> |
|||
<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> |
|||
import { mapGetters, mapState } from 'vuex'; |
|||
import pManageProject from '../../plugins/p-manage-project/p-manage-project.vue'; |
|||
|
|||
export default { |
|||
components: { pManageProject }, |
|||
name: 'Plugin', |
|||
props: { |
|||
task: { default: () => {}, type: Object }, |
|||
pluginId: { default: '1', type: String }, |
|||
styleType: { default: 0, type: Number }, |
|||
pluginTaskId: { default: '', type: String }, |
|||
param: { type: String, default: '' }, |
|||
}, |
|||
|
|||
data() { |
|||
return { pluginContent: null }; |
|||
}, |
|||
|
|||
computed: { |
|||
...mapState('role', ['roleId']), |
|||
...mapState('user', ['token']), |
|||
...mapGetters('user', ['userId']), |
|||
...mapGetters('project', ['projectId']), |
|||
|
|||
// 插件名称 |
|||
// pluginComponent() { |
|||
// const target = this.$t.plugin.defaults.find(item => item.id === +this.pluginId); |
|||
// if (!target) return ''; |
|||
// return target.component; |
|||
// }, |
|||
}, |
|||
|
|||
created() { |
|||
this.getPlugin(); |
|||
}, |
|||
|
|||
methods: { |
|||
// 获取插件信息 |
|||
async getPlugin() { |
|||
const { pluginId, styleType } = this; |
|||
const params = { pluginId, styleType }; |
|||
this.$t.$q.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${this.pluginTaskId}`); |
|||
this.pluginContent = str; |
|||
} else { |
|||
this.pluginContent = data.html; |
|||
} |
|||
|
|||
const str = data.js.replace(new RegExp(uuid, 'g'), `p${this.pluginTaskId}`); |
|||
this.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); |
|||
// } |
|||
// } |
|||
}, |
|||
|
|||
// 创建script dom |
|||
handleDom(js) { |
|||
const { pluginTaskId } = this; |
|||
let domList = Array.from(document.getElementsByTagName('script')); |
|||
const index = domList.findIndex(item => item.id === `p${pluginTaskId}`); |
|||
if (index >= 0) { |
|||
document.body.removeChild(document.getElementById(`p${pluginTaskId}`)); |
|||
} |
|||
const scriptDom = document.createElement('script'); |
|||
scriptDom.id = `p${pluginTaskId}`; |
|||
scriptDom.setAttribute('data-type', 'plugin'); |
|||
scriptDom.innerHTML = js; |
|||
this.$nextTick(() => { |
|||
document.body.append(scriptDom); |
|||
}); |
|||
}, |
|||
}, |
|||
}; |
|||
</script> |
@ -1,233 +0,0 @@ |
|||
<template> |
|||
<view class="px-2 bg-white wrap"> |
|||
<view class="home-box u-skeleton"> |
|||
<scroll-view :enable-flex="true" :scroll-left="scrollLeft" :throttle="false" scroll-with-animation scroll-x> |
|||
<view class="tab-box"> |
|||
<!-- 角色项 |
|||
default-tab-choice 是 我的角色 && 当前展示 |
|||
default-tab-item 是 我的角色 && 当前不展示 |
|||
tab-choice 不是我的 && 当前展示 |
|||
--> |
|||
<view |
|||
:class="{ |
|||
'default-tab-choice': item.mine == 1 && roleId === item.id, |
|||
'default-tab-item': item.mine == 1 && roleId !== item.id, |
|||
'tab-choice': item.mine == 0 && roleId === item.id, |
|||
}" |
|||
:key="index" |
|||
@click="changeRole(item.id, index)" |
|||
class="tab-item" |
|||
v-for="(item, index) in roles" |
|||
> |
|||
<view class="tab-children u-skeleton-fillet u-font-14"> |
|||
{{ item.name }} |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</scroll-view> |
|||
</view> |
|||
<!-- 骨架屏 --> |
|||
<u-skeleton :animation="true" :loading="loading" bg-color="#fff"></u-skeleton> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import { mapState, mapMutations, mapActions } from 'vuex'; |
|||
|
|||
export default { |
|||
name: 'Roles', |
|||
data() { |
|||
return { |
|||
tabIndex: 0, // 当前访问的 index 默认为0 |
|||
tabList: [], // tab dom节点集合 |
|||
scrollLeft: 0, // scrollview需要滚动的距离 |
|||
loading: false, // 是否显示骨架屏组件 |
|||
roles: [ |
|||
{ id: 1, name: '项目经理', mine: 0, pm: 1, sequence: 1 }, |
|||
{ id: 2, name: '运维', mine: 0, pm: 0, sequence: 2 }, |
|||
], |
|||
}; |
|||
}, |
|||
|
|||
computed: { |
|||
...mapState('role', ['visibleRoles', 'roleId']), |
|||
...mapState('task', ['tasks']), |
|||
}, |
|||
|
|||
watch: { |
|||
visibleRoles(val) { |
|||
if (val && val.length) { |
|||
this.roles = [...this.visibleRoles]; |
|||
this.loading = false; |
|||
} |
|||
}, |
|||
}, |
|||
|
|||
mounted() { |
|||
if (!this.visibleRoles || !this.visibleRoles.length) { |
|||
this.loading = true; |
|||
} else { |
|||
this.roles = [...this.visibleRoles]; |
|||
} |
|||
}, |
|||
|
|||
methods: { |
|||
...mapActions('task', ['handleRegularTask']), |
|||
...mapMutations('role', ['setRoleId']), |
|||
...mapMutations('task', ['setPermanents', 'clearEndFlag']), |
|||
|
|||
// 设置滚动位置 |
|||
setCurrentRole(index) { |
|||
const query = uni.createSelectorQuery().in(this); |
|||
query |
|||
.selectAll('.tab-children') |
|||
.boundingClientRect(data => { |
|||
data.forEach(item => { |
|||
this.tabList.push({ |
|||
width: item.width, |
|||
left: item.left, |
|||
}); |
|||
}); |
|||
}) |
|||
.exec(); |
|||
|
|||
const system = uni.getSystemInfoSync(); // 获取系统信息 |
|||
console.log('system: ', system); |
|||
// 当前滚动的位置 |
|||
let left = 0; |
|||
let screenWidth = system.windowWidth; |
|||
for (let i = 0; i < index; i++) { |
|||
left += this.tabList[i].width + this.tabList[i].left * 2; |
|||
} |
|||
left += this.tabList[index].width; |
|||
this.scrollLeft = left - screenWidth / 2; |
|||
}, |
|||
|
|||
// 切换角色 |
|||
// 查任务这里不用管 project监听了roleId的变化 |
|||
// 时间基准点不用管 project监听了roleId 里处理了 |
|||
changeRole(id, index) { |
|||
try { |
|||
// 清除多余的script |
|||
this.clearPluginScript(); |
|||
this.$nextTick(() => { |
|||
this.setRoleId(id); |
|||
// 改变index 即手动点击切换 我在此时将当前元素赋值给左边距 实现自动滚动 |
|||
this.setCurrentRole(index); |
|||
}); |
|||
} catch (error) { |
|||
console.error('role.vue changeRole error: ', error); |
|||
} |
|||
}, |
|||
|
|||
// 清除插件script |
|||
clearPluginScript() { |
|||
try { |
|||
const scripts = document.querySelectorAll('script[data-type=plugin]'); |
|||
|
|||
for (let i = 0; i < scripts.length; i++) { |
|||
document.body.removeChild(scripts[i]); |
|||
} |
|||
} catch (error) { |
|||
console.error('clearPluginScript error: ', error); |
|||
} |
|||
}, |
|||
}, |
|||
}; |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.home-box { |
|||
// 对此盒子进行 sticky 粘性定位 |
|||
position: sticky; |
|||
top: 0; |
|||
background: #fff; // 设置背景 否则会透明 |
|||
|
|||
/* #ifdef H5 */ |
|||
//粘性定位 在h5下 加 原生头部高度 44px |
|||
top: 88rpx; |
|||
/* #endif */ |
|||
|
|||
.tab-box { |
|||
position: relative; |
|||
white-space: nowrap; |
|||
// height: 84rpx; |
|||
|
|||
.tab-item { |
|||
// width: 20%; |
|||
// height: 84rpx; |
|||
padding: 20rpx 24rpx; |
|||
position: relative; |
|||
display: inline-block; |
|||
text-align: center; |
|||
font-size: 30rpx; |
|||
transition-property: background-color, width; |
|||
} |
|||
|
|||
.default-tab-item { |
|||
color: $roleChoiceColor; |
|||
} |
|||
|
|||
.default-tab-choice { |
|||
//当前选中 基于此类名给与底部选中框 |
|||
position: relative; |
|||
color: $roleChoiceColor; |
|||
font-weight: 600; |
|||
} |
|||
|
|||
.default-tab-choice:before { |
|||
content: ''; |
|||
position: absolute; |
|||
left: 0; |
|||
bottom: -20rpx; |
|||
width: 100%; |
|||
height: 6rpx; |
|||
border-radius: 2rpx; |
|||
background: $roleChoiceColor; |
|||
} |
|||
|
|||
.tab-choice { |
|||
//当前选中 基于此类名给与底部选中框 |
|||
position: relative; |
|||
color: $uni-color-primary; |
|||
font-weight: 600; |
|||
} |
|||
|
|||
.tab-choice:before { |
|||
content: ''; |
|||
position: absolute; |
|||
left: 0; |
|||
bottom: -20rpx; |
|||
width: 100%; |
|||
height: 6rpx; |
|||
border-radius: 2rpx; |
|||
background: $uni-color-primary; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// // 删除 底部滚动条 |
|||
/* #ifndef APP-NVUE */ |
|||
::-webkit-scrollbar, |
|||
::-webkit-scrollbar, |
|||
::-webkit-scrollbar { |
|||
display: none; |
|||
width: 0 !important; |
|||
height: 0 !important; |
|||
-webkit-appearance: none; |
|||
background: transparent; |
|||
} |
|||
|
|||
/* #endif */ |
|||
/* #ifdef H5 */ |
|||
// 通过样式穿透,隐藏H5下,scroll-view下的滚动条 |
|||
scroll-view ::v-deep ::-webkit-scrollbar { |
|||
display: none; |
|||
} |
|||
|
|||
/* #endif */ |
|||
|
|||
.skeleton { |
|||
height: 44rpx; |
|||
} |
|||
</style> |
@ -1,84 +0,0 @@ |
|||
# 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 | |
|||
|
@ -1,187 +0,0 @@ |
|||
<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> |
|||
/** |
|||
* 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 |
|||
* |
|||
* */ |
|||
export default { |
|||
props: { |
|||
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' |
|||
// } |
|||
}, |
|||
computed: { |
|||
avatarClass() { |
|||
if (this.avatar == 'top') { |
|||
return ['lx-skeleton_avator__top']; |
|||
} else if (this.avatar == 'left') { |
|||
return ['lx-skeleton_avator__left']; |
|||
} else { |
|||
return ''; |
|||
} |
|||
}, |
|||
animationClass() { |
|||
return [this.animate ? 'lx-skeleton_animation' : '']; |
|||
}, |
|||
slotClass() { |
|||
return [!this.loading ? 'show' : 'hide']; |
|||
}, |
|||
avatarShapeClass() { |
|||
return [this.avatarShape == 'round' ? 'lx-skeleton_avator__round' : '']; |
|||
}, |
|||
bannerClass() { |
|||
return [this.banner ? 'lx-skeleton_banner' : '']; |
|||
}, |
|||
}, |
|||
data() { |
|||
return {}; |
|||
}, |
|||
}; |
|||
</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,119 +0,0 @@ |
|||
<template> |
|||
<!-- 时间间隔栏 --> |
|||
<!-- <Barrier /> --> |
|||
|
|||
<scroll-view |
|||
:lower-threshold="300" |
|||
scroll-y="true" |
|||
:upper-threshold="300" |
|||
:scroll-into-view="viewId" |
|||
@scroll="scroll" |
|||
@scrolltolower="handleScrollBottom" |
|||
@scrolltoupper="handleScrollTop" |
|||
id="scroll" |
|||
> |
|||
<!-- 时间轴 --> |
|||
<!-- <u-divider bg-color="#f3f4f6" class="pt-5" fontSize="14px" v-if="topEnd">到顶啦</u-divider> --> |
|||
<TimeBox /> |
|||
<!-- <u-divider bg-color="#f3f4f6" class="pb-5" fontSize="14px" v-if="bottomEnd">到底啦</u-divider> --> |
|||
</scroll-view> |
|||
</template> |
|||
|
|||
<script> |
|||
// import Barrier from './component/Barrier.vue'; |
|||
import { mapState, mapMutations, mapGetters } from 'vuex'; |
|||
import TimeBox from './component/TimeBox.vue'; |
|||
import mixin from '@/mixins/timeline'; |
|||
|
|||
export default { |
|||
name: 'TimeLine', |
|||
components: { TimeBox }, |
|||
mixins: [mixin], |
|||
|
|||
data() { |
|||
return { top: 0 }; |
|||
}, |
|||
|
|||
computed: { |
|||
...mapState('role', ['visibleRoles']), |
|||
...mapState('task', ['scrollTop', 'showTips', 'tasks', 'topEnd', 'bottomEnd', 'showSkeleton', 'timeNode', 'viewId']), |
|||
...mapGetters('task', ['timeGranularity']), |
|||
}, |
|||
|
|||
methods: { |
|||
...mapMutations('task', ['setScrollTop', 'setShrink', 'setUpTasks', 'setDownTasks', 'setViewId']), |
|||
|
|||
// 滚动 |
|||
scroll(e) { |
|||
this.top = e.detail.scrollTop; |
|||
this.setShrink(this.top > this.scrollTop); |
|||
this.setScrollTop(this.top); |
|||
}, |
|||
|
|||
// 滚动到顶部 |
|||
async handleScrollTop() { |
|||
if (!this.tasks || !this.tasks.length || this.showSkeleton) return; |
|||
const startTime = this.tasks[0].planStart - 0; |
|||
if ((this.tasks[0].plugins && this.tasks[0].plugins.length === 0) || this.topEnd) { |
|||
// 没有数据时 自动加载数据 |
|||
console.warn('滚动到顶部没有数据时: '); |
|||
const addTasks = this.setTime(startTime, true); |
|||
this.setUpTasks(addTasks); |
|||
} else { |
|||
// 有数据时 |
|||
console.warn('滚动到顶部有数据时: '); |
|||
const upQuery = { |
|||
timeNode: startTime, |
|||
queryType: 0, |
|||
queryNum: 6, |
|||
}; |
|||
await this.$emit('getTasks', upQuery); |
|||
} |
|||
}, |
|||
|
|||
// 滚动到底部 |
|||
async handleScrollBottom() { |
|||
if (!this.tasks || !this.tasks.length || this.showSkeleton) return; |
|||
const { tasks, timeGranularity } = this; |
|||
const startTime = tasks[tasks.length - 1].planStart - 0; |
|||
if ((tasks[0].plugins && tasks[0].plugins.length === 0) || this.bottomEnd) { |
|||
// 没有数据时 自动加载数据 |
|||
console.warn('滚动到底部没有数据时: '); |
|||
const addTasks = this.setTime(startTime, false); |
|||
this.setDownTasks(addTasks); |
|||
} else { |
|||
// 时间基准点=最后一个任务的开始时间+当前时间颗粒度 |
|||
console.warn('滚动到底部有数据时: '); |
|||
const timeNode = this.$t.time.add(startTime, 1, timeGranularity).valueOf(); |
|||
const downQuery = { |
|||
timeNode, |
|||
queryType: 1, |
|||
queryNum: 6, |
|||
}; |
|||
await this.$emit('getTasks', downQuery); |
|||
} |
|||
}, |
|||
|
|||
// 设置自动滚动位置 |
|||
setScrollPosition() { |
|||
const { tasks, timeNode } = this; |
|||
for (let i = 0; i < tasks.length; i++) { |
|||
const item = tasks[i]; |
|||
const show = this.$t.time.isSame(+item.planStart, +timeNode, this.timeGranularity); |
|||
// 如果storage里有timeNode,修改store里的timeNode |
|||
const taskId = this.$t.storage.getStorageSync('taskId'); |
|||
// 清空storage |
|||
if (taskId) { |
|||
this.setViewId(`a${taskId}`); |
|||
this.$t.storage.setStorageSync('taskId', ''); |
|||
return; |
|||
} |
|||
if (show) { |
|||
this.setViewId(`a${item.id}`); |
|||
return; |
|||
} |
|||
} |
|||
}, |
|||
}, |
|||
}; |
|||
</script> |
@ -1,42 +0,0 @@ |
|||
<!-- |
|||
* @Author: aBin |
|||
* @email: binbin0314@126.com |
|||
* @Date: 2021-07-19 14:22:54 |
|||
* @LastEditors: aBin |
|||
* @LastEditTime: 2021-07-20 11:46:04 |
|||
--> |
|||
<template> |
|||
<view class> |
|||
<!-- :class="{ active: cycleTasks.time.start === filter.startTime }" --> |
|||
<view class="cycle-time active"> |
|||
<!-- {{ $util.formatStartTimeToCycleTime(filter.time, cycleTasks.time.start) }} --> |
|||
2021年30周 |
|||
</view> |
|||
</view> |
|||
</template> |
|||
<script> |
|||
export default { |
|||
name: 'Barrier', |
|||
data() { |
|||
return {}; |
|||
}, |
|||
}; |
|||
</script> |
|||
<style scoped lang="scss"> |
|||
.cycle-time { |
|||
padding: 8rpx 16rpx; |
|||
margin-bottom: 16rpx; |
|||
background: #fafafc; |
|||
color: $uni-text-color; |
|||
font-size: 28rpx; |
|||
position: sticky; |
|||
top: -1px; |
|||
left: 0; |
|||
z-index: 99; |
|||
|
|||
&.active { |
|||
background: $uni-color-primary; |
|||
color: $uni-text-color-inverse; |
|||
} |
|||
} |
|||
</style> |
@ -1,11 +0,0 @@ |
|||
<template> |
|||
<view class="flex justify-between" style="min-width: 90px"> |
|||
<u-icon custom-prefix="custom-icon" name="C-bxl-redux" size="17px"></u-icon> |
|||
<u-icon custom-prefix="custom-icon" name="attachment" size="21px"></u-icon> |
|||
<u-icon custom-prefix="custom-icon" name="moneycollect" size="20px"></u-icon> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default {}; |
|||
</script> |
@ -1,142 +0,0 @@ |
|||
<template> |
|||
<view class="column"> |
|||
<!-- v-if="tasks && tasks.length" --> |
|||
<view> |
|||
<view :key="task.id" v-for="task in tasks" :id="`a${task.id}`"> |
|||
<view class="flex"> |
|||
<TimeStatus :task="task" /> |
|||
<view class="flex items-center justify-between flex-1 ml-2 task-column"> |
|||
<view v-if="task.process !== 4">{{ $moment(+task.planStart).format(startTimeFormat) }}</view> |
|||
<view v-else>{{ $moment(+task.planStart).format('D日') }}</view> |
|||
|
|||
<!-- 任务功能菜单 --> |
|||
<TaskTools v-if="task.process !== 4" /> |
|||
</view> |
|||
</view> |
|||
<view class="border-l-2 border-gray-300 plugin"> |
|||
<view class="h-3" v-if="task.process === 4"></view> |
|||
<view class="ml-3 overflow-hidden shadow-lg task-box"> |
|||
<u-card |
|||
:show-foot="false" |
|||
:show-head="false" |
|||
:style="{ height: setHeight(task.panel) }" |
|||
class="h-16" |
|||
margin="0" |
|||
v-if="showSkeleton" |
|||
> |
|||
<view slot="body"> |
|||
<view> |
|||
<skeleton :banner="false" :loading="true" :row="4" animate class="mt-2 u-line-2 skeleton"></skeleton> |
|||
</view> |
|||
</view> |
|||
</u-card> |
|||
|
|||
<u-card |
|||
@click="onClickTask(task.planStart - 0, task.id)" |
|||
:show-foot="false" |
|||
:show-head="false" |
|||
:style="{ height: setHeight(task.panel) }" |
|||
class="h-16" |
|||
margin="0" |
|||
v-if="tasks && tasks.length && task.process !== 4 && !showSkeleton" |
|||
> |
|||
<!-- 任务面板插件 --> |
|||
<view slot="body"> |
|||
<view class="p-0 u-col-between"> |
|||
<view :key="pIndex" v-for="(row, pIndex) in task.plugins"> |
|||
<view class="grid gap-2" v-if="row.length"> |
|||
<Plugin |
|||
:class="[`row-span-${plugin.row}`, `col-span-${plugin.col}`]" |
|||
:task="task" |
|||
:key="plugin.pluginTaskId" |
|||
:plugin-task-id="plugin.pluginTaskId" |
|||
:plugin-id="plugin.pluginId" |
|||
:param="plugin.param" |
|||
:style-type="styleType || 0" |
|||
v-for="plugin in row" |
|||
/> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</u-card> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
<!-- 局部弹框操作栏 --> |
|||
<Tips /> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import { mapState, mapMutations, mapGetters, mapActions } from 'vuex'; |
|||
import Skeleton from '@/components/Skeleton/Skeleton'; |
|||
import TimeStatus from './TimeStatus.vue'; |
|||
import TaskTools from './TaskTools.vue'; |
|||
|
|||
export default { |
|||
name: 'TimeBox', |
|||
components: { TimeStatus, Skeleton, TaskTools }, |
|||
|
|||
data() { |
|||
return { currentComponent: '', styleType: 0 }; |
|||
}, |
|||
|
|||
computed: { |
|||
...mapState('role', ['roleId']), |
|||
...mapState('task', ['timeUnit', 'tasks', 'taskLoading', 'topEnd', 'bottomEnd', 'showSkeleton']), |
|||
...mapGetters('task', ['startTimeFormat']), |
|||
}, |
|||
|
|||
methods: { |
|||
...mapActions('task', ['getGlobal']), |
|||
...mapMutations('task', ['setTipsContent', 'setTipsContent']), |
|||
|
|||
// 设置任务面板高度 |
|||
setHeight(panel) { |
|||
if (panel && panel.height) { |
|||
return panel.height + 'px'; |
|||
} else { |
|||
return 'auto'; |
|||
} |
|||
}, |
|||
|
|||
/** |
|||
* 点击了定期任务的面板 更新可变的日常任务 |
|||
* @param {number} planStart 任务计划开始时间 |
|||
* @param {string} taskId 任务id |
|||
*/ |
|||
onClickTask(planStart, taskId) { |
|||
const param = { roleId: this.roleId, timeNode: planStart, timeUnit: this.timeUnit }; |
|||
this.getGlobal(param); |
|||
this.$t.storage.setStorageSync('taskId', taskId); |
|||
this.$t.storage.setStorageSync('roleId', this.roleId); |
|||
}, |
|||
}, |
|||
}; |
|||
</script> |
|||
|
|||
<style scoped lang="scss"> |
|||
.task-box { |
|||
border-radius: 24rpx; |
|||
} |
|||
.column { |
|||
padding: 24px 14px; |
|||
} |
|||
.task-column { |
|||
height: 33px; |
|||
} |
|||
.plugin { |
|||
margin-top: 8px; |
|||
margin-bottom: 8px; |
|||
margin-left: 15px; |
|||
} |
|||
/deep/ .ml-2 { |
|||
margin-left: 16px; |
|||
} |
|||
|
|||
/deep/ .ml-3 { |
|||
margin-left: 20px; |
|||
} |
|||
</style> |
@ -1,211 +0,0 @@ |
|||
<template> |
|||
<view class="u-font-14"> |
|||
<view |
|||
class="flex items-center justify-center rounded-full icon-column" |
|||
:style="{ color: orderStyle.color }" |
|||
@click="changeStatus(task.process, $event)" |
|||
> |
|||
<!-- 1进行中 2暂停中 3已完成 --> |
|||
<u-circle-progress |
|||
:percent="orderStyle.persent - 0" |
|||
:active-color="orderStyle.color" |
|||
bg-color="rgba(255,255,255,0)" |
|||
border-width="4" |
|||
:width="task.process !== 4 ? 66 : 50" |
|||
v-if="task.process === 1 || task.process === 2 || task.process === 3" |
|||
> |
|||
<view class="u-progress-content"> |
|||
<view class="u-progress-dot"></view> |
|||
<view class="u-progress-info"> |
|||
<u-icon :name="orderStyle.icon" v-if="orderStyle.icon" size="15px"></u-icon> |
|||
<template v-else>{{ computeDurationText() }}</template> |
|||
</view> |
|||
</view> |
|||
</u-circle-progress> |
|||
<!-- 0未开始 4添加任务 --> |
|||
<view class="flex items-center justify-center rounded-full progress-box" v-else :class="task.process === 4 ? 'progress-box-4' : ''"> |
|||
<view class="u-progress-content"> |
|||
<view class="u-progress-dot"></view> |
|||
<view class="u-progress-info"> |
|||
<span v-if="orderStyle.icon"> |
|||
<u-icon :name="orderStyle.icon" v-if="task.process !== 4" size="15px"></u-icon> |
|||
<u-icon :name="orderStyle.icon" v-else size="15px"></u-icon> |
|||
</span> |
|||
<template v-else>{{ computeDurationText() }}</template> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import { mapState, mapMutations } from 'vuex'; |
|||
|
|||
export default { |
|||
name: 'TimeStatus', |
|||
props: { task: { type: Object, default: () => {} } }, |
|||
|
|||
data() { |
|||
return { |
|||
time: '', |
|||
start: [{ text: '确认开始任务', color: 'blue' }], |
|||
pause: [{ text: '继续' }, { text: '重新开始任务', color: 'blue' }, { text: '结束' }], |
|||
proceed: [{ text: '暂停' }, { text: '重新开始任务', color: 'blue' }, { text: '结束' }], |
|||
again: [{ text: '重新开始任务', color: 'blue' }], |
|||
timer: null, |
|||
}; |
|||
}, |
|||
|
|||
computed: { |
|||
...mapState('task', ['tip']), |
|||
status() { |
|||
return this.task ? this.task.process : 0; |
|||
}, |
|||
taskName() { |
|||
return this.task ? this.task.name : ''; |
|||
}, |
|||
taskId() { |
|||
return this.task ? this.task.id : ''; |
|||
}, |
|||
// 图标文本颜色 |
|||
// 任务状态 0未开始 1进行中 2暂停中 3已完成 |
|||
orderStyle() { |
|||
let color = '#9CA3AF'; |
|||
let icon = 'play-right-fill'; |
|||
let persent = 100; |
|||
switch (this.status) { |
|||
case 1: // 进行中 |
|||
color = '#60A5FA'; |
|||
icon = ''; |
|||
if (+this.computeCyclePersent() > 100) { |
|||
persent = 96; |
|||
} else { |
|||
persent = this.computeCyclePersent(); |
|||
} |
|||
break; |
|||
case 2: // 暂停中 |
|||
color = '#F87171'; |
|||
icon = 'pause'; |
|||
persent = 50; // TODO: 暂时这样 暂停状态没有计算剩余多少时间 |
|||
break; |
|||
case 3: // 已结束 |
|||
color = '#34D399'; |
|||
icon = 'checkmark'; |
|||
persent = 100; |
|||
break; |
|||
case 4: // 添加任务 |
|||
color = '#60A5FA'; |
|||
icon = 'plus'; |
|||
persent = 100; |
|||
break; |
|||
default: |
|||
// 未开始 |
|||
color = '#9CA3AF'; |
|||
icon = 'play-right'; |
|||
persent = 100; |
|||
break; |
|||
} |
|||
return { color, icon, persent }; |
|||
}, |
|||
}, |
|||
|
|||
methods: { |
|||
...mapMutations('task', ['setTip']), |
|||
|
|||
/** |
|||
* 点击了图标 修改任务状态 |
|||
* @param {object} event |
|||
*/ |
|||
changeStatus(process, event) { |
|||
if (process === 4) { |
|||
this.addTask(); |
|||
return; |
|||
} |
|||
// return false; |
|||
const { status, taskId, taskName, tip } = this; |
|||
tip.status = status; |
|||
tip.taskId = taskId; |
|||
tip.left = event.target.x; |
|||
tip.top = event.target.y; |
|||
tip.show = true; |
|||
tip.text = this.genetateTips(status, taskName); |
|||
|
|||
this.setTip(tip); |
|||
}, |
|||
|
|||
// 新建任务 |
|||
addTask() { |
|||
this.$t.ui.showToast('新建任务'); |
|||
}, |
|||
|
|||
// 计算圆环的弧度百分比 |
|||
computeCyclePersent() { |
|||
if (!this.task || !this.task.realStart || !this.task.planDuration) return 100; |
|||
const { realStart, planDuration } = this.task; |
|||
return (((Date.now() - +realStart) * 100) / +planDuration).toFixed(2); |
|||
}, |
|||
|
|||
/** |
|||
* 计算tip的标题内容 |
|||
*/ |
|||
genetateTips(status, content) { |
|||
switch (status) { |
|||
case 0: |
|||
return `确认开始任务"${content}"吗?`; |
|||
case 1: |
|||
return `请选择要执行的操作`; |
|||
case 2: |
|||
return `请选择要执行的操作`; |
|||
case 3: |
|||
return `是否要重新开始此任务`; |
|||
} |
|||
}, |
|||
|
|||
// 计算进行中状态剩余时间 |
|||
// 预计结束时间 = realStart(实际开始) + planDuration(计划时长) |
|||
// 剩余时间 = 预计结束时间 - 当前时间 |
|||
// 剩余时间 = realStart + planDuration - Date.now() |
|||
computeDurationText() { |
|||
if (this.timer) { |
|||
clearTimeout(this.timer); |
|||
this.timer = null; |
|||
} |
|||
const { realStart, planDuration } = this.task; |
|||
const leftTime = +realStart + +planDuration - Date.now(); // 剩余时间 |
|||
const { num, time } = this.$t.time.computeDurationText(leftTime); |
|||
this.$nextTick(() => { |
|||
if (!time) return; |
|||
this.timer = setTimeout(() => { |
|||
this.computeDurationText(); |
|||
}, time); |
|||
}); |
|||
return num; |
|||
}, |
|||
}, |
|||
}; |
|||
</script> |
|||
|
|||
<style scoped lang="scss"> |
|||
.icon-column { |
|||
height: 33px; |
|||
width: 33px; |
|||
} |
|||
.one { |
|||
height: 33px; |
|||
width: 33px; |
|||
} |
|||
|
|||
.progress-box { |
|||
background: rgba(255, 255, 255, 0); |
|||
width: 33px; |
|||
height: 33px; |
|||
border: 2px solid #9ca3af; |
|||
} |
|||
|
|||
.progress-box-4 { |
|||
width: 25px; |
|||
height: 25px; |
|||
border: 2px solid #60a5fa; |
|||
} |
|||
</style> |
@ -1,7 +0,0 @@ |
|||
<!-- |
|||
* @Author: aBin |
|||
* @email: binbin0314@126.com |
|||
* @Date: 2021-07-19 15:40:02 |
|||
* @LastEditors: aBin |
|||
* @LastEditTime: 2021-07-19 15:40:03 |
|||
--> |
@ -1,95 +0,0 @@ |
|||
<template> |
|||
<view |
|||
class="fixed shadow-2xl" |
|||
style="z-index: 1000" |
|||
:style="{ |
|||
left: tip.left + 'px', |
|||
top: height - tip.top > 110 ? tip.top + 'px' : '', |
|||
bottom: 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="headStyle" |
|||
:footStyle="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> |
|||
import { mapState, mapMutations } from 'vuex'; |
|||
|
|||
export default { |
|||
name: 'Tips', |
|||
props: { title: { default: '提示', type: String } }, |
|||
|
|||
computed: mapState('task', ['tip']), |
|||
|
|||
data() { |
|||
return { |
|||
footStyle: { padding: '4px 15px' }, |
|||
headStyle: { paddingTop: '8px', paddingBottom: '8px' }, |
|||
height: 0, |
|||
}; |
|||
}, |
|||
|
|||
mounted() { |
|||
const system = uni.getSystemInfoSync(); |
|||
this.height = system.windowHeight; |
|||
}, |
|||
|
|||
methods: { |
|||
...mapMutations('task', ['setTipShow']), |
|||
// 点击了确认 |
|||
onConfirm() { |
|||
this.onCancel(); |
|||
}, |
|||
|
|||
/** |
|||
* 执行修改任务状态的动作 |
|||
* @param {number} type 状态码 0开始 1暂停 2继续 3完成 默认0 |
|||
*/ |
|||
async onChangeStatus(type) { |
|||
try { |
|||
const param = { id: this.tip.taskId, type }; |
|||
await uni.$u.api.updateTaskType(param); |
|||
if (type === 0) { |
|||
this.$t.ui.showToast('项目已重新开始'); |
|||
} else if (type === 1) { |
|||
this.$t.ui.showToast('项目已暂停'); |
|||
} else if (type === 2) { |
|||
this.$t.ui.showToast('项目继续'); |
|||
} else if (type === 3) { |
|||
this.$t.ui.showToast('项目结束'); |
|||
} |
|||
this.tip.show = false; |
|||
// TODO: 更新界面 不要整体刷新 |
|||
// location.reload(); |
|||
// this.$router.go(0); |
|||
} catch (error) { |
|||
console.error(error); |
|||
this.$t.ui.showToast(error.msg || '操作失败'); |
|||
} |
|||
}, |
|||
|
|||
// 点击了取消 |
|||
onCancel() { |
|||
this.setTipShow(false); |
|||
}, |
|||
}, |
|||
}; |
|||
</script> |
@ -1,65 +0,0 @@ |
|||
<template> |
|||
<view> |
|||
<u-navbar :custom-back="onBack" class="overflow-hidden"> |
|||
<view class="flex justify-start flex-1 px-3 font-bold min-0"> |
|||
<view class="truncate">{{ project.name }}</view> |
|||
</view> |
|||
<view class="mr-2" slot="right"> |
|||
<u-icon class="m-1" name="xuanzhong2" custom-prefix="custom-icon" size="20px" @click="lwbs"></u-icon> |
|||
<u-icon class="m-1" name="shuaxin1" custom-prefix="custom-icon" size="20px" @click="projectOverview"></u-icon> |
|||
<u-icon class="m-1" name="home" custom-prefix="custom-icon" size="20px" @click="openIndex"></u-icon> |
|||
<u-icon class="m-1" name="xuanxiang" custom-prefix="custom-icon" size="20px" @click="operation"></u-icon> |
|||
</view> |
|||
</u-navbar> |
|||
</view> |
|||
</template> |
|||
<script> |
|||
import { mapGetters, mapState } from 'vuex'; |
|||
|
|||
export default { |
|||
name: 'ProjectTitle', |
|||
data() { |
|||
return { showBack: false }; |
|||
}, |
|||
computed: { |
|||
...mapState('project', ['project']), |
|||
...mapGetters('user', ['userId']), |
|||
}, |
|||
|
|||
methods: { |
|||
// 点击返回按钮 |
|||
onBack() { |
|||
// eslint-disable-next-line no-undef |
|||
const pages = getCurrentPages(); // 获取页面栈数组 |
|||
if (pages.length > 1) { |
|||
uni.navigateBack(); |
|||
} else { |
|||
this.$u.route('/', { u: this.userId }); |
|||
} |
|||
}, |
|||
|
|||
// LWBS提示 |
|||
lwbs() { |
|||
// this.$t.ui.showToast('LWBS'); |
|||
}, |
|||
//项目概览 |
|||
projectOverview() { |
|||
// this.$t.ui.showToast('项目概览'); |
|||
}, |
|||
// 回到首页 |
|||
openIndex() { |
|||
this.$u.route('/', { u: this.userId }); |
|||
}, |
|||
//操作 |
|||
operation() { |
|||
// this.$t.ui.showToast('操作'); |
|||
}, |
|||
}, |
|||
}; |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
/deep/ .u-slot-content { |
|||
min-width: 0; |
|||
} |
|||
</style> |
@ -1,3 +0,0 @@ |
|||
export const db = null; // indexedDB 对象
|
|||
export const name = 'TALL_indexedDB'; // indexDB name
|
|||
export const version = 1; // indexDB version
|
@ -1,97 +0,0 @@ |
|||
// 定义插件相关信息
|
|||
/* eslint-disable */ |
|||
export default { |
|||
defaults: [ |
|||
{ |
|||
id: 1, |
|||
name: 'TASK_NAME', |
|||
description: '任务名插件', |
|||
component: 'p-task-title', |
|||
}, |
|||
{ |
|||
id: 2, |
|||
name: 'TASK_DESCRIPTION', |
|||
description: '任务描述插件', |
|||
component: 'p-task-description', |
|||
}, |
|||
{ |
|||
id: 3, |
|||
name: 'TASK_DURATION_DELAY', |
|||
description: '任务时长延迟插件(+-1min)时间格式可设置', |
|||
component: 'p-task-duration-delay', |
|||
}, |
|||
{ |
|||
id: 4, |
|||
name: 'TASK_START_TIME_DELAY', |
|||
description: '任务开始时间延迟插件(+-1hour)', |
|||
component: 'p-task-start-time-delay', |
|||
}, |
|||
{ |
|||
id: 5, |
|||
name: 'DELIVERABLE', |
|||
description: '交付物插件(人 + 交付物)可配置【仅人】 or 【仅交付物】 or 【人+交付物】', |
|||
component: 'p-deliverable', |
|||
}, |
|||
{ |
|||
id: 6, |
|||
name: 'SUBTASKS', |
|||
description: '子任务插件:显示子任务', |
|||
component: 'p-subtasks', |
|||
}, |
|||
{ |
|||
id: 7, |
|||
name: 'SUB_PROJECT', |
|||
description: '子项目插件:显示子项目', |
|||
component: 'p-sub-project', |
|||
}, |
|||
{ |
|||
id: 8, |
|||
name: 'TASK_COUNTDOWN', |
|||
description: '任务倒计时插件', |
|||
component: 'p-task-countdown', |
|||
}, |
|||
{ |
|||
id: 9, |
|||
name: 'MANAGE_PROJECT', |
|||
description: '项目信息管理插件', |
|||
component: 'p-manage-project', |
|||
}, |
|||
|
|||
{ |
|||
id: 10, |
|||
name: 'MANAGE_ROLE', |
|||
description: '角色信息管理插件', |
|||
component: 'p-manage-role', |
|||
}, |
|||
{ |
|||
id: 11, |
|||
name: 'MANAGE_MEMBER', |
|||
description: '成员信息管理插件', |
|||
component: 'p-manage-member', |
|||
}, |
|||
{ |
|||
id: 12, |
|||
name: 'MANAGE_TASK', |
|||
description: '任务信息管理插件', |
|||
component: 'p-manage-task', |
|||
}, |
|||
{ |
|||
id: 13, |
|||
name: 'WBS_IMPORT', |
|||
description: '导入WBS新建项目', |
|||
component: 'p-wbs-import', |
|||
}, |
|||
{ |
|||
id: 14, |
|||
name: 'WBS_IMPORT_UPDATE', |
|||
description: '导入WBS更新项目', |
|||
component: 'p-wbs-update', |
|||
}, |
|||
{ |
|||
id: 15, |
|||
name: 'DELIVER_CHECK', |
|||
description: '交付物检查', |
|||
component: 'p-deliver-check', |
|||
}, |
|||
], // 默认插件id列表
|
|||
}; |
@ -1,17 +0,0 @@ |
|||
export default { |
|||
timeUnits: [ |
|||
// 时间颗粒度
|
|||
{ id: 0, value: '毫秒', format: 'x', cycle: 'YY-M-D HH:mm:ss', granularity: 'millisecond' }, |
|||
{ id: 1, value: '秒', format: 'x', cycle: 'YY-M-D HH:mm:ss', granularity: 'second' }, |
|||
{ id: 2, value: '分', format: 'ss', cycle: 'YY-M-D HH:mm', granularity: 'minute' }, |
|||
{ id: 3, value: '时', format: 'mm', cycle: 'YY-M-D HH时', granularity: 'hour' }, |
|||
{ id: 4, value: '天', format: 'D日 HH:mm', cycle: 'YY-M-D', granularity: 'day' }, |
|||
{ id: 5, value: '周', format: 'D日 HH:mm', cycle: '', granularity: 'week' }, |
|||
{ id: 6, value: '月', format: 'D日 H:m', cycle: 'YYYY年', granularity: 'month' }, |
|||
{ id: 7, value: '季度', format: '', cycle: 'YYYY年', granularity: 'quarter' }, |
|||
{ id: 8, value: '年', format: 'YYYY', cycle: '', granularity: 'year' }, |
|||
{ id: 9, value: '年代', format: '', cycle: '', granularity: '' }, |
|||
{ id: 10, value: '世纪', format: '', cycle: '', granularity: '' }, |
|||
{ id: 11, value: '千年', format: '', cycle: '', granularity: '' }, |
|||
], |
|||
}; |
@ -1,44 +0,0 @@ |
|||
import { mapGetters } from 'vuex'; |
|||
|
|||
const mixin = { |
|||
computed: mapGetters('task', ['timeGranularity']), |
|||
|
|||
methods: { |
|||
/** |
|||
* 设置时间轴空数据 |
|||
* @param {*} startTime |
|||
* @param {*} show true 向上加载,false 向下加载 |
|||
*/ |
|||
setTime(startTime, show) { |
|||
let { timeGranularity } = this; |
|||
let arr = []; |
|||
let str = {}; |
|||
if (show) { |
|||
for (let i = 10; i > 0; i--) { |
|||
str = { |
|||
id: this.$u.guid(20, false, 10), |
|||
panel: {}, |
|||
plugins: [], |
|||
process: 4, |
|||
planStart: this.$t.time.add(startTime, `-${i}` - 0, timeGranularity).valueOf(), |
|||
}; |
|||
arr.push(str); |
|||
} |
|||
} else { |
|||
for (let i = 0; i < 10; i++) { |
|||
str = { |
|||
id: this.$u.guid(20, false, 10), |
|||
panel: {}, |
|||
plugins: [], |
|||
process: 4, |
|||
planStart: this.$t.time.add(startTime, i + 1, timeGranularity).valueOf(), |
|||
}; |
|||
arr.push(str); |
|||
} |
|||
} |
|||
return arr; |
|||
}, |
|||
}, |
|||
}; |
|||
|
|||
export default mixin; |
@ -1,11 +1,34 @@ |
|||
<template> |
|||
<web-view src="" /> |
|||
<web-view :src="src" /> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
data() { |
|||
return { src: '' }; |
|||
}, |
|||
|
|||
onLoad(options) { |
|||
console.log('options: ', options); |
|||
if (!options) { |
|||
this.$t.ui.showModal('缺少参数, 请返回重试'); |
|||
} else { |
|||
const { p, u, pname, url } = options; |
|||
if (pname) { |
|||
uni.setNavigationBarTitle({ title: pname }); |
|||
} |
|||
if (!u) { |
|||
this.$t.ui.showToast('缺少用户信息, 请登录'); |
|||
return; |
|||
} |
|||
|
|||
if (!p || !url) { |
|||
this.$t.ui.showToast('缺少项目信息, 请重新打开'); |
|||
return; |
|||
} |
|||
|
|||
this.src = `https://www.tall.wiki/tall/v3.0.1/#/pages/project/project?u=${u}&p=${p}&url=${url}&pname=${pname}`; |
|||
} |
|||
}, |
|||
}; |
|||
</script> |
|||
|
@ -1,376 +0,0 @@ |
|||
<template> |
|||
<view :style="{ height: height }" class="flex flex-col overflow-hidden u-font-14"> |
|||
<!-- 标题栏 --> |
|||
<Title /> |
|||
|
|||
<view class="container flex flex-col flex-1 overflow-hidden bg-gray-100" style="margin: auto"> |
|||
<!-- 角色栏 --> |
|||
<Roles /> |
|||
|
|||
<!-- 日常任务面板 --> |
|||
<Globals /> |
|||
|
|||
<!-- 定期任务面板 --> |
|||
<TimeLine @getTasks="getTasks" class="flex-1 overflow-hidden" ref="child" /> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import { mapState, mapGetters, mapMutations, mapActions } from 'vuex'; |
|||
import mixin from '@/mixins/timeline'; |
|||
|
|||
export default { |
|||
mixins: [mixin], |
|||
data() { |
|||
return { height: '', show: false }; |
|||
}, |
|||
|
|||
computed: { |
|||
...mapState('user', ['user', 'token']), |
|||
...mapState('role', ['visibleRoles', 'roleId']), |
|||
...mapState('task', ['timeNode', 'timeUnit', 'tasks', 'regularTask']), |
|||
...mapState('project', ['project']), |
|||
...mapGetters('task', ['timeGranularity']), |
|||
...mapGetters('project', ['projectId']), |
|||
}, |
|||
|
|||
onLoad(options) { |
|||
this.init(options); |
|||
}, |
|||
|
|||
watch: { |
|||
/** |
|||
* 当时间基准点发生变化时 |
|||
* 重新根据时间和角色查询普通日常任务 |
|||
* 永久日常任务不发生改变 |
|||
*/ |
|||
timeNode(val) { |
|||
if (val && this.roleId) { |
|||
// 根据时间和角色查找日常任务 |
|||
this.initTasks(); |
|||
} |
|||
}, |
|||
|
|||
/** |
|||
* 当角色发生变化时 |
|||
* 重新查询永久日常任务和普通日常任务 |
|||
* 注意: 切换角色后 重新设置了时间基准点 时间基准点一定会变 |
|||
* 所以监听时间基准点获取 可变日常任务即可 这里不用获取 避免重复获取 |
|||
*/ |
|||
roleId(val) { |
|||
if (val) { |
|||
this.setTimeNode(Date.now()); |
|||
// 根据角色查找永久的日常任务 |
|||
const params = { roleId: val, projectId: this.projectId }; |
|||
this.getPermanent(params); |
|||
} |
|||
}, |
|||
}, |
|||
|
|||
mounted() { |
|||
const system = uni.getSystemInfoSync(); |
|||
this.height = system.windowHeight + 'px'; |
|||
}, |
|||
|
|||
onUnload() { |
|||
this.clearEndFlag(); |
|||
this.clearTasks(); |
|||
this.setRoleId(); |
|||
}, |
|||
|
|||
methods: { |
|||
...mapActions('user', ['getToken']), |
|||
...mapActions('task', ['getRegulars', 'getPermanent', 'getGlobal']), |
|||
...mapMutations('user', ['setToken']), |
|||
...mapMutations('project', ['setProject', 'setProjectName']), |
|||
...mapMutations('role', ['setInvisibleRoles', 'setVisibleRoles', 'setRoleId']), |
|||
...mapMutations('task', [ |
|||
'setPermanents', |
|||
'setUpTasks', |
|||
'setDownTasks', |
|||
'setDailyTasks', |
|||
'setTimeNode', |
|||
'clearTasks', |
|||
'clearEndFlag', |
|||
'setShowSkeleton', |
|||
'setTopEnd', |
|||
'setBottomEnd', |
|||
]), |
|||
|
|||
/** |
|||
* 初始化 |
|||
* @param {object | null} options |
|||
*/ |
|||
init(options) { |
|||
if (!this.token) { |
|||
// 不论有没有token都直接从userId获取token |
|||
// token有过期时间 从本地获取可能是过期 干脆直接从userId获取 |
|||
if (!options || !options.u) { |
|||
// 参数里没有u (userId)提示 |
|||
this.$t.ui.showToast('缺少用户信息参数'); |
|||
} else { |
|||
this.getToken(options.u); |
|||
} |
|||
} |
|||
|
|||
// 参数里有项目名称 就设置标题里的项目名称 |
|||
options && options.pname && this.setProjectName(options.pname); |
|||
|
|||
if (!options || !options.p) { |
|||
// 没有项目id参数 |
|||
this.$t.ui.showToast('缺少项目信息参数'); |
|||
} else { |
|||
// 根据项目id获取项目信息 |
|||
this.getProjectById({ projectId: options.p }); |
|||
} |
|||
}, |
|||
|
|||
/** |
|||
* 通过项目id获取项目信息 |
|||
* @param {string} projectId |
|||
* @param {object} params 提交的参数 |
|||
*/ |
|||
async getProjectById(params) { |
|||
try { |
|||
const data = await uni.$u.api.findProjectById(params); |
|||
this.setProject(data); |
|||
// 根据项目id获取角色列表 |
|||
this.getRoles(params); |
|||
} catch (error) { |
|||
console.log('error: ', error || '获取项目信息失败'); |
|||
} |
|||
}, |
|||
|
|||
/** |
|||
* 通过项目id获取角色信息 |
|||
* @param {string} projectId |
|||
* @param {object} params 提交的参数 |
|||
*/ |
|||
getRoles(params) { |
|||
this.$t.$q.findShowRole(params, (err, data) => { |
|||
if (err) { |
|||
console.error('err: ', err || '获取角色信息失败'); |
|||
} else { |
|||
this.setInvisibleRoles(data ? data.invisibleList : []); |
|||
this.setVisibleRoles(data ? data.visibleList : []); |
|||
this.setInitialRoleId(data ? data.visibleList : []); |
|||
} |
|||
}); |
|||
}, |
|||
|
|||
// 初始化 时间轴 |
|||
async initTasks() { |
|||
// 清空日常任务的数据 |
|||
this.setPermanents([]); |
|||
this.setDailyTasks([]); |
|||
// 清空定期任务数据 |
|||
this.clearTasks(); |
|||
// 到顶的标志复位 |
|||
// 到底的标志复位 |
|||
this.clearEndFlag(); |
|||
// 根据时间和角色查找日常任务 |
|||
this.getGlobalData(); |
|||
// 向上加载空数据 |
|||
this.setPrevTasks(); |
|||
// storage没有存储值就跳转到当前时间位置 |
|||
const storageTaskId = this.$t.storage.getStorageSync('taskId'); |
|||
this.$nextTick(() => { |
|||
if (!storageTaskId) { |
|||
this.$refs.child.setScrollPosition(); |
|||
} |
|||
}); |
|||
// 向下加载空数据 |
|||
this.setNextTasks(); |
|||
|
|||
await this.getInitTasks(); |
|||
// 根据项目id获取角色列表 |
|||
// 从详情页返回时跳转到之前的位置 storage有存储值 |
|||
this.$nextTick(() => { |
|||
if (storageTaskId) { |
|||
this.$refs.child.setScrollPosition(); |
|||
} |
|||
}); |
|||
}, |
|||
|
|||
// 切换了 颗粒度 || 角色时候 获取初始定期任务 |
|||
getInitTasks() { |
|||
// 预加载 上下的定期任务 |
|||
function fn(that) { |
|||
const detailId = that.tasks.findIndex(task => task.detailId); |
|||
console.log('detailId: ', detailId); |
|||
if (detailId !== -1) { |
|||
that.$nextTick(() => { |
|||
// 向上拿数据 |
|||
const { tasks, timeGranularity } = that; |
|||
that.getTasks({ timeNode: +tasks[0].planStart, queryType: 0, queryNum: 6 }); |
|||
// 向下拿数据 |
|||
const nextQueryTime = +that.$t.time.add(+tasks[tasks.length - 1].planStart, 1, timeGranularity); |
|||
that.getTasks({ timeNode: nextQueryTime, queryType: 1, queryNum: 6 }); |
|||
}); |
|||
} else { |
|||
// 没有任务 上下显示时间刻度 |
|||
// 向上加载 |
|||
// that.setPrevTasks(); |
|||
// // 向下加载 |
|||
// that.setNextTasks(); |
|||
} |
|||
} |
|||
// 根据时间基准点和角色查找定期任务 |
|||
this.getTasks({ queryType: 0 }); |
|||
|
|||
// 根据项目id获取角色列表 |
|||
this.getTasks({ queryType: 1 }, fn); |
|||
}, |
|||
|
|||
// 设置 初始显示角色信息 |
|||
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 = this.$t.storage.getStorageSync('roleId'); |
|||
const currentRoleId = storageRoleId ? storageRoleId : currentRole ? currentRole.id : ''; |
|||
this.setRoleId(currentRoleId); |
|||
// 清空storage |
|||
this.$t.storage.setStorageSync('roleId', ''); |
|||
}, |
|||
|
|||
/** |
|||
* 根据时间基准点和角色查找定期任务 |
|||
* @param {object} query |
|||
* @param {string} query.roleId 角色id |
|||
* @param {string} query.timeNode 时间基准点 默认当前 |
|||
* @param {string} query.timeUnit 时间颗粒度 默认天 |
|||
* @param {string} query.queryNum 查找颗粒度数量 默认3个 |
|||
* @param {string} query.queryType 0向上查找 1向下查找(默认) 下查包含自己,上查不包含 |
|||
*/ |
|||
getTasks(query, fn) { |
|||
const that = this; |
|||
that.setShowSkeleton(true); |
|||
const { roleId, timeNode, timeUnit, projectId } = that; |
|||
const params = { |
|||
roleId, |
|||
timeNode: query.timeNode || timeNode, |
|||
timeUnit: query.timeUnit || timeUnit, |
|||
queryNum: query.queryNum || 3, |
|||
queryType: query.queryType, |
|||
projectId, |
|||
}; |
|||
|
|||
that.$t.$q.getRegularTask(params, function (err, data) { |
|||
console.log('data: ', data); |
|||
console.log('that.show: ', that.show); |
|||
if (!that.show) { |
|||
if (err) { |
|||
that.setShowSkeleton(false); |
|||
console.error('err: ', err); |
|||
} else { |
|||
that.show = true; |
|||
that.setShowSkeleton(false); |
|||
// 0 -> 向上 1 -> 向下 |
|||
if (data && data.length) { |
|||
that.replacePrevData(data, params.queryType); |
|||
that.show = false; |
|||
} else { |
|||
params.queryType === 0 ? that.setPrevTasks() : that.setNextTasks(); |
|||
that.show = false; |
|||
} |
|||
if (that.tasks.length && fn) { |
|||
fn(that); |
|||
} |
|||
} |
|||
} |
|||
}); |
|||
}, |
|||
|
|||
// 获取可变全局任务 |
|||
getGlobalData() { |
|||
const { roleId, timeNode, timeUnit, projectId } = this; |
|||
const param = { roleId, timeNode, timeUnit, projectId }; |
|||
this.getGlobal(param); |
|||
}, |
|||
|
|||
// 设置时间轴向上的空数据 |
|||
setPrevTasks() { |
|||
this.setTopEnd(true); |
|||
let sTime = ''; |
|||
if (!this.tasks || !this.tasks.length) { |
|||
sTime = +new Date().getTime(); |
|||
} else { |
|||
sTime = +this.tasks[0].planStart - 0; |
|||
} |
|||
const initData = this.setTime(sTime, true); |
|||
this.setUpTasks(initData); |
|||
}, |
|||
|
|||
// 设置时间轴向下的空数据 |
|||
setNextTasks() { |
|||
this.setBottomEnd(true); |
|||
let sTime = ''; |
|||
if (!this.tasks || !this.tasks.length) { |
|||
sTime = +new Date().getTime(); |
|||
} else { |
|||
sTime = +this.tasks[this.tasks.length - 1].planStart - 0; |
|||
} |
|||
const initData = this.setTime(sTime, false); |
|||
this.setDownTasks(initData); |
|||
}, |
|||
|
|||
// 筛选相同的时间替换数据 |
|||
replacePrevData(data, type) { |
|||
let newTasks = [...this.tasks]; |
|||
let newDate = this.$u.deepClone(data); |
|||
|
|||
console.log('newDate: ', newDate); |
|||
|
|||
if (newDate && newDate.length) { |
|||
for (let i = 0; i < newTasks.length; i++) { |
|||
// const task = newTasks[i]; |
|||
let arr = []; |
|||
for (let j = 0; j < newDate.length; j++) { |
|||
const item = newDate[j]; |
|||
if (newTasks[i].id === item.id) { |
|||
console.log('j', j, i); |
|||
newTasks[i] = item; |
|||
break; |
|||
} |
|||
// 查找有没有超出时间刻度的时间 |
|||
if (+newTasks[0].planStart > +newDate[0].planStart) { |
|||
this.setPrevTasks(); |
|||
newTasks = [...this.tasks]; |
|||
i--; |
|||
break; |
|||
} else if (+newDate[newDate.length - 1].planStart > +newTasks[newTasks.length - 1].planStart) { |
|||
this.setNextTasks(); |
|||
newTasks = [...this.tasks]; |
|||
i--; |
|||
break; |
|||
} else { |
|||
// 筛选相同的时间替换数据 |
|||
const taskItem = this.$t.time.isSame(+newTasks[i].planStart, +item.planStart, this.timeGranularity); |
|||
if (taskItem) { |
|||
arr.push(item); |
|||
if (newTasks[i].detailId) { |
|||
newTasks.splice(i, 0, item); |
|||
} else { |
|||
if (arr.length === 1) { |
|||
newTasks.splice(i, 1, item); |
|||
} |
|||
if (arr.length > 1) { |
|||
newTasks.splice(i, 0, item); |
|||
} |
|||
} |
|||
i++; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
this.clearTasks(); |
|||
type === 0 ? this.setUpTasks(newTasks) : this.setDownTasks(newTasks); |
|||
console.log('newTasks: ', newTasks.length); |
|||
console.log('end'); |
|||
}, |
|||
}, |
|||
}; |
|||
</script> |
@ -1,55 +0,0 @@ |
|||
<template> |
|||
<view class="container p-3"> |
|||
<u-button type="primary" class="mb-3" @click="add">add</u-button> |
|||
<u-button type="primary" class="mb-3" @click="findOne">findOne</u-button> |
|||
<u-button type="primary" class="mb-3" @click="find">find</u-button> |
|||
<u-button type="primary" class="mb-3" @click="update">更新update</u-button> |
|||
<u-button type="primary" class="mb-3" @click="remove">删除remove</u-button> |
|||
<u-button type="primary" class="mb-3" @click="createIndex">创建索引和查询</u-button> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
created() { |
|||
// this.$t.$q.getProjects(this.$moment().startOf('day').valueOf(), this.$moment().endOf('day').valueOf(), (err, data) => { |
|||
// if (err) { |
|||
// console.error(err); |
|||
// } else { |
|||
// console.log('data: ' + data); |
|||
// } |
|||
// }); |
|||
}, |
|||
|
|||
methods: { |
|||
add() { |
|||
this.$db.create('projects', { id: '124', sex: ' man' }); |
|||
}, |
|||
|
|||
async findOne() { |
|||
const data = await this.$db.findOne('projects', '124'); |
|||
console.log(data); |
|||
}, |
|||
|
|||
async find() { |
|||
const data = await this.$db.find('projects'); |
|||
console.log(data); |
|||
}, |
|||
|
|||
async update() { |
|||
const data = await this.$db.update('projects', { id: '125', sex: 'woman' }); |
|||
console.log('update data: ', data); |
|||
}, |
|||
|
|||
async remove() { |
|||
const data = await this.$db.remove('projects', '123'); |
|||
console.log('update data: ', data); |
|||
}, |
|||
|
|||
async createIndex() { |
|||
// const data = await this.$db.createIndex('projects', 'sex', 'woman'); |
|||
console.log('update data: '); |
|||
}, |
|||
}, |
|||
}; |
|||
</script> |
@ -1,7 +0,0 @@ |
|||
<template> |
|||
<view>交付物检查</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default {}; |
|||
</script> |
@ -1,20 +0,0 @@ |
|||
<template> |
|||
<!-- 交付物 --> |
|||
<view>交付物</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
name: 'p-deliverable', |
|||
props: { item: { type: Object, default: null } }, |
|||
data() { |
|||
return {}; |
|||
}, |
|||
|
|||
computed: {}, |
|||
methods: {}, |
|||
watch: {}, |
|||
}; |
|||
</script> |
|||
|
|||
<style></style> |
@ -1,7 +0,0 @@ |
|||
<template> |
|||
<view>成员管理</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default {}; |
|||
</script> |
@ -1,7 +0,0 @@ |
|||
<template> |
|||
<view>项目管理</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default {}; |
|||
</script> |
@ -1,7 +0,0 @@ |
|||
<template> |
|||
<view>角色管理</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default {}; |
|||
</script> |
@ -1,7 +0,0 @@ |
|||
<template> |
|||
<view>任务管理</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default {}; |
|||
</script> |
@ -1,60 +0,0 @@ |
|||
<template> |
|||
<!-- 子项目插件 --> |
|||
<view> |
|||
<view v-for="item in sonProject" :key="item.detailId"> |
|||
<span class="text-xs text-blue-500" @click="openProject(item)">{{ item.name }}</span> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import { mapGetters } from 'vuex'; |
|||
|
|||
export default { |
|||
name: 'p-subproject', |
|||
props: { |
|||
task: { |
|||
type: Object, |
|||
default: () => {}, |
|||
}, |
|||
}, |
|||
data() { |
|||
return { sonProject: [] }; |
|||
}, |
|||
|
|||
computed: mapGetters('project', ['projectId']), |
|||
|
|||
mounted() { |
|||
this.getSonProject(); |
|||
}, |
|||
|
|||
methods: { |
|||
async getSonProject() { |
|||
try { |
|||
const data = await this.$u.api.findSonProject({ projectId: this.task.detailId }); |
|||
this.sonProject = data; |
|||
} catch (error) { |
|||
console.error('p-subproject.vue getSonProject error: ', error); |
|||
} |
|||
}, |
|||
|
|||
/** |
|||
* 打开项目 |
|||
* @param {object} project 所点击的项目的信息 |
|||
*/ |
|||
openProject(project) { |
|||
const { name, id, url } = project; |
|||
url && (uni.$t.domain = url); |
|||
this.$u.route('pages/project/project', { |
|||
u: this.userId, |
|||
p: id, |
|||
pname: name, |
|||
url: encodeURIComponent(url), |
|||
}); |
|||
}, |
|||
}, |
|||
watch: {}, |
|||
}; |
|||
</script> |
|||
|
|||
<style></style> |
@ -1,39 +0,0 @@ |
|||
<template> |
|||
<view> |
|||
<view v-for="item in sonTask" :key="item.detailId"> |
|||
<span class="text-xs text-gray-500">{{ item.name }}</span> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
name: 'p-subtasks', |
|||
props: { |
|||
task: { |
|||
type: Object, |
|||
default: () => {}, |
|||
}, |
|||
}, |
|||
data() { |
|||
return { sonTask: [] }; |
|||
}, |
|||
|
|||
created() { |
|||
this.getSonTask(); |
|||
}, |
|||
|
|||
methods: { |
|||
async getSonTask() { |
|||
try { |
|||
const data = await this.$u.api.findSonTask({ detailId: this.task.detailId }); |
|||
this.sonTask = data; |
|||
} catch (error) { |
|||
console.error('p-subtasks.vue getSonTask error: ', error); |
|||
} |
|||
}, |
|||
}, |
|||
}; |
|||
</script> |
|||
|
|||
<style></style> |
@ -1,20 +0,0 @@ |
|||
<template> |
|||
<!-- 任务倒计时插件 --> |
|||
<view>任务倒计时插件</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
name: 'p-task-countdown', |
|||
props: { item: { type: Object, default: null } }, |
|||
data() { |
|||
return {}; |
|||
}, |
|||
|
|||
computed: {}, |
|||
methods: {}, |
|||
watch: {}, |
|||
}; |
|||
</script> |
|||
|
|||
<style></style> |
@ -1,16 +0,0 @@ |
|||
<template> |
|||
<!-- 任务描述 --> |
|||
<view>{{ task.description }}</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
name: 'p-task-description', |
|||
props: { |
|||
task: { |
|||
type: Object, |
|||
default: () => {}, |
|||
}, |
|||
}, |
|||
}; |
|||
</script> |
@ -1,34 +0,0 @@ |
|||
<template> |
|||
<view v-if="realDuration && planDuration"> |
|||
<!-- 任务时长延迟插件 --> |
|||
<!-- 超时 --> |
|||
<span class="font-bold text-green-500" v-if="realDuration - 0 > planDuration - 0"> |
|||
+{{ $t.time.formatDuration(realDuration - planDuration) }} |
|||
</span> |
|||
<!-- 延时 --> |
|||
<span class="font-bold text-red-500" v-if="realDuration - 0 < planDuration - 0"> |
|||
-{{ $t.time.formatDuration(planDuration - realDuration) }} |
|||
</span> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
name: 'p-task-duration-delay', |
|||
props: { |
|||
task: { |
|||
type: Object, |
|||
default: () => {}, |
|||
}, |
|||
}, |
|||
|
|||
computed: { |
|||
realDuration() { |
|||
return this.task.realDuration; |
|||
}, |
|||
planDuration() { |
|||
return this.task.planDuration; |
|||
}, |
|||
}, |
|||
}; |
|||
</script> |
@ -1,22 +0,0 @@ |
|||
<template> |
|||
<view v-if="realStart && planStart"> |
|||
<!-- 任务开始时间延迟插件 --> |
|||
<!-- 超时 --> |
|||
<span>{{ $t.time.formatDuration(+realStart - +planStart) }}</span> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
name: 'p-task-start-time-delay', |
|||
props: { task: { type: Object, default: () => {} } }, |
|||
computed: { |
|||
realStart() { |
|||
return this.task.realStart; |
|||
}, |
|||
planStart() { |
|||
return this.task.planStart; |
|||
}, |
|||
}, |
|||
}; |
|||
</script> |
@ -1,16 +0,0 @@ |
|||
<template> |
|||
<!-- 任务名插件 --> |
|||
<view>{{ task.name }}</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
name: 'p-task-title', |
|||
props: { |
|||
task: { |
|||
type: Object, |
|||
default: () => {}, |
|||
}, |
|||
}, |
|||
}; |
|||
</script> |
@ -1,85 +0,0 @@ |
|||
<template> |
|||
<view> |
|||
<view @click="handleUpload" v-if="task.name === '导入WBS新建项目'">{{ task.name }}</view> |
|||
<view @click="handleUpdate" v-if="task.name === '导入WBS更新项目'">{{ task.name }}</view> |
|||
<!-- 全局提示框 --> |
|||
<u-top-tips ref="uTips"></u-top-tips> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import { mapGetters, mapMutations } from 'vuex'; |
|||
|
|||
export default { |
|||
name: 'p-wbs-import', |
|||
props: { |
|||
task: { |
|||
type: Object, |
|||
default: () => {}, |
|||
}, |
|||
}, |
|||
data() { |
|||
return {}; |
|||
}, |
|||
|
|||
computed: { |
|||
...mapGetters('user', ['userId']), |
|||
...mapGetters('project', ['projectId']), |
|||
}, |
|||
|
|||
methods: { |
|||
...mapMutations('project', ['setShowAlert']), |
|||
|
|||
// 导入wbs |
|||
async handleUpload() { |
|||
try { |
|||
const data = await this.$u.api.import(); |
|||
// 导入WBS成功后 |
|||
// 直接打开导入的项目 |
|||
this.onUploadSuccess(); |
|||
setTimeout(() => { |
|||
this.$u.route('/pages/project/project', { |
|||
u: this.userId, |
|||
p: data.id, |
|||
pname: data.pname, |
|||
url: data.url, |
|||
}); |
|||
}, 2000); |
|||
} catch (error) { |
|||
this.onUploadError(error); |
|||
} |
|||
}, |
|||
|
|||
// 更新项目 |
|||
// TODO: 更新接口没写完 |
|||
async handleUpdate() { |
|||
try { |
|||
await this.$u.api.import({ projectId: this.projectId }); |
|||
// 导入WBS成功后 |
|||
// 直接打开导入的项目 |
|||
this.onUploadSuccess(); |
|||
} catch (error) { |
|||
this.onUploadError(error); |
|||
} |
|||
}, |
|||
|
|||
// 导入成功 |
|||
onUploadSuccess() { |
|||
this.$refs.uTips.show({ |
|||
title: '导入成功,即将打开新项目', |
|||
type: 'success', |
|||
duration: '3000', |
|||
}); |
|||
}, |
|||
|
|||
// 导入失败 |
|||
onUploadError(error) { |
|||
this.$refs.uTips.show({ |
|||
title: error || '导入失败', |
|||
type: 'error', |
|||
duration: '6000', |
|||
}); |
|||
}, |
|||
}, |
|||
}; |
|||
</script> |
Before Width: | Height: | Size: 853 B |
Before Width: | Height: | Size: 3.9 KiB |
@ -1,3 +0,0 @@ |
|||
const actions = {}; |
|||
|
|||
export default actions; |
@ -1,3 +0,0 @@ |
|||
const getters = {}; |
|||
|
|||
export default getters; |
@ -1,12 +0,0 @@ |
|||
import state from './state'; |
|||
import getters from './getters'; |
|||
import mutations from './mutations'; |
|||
import actions from './actions'; |
|||
|
|||
export default { |
|||
namespaced: true, |
|||
state, |
|||
getters, |
|||
mutations, |
|||
actions, |
|||
}; |
@ -1,3 +0,0 @@ |
|||
const mutations = {}; |
|||
|
|||
export default mutations; |
@ -1,7 +0,0 @@ |
|||
const state = { |
|||
db: null, // indexedDB对象
|
|||
name: 'TALL_indexedDB', |
|||
version: 1, |
|||
}; |
|||
|
|||
export default state; |
@ -1,12 +1,9 @@ |
|||
import Vue from 'vue'; |
|||
import Vuex from 'vuex'; |
|||
import db from './db/index'; |
|||
import user from './user/index'; |
|||
import messages from './messages/index'; |
|||
import socket from './socket/index'; |
|||
import project from './project/index'; |
|||
import role from './role/index'; |
|||
import task from './task/index'; |
|||
import socket from './socket/index'; |
|||
import user from './user/index'; |
|||
|
|||
Vue.use(Vuex); |
|||
export default new Vuex.Store({ modules: { db, user, messages, socket, project, role, task } }); |
|||
export default new Vuex.Store({ modules: { user, messages, socket, project } }); |
|||
|
@ -1,3 +0,0 @@ |
|||
const actions = {}; |
|||
|
|||
export default actions; |
@ -1,3 +0,0 @@ |
|||
const getters = {}; |
|||
|
|||
export default getters; |
@ -1,12 +0,0 @@ |
|||
import state from './state'; |
|||
import getters from './getters'; |
|||
import mutations from './mutations'; |
|||
import actions from './actions'; |
|||
|
|||
export default { |
|||
namespaced: true, |
|||
state, |
|||
getters, |
|||
mutations, |
|||
actions, |
|||
}; |
@ -1,30 +0,0 @@ |
|||
const mutations = { |
|||
/** |
|||
* 设置不展示的角色信息 |
|||
* @param {Object} state |
|||
* @param {Array} data 服务端返回的模板数组 |
|||
*/ |
|||
setInvisibleRoles(state, data) { |
|||
state.invisibleRoles = data || []; |
|||
}, |
|||
|
|||
/** |
|||
* 设置展示的角色信息 |
|||
* @param {Object} state |
|||
* @param {Array} data 服务端返回的模板数组 |
|||
*/ |
|||
setVisibleRoles(state, data) { |
|||
state.visibleRoles = data || []; |
|||
}, |
|||
|
|||
/** |
|||
* 设置当前角色信息 |
|||
* @param {Object} state |
|||
* @param {string} roleId 当前正在展示的角色的id |
|||
*/ |
|||
setRoleId(state, roleId) { |
|||
state.roleId = roleId; |
|||
}, |
|||
}; |
|||
|
|||
export default mutations; |
@ -1,7 +0,0 @@ |
|||
const state = { |
|||
invisibleRoles: [], // 不展示的角色信息
|
|||
visibleRoles: [], // 展示的角色信息
|
|||
roleId: '', // 当前展示查看的角色id
|
|||
}; |
|||
|
|||
export default state; |
@ -1,33 +0,0 @@ |
|||
const actions = { |
|||
/** |
|||
* 根据角色查找永久的日常任务 |
|||
* @param {*} commit |
|||
* @param {string} roleId 角色id |
|||
*/ |
|||
getPermanent({ commit }, param) { |
|||
uni.$t.$q.getPermanent(param, (err, data) => { |
|||
if (err) { |
|||
console.error('err: ', err); |
|||
} else { |
|||
commit('setPermanents', data); |
|||
} |
|||
}); |
|||
}, |
|||
|
|||
/** |
|||
* 根据时间和角色查找日常任务 |
|||
* @param {*} commit |
|||
* @param {object} param 请求参数 roleId, timeNode, timeUnit |
|||
*/ |
|||
getGlobal({ commit }, param) { |
|||
uni.$t.$q.getGlobal(param, (err, data) => { |
|||
if (err) { |
|||
console.error('err: ', err); |
|||
} else { |
|||
commit('setDailyTasks', data); |
|||
} |
|||
}); |
|||
}, |
|||
}; |
|||
|
|||
export default actions; |
@ -1,23 +0,0 @@ |
|||
const getters = { |
|||
// 所有的日常任务 永久 + 可变 日常任务
|
|||
globals({ dailyTasks, permanents }) { |
|||
return [...permanents, ...dailyTasks]; |
|||
}, |
|||
|
|||
unitConfig({ timeUnit }) { |
|||
const target = uni.$t.timeConfig.timeUnits.find(item => item.id === timeUnit); |
|||
return target; |
|||
}, |
|||
|
|||
// 计算任务开始时间的格式
|
|||
startTimeFormat(state, { unitConfig }) { |
|||
return unitConfig.format || 'D日 HH:mm'; |
|||
}, |
|||
|
|||
// 计算颗粒度 对应的 dayjs add 的单位
|
|||
timeGranularity(state, { unitConfig }) { |
|||
return unitConfig.granularity; |
|||
}, |
|||
}; |
|||
|
|||
export default getters; |
@ -1,12 +0,0 @@ |
|||
import state from './state'; |
|||
import getters from './getters'; |
|||
import mutations from './mutations'; |
|||
import actions from './actions'; |
|||
|
|||
export default { |
|||
namespaced: true, |
|||
state, |
|||
getters, |
|||
mutations, |
|||
actions, |
|||
}; |
@ -1,168 +0,0 @@ |
|||
const mutations = { |
|||
/** |
|||
* 记录时间轴向上滚动的距离 |
|||
* @param { object } state |
|||
* @param { number } num |
|||
*/ |
|||
setScrollTop(state, num) { |
|||
state.scrollTop = num; |
|||
}, |
|||
|
|||
/** |
|||
* 记录时间轴向上滚动的距离 |
|||
* @param { object } state |
|||
* @param { string } data |
|||
*/ |
|||
setViewId(state, data) { |
|||
state.viewId = data; |
|||
}, |
|||
|
|||
/** |
|||
* 设置日常任务当前是否应该处于收缩状态 |
|||
* @param { object } state |
|||
* @param { boolean } data |
|||
*/ |
|||
setShrink(state, data) { |
|||
state.isShrink = data; |
|||
}, |
|||
|
|||
/** |
|||
* 设置tip的值 |
|||
* @param {object} state |
|||
* @param {object} data |
|||
*/ |
|||
setTip(state, data) { |
|||
if (!data) return; |
|||
state.tip = { ...data }; |
|||
}, |
|||
|
|||
/** |
|||
* 是否显示tips |
|||
* @param { object } state |
|||
* @param { boolean } show |
|||
*/ |
|||
setTipShow(state, show) { |
|||
state.tip.show = show; |
|||
}, |
|||
|
|||
/** |
|||
* 是否显示tips |
|||
* @param { object } state |
|||
* @param { number } status |
|||
*/ |
|||
setStatus(state, status) { |
|||
state.tip.status = status; |
|||
}, |
|||
|
|||
/** |
|||
* 设置时间基准点 |
|||
* @param { object } state |
|||
* @param { number } data |
|||
*/ |
|||
setTimeNode(state, data) { |
|||
state.timeNode = data; |
|||
}, |
|||
|
|||
/** |
|||
* 设置时间颗粒度 |
|||
* @param { object } state |
|||
* @param { number } data |
|||
*/ |
|||
setTimeUnit(state, data) { |
|||
state.timeUnit = data; |
|||
}, |
|||
|
|||
/** |
|||
* 设置向上查到的定期任务数据 |
|||
* @param {Object} state |
|||
* @param {Array} data 服务端返回的模板数组 |
|||
*/ |
|||
setUpTasks(state, data) { |
|||
if (!state.tasks.length) { |
|||
state.tasks = [...data]; |
|||
} else { |
|||
state.tasks = [...data.concat(state.tasks)]; |
|||
} |
|||
}, |
|||
|
|||
/** |
|||
* 设置向下查到的定期任务数据 |
|||
* @param {Object} state |
|||
* @param {Array} data 服务端返回的模板数组 |
|||
*/ |
|||
setDownTasks(state, data) { |
|||
if (!state.tasks && !state.tasks.length) { |
|||
state.tasks = [...data]; |
|||
} else { |
|||
state.tasks = [...state.tasks.concat(data)]; |
|||
} |
|||
}, |
|||
|
|||
/** |
|||
* 设置日常任务数据 |
|||
* @param {Object} state |
|||
* @param {Array} data 服务端返回的模板数组 |
|||
*/ |
|||
setDailyTasks(state, data) { |
|||
state.dailyTasks = data || []; |
|||
}, |
|||
|
|||
/** |
|||
* 设置永久固定任务 |
|||
* @param {object} state |
|||
* @param {array} tasks 服务端查询到的永久日常任务书籍 |
|||
*/ |
|||
setPermanents(state, tasks) { |
|||
state.permanents = tasks || []; |
|||
}, |
|||
|
|||
/** |
|||
* 设置时间轴是否继续向上查任务 |
|||
* @param {Object} state |
|||
* @param {Boolean} show |
|||
*/ |
|||
setTopEnd(state, show) { |
|||
state.topEnd = show; |
|||
}, |
|||
|
|||
/** |
|||
* 设置时间轴是否继续向下查任务 |
|||
* @param {Object} state |
|||
* @param {Boolean} show |
|||
*/ |
|||
setBottomEnd(state, show) { |
|||
state.bottomEnd = show; |
|||
}, |
|||
|
|||
// 清空标志位 如切换角色等使用
|
|||
clearEndFlag(state) { |
|||
state.topEnd = false; |
|||
state.bottomEnd = false; |
|||
}, |
|||
|
|||
// 清空定期任务
|
|||
clearTasks(state) { |
|||
state.tasks = []; |
|||
}, |
|||
|
|||
/** |
|||
* 收到消息设置任务状态 |
|||
* @param {Object} state |
|||
* @param {Array} data 服务端返回的模板数组 |
|||
*/ |
|||
setTaskStatus(state, data) { |
|||
const item = state.tasks.find(i => i.id === data.id); |
|||
item.process = data.taskStatus; |
|||
}, |
|||
|
|||
/** |
|||
* 设置骨架屏是否显示 |
|||
* @param {Object} state |
|||
* @param {Boolean} show |
|||
*/ |
|||
setShowSkeleton(state, show) { |
|||
state.showSkeleton = show; |
|||
}, |
|||
}; |
|||
|
|||
export default mutations; |
@ -1,23 +0,0 @@ |
|||
const state = { |
|||
scrollTop: 0, |
|||
viewId: '', // 时间轴自动滚动的位置
|
|||
isShrink: false, // true: 收起, false:展开
|
|||
tip: { |
|||
taskId: '', // 当前正在修改状态的任务的id
|
|||
show: false, |
|||
status: 0, // 所点击任务的当前状态码
|
|||
text: '', |
|||
left: 0, // 鼠标点击位置距离左边的距离
|
|||
top: 0, // 鼠标点击位置距离上边的距离
|
|||
}, |
|||
timeNode: new Date().getTime(), // 时间基准点
|
|||
timeUnit: 4, // 时间颗粒度
|
|||
topEnd: false, // 时间轴向上查任务到顶了
|
|||
bottomEnd: false, // 时间轴向下查任务到底了
|
|||
permanents: [], // 永久日常任务
|
|||
dailyTasks: [], // 日常任务
|
|||
tasks: [], // 所有的定期任务
|
|||
showSkeleton: false, // 定期任务骨架屏
|
|||
}; |
|||
|
|||
export default state; |
@ -1,46 +0,0 @@ |
|||
import Time from '../../utils/time.js'; |
|||
|
|||
// 测试计算进行中剩余时长显示数值
|
|||
describe('utils/time.js computeDurationText function', () => { |
|||
const { computeDurationText } = Time; |
|||
// const leftTime = +realStart + +planDuration - Date.now(); // 剩余时间
|
|||
it ('leftTime is 60ms, num=60, time=16', () => { |
|||
expect(computeDurationText(60)).toEqual({ num: 60, time: 16 }) |
|||
}) |
|||
|
|||
it ('leftTime is 300ms, num=300, time=16', () => { |
|||
expect(computeDurationText(300)).toEqual({ num: 300, time: 16 }) |
|||
}) |
|||
|
|||
it ('leftTime is 10s20ms, num=10, time=1000', () => { |
|||
expect(computeDurationText(10*1000 + 20)).toEqual({ num: 10, time: 1000 }) |
|||
}) |
|||
|
|||
it ('leftTime is 8分钟10s20ms, num=8, time=1000', () => { |
|||
expect(computeDurationText(8*60*1000 + 10*1000 + 20)).toEqual({ num: 8, time: 1000 }) |
|||
}) |
|||
|
|||
it ('leftTime is 3小时8分钟10s20ms, num=3, time=1000', () => { |
|||
expect(computeDurationText(3*60*60*1000 + 8*60*1000 + 10*1000 + 20)).toEqual({ num: 3, time: 1000 }) |
|||
}) |
|||
|
|||
it ('leftTime is 11天3小时8分钟10s20ms, num=11, time=60 * 60 * 1000', () => { |
|||
expect(computeDurationText(11*24*60*60*1000 + 3*60*60*1000 + 8*60*1000 + 10*1000 + 20)).toEqual({ num: 11, time: 60 * 60 * 1000 }) |
|||
}) |
|||
|
|||
it ('leftTime is 2个月11天3小时8分钟10s20ms, num=2, time=60 * 60 * 1000', () => { |
|||
expect(computeDurationText(2*30*24*60*60*1000 + 11*24*60*60*1000 + 3*60*60*1000 + 8*60*1000 + 10*1000 + 20)).toEqual({ num: 2, time: 60 * 60 * 1000 }) |
|||
}) |
|||
|
|||
it ('leftTime is 7年2个月11天3小时8分钟10s20ms, num=7, time=60 * 60 * 1000', () => { |
|||
expect(computeDurationText(7*12*30*24*60*60*1000 + 2*30*24*60*60*1000 + 11*24*60*60*1000 + 3*60*60*1000 + 8*60*1000 + 10*1000 + 20)).toEqual({ num: 7, time: 60 * 60 * 1000 }) |
|||
}) |
|||
|
|||
it ('leftTime <=0, num=0, time=null', () => { |
|||
expect(computeDurationText(-10)).toEqual({ num: 0, time: null }) |
|||
}) |
|||
|
|||
it ('leftTime 不是数字, num=0, time=null', () => { |
|||
expect(computeDurationText('abc')).toEqual({ num: 0, time: null }) |
|||
}) |
|||
}) |
@ -1,163 +0,0 @@ |
|||
import { name } from '@/config/db'; |
|||
import { curry } from 'lodash'; |
|||
|
|||
// 创建表
|
|||
const createCollection = (Vue, db) => { |
|||
// projects项目表
|
|||
!db.objectStoreNames.contains('projects') && db.createObjectStore('projects', { keyPath: 'id' }); |
|||
// roles 角色表
|
|||
!db.objectStoreNames.contains('roles') && db.createObjectStore('roles', { keyPath: 'id' }); |
|||
// plan_tasks 定期任务
|
|||
!db.objectStoreNames.contains('plan_tasks') && db.createObjectStore('plan_tasks', { keyPath: 'id' }); |
|||
// fixed_tasks 固定全局任务
|
|||
Vue.prototype.$db.fixed_tasks = !db.objectStoreNames.contains('fixed_tasks') && db.createObjectStore('fixed_tasks', { keyPath: 'id' }); |
|||
// variable_tasks 可变全局任务
|
|||
Vue.prototype.$db.variable_tasks = |
|||
!db.objectStoreNames.contains('variable_tasks') && db.createObjectStore('variable_tasks', { keyPath: 'id' }); |
|||
// plugins 插件表
|
|||
Vue.prototype.$db.plugins = !db.objectStoreNames.contains('plugins') && db.createObjectStore('plugins', { keyPath: 'id' }); |
|||
}; |
|||
|
|||
/** |
|||
* 新增数据 |
|||
* |
|||
* @param {object} db 数据库database |
|||
* @param {string} collection 集合/表 |
|||
* @param {object} data 数据 |
|||
*/ |
|||
const create = (db, collection, data) => { |
|||
return new Promise((resolve, reject) => { |
|||
const request = db.transaction([collection], 'readwrite').objectStore(collection).add(data); |
|||
request.onsuccess = () => resolve(); |
|||
|
|||
request.onerror = event => { |
|||
const { name, message } = event.target.error; |
|||
if (name === 'ConstraintError') { |
|||
reject('数据已存在'); |
|||
} else { |
|||
reject(message); |
|||
} |
|||
}; |
|||
}); |
|||
}; |
|||
|
|||
/** |
|||
* 找到1条数据 |
|||
* |
|||
* @param {object} db 数据库database |
|||
* @param {string} collection 集合/表 |
|||
* @param {string} key 索引关键字 一般是id |
|||
*/ |
|||
const findOne = (db, collection, key) => { |
|||
return new Promise((resolve, reject) => { |
|||
const request = db.transaction([collection]).objectStore(collection).get(key); |
|||
request.onerror = event => reject(event.target.error.message); |
|||
request.onsuccess = event => resolve(event.target.result); |
|||
}); |
|||
}; |
|||
|
|||
/** |
|||
* 找到所有数据 |
|||
* |
|||
* @param {object} db 数据库database |
|||
* @param {string} collection 集合/表 |
|||
*/ |
|||
const find = (db, collection) => { |
|||
return new Promise((resolve, reject) => { |
|||
const request = db.transaction(collection).objectStore(collection).openCursor(); |
|||
let result = []; |
|||
|
|||
request.onerror = event => reject(event.target.error.message); |
|||
request.onsuccess = event => { |
|||
const cursor = event.target.result; |
|||
if (cursor) { |
|||
result.push(cursor.value); |
|||
cursor.continue(); |
|||
} else { |
|||
resolve(result); |
|||
} |
|||
}; |
|||
}); |
|||
}; |
|||
|
|||
/** |
|||
* 更新数据 |
|||
* |
|||
* @param {object} db 数据库database |
|||
* @param {string} collection 集合/表 |
|||
* @param {object} newData 新数据 |
|||
*/ |
|||
const update = (db, collection, newData) => { |
|||
return new Promise((resolve, reject) => { |
|||
const request = db.transaction([collection], 'readwrite').objectStore(collection).put(newData); |
|||
request.onerror = event => reject(event.target.error.message); |
|||
request.onsuccess = () => resolve(newData); |
|||
}); |
|||
}; |
|||
|
|||
/** |
|||
* 移除数据 通过关键字 |
|||
* |
|||
* @param {object} db 数据库database |
|||
* @param {string} collection 集合/表 |
|||
* @param {string} key 关键字 |
|||
*/ |
|||
const remove = (db, collection, key) => { |
|||
return new Promise((resolve, reject) => { |
|||
const request = db.transaction([collection], 'readwrite').objectStore(collection).delete(key); |
|||
request.onerror = event => reject(event.target.error.message); |
|||
request.onsuccess = () => resolve(); |
|||
}); |
|||
}; |
|||
|
|||
/** |
|||
* 创建索引 |
|||
* |
|||
* @param {object} db 数据库database |
|||
* @param {string} collection 集合/表 |
|||
* @param {string} field 创建索引的字段名称 |
|||
* @param {string} key 关键字 |
|||
*/ |
|||
const createIndexAndFind = (db, collection, field, key) => { |
|||
return new Promise((resolve, reject) => { |
|||
const store = db.transaction([collection], 'readonly').objectStore(collection); |
|||
store.createIndex(field, field); |
|||
const index = store.index(field); |
|||
const request = index.get(key); |
|||
request.onerror = event => reject(event.target.error.message); |
|||
request.onsuccess = event => resolve(event.target.result); |
|||
}); |
|||
}; |
|||
|
|||
const curriedCreate = curry(create); |
|||
export const curriedFindOne = curry(findOne); |
|||
export const curriedFind = curry(find); |
|||
export const curriedRemove = curry(remove); |
|||
export const curriedUpdate = curry(update); |
|||
export const curriedIndex = curry(createIndexAndFind); |
|||
|
|||
const install = Vue => { |
|||
uni.$db = Vue.prototype.$db = {}; |
|||
Vue.prototype.$db.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB; |
|||
const request = Vue.prototype.$db.indexedDB.open(name, Date.now()); // IDBRequest 对象
|
|||
request.onerror = error => console.error('打开数据库失败', error); |
|||
request.onsuccess = event => { |
|||
console.log('INDEXED_DB OPEN SUCCESS'); |
|||
Vue.prototype.$db.db = event.target.result; |
|||
}; |
|||
request.onupgradeneeded = event => { |
|||
console.log('INDEXED_DB OPEN onupgradeneeded'); |
|||
Vue.prototype.$db.db = event.target.result; |
|||
// 创建表
|
|||
createCollection(Vue, Vue.prototype.$db.db); |
|||
|
|||
Vue.prototype.$db.create = curriedCreate(Vue.prototype.$db.db); // create 新增数据,颗粒化以后就不用再传db数据了
|
|||
Vue.prototype.$db.findOne = curriedFindOne(Vue.prototype.$db.db); // 查一条
|
|||
Vue.prototype.$db.find = curriedFind(Vue.prototype.$db.db); // 查集合里的所有数据
|
|||
Vue.prototype.$db.update = curriedUpdate(Vue.prototype.$db.db); // 更新某条数据
|
|||
Vue.prototype.$db.remove = curriedRemove(Vue.prototype.$db.db); // 删除某条数据
|
|||
// Vue.prototype.$db.createIndex = curriedIndex(Vue.prototype.$db.db); // 创建索引
|
|||
}; |
|||
}; |
|||
|
|||
export default { install }; |
Loading…
Reference in new issue