kuaifan 5 лет назад
Родитель
Сommit
55113f72d5

+ 1 - 1
app/Http/Controllers/Api/UsersController.php

@@ -193,7 +193,7 @@ class UsersController extends Controller
             ->whereRaw($whereRaw)
             ->orderBy('id')
             ->cacheMinutes(now()->addSeconds(10))
-            ->take(Min(Max(Base::nullShow(Request::input('take'), 10), 1), 100))
+            ->take(Base::getPaginate(100, 10, 'take'))
             ->get();
         foreach ($lists AS $key => $item) {
             $lists[$key]['userimg'] = Users::userimg($item['userimg']);

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
     "name": "wookteam",
-    "version": "1.5.3",
+    "version": "1.5.4",
     "description": "WookTeam是一款轻量级的开源在线团队协作工具,提供各类文档工具、在线思维导图、在线流程图、项目管理、任务分发、即时IM,知识库管理等工具。",
     "scripts": {
         "ide-helper": "php artisan ide-helper:generate",

+ 2 - 2
resources/assets/js/_components/scrollerX.vue

@@ -8,7 +8,7 @@
 
 <script>
     export default {
-        name: 'x-scroller',
+        name: 'ScrollerX',
 
         props: ['effect'],
 
@@ -104,7 +104,7 @@
         background: linear-gradient(to right, rgba(255, 255, 255, 0), rgba(255, 255, 255, 0.8), #ffffff);
         pointer-events: none
     }
-    
+
     .scroller-uneffect {
         &:before,
         &:after {

+ 2 - 2
resources/assets/js/main/components/ImgUpload.vue

@@ -41,7 +41,7 @@
                 </div>
             </div>
         </div>
-        <Modal :title="$L('浏览图片空间的图片')" v-model="browseVisible" class="img-upload-modal" width="710" :styles="{top: '35px',paddingBottom: '35px'}">
+        <Modal :title="$L('浏览图片空间的图片')" v-model="browseVisible" class="img-upload-modal" class-name="simple-modal" width="710" :styles="{top: '35px',paddingBottom: '35px'}">
             <div class="browse-load" v-if="isLoading">{{$L('加载中...')}}</div>
             <div class="browse-list" :class="httpType==='input'?'browse-list-disabled':''" ref="browselistbox">
                 <div class="browse-item" v-for="item in browseList" @click="browseItem(item)">
@@ -64,7 +64,7 @@
                 <Button v-if="httpType===''" type="primary" @click="handleCallback(true)">{{$L('完成')}}</Button>
             </div>
         </Modal>
-        <Modal :title="$L('查看图片')" v-model="visible" class="img-upload-modal" draggable>
+        <Modal :title="$L('查看图片')" v-model="visible" class="img-upload-modal" class-name="simple-modal" draggable>
             <div style="max-height:480px;overflow:auto;">
                 <a :href="imgVisible" target="_blank"><img :src="imgVisible" v-if="visible" style="max-width:100%;max-height:900px;display:block;margin:0 auto"></a>
             </div>

+ 1 - 1
resources/assets/js/main/components/MDEditor/index.vue

@@ -9,7 +9,7 @@
                 <MarkdownPro ref="md2" v-if="transfer" v-model="content" :toolbars="toolbars" :is-custom-fullscreen="transfer" height="100%" @on-custom="customClick"></MarkdownPro>
             </div>
         </Modal>
-        <Modal v-model="html2md" title="html转markdown" okText="转换成markdown" width="680" @on-ok="htmlOk" transfer>
+        <Modal v-model="html2md" title="html转markdown" okText="转换成markdown" width="680" class-name="simple-modal" @on-ok="htmlOk" transfer>
             <Input type="textarea" v-model="htmlValue" :rows="14" placeholder="请输入html代码..." />
         </Modal>
     </div>

+ 127 - 39
resources/assets/js/main/components/UserInput.vue

@@ -1,9 +1,13 @@
 <template>
     <div v-clickoutside="handleClose" @click="handleClose">
+        <!--已选列表-->
         <div v-if="multipleLists.length > 0" class="user-id-multiple">
-            <Tag v-for="(item, index) in multipleLists" :key="index" @on-close="() => { multipleLists.splice(index, 1); callMultipleLists() }" closable><UserView :username="item.username"/></Tag>
+            <div v-for="(item, index) in multipleLists" :key="index" class="user-id-multiple-item">
+                <Tag @on-close="multipleLists.splice(index,1)" :closable="!existMultipleDisabled(item.username)"><UserView :username="item.username" showimg/></Tag>
+            </div>
         </div>
 
+        <!--输入框区域-->
         <div class="user-id-input" ref="reference">
             <Input v-model="nickName" :placeholder="placeholder" :disabled="disabled" icon="md-search" @on-click="searchEnter" @on-enter="searchEnter" @on-blur="searchEnter(true)">
                 <div v-if="$slots.prepend !== undefined" slot="prepend"><slot name="prepend"></slot></div>
@@ -13,6 +17,7 @@
             <div v-if="spinShow" class="user-id-spin"><div><w-loading></w-loading></div></div>
         </div>
 
+        <!--弹出选择表-->
         <transition name="fade">
             <div
                 v-show="!disabled && visible"
@@ -22,12 +27,14 @@
                 v-transfer-dom>
                 <Table highlight-row
                        v-if="searchShow"
+                       ref="myTable"
                        size="small"
                        class="user-id-input-table"
                        :style="tableStyle"
                        :columns="columns"
                        :data="userLists"
                        @on-row-click="userChange"
+                       @on-selection-change="userSelect"
                        :no-data-text="noDataText"></Table>
             </div>
         </transition>
@@ -35,9 +42,20 @@
 </template>
 <style lang="scss" scoped>
     .user-id-multiple {
-        margin-bottom: 4px;
+        margin-bottom: 6px;
         overflow: auto;
         white-space: normal;
+        .ivu-tag {
+            padding-left: 7px;
+        }
+        .user-id-multiple-item {
+            display: inline-block;
+        }
+        .user-view-inline {
+            height: 20px;
+            line-height: 20px;
+            vertical-align: top;
+        }
     }
     .user-id-input {
         display: inline-block;
@@ -100,6 +118,14 @@
         .ivu-table-small td {
             cursor: pointer;
         }
+
+        .ivu-table-cell {
+            padding-left: 12px;
+            padding-right: 12px;
+            &.ivu-table-cell-with-selection {
+                padding-right: 2px;
+            }
+        }
     }
 </style>
 <script>
@@ -156,6 +182,9 @@
             multiple: {
                 type: Boolean,
                 default: false
+            },
+            multipleDisabled: {
+                default: ''
             }
         },
         data () {
@@ -180,35 +209,46 @@
         created() {
             this.columns = [
                 {
+                    "title": this.$L("头像"),
+                    "width": 60,
+                    "align": 'center',
+                    render: (h, params) => {
+                        return h('UserImg', {
+                            props: {
+                                info: params.row,
+                            },
+                            style: {
+                                width: "28px",
+                                height: "28px",
+                                fontSize: "14px",
+                                lineHeight: "28px",
+                                borderRadius: "14px",
+                                verticalAlign: "middle"
+                            },
+                        });
+                    }
+                }, {
                     "title": this.$L("用户名"),
                     "key": "username",
                     "minWidth": 80,
                     "ellipsis": true,
-                    "tooltip": true,
-                    render: (h, params) => {
-                        let arr = [];
-                        let username = params.row.username;
-                        let mLists = this.multipleLists.filter((item) => { return item.username == username; });
-                        if (mLists.length > 0) {
-                            arr.push(h('Icon', {
-                                props: { type: 'md-checkmark' },
-                                style: { marginRight: '6px', fontSize: '16px', color: '#FF5722' },
-                            }));
-                        }
-                        arr.push(h('span', username));
-                        return h('div', arr);
-                    }
                 }, {
                     "title": this.$L("昵称"),
                     "key": "nickname",
                     "minWidth": 80,
                     "ellipsis": true,
-                    "tooltip": true,
                     render: (h, params) => {
                         return h('span', params.row.nickname || '-');
                     }
                 }
             ];
+            if (this.multiple) {
+                this.columns.unshift({
+                    type: 'selection',
+                    width: 30,
+                    align: 'center'
+                });
+            }
             this.noDataText = this.$L("数据加载中.....");
         },
         watch: {
@@ -247,10 +287,10 @@
                         }
                         this.noDataText = this.$L("数据加载中.....");
                         $A.apiAjax({
-                            url: window.location.origin + '/api/users/searchinfo',
+                            url: 'users/searchinfo',
                             data: {
                                 where: where,
-                                pagesize: 1
+                                take: 1
                             },
                             beforeSend: () => {
                                 this.spinShow = true;
@@ -303,9 +343,20 @@
             searchShow(val) {
                 if (val) {
                     this.handleShowPopper();
+                    this.updateMultipleLists();
                 } else {
                     this.handleClosePopper();
                 }
+            },
+
+            multipleLists: {
+                handler() {
+                    if (this.searchShow) {
+                        this.updateMultipleLists();
+                    }
+                    this.emitMultipleLists();
+                },
+                deep: true
             }
         },
         computed: {
@@ -386,10 +437,10 @@
                 }
                 this.noDataText = this.$L("数据加载中.....");
                 $A.apiAjax({
-                    url: window.location.origin + '/api/users/searchinfo',
+                    url: 'users/searchinfo',
                     data: {
                         where: where,
-                        pagesize: 30
+                        take: 30
                     },
                     beforeSend: () => {
                         this.spinShow = true;
@@ -404,11 +455,18 @@
                     success: (res) => {
                         if (res.ret === 1) {
                             this.userLists = res.data;
-                            for (let i = 0; i < this.userLists.length; i++) {
-                                if (this.userLists[i].id == this.userName) {
-                                    this.userLists[i]['_highlight'] = true;
+                            this.userLists.forEach((item) => {
+                                if (this.multiple) {
+                                    if (this.existMultipleDisabled(item.username)) {
+                                        item._disabled = true;
+                                    }
+                                } else {
+                                    if (item.username == this.userName) {
+                                        item._highlight = true;
+                                    }
+
                                 }
-                            }
+                            });
                             this.searchShow = true;
                         } else {
                             this.$Message.warning(res.msg);
@@ -420,9 +478,12 @@
 
             userChange(item) {
                 if (this.multiple) {
-                    let tempLists = this.multipleLists.filter((res) => { return res.username == item.username });
+                    if (this.existMultipleDisabled(item.username)) {
+                        return;
+                    }
+                    let tempLists = this.multipleLists.filter(({username}) => username == item.username);
                     if (tempLists.length > 0) {
-                        this.multipleLists = this.multipleLists.filter((res) => { return res.username != item.username });
+                        this.multipleLists = this.multipleLists.filter(({username}) => username != item.username);
                     } else {
                         this.addMultipleLists(item);
                     }
@@ -438,6 +499,23 @@
                 }
             },
 
+            userSelect() {
+                if (this.multiple) {
+                    let lists = this.$refs.myTable.objData, item, inThe;
+                    for (let index in lists) {
+                        if (lists.hasOwnProperty(index)) {
+                            item = lists[index];
+                            inThe = this.multipleLists.find(({username}) => username == item.username);
+                            if (item._isChecked) {
+                                !inThe && this.multipleLists.push(item);
+                            } else {
+                                inThe && (this.multipleLists = this.multipleLists.filter(({username}) => username != item.username));
+                            }
+                        }
+                    }
+                }
+            },
+
             handleClose(e) {
                 if (this.multiple && $A(e.target).parents('.user-id-input-table').length > 0) {
                     return;
@@ -447,20 +525,31 @@
                 }
             },
 
+            existMultipleDisabled(username) {
+                return this.multipleDisabled && $A.strExists(`,${this.multipleDisabled},`, `,${username},`)
+            },
+
             addMultipleLists(item) {
-                let inn = false;
-                this.multipleLists.some((tmp) => {
-                    if (tmp.username == item.username) {
-                        return inn = true;
-                    }
-                })
-                if (!inn) {
+                let inThe = this.multipleLists.find(({username}) => username == item.username);
+                if (!inThe) {
                     this.multipleLists.push(item);
-                    this.callMultipleLists();
                 }
             },
 
-            callMultipleLists() {
+            updateMultipleLists() {
+                this.$nextTick(() => {
+                    let lists = this.$refs.myTable.objData, item, inThe;
+                    for (let index in lists) {
+                        if (lists.hasOwnProperty(index)) {
+                            item = lists[index];
+                            inThe = this.multipleLists.find(({username}) => username == item.username);
+                            this.$set(item, "_isChecked", !!inThe);
+                        }
+                    }
+                });
+            },
+
+            emitMultipleLists() {
                 let val = '';
                 this.multipleLists.forEach((tmp) => {
                     if (val) {
@@ -495,11 +584,10 @@
         mounted() {
             this.updatePopper();
             //
-            if ($A.count(this.value) > 0) {
-                this.userName = this.value;
-            }
             if (this.multiple) {
                 this.multipleLists = this.formatMultipleLists(this.value);
+            } else if ($A.count(this.value) > 0) {
+                this.userName = this.value;
             }
         }
     };

+ 77 - 11
resources/assets/js/main/components/UserView.vue

@@ -1,18 +1,61 @@
 <template>
     <div class="user-view-inline">
-        <Tooltip :disabled="loadIng" :delay="delay" :transfer="transfer" :placement="placement" @on-popper-show="popperShow">
-            {{nickname || username}}
-            <div slot="content">
-                <div>{{$L('用户名')}}: {{username}}</div>
-                <div>{{$L('职位/职称')}}: {{profession || '-'}}</div>
-            </div>
-        </Tooltip>
+        <div class="user-view-info">
+            <UserImg v-if="showimg" class="user-view-img" :info="userInfo"/>
+            <Tooltip :disabled="loadIng" :delay="delay" :transfer="transfer" :placement="placement" maxWidth="auto" @on-popper-show="popperShow">
+                {{nickname || username}}
+                <div slot="content" style="white-space:normal">
+                    <div>{{$L('用户名')}}: {{username}}</div>
+                    <div>{{$L('职位/职称')}}: {{profession || '-'}}</div>
+                </div>
+            </Tooltip>
+        </div>
     </div>
 </template>
 
+<style lang="scss">
+    .user-view-inline {
+        .user-view-info {
+            .ivu-tooltip {
+                max-width: 100%;
+                display: flex;
+                flex-direction: column;
+                justify-content: center;
+                .ivu-tooltip-rel {
+                    max-width: 100%;
+                    white-space: nowrap;
+                    overflow: hidden;
+                    text-overflow: ellipsis;
+                }
+            }
+            .user-view-img {
+                .usertext-container-text {
+                    transform: scale(0.86);
+                }
+            }
+        }
+    }
+</style>
 <style lang="scss" scoped>
     .user-view-inline {
         display: inline-block;
+        max-width: 100%;
+        .user-view-info {
+            display: flex;
+            align-items: center;
+            .user-view-img {
+                width: 16px;
+                height: 16px;
+                font-size: 12px;
+                line-height: 16px;
+                border-radius: 50%;
+                margin-right: 3px;
+            }
+            .user-view-title {
+                flex: 1;
+                line-height: 1.2;
+            }
+        }
     }
 </style>
 
@@ -34,6 +77,10 @@
             placement: {
                 default: 'bottom'
             },
+            showimg: {
+                type: Boolean,
+                default: false
+            },
             info: {
                 default: null
             },
@@ -43,6 +90,7 @@
                 loadIng: true,
 
                 nickname: null,
+                userimg: '',
                 profession: ''
             }
         },
@@ -53,12 +101,31 @@
             username() {
                 this.getUserData(300);
             },
+            info: {
+                handler() {
+                    this.upInfo()
+                },
+                deep: true
+            }
+        },
+        computed: {
+            userInfo() {
+                const {username, nickname, userimg} = this;
+                return {username, nickname, userimg}
+            }
         },
         methods: {
             isJson(obj) {
                 return typeof (obj) == "object" && Object.prototype.toString.call(obj).toLowerCase() == "[object object]" && typeof obj.length == "undefined";
             },
 
+            upInfo() {
+                if (this.isJson(this.info)) {
+                    this.$set(this.info, 'nickname', this.nickname);
+                    this.$set(this.info, 'userimg', this.userimg);
+                }
+            },
+
             popperShow() {
                 this.getUserData(30)
             },
@@ -67,17 +134,16 @@
                 $A.getUserBasic(this.username, (data, success) => {
                     if (success) {
                         this.nickname = data.nickname;
+                        this.userimg = data.userimg;
                         this.profession = data.profession;
                     } else {
                         this.nickname = '';
+                        this.userimg = '';
                         this.profession = '';
                     }
                     this.loadIng = false;
                     this.$emit("on-result", data);
-                    //
-                    if (this.isJson(this.info)) {
-                        this.$set(this.info, 'nickname', this.nickname);
-                    }
+                    this.upInfo();
                 }, cacheTime);
             }
         }

+ 3 - 2
resources/assets/js/main/components/chat/Index.vue

@@ -29,7 +29,7 @@
                         <UserImg :info="dialog" class="avatar"/>
                         <div class="user-msg-box">
                             <div class="user-msg-title">
-                                <span><user-view :username="dialog.username" placement="right" @on-result="userViewResult(dialog, $event)"/></span>
+                                <span><UserView :username="dialog.username" placement="right" @on-result="userViewResult(dialog, $event)"/></span>
                                 <em>{{formatCDate(dialog.lastdate)}}</em>
                             </div>
                             <div class="user-msg-text">{{dialog.lasttext}}</div>
@@ -72,7 +72,7 @@
              @dragover.prevent="messageDragOver(true)"
              @dragleave.prevent="messageDragOver(false)">
             <div class="manage-title">
-                <user-view :username="dialogTarget.username"/>
+                <UserView :username="dialogTarget.username"/>
                 <Dropdown class="manage-title-right" placement="bottom-end" trigger="click" @on-click="dialogDropdown" transfer>
                     <Icon type="ios-more"/>
                     <DropdownMenu slot="list">
@@ -576,6 +576,7 @@
                 padding: 8px 0;
                 display: flex;
                 align-items: center;
+                height: 40px;
                 .quick-item {
                     color: #444444;
                     font-size: 24px;

+ 4 - 4
resources/assets/js/main/components/chat/Message.vue

@@ -9,7 +9,7 @@
                 </div>
                 <div class="item-right">
                     <div class="item-username" @click="clickUser">
-                        <em class="item-name"><user-view :username="userName" :info="info" placement="left"/></em>
+                        <em class="item-name"><UserView :username="userName" :info="info" placement="left"/></em>
                         <em v-if="info.indate" class="item-date">{{formatCDate(info.indate)}}</em>
                     </div>
                     <div class="item-text" :class="{'text-emoji':textIsEmoji(info.text), 'text-error':info.error}">
@@ -32,7 +32,7 @@
                 <UserImg :info="info" @click="clickUser" class="item-userimg"/>
                 <div class="item-left">
                     <div class="item-username" @click="clickUser">
-                        <em class="item-name"><user-view :username="userName" :info="info" placement="right"/></em>
+                        <em class="item-name"><UserView :username="userName" :info="info" placement="right"/></em>
                         <em v-if="info.__usertag" class="item-tag">{{info.__usertag}}</em>
                         <em v-if="info.indate" class="item-date">{{formatCDate(info.indate)}}</em>
                     </div>
@@ -61,7 +61,7 @@
                 </div>
                 <div class="item-right">
                     <div class="item-username" @click="clickUser">
-                        <em class="item-name"><user-view :username="userName" :info="info" placement="left"/></em>
+                        <em class="item-name"><UserView :username="userName" :info="info" placement="left"/></em>
                         <em v-if="info.indate" class="item-date">{{formatCDate(info.indate)}}</em>
                     </div>
                     <div v-if="info.url==='loading'" class="item-loading">
@@ -84,7 +84,7 @@
                 <UserImg :info="info" @click="clickUser" class="item-userimg"/>
                 <div class="item-left">
                     <div class="item-username" @click="clickUser">
-                        <em class="item-name"><user-view :username="userName" :info="info" placement="right"/></em>
+                        <em class="item-name"><UserView :username="userName" :info="info" placement="right"/></em>
                         <em v-if="info.__usertag" class="item-tag">{{info.__usertag}}</em>
                         <em v-if="info.indate" class="item-date">{{formatCDate(info.indate)}}</em>
                     </div>

+ 1 - 0
resources/assets/js/main/components/docs/users.vue

@@ -64,6 +64,7 @@
                             fontSize: "16px",
                             lineHeight: "30px",
                             borderRadius: "15px",
+                            verticalAlign: "middle"
                         },
                     });
                 }

+ 9 - 1
resources/assets/js/main/components/project/task/detail/detail.vue

@@ -55,7 +55,7 @@
                     <li v-if="followerLength(detail.follower) > 0" class="text-follower detail-icon">
                         <span>{{$L('关注者:')}}</span>
                         <em>
-                            <Tag v-for="(fname, findex) in detail.follower" :key="findex" closable @on-close="handleTask('unattention', {username:fname,uisynch:true})"><UserView :username="fname"/></Tag>
+                            <Tag v-for="(fname, findex) in detail.follower" :key="findex" closable @on-close="handleTask('unattention', {username:fname,uisynch:true})"><UserView :username="fname" showimg/></Tag>
                         </em>
                     </li>
                     <li class="text-level detail-icon">
@@ -1083,6 +1083,14 @@
                             &:before {
                                 content: "\E90D";
                             }
+                            .ivu-tag {
+                                padding: 0 6px;
+                            }
+                            .user-view-inline {
+                                height: 20px;
+                                line-height: 20px;
+                                vertical-align: top;
+                            }
                         }
                         &.text-level {
                             &:before {

+ 1 - 0
resources/assets/js/main/components/project/users.vue

@@ -64,6 +64,7 @@
                             fontSize: "16px",
                             lineHeight: "30px",
                             borderRadius: "15px",
+                            verticalAlign: "middle"
                         },
                     });
                 }

+ 1 - 1
resources/assets/js/main/components/report/add.vue

@@ -12,7 +12,7 @@
             </div>
             <t-editor class="add-edit" v-model="dataDetail.content" height="100%"></t-editor>
             <div class="add-input">
-                <user-input v-model="dataDetail.ccuser" :nousername="$A.getUserName()" :placeholder="$L('输入关键词搜索')" multiple><span slot="prepend">{{$L('抄送人')}}</span></user-input>
+                <UserInput v-model="dataDetail.ccuser" :nousername="$A.getUserName()" :placeholder="$L('输入关键词搜索')" multiple><span slot="prepend">{{$L('抄送人')}}</span></UserInput>
             </div>
             <div class="add-footer">
                 <Button :loading="loadIng > 0" type="primary" @click="handleSubmit" style="margin-right:6px">{{$L('保存')}}</Button>

+ 4 - 2
resources/assets/js/main/pages/docs.vue

@@ -57,7 +57,8 @@
             v-model="addBookShow"
             :title="$L(addBookId > 0 ? '修改标题' : '新建知识库')"
             :closable="false"
-            :mask-closable="false">
+            :mask-closable="false"
+            class-name="simple-modal">
             <Form ref="bookAdd" :model="formBookAdd" :rules="ruleBookAdd" :label-width="110">
                 <FormItem prop="title" :label="$L('知识库名称')" style="margin-right:28px">
                     <Input type="text" v-model="formBookAdd.title" :maxlength="32"></Input>
@@ -73,7 +74,8 @@
             v-model="addSectionShow"
             :title="$L(addSectionId > 0 ? '修改文档标题' : '新建文档')"
             :closable="false"
-            :mask-closable="false">
+            :mask-closable="false"
+            class-name="simple-modal">
             <Form ref="sectionAdd" :model="formSectionAdd" :rules="ruleSectionAdd" :label-width="110">
                 <FormItem prop="title" :label="$L('文档标题')" style="margin-right:28px">
                     <Input type="text" v-model="formSectionAdd.title" :maxlength="32"></Input>

+ 12 - 2
resources/assets/js/main/pages/docs/edit.vue

@@ -25,8 +25,7 @@
                     <ul class="synch-lists" slot="content">
                         <li class="title">{{$L('正在协作会员')}}:</li>
                         <li v-for="(item, key) in synchUsersS" :key="key" @click="handleSynch(item.username)">
-                            <UserImg :info="item" class="synch-userimg"/>
-                            <UserView class="synch-username" placement="right" :username="item.username"/>
+                            <UserView class="synch-username" placement="right" :username="item.username" showimg/>
                             <span v-if="item.username==userInfo.username" class="synch-self">{{$L('自己')}}</span>
                         </li>
                     </ul>
@@ -98,6 +97,17 @@
 
 <style lang="scss">
     .docs-edit {
+        .edit-box {
+            .synch-username {
+                .user-view-img {
+                    width: 24px;
+                    height: 24px;
+                    font-size: 14px;
+                    line-height: 24px;
+                    border-radius: 12px;
+                }
+            }
+        }
         .body-text {
             .mdeditor-box {
                 position: relative;

+ 2 - 1
resources/assets/js/main/pages/index.vue

@@ -94,7 +94,8 @@
 
         <Modal
             v-model="loginShow"
-            :mask-closable="false">
+            :mask-closable="false"
+            class-name="simple-modal">
             <Form ref="login" :model="formLogin" :rules="ruleLogin">
                 <FormItem prop="username">
                     <Input type="text" v-model="formLogin.username" :placeholder="$L('用户名')" @on-enter="onLogin">

+ 2 - 1
resources/assets/js/main/pages/project.vue

@@ -75,7 +75,8 @@
             v-model="addShow"
             :title="$L('新建项目')"
             :closable="false"
-            :mask-closable="false">
+            :mask-closable="false"
+            class-name="simple-modal">
             <Form ref="add" :model="formAdd" :rules="ruleAdd" :label-width="80">
                 <FormItem prop="title" :label="$L('项目名称')">
                     <Input type="text" v-model="formAdd.title"></Input>

+ 48 - 39
resources/assets/js/main/pages/project/panel.vue

@@ -37,7 +37,7 @@
                     v-for="label in projectLabel"
                     :key="label.id"
                     class="label-item label-draggable"
-                    :class="{'label-scroll': label.hasScroll === true && label.getFocus !== true}"
+                    :class="{'label-scroll': label.hasScroll === true && label.endScroll !== true}"
                     @mouseenter="projectMouse(label)">
                     <div class="label-body">
                         <div class="title-box">
@@ -54,45 +54,46 @@
                                 </DropdownMenu>
                             </Dropdown>
                         </div>
-                        <draggable
-                            v-model="label.taskLists"
-                            :ref="'draggable_' + label.id"
+                        <ScrollerY
+                            :ref="'box_' + label.id"
                             class="task-box"
-                            group="task"
-                            draggable=".task-draggable"
-                            :animation="150"
-                            :disabled="projectSortDisabled"
-                            @sort="projectSortUpdate(false)"
-                            @remove="projectSortUpdate(false)">
-                            <div v-for="task in label.taskLists" :key="task.id" class="task-item task-draggable">
-                                <div class="task-shadow" :class="[
+                            @on-scroll="projectBoxScroll($event, label)">
+                            <draggable
+                                v-model="label.taskLists"
+                                class="task-main"
+                                group="task"
+                                draggable=".task-draggable"
+                                :animation="150"
+                                :disabled="projectSortDisabled"
+                                @sort="projectSortUpdate(false)"
+                                @remove="projectSortUpdate(false)">
+                                <div v-for="task in label.taskLists" :key="task.id" class="task-item task-draggable">
+                                    <div class="task-shadow" :class="[
                                         'p'+task.level,
                                         task.complete ? 'complete' : '',
                                         task.overdue ? 'overdue' : '',
                                         task.isNewtask === true ? 'newtask' : ''
                                     ]" @click="openTaskModal(task)">
-                                    <div v-if="task.subtask.length > 0" class="subtask-progress"><em :style="{width: subtaskProgress(task.subtask) + '%'}"></em></div>
-                                    <div class="task-title">{{task.title}}<Icon v-if="task.desc" type="ios-list-box-outline" /></div>
-                                    <div class="task-more">
-                                        <div v-if="task.overdue" class="task-status">{{$L('已超期')}}</div>
-                                        <div v-else-if="task.complete" class="task-status">{{$L('已完成')}}</div>
-                                        <div v-else class="task-status">{{$L('未完成')}}</div>
-                                        <Tooltip class="task-userimg" :content="task.nickname || task.username" transfer>
-                                            <UserImg :info="task" class="avatar"/>
-                                        </Tooltip>
+                                        <div v-if="task.subtask.length > 0" class="subtask-progress"><em :style="{width: subtaskProgress(task.subtask) + '%'}"></em></div>
+                                        <div class="task-title">{{task.title}}<Icon v-if="task.desc" type="ios-list-box-outline" /></div>
+                                        <div class="task-more">
+                                            <div v-if="task.overdue" class="task-status">{{$L('已超期')}}</div>
+                                            <div v-else-if="task.complete" class="task-status">{{$L('已完成')}}</div>
+                                            <div v-else class="task-status">{{$L('未完成')}}</div>
+                                            <Tooltip class="task-userimg" :content="task.nickname || task.username" transfer><UserImg :info="task" class="avatar"/></Tooltip>
+                                        </div>
                                     </div>
                                 </div>
-                            </div>
-                            <div slot="footer">
-                                <project-add-task
-                                    :ref="'add_' + label.id"
-                                    :placeholder='`${$L("添加任务至")}"${label.title}"`'
-                                    :projectid="label.projectid"
-                                    :labelid="label.id"
-                                    @on-focus="(f)=>{$set(label,'getFocus',f)}"
-                                    @on-add-success="addTaskSuccess($event, label)"></project-add-task>
-                            </div>
-                        </draggable>
+                                <div slot="footer">
+                                    <project-add-task
+                                        :ref="'add_' + label.id"
+                                        :placeholder='`${$L("添加任务至")}"${label.title}"`'
+                                        :projectid="label.projectid"
+                                        :labelid="label.id"
+                                        @on-add-success="addTaskSuccess($event, label)"></project-add-task>
+                                </div>
+                            </draggable>
+                        </ScrollerY>
                     </div>
                     <div class="label-bottom" @click="projectFocus(label)">
                         <Icon class="label-bottom-icon" type="ios-add" />
@@ -192,7 +193,7 @@
                 &.label-scroll {
                     &:hover {
                         .label-bottom {
-                            transform: translate(0, 0);
+                            transform: translate(-50%, 0);
                         }
                     }
                 }
@@ -238,13 +239,15 @@
                         }
                     }
                     .task-box {
+                        position: relative;
                         flex: 1;
                         width: 100%;
-                        overflow: auto;
-                        display: flex;
-                        flex-direction: column;
                         padding: 0 12px 2px;
                         transform: translateZ(0);
+                        .task-main {
+                            display: flex;
+                            flex-direction: column;
+                        }
                         .task-item {
                             width: 100%;
                             .task-shadow {
@@ -364,8 +367,8 @@
                 }
                 .label-bottom {
                     position: absolute;
+                    left: 50%;
                     bottom: 14px;
-                    right: 28px;
                     z-index: 1;
                     width: 36px;
                     height: 36px;
@@ -375,7 +378,7 @@
                     align-items: center;
                     justify-content: center;
                     transition: transform 0.2s;
-                    transform: translate(0, 200%);
+                    transform: translate(-50%, 200%);
                     cursor: pointer;
                     .label-bottom-icon {
                         color: #ffffff;
@@ -400,9 +403,11 @@
     import WDrawer from "../../components/iview/WDrawer";
     import ProjectGantt from "../../components/project/gantt/index";
     import ProjectSetting from "../../components/project/setting";
+    import ScrollerY from "../../../_components/ScrollerY";
 
     export default {
         components: {
+            ScrollerY,
             ProjectSetting,
             ProjectGantt,
             WDrawer,
@@ -890,7 +895,7 @@
 
             projectMouse(label) {
                 let hasScroll = false;
-                let el = this.$refs['draggable_' + label.id]
+                let el = this.$refs['box_' + label.id]
                 if (el && el.length > 0) {
                     el = el[0].$el;
                     hasScroll = el.scrollHeight > el.offsetHeight;
@@ -898,6 +903,10 @@
                 this.$set(label, 'hasScroll', hasScroll)
             },
 
+            projectBoxScroll(e, label) {
+                this.$set(label, 'endScroll', e.scrollE < 50)
+            },
+
             projectFocus(label) {
                 let el = this.$refs['add_' + label.id];
                 if (el && el.length > 0) {

+ 3 - 1
resources/assets/js/main/pages/team.vue

@@ -34,7 +34,8 @@
             v-model="addShow"
             :title="$L('添加团队成员')"
             :closable="false"
-            :mask-closable="false">
+            :mask-closable="false"
+            class-name="simple-modal">
             <Form ref="add" :model="formAdd" :rules="ruleAdd" :label-width="80">
                 <FormItem prop="userimg" :label="$L('头像')">
                     <ImgUpload v-model="formAdd.userimg"></ImgUpload>
@@ -134,6 +135,7 @@
                             fontSize: "16px",
                             lineHeight: "30px",
                             borderRadius: "15px",
+                            verticalAlign: "middle"
                         },
                     });
                 }

+ 9 - 0
resources/assets/sass/main.scss

@@ -291,3 +291,12 @@
         }
     }
 }
+
+.ivu-modal-wrap {
+    &.simple-modal {
+        .ivu-modal-header,
+        .ivu-modal-footer {
+            border-color: transparent;
+        }
+    }
+}