kuaifan 5 yıl önce
ebeveyn
işleme
0811cc3680

+ 2 - 6
resources/assets/js/main/components/FullCalendar/FullCalendar.vue

@@ -15,12 +15,8 @@
 
             <div slot="header-right" class="btn-group">
                 <div>
-                    <button :class="{cancel:tableType=='month',primary:tableType=='week'}" @click="changeType('week')">
-                        周
-                    </button>
-                    <button :class="{cancel:tableType=='week',primary:tableType=='month'}" @click="changeType('month')">
-                        月
-                    </button>
+                    <button :class="{cancel:tableType=='month',primary:tableType=='week'}" @click="changeType('week')">周</button>
+                    <button :class="{cancel:tableType=='week',primary:tableType=='month'}" @click="changeType('month')">月</button>
                 </Div>
             </div>
         </fc-header>

+ 2 - 2
resources/assets/js/main/components/FullCalendar/components/body.vue

@@ -26,7 +26,7 @@
                                      @click="eventClick(event,$event)">
                                     <img class="avatar" v-if="event.avatar" :src="event.avatar" @error="($set(event, 'avatar', null))"/>
                                     <span v-else :class="`icon icon${event.id%4}`">{{event.name}}</span>
-                                    <p class="info">{{isBegin(event, day.date, day.weekDay)}}</p>
+                                    <p class="info" v-html="isBegin(event, day.date, day.weekDay)"></p>
                                     <div id="card" :class="cardClass" v-if="event &&showCard == event.id" @click.stop>
                                         <slot name="body-card"></slot>
                                     </div>
@@ -65,7 +65,7 @@
                                  @click="eventClick(event,$event)">
                                 <img class="avatar" v-if="event.avatar" :src="event.avatar" @error="($set(event, 'avatar', null))"/>
                                 <span v-else :class="`icon icon${event.id%4}`">{{event.name}}</span>
-                                <p class="info">{{event.title}}</p>
+                                <p class="info" v-html="event.title"></p>
                                 <div id="card" :class="cardClass" v-if="event && showCard == event.id" @click.stop>
                                     <slot name="body-card"></slot>
                                 </div>

+ 17 - 6
resources/assets/js/main/components/project/task/detail/detail.vue

@@ -3,7 +3,7 @@
         <div class="task-detail-main">
             <div class="detail-left">
                 <div class="detail-title-box detail-icon">
-                    <input v-model="detail.title" :disabled="!!loadData.title" type="text" maxlength="60" @blur="handleTask('title')">
+                    <input v-model="detail.title" :disabled="!!loadData.title" type="text" maxlength="60" @keydown.enter="(e)=>{e.target.blur()}" @blur="handleTask('title')">
                     <div class="time">
                         <span class="z-nick">{{detail.createuser}}</span>
                         创建于:
@@ -12,7 +12,7 @@
                 </div>
                 <div class="detail-desc-box detail-icon">
                     <div class="detail-h2"><strong class="active">描述</strong></div>
-                    <textarea v-model="detail.desc" placeholder="添加详细描述..." @blur="handleTask('desc')"></textarea>
+                    <textarea v-model="detail.desc" placeholder="添加详细描述..."  @keydown="descKeydown" @blur="handleTask('desc')"></textarea>
                 </div>
                 <ul class="detail-text-box">
                     <li v-if="detail.startdate > 0 && detail.enddate > 0" class="text-time detail-icon">
@@ -63,7 +63,7 @@
                         <DropdownItem v-for="level in [1,2,3,4]" :key="level" :name="`level-${level}`" :class="`p${level}`">{{levelFormt(level)}}<Icon v-if="detail.level==level" type="md-checkmark" class="checkmark"/></DropdownItem>
                     </DropdownMenu>
                 </Dropdown>
-                <Poptip placement="bottom" class="block" transfer>
+                <Poptip placement="bottom" class="block">
                     <Button :loading="!!loadData.username" icon="md-person" class="btn">负责人</Button>
                     <div slot="content">
                         <div style="width:240px">
@@ -72,7 +72,7 @@
                         </div>
                     </div>
                 </Poptip>
