浏览代码

no message

kuaifan 5 年之前
父节点
当前提交
a64e475759
共有 31 个文件被更改,包括 491 次插入88 次删除
  1. 4 0
      app/Http/Controllers/Api/DocsController.php
  2. 20 0
      app/Http/Controllers/Api/ProjectController.php
  3. 29 0
      app/Http/Controllers/Api/SystemController.php
  4. 11 3
      app/Http/Controllers/Api/UsersController.php
  5. 3 0
      app/Http/Middleware/VerifyCsrfToken.php
  6. 1 1
      app/Module/Base.php
  7. 32 0
      database/migrations/2020_09_03_214153_alter_pre_users_2.php
  8. 32 0
      database/migrations/2020_09_03_214217_alter_pre_docs_content.php
  9. 1 1
      package.json
  10. 3 0
      resources/assets/js/_components/ScrollerY.vue
  11. 11 3
      resources/assets/js/main/components/FullCalendar/FullCalendar.vue
  12. 25 16
      resources/assets/js/main/components/FullCalendar/components/body.vue
  13. 2 2
      resources/assets/js/main/components/FullCalendar/components/dateFunc.js
  14. 3 2
      resources/assets/js/main/components/ImgUpload.vue
  15. 3 1
      resources/assets/js/main/components/MDEditor/README.md
  16. 6 0
      resources/assets/js/main/components/MDEditor/assets/css/index.less
  17. 5 0
      resources/assets/js/main/components/MDEditor/components/pro/index.vue
  18. 3 0
      resources/assets/js/main/components/MDEditor/components/simple/index.vue
  19. 1 0
      resources/assets/js/main/components/MDEditor/config/tools.js
  20. 83 1
      resources/assets/js/main/components/MDEditor/index.vue
  21. 95 2
      resources/assets/js/main/components/TEditor.vue
  22. 15 1
      resources/assets/js/main/components/WHeader.vue
  23. 2 2
      resources/assets/js/main/components/chat/Upload.vue
  24. 1 1
      resources/assets/js/main/components/docs/setting.vue
  25. 57 39
      resources/assets/js/main/components/gantt/index.vue
  26. 4 3
      resources/assets/js/main/components/project/task/files.vue
  27. 11 8
      resources/assets/js/main/components/project/todo/calendar.vue
  28. 1 1
      resources/assets/js/main/pages/docs/edit.vue
  29. 4 0
      resources/assets/js/main/pages/project/panel.vue
  30. 14 0
      resources/assets/js/main/pages/team.vue
  31. 9 1
      resources/lang/en/general.js

+ 4 - 0
app/Http/Controllers/Api/DocsController.php

@@ -593,6 +593,7 @@ class DocsController extends Controller
             return Base::retError(['已被会员【%】锁定!', Users::nickname($row['lockname'])]);
         }
         $content = Base::getPostValue('content');
+        $text = '';
         if ($row['type'] == 'document') {
             $data = Base::json2array($content);
             $isRep = false;
@@ -607,14 +608,17 @@ class DocsController extends Controller
                     $isRep = true;
                 }
             }
+            $text = strip_tags($data['content']);
             if ($isRep == true) {
                 $content = Base::array2json($data);
             }
         }
+        DB::table('docs_content')->where('sid', $id)->update(['text' => '']);
         DB::table('docs_content')->insert([
             'bookid' => $row['bookid'],
             'sid' => $id,
             'content' => $content,
+            'text' => $text,
             'username' => $user['username'],
             'indate' => Base::time()
         ]);

+ 20 - 0
app/Http/Controllers/Api/ProjectController.php

@@ -1289,6 +1289,26 @@ class ProjectController extends Controller
                     $overdue ? '是' : '-',
                     date("Y-m-d H:i:s", $info['indate'])
                 ];
+                $subtask = Base::string2array($info['subtask']);
+                if ($subtask) {
+                    foreach ($subtask AS $subInfo) {
+                        $data[] = [
+                            '',
+                            $subInfo['detail'],
+                            '',
+                            '',
+                            '',
+                            $subInfo['uname'] ?: '',
+                            $subInfo['uname'] ? (DBCache::table('users')->where('username', $subInfo['uname'])->value('nickname') ?: $subInfo['uname']) : $subInfo['uname'],
+                            '',
+                            '',
+                            '',
+                            $subInfo['status'] == 'complete' ? '已完成' : '未完成',
+                            '',
+                            date("Y-m-d H:i:s", $subInfo['time'])
+                        ];
+                    }
+                }
             }
             $res = BillExport::create()->setHeadings($headings)->setData($data)->store($filePath . "/" . $fileName);
             if ($res != 1) {

+ 29 - 0
app/Http/Controllers/Api/SystemController.php

@@ -249,6 +249,35 @@ class SystemController extends Controller
     }
 
     /**
+     * 上传文件
+     */
+    public function fileupload()
+    {
+        if (Users::token2userid() === 0) {
+            return Base::retError('身份失效,等重新登录!');
+        }
+        $path = "uploads/files/" . Users::token2userid() . "/" . date("Ym") . "/";
+        $image64 = trim(Base::getPostValue('image64'));
+        $fileName = trim(Base::getPostValue('filename'));
+        if ($image64) {
+            $data = Base::image64save([
+                "image64" => $image64,
+                "path" => $path,
+                "fileName" => $fileName,
+            ]);
+        } else {
+            $data = Base::upload([
+                "file" => Request::file('files'),
+                "type" => 'file',
+                "path" => $path,
+                "fileName" => $fileName,
+            ]);
+        }
+        //
+        return $data;
+    }
+
+    /**
      * 清理opcache数据
      * @return int
      */

