Browse Source

feat: 新建任务

tall
xuesinan 4 years ago
parent
commit
131ce0f201
  1. 3
      CHANGELOG.md
  2. 87
      src/components/ChooseChecker/ChooseChecker copy.vue
  3. 4
      src/components/TimeLine/component/TaskTools.vue
  4. 2
      src/components/TimeLine/component/TimeBox.vue
  5. 1
      src/components/TimeLine/component/TimeStatus.vue
  6. 90
      src/components/Title/components/CreateTask.vue
  7. 9
      src/pages/project/project.vue
  8. 149
      src/plugins/p-delivery-history/p-delivery-history1.vue
  9. 280
      src/plugins/p-upload-deliverable/p-upload-deliverable1.vue

3
CHANGELOG.md

@ -1,4 +1,4 @@
# 0.1.0 (2021-12-07)
# 0.1.0 (2021-12-13)
### 🌟 新功能
范围|描述|commitId
@ -54,6 +54,7 @@
- | 新建任务 部分提交参数 | 6a422f6
- | 新建任务,分享项目弹出层样式修改 | efbc679
- | 新建形目 | f7d7108
- | 新增任务 | 9b542fc
- | 引入dayjs | 29b8b93
- | 字体大小更改 | 82cfdd4
- | api封装 | 7d4edfc

87
src/components/ChooseChecker/ChooseChecker copy.vue

@ -0,0 +1,87 @@
<template>
<view class="my-3" v-if="allMembers && allMembers.length">
<view class="flex justify-between">
<view class="flex flex-wrap text-center items-center">
<u-tag
:type="member.checked ? 'primary' : 'info'"
:mode="member.checked ? 'dark' : 'light'"
v-for="(member, index) in topMembers"
:key="member.memberId"
class="mb-2 mr-3"
style="width: 60px"
:text="member.name"
:closeable="false"
@click="tagClick(index, member, 'topMembers')"
/>
<span class="ml-2" v-if="!show" @click="show = true">...</span>
</view>
</view>
<!-- 折叠起来的 -->
<view class="flex flex-wrap text-center items-center" v-if="show">
<u-tag
:type="member.checked ? 'primary' : 'info'"
:mode="member.checked ? 'dark' : 'light'"
v-for="(member, index) in bottomMembers"
:key="member.memberId"
class="mb-2 mr-3"
style="width: 60px"
:text="member.name"
:closeable="false"
@click="tagClick(index, member, 'bottomMembers')"
/>
<u-icon class="ml-2" name="arrow-up" v-if="show" size="26" @click="show = false"></u-icon>
</view>
</view>
</template>
<script>
import { mapState } from 'vuex';
export default {
props: {
checkerList: {
default: () => [],
type: Array,
},
},
data() {
return { allMembers: [], show: false, topMembers: [], bottomMembers: [] };
},
computed: mapState('role', ['members']),
mounted() {
if (this.members && this.members.length) {
this.allMembers = this.members;
// TODO:
this.allMembers.forEach(item => {
item.checked = false;
});
this.topMembers = this.members.slice(0, 3);
this.bottomMembers = this.members.slice(3);
}
},
methods: {
tagClick(index, item, membersType) {
//
const arr = this.$u.deepClone(this[membersType]);
arr[index].checked = !arr[index].checked;
this[membersType] = [...arr];
// idcheckerList
this.$emit('setCheckerList', arr[index].checked, item);
},
//
clearChecked() {
for (let i = 0; i < this.topMembers.length; i++) {
this.topMembers[i].checked = false;
}
for (let i = 0; i < this.bottomMembers.length; i++) {
this.bottomMembers[i].checked = false;
}
},
},
};
</script>

4
src/components/TimeLine/component/TaskTools.vue

@ -9,6 +9,9 @@
<!-- 右上角 ... 弹窗 -->
<view class="popup border shadow-md" v-if="show">
<view class="flex justify-center pb-3 border-b-1">
<span>添加插件</span>
</view>
<view class="flex justify-center py-3 border-b-1">
<span @click="createTask">新建任务</span>
</view>
<view class="flex pt-3 justify-center">
@ -24,6 +27,7 @@
:startTime="startTime"
:endTime="endTime"
:task="task"
:source="'regular'"
@showTime="showTime"
@closeMask="closeMask"
class="thirdPopup flex transition-transform"