-                <Poptip ref="timeRef" placement="bottom" class="block" @on-popper-show="handleTask('opentime')" transfer>
+                <Poptip ref="timeRef" placement="bottom" class="block" @on-popper-show="handleTask('opentime')">
                     <Button :loading="!!loadData.plannedtime || !!loadData.unplannedtime" icon="md-calendar" class="btn">计划时间</Button>
                     <div slot="content">
                         <div style="width:280px">
@@ -214,6 +214,17 @@
                 }
             },
 
+            descKeydown(e) {
+                e = e || event;
+                if (e.keyCode == 13) {
+                    if (e.shiftKey) {
+                        return;
+                    }
+                    e.preventDefault();
+                    e.target.blur();
+                }
+            },
+
             commentKeydown(e) {
                 e = e || event;
                 if (e.keyCode == 13) {
@@ -440,7 +451,7 @@
 <style lang="scss" scoped>
     .project-task-detail-window {
         position: fixed;
-        z-index: 99;
+        z-index: 1001;
         top: 0;
         left: 0;
         height: 100%;
@@ -465,7 +476,7 @@
             max-width: 800px;
             max-height: 92%;
             background: #ffffff;
-            overflow: hidden;
+            overflow: visible;
             border-radius: 4px;
             padding: 10px 20px 2px;
             transform: translateZ(0);

+ 56 - 2
resources/assets/js/main/components/project/todo/attention.vue

@@ -18,6 +18,8 @@
 </style>
 <script>
     import DrawerTabsContainer from "../../DrawerTabsContainer";
+    import Task from "../../../mixins/task";
+
     export default {
         name: 'TodoAttention',
         components: {DrawerTabsContainer},
@@ -27,6 +29,9 @@
                 default: true
             },
         },
+        mixins: [
+            Task
+        ],
         data () {
             return {
                 loadYet: false,
@@ -46,6 +51,55 @@
                 "title": "任务名称",
                 "key": 'title',
                 "minWidth": 100,
+                render: (h, params) => {
+                    return h('div', [
+                        h('Icon', {
+                            props: { type: params.row.complete ? 'md-checkbox-outline' : 'md-square-outline' },
+                            style: {marginRight: '4px', cursor: 'pointer', fontSize: '15px'},
+                            on: {
+                                click: () => {
+                                    this.taskComplete(params.row, !params.row.complete, (detail) => {
+                                        this.$emit("change", detail.complete ? 'complete' : 'unfinished', detail);
+                                    });
+                                }
+                            }
+                        }),
+                        h('span', {
+                            style: {cursor: 'pointer'},
+                            on: {
+                                click: () => {
+                                    let taskDetail = params.row;
+                                    this.taskDetail(taskDetail, (act, detail) => {
+                                        for (let key in detail) {
+                                            if (detail.hasOwnProperty(key)) {
+                                                this.$set(taskDetail, key, detail[key])
+                                            }
+                                        }
+                                        //
+                                        switch (act) {
+                                            case "username":    // 负责人
+                                            case "delete":      // 删除任务
+                                            case "archived":    // 归档
+                                                this.lists.some((task, i) => {
+                                                    if (task.id == detail.id) {
+                                                        this.lists.splice(i, 1);
+                                                        return true;
+                                                    }
+                                                });
+                                                break;
+
+                                            case "unarchived":  // 取消归档
+                                                this.lists.unshift(detail);
+                                                break;
+                                        }
+                                        //
+                                        this.$emit("change", act, detail);
+                                    });
+                                }
+                            }
+                        }, params.row.title)
+                    ]);
+                }
             }, {
                 "title": "创建人",
                 "key": 'createuser',
@@ -55,10 +109,10 @@
                 "key": 'username',
                 "minWidth": 80,
             }, {
-                "title": "完成时间",
+                "title": "添加时间",
                 "width": 160,
                 render: (h, params) => {
-                    return h('span', $A.formatDate("Y-m-d H:i:s", params.row.complete));
+                    return h('span', $A.formatDate("Y-m-d H:i:s", params.row.inorder));
                 }
             }];
         },

+ 71 - 19
resources/assets/js/main/components/project/todo/calendar.vue

@@ -37,8 +37,34 @@
         },
 
         methods: {
-            clickEvent(event, jsEvent){
-                console.log(event, jsEvent)
+            clickEvent(event){
+                this.taskDetail(event.id, (act, detail) => {
+                    let data = this.formatTaskData(detail);
+                    for (let key in data) {
+                        if (data.hasOwnProperty(key)) {
+                            this.$set(event, key, data[key])
+                        }
+                    }
+                    //
+                    switch (act) {
+                        case "username":    // 负责人
+                        case "delete":      // 删除任务
+                        case "archived":    // 归档
+                            this.lists.some((task, i) => {
+                                if (task.id == detail.id) {
+                                    this.lists.splice(i, 1);
+                                    return true;
+                                }
+                            });
+                            break;
+
+                        case "unarchived":  // 取消归档
+                            this.lists.push(data);
+                            break;
+                    }
+                    //
+                    this.$emit("change", act, detail);
+                });
             },
 
             changeDateRange(startdate, enddate){
@@ -66,23 +92,7 @@
                             let inLists,
                                 data;
                             res.data.lists.forEach((temp) => {
-                                let title = temp.title;
-                                let startdate = temp.startdate || temp.indate;
-                                let enddate = temp.enddate || temp.indate;
-                                if (startdate != enddate) {
-                                    title = $A.formatDate('H:i', startdate) + "~" + $A.formatDate('H:i', enddate) + " " + title;
-                                } else {
-                                    title = $A.formatDate('H:i', startdate) + " " + title;
-                                }
-                                data = {
-                                    "id": temp.id,
-                                    "start": $A.formatDate('Y-m-d H:i:s', startdate),
-                                    "end": $A.formatDate('Y-m-d H:i:s', enddate),
-                                    "title": title,
-                                    "color": "#D8F8F2",
-                                    "avatar": temp.userimg,
-                                    "name": temp.nickname || temp.username
-                                };
+                                data = this.formatTaskData(temp);
                                 inLists = false;
                                 this.lists.some((item, i) => {
                                     if (item.id == data.id) {
@@ -101,6 +111,48 @@
                     }
                 });
             },
+
+            formatTaskData(taskData) {
+                let title = taskData.title;
+                let startdate = taskData.startdate || taskData.indate;
+                let enddate = taskData.enddate || taskData.indate;
+                if (startdate != enddate) {
+                    let ymd1 = $A.formatDate('Y-m-d', startdate)
+                    let ymd2 = $A.formatDate('Y-m-d', enddate)
+                    let time = ymd1 + "~" + ymd2;
+                    if (ymd1 == ymd2) {
+                        time = $A.formatDate('H:i', startdate) + "~" + $A.formatDate('H:i', enddate);
+                    } else if (ymd1.substring(0, 4) == ymd2.substring(0, 4)) {
+                        time = $A.formatDate('m-d', startdate) + "~" + $A.formatDate('m-d', enddate);
+                    }
+                    title = time + " " + title;
+                } else {
+                    title = $A.formatDate('H:i', startdate) + " " + title;
+                }
+                //
+                if (taskData.complete) {
+                    title = '<span style="text-decoration:line-through">' + title + '</span>';
+                }
+                let color = '#D8F8F2';
+                if (taskData.level === 1) {
+                    color = 'rgba(248, 14, 21, 0.6)';
+                }else if (taskData.level === 2) {
+                    color = 'rgba(236, 196, 2, 0.5)';
+                }else if (taskData.level === 3) {
+                    color = 'rgba(0, 159, 227, 0.7)';
+                }else if (taskData.level === 4) {
+                    color = 'rgba(121, 170, 28, 0.7)';
+                }
+                return {
+                    "id": taskData.id,
+                    "start": $A.formatDate('Y-m-d H:i:s', startdate),
+                    "end": $A.formatDate('Y-m-d H:i:s', enddate),
+                    "title": title,
+                    "color": color,
+                    "avatar": taskData.userimg,
+                    "name": taskData.nickname || taskData.username
+                };
+            }
         }
     }
 </script>

+ 55 - 1
resources/assets/js/main/components/project/todo/complete.vue

@@ -18,6 +18,8 @@
 </style>
 <script>
     import DrawerTabsContainer from "../../DrawerTabsContainer";
+    import Task from "../../../mixins/task";
+
     export default {
         name: 'TodoComplete',
         components: {DrawerTabsContainer},
@@ -27,6 +29,9 @@
                 default: true
             },
         },
+        mixins: [
+            Task
+        ],
         data () {
             return {
                 loadYet: false,
@@ -46,6 +51,55 @@
                 "title": "任务名称",
                 "key": 'title',
                 "minWidth": 100,
+                render: (h, params) => {
+                    return h('div', [
+                        h('Icon', {
+                            props: { type: params.row.complete ? 'md-checkbox-outline' : 'md-square-outline' },
+                            style: {marginRight: '4px', cursor: 'pointer', fontSize: '15px'},
+                            on: {
+                                click: () => {
+                                    this.taskComplete(params.row, !params.row.complete, (detail) => {
+                                        this.$emit("change", detail.complete ? 'complete' : 'unfinished', detail);
+                                    });
+                                }
+                            }
+                        }),
+                        h('span', {
+                            style: {cursor: 'pointer'},
+                            on: {
+                                click: () => {
+                                    let taskDetail = params.row;
+                                    this.taskDetail(taskDetail, (act, detail) => {
+                                        for (let key in detail) {
+                                            if (detail.hasOwnProperty(key)) {
+                                                this.$set(taskDetail, key, detail[key])
+                                            }
+                                        }
+                                        //
+                                        switch (act) {
+                                            case "username":    // 负责人
+                                            case "delete":      // 删除任务
+                                            case "archived":    // 归档
+                                                this.lists.some((task, i) => {
+                                                    if (task.id == detail.id) {
+                                                        this.lists.splice(i, 1);
+                                                        return true;
+                                                    }
+                                                });
+                                                break;
+
+                                            case "unarchived":  // 取消归档
+                                                this.lists.unshift(detail);
+                                                break;
+                                        }
+                                        //
+                                        this.$emit("change", act, detail);
+                                    });
+                                }
+                            }
+                        }, params.row.title)
+                    ]);
+                }
             }, {
                 "title": "创建人",
                 "key": 'createuser',
@@ -58,7 +112,7 @@
                 "title": "完成时间",
                 "width": 160,
                 render: (h, params) => {
-                    return h('span', $A.formatDate("Y-m-d H:i:s", params.row.complete));
+                    return h('span', $A.formatDate("Y-m-d H:i:s", params.row.completedate));
                 }
             }];
         },