+ 11 - 3
app/Http/Controllers/Api/UsersController.php

@@ -308,7 +308,8 @@ class UsersController extends Controller
         $encrypt = Base::generatePassword(6);
         DB::table('users')->where('id', $user['id'])->update([
             'encrypt' => $encrypt,
-            'userpass' => Base::md52($newpass, $encrypt)
+            'userpass' => Base::md52($newpass, $encrypt),
+            'changepass' => 0
         ]);
         return Base::retSuccess('修改成功');
     }
@@ -375,6 +376,7 @@ class UsersController extends Controller
      * @apiParam {Object} [userimg]             会员头像
      * @apiParam {String} [nickname]            昵称
      * @apiParam {String} [profession]          职位/职称
+     * @apiParam {Number} changepass            登陆是否需要修改密码
      */
     public function team__add()
     {
@@ -406,9 +408,9 @@ class UsersController extends Controller
         $profession = trim(Request::input('profession'));
         if ($profession) {
             if (mb_strlen($profession) < 2) {
-                return Base::retError('称不可以少于2个字!');
+                return Base::retError('职位/职称不可以少于2个字!');
             } elseif (mb_strlen($profession) > 20) {
-                return Base::retError('称最多只能设置20个字!');
+                return Base::retError('职位/职称最多只能设置20个字!');
             }
         }
         //
@@ -418,6 +420,7 @@ class UsersController extends Controller
             'userimg' => $userimg ?: '',
             'nickname' => $nickname ?: '',
             'profession' => $profession ?: '',
+            'changepass' => intval(Request::input('changepass')),
         ];
         if ($id > 0) {
             //开始修改
@@ -436,6 +439,11 @@ class UsersController extends Controller
             return Base::retSuccess('修改成功!');
         } else {
             //开始注册
+            if (strlen($userpass) < 6) {
+                return Base::retError('密码设置不能小于6位数!');
+            } elseif (strlen($userpass) > 32) {
+                return Base::retError('密码最多只能设置32位数!');
+            }
             $username = trim(Request::input('username'));
             $array = array_values(array_filter(array_unique(explode(",", $username))));
             if (empty($array)) {

+ 3 - 0
app/Http/Middleware/VerifyCsrfToken.php

@@ -15,6 +15,9 @@ class VerifyCsrfToken extends Middleware
         //上传图片
         'api/system/imgupload/',
 
+        //上传文件
+        'api/system/fileupload/',
+
         //上传项目文件
         'api/project/files/upload/',
 

+ 1 - 1
app/Module/Base.php

@@ -2085,7 +2085,7 @@ class Base
                     $type = ['zip'];
                     break;
                 case 'file':
-                    $type = ['jpg', 'jpeg', 'png', 'gif', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'esp'];
+                    $type = ['jpg', 'jpeg', 'png', 'gif', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'esp', 'pdf', 'rar', 'zip', 'gz'];
                     break;
                 default:
                     return Base::retError('错误的类型参数');

+ 32 - 0
database/migrations/2020_09_03_214153_alter_pre_users_2.php

@@ -0,0 +1,32 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class AlterPreUsers2 extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::table('users', function (Blueprint $table) {
+            $table->tinyInteger('changepass')->after('loginnum')->nullable()->default(0)->comment('登陆需要修改密码');
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::table('users', function (Blueprint $table) {
+            $table->dropColumn('changepass');
+        });
+    }
+}

+ 32 - 0
database/migrations/2020_09_03_214217_alter_pre_docs_content.php

@@ -0,0 +1,32 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class AlterPreDocsContent extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::table('docs_content', function (Blueprint $table) {
+            $table->text('text')->after('content')->nullable()->comment('内容(主要用于文档类型搜索)');
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::table('docs_content', function (Blueprint $table) {
+            $table->dropColumn('text');
+        });
+    }
+}

+ 1 - 1
package.json

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

+ 3 - 0
resources/assets/js/_components/ScrollerY.vue

@@ -88,6 +88,9 @@
                 } else {
                     $A(this.$refs.scrollerView).stop().animate({"scrollTop": top});
                 }
+            },
+            scrollToBottom(animate) {
+                this.scrollTo(this.$refs.scrollerView.scrollHeight, animate);
             }
         }
     }

+ 11 - 3
resources/assets/js/main/components/FullCalendar/FullCalendar.vue

@@ -150,7 +150,7 @@
             border: 0;
             outline: none;
             box-shadow: unset;
-            background-color: #5272FF;
+            background-color: #2d8cf0;
             color: #fff;
 
             &:hover {
@@ -163,10 +163,18 @@
             display: flex;
             justify-content: flex-end;
 
+            > div {
+                display: flex;
+                align-items: center;
+                border-radius: 6px;
+                overflow: hidden;
+            }
+
             button {
-                width: 80px;
+                min-width: 48px;
                 cursor: pointer;
-                height: 26px;
+                height: 28px;
+                padding: 0 12px;
             }
         }
     }

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

@@ -20,9 +20,12 @@
                             <p class="day-number">{{day.monthDay}}</p>
                             <div class="event-box" v-if="day.events.length">
                                 <div class="event-item"
-                                     :class="{selected: showCard == event.id}"
                                      v-for="event in day.events"