2
src/components/TimeLine/component/TimeBox.vue

@ -44,7 +44,7 @@
<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">
<view class="grid gap-2 grid-cols-1" v-if="row.length">
<Plugin
:class="[`row-span-${plugin.row}`, `col-span-${plugin.col}`]"
:task="task"

1
src/components/TimeLine/component/TimeStatus.vue

@ -44,6 +44,7 @@
:startTime="startTime"
:endTime="endTime"
:task="task"
:source="'regular'"
@showTime="showTime"
@closeMask="closeMask"
class="thirdPopup flex transition-transform"

90
src/components/Title/components/CreateTask.vue

@ -3,6 +3,7 @@
<div class="form">
<!-- 项目名称 -->
<view class="new-projects-title font-bold text-base flex justify-center items-center text-black">新建任务</view>
<div class="form-item flex items-center">
<div class="mr-4">名称<span class="text-red-500">*</span></div>
<u-input :maxlength="8" v-model="name" type="text" :inputAlign="'right'" :clearable="false" placeholder="请输入任务名称" />
@ -10,7 +11,7 @@
<!-- 开始时间 -->
<div class="form-item flex items-center">
<div class="mr-4">开始时间</div>
<div class="mr-4">开始时间<span class="text-red-500" v-if="source === 'regular'">*</span></div>
<div class="flex justify-end items-center flex-1">
<u-input
placeholder="请选择开始时间"
@ -26,7 +27,7 @@
<!-- 结束时间 -->
<div class="form-item flex items-center">
<div class="mr-4">结束时间</div>
<div class="mr-4">结束时间<span class="text-red-500" v-if="source === 'regular'">*</span></div>
<div class="flex justify-end items-center flex-1">
<u-input
placeholder="请选择结束时间"
@ -95,8 +96,8 @@
<!-- 检查人多选框 -->
<div class="form-item flex justify-between items-center">
<div class="mr-4">检查人<span class="text-red-500">*</span></div>
<div label="检查人" class="flex-1">
<div class="mr-4 flex-shrink-0">检查人<span class="text-red-500">*</span></div>
<div label="检查人" class="flex-1" style="width: calc(100% - 65px)">
<u-dropdown ref="dropdown">
<u-dropdown-item :title="checkerList">
<view class="slot-content bg-white">
@ -116,7 +117,7 @@
</div>
<!-- 是否是日常任务 -->
<div class="form-item flex justify-between items-center">
<div class="form-item flex justify-between items-center" v-if="!source">
是否是日常任务
<u-switch v-model="isGlobal" size="28"></u-switch>
</div>
@ -135,6 +136,11 @@
</div>
</div>
<!-- <div class="form-item flex items-center">
<div class="mr-4">添加插件</div>
<u-input :maxlength="8" v-model="name" type="text" :inputAlign="'right'" :clearable="false" placeholder="选择插件" />
</div> -->
<div class="form-btn flex items-center mt-4">
<u-button type="primary" size="medium" @click="setParameters">提交</u-button>
</div>
@ -159,22 +165,26 @@ export default {
type: Object,
default: null,
},
source: {
type: String,
default: '',
},
},
data() {
return {
arrow: true,
show: false,
// arrow: true,
// show: false,
isGlobal: false, //
name: '', //
showChooseTime: false,
// showChooseTime: false,
// timeValue: '', //
// taskStartTime: '', //
// taskEndTime: '', //
description: '', //
projectShow: false, //
// projectShow: false, //
processTaskId: '', //
type: 'text',
border: true,
// type: 'text',
// border: true,
roleList: undefined, //
checkerList: undefined, //
roleOptions: [], //
@ -271,16 +281,16 @@ export default {
},
//
openDropdown() {
this.arrow = !this.arrow;
this.show = true;
},
// openDropdown() {
// this.arrow = !this.arrow;
// this.show = true;
// },
//
closeSecondDropdown() {
this.arrow = !this.arrow;
this.show = false;
},
// //
// closeSecondDropdown() {
// this.arrow = !this.arrow;
// this.show = false;
// },
/**
* 模糊查询 查找项目下的任务
@ -317,6 +327,10 @@ export default {
//
async setParameters() {
if (this.source) {
this.isGlobal = 0;
}
const {
projectId,
task,
@ -373,7 +387,7 @@ export default {
roleIdList: hasRole ? [roleId] : roleIdList,
description,
projectId,
parentTaskId: task && task.id ? task.id : '', //
parentTaskId: task && task.process !== 4 && task.id ? task.id : '', //
processTaskId, // TODO
checkerIdList,
global: isGlobal ? 1 : 0,
@ -407,7 +421,7 @@ export default {
};
// store
//
if (!this.task || !this.task.id) {
if (!this.task || !this.task.id || this.task.process === 4) {
this.addNewTasks(newTasks);
}
} catch (error) {
@ -420,7 +434,7 @@ export default {
addNewTasks(data) {
const oldTasks = this.$u.deepClone(this.tasks);
let res = data.data;
console.log('添加任务后更新tasks', data);
//
if (data.processTaskId) {
const index = oldTasks.find(item => item.detailId === data.processTaskId);
@ -434,8 +448,6 @@ export default {
//
setAddPosition(res, oldTasks) {
console.log('设置添加位置', res, oldTasks);
if (res.planStart - 0 < oldTasks[0].planStart - 0) {
//
oldTasks.splice(0, 0, res);
@ -479,7 +491,7 @@ export default {
.drop-item {
border-bottom: 1px solid #f1f1f1;
padding: 16rpx;
padding: 16rpx 32rpx;
}
// ::v-deep.u-input--border {
@ -535,9 +547,35 @@ export default {
height: 39px;
}
::v-deep .u-dropdown__menu {
height: 48px !important;
}
::v-deep .u-dropdown__menu__item {
width: 100%;
}
::v-deep .u-dropdown__menu__item .u-flex {
border: 0 !important;
// border-bottom: 1px solid #dcdfe6 !important;
padding: 0 !important;
}
::v-deep .u-dropdown__menu__item__arrow {
margin-left: 10px;
flex-shrink: 0;
}
::v-deep .u-dropdown__menu__item__text {
width: calc(100% - 23px);
text-align: right;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
::v-deep .u-dropdown__content {
top: 48px !important;
box-shadow: 0 4px 6px 1px rgba(0, 0, 0, 0.1) !important;
}
</style>

9
src/pages/project/project.vue

@ -73,7 +73,7 @@ export default {
...mapGetters('user', ['userId']),
},
onLoad(options) {
async onLoad(options) {
if (options.share && options.share === '1') {
this.shareInit(options);
} else {
@ -147,6 +147,7 @@ export default {
methods: {
...mapActions('user', ['getToken']),
...mapActions('task', ['getRegulars', 'getPermanent', 'getGlobal']),
...mapActions('role', ['getAllMembers']),
...mapMutations('user', ['setToken']),
...mapMutations('project', ['setProject', 'setProjectName']),
...mapMutations('role', ['setInvisibleRoles', 'setVisibleRoles', 'setRoleId']),
@ -206,9 +207,9 @@ export default {
} else {
//
//
// that.setPrevPlaceholderTasks();
that.setPrevPlaceholderTasks();
// //
// that.setNextPlaceholderTasks();
that.setNextPlaceholderTasks();
}
}
//
@ -368,6 +369,8 @@ export default {
this.getProjectById({ projectId: options.p, num: 0 }); // id
//
// this.handleQueryNotWrite(options.p);
// id
this.getAllMembers({ projectId: options.p });
}
},

149
src/plugins/p-delivery-history/p-delivery-history1.vue

@ -0,0 +1,149 @@
<template>
<!-- 交付物 -->
<view class="mt-3">
<view v-if="lists && lists.length">
<view :key="list.id" v-for="list in lists">
<view class="text-gray-400 u-font-12 font-thin leading-none">
<span class="mr-2">{{ list.name }}</span>
<span>{{ $moment(+list.time).format('YYYY-MM-DD HH:mm:ss') }}</span>
</view>
<view class="mt-2 py-1 px-2.5 border border-gray-200 rounded flex flex-wrap overflow-hidden break-all" v-if="list.content">
<a :href="list.content" class="text-blue-500 u-font-12 font-thin" target="_blank" v-if="CheckUrl(list.content)">{{
list.content
}}</a>
<span v-else>{{ list.content }}</span>
</view>
<view :class="index === 0 ? 'mt-4' : 'mt-3'" v-for="(checker, index) in list.checkerList" :key="index">
<view class="flex justify-between leading-none">
<view>
{{ checker.checkerName }}
<span v-if="checker.isMine">()</span>
</view>
<view>
<span class="text-blue-500" v-if="checker.status === 1">通过</span>
<span class="text-red-500" v-if="checker.status === 2">驳回</span>
<span class="ml-4" v-if="checker.status !== 0">{{ checker.score }}</span>
<span class="text-gray-400" v-if="checker.status === 0 && !checker.isMine">未审核</span>
<view v-if="checker.status === 0 && checker.isMine">
<u-button class="action-btn mr-2" @click="showScore(checker.checkId, 1)" size="mini" shape="circle" type="primary">
通过
</u-button>
<u-button class="action-btn" @click="showScore(checker.checkId, 2)" size="mini" shape="circle" type="error">驳回</u-button>
</view>
</view>
</view>
<view class="text-gray-400 text-xs mt-1">{{ checker.remark }}</view>
</view>
</view>
</view>
<u-empty icon-size="90" mode="history" text="暂未上传交付物" v-else></u-empty>
<!-- 评分 -->
<uni-popup :maskClick="false" background-color="#fff" ref="popup" type="bottom">
<PDeliverCheck @closeScore="closeScore" @submit="submit"></PDeliverCheck>
</uni-popup>
</view>
</template>
<script>
import { mapGetters } from 'vuex';
import UniPopup from '../../components/uni-popup/uni-popup.vue';
import PDeliverCheck from '../p-deliver-check/p-deliver-check.vue';
export default {
name: 'p-delivery-history',
props: { task: { type: Object, default: null } },
components: { PDeliverCheck, UniPopup },
data() {
return {
lists: [],
show: false,
options: null,
loading: true, //
};
},
computed: mapGetters('project', ['projectId']),
mounted() {
this.getDeliverOfTask();
},
methods: {
async getDeliverOfTask() {
try {
const { projectId, task } = this;
const params = { projectId, taskSubId: task.id };
const data = await this.$u.api.queryDeliverOfTask(params);
this.lists = data;
} catch (error) {
console.error('p-delivery-history.vue getDeliverOfTask error: ', error);
this.$t.ui.showToast(error.msg || '提交失败');
}
},
showScore(checkId, status) {
// refuni-popup , type ['top','left','bottom','right','center']
this.$refs.popup.open('bottom');
this.options = { checkId, status };
},
closeScore() {
this.$refs.popup.close('bottom');
},
async submit(remark, score) {
try {
await this.checkDeliver(remark, score);
this.closeScore();
} catch (error) {
console.error('error: ', error);
}
},
/**
* 检查交付物
* @param {string} checkId 检查记录id
* @param {string} projectId 项目id
* @param {string} remark 评论
* @param {number} score 分数
* @param {number} status 检查状态(1-通过,2-驳回)
*/
async checkDeliver(remark, score) {
try {
this.show = true;
const { projectId, options } = this;
const { checkId, status } = options;
const params = { checkId, projectId, status, remark, score };
await this.$u.api.checkDeliver(params);
this.$t.ui.showToast('交付物检查成功');
this.options = null;
this.getDeliverOfTask();
} catch (error) {
console.error('p-delivery-history.vue checkDeliver error: ', error);
this.$t.ui.showToast('交付物检查失败,请稍后重试');
this.options = null;
}
},
//
CheckUrl(url) {
var reg = /^([hH][tT]{2}[pP]:\/\/|[hH][tT]{2}[pP][sS]:\/\/)(.)+$/;
if (!reg.test(url)) {
return false;
} else {
return true;
}
},
},
};
</script>
<style scoped>
.action-btn {
padding: 0;
width: 80rpx;
height: 40rpx;
line-height: 40rpx;
}
</style>