+ 34 - 0
resources/assets/js/main/mixins/task.js

@@ -0,0 +1,34 @@
+export default {
+    methods: {
+        taskComplete(taskDetail, complete, callback = null) {
+            if (taskDetail['loadIng'] === true) {
+                return;
+            }
+            this.$set(taskDetail, 'loadIng', true);
+            this.$set(taskDetail, 'complete', !!complete);
+            $A.aAjax({
+                url: 'project/task/edit',
+                data: {
+                    act: complete ? 'complete' : 'unfinished',
+                    taskid: taskDetail.id,
+                },
+                complete: () => {
+                    this.$set(taskDetail, 'loadIng', false);
+                },
+                error: () => {
+                    this.$set(taskDetail, 'complete', !complete);
+                    alert(this.$L('网络繁忙,请稍后再试!'));
+                },
+                success: (res) => {
+                    if (res.ret === 1) {
+                        this.$Message.success(res.msg);
+                        typeof callback === "function" && callback(res.date);
+                    } else {
+                        this.$set(taskDetail, 'complete', !complete);
+                        this.$Modal.error({title: this.$L('温馨提示'), content: res.msg});
+                    }
+                }
+            });
+        },
+    }
+}

+ 109 - 32
resources/assets/js/main/pages/todo.vue

@@ -51,8 +51,9 @@
                                     @sort="taskSortUpdate"
                                     @remove="taskSortUpdate">
                                     <div v-for="task in taskDatas[index].lists" class="content-li task-draggable" :key="task.id" :class="{complete:task.complete}" @click="openTaskModal(task)">