-                                     :style="`background-color: ${showCard == event.id ? (event.selectedColor||'#C7E6FD') : (event.color||'#C7E6FD')}`"
+                                     :class="{selected: showCard == event.id}"
+                                     :style="{
+                                        'color': event.color||'#333333',
+                                        'background-color': showCard == event.id ? (event.selectedColor||'#C7E6FD') : (event.backgroundColor||'#C7E6FD'),
+                                     }"
                                      @click="eventClick(event,$event)">
                                     <img class="avatar" v-if="event.avatar" :src="event.avatar" @error="($set(event, 'avatar', null))"/>
                                     <span v-else class="icon" :style="iconStyle(event.name)">{{event.name}}</span>
@@ -58,9 +61,13 @@
                     <div class="events-day" v-for="item in weekDate" v-if="weekDate.length"
                          :class="{today: item.isToday}">
                         <div class="event-box" v-if="item.events.length">
-                            <div class="event-item" v-for="event in item.events"
+                            <div class="event-item"
+                                 v-for="event in item.events"
                                  :class="{selected: showCard == event.id}"
-                                 :style="`background-color: ${showCard == event.id ? (event.selectedColor||'#C7E6FD') : (event.color||'#C7E6FD')}`"
+                                 :style="{
+                                    'color': event.color||'#333333',
+                                    'background-color': showCard == event.id ? (event.selectedColor||'#C7E6FD') : (event.backgroundColor||'#C7E6FD'),
+                                 }"
                                  v-if="isTheday(item.date, event.start) && isInTime(time, event.start)"
                                  @click="eventClick(event,$event)">
                                 <img class="avatar" v-if="event.avatar" :src="event.avatar" @error="($set(event, 'avatar', null))"/>
@@ -359,7 +366,8 @@
         background-color: #fff;
         display: flex;
         margin-top: 12px;
-        border: 1px solid #D0D9FF;
+        border-radius: 4px;
+        border: 1px solid #EEEEEE;
 
         .left-info {
             width: 60px;
@@ -368,10 +376,6 @@
                 height: 100%;
                 position: relative;
 
-                &.first {
-                    border-top: 1px solid #EFF2FF;
-                }
-
                 &:nth-child(2) {
                     border-top: 1px solid #EFF2FF;
                     border-bottom: 1px solid #EFF2FF;
@@ -404,7 +408,7 @@
 
             .weeks {
                 display: flex;
-                border-bottom: 1px solid #FFCC36;
+                border-bottom: 1px solid #EEEEEE;
 
                 .week {
                     flex: 1;
@@ -413,6 +417,7 @@
                     display: flex;
                     align-items: center;
                     justify-content: center;
+                    font-weight: normal;
                 }
             }
 
@@ -448,7 +453,7 @@
                             }
 
                             &.today {
-                                background-color: #FFFCF3;
+                                background-color: #F9FAFB;
                             }
 
                             &:last-child {
@@ -462,9 +467,9 @@
                                     cursor: pointer;
                                     font-size: 12px;
                                     background-color: #C7E6FD;
-                                    margin-bottom: 2px;
+                                    margin-bottom: 4px;
                                     color: rgba(0, 0, 0, .87);
-                                    padding: 8px 0 8px 4px;
+                                    padding: 6px 0 6px 4px;
                                     height: auto;
                                     line-height: 30px;
                                     display: flex;
@@ -502,6 +507,7 @@
                                         text-align: center;
                                         color: #fff;
                                         display: inline-block;
+                                        overflow: hidden;
                                     }
 
                                     .info {
@@ -661,7 +667,7 @@
                         }
 
                         &.today {
-                            background-color: #FFFCF3;
+                            background-color: #F9FAFB;
                         }
                     }
 
@@ -669,9 +675,9 @@
                         cursor: pointer;
                         font-size: 12px;
                         background-color: #C7E6FD;
-                        margin-bottom: 2px;
+                        margin-bottom: 4px;
                         color: rgba(0, 0, 0, .87);
-                        padding: 8px 0 8px 4px;
+                        padding: 6px 0 6px 4px;
                         height: auto;
                         line-height: 30px;
                         display: flex;