280
src/plugins/p-upload-deliverable/p-upload-deliverable1.vue

@ -0,0 +1,280 @@
<template>
<!-- 上传交付物 -->
<view class="pt-2 relative">
<template v-if="history.length === 0">
<u-input
:auto-height="autoHeight"
:border="border"
:height="height"
:type="type"
v-model="content"
placeholder="请输入链接/内容"
width="100"
/>
<view class="to-examine mt-2">
<view class="examine-title flex justify-between items-center">
<view class="flex-shrink-0" style="color: #999">审核人</view>
<view class="flex items-center justify-end" style="color: #595959; width: calc(100% - 60px)">
<text class="examine-people text-right mr-2">{{ examinePerpol }}</text>
<u-icon name="arrow-down" v-if="!isShowMembers" @click="showMembers"></u-icon>
<u-icon name="arrow-up" v-else @click="showMembers"></u-icon>
</view>
</view>
<view class="examine-con" v-if="isShowMembers">
<u-checkbox-group @change="checkboxGroupChange">
<u-checkbox
@change="checkboxChange($event, item)"
v-model="item.checked"
v-for="(item, index) in list"
:key="index"
:name="item.name"
:data-id="item.id"
>
{{ item.name }}
</u-checkbox>
</u-checkbox-group>
</view>
</view>
</template>
<template v-else>
<template v-for="(item, index) in history">
<view class="to-examine py-1 flex flex-wrap overflow-hidden break-all" :key="index" v-if="item.content">
<a :href="item.content" class="text-blue-500 u-font-12 font-thin" target="_blank" v-if="CheckUrl(item.content)">
{{ item.content }}
</a>
<span v-else>{{ item.content }}</span>
</view>
<!-- <template v-if="showHistory"> -->
<view :class="index === 0 ? 'mt-4' : 'mt-3'" v-for="(checker, index) in item.checkerList" :key="index">
<view class="flex justify-between">
<view class="leading-none flex items-center">
{{ checker.checkerName }}
<span class="leading-none" v-if="checker.isMine">()</span>
</view>
<view class="flex items-center">
<span class="text-blue-500" v-if="checker.status === 1">通过</span>
<span class="text-red-500" v-if="checker.status === 2">驳回</span>
<span class="ml-4" v-if="checker.status !== 0">{{ checker.score }}</span>
<span class="text-gray-400 leading-none" v-if="checker.status === 0 && !checker.isMine">未审核</span>
<view v-if="checker.status === 0 && checker.isMine">
<u-button @click="showScore(checker.checkId, 2)" class="mr-3" plain size="mini" type="error">驳回</u-button>
<u-button @click="showScore(checker.checkId, 1)" plain size="mini" type="primary">通过</u-button>
</view>
</view>
</view>
<view class="text-gray-400 text-xs mt-1">{{ checker.remark }}</view>
</view>
<!-- </template> -->
</template>
</template>
<!-- 选择检查人 -->
<!-- <ChooseChecker ref="checker" :checkerList="checkerList" @setCheckerList="setCheckerList"></ChooseChecker> -->
<view class="submit-delivery">
<u-button @click="submit" size="mini" type="primary" v-if="currStatus === 0">提交</u-button>
<template v-if="currStatus === 1">
<text class="mr-2">待审核</text>
<!-- <u-icon @click="changeShowHistory" name="arrow-right" v-if="!showHistory"></u-icon>
<u-icon @click="changeShowHistory" name="arrow-down" v-else></u-icon> -->
</template>
</view>
<!-- <view class="mt-2 flex justify-between">
<u-icon @click="changeShowHistory" name="arrow-up" v-if="showHistory"></u-icon>
<u-icon @click="changeShowHistory" name="arrow-down" v-else></u-icon>
</view>
<p-delivery-history :task="task" v-if="showHistory" /> -->
</view>
</template>
<script>
// import ChooseChecker from '@/components/ChooseChecker/ChooseChecker.vue';
import { mapState, mapGetters } from 'vuex';
export default {
name: 'p-upload-deliverable',
// components: { ChooseChecker },
props: { task: { type: Object, default: null } },
data() {
return {
content: '',
type: 'textarea',
border: true,
height: 40,
autoHeight: true,
checkerList: [],
// showHistory: false, //
list: [],
examinePerpol: '请选择审核人',
isShowMembers: false, //
currStatus: 0, //
history: [],
};
},
computed: {
...mapState('role', ['members']),
...mapGetters('project', ['projectId']),
},
mounted() {
if (this.members.length > 0) {
this.list = [];
if (this.members.length) {
this.members.forEach(member => {
const item = { id: member.memberId, name: member.name, checked: false };
this.list.push(item);
});
}
}
this.getDeliverOfTask();
},
methods: {
// checkbox
checkboxChange(e, data) {
if (e.value) {
this.checkerList.push(data.id);
} else {
const index = this.checkerList.findIndex(checker => checker === data.id);
this.checkerList.splice(index, 1);
}
},
// checkboxcheckbox-group
checkboxGroupChange(e) {
this.examinePerpol = e.toString();
},
//
showMembers() {
this.isShowMembers = !this.isShowMembers;
},
//
// setCheckerList(checked, item) {
// if (checked) {
// this.checkerList.push(item.memberId);
// } else {
// const index = this.checkerList.findIndex(checker => checker === item.memberId);
// this.checkerList.splice(index, 1);
// }
// },
//
// changeShowHistory() {
// this.showHistory = !this.showHistory;
// },
//
async submit() {
try {
const { content, checkerList, projectId, task } = this;
if (!this.checkerList.length) {
this.$t.ui.showToast('请选择检查人');
return;
}
const params = { content, checkerList, projectId, taskSubId: task.id };
await this.$u.api.saveDeliver(params);
this.$t.ui.showToast('交付物提交成功');
this.currStatus = 1;
this.isShowMembers = false;
// this.content = '';
// this.checkerList = [];
// this.$refs.checker.clearChecked();
} catch (error) {
console.error('p-upload-deliverable.vue submit error: ', error);
this.$t.ui.showToast('交付物提交失败,请稍后重试');
}
},
async getDeliverOfTask() {
try {
const { projectId, task } = this;
const params = { projectId, taskSubId: task.id };
const data = await this.$u.api.queryDeliverOfTask(params);
console.log('1111111', data);
if (data.length > 0) {
data.forEach(item => {
item.checkerList.forEach(v => {
if (v.status === 0) {
this.currStatus = 1;
}
});
});
this.history = data;
}
} catch (error) {
console.error('p-delivery-history.vue getDeliverOfTask error: ', error);
this.$t.ui.showToast(error.msg || '提交失败');
}
},
//
CheckUrl(url) {
var reg = /^([hH][tT]{2}[pP]:\/\/|[hH][tT]{2}[pP][sS]:\/\/)(.)+$/;
if (!reg.test(url)) {
return false;
} else {
return true;
}
},
},
};
</script>
<style scoped lang="scss">
.to-examine {
padding-left: 20rpx;
padding-right: 20rpx;
border: 2rpx solid #dcdfe6;
border-radius: 8rpx;
.examine-title {
height: 60rpx;
}
.examine-people {
width: calc(100% - 22px);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.examine-con {
margin: 10rpx 0 20rpx;
}
}
::v-deep .u-checkbox__label {
font-size: 24rpx;
color: #999999;
}
::v-deep .u-checkbox__icon-wrap {
width: 20rpx !important;
height: 20rpx !important;
border-radius: 50%;
}
::v-deep .u-checkbox__label {
margin-left: 16rpx;
}
::v-deep .u-icon__icon.uicon-checkbox-mark {
transform: scale(0.5);
}
.submit-delivery {
position: absolute;
right: 0;
top: -25px;
}
</style>
Loading…
Cancel
Save