-                                        <Icon v-if="task.complete" class="task-check" type="md-checkbox-outline" />
-                                        <Icon v-else class="task-check" type="md-square-outline" />
+                                        <Icon v-if="task.complete" class="task-check" type="md-checkbox-outline" @click.stop="taskComplete(task, false)"/>
+                                        <Icon v-else class="task-check" type="md-square-outline" @click.stop="taskComplete(task, true)"/>
+                                        <div v-if="!!task.loadIng" class="task-loading"><w-loading></w-loading></div>
                                         <div v-if="task.overdue" class="task-overdue">[超期]</div>
                                         <div class="task-title">{{task.title}}</div>
                                     </div>
@@ -70,13 +71,13 @@
         <Drawer v-model="todoDrawerShow" width="75%">
             <Tabs v-if="todoDrawerShow" v-model="todoDrawerTab">
                 <TabPane :label="$L('待办日程')" name="calendar">
-                    <todo-calendar :canload="todoDrawerShow && todoDrawerTab == 'calendar'"></todo-calendar>
+                    <todo-calendar :canload="todoDrawerShow && todoDrawerTab == 'calendar'" @change="changeTaskProcess"></todo-calendar>
                 </TabPane>
                 <TabPane :label="$L('已完成的任务')" name="complete">
-                    <todo-complete :canload="todoDrawerShow && todoDrawerTab == 'complete'"></todo-complete>
+                    <todo-complete :canload="todoDrawerShow && todoDrawerTab == 'complete'" @change="changeTaskProcess"></todo-complete>
                 </TabPane>
                 <TabPane :label="$L('我关注的任务')" name="attention">