@@ -763,6 +769,9 @@
                     }
 
                     &:last-child {
+                        .events-day {
+                            border-bottom: 0;
+                        }
                         .single {
                             border-bottom: 0;
                         }

+ 2 - 2
resources/assets/js/main/components/FullCalendar/components/dateFunc.js

@@ -50,14 +50,14 @@ let dateFunc = {
         let dt = new Date(date.getFullYear(), date.getMonth() + 1, 1, 0, 0) // 1st day of next month
         return new Date(dt.setDate(dt.getDate() - 1)) // last day of this month
     },
-// 获取当前周日期数组
+    // 获取当前周日期数组
     getDates(date) {
         let new_Date = date
         let timesStamp = new Date(new_Date.getFullYear(), new_Date.getMonth(), new_Date.getDate(), 0, 0, 0).getTime()
         // let timesStamp = new_Date.getTime();
         let currenDay = new_Date.getDay();
         let dates = [];
-        for (let i = 0; i < 8; i++) {
+        for (let i = 0; i < 7; i++) {
             dates.push(new Date(timesStamp + 24 * 60 * 60 * 1000 * (i - (currenDay + 6) % 7)));
         }
         return dates

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

@@ -28,7 +28,7 @@
                             :action="actionUrl"
                             :data="uploadParams"
                             :show-upload-list="false"
-                            :max-size="2048"
+                            :max-size="maxSize"
                             :format="['jpg', 'jpeg', 'gif', 'png']"
                             :default-file-list="defaultList"
                             :on-success="handleSuccess"
@@ -335,6 +335,7 @@
                 maxNum: Math.min(Math.max($A.runNum(this.num), 1), 99),
                 httpValue: '',
                 httpType: '',
+                maxSize: 2048
             }
         },
         mounted () {
@@ -451,7 +452,7 @@
                 //上传大小错误
                 this.$Modal.warning({
                     title: this.$L('超出文件大小限制'),
-                    content: this.$L('文件 % 太大,不能超过2M。', file.name)
+                    content: this.$L('文件 % 太大,不能超过%。', file.name, $A.bytesToSize(this.maxSize * 1024))
                 });
             },
             handleBeforeUpload () {

+ 3 - 1
resources/assets/js/main/components/MDEditor/README.md

@@ -4,8 +4,10 @@
 
 新增`toolbars.custom_uploadImage`参数
 
+新增`toolbars.custom_uploadFile`参数
+
 新增`toolbars.custom_fullscreen`参数
 
 新增`isCustomFullscreen`参数
 
-修改`height`参数
+修改`height`参数

+ 6 - 0
resources/assets/js/main/components/MDEditor/assets/css/index.less

@@ -97,6 +97,12 @@
             .title {
                 font-size: 16px !important;
             }
+
+            .icon-svg {
+                display: flex;
+                align-items: center;
+                justify-content: center;
+            }
         }
 
         .empty {

文件差异内容过多而无法显示
+ 5 - 0
resources/assets/js/main/components/MDEditor/components/pro/index.vue


+ 3 - 0
resources/assets/js/main/components/MDEditor/components/simple/index.vue

@@ -94,6 +94,9 @@
             <li v-if="tools.custom_uploadImage" name="上传图片">
                 <span @click="custom_chooseImage" class="iconfont icon-upload-img"></span>
             </li>
+            <li v-if="tools.custom_uploadFile" name="上传文件">
+                <span @click="custom_chooseFile" class="iconfont icon-upload-img"></span>
+            </li>
             <li v-if="tools.table" name="表格">
                 <span @click="insertTable" class="iconfont icon-table"></span>
             </li>

+ 1 - 0
resources/assets/js/main/components/MDEditor/config/tools.js

@@ -21,6 +21,7 @@ export default {
     uploadImage:false,
     custom_image:false,
     custom_uploadImage:false,
+    custom_uploadFile:false,
     table: true,
     checked: true,
     notChecked: true,

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

@@ -3,6 +3,23 @@
         <div class="mdeditor-box">
             <MarkdownPro ref="md1" v-model="content" :height="height" :toolbars="toolbars" :is-custom-fullscreen="transfer" @on-custom="customClick"></MarkdownPro>
             <img-upload ref="myUpload" class="teditor-upload" type="callback" @on-callback="editorImage" num="50" style="display:none;"></img-upload>
+            <Upload
+                name="files"
+                ref="fileUpload"
+                class="teditor-upload"
+                :action="actionUrl"
+                :data="params"
+                multiple
+                :format="uploadFormat"
+                :show-upload-list="false"
+                :max-size="maxSize"
+                :on-progress="handleProgress"
+                :on-success="handleSuccess"
+                :on-error="handleError"
+                :on-format-error="handleFormatError"
+                :on-exceeded-size="handleMaxSize"
+                :before-upload="handleBeforeUpload">
+            </Upload>
         </div>
         <Modal v-model="transfer" class="mdeditor-transfer" footer-hide fullscreen transfer :closable="false">
             <div class="mdeditor-transfer-body">
@@ -89,6 +106,7 @@
 
                         custom_image: true,
                         custom_uploadImage: true,
+                        custom_uploadFile: true,
                         custom_fullscreen: true,
                     };
                 }
@@ -100,6 +118,12 @@
                 transfer: false,
                 html2md: false,
                 htmlValue: '',
+
+                uploadIng: 0,
+                uploadFormat: ['jpg', 'jpeg', 'png', 'gif', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'esp', 'pdf', 'rar', 'zip', 'gz'],
+                actionUrl: $A.apiUrl('system/fileupload'),
+                params: { token: $A.getToken() },
+                maxSize: 10240
             };
         },
         mounted() {
@@ -142,6 +166,10 @@
                         this.$refs.myUpload.handleClick();
                         break;
                     }
+                    case "file-upload": {
+                        this.$refs.fileUpload.handleClick();
+                        break;
+                    }
                     case "fullscreen": {
                         this.transfer = !this.transfer;
                         break;
@@ -183,7 +211,61 @@
                 }
                 script.src = url;
                 document.body.appendChild(script);
-            }
+            },
+
+            /********************文件上传部分************************/
+
+            handleProgress() {
+                //开始上传
+                this.uploadIng++;
+            },
+
+            handleSuccess(res, file) {
+                //上传完成
+                this.uploadIng--;
+                if (res.ret === 1) {
+                    let con = `[${res.data.name} (${$A.bytesToSize(res.data.size * 1024)})](${res.data.url})`;
+                    if (this.transfer) {
+                        this.$refs.md2.insertContent(con);
+                    } else {
+                        this.$refs.md1.insertContent(con);
+                    }
+                } else {
+                    this.$Modal.warning({
+                        title: this.$L('上传失败'),
+                        content: this.$L('文件 % 上传失败,%', file.name, res.msg)
+                    });
+                }
+            },
+
+            handleError() {
+                //上传错误
+                this.uploadIng--;
+            },
+
+            handleFormatError(file) {
+                //上传类型错误
+                this.$Modal.warning({
+                    title: this.$L('文件格式不正确'),
+                    content: this.$L('文件 % 格式不正确,仅支持上传:%', file.name, this.uploadFormat.join(','))
+                });
+            },
+
+            handleMaxSize(file) {
+                //上传大小错误
+                this.$Modal.warning({
+                    title: this.$L('超出文件大小限制'),
+                    content: this.$L('文件 % 太大,不能超过%。', file.name, $A.bytesToSize(this.maxSize * 1024))
+                });
+            },
+
+            handleBeforeUpload() {
+                //上传前判断
+                this.params = {
+                    token: $A.getToken(),
+                };
+                return true;
+            },
         }
     }
 </script>

+ 95 - 2
resources/assets/js/main/components/TEditor.vue

@@ -7,7 +7,28 @@
                 <div>{{$L('加载组件中...')}}</div>
             </Spin>
             <img-upload ref="myUpload" class="teditor-upload" type="callback" @on-callback="editorImage" num="50" style="margin-top:5px;height:26px;"></img-upload>
+            <Upload
+                name="files"
+                ref="fileUpload"
+                class="teditor-upload"
+                :action="actionUrl"
+                :data="params"
+                multiple
+                :format="uploadFormat"
+                :show-upload-list="false"
+                :max-size="maxSize"
+                :on-progress="handleProgress"
+                :on-success="handleSuccess"
+                :on-error="handleError"
+                :on-format-error="handleFormatError"
+                :on-exceeded-size="handleMaxSize"
+                :before-upload="handleBeforeUpload">
+            </Upload>
         </div>
+        <Spin fix v-if="uploadIng > 0">
+            <Icon type="ios-loading" size=18 class="teditor-spin-icon-load"></Icon>
+            <div>{{$L('正在上传文件...')}}</div>
+        </Spin>
         <Modal v-model="transfer" class="teditor-transfer" @on-visible-change="transferChange" footer-hide fullscreen transfer>
             <div slot="close">
                 <Button type="primary" size="small">{{$L('完成')}}</Button>
@@ -143,7 +164,7 @@
             },
             toolbar: {
                 type: String,
-                default: ' undo redo | styleselect | uploadImages | bold italic underline forecolor backcolor | alignleft aligncenter alignright | bullist numlist outdent indent | link image emoticons media codesample | preview screenload',
+                default: ' undo redo | styleselect | uploadImages | uploadFiles | bold italic underline forecolor backcolor | alignleft aligncenter alignright | bullist numlist outdent indent | link image emoticons media codesample | preview screenload',
             },
             other_options: {
                 type: Object,
@@ -167,6 +188,12 @@
 
                 spinShow: true,
                 transfer: false,
+
+                uploadIng: 0,
+                uploadFormat: ['jpg', 'jpeg', 'png', 'gif', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'esp', 'pdf', 'rar', 'zip', 'gz'],
+                actionUrl: $A.apiUrl('system/fileupload'),
+                params: { token: $A.getToken() },
+                maxSize: 10240
             };
         },
         mounted() {
@@ -238,7 +265,7 @@
                         },
                         insert: {
                             title: "Insert",
-                            items: "image link media addcomment pageembed template codesample inserttable | charmap emoticons hr | pagebreak nonbreaking anchor toc | insertdatetime | uploadImages browseImages"
+                            items: "image link media addcomment pageembed template codesample inserttable | charmap emoticons hr | pagebreak nonbreaking anchor toc | insertdatetime | uploadImages browseImages | uploadFiles"
                         }
                     },
                     codesample_languages: [
@@ -291,6 +318,23 @@
                                 this.$refs.myUpload.browsePicture();
                             }
                         });
+                        editor.ui.registry.addButton('uploadFiles', {
+                            text: this.$L('文件'),
+                            tooltip: this.$L('上传文件'),
+                            onAction: () => {
+                                if (this.handleBeforeUpload()) {
+                                    this.$refs.fileUpload.handleClick();
+                                }
+                            }
+                        });
+                        editor.ui.registry.addMenuItem('uploadFiles', {
+                            text: this.$L('上传文件'),
+                            onAction: () => {
+                                if (this.handleBeforeUpload()) {
+                                    this.$refs.fileUpload.handleClick();
+                                }
+                            }
+                        });
                         if (isFull) {
                             editor.ui.registry.addButton('screenload', {
                                 icon: 'fullscreen',
@@ -439,6 +483,55 @@
                     }
                 }
             },
+
+            /********************文件上传部分************************/
+
+            handleProgress() {
+                //开始上传
+                this.uploadIng++;
+            },
+
+            handleSuccess(res, file) {
+                //上传完成
+                this.uploadIng--;
+                if (res.ret === 1) {
+                    this.insertContent(`<a href="${res.data.url}" target="_blank">${res.data.name} (${$A.bytesToSize(res.data.size * 1024)})</a>`);
+                } else {
+                    this.$Modal.warning({
+                        title: this.$L('上传失败'),
+                        content: this.$L('文件 % 上传失败,%', file.name, res.msg)
+                    });
+                }
+            },
+
+            handleError() {
+                //上传错误
+                this.uploadIng--;
+            },
+
+            handleFormatError(file) {
+                //上传类型错误
+                this.$Modal.warning({
+                    title: this.$L('文件格式不正确'),
+                    content: this.$L('文件 % 格式不正确,仅支持上传:%', file.name, this.uploadFormat.join(','))
+                });
+            },
+
+            handleMaxSize(file) {
+                //上传大小错误
+                this.$Modal.warning({
+                    title: this.$L('超出文件大小限制'),
+                    content: this.$L('文件 % 太大,不能超过%。', file.name, $A.bytesToSize(this.maxSize * 1024))
+                });
+            },
+
+            handleBeforeUpload() {
+                //上传前判断
+                this.params = {
+                    token: $A.getToken(),
+                };
+                return true;
+            },
         }
     }
 </script>