-                    <todo-attention :canload="todoDrawerShow && todoDrawerTab == 'attention'"></todo-attention>
+                    <todo-attention :canload="todoDrawerShow && todoDrawerTab == 'attention'" @change="changeTaskProcess"></todo-attention>
                 </TabPane>
             </Tabs>
         </Drawer>
@@ -236,7 +237,13 @@
                                     .task-check {
                                         font-size: 16px;
                                         padding-right: 6px;
-                                        padding-top: 2px;
+                                        padding-top: 3px;
+                                    }
+                                    .task-loading {
+                                        width: 15px;
+                                        height: 15px;
+                                        margin-right: 6px;
+                                        margin-top: 3px;
                                     }
                                     .task-overdue {
                                         color: #ff0000;
@@ -303,8 +310,13 @@
     import TodoComplete from "../components/project/todo/complete";
     import TodoAttention from "../components/project/todo/attention";
 
+    import Task from "../mixins/task";
+
     export default {
         components: {draggable, TodoAttention, TodoComplete, TodoCalendar, WContent, WHeader, WLoading},
+        mixins: [
+            Task
+        ],
         data () {
             return {
                 userInfo: {},
@@ -364,12 +376,20 @@
             getTaskLists(index, isNext) {
                 let taskData = this.taskDatas[index];
                 let currentPage = 1;
+                let pagesize = 20;
+                let withNextPage = false;
+                //
                 if (isNext === true) {
                     if (taskData.hasMorePages !== true) {
                         return;
                     }
                     currentPage = Math.max(1, $A.runNum(taskData['currentPage']));
-                    currentPage++;
+                    let tempLists = this.taskDatas[detail.level].lists.filter((item) => { return item.complete == 0; });
+                    if (tempLists.length >= currentPage * pagesize) {
+                        currentPage++;
+                    } else {
+                        withNextPage = true;
+                    }
                 }
                 this.$set(taskData, 'hasMorePages', false);
                 this.$set(taskData, 'loadIng', $A.runNum(taskData.loadIng) + 1);
@@ -380,7 +400,7 @@
                         level: index,
                         sorts: {key:'userorder', order:'desc'},
                         page: currentPage,
-                        pagesize: 20,
+                        pagesize: pagesize,
                     },
                     complete: () => {
                         this.taskSortDisabled = false;
@@ -404,6 +424,9 @@
                             this.taskSortData = this.getTaskSort();
                             this.$set(taskData, 'currentPage', res.data.currentPage);
                             this.$set(taskData, 'hasMorePages', res.data.hasMorePages);
+                            if (res.data.currentPage && withNextPage) {
+                                this.getTaskLists(index, true);
+                            }
                         } else {
                             this.$set(taskData, 'lists', []);
                             this.$set(taskData, 'hasMorePages', false);
@@ -518,9 +541,6 @@
                     success: (res) => {
                         if (res.ret === 1) {
                             this.$Message.success(res.msg);
-                            res.data.forEach((level) => {
-                                this.getTaskLists(level.toString());
-                            });
                         } else {
                             this.refreshTask();
                             this.$Modal.error({title: this.$L('温馨提示'), content: res.msg});
@@ -529,28 +549,85 @@
                 });
             },
 
-            openTaskModal(task) {
-                this.taskDetail(task, (act, detail) => {
-                    for (let key in detail) {
-                        if (detail.hasOwnProperty(key)) {
-                            this.$set(task, key, detail[key])
+            openTaskModal(taskDetail) {
+                this.taskDetail(taskDetail, (act, detail) => {
+                    this.changeTaskProcess(act, detail);
+                });
+            },
+
+            changeTaskProcess(act, detail) {
+                for (let level in this.taskDatas) {
+                    this.taskDatas[level].lists.some((task, i) => {
+                        if (task.id == detail.id) {
+                            for (let key in detail) {
+                                if (detail.hasOwnProperty(key)) {
+                                    this.$set(task, key, detail[key])
+                                }
+                            }
+                            return true;
                         }
-                    }
-                    switch (act) {
-                        case "title": // 标题
-                        case "desc": // 描述
-                        case "level": // 优先级
-                        case "username": // 负责人
-                        case "plannedtime": // 设置计划时间
-                        case "unplannedtime": // 取消计划时间
-                        case "complete": // 标记完成
-                        case "unfinished": // 标记未完成
-                        case "archived": // 归档
-                        case "unarchived": // 取消归档
-                        case "delete": // 删除任务
-                        case "comment": // 评论
-                    }
-                })
+                    });
+                }
+                //
+                switch (act) {
+                    case "title": // 标题
+                    case "desc": // 描述
+                    case "plannedtime": // 设置计划时间
+                    case "unplannedtime": // 取消计划时间
+                    case "complete": // 标记完成
+                    case "unfinished": // 标记未完成
+                    case "comment": // 评论
+                        // 这些都不用处理
+                        break;
+
+                    case "level":       // 优先级
+                        for (let level in this.taskDatas) {
+                            this.taskDatas[level].lists.some((task, i) => {
+                                if (task.id == detail.id) {
+                                    this.taskDatas[level].lists.splice(i, 1);
+                                    return true;
+                                }
+                            });
+                            if (level == detail.level) {
+                                this.taskDatas[level].lists.some((task, i) => {
+                                    if (detail.userorder > task.userorder || (detail.userorder == task.userorder && detail.id > task.id)) {
+                                        this.taskDatas[level].lists.splice(i, 0, detail);
+                                        return true;
+                                    }
+                                });
+                            }
+                        }
+                        this.taskSortData = this.getTaskSort();
+                        break;
+
+                    case "username":    // 负责人
+                    case "delete":      // 删除任务
+                    case "archived":    // 归档
+                        for (let level in this.taskDatas) {
+                            this.taskDatas[level].lists.some((task, i) => {
+                                if (task.id == detail.id) {
+                                    this.taskDatas[level].lists.splice(i, 1);
+                                    return true;
+                                }
+                            });
+                        }
+                        this.taskSortData = this.getTaskSort();
+                        break;
+
+                    case "unarchived":  // 取消归档
+                        for (let level in this.taskDatas) {
+                            if (level == detail.level) {
+                                this.taskDatas[level].lists.some((task, i) => {
+                                    if (detail.userorder > task.userorder || (detail.userorder == task.userorder && detail.id > task.id)) {
+                                        this.taskDatas[level].lists.splice(i, 0, detail);
+                                        return true;
+                                    }
+                                });
+                            }
+                        }
+                        this.taskSortData = this.getTaskSort();
+                        break;
+                }
             }
         },
     }