+ 15 - 1
resources/assets/js/main/components/WHeader.vue

@@ -125,6 +125,7 @@
                     </Form>
                 </TabPane>
                 <TabPane :label="$L('账号密码')" name="account">
+                    <Alert v-if="userInfo.changepass" type="warning" showIcon>{{$L('请先修改登录密码!')}}</Alert>
                     <Form ref="formPass" :model="formPass" :rules="rulePass" :label-width="100" @submit.native.prevent>
                         <FormItem :label="$L('旧密码')" prop="oldpass">
                             <Input v-model="formPass.oldpass" type="password"></Input>
@@ -425,6 +426,7 @@
                 this.formDatum__reset = $A.cloneData(this.formDatum);
                 this.formSetting__reset = $A.cloneData(this.formSetting);
                 this.formPass__reset = $A.cloneData(this.formPass);
+                this.changepass();
             };
             this.userInfo = $A.getUserInfo((res) => {
                 this.userInfo = res;
@@ -444,11 +446,23 @@
             '$route' () {
                 this.tabActive = this.$route.meta.tabActive;
                 this.systemDrawerShow = false;
-                this.userDrawerShow = false;
                 this.chatDrawerShow = $A.urlParameter("open") === 'chat' && $A.getToken() !== false;
+                if (!this.userInfo.changepass) {
+                    this.userDrawerShow = false;
+                }
             }
         },
         methods: {
+            changepass() {
+                if (this.userInfo.changepass) {
+                    this.userDrawerShow = true;
+                    this.userDrawerTab = 'account';
+                    setTimeout(() => {
+                        this.changepass()
+                    }, 500);
+                }
+            },
+
             getBgUrl(id, thumb) {
                 id = Math.max(1, parseInt(id));
                 return 'url(' + window.location.origin + '/images/bg/' + (thumb ? 'thumb/' : '') + id + '.jpg' + ')';

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

@@ -31,7 +31,7 @@ export default {
 
     data() {
         return {
-            uploadFormat: ['jpg', 'jpeg', 'png', 'gif', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt'],
+            uploadFormat: ['jpg', 'jpeg', 'png', 'gif', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'esp', 'pdf', 'rar', 'zip', 'gz'],
             actionUrl: $A.apiUrl('chat/files/upload'),
             params: {
                 username: this.target,
@@ -86,7 +86,7 @@ export default {
             //上传大小错误
             this.$Modal.warning({
                 title: this.$L('超出文件大小限制'),
-                content: this.$L('文件 % 太大,不能超过%。', file.name, this.maxSize + 'KB')
+                content: this.$L('文件 % 太大,不能超过%。', file.name, $A.bytesToSize(this.maxSize * 1024))
             });
         },
 

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

@@ -36,7 +36,7 @@
                                     <Radio label="reg" disabled>{{$L('注册会员')}}</Radio>
                                 </RadioGroup>
                                 <RadioGroup v-else v-model="formSystem.role_look">
-                                    <Radio label="edit">{{$L('修改权限')}}</Radio>
+                                    <Radio label="edit">{{$L('修改权限')}}</Radio>
                                     <Radio label="reg">{{$L('注册会员')}}</Radio>
                                 </RadioGroup>
                             </div>

+ 57 - 39
resources/assets/js/main/components/gantt/index.vue

@@ -14,14 +14,14 @@
                 </li>
             </ul>
         </div>
-        <div class="gantt-right">
+        <div ref="ganttRight" class="gantt-right">
             <div class="gantt-chart">
                 <ul class="gantt-month">
                     <li v-for="(item, key) in monthNum" :key="key" :style="monthStyle(key)">
                         <div class="month-format">{{monthFormat(key)}}</div>
                     </li>
                 </ul>
-                <ul class="gantt-date">
+                <ul class="gantt-date" @mousedown="dateMouseDown">
                     <li v-for="(item, key) in dateNum" :key="key" :style="dateStyle(key)">
                         <div class="date-format">
                             <div class="format-day">{{dateFormat(key, 'day')}}</div>
@@ -75,18 +75,20 @@ export default {
 
             mouseItem: null,
             mouseBak: {},
+
+            dateMove: null
         }
     },
     mounted() {
         this.dateWidth = this.itemWidth;
-        this.$refs.ganttTimeline.addEventListener('mousewheel', this.handleScroll, false);
+        this.$refs.ganttRight.addEventListener('mousewheel', this.handleScroll, false);
         document.addEventListener('mousemove', this.itemMouseMove);
         document.addEventListener('mouseup', this.itemMouseUp);
         window.addEventListener("resize", this.handleResize, false);
         this.handleResize();
     },
     beforeDestroy() {
-        this.$refs.ganttTimeline.removeEventListener('mousewheel', this.handleScroll, false);
+        this.$refs.ganttRight.removeEventListener('mousewheel', this.handleScroll, false);
         document.removeEventListener('mousemove', this.itemMouseMove);
         document.removeEventListener('mouseup', this.itemMouseUp);
         window.removeEventListener("resize", this.handleResize, false);
@@ -182,7 +184,7 @@ export default {
                 if (type == 'day') {
                     return date.getDate();
                 } else if (type == 'wook') {
-                    return `星期${'日一二三四五六'.charAt(date.getDay())}`;
+                    return this.$L(`星期${'日一二三四五六'.charAt(date.getDay())}`);
                 } else {
                     return date;
                 }
@@ -268,6 +270,13 @@ export default {
         handleResize() {
             this.ganttWidth = this.$refs.ganttTimeline.clientWidth;
         },
+        dateMouseDown(e) {
+            e.preventDefault();
+            this.mouseItem = null;
+            this.dateMove = {
+                clientX: e.clientX
+            };
+        },
         itemMouseDown(e, item) {
             e.preventDefault();
             let type = 'moveX';
@@ -283,45 +292,52 @@ export default {
                 value: item[type],
             };
             this.mouseItem = item;
+            this.dateMove = null;
         },
         itemMouseMove(e) {
-            if (this.mouseItem == null) {
-                return;
+            if (this.mouseItem != null) {
+                e.preventDefault();
+                var diff = e.clientX - this.mouseBak.clientX;
+                this.$set(this.mouseItem, this.mouseBak.type, this.mouseBak.value + diff);
+            } else if (this.dateMove != null) {
+                e.preventDefault();
+                let moveX = (this.dateMove.clientX - e.clientX) * 5;
+                this.dateMove.clientX = e.clientX;
+                this.mouseWidth+= moveX;
+                this.mouseScaleWidth+= moveX * (100 / this.dateWidth);
             }
-            e.preventDefault();
-            var diff = e.clientX - this.mouseBak.clientX;
-            this.$set(this.mouseItem, this.mouseBak.type, this.mouseBak.value + diff);
         },
         itemMouseUp(e) {
-            if (this.mouseItem == null) {
-                return;
-            }
-            const {start, end} = this.mouseItem.time;
-            let isM = false;
-            //一个宽度的时间
-            let oneWidthTime = 86400000 / this.dateWidth;
-            //修改起止时间
-            if (typeof this.mouseItem.moveX === "number" && this.mouseItem.moveX != 0) {
-                let moveTime = this.mouseItem.moveX * oneWidthTime;
-                this.$set(this.mouseItem.time, 'start', start + moveTime);
-                this.$set(this.mouseItem.time, 'end', end + moveTime);
-                this.$set(this.mouseItem, 'moveX', 0);
-                isM = true;
-            }
-            //修改结束时间
-            if (typeof this.mouseItem.moveW === "number" && this.mouseItem.moveW != 0) {
-                let moveTime = this.mouseItem.moveW * oneWidthTime;
-                this.$set(this.mouseItem.time, 'end', end + moveTime);
-                this.$set(this.mouseItem, 'moveW', 0);
-                isM = true;
-            }
-            //
-            if (isM) {
-                this.$emit("on-change", this.mouseItem)
-            } else if (e.target.className == 'timeline-title') {
-                this.clickItem(this.mouseItem);
+            if (this.mouseItem != null) {
+                const {start, end} = this.mouseItem.time;
+                let isM = false;
+                //一个宽度的时间
+                let oneWidthTime = 86400000 / this.dateWidth;
+                //修改起止时间
+                if (typeof this.mouseItem.moveX === "number" && this.mouseItem.moveX != 0) {
+                    let moveTime = this.mouseItem.moveX * oneWidthTime;
+                    this.$set(this.mouseItem.time, 'start', start + moveTime);
+                    this.$set(this.mouseItem.time, 'end', end + moveTime);
+                    this.$set(this.mouseItem, 'moveX', 0);
+                    isM = true;
+                }
+                //修改结束时间
+                if (typeof this.mouseItem.moveW === "number" && this.mouseItem.moveW != 0) {
+                    let moveTime = this.mouseItem.moveW * oneWidthTime;
+                    this.$set(this.mouseItem.time, 'end', end + moveTime);
+                    this.$set(this.mouseItem, 'moveW', 0);
+                    isM = true;
+                }
+                //
+                if (isM) {
+                    this.$emit("on-change", this.mouseItem)
+                } else if (e.target.className == 'timeline-title') {
+                    this.clickItem(this.mouseItem);
+                }
+                this.mouseItem = null;
+            } else if (this.dateMove != null) {
+                this.dateMove = null;
             }
-            this.mouseItem = null;
         },
         scrollPosition(pos) {
             let date = new Date();
@@ -483,6 +499,7 @@ export default {
                 right: 0;
                 bottom: 0;
                 z-index: 2;
+                cursor: move;
                 &:before {
                     content: "";
                     position: absolute;
@@ -534,7 +551,8 @@ export default {
                 right: 0;
                 bottom: 0;
                 z-index: 3;
-                overflow: auto;
+                overflow-x: hidden;
+                overflow-y: auto;
                 > li {
                     cursor: default;
                     height: 40px;

+ 4 - 3
resources/assets/js/main/components/project/task/files.vue

@@ -30,7 +30,7 @@
                     multiple
                     :format="uploadFormat"
                     :show-upload-list="false"
-                    :max-size="10240"
+                    :max-size="maxSize"
                     :on-success="handleSuccess"
                     :on-format-error="handleFormatError"
                     :on-exceeded-size="handleMaxSize"
@@ -100,7 +100,7 @@
                 lastPage: 0,
                 noDataText: "",
 
-                uploadFormat: ['jpg', 'jpeg', 'png', 'gif', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt'],
+                uploadFormat: ['jpg', 'jpeg', 'png', 'gif', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'esp', 'pdf', 'rar', 'zip', 'gz'],
                 actionUrl: $A.apiUrl('project/files/upload'),
                 params: {
                     token: $A.getToken(),
@@ -108,6 +108,7 @@
                     projectid: this.projectid
                 },
                 uploadList: [],
+                maxSize: 10240
             }
         },
 
@@ -546,7 +547,7 @@
                 //上传大小错误
                 this.$Modal.warning({
                     title: this.$L('超出文件大小限制'),
-                    content: this.$L('文件 % 太大,不能超过2M。', file.name)
+                    content: this.$L('文件 % 太大,不能超过%。', file.name, $A.bytesToSize(this.maxSize * 1024))
                 });
             },
 

+ 11 - 8
resources/assets/js/main/components/project/todo/calendar.vue

@@ -155,15 +155,16 @@
                 if (taskData.complete) {
                     title = '<span style="text-decoration:line-through">' + title + '</span>';
                 }
-                let color = '#D8F8F2';
+                let color = '#ffffff';
+                let backgroundColor = '#2d8cf0';
                 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)';
+                    backgroundColor = '#ed4014';
+                } else if (taskData.level === 2) {
+                    backgroundColor = '#f90';
+                } else if (taskData.level === 3) {
+                    backgroundColor = '#2db7f5';
+                } else if (taskData.level === 4) {
+                    backgroundColor = '#19be6b';
                 }
                 let data = {
                     "id": taskData.id,
@@ -171,6 +172,8 @@
                     "end": $A.formatDate('Y-m-d H:i:s', enddate),
                     "title": title,
                     "color": color,
+                    "backgroundColor": backgroundColor,
+                    "selectedColor": backgroundColor,
                     "name": taskData.nickname || taskData.username
                 };
                 if (this.isShowImg(taskData.userimg)) {

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

@@ -902,7 +902,7 @@
                         break;
 
                     case 'flow':
-                        this.$refs.myFlow[act == 'pdf' ? 'exportPDF' : 'exportPNG'](this.docDetail.title, 1);
+                        this.$refs.myFlow[act == 'pdf' ? 'exportPDF' : 'exportPNG'](this.docDetail.title, 3);
                         break;
 
                     case 'sheet':

+ 4 - 0
resources/assets/js/main/pages/project/panel.vue

@@ -1010,6 +1010,10 @@
                 if (el && el.length > 0) {
                     el[0].setFocus();
                 }
+                el = this.$refs['box_' + label.id];
+                if (el && el.length > 0) {
+                    el[0].scrollToBottom(false);
+                }
             },
 
             subtaskProgress(task) {

+ 14 - 0
resources/assets/js/main/pages/team.vue

@@ -55,6 +55,9 @@
                     <div slot="label"><em v-if="formData.id == 0" class="team-add-red-input">*</em>{{$L('登录密码')}}</div>
                     <Input type="password" v-model="formData.userpass" :placeholder="$L(formData.id > 0 ? '留空不修改' : '最少6位数')"></Input>
                 </FormItem>
+                <FormItem class="team-add-form-item-changepass" :class="{show:formData.userpass}" v-if="formData.id == 0" prop="changepass">
+                    <Checkbox v-model="formData.changepass" :true-value="1" :false-value="0">{{$L('首次登陆需修改密码')}}</Checkbox>
+                </FormItem>
             </Form>
             <div slot="footer">
                 <Button type="default" @click="addShow=false">{{$L('取消')}}</Button>
@@ -74,6 +77,16 @@
         font-size: 14px;
         color: #ed4014;
     }
+    .team-add-form-item-changepass {
+        margin-top: -18px;
+        margin-bottom: 18px;
+        height: 0;
+        overflow: hidden;
+        transition: all 0.2s;
+        &.show {
+            height: 32px;
+        }
+    }
 </style>
 <style lang="scss" scoped>
     .team {
@@ -362,6 +375,7 @@
                             username: '',
                             nickname: '',
                             userpass: '',
+                            changepass: 1
                         }
                         break;
                     }

+ 9 - 1
resources/lang/en/general.js

@@ -16,7 +16,7 @@ export default {
     "文件格式不正确": "File format is incorrect",
     "文件 % 格式不正确,请上传 jpg、jpeg、gif、png 格式的图片。": "% File format is incorrect, please upload pictures jpg, jpeg, gif, png format.",
     "超出文件大小限制": "File size limit exceeded",
-    "文件 % 太大,不能超过2M。": "% File is too large, no more than 2M.",
+    "文件 % 太大,不能超过%。": "File % is too large to exceed %.",
     "温馨提示": "Tips",
     "最多只能上传 % 张图片。": "% Can only upload pictures.",
     "最多只能选择 % 张图片。": "Select at most% pictures.",
@@ -428,6 +428,7 @@ export default {
     "天": "Day",
     "任务完成 % 天后自动归档。": "Tasks are automatically archived % days after completion.",
     "修改权限": "Modify permissions",
+    "同修改权限": "Same as modify permissions",
     "阅读权限": "Read permissions",
     "私有文库": "Private",
     "成员开放": "Members",
@@ -520,4 +521,11 @@ export default {
     "导出列表": "Export List",
     "导出结果": "Export Results",
     "点击下载 (%)": "Click to download (%)",
+    "星期一": "Monday",
+    "星期二": "Tuesday",
+    "星期三": "Wednesday",
+    "星期四": "Thursday",
+    "星期五": "Friday",
+    "星期六": "Saturday",
+    "星期日": "Sunday",
 }