Merge branch 'master' of github.com:kuaifan/dootask into develop
# Conflicts: # app/Http/Controllers/Api/DialogController.php # app/Http/Controllers/Api/ProjectController.php # electron/package.json # package.json # public/css/app.css # public/docs/assets/main.bundle.js # public/js/app.js # public/js/build/146.js.LICENSE.txt # public/js/build/161.js # public/js/build/199.js # public/js/build/199.js.LICENSE.txt # public/js/build/218.js # public/js/build/218.js.LICENSE.txt # public/js/build/244.js # public/js/build/244.js.LICENSE.txt # public/js/build/309.js # public/js/build/423.js # public/js/build/43.js # public/js/build/693.js # public/js/build/717.js # public/js/build/717.js.LICENSE.txt # public/js/build/79.js.LICENSE.txt # resources/assets/js/pages/manage/components/TaskAdd.vue
This commit is contained in:
commit
35bd038802
@ -331,7 +331,61 @@ class DialogController extends AbstractController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @api {get} api/dialog/msg/download 09. 文件下载
|
* @api {get} api/dialog/msg/detail 09. 消息详情
|
||||||
|
*
|
||||||
|
* @apiDescription 需要token身份
|
||||||
|
* @apiVersion 1.0.0
|
||||||
|
* @apiGroup dialog
|
||||||
|
* @apiName msg__detail
|
||||||
|
*
|
||||||
|
* @apiParam {Number} msg_id 消息ID
|
||||||
|
*
|
||||||
|
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||||
|
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||||
|
* @apiSuccess {Object} data 返回数据
|
||||||
|
*/
|
||||||
|
public function msg__detail()
|
||||||
|
{
|
||||||
|
User::auth();
|
||||||
|
//
|
||||||
|
$msg_id = intval(Request::input('msg_id'));
|
||||||
|
//
|
||||||
|
$dialogMsg = WebSocketDialogMsg::whereId($msg_id)->first();
|
||||||
|
if (empty($dialogMsg)) {
|
||||||
|
return Base::retError("文件不存在");
|
||||||
|
}
|
||||||
|
$data = $dialogMsg->toArray();
|
||||||
|
//
|
||||||
|
if ($data['type'] == 'file') {
|
||||||
|
$codeExt = ['txt'];
|
||||||
|
$officeExt = ['doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx'];
|
||||||
|
$localExt = ['jpg', 'jpeg', 'png', 'gif'];
|
||||||
|
$msg = Base::json2array($dialogMsg->getRawOriginal('msg'));
|
||||||
|
$filePath = public_path($msg['path']);
|
||||||
|
if (in_array($msg['ext'], $codeExt) && $msg['size'] < 2 * 1024 * 1024) {
|
||||||
|
// 文本预览,限制2M内的文件
|
||||||
|
$data['content'] = file_get_contents($filePath);
|
||||||
|
$data['file_mode'] = 1;
|
||||||
|
} elseif (in_array($msg['ext'], $officeExt)) {
|
||||||
|
// office预览
|
||||||
|
$data['file_mode'] = 2;
|
||||||
|
} else {
|
||||||
|
// 其他预览
|
||||||
|
if (in_array($msg['ext'], $localExt)) {
|
||||||
|
$url = Base::fillUrl($msg['path']);
|
||||||
|
} else {
|
||||||
|
$url = 'http://' . env('APP_IPPR') . '.3/' . $msg['path'];
|
||||||
|
}
|
||||||
|
$data['url'] = base64_encode($url);
|
||||||
|
$data['file_mode'] = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//
|
||||||
|
return Base::retSuccess("success", $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @api {get} api/dialog/msg/download 10. 文件下载
|
||||||
*
|
*
|
||||||
* @apiDescription 需要token身份
|
* @apiDescription 需要token身份
|
||||||
* @apiVersion 1.0.0
|
* @apiVersion 1.0.0
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers\Api;
|
namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
|
|
||||||
use App\Exceptions\ApiException;
|
use App\Exceptions\ApiException;
|
||||||
use App\Models\AbstractModel;
|
use App\Models\AbstractModel;
|
||||||
use App\Models\File;
|
use App\Models\File;
|
||||||
@ -10,10 +9,12 @@ use App\Models\FileContent;
|
|||||||
use App\Models\FileLink;
|
use App\Models\FileLink;
|
||||||
use App\Models\FileUser;
|
use App\Models\FileUser;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
use App\Models\WebSocketDialogMsg;
|
||||||
use App\Module\Base;
|
use App\Module\Base;
|
||||||
use App\Module\Ihttp;
|
use App\Module\Ihttp;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Request;
|
use Request;
|
||||||
|
use Response;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @apiDefine file
|
* @apiDefine file
|
||||||
@ -380,11 +381,11 @@ class FileController extends AbstractController
|
|||||||
* @apiName content
|
* @apiName content
|
||||||
*
|
*
|
||||||
* @apiParam {Number|String} id
|
* @apiParam {Number|String} id
|
||||||
* - Number 文件ID(需要登录)
|
* - Number: 文件ID(需要登录)
|
||||||
* - String 链接码(不需要登录,用于预览)
|
* - String: 链接码(不需要登录,用于预览)
|
||||||
* @apiParam {String} down 直接下载
|
* @apiParam {String} down 直接下载
|
||||||
* - no: 浏览(默认)
|
* - no: 浏览(默认)
|
||||||
* - yes: 下载
|
* - yes: 下载(office文件直接下载)
|
||||||
*
|
*
|
||||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||||
|
@ -1084,7 +1084,61 @@ class ProjectController extends AbstractController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @api {get} api/project/task/filedown 23. 下载任务文件
|
* @api {get} api/project/task/filedetail 23. 获取任务文件详情
|
||||||
|
*
|
||||||
|
* @apiDescription 需要token身份(限:项目、任务负责人)
|
||||||
|
* @apiVersion 1.0.0
|
||||||
|
* @apiGroup project
|
||||||
|
* @apiName task__filedetail
|
||||||
|
*
|
||||||
|
* @apiParam {Number} file_id 文件ID
|
||||||
|
*
|
||||||
|
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||||
|
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||||
|
* @apiSuccess {Object} data 返回数据
|
||||||
|
*/
|
||||||
|
public function task__filedetail()
|
||||||
|
{
|
||||||
|
User::auth();
|
||||||
|
//
|
||||||
|
$file_id = intval(Request::input('file_id'));
|
||||||
|
//
|
||||||
|
$file = ProjectTaskFile::find($file_id);
|
||||||
|
if (empty($file)) {
|
||||||
|
return Base::retError("文件不存在");
|
||||||
|
}
|
||||||
|
$data = $file->toArray();
|
||||||
|
$data['path'] = $file->getRawOriginal('path');
|
||||||
|
//
|
||||||
|
ProjectTask::userTask($file->task_id, true, true);
|
||||||
|
//
|
||||||
|
$codeExt = ['txt'];
|
||||||
|
$officeExt = ['doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx'];
|
||||||
|
$localExt = ['jpg', 'jpeg', 'png', 'gif'];
|
||||||
|
$filePath = public_path($data['path']);
|
||||||
|
if (in_array($data['ext'], $codeExt) && $data['size'] < 2 * 1024 * 1024) {
|
||||||
|
// 文本预览,限制2M内的文件
|
||||||
|
$data['content'] = file_get_contents($filePath);
|
||||||
|
$data['file_mode'] = 1;
|
||||||
|
} elseif (in_array($data['ext'], $officeExt)) {
|
||||||
|
// office预览
|
||||||
|
$data['file_mode'] = 2;
|
||||||
|
} else {
|
||||||
|
// 其他预览
|
||||||
|
if (in_array($data['ext'], $localExt)) {
|
||||||
|
$url = Base::fillUrl($data['path']);
|
||||||
|
} else {
|
||||||
|
$url = 'http://' . env('APP_IPPR') . '.3/' . $data['path'];
|
||||||
|
}
|
||||||
|
$data['url'] = base64_encode($url);
|
||||||
|
$data['file_mode'] = 3;
|
||||||
|
}
|
||||||
|
//
|
||||||
|
return Base::retSuccess('success', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @api {get} api/project/task/filedown 24. 下载任务文件
|
||||||
*
|
*
|
||||||
* @apiDescription 需要token身份(限:项目、任务负责人)
|
* @apiDescription 需要token身份(限:项目、任务负责人)
|
||||||
* @apiVersion 1.0.0
|
* @apiVersion 1.0.0
|
||||||
@ -1118,7 +1172,7 @@ class ProjectController extends AbstractController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @api {post} api/project/task/add 24. 添加任务
|
* @api {post} api/project/task/add 25. 添加任务
|
||||||
*
|
*
|
||||||
* @apiDescription 需要token身份
|
* @apiDescription 需要token身份
|
||||||
* @apiVersion 1.0.0
|
* @apiVersion 1.0.0
|
||||||
@ -1189,7 +1243,7 @@ class ProjectController extends AbstractController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @api {get} api/project/task/addsub 25. 添加子任务
|
* @api {get} api/project/task/addsub 26. 添加子任务
|
||||||
*
|
*
|
||||||
* @apiDescription 需要token身份(限:项目、任务负责人)
|
* @apiDescription 需要token身份(限:项目、任务负责人)
|
||||||
* @apiVersion 1.0.0
|
* @apiVersion 1.0.0
|
||||||
@ -1229,7 +1283,7 @@ class ProjectController extends AbstractController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @api {post} api/project/task/update 26. 修改任务、子任务
|
* @api {post} api/project/task/update 27. 修改任务、子任务
|
||||||
*
|
*
|
||||||
* @apiDescription 需要token身份(限:项目、任务负责人)
|
* @apiDescription 需要token身份(限:项目、任务负责人)
|
||||||
* @apiVersion 1.0.0
|
* @apiVersion 1.0.0
|
||||||
@ -1679,6 +1733,9 @@ class ProjectController extends AbstractController
|
|||||||
if ($usertype == 'replace' && empty($userids)) {
|
if ($usertype == 'replace' && empty($userids)) {
|
||||||
throw new ApiException("状态[{$item['name']}]设置错误,设置流转模式时必须填写状态负责人");
|
throw new ApiException("状态[{$item['name']}]设置错误,设置流转模式时必须填写状态负责人");
|
||||||
}
|
}
|
||||||
|
if ($usertype == 'merge' && empty($userids)) {
|
||||||
|
throw new ApiException("状态[{$item['name']}]设置错误,设置剔除模式时必须填写状态负责人");
|
||||||
|
}
|
||||||
if ($userlimit && empty($userids)) {
|
if ($userlimit && empty($userids)) {
|
||||||
throw new ApiException("状态[{$item['name']}]设置错误,设置限制负责人时必须填写状态负责人");
|
throw new ApiException("状态[{$item['name']}]设置错误,设置限制负责人时必须填写状态负责人");
|
||||||
}
|
}
|
||||||
|
@ -30,9 +30,6 @@ class VerifyCsrfToken extends Middleware
|
|||||||
// 修改任务
|
// 修改任务
|
||||||
'api/project/task/update/',
|
'api/project/task/update/',
|
||||||
|
|
||||||
// 上传任务问题
|
|
||||||
'api/project/task/upload/',
|
|
||||||
|
|
||||||
// 聊天发文件
|
// 聊天发文件
|
||||||
'api/dialog/msg/sendfile/',
|
'api/dialog/msg/sendfile/',
|
||||||
|
|
||||||
|
@ -342,11 +342,13 @@ class ProjectTask extends AbstractModel
|
|||||||
$content = $data['content'];
|
$content = $data['content'];
|
||||||
$times = $data['times'];
|
$times = $data['times'];
|
||||||
$owner = $data['owner'];
|
$owner = $data['owner'];
|
||||||
|
$add_assist = intval($data['add_assist']);
|
||||||
$subtasks = $data['subtasks'];
|
$subtasks = $data['subtasks'];
|
||||||
$p_level = intval($data['p_level']);
|
$p_level = intval($data['p_level']);
|
||||||
$p_name = $data['p_name'];
|
$p_name = $data['p_name'];
|
||||||
$p_color = $data['p_color'];
|
$p_color = $data['p_color'];
|
||||||
$top = intval($data['top']);
|
$top = intval($data['top']);
|
||||||
|
$userid = User::userid();
|
||||||
//
|
//
|
||||||
if (ProjectTask::whereProjectId($project_id)
|
if (ProjectTask::whereProjectId($project_id)
|
||||||
->whereNull('project_tasks.complete_at')
|
->whereNull('project_tasks.complete_at')
|
||||||
@ -411,8 +413,13 @@ class ProjectTask extends AbstractModel
|
|||||||
$tmpArray[] = $uid;
|
$tmpArray[] = $uid;
|
||||||
}
|
}
|
||||||
$owner = $tmpArray;
|
$owner = $tmpArray;
|
||||||
|
// 协助人员
|
||||||
|
$assist = [];
|
||||||
|
if (!in_array($userid, $owner) && $add_assist) {
|
||||||
|
$assist = [$userid];
|
||||||
|
}
|
||||||
// 创建人
|
// 创建人
|
||||||
$task->userid = User::userid();
|
$task->userid = $userid;
|
||||||
// 排序位置
|
// 排序位置
|
||||||
if ($top) {
|
if ($top) {
|
||||||
$task->sort = intval(self::whereColumnId($task->column_id)->orderBy('sort')->value('sort')) - 1;
|
$task->sort = intval(self::whereColumnId($task->column_id)->orderBy('sort')->value('sort')) - 1;
|
||||||
@ -434,7 +441,7 @@ class ProjectTask extends AbstractModel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
//
|
//
|
||||||
return AbstractModel::transaction(function() use ($times, $subtasks, $content, $owner, $task) {
|
return AbstractModel::transaction(function() use ($assist, $times, $subtasks, $content, $owner, $task) {
|
||||||
$task->save();
|
$task->save();
|
||||||
$owner = array_values(array_unique($owner));
|
$owner = array_values(array_unique($owner));
|
||||||
foreach ($owner as $uid) {
|
foreach ($owner as $uid) {
|
||||||
@ -446,6 +453,16 @@ class ProjectTask extends AbstractModel
|
|||||||
'owner' => 1,
|
'owner' => 1,
|
||||||
])->save();
|
])->save();
|
||||||
}
|
}
|
||||||
|
$assist = array_values(array_unique(array_diff($assist, $owner)));
|
||||||
|
foreach ($assist as $uid) {
|
||||||
|
ProjectTaskUser::createInstance([
|
||||||
|
'project_id' => $task->project_id,
|
||||||
|
'task_id' => $task->id,
|
||||||
|
'task_pid' => $task->parent_id ?: $task->id,
|
||||||
|
'userid' => $uid,
|
||||||
|
'owner' => 0,
|
||||||
|
])->save();
|
||||||
|
}
|
||||||
if ($content) {
|
if ($content) {
|
||||||
ProjectTaskContent::createInstance([
|
ProjectTaskContent::createInstance([
|
||||||
'project_id' => $task->project_id,
|
'project_id' => $task->project_id,
|
||||||
@ -541,13 +558,17 @@ class ProjectTask extends AbstractModel
|
|||||||
if ($newFlowItem->userids) {
|
if ($newFlowItem->userids) {
|
||||||
// 判断自动添加负责人
|
// 判断自动添加负责人
|
||||||
$flowData['owner'] = $data['owner'] = $this->taskUser->where('owner', 1)->pluck('userid')->toArray();
|
$flowData['owner'] = $data['owner'] = $this->taskUser->where('owner', 1)->pluck('userid')->toArray();
|
||||||
if ($newFlowItem->usertype == "replace") {
|
if (in_array($newFlowItem->usertype, ["replace", "merge"])) {
|
||||||
// 流转模式
|
// 流转模式、剔除模式
|
||||||
if ($this->parent_id === 0) {
|
if ($this->parent_id === 0) {
|
||||||
$flowData['assist'] = $data['assist'] = $this->taskUser->where('owner', 0)->pluck('userid')->toArray();
|
$flowData['assist'] = $data['assist'] = $this->taskUser->where('owner', 0)->pluck('userid')->toArray();
|
||||||
$data['assist'] = array_merge($data['assist'], $data['owner']);
|
$data['assist'] = array_merge($data['assist'], $data['owner']);
|
||||||
}
|
}
|
||||||
$data['owner'] = $newFlowItem->userids;
|
$data['owner'] = $newFlowItem->userids;
|
||||||
|
// 判断剔除模式:保留操作状态的人员
|
||||||
|
if ($newFlowItem->usertype == "merge") {
|
||||||
|
$data['owner'][] = User::userid();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// 添加模式
|
// 添加模式
|
||||||
$data['owner'] = array_merge($data['owner'], $newFlowItem->userids);
|
$data['owner'] = array_merge($data['owner'], $newFlowItem->userids);
|
||||||
|
@ -204,7 +204,19 @@ class WebSocketService implements WebSocketHandlerInterface
|
|||||||
*/
|
*/
|
||||||
private function deleteUser($fd)
|
private function deleteUser($fd)
|
||||||
{
|
{
|
||||||
WebSocket::whereFd($fd)->delete();
|
$array = [];
|
||||||
|
WebSocket::whereFd($fd)->chunk(10, function($list) use (&$array) {
|
||||||
|
/** @var WebSocket $item */
|
||||||
|
foreach ($list as $item) {
|
||||||
|
$item->delete();
|
||||||
|
if ($item->path && str_starts_with($item->path, "file/content/")) {
|
||||||
|
$array[$item->path] = $item->path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
foreach ($array as $path) {
|
||||||
|
$this->pushPath($path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -96,7 +96,7 @@ services:
|
|||||||
|
|
||||||
fileview:
|
fileview:
|
||||||
container_name: "dootask-fileview-${APP_ID}"
|
container_name: "dootask-fileview-${APP_ID}"
|
||||||
image: "kuaifan/fileview:4.1.0-SNAPSHOT"
|
image: "kuaifan/fileview:4.1.0-SNAPSHOT-RC3"
|
||||||
environment:
|
environment:
|
||||||
TZ: "Asia/Shanghai"
|
TZ: "Asia/Shanghai"
|
||||||
KK_CONTEXT_PATH: "/fileview"
|
KK_CONTEXT_PATH: "/fileview"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "DooTask",
|
"name": "DooTask",
|
||||||
"version": "0.7.41",
|
"version": "0.7.66",
|
||||||
"description": "DooTask is task management system.",
|
"description": "DooTask is task management system.",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "DooTask",
|
"name": "DooTask",
|
||||||
"version": "0.7.41",
|
"version": "0.7.66",
|
||||||
"description": "DooTask is task management system.",
|
"description": "DooTask is task management system.",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "./cmd dev",
|
"start": "./cmd dev",
|
||||||
@ -64,7 +64,7 @@
|
|||||||
"stylus-loader": "^6.2.0",
|
"stylus-loader": "^6.2.0",
|
||||||
"tinymce": "^5.10.2",
|
"tinymce": "^5.10.2",
|
||||||
"tui-calendar-hi": "^1.15.1-5",
|
"tui-calendar-hi": "^1.15.1-5",
|
||||||
"view-design-hi": "^4.7.0-8",
|
"view-design-hi": "^4.7.0-11",
|
||||||
"vue": "^2.6.14",
|
"vue": "^2.6.14",
|
||||||
"vue-clipboard2": "^0.3.3",
|
"vue-clipboard2": "^0.3.3",
|
||||||
"vue-emoji-picker": "^1.0.3",
|
"vue-emoji-picker": "^1.0.3",
|
||||||
|
2
public/css/app.css
vendored
2
public/css/app.css
vendored
File diff suppressed because one or more lines are too long
2
public/css/iview.css
vendored
2
public/css/iview.css
vendored
File diff suppressed because one or more lines are too long
6
public/docs/assets/main.bundle.js
vendored
6
public/docs/assets/main.bundle.js
vendored
File diff suppressed because one or more lines are too long
2
public/js/app.js
vendored
2
public/js/app.js
vendored
File diff suppressed because one or more lines are too long
1
public/js/build/159.js
vendored
Normal file
1
public/js/build/159.js
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
"use strict";(self.webpackChunkDooTask=self.webpackChunkDooTask||[]).push([[159],{26071:(e,t,i)=>{i.d(t,{Z:()=>r});var n=i(1519),o=i.n(n)()((function(e){return e[1]}));o.push([e.id,".component-only-office[data-v-c9bf06c2]{align-items:center;bottom:0;display:flex;justify-content:center;left:0;position:absolute;right:0;top:0}.component-only-office .placeholder[data-v-c9bf06c2]{flex:1;height:100%;width:100%}.component-only-office .office-loading[data-v-c9bf06c2]{align-items:center;bottom:0;display:flex;justify-content:center;left:0;position:absolute;right:0;top:0;z-index:2}",""]);const r=o},36159:(e,t,i)=>{i.r(t),i.d(t,{default:()=>u});var n=i(20629);function o(e,t){var i=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),i.push.apply(i,n)}return i}function r(e){for(var t=1;t<arguments.length;t++){var i=null!=arguments[t]?arguments[t]:{};t%2?o(Object(i),!0).forEach((function(t){s(e,t,i[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(i)):o(Object(i)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(i,t))}))}return e}function s(e,t,i){return t in e?Object.defineProperty(e,t,{value:i,enumerable:!0,configurable:!0,writable:!0}):e[t]=i,e}const l={name:"OnlyOffice",props:{id:{type:String,default:function(){return"office_"+Math.round(1e4*Math.random())}},code:{type:String,default:""},value:{type:[Object,Array],default:function(){return{}}},readOnly:{type:Boolean,default:!1}},data:function(){return{loadIng:0,docEditor:null}},mounted:function(){},beforeDestroy:function(){null!==this.docEditor&&(this.docEditor.destroyEditor(),this.docEditor=null)},computed:r(r({},(0,n.rn)(["userToken","userInfo","themeIsDark"])),{},{fileType:function(){return this.getType(this.value.type)},fileName:function(){return this.value.name}}),watch:{"value.id":{handler:function(e){var t=this;e&&(this.loadIng++,$A.loadScript($A.apiUrl("../office/web-apps/apps/api/documents/api.js"),(function(e){t.loadIng--,null!==e?$A.modalAlert("组件加载失败!"):t.loadFile()})))},immediate:!0}},methods:{getType:function(e){switch(e){case"word":return"docx";case"excel":return"xlsx";case"ppt":return"pptx"}return e},loadFile:function(){var e=this;null!==this.docEditor&&(this.docEditor.destroyEditor(),this.docEditor=null);var t="zh";switch(this.getLanguage()){case"CN":case"TC":t="zh";break;default:t="en"}var i=this.code||this.value.id,n=$A.strExists(this.fileName,".")?this.fileName:this.fileName+"."+this.fileType,o={document:{fileType:this.fileType,key:this.fileType+"-"+i,title:n,url:"http://nginx/api/file/content/?id="+i+"&token="+this.userToken},editorConfig:{mode:"edit",lang:t,user:{id:this.userInfo.userid,name:this.userInfo.nickname},customization:{uiTheme:this.themeIsDark?"theme-dark":"theme-classic-light"},callbackUrl:"http://nginx/api/file/content/office?id="+i+"&token="+this.userToken}};if(/\/hideenOfficeTitle\//.test(window.navigator.userAgent)&&(o.document.title=" "),$A.leftExists(i,"msgFile_")?o.document.url="http://nginx/api/dialog/msg/download/?msg_id="+$A.leftDelete(i,"msgFile_")+"&token="+this.userToken:$A.leftExists(i,"taskFile_")&&(o.document.url="http://nginx/api/project/task/filedown/?file_id="+$A.leftDelete(i,"taskFile_")+"&token="+this.userToken),this.readOnly&&(o.editorConfig.mode="view",o.editorConfig.callbackUrl=null,!o.editorConfig.user.id)){var r=$A.getStorageInt("viewer");r||(r=$A.randNum(1e3,99999),$A.setStorage("viewer",r)),o.editorConfig.user.id="viewer_"+r,o.editorConfig.user.name="Viewer_"+r}this.$nextTick((function(){e.docEditor=new DocsAPI.DocEditor(e.id,o)}))}}};var a=i(93379),c=i.n(a),d=i(26071),f={insert:"head",singleton:!1};c()(d.Z,f);d.Z.locals;const u=(0,i(51900).Z)(l,(function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("div",{staticClass:"component-only-office"},[i("div",{staticClass:"placeholder",attrs:{id:this.id}}),e._v(" "),e.loadIng>0?i("div",{staticClass:"office-loading"},[i("Loading")],1):e._e()])}),[],!1,null,"c9bf06c2",null).exports}}]);
|
2
public/js/build/196.js
vendored
Normal file
2
public/js/build/196.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
public/js/build/214.js
vendored
1
public/js/build/214.js
vendored
@ -1 +0,0 @@
|
|||||||
"use strict";(self.webpackChunkDooTask=self.webpackChunkDooTask||[]).push([[214],{96099:(e,t,i)=>{i.d(t,{Z:()=>r});var n=i(1519),o=i.n(n)()((function(e){return e[1]}));o.push([e.id,".component-only-office[data-v-ba382ddc]{align-items:center;bottom:0;display:flex;justify-content:center;left:0;position:absolute;right:0;top:0}.component-only-office .placeholder[data-v-ba382ddc]{flex:1;height:100%;width:100%}.component-only-office .office-loading[data-v-ba382ddc]{align-items:center;bottom:0;display:flex;justify-content:center;left:0;position:absolute;right:0;top:0;z-index:2}",""]);const r=o},29214:(e,t,i)=>{i.r(t),i.d(t,{default:()=>f});var n=i(20629);function o(e,t){var i=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),i.push.apply(i,n)}return i}function r(e){for(var t=1;t<arguments.length;t++){var i=null!=arguments[t]?arguments[t]:{};t%2?o(Object(i),!0).forEach((function(t){a(e,t,i[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(i)):o(Object(i)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(i,t))}))}return e}function a(e,t,i){return t in e?Object.defineProperty(e,t,{value:i,enumerable:!0,configurable:!0,writable:!0}):e[t]=i,e}const c={name:"OnlyOffice",props:{id:{type:String,default:function(){return"office_"+Math.round(1e4*Math.random())}},code:{type:String,default:""},value:{type:[Object,Array],default:function(){return{}}},readOnly:{type:Boolean,default:!1}},data:function(){return{loadIng:0,docEditor:null}},mounted:function(){},beforeDestroy:function(){null!==this.docEditor&&(this.docEditor.destroyEditor(),this.docEditor=null)},computed:r(r({},(0,n.rn)(["userToken","userInfo","themeIsDark"])),{},{fileType:function(){return this.getType(this.value.type)},fileName:function(){return this.value.name}}),watch:{"value.id":{handler:function(e){var t=this;e&&(this.loadIng++,$A.loadScript($A.apiUrl("../office/web-apps/apps/api/documents/api.js"),(function(e){t.loadIng--,null!==e?$A.modalAlert("组件加载失败!"):t.loadFile()})))},immediate:!0}},methods:{getType:function(e){switch(e){case"word":return"docx";case"excel":return"xlsx";case"ppt":return"pptx"}return""},loadFile:function(){var e=this;null!==this.docEditor&&(this.docEditor.destroyEditor(),this.docEditor=null);var t="zh";switch(this.getLanguage()){case"CN":case"TC":t="zh";break;default:t="en"}var i=this.code||this.value.id,n={document:{fileType:this.fileType,key:this.fileType+"-"+i,title:this.fileName+"."+this.fileType,url:"http://nginx/api/file/content/?id="+i+"&token="+this.userToken},editorConfig:{mode:"edit",lang:t,user:{id:this.userInfo.userid,name:this.userInfo.nickname},customization:{uiTheme:this.themeIsDark?"theme-dark":"theme-classic-light"},callbackUrl:"http://nginx/api/file/content/office?id="+i+"&token="+this.userToken}};if(this.readOnly&&(n.editorConfig.mode="view",n.editorConfig.callbackUrl=null,!n.editorConfig.user.id)){var o=$A.getStorageInt("viewer");o||(o=$A.randNum(1e3,99999),$A.setStorage("viewer",o)),n.editorConfig.user.id="viewer_"+o,n.editorConfig.user.name="Viewer_"+o}this.$nextTick((function(){e.docEditor=new DocsAPI.DocEditor(e.id,n)}))}}};var l=i(93379),s=i.n(l),d=i(96099),u={insert:"head",singleton:!1};s()(d.Z,u);d.Z.locals;const f=(0,i(51900).Z)(c,(function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("div",{staticClass:"component-only-office"},[i("div",{staticClass:"placeholder",attrs:{id:this.id}}),e._v(" "),e.loadIng>0?i("div",{staticClass:"office-loading"},[i("Loading")],1):e._e()])}),[],!1,null,"ba382ddc",null).exports}}]);
|
|
1
public/js/build/264.js
vendored
Normal file
1
public/js/build/264.js
vendored
Normal file
File diff suppressed because one or more lines are too long
2
public/js/build/30.js
vendored
2
public/js/build/30.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
public/js/build/428.js
vendored
Normal file
1
public/js/build/428.js
vendored
Normal file
File diff suppressed because one or more lines are too long
2
public/js/build/43.js
vendored
2
public/js/build/43.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
public/js/build/660.js
vendored
2
public/js/build/660.js
vendored
File diff suppressed because one or more lines are too long
2
public/js/build/714.js
vendored
Normal file
2
public/js/build/714.js
vendored
Normal file
File diff suppressed because one or more lines are too long
2
public/js/build/79.js
vendored
2
public/js/build/79.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
public/js/build/954.js
vendored
2
public/js/build/954.js
vendored
File diff suppressed because one or more lines are too long
6
resources/assets/js/app.js
vendored
6
resources/assets/js/app.js
vendored
@ -11,7 +11,11 @@ import Language from './language/index'
|
|||||||
import store from './store/index'
|
import store from './store/index'
|
||||||
|
|
||||||
Vue.use(Vuex);
|
Vue.use(Vuex);
|
||||||
Vue.use(ViewUI);
|
Vue.use(ViewUI, {
|
||||||
|
modal: {
|
||||||
|
checkEscClose: true
|
||||||
|
}
|
||||||
|
});
|
||||||
Vue.use(VueRouter);
|
Vue.use(VueRouter);
|
||||||
Vue.use(Language);
|
Vue.use(Language);
|
||||||
|
|
||||||
|
@ -63,6 +63,7 @@
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
},
|
},
|
||||||
|
beforeClose: Function
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
@ -136,12 +137,25 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
mask () {
|
mask() {
|
||||||
if (this.maskClosable) {
|
if (this.maskClosable) {
|
||||||
this.close()
|
this.close()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
close() {
|
close() {
|
||||||
|
if (!this.beforeClose) {
|
||||||
|
return this.handleClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
const before = this.beforeClose();
|
||||||
|
|
||||||
|
if (before && before.then) {
|
||||||
|
before.then(this.handleClose);
|
||||||
|
} else {
|
||||||
|
this.handleClose();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleClose () {
|
||||||
this.$emit("input", false)
|
this.$emit("input", false)
|
||||||
},
|
},
|
||||||
escClose(e) {
|
escClose(e) {
|
||||||
|
@ -123,7 +123,7 @@ export default {
|
|||||||
case 'ppt':
|
case 'ppt':
|
||||||
return 'pptx'
|
return 'pptx'
|
||||||
}
|
}
|
||||||
return '';
|
return type;
|
||||||
},
|
},
|
||||||
|
|
||||||
loadFile() {
|
loadFile() {
|
||||||
@ -144,11 +144,12 @@ export default {
|
|||||||
}
|
}
|
||||||
//
|
//
|
||||||
let fileKey = this.code || this.value.id;
|
let fileKey = this.code || this.value.id;
|
||||||
|
let fileName = $A.strExists(this.fileName, '.') ? this.fileName : (this.fileName + '.' + this.fileType);
|
||||||
const config = {
|
const config = {
|
||||||
"document": {
|
"document": {
|
||||||
"fileType": this.fileType,
|
"fileType": this.fileType,
|
||||||
"key": this.fileType + '-' + fileKey,
|
"key": this.fileType + '-' + fileKey,
|
||||||
"title": this.fileName + '.' + this.fileType,
|
"title": fileName,
|
||||||
"url": 'http://nginx/api/file/content/?id=' + fileKey + '&token=' + this.userToken,
|
"url": 'http://nginx/api/file/content/?id=' + fileKey + '&token=' + this.userToken,
|
||||||
},
|
},
|
||||||
"editorConfig": {
|
"editorConfig": {
|
||||||
@ -164,6 +165,14 @@ export default {
|
|||||||
"callbackUrl": 'http://nginx/api/file/content/office?id=' + fileKey + '&token=' + this.userToken,
|
"callbackUrl": 'http://nginx/api/file/content/office?id=' + fileKey + '&token=' + this.userToken,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
if (/\/hideenOfficeTitle\//.test(window.navigator.userAgent)) {
|
||||||
|
config.document.title = " ";
|
||||||
|
}
|
||||||
|
if ($A.leftExists(fileKey, "msgFile_")) {
|
||||||
|
config.document.url = 'http://nginx/api/dialog/msg/download/?msg_id=' + $A.leftDelete(fileKey, "msgFile_") + '&token=' + this.userToken;
|
||||||
|
} else if ($A.leftExists(fileKey, "taskFile_")) {
|
||||||
|
config.document.url = 'http://nginx/api/project/task/filedown/?file_id=' + $A.leftDelete(fileKey, "taskFile_") + '&token=' + this.userToken;
|
||||||
|
}
|
||||||
if (this.readOnly) {
|
if (this.readOnly) {
|
||||||
config.editorConfig.mode = "view";
|
config.editorConfig.mode = "view";
|
||||||
config.editorConfig.callbackUrl = null;
|
config.editorConfig.callbackUrl = null;
|
||||||
|
@ -55,7 +55,7 @@ export default {
|
|||||||
},
|
},
|
||||||
autoEdit(val) {
|
autoEdit(val) {
|
||||||
if (val === true) {
|
if (val === true) {
|
||||||
this.onEdit();
|
setTimeout(this.onEdit, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1,22 +1,23 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-if="ready" :class="['common-user', maxHiddenClass]">
|
<div :class="['common-user', maxHiddenClass]">
|
||||||
<Select
|
<Select
|
||||||
v-model="values"
|
ref="select"
|
||||||
|
v-model="selects"
|
||||||
:transfer="transfer"
|
:transfer="transfer"
|
||||||
:remote-method="searchUser"
|
|
||||||
:placeholder="placeholder"
|
:placeholder="placeholder"
|
||||||
:size="size"
|
:size="size"
|
||||||
:loading="loading"
|
:loading="loadIng > 0"
|
||||||
:loading-text="$L('加载中...')"
|
:loading-text="$L('加载中...')"
|
||||||
:default-label="value"
|
:default-label="value"
|
||||||
:default-event-object="true"
|
:default-event-object="true"
|
||||||
:multipleMax="multipleMax"
|
:multiple-max="multipleMax"
|
||||||
:multipleUncancelable="uncancelable"
|
:multiple-uncancelable="uncancelable"
|
||||||
|
:remote-method="searchUser"
|
||||||
|
@on-query-change="searchUser"
|
||||||
|
@on-open-change="openChange"
|
||||||
multiple
|
multiple
|
||||||
filterable
|
filterable
|
||||||
transfer-class-name="common-user-transfer"
|
transfer-class-name="common-user-transfer">
|
||||||
@on-open-change="openChange"
|
|
||||||
@on-set-default-options="setDefaultOptions">
|
|
||||||
<div v-if="multipleMax" slot="drop-prepend" class="user-drop-prepend">{{$L('最多只能选择' + multipleMax + '个')}}</div>
|
<div v-if="multipleMax" slot="drop-prepend" class="user-drop-prepend">{{$L('最多只能选择' + multipleMax + '个')}}</div>
|
||||||
<slot name="option-prepend"></slot>
|
<slot name="option-prepend"></slot>
|
||||||
<Option
|
<Option
|
||||||
@ -33,7 +34,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</Option>
|
</Option>
|
||||||
</Select>
|
</Select>
|
||||||
<div v-if="!initialized" class="common-user-loading"><Loading/></div>
|
<div v-if="loadIng > 0" class="common-user-loading"><Loading/></div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -87,36 +88,23 @@
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
ready: false,
|
loadIng: 0,
|
||||||
initialized: false,
|
|
||||||
loading: false,
|
|
||||||
openLoad: false,
|
|
||||||
values: [],
|
|
||||||
|
|
||||||
|
selects: [],
|
||||||
list: [],
|
list: [],
|
||||||
options: [],
|
|
||||||
|
searchKey: null,
|
||||||
|
searchHistory: [],
|
||||||
|
|
||||||
subscribe: null,
|
subscribe: null,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
if ($A.isArray(this.value)) {
|
|
||||||
this.values = $A.cloneJSON(this.value);
|
|
||||||
} else {
|
|
||||||
this.$emit('input', this.value ? [this.value] : []);
|
|
||||||
}
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.ready = true;
|
|
||||||
});
|
|
||||||
this.subscribe = Store.subscribe('cacheUserActive', (data) => {
|
this.subscribe = Store.subscribe('cacheUserActive', (data) => {
|
||||||
let index = this.list.findIndex(({userid}) => userid == data.userid);
|
let index = this.list.findIndex(({userid}) => userid == data.userid);
|
||||||
if (index > -1) {
|
if (index > -1) {
|
||||||
this.initialized = true;
|
|
||||||
this.$set(this.list, index, Object.assign({}, this.list[index], data));
|
this.$set(this.list, index, Object.assign({}, this.list[index], data));
|
||||||
}
|
this.handleSelectData();
|
||||||
let option = this.options.find(({value}) => value == data.userid);
|
|
||||||
if (option) {
|
|
||||||
this.$set(option, 'label', data.nickname)
|
|
||||||
this.$set(option, 'avatar', data.userimg)
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -128,9 +116,9 @@
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
maxHiddenClass() {
|
maxHiddenClass() {
|
||||||
const {multipleMax, maxHiddenInput, values} = this;
|
const {multipleMax, maxHiddenInput, selects} = this;
|
||||||
if (multipleMax && maxHiddenInput) {
|
if (multipleMax && maxHiddenInput) {
|
||||||
if (values.length >= multipleMax) {
|
if (selects.length >= multipleMax) {
|
||||||
return 'hidden-input'
|
return 'hidden-input'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -138,61 +126,62 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
value(val) {
|
value: {
|
||||||
this.values = val;
|
handler() {
|
||||||
|
this.valueChange()
|
||||||
|
},
|
||||||
|
immediate: true,
|
||||||
},
|
},
|
||||||
values(val) {
|
selects(val) {
|
||||||
this.$emit('input', val);
|
this.$emit('input', val);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
openChange(show) {
|
searchUser(key) {
|
||||||
if (show && !this.openLoad) {
|
if (typeof key !== "string") key = "";
|
||||||
this.openLoad = true;
|
if (key == this.searchKey) return;
|
||||||
if (this.list.length == this.values.length || this.list.length <= 1) {
|
this.searchKey = key;
|
||||||
this.$nextTick(this.searchUser);
|
//
|
||||||
|
const history = this.searchHistory.find(item => item.key == key);
|
||||||
|
if (history) this.list = history.data;
|
||||||
|
//
|
||||||
|
if (!history) this.loadIng++;
|
||||||
|
setTimeout(() => {
|
||||||
|
if (this.searchKey != key) {
|
||||||
|
if (!history) this.loadIng--;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
setDefaultOptions(options) {
|
|
||||||
this.options = options;
|
|
||||||
options.forEach(({value, label}) => {
|
|
||||||
this.list.push({
|
|
||||||
userid: value,
|
|
||||||
nickname: label,
|
|
||||||
});
|
|
||||||
this.$store.dispatch("getUserBasic", {userid: value});
|
|
||||||
});
|
|
||||||
if (this.list.length == 0) {
|
|
||||||
this.initialized = true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
searchUser(query) {
|
|
||||||
if (query !== '') {
|
|
||||||
this.loading = true;
|
|
||||||
this.$store.dispatch("call", {
|
this.$store.dispatch("call", {
|
||||||
url: 'users/search',
|
url: 'users/search',
|
||||||
data: {
|
data: {
|
||||||
keys: {
|
keys: {
|
||||||
key: query || '',
|
key,
|
||||||
project_id: this.projectId,
|
project_id: this.projectId,
|
||||||
no_project_id: this.noProjectId,
|
no_project_id: this.noProjectId,
|
||||||
},
|
},
|
||||||
take: 30
|
take: 30
|
||||||
},
|
},
|
||||||
}).then(({data}) => {
|
}).then(({data}) => {
|
||||||
this.loading = false;
|
if (!history) this.loadIng--;
|
||||||
this.list = data;
|
this.list = data;
|
||||||
|
//
|
||||||
|
const index = this.searchHistory.findIndex(item => item.key == key);
|
||||||
|
const tmpData = {
|
||||||
|
key,
|
||||||
|
data,
|
||||||
|
time: $A.Time()
|
||||||
|
};
|
||||||
|
if (index > -1) {
|
||||||
|
this.searchHistory.splice(index, 1, tmpData)
|
||||||
|
} else {
|
||||||
|
this.searchHistory.push(tmpData)
|
||||||
|
}
|
||||||
}).catch(({msg}) => {
|
}).catch(({msg}) => {
|
||||||
this.loading = false;
|
if (!history) this.loadIng--;
|
||||||
this.list = [];
|
this.list = [];
|
||||||
$A.messageWarning(msg);
|
$A.messageWarning(msg);
|
||||||
});
|
});
|
||||||
} else {
|
}, this.searchHistory.length > 0 ? 300 : 0)
|
||||||
this.list = [];
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
isDisabled(userid) {
|
isDisabled(userid) {
|
||||||
@ -200,6 +189,48 @@
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return this.disabledChoice.includes(userid)
|
return this.disabledChoice.includes(userid)
|
||||||
|
},
|
||||||
|
|
||||||
|
openChange(show) {
|
||||||
|
if (show) {
|
||||||
|
this.$nextTick(this.searchUser);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
valueChange() {
|
||||||
|
if (this.selects == this.value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ($A.isArray(this.value)) {
|
||||||
|
this.selects = $A.cloneJSON(this.value);
|
||||||
|
} else if (this.value) {
|
||||||
|
this.selects = [this.value]
|
||||||
|
} else {
|
||||||
|
this.selects = [];
|
||||||
|
}
|
||||||
|
this.selects.some(userid => {
|
||||||
|
if (!this.list.find(item => item.userid == userid)) {
|
||||||
|
this.list.push({userid, nickname: userid});
|
||||||
|
this.$store.dispatch("getUserBasic", {userid});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
handleSelectData() {
|
||||||
|
this.__handleSelectTimeout && clearTimeout(this.__handleSelectTimeout);
|
||||||
|
this.__handleSelectTimeout = setTimeout(() => {
|
||||||
|
if (!this.$refs.select) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const list = this.$refs.select.getValue();
|
||||||
|
list && list.some(option => {
|
||||||
|
const data = this.list.find(({userid}) => userid == option.value)
|
||||||
|
if (data) {
|
||||||
|
this.$set(option, 'label', data.nickname)
|
||||||
|
this.$set(option, 'avatar', data.userimg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, 100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -214,6 +214,7 @@
|
|||||||
@on-click="show768Menu=!show768Menu">
|
@on-click="show768Menu=!show768Menu">
|
||||||
<div class="manage-mini-menu">
|
<div class="manage-mini-menu">
|
||||||
<Icon :type="show768Menu ? 'md-close' : 'md-menu'" />
|
<Icon :type="show768Menu ? 'md-close' : 'md-menu'" />
|
||||||
|
<Badge :count="unreadTotal"/>
|
||||||
</div>
|
</div>
|
||||||
</DragBallComponent>
|
</DragBallComponent>
|
||||||
</div>
|
</div>
|
||||||
@ -300,10 +301,6 @@ export default {
|
|||||||
//
|
//
|
||||||
document.addEventListener('keydown', this.shortcutEvent);
|
document.addEventListener('keydown', this.shortcutEvent);
|
||||||
window.addEventListener('resize', this.innerHeightListener);
|
window.addEventListener('resize', this.innerHeightListener);
|
||||||
//
|
|
||||||
if (this.$Electron) {
|
|
||||||
this.$Electron.ipcRenderer.send('setDockBadge', 0);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
@ -357,6 +354,10 @@ export default {
|
|||||||
return this.dashboardTask.today.length + this.dashboardTask.overdue.length
|
return this.dashboardTask.today.length + this.dashboardTask.overdue.length
|
||||||
},
|
},
|
||||||
|
|
||||||
|
unreadTotal() {
|
||||||
|
return this.msgAllUnread + this.dashboardTotal + this.reportUnreadNumber;
|
||||||
|
},
|
||||||
|
|
||||||
currentLanguage() {
|
currentLanguage() {
|
||||||
return this.languageList[this.languageType] || 'Language'
|
return this.languageList[this.languageType] || 'Language'
|
||||||
},
|
},
|
||||||
@ -421,24 +422,6 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
msgAllUnread() {
|
|
||||||
if (this.$Electron) {
|
|
||||||
this.$Electron.ipcRenderer.send('setDockBadge', this.msgAllUnread + this.dashboardTotal + this.reportUnreadNumber);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
dashboardTotal() {
|
|
||||||
if (this.$Electron) {
|
|
||||||
this.$Electron.ipcRenderer.send('setDockBadge', this.msgAllUnread + this.dashboardTotal + this.reportUnreadNumber);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
reportUnreadNumber() {
|
|
||||||
if (this.$Electron) {
|
|
||||||
this.$Electron.ipcRenderer.send('setDockBadge', this.msgAllUnread + this.dashboardTotal + this.reportUnreadNumber);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
projectKeyValue(val) {
|
projectKeyValue(val) {
|
||||||
if (val == '') {
|
if (val == '') {
|
||||||
return;
|
return;
|
||||||
@ -468,6 +451,15 @@ export default {
|
|||||||
}, 5000)
|
}, 5000)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
unreadTotal: {
|
||||||
|
handler(num) {
|
||||||
|
if (this.$Electron) {
|
||||||
|
this.$Electron.ipcRenderer.send('setDockBadge', num);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true
|
||||||
|
},
|
||||||
|
|
||||||
wsMsg: {
|
wsMsg: {
|
||||||
handler(info) {
|
handler(info) {
|
||||||
const {type, action} = info;
|
const {type, action} = info;
|
||||||
|
@ -1,16 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="dialog-view" :data-id="msgData.id">
|
<div :class="`dialog-view ${msgData.type}`" :data-id="msgData.id">
|
||||||
|
|
||||||
<!--文本-->
|
<!--文本-->
|
||||||
<div v-if="msgData.type === 'text'" class="dialog-content">
|
<div v-if="msgData.type === 'text'" class="dialog-content">
|
||||||
<pre class="no-dark-mode" v-html="textMsg(msgData.msg.text)"></pre>
|
<pre class="no-dark-mode">{{textMsg(msgData.msg.text)}}</pre>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!--等待-->
|
<!--等待-->
|
||||||
<div v-else-if="msgData.type === 'loading'" class="dialog-content loading"><Loading/></div>
|
<div v-else-if="msgData.type === 'loading'" class="dialog-content loading"><Loading/></div>
|
||||||
|
|
||||||
<!--文件-->
|
<!--文件-->
|
||||||
<div v-else-if="msgData.type === 'file'" :class="['dialog-content', msgData.msg.type]">
|
<div v-else-if="msgData.type === 'file'" :class="['dialog-content', msgData.msg.type]">
|
||||||
<div class="dialog-file" @click="downFile">
|
<div class="dialog-file">
|
||||||
<img v-if="msgData.msg.type === 'img'" class="file-img" :style="imageStyle(msgData.msg)" :src="msgData.msg.thumb"/>
|
<img v-if="msgData.msg.type === 'img'" class="file-img" :style="imageStyle(msgData.msg)" :src="msgData.msg.thumb" @click="viewFile"/>
|
||||||
<div v-else class="file-box">
|
<div v-else class="file-box">
|
||||||
<img class="file-thumb" :src="msgData.msg.thumb"/>
|
<img class="file-thumb" :src="msgData.msg.thumb"/>
|
||||||
<div class="file-info">
|
<div class="file-info">
|
||||||
@ -19,7 +21,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="dialog-file-menu">
|
||||||
|
<div class="file-menu-warp"></div>
|
||||||
|
<div class="file-menu-icon">
|
||||||
|
<Icon @click="viewFile" type="md-eye" />
|
||||||
|
<Icon @click="downFile" type="md-arrow-round-down" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!--未知-->
|
<!--未知-->
|
||||||
<div v-else class="dialog-content unknown">{{$L("未知的消息类型")}}</div>
|
<div v-else class="dialog-content unknown">{{$L("未知的消息类型")}}</div>
|
||||||
|
|
||||||
@ -138,8 +148,7 @@ export default {
|
|||||||
if (!text) {
|
if (!text) {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
text = text.trim().replace(/(\n\x20*){3,}/g, "<br/><br/>");
|
text = text.trim().replace(/(\n\x20*){3,}/g, "\n\n");
|
||||||
text = text.trim().replace(/\n/g, "<br/>");
|
|
||||||
return text;
|
return text;
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -167,6 +176,25 @@ export default {
|
|||||||
return {};
|
return {};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
viewFile() {
|
||||||
|
if (this.$Electron) {
|
||||||
|
this.$Electron.ipcRenderer.send('windowRouter', {
|
||||||
|
title: `${this.msgData.msg.name} (${$A.bytesToSize(this.msgData.msg.size)})`,
|
||||||
|
titleFixed: true,
|
||||||
|
name: 'file-msg-' + this.msgData.id,
|
||||||
|
path: "/single/file/msg/" + this.msgData.id,
|
||||||
|
force: false,
|
||||||
|
config: {
|
||||||
|
parent: null,
|
||||||
|
width: Math.min(window.screen.availWidth, 1440),
|
||||||
|
height: Math.min(window.screen.availHeight, 900),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
window.open($A.apiUrl(`../single/file/msg/${this.msgData.id}`))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
downFile() {
|
downFile() {
|
||||||
$A.modalConfirm({
|
$A.modalConfirm({
|
||||||
title: '下载文件',
|
title: '下载文件',
|
||||||
|
@ -213,12 +213,21 @@ export default {
|
|||||||
watch: {
|
watch: {
|
||||||
'$route': {
|
'$route': {
|
||||||
handler (route) {
|
handler (route) {
|
||||||
if (route.query && route.query.sendmsg && this.msgText == '') {
|
if ($A.isJson(window.__sendDialogMsg) && window.__sendDialogMsg.time > $A.Time()) {
|
||||||
|
const {msgFile, msgText} = window.__sendDialogMsg;
|
||||||
|
window.__sendDialogMsg = null;
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if ($A.isArray(msgFile) && msgFile.length > 0) {
|
||||||
|
this.sendFileMsg(msgFile);
|
||||||
|
} else if (msgText) {
|
||||||
|
this.sendMsg(msgText);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (route.query && route.query._) {
|
||||||
let query = $A.cloneJSON(route.query);
|
let query = $A.cloneJSON(route.query);
|
||||||
delete query.sendmsg;
|
delete query._;
|
||||||
this.goForward({query}, true);
|
this.goForward({query}, true);
|
||||||
this.msgText = route.query.sendmsg;
|
|
||||||
this.$nextTick(this.sendMsg);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
immediate: true
|
immediate: true
|
||||||
@ -279,24 +288,11 @@ export default {
|
|||||||
this.msgText = '';
|
this.msgText = '';
|
||||||
},
|
},
|
||||||
|
|
||||||
chatKeydown(e) {
|
sendFileMsg(files) {
|
||||||
if (e.keyCode === 13) {
|
if (files.length > 0) {
|
||||||
if (e.shiftKey) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
e.preventDefault();
|
|
||||||
this.sendMsg();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
pasteDrag(e, type) {
|
|
||||||
const files = type === 'drag' ? e.dataTransfer.files : e.clipboardData.files;
|
|
||||||
const postFiles = Array.prototype.slice.call(files);
|
|
||||||
if (postFiles.length > 0) {
|
|
||||||
e.preventDefault();
|
|
||||||
this.pasteFile = [];
|
this.pasteFile = [];
|
||||||
this.pasteItem = [];
|
this.pasteItem = [];
|
||||||
postFiles.some(file => {
|
files.some(file => {
|
||||||
let reader = new FileReader();
|
let reader = new FileReader();
|
||||||
reader.readAsDataURL(file);
|
reader.readAsDataURL(file);
|
||||||
reader.onload = ({target}) => {
|
reader.onload = ({target}) => {
|
||||||
@ -313,10 +309,28 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
pasteSend() {
|
chatKeydown(e) {
|
||||||
this.pasteFile.some(file => {
|
if (e.keyCode === 13) {
|
||||||
this.$refs.chatUpload.upload(file)
|
if (e.shiftKey) {
|
||||||
});
|
return;
|
||||||
|
}
|
||||||
|
e.preventDefault();
|
||||||
|
this.sendMsg();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
pasteDrag(e, type) {
|
||||||
|
const files = type === 'drag' ? e.dataTransfer.files : e.clipboardData.files;
|
||||||
|
const postFiles = Array.prototype.slice.call(files);
|
||||||
|
if (postFiles.length > 0) {
|
||||||
|
e.preventDefault();
|
||||||
|
this.sendFileMsg(postFiles);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
chatPasteDrag(e, type) {
|
||||||
|
this.dialogDrag = false;
|
||||||
|
this.pasteDrag(e, type);
|
||||||
},
|
},
|
||||||
|
|
||||||
chatDragOver(show, e) {
|
chatDragOver(show, e) {
|
||||||
@ -335,29 +349,10 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
chatPasteDrag(e, type) {
|
pasteSend() {
|
||||||
this.dialogDrag = false;
|
this.pasteFile.some(file => {
|
||||||
const files = type === 'drag' ? e.dataTransfer.files : e.clipboardData.files;
|
this.$refs.chatUpload.upload(file)
|
||||||
const postFiles = Array.prototype.slice.call(files);
|
});
|
||||||
if (postFiles.length > 0) {
|
|
||||||
e.preventDefault();
|
|
||||||
this.pasteFile = [];
|
|
||||||
this.pasteItem = [];
|
|
||||||
postFiles.some(file => {
|
|
||||||
let reader = new FileReader();
|
|
||||||
reader.readAsDataURL(file);
|
|
||||||
reader.onload = ({target}) => {
|
|
||||||
this.pasteFile.push(file)
|
|
||||||
this.pasteItem.push({
|
|
||||||
type: $A.getMiddle(file.type, null, '/'),
|
|
||||||
name: file.name,
|
|
||||||
size: file.size,
|
|
||||||
result: target.result
|
|
||||||
})
|
|
||||||
this.pasteShow = true
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
chatFile(type, file) {
|
chatFile(type, file) {
|
||||||
|
@ -112,6 +112,7 @@ export default {
|
|||||||
document.addEventListener('keydown', this.keySave);
|
document.addEventListener('keydown', this.keySave);
|
||||||
window.addEventListener('message', this.handleMessage)
|
window.addEventListener('message', this.handleMessage)
|
||||||
},
|
},
|
||||||
|
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
document.removeEventListener('keydown', this.keySave);
|
document.removeEventListener('keydown', this.keySave);
|
||||||
window.removeEventListener('message', this.handleMessage)
|
window.removeEventListener('message', this.handleMessage)
|
||||||
@ -130,6 +131,18 @@ export default {
|
|||||||
deep: true,
|
deep: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
value: {
|
||||||
|
handler(val) {
|
||||||
|
if (val) {
|
||||||
|
this.ready = true;
|
||||||
|
this.editUser = [this.userId];
|
||||||
|
} else {
|
||||||
|
this.fileContent[this.fileId] = this.contentDetail;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
|
|
||||||
wsMsg: {
|
wsMsg: {
|
||||||
handler(info) {
|
handler(info) {
|
||||||
const {type, data} = info;
|
const {type, data} = info;
|
||||||
@ -158,21 +171,15 @@ export default {
|
|||||||
deep: true,
|
deep: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
value: {
|
wsOpenNum() {
|
||||||
handler(val) {
|
if (this.$isSubElectron) {
|
||||||
if (val) {
|
this.$store.dispatch("websocketPath", "file/content/" + this.fileId);
|
||||||
this.ready = true;
|
}
|
||||||
this.editUser = [this.userId];
|
|
||||||
} else {
|
|
||||||
this.fileContent[this.fileId] = this.contentDetail;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
immediate: true,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(['fileContent', 'wsMsg', 'userId']),
|
...mapState(['fileContent', 'wsMsg', 'userId', 'wsOpenNum']),
|
||||||
|
|
||||||
equalContent() {
|
equalContent() {
|
||||||
return this.contentBak == $A.jsonStringify(this.contentDetail);
|
return this.contentBak == $A.jsonStringify(this.contentDetail);
|
||||||
@ -246,6 +253,10 @@ export default {
|
|||||||
this.loadContent--;
|
this.loadContent--;
|
||||||
this.contentDetail = data.content;
|
this.contentDetail = data.content;
|
||||||
this.updateBak();
|
this.updateBak();
|
||||||
|
//
|
||||||
|
if (this.$isSubElectron) {
|
||||||
|
this.$store.dispatch("websocketConnection")
|
||||||
|
}
|
||||||
}).catch(({msg}) => {
|
}).catch(({msg}) => {
|
||||||
$A.modalError(msg);
|
$A.modalError(msg);
|
||||||
this.loadIng--;
|
this.loadIng--;
|
||||||
|
@ -35,7 +35,7 @@
|
|||||||
</ETooltip>
|
</ETooltip>
|
||||||
</li>
|
</li>
|
||||||
<li :class="['project-icon', searchText!='' ? 'active' : '']">
|
<li :class="['project-icon', searchText!='' ? 'active' : '']">
|
||||||
<Tooltip :always="searchAlways" @on-popper-show="searchFocus" theme="light">
|
<Tooltip :always="searchText!=''" @on-popper-show="searchFocus" theme="light" :rawIndex="10">
|
||||||
<Icon class="menu-icon" type="ios-search" @click="searchFocus" />
|
<Icon class="menu-icon" type="ios-search" @click="searchFocus" />
|
||||||
<div slot="content">
|
<div slot="content">
|
||||||
<Input v-model="searchText" ref="searchInput" :placeholder="$L('名称、描述...')" class="search-input" clearable/>
|
<Input v-model="searchText" ref="searchInput" :placeholder="$L('名称、描述...')" class="search-input" clearable/>
|
||||||
@ -343,7 +343,7 @@
|
|||||||
:mask-closable="false">
|
:mask-closable="false">
|
||||||
<Form :model="userData" label-width="auto" @submit.native.prevent>
|
<Form :model="userData" label-width="auto" @submit.native.prevent>
|
||||||
<FormItem prop="userids" :label="$L('项目成员')">
|
<FormItem prop="userids" :label="$L('项目成员')">
|
||||||
<UserInput v-if="userShow" v-model="userData.userids" :uncancelable="userData.uncancelable" :multiple-max="100" :placeholder="$L('选择项目成员')"/>
|
<UserInput v-model="userData.userids" :uncancelable="userData.uncancelable" :multiple-max="100" :placeholder="$L('选择项目成员')"/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</Form>
|
</Form>
|
||||||
<div slot="footer" class="adaption">
|
<div slot="footer" class="adaption">
|
||||||
@ -405,7 +405,7 @@
|
|||||||
:mask-closable="false">
|
:mask-closable="false">
|
||||||
<Form :model="transferData" label-width="auto" @submit.native.prevent>
|
<Form :model="transferData" label-width="auto" @submit.native.prevent>
|
||||||
<FormItem prop="owner_userid" :label="$L('项目负责人')">
|
<FormItem prop="owner_userid" :label="$L('项目负责人')">
|
||||||
<UserInput v-if="transferShow" v-model="transferData.owner_userid" :multiple-max="1" :placeholder="$L('选择项目负责人')"/>
|
<UserInput v-model="transferData.owner_userid" :multiple-max="1" :placeholder="$L('选择项目负责人')"/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</Form>
|
</Form>
|
||||||
<div slot="footer" class="adaption">
|
<div slot="footer" class="adaption">
|
||||||
@ -418,8 +418,9 @@
|
|||||||
<DrawerOverlay
|
<DrawerOverlay
|
||||||
v-model="workflowShow"
|
v-model="workflowShow"
|
||||||
placement="right"
|
placement="right"
|
||||||
|
:beforeClose="workflowBeforeClose"
|
||||||
:size="1280">
|
:size="1280">
|
||||||
<ProjectWorkflow v-if="workflowShow" :project-id="projectId"/>
|
<ProjectWorkflow ref="workflow" v-if="workflowShow" :project-id="projectId"/>
|
||||||
</DrawerOverlay>
|
</DrawerOverlay>
|
||||||
|
|
||||||
<!--查看项目动态-->
|
<!--查看项目动态-->
|
||||||
@ -550,17 +551,6 @@ export default {
|
|||||||
|
|
||||||
...mapGetters(['projectData', 'projectParameter', 'transforTasks']),
|
...mapGetters(['projectData', 'projectParameter', 'transforTasks']),
|
||||||
|
|
||||||
searchAlways() {
|
|
||||||
return !(!this.searchText
|
|
||||||
|| this.settingShow
|
|
||||||
|| this.userShow
|
|
||||||
|| this.inviteShow
|
|
||||||
|| this.transferShow
|
|
||||||
|| this.workflowShow
|
|
||||||
|| this.logShow
|
|
||||||
|| this.archivedTaskShow);
|
|
||||||
},
|
|
||||||
|
|
||||||
userWaitRemove() {
|
userWaitRemove() {
|
||||||
const {userids, useridbak} = this.userData;
|
const {userids, useridbak} = this.userData;
|
||||||
if (!userids) {
|
if (!userids) {
|
||||||
@ -1230,6 +1220,27 @@ export default {
|
|||||||
this.$store.dispatch('toggleProjectParameter', 'completedTask');
|
this.$store.dispatch('toggleProjectParameter', 'completedTask');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
workflowBeforeClose() {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
if (!this.$refs.workflow.existDiff()) {
|
||||||
|
resolve()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
$A.modalConfirm({
|
||||||
|
content: '设置尚未保存,是否放弃修改?',
|
||||||
|
cancelText: '放弃',
|
||||||
|
okText: '保存',
|
||||||
|
onCancel: () => {
|
||||||
|
resolve()
|
||||||
|
},
|
||||||
|
onOk: () => {
|
||||||
|
this.$refs.workflow.saveAll()
|
||||||
|
resolve()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
myFilter(task, chackCompleted = true) {
|
myFilter(task, chackCompleted = true) {
|
||||||
if (task.archived_at) {
|
if (task.archived_at) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -143,14 +143,16 @@
|
|||||||
:mask-closable="false">
|
:mask-closable="false">
|
||||||
<Form :model="userData" label-width="auto" @submit.native.prevent>
|
<Form :model="userData" label-width="auto" @submit.native.prevent>
|
||||||
<FormItem prop="userids" :label="$L('状态负责人')">
|
<FormItem prop="userids" :label="$L('状态负责人')">
|
||||||
<UserInput v-if="userShow" v-model="userData.userids" :project-id="projectId" :multiple-max="5" :placeholder="$L('选择状态负责人')"/>
|
<UserInput v-model="userData.userids" :project-id="projectId" :multiple-max="5" :placeholder="$L('选择状态负责人')"/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem prop="usertype" :label="$L('流转模式')">
|
<FormItem prop="usertype" :label="$L('流转模式')">
|
||||||
<RadioGroup v-model="userData.usertype">
|
<RadioGroup v-model="userData.usertype">
|
||||||
<Radio label="add">{{$L('添加模式')}}</Radio>
|
<Radio label="add">{{$L('添加模式')}}</Radio>
|
||||||
<Radio label="replace">{{$L('流转模式')}}</Radio>
|
<Radio label="replace">{{$L('流转模式')}}</Radio>
|
||||||
|
<Radio label="merge">{{$L('剔除模式')}}</Radio>
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
<div v-if="userData.usertype=='replace'" class="form-tip">{{$L('流转到此状态时改变任务负责人为状态负责人,原本的任务负责人移至协助人员。')}}</div>
|
<div v-if="userData.usertype=='replace'" class="form-tip">{{$L('流转到此状态时改变任务负责人为状态负责人,原本的任务负责人移至协助人员。')}}</div>
|
||||||
|
<div v-else-if="userData.usertype=='merge'" class="form-tip">{{$L('流转到此状态时改变任务负责人为状态负责人(并保留操作状态的人员),原本的任务负责人移至协助人员。')}}</div>
|
||||||
<div v-else class="form-tip">{{$L('流转到此状态时添加状态负责人至任务负责人。')}}</div>
|
<div v-else class="form-tip">{{$L('流转到此状态时添加状态负责人至任务负责人。')}}</div>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem prop="userlimit" :label="$L('限制负责人')">
|
<FormItem prop="userlimit" :label="$L('限制负责人')">
|
||||||
@ -259,6 +261,12 @@ export default {
|
|||||||
return JSON.stringify(project_flow_item) != project_flow_bak
|
return JSON.stringify(project_flow_item) != project_flow_bak
|
||||||
},
|
},
|
||||||
|
|
||||||
|
existDiff() {
|
||||||
|
return !!this.list.find(data => {
|
||||||
|
return this.contrast(data.project_flow_item, data.project_flow_bak)
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
onCreate() {
|
onCreate() {
|
||||||
let id = -1 * $A.randNum(1000, 10000);
|
let id = -1 * $A.randNum(1000, 10000);
|
||||||
this.list.push({
|
this.list.push({
|
||||||
@ -466,6 +474,14 @@ export default {
|
|||||||
$A.modalError(msg);
|
$A.modalError(msg);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
saveAll() {
|
||||||
|
this.list.some(data => {
|
||||||
|
if (this.contrast(data.project_flow_item, data.project_flow_bak)) {
|
||||||
|
this.onSave(data)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -39,7 +39,6 @@
|
|||||||
<Col span="22">
|
<Col span="22">
|
||||||
<div class="report-users">
|
<div class="report-users">
|
||||||
<UserInput
|
<UserInput
|
||||||
v-if="userInputShow"
|
|
||||||
v-model="reportData.receive"
|
v-model="reportData.receive"
|
||||||
:disabledChoice="[userId]"
|
:disabledChoice="[userId]"
|
||||||
:placeholder="$L('选择接收人')"
|
:placeholder="$L('选择接收人')"
|
||||||
@ -95,7 +94,6 @@ export default {
|
|||||||
offset: 0 // 以当前日期为基础的周期偏移量。例如选择了上一周那么就是 -1,上一天同理。
|
offset: 0 // 以当前日期为基础的周期偏移量。例如选择了上一周那么就是 -1,上一天同理。
|
||||||
},
|
},
|
||||||
disabledType: false,
|
disabledType: false,
|
||||||
userInputShow: true,
|
|
||||||
prevCycleText: "",
|
prevCycleText: "",
|
||||||
nextCycleText: "",
|
nextCycleText: "",
|
||||||
};
|
};
|
||||||
@ -140,7 +138,7 @@ export default {
|
|||||||
title: '覆盖提交',
|
title: '覆盖提交',
|
||||||
content: '你已提交过此日期的报告,是否覆盖提交?',
|
content: '你已提交过此日期的报告,是否覆盖提交?',
|
||||||
loading: true,
|
loading: true,
|
||||||
zIndex: 2000,
|
append: this.$el,
|
||||||
onOk: () => {
|
onOk: () => {
|
||||||
this.doSubmit();
|
this.doSubmit();
|
||||||
}
|
}
|
||||||
@ -217,7 +215,6 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
getDetail(reportId) {
|
getDetail(reportId) {
|
||||||
this.userInputShow = false;
|
|
||||||
this.$store.dispatch("call", {
|
this.$store.dispatch("call", {
|
||||||
url: 'report/detail',
|
url: 'report/detail',
|
||||||
data: {
|
data: {
|
||||||
@ -231,12 +228,10 @@ export default {
|
|||||||
this.reportData.type = data.type_val;
|
this.reportData.type = data.type_val;
|
||||||
this.reportData.id = reportId;
|
this.reportData.id = reportId;
|
||||||
this.disabledType = true;
|
this.disabledType = true;
|
||||||
this.userInputShow = true;
|
|
||||||
// msg 结果描述
|
// msg 结果描述
|
||||||
}).catch(({msg}) => {
|
}).catch(({msg}) => {
|
||||||
// msg 错误原因
|
// msg 错误原因
|
||||||
$A.messageError(msg);
|
$A.messageError(msg);
|
||||||
this.userInputShow = true;
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -259,18 +254,15 @@ export default {
|
|||||||
|
|
||||||
// 获取上一次接收人
|
// 获取上一次接收人
|
||||||
getLastSubmitter() {
|
getLastSubmitter() {
|
||||||
this.userInputShow = false;
|
|
||||||
this.$store.dispatch("call", {
|
this.$store.dispatch("call", {
|
||||||
url: 'report/last_submitter',
|
url: 'report/last_submitter',
|
||||||
}).then(({data, msg}) => {
|
}).then(({data, msg}) => {
|
||||||
// data 结果数据
|
// data 结果数据
|
||||||
this.reportData.receive = data;
|
this.reportData.receive = data;
|
||||||
this.userInputShow = true;
|
|
||||||
// msg 结果描述
|
// msg 结果描述
|
||||||
}).catch(({msg}) => {
|
}).catch(({msg}) => {
|
||||||
// msg 错误原因
|
// msg 错误原因
|
||||||
$A.messageError(msg);
|
$A.messageError(msg);
|
||||||
this.userInputShow = true;
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
:clearable="false"
|
:clearable="false"
|
||||||
:placeholder="$L('请选择项目')"
|
:placeholder="$L('请选择项目')"
|
||||||
:load-data="cascaderLoadData"
|
:load-data="cascaderLoadData"
|
||||||
|
@on-change="cascaderChange"
|
||||||
@on-input-change="cascaderInputChange"
|
@on-input-change="cascaderInputChange"
|
||||||
@on-visible-change="cascaderShow=!cascaderShow"
|
@on-visible-change="cascaderShow=!cascaderShow"
|
||||||
filterable/>
|
filterable/>
|
||||||
@ -81,8 +82,13 @@
|
|||||||
v-model="addData.owner"
|
v-model="addData.owner"
|
||||||
:multiple-max="10"
|
:multiple-max="10"
|
||||||
:placeholder="$L('选择任务负责人')"
|
:placeholder="$L('选择任务负责人')"
|
||||||
:project-id="addData.project_id"
|
:project-id="addData.project_id"/>
|
||||||
:transfer="false"/>
|
<div v-if="showAddAssist" class="task-add-assist">
|
||||||
|
<Checkbox v-model="addData.add_assist" :true-value="1" :false-value="0">{{$L('加入任务协助人员列表')}}</Checkbox>
|
||||||
|
<ETooltip :content="$L('你不是任务负责人时建议加入任务协助人员列表')">
|
||||||
|
<Icon type="ios-alert-outline" />
|
||||||
|
</ETooltip>
|
||||||
|
</div>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<div class="subtasks">
|
<div class="subtasks">
|
||||||
<div v-if="addData.subtasks.length > 0" class="sublist">
|
<div v-if="addData.subtasks.length > 0" class="sublist">
|
||||||
@ -170,6 +176,7 @@ export default {
|
|||||||
name: "",
|
name: "",
|
||||||
content: "",
|
content: "",
|
||||||
owner: 0,
|
owner: 0,
|
||||||
|
add_assist: 1,
|
||||||
project_id: 0,
|
project_id: 0,
|
||||||
column_id: 0,
|
column_id: 0,
|
||||||
times: [],
|
times: [],
|
||||||
@ -236,6 +243,14 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
showAddAssist() {
|
||||||
|
const {owner} = this.addData;
|
||||||
|
if ($A.isArray(owner) && owner.includes(this.userId)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return owner != this.userId;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@ -256,18 +271,22 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
'addData.project_id'(id) {
|
'addData.project_id'(id) {
|
||||||
$A.setStorage("cacheAddTaskProjectId", id);
|
if (id > 0) {
|
||||||
|
$A.setStorage("cacheAddTaskProjectId", id);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
'addData.column_id'(id) {
|
'addData.column_id'(id) {
|
||||||
const {project_id, column_id} = this.addData;
|
const {project_id} = this.addData;
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
if (project_id && column_id) {
|
if (project_id && id) {
|
||||||
this.$set(this.addData, 'cascader', [project_id, column_id]);
|
this.$set(this.addData, 'cascader', [project_id, id]);
|
||||||
} else {
|
} else {
|
||||||
this.$set(this.addData, 'cascader', []);
|
this.$set(this.addData, 'cascader', []);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
$A.setStorage("cacheAddTaskColumnId", id);
|
if (id > 0) {
|
||||||
|
$A.setStorage("cacheAddTaskColumnId", id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@ -412,6 +431,10 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
cascaderChange(value) {
|
||||||
|
value[1] && this.$set(this.addData, 'column_id', value[1])
|
||||||
|
},
|
||||||
|
|
||||||
cascaderInputChange(key) {
|
cascaderInputChange(key) {
|
||||||
this.cascaderValue = key || "";
|
this.cascaderValue = key || "";
|
||||||
//
|
//
|
||||||
@ -470,6 +493,7 @@ export default {
|
|||||||
name: "",
|
name: "",
|
||||||
content: "",
|
content: "",
|
||||||
owner: 0,
|
owner: 0,
|
||||||
|
add_assist: 1,
|
||||||
column_id: 0,
|
column_id: 0,
|
||||||
times: [],
|
times: [],
|
||||||
subtasks: [],
|
subtasks: [],
|
||||||
|
@ -45,12 +45,10 @@
|
|||||||
:width="240"
|
:width="240"
|
||||||
placement="bottom"
|
placement="bottom"
|
||||||
@on-popper-show="openOwner"
|
@on-popper-show="openOwner"
|
||||||
@on-popper-hide="ownerShow=false"
|
|
||||||
@on-ok="onOwner"
|
@on-ok="onOwner"
|
||||||
transfer>
|
transfer>
|
||||||
<div slot="content">
|
<div slot="content">
|
||||||
<UserInput
|
<UserInput
|
||||||
v-if="ownerShow"
|
|
||||||
v-model="ownerData.owner_userid"
|
v-model="ownerData.owner_userid"
|
||||||
:multiple-max="1"
|
:multiple-max="1"
|
||||||
:project-id="taskDetail.project_id"
|
:project-id="taskDetail.project_id"
|
||||||
@ -66,7 +64,12 @@
|
|||||||
</Poptip>
|
</Poptip>
|
||||||
</li>
|
</li>
|
||||||
<!--主任务-->
|
<!--主任务-->
|
||||||
<div v-else-if="ready" :class="{'task-detail':true, 'open-dialog': hasOpenDialog, 'completed': taskDetail.complete_at}">
|
<div
|
||||||
|
v-else-if="ready"
|
||||||
|
:class="{'task-detail':true, 'open-dialog': hasOpenDialog, 'completed': taskDetail.complete_at}"
|
||||||
|
@drop.prevent="taskPasteDrag($event, 'drag')"
|
||||||
|
@dragover.prevent="taskDragOver(true, $event)"
|
||||||
|
@dragleave.prevent="taskDragOver(false, $event)">
|
||||||
<div v-show="taskDetail.id > 0" class="task-info">
|
<div v-show="taskDetail.id > 0" class="task-info">
|
||||||
<div class="head">
|
<div class="head">
|
||||||
<TaskMenu
|
<TaskMenu
|
||||||
@ -188,12 +191,10 @@
|
|||||||
class="item-content user"
|
class="item-content user"
|
||||||
placement="bottom"
|
placement="bottom"
|
||||||
@on-popper-show="openOwner"
|
@on-popper-show="openOwner"
|
||||||
@on-popper-hide="ownerShow=false"
|
|
||||||
@on-ok="onOwner"
|
@on-ok="onOwner"
|
||||||
transfer>
|
transfer>
|
||||||
<div slot="content">
|
<div slot="content">
|
||||||
<UserInput
|
<UserInput
|
||||||
v-if="ownerShow"
|
|
||||||
v-model="ownerData.owner_userid"
|
v-model="ownerData.owner_userid"
|
||||||
:multiple-max="10"
|
:multiple-max="10"
|
||||||
:project-id="taskDetail.project_id"
|
:project-id="taskDetail.project_id"
|
||||||
@ -218,12 +219,10 @@
|
|||||||
class="item-content user"
|
class="item-content user"
|
||||||
placement="bottom"
|
placement="bottom"
|
||||||
@on-popper-show="openAssist"
|
@on-popper-show="openAssist"
|
||||||
@on-popper-hide="assistShow=false"
|
|
||||||
@on-ok="onAssist"
|
@on-ok="onAssist"
|
||||||
transfer>
|
transfer>
|
||||||
<div slot="content">
|
<div slot="content">
|
||||||
<UserInput
|
<UserInput
|
||||||
v-if="assistShow"
|
|
||||||
v-model="assistData.assist_userid"
|
v-model="assistData.assist_userid"
|
||||||
:multiple-max="10"
|
:multiple-max="10"
|
||||||
:project-id="taskDetail.project_id"
|
:project-id="taskDetail.project_id"
|
||||||
@ -274,18 +273,22 @@
|
|||||||
<li v-for="file in fileList">
|
<li v-for="file in fileList">
|
||||||
<img v-if="file.id" class="file-ext" :src="file.thumb"/>
|
<img v-if="file.id" class="file-ext" :src="file.thumb"/>
|
||||||
<Loading v-else class="file-load"/>
|
<Loading v-else class="file-load"/>
|
||||||
<div class="file-name" @click="downFile(file)">{{file.name}}</div>
|
<div class="file-name">{{file.name}}</div>
|
||||||
<div class="file-size">{{$A.bytesToSize(file.size)}}</div>
|
<div class="file-size">{{$A.bytesToSize(file.size)}}</div>
|
||||||
<EPopover v-model="file._deling" class="file-delete">
|
<div class="file-menu" :class="{show:file._show_menu}">
|
||||||
<div class="task-detail-delete-file-popover">
|
<Icon @click="viewFile(file)" type="md-eye" />
|
||||||
<p>{{$L('你确定要删除这个文件吗?')}}</p>
|
<Icon @click="downFile(file)" type="md-arrow-round-down" />
|
||||||
<div class="buttons">
|
<EPopover v-model="file._show_menu" class="file-delete">
|
||||||
<Button size="small" type="text" @click="file._deling=false">{{$L('取消')}}</Button>
|
<div class="task-detail-delete-file-popover">
|
||||||
<Button size="small" type="primary" @click="deleteFile(file)">{{$L('确定')}}</Button>
|
<p>{{$L('你确定要删除这个文件吗?')}}</p>
|
||||||
|
<div class="buttons">
|
||||||
|
<Button size="small" type="text" @click="file._show_menu=false">{{$L('取消')}}</Button>
|
||||||
|
<Button size="small" type="primary" @click="deleteFile(file)">{{$L('确定')}}</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<i slot="reference" class="taskfont del"></i>
|
||||||
<i slot="reference" :class="['taskfont', file._deling ? 'deling' : '']"></i>
|
</EPopover>
|
||||||
</EPopover>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul class="item-content">
|
<ul class="item-content">
|
||||||
@ -342,7 +345,7 @@
|
|||||||
</EDropdown>
|
</EDropdown>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<TaskUpload ref="upload" class="upload"/>
|
<TaskUpload ref="upload" class="upload" @on-select-file="onSelectFile"/>
|
||||||
</div>
|
</div>
|
||||||
<div v-show="taskDetail.id > 0" class="task-dialog" :style="dialogStyle">
|
<div v-show="taskDetail.id > 0" class="task-dialog" :style="dialogStyle">
|
||||||
<template v-if="hasOpenDialog">
|
<template v-if="hasOpenDialog">
|
||||||
@ -377,7 +380,7 @@
|
|||||||
<div v-else class="no-dialog">
|
<div v-else class="no-dialog">
|
||||||
<div class="no-tip">{{$L('暂无消息')}}</div>
|
<div class="no-tip">{{$L('暂无消息')}}</div>
|
||||||
<div class="no-input">
|
<div class="no-input">
|
||||||
<Input
|
<DragInput
|
||||||
class="dialog-input"
|
class="dialog-input"
|
||||||
v-model="msgText"
|
v-model="msgText"
|
||||||
type="textarea"
|
type="textarea"
|
||||||
@ -386,8 +389,9 @@
|
|||||||
:autosize="{ minRows: 1, maxRows: 3 }"
|
:autosize="{ minRows: 1, maxRows: 3 }"
|
||||||
:maxlength="255"
|
:maxlength="255"
|
||||||
:placeholder="$L('输入消息...')"
|
:placeholder="$L('输入消息...')"
|
||||||
@on-keydown="msgKeydown"/>
|
@on-keydown="msgKeydown"
|
||||||
<div class="no-send" @click="openSend">
|
@on-input-paste="msgPasteDrag"/>
|
||||||
|
<div class="no-send" @click="msgDialog">
|
||||||
<Loading v-if="sendLoad > 0"/>
|
<Loading v-if="sendLoad > 0"/>
|
||||||
<Icon v-else type="md-send" />
|
<Icon v-else type="md-send" />
|
||||||
</div>
|
</div>
|
||||||
@ -396,6 +400,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="!taskDetail.id" class="task-load"><Loading/></div>
|
<div v-if="!taskDetail.id" class="task-load"><Loading/></div>
|
||||||
|
<div v-if="dialogDrag" class="drag-over" @click="dialogDrag=false">
|
||||||
|
<div class="drag-text">{{$L('拖动到这里发送')}}</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -409,10 +416,11 @@ import DialogWrapper from "./DialogWrapper";
|
|||||||
import ProjectLog from "./ProjectLog";
|
import ProjectLog from "./ProjectLog";
|
||||||
import {Store} from "le5le-store";
|
import {Store} from "le5le-store";
|
||||||
import TaskMenu from "./TaskMenu";
|
import TaskMenu from "./TaskMenu";
|
||||||
|
import DragInput from "../../../components/DragInput";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "TaskDetail",
|
name: "TaskDetail",
|
||||||
components: {TaskMenu, ProjectLog, DialogWrapper, TaskUpload, UserInput, TaskPriority, TEditor},
|
components: {DragInput, TaskMenu, ProjectLog, DialogWrapper, TaskUpload, UserInput, TaskPriority, TEditor},
|
||||||
props: {
|
props: {
|
||||||
taskId: {
|
taskId: {
|
||||||
type: Number,
|
type: Number,
|
||||||
@ -434,14 +442,12 @@ export default {
|
|||||||
|
|
||||||
taskDetail: {},
|
taskDetail: {},
|
||||||
|
|
||||||
ownerShow: false,
|
|
||||||
ownerData: {},
|
ownerData: {},
|
||||||
ownerLoad: 0,
|
ownerLoad: 0,
|
||||||
|
|
||||||
receiveShow: false,
|
receiveShow: false,
|
||||||
|
|
||||||
assistForce: false,
|
assistForce: false,
|
||||||
assistShow: false,
|
|
||||||
assistData: {},
|
assistData: {},
|
||||||
assistLoad: 0,
|
assistLoad: 0,
|
||||||
|
|
||||||
@ -461,6 +467,7 @@ export default {
|
|||||||
innerHeight: Math.min(1100, window.innerHeight),
|
innerHeight: Math.min(1100, window.innerHeight),
|
||||||
|
|
||||||
msgText: '',
|
msgText: '',
|
||||||
|
msgFile: [],
|
||||||
navActive: 'dialog',
|
navActive: 'dialog',
|
||||||
logLoadIng: false,
|
logLoadIng: false,
|
||||||
|
|
||||||
@ -489,6 +496,7 @@ export default {
|
|||||||
toolbar: 'uploadImages | uploadFiles | bold italic underline forecolor backcolor | codesample | preview screenload'
|
toolbar: 'uploadImages | uploadFiles | bold italic underline forecolor backcolor | codesample | preview screenload'
|
||||||
},
|
},
|
||||||
|
|
||||||
|
dialogDrag: false,
|
||||||
receiveTaskSubscribe: null,
|
receiveTaskSubscribe: null,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -524,6 +532,8 @@ export default {
|
|||||||
'taskContents',
|
'taskContents',
|
||||||
'taskFiles',
|
'taskFiles',
|
||||||
'taskPriority',
|
'taskPriority',
|
||||||
|
|
||||||
|
'windowMax768'
|
||||||
]),
|
]),
|
||||||
|
|
||||||
projectName() {
|
projectName() {
|
||||||
@ -582,7 +592,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
hasOpenDialog() {
|
hasOpenDialog() {
|
||||||
return this.taskDetail.dialog_id > 0 && !this.$store.state.windowMax768;
|
return this.taskDetail.dialog_id > 0 && !this.windowMax768;
|
||||||
},
|
},
|
||||||
|
|
||||||
dialogStyle() {
|
dialogStyle() {
|
||||||
@ -779,7 +789,6 @@ export default {
|
|||||||
const list = this.getOwner.map(({userid}) => userid)
|
const list = this.getOwner.map(({userid}) => userid)
|
||||||
this.$set(this.taskDetail, 'owner_userid', list)
|
this.$set(this.taskDetail, 'owner_userid', list)
|
||||||
this.$set(this.ownerData, 'owner_userid', list)
|
this.$set(this.ownerData, 'owner_userid', list)
|
||||||
this.ownerShow = true;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
onOwner(pick) {
|
onOwner(pick) {
|
||||||
@ -815,13 +824,11 @@ export default {
|
|||||||
this.$store.dispatch("taskUpdate", data).then(({msg}) => {
|
this.$store.dispatch("taskUpdate", data).then(({msg}) => {
|
||||||
$A.messageSuccess(msg);
|
$A.messageSuccess(msg);
|
||||||
this.ownerLoad--;
|
this.ownerLoad--;
|
||||||
this.ownerShow = false;
|
|
||||||
this.receiveShow = false;
|
this.receiveShow = false;
|
||||||
this.$store.dispatch("getTaskOne", this.taskDetail.id).catch(() => {})
|
this.$store.dispatch("getTaskOne", this.taskDetail.id).catch(() => {})
|
||||||
}).catch(({msg}) => {
|
}).catch(({msg}) => {
|
||||||
$A.modalError(msg);
|
$A.modalError(msg);
|
||||||
this.ownerLoad--;
|
this.ownerLoad--;
|
||||||
this.ownerShow = false;
|
|
||||||
this.receiveShow = false;
|
this.receiveShow = false;
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@ -831,7 +838,6 @@ export default {
|
|||||||
this.$set(this.taskDetail, 'assist_userid', list)
|
this.$set(this.taskDetail, 'assist_userid', list)
|
||||||
this.$set(this.assistData, 'assist_userid', list);
|
this.$set(this.assistData, 'assist_userid', list);
|
||||||
this.$set(this.assistData, 'disabled', this.getOwner.map(({userid}) => userid))
|
this.$set(this.assistData, 'disabled', this.getOwner.map(({userid}) => userid))
|
||||||
this.assistShow = true;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
onAssist() {
|
onAssist() {
|
||||||
@ -847,12 +853,10 @@ export default {
|
|||||||
}).then(({msg}) => {
|
}).then(({msg}) => {
|
||||||
$A.messageSuccess(msg);
|
$A.messageSuccess(msg);
|
||||||
this.assistLoad--;
|
this.assistLoad--;
|
||||||
this.assistShow = false;
|
|
||||||
this.$store.dispatch("getTaskOne", this.taskDetail.id).catch(() => {})
|
this.$store.dispatch("getTaskOne", this.taskDetail.id).catch(() => {})
|
||||||
}).catch(({msg}) => {
|
}).catch(({msg}) => {
|
||||||
$A.modalError(msg);
|
$A.modalError(msg);
|
||||||
this.assistLoad--;
|
this.assistLoad--;
|
||||||
this.assistShow = false;
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -988,14 +992,13 @@ export default {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.msgDialog();
|
if (this.msgText) {
|
||||||
|
this.msgDialog();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
msgDialog() {
|
msgDialog() {
|
||||||
if (!this.msgText) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this.sendLoad > 0) {
|
if (this.sendLoad > 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1011,18 +1014,26 @@ export default {
|
|||||||
this.$store.dispatch("getDialogOne", data.dialog_id).then(() => {
|
this.$store.dispatch("getDialogOne", data.dialog_id).then(() => {
|
||||||
this.sendLoad--;
|
this.sendLoad--;
|
||||||
if ($A.isSubElectron) {
|
if ($A.isSubElectron) {
|
||||||
this.resizeDialog();
|
this.resizeDialog().then(() => {
|
||||||
|
this.sendDialogMsg();
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
if (this.$store.state.windowMax768) {
|
if (this.windowMax768) {
|
||||||
this.goForward({path: '/manage/messenger', query: {sendmsg: this.msgText}});
|
window.__sendDialogMsg = {
|
||||||
|
time: $A.Time() + 10,
|
||||||
|
msgText: this.msgText,
|
||||||
|
msgFile: this.msgFile
|
||||||
|
};
|
||||||
|
this.msgFile = [];
|
||||||
|
this.msgText = "";
|
||||||
|
this.goForward({path: '/manage/messenger', query: {_: $A.randomString(6)}});
|
||||||
$A.setStorage("messenger::dialogId", data.dialog_id)
|
$A.setStorage("messenger::dialogId", data.dialog_id)
|
||||||
this.$store.state.dialogOpenId = data.dialog_id;
|
this.$store.state.dialogOpenId = data.dialog_id;
|
||||||
this.$store.dispatch('openTask', 0);
|
this.$store.dispatch('openTask', 0);
|
||||||
} else {
|
} else {
|
||||||
this.$refs.dialog.sendMsg(this.msgText);
|
this.sendDialogMsg();
|
||||||
}
|
}
|
||||||
this.msgText = "";
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}).catch(({msg}) => {
|
}).catch(({msg}) => {
|
||||||
@ -1035,39 +1046,53 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
openSend() {
|
sendDialogMsg() {
|
||||||
if (this.sendLoad > 0) {
|
if (this.msgFile.length > 0) {
|
||||||
return;
|
this.$refs.dialog.sendFileMsg(this.msgFile);
|
||||||
|
} else if (this.msgText) {
|
||||||
|
this.$refs.dialog.sendMsg(this.msgText);
|
||||||
}
|
}
|
||||||
this.sendLoad++;
|
this.msgFile = [];
|
||||||
//
|
this.msgText = "";
|
||||||
this.$store.dispatch("call", {
|
},
|
||||||
url: 'project/task/dialog',
|
|
||||||
data: {
|
msgPasteDrag(e, type) {
|
||||||
task_id: this.taskDetail.id,
|
const files = type === 'drag' ? e.dataTransfer.files : e.clipboardData.files;
|
||||||
},
|
this.msgFile = Array.prototype.slice.call(files);
|
||||||
}).then(({data}) => {
|
if (this.msgFile.length > 0) {
|
||||||
this.sendLoad--;
|
e.preventDefault();
|
||||||
this.$store.dispatch("saveTask", data);
|
this.msgDialog()
|
||||||
this.$store.dispatch("getDialogOne", data.dialog_id).catch(() => {})
|
}
|
||||||
if ($A.isSubElectron) {
|
},
|
||||||
this.resizeDialog();
|
|
||||||
} else {
|
taskPasteDrag(e, type) {
|
||||||
this.$nextTick(() => {
|
this.dialogDrag = false;
|
||||||
this.goForward({path: '/manage/messenger', query: {sendmsg: this.msgText}});
|
this.msgPasteDrag(e, type);
|
||||||
$A.setStorage("messenger::dialogId", data.dialog_id)
|
},
|
||||||
this.$store.state.dialogOpenId = data.dialog_id;
|
|
||||||
this.$store.dispatch('openTask', 0);
|
taskDragOver(show, e) {
|
||||||
});
|
let random = (this.__dialogDrag = $A.randomString(8));
|
||||||
|
if (!show) {
|
||||||
|
setTimeout(() => {
|
||||||
|
if (random === this.__dialogDrag) {
|
||||||
|
this.dialogDrag = show;
|
||||||
|
}
|
||||||
|
}, 150);
|
||||||
|
} else {
|
||||||
|
if (e.dataTransfer.effectAllowed === 'move') {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}).catch(({msg}) => {
|
this.dialogDrag = true;
|
||||||
this.sendLoad--;
|
}
|
||||||
$A.modalError(msg);
|
},
|
||||||
});
|
|
||||||
|
onSelectFile(file) {
|
||||||
|
this.msgFile = [file];
|
||||||
|
this.msgDialog()
|
||||||
},
|
},
|
||||||
|
|
||||||
deleteFile(file) {
|
deleteFile(file) {
|
||||||
this.$set(file, '_deling', false);
|
this.$set(file, '_show_menu', false);
|
||||||
this.$store.dispatch("forgetTaskFile", file.id)
|
this.$store.dispatch("forgetTaskFile", file.id)
|
||||||
//
|
//
|
||||||
this.$store.dispatch("call", {
|
this.$store.dispatch("call", {
|
||||||
@ -1083,9 +1108,7 @@ export default {
|
|||||||
|
|
||||||
openMenu(task) {
|
openMenu(task) {
|
||||||
const el = this.$refs[`taskMenu_${task.id}`];
|
const el = this.$refs[`taskMenu_${task.id}`];
|
||||||
if (el) {
|
el && el.handleClick()
|
||||||
el.handleClick()
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
openNewWin() {
|
openNewWin() {
|
||||||
@ -1112,25 +1135,43 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
resizeDialog() {
|
resizeDialog() {
|
||||||
this.$Electron.ipcRenderer.sendSync('windowSize', {
|
return new Promise(resolve => {
|
||||||
width: Math.max(1100, window.innerWidth),
|
this.$Electron.ipcRenderer.sendSync('windowSize', {
|
||||||
height: Math.max(720, window.innerHeight),
|
width: Math.max(1100, window.innerWidth),
|
||||||
minWidth: 800,
|
height: Math.max(720, window.innerHeight),
|
||||||
minHeight: 600,
|
minWidth: 800,
|
||||||
autoZoom: true,
|
minHeight: 600,
|
||||||
});
|
autoZoom: true,
|
||||||
if (this.msgText) {
|
});
|
||||||
let num = 0;
|
let num = 0;
|
||||||
let interval = setInterval(() => {
|
let interval = setInterval(() => {
|
||||||
num++;
|
num++;
|
||||||
if (this.$refs.dialog || num > 20) {
|
if (this.$refs.dialog || num > 20) {
|
||||||
clearInterval(interval);
|
clearInterval(interval);
|
||||||
if (this.$refs.dialog) {
|
if (this.$refs.dialog) {
|
||||||
this.$refs.dialog.sendMsg(this.msgText);
|
resolve()
|
||||||
this.msgText = "";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, 100);
|
}, 100);
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
viewFile(file) {
|
||||||
|
if (this.$Electron) {
|
||||||
|
this.$Electron.ipcRenderer.send('windowRouter', {
|
||||||
|
title: `${file.name} (${$A.bytesToSize(file.size)})`,
|
||||||
|
titleFixed: true,
|
||||||
|
name: 'file-task-' + file.id,
|
||||||
|
path: "/single/file/task/" + file.id,
|
||||||
|
force: false,
|
||||||
|
config: {
|
||||||
|
parent: null,
|
||||||
|
width: Math.min(window.screen.availWidth, 1440),
|
||||||
|
height: Math.min(window.screen.availHeight, 900),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
window.open($A.apiUrl(`../single/file/task/${file.id}`))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -2,23 +2,18 @@
|
|||||||
<Upload
|
<Upload
|
||||||
name="files"
|
name="files"
|
||||||
ref="upload"
|
ref="upload"
|
||||||
:action="actionUrl"
|
action=""
|
||||||
:headers="headers"
|
|
||||||
:data="params"
|
|
||||||
multiple
|
multiple
|
||||||
:format="uploadFormat"
|
:format="uploadFormat"
|
||||||
:show-upload-list="false"
|
:show-upload-list="false"
|
||||||
:max-size="maxSize"
|
:max-size="maxSize"
|
||||||
:on-progress="handleProgress"
|
|
||||||
:on-success="handleSuccess"
|
|
||||||
:on-format-error="handleFormatError"
|
:on-format-error="handleFormatError"
|
||||||
:on-exceeded-size="handleMaxSize">
|
:on-exceeded-size="handleMaxSize"
|
||||||
|
:before-upload="handleBeforeUpload">
|
||||||
</Upload>
|
</Upload>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {mapState} from "vuex";
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'TaskUpload',
|
name: 'TaskUpload',
|
||||||
props: {
|
props: {
|
||||||
@ -31,54 +26,10 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
uploadFormat: ['jpg', 'jpeg', 'png', 'gif', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'esp', 'pdf', 'rar', 'zip', 'gz', 'ai', 'avi', 'bmp', 'cdr', 'eps', 'mov', 'mp3', 'mp4', 'pr', 'psd', 'svg', 'tif'],
|
uploadFormat: ['jpg', 'jpeg', 'png', 'gif', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'esp', 'pdf', 'rar', 'zip', 'gz', 'ai', 'avi', 'bmp', 'cdr', 'eps', 'mov', 'mp3', 'mp4', 'pr', 'psd', 'svg', 'tif'],
|
||||||
actionUrl: $A.apiUrl('project/task/upload'),
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
|
||||||
...mapState(['userToken', 'taskId', 'taskFiles']),
|
|
||||||
|
|
||||||
headers() {
|
|
||||||
return {
|
|
||||||
fd: $A.getStorageString("userWsFd"),
|
|
||||||
token: this.userToken,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
params() {
|
|
||||||
return {
|
|
||||||
task_id: this.taskId,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
handleProgress(event, file) {
|
|
||||||
//上传时
|
|
||||||
if (typeof file.tempId === "undefined") {
|
|
||||||
file.tempId = $A.randomString(8);
|
|
||||||
file.task_id = this.taskId;
|
|
||||||
this.taskFiles.push(file);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
handleSuccess({ret, data, msg}, file) {
|
|
||||||
//上传完成
|
|
||||||
let index = this.taskFiles.findIndex(({tempId}) => tempId == file.tempId);
|
|
||||||
if (index > -1) {
|
|
||||||
this.taskFiles.splice(index, 1);
|
|
||||||
}
|
|
||||||
if (ret === 1) {
|
|
||||||
this.taskFiles.push(data);
|
|
||||||
} else {
|
|
||||||
this.$refs.upload.fileList.pop();
|
|
||||||
$A.modalWarning({
|
|
||||||
title: '发送失败',
|
|
||||||
content: '文件 ' + file.name + ' 发送失败,' + msg
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
handleFormatError(file) {
|
handleFormatError(file) {
|
||||||
//上传类型错误
|
//上传类型错误
|
||||||
$A.modalWarning({
|
$A.modalWarning({
|
||||||
@ -95,15 +46,16 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
handleBeforeUpload(file) {
|
||||||
|
// 拦截上传
|
||||||
|
this.$emit("on-select-file", file)
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
handleClick() {
|
handleClick() {
|
||||||
//手动上传
|
//手动上传
|
||||||
this.$refs.upload.handleClick()
|
this.$refs.upload.handleClick()
|
||||||
},
|
},
|
||||||
|
|
||||||
upload(file) {
|
|
||||||
//手动传file
|
|
||||||
this.$refs.upload.upload(file);
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -62,10 +62,12 @@
|
|||||||
<i class="taskfont"></i>
|
<i class="taskfont"></i>
|
||||||
<em>{{item.sub_complete}}/{{item.sub_num}}</em>
|
<em>{{item.sub_complete}}/{{item.sub_num}}</em>
|
||||||
</div>
|
</div>
|
||||||
<div :class="['item-icon', item.today ? 'today' : '', item.overdue ? 'overdue' : '']">
|
<ETooltip :content="item.end_at" placement="right">
|
||||||
<i class="taskfont"></i>
|
<div :class="['item-icon', item.today ? 'today' : '', item.overdue ? 'overdue' : '']">
|
||||||
<em>{{expiresFormat(item.end_at)}}</em>
|
<i class="taskfont"></i>
|
||||||
</div>
|
<em>{{expiresFormat(item.end_at)}}</em>
|
||||||
|
</div>
|
||||||
|
</ETooltip>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</template>
|
</template>
|
||||||
|
@ -775,8 +775,8 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
openFile(item) {
|
openFile(item, checkMenuVisible = true) {
|
||||||
if (this.contextMenuVisible) {
|
if (checkMenuVisible && this.contextMenuVisible) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this.fileList.findIndex((file) => file._edit === true) > -1) {
|
if (this.fileList.findIndex((file) => file._edit === true) > -1) {
|
||||||
@ -800,8 +800,9 @@ export default {
|
|||||||
|
|
||||||
openSingle(item) {
|
openSingle(item) {
|
||||||
this.$Electron.ipcRenderer.send('windowRouter', {
|
this.$Electron.ipcRenderer.send('windowRouter', {
|
||||||
title: item.name,
|
title: this.formatName(item),
|
||||||
titleFixed: true,
|
titleFixed: true,
|
||||||
|
userAgent: "/hideenOfficeTitle/",
|
||||||
name: 'file-' + item.id,
|
name: 'file-' + item.id,
|
||||||
path: "/single/file/" + item.id,
|
path: "/single/file/" + item.id,
|
||||||
force: false, // 如果窗口已存在不重新加载
|
force: false, // 如果窗口已存在不重新加载
|
||||||
@ -842,7 +843,7 @@ export default {
|
|||||||
dropFile(item, command) {
|
dropFile(item, command) {
|
||||||
switch (command) {
|
switch (command) {
|
||||||
case 'open':
|
case 'open':
|
||||||
this.openFile(item);
|
this.openFile(item, false);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'rename':
|
case 'rename':
|
||||||
@ -912,7 +913,6 @@ export default {
|
|||||||
|
|
||||||
case 'download':
|
case 'download':
|
||||||
if (!item.ext) {
|
if (!item.ext) {
|
||||||
$A.modalError("此文件不支持下载");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$A.modalConfirm({
|
$A.modalConfirm({
|
||||||
|
151
resources/assets/js/pages/single/fileMsg.vue
Normal file
151
resources/assets/js/pages/single/fileMsg.vue
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
<template>
|
||||||
|
<div class="single-file-msg">
|
||||||
|
<PageTitle :title="title"/>
|
||||||
|
<Loading v-if="loadIng > 0"/>
|
||||||
|
<template v-else>
|
||||||
|
<AceEditor v-if="isCode" v-model="codeContent" :ext="codeExt" class="view-editor" readOnly/>
|
||||||
|
<OnlyOffice v-else-if="isOffice" v-model="officeContent" :code="officeCode" readOnly/>
|
||||||
|
<iframe v-else-if="isPreview" class="preview-iframe" :src="previewUrl"/>
|
||||||
|
<div v-else class="no-support">{{$L('不支持单独查看此消息')}}</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.single-file-msg {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
.preview-iframe,
|
||||||
|
.ace_editor,
|
||||||
|
.no-support {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border: 0;
|
||||||
|
margin: 0;
|
||||||
|
outline: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.preview-iframe {
|
||||||
|
background: 0 0;
|
||||||
|
float: none;
|
||||||
|
max-width: none;
|
||||||
|
}
|
||||||
|
.view-editor,
|
||||||
|
.no-support {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<style lang="scss">
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
import AceEditor from "../../components/AceEditor";
|
||||||
|
import OnlyOffice from "../../components/OnlyOffice";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {OnlyOffice, AceEditor},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loadIng: 0,
|
||||||
|
|
||||||
|
msgDetail: {},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
//
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
'$route': {
|
||||||
|
handler() {
|
||||||
|
this.getInfo();
|
||||||
|
},
|
||||||
|
immediate: true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
title() {
|
||||||
|
const {msg} = this.msgDetail;
|
||||||
|
if (msg && msg.name) {
|
||||||
|
return msg.name;
|
||||||
|
}
|
||||||
|
return "Loading..."
|
||||||
|
},
|
||||||
|
|
||||||
|
isCode() {
|
||||||
|
return this.msgDetail.type == 'file' && this.msgDetail.file_mode == 1;
|
||||||
|
},
|
||||||
|
codeContent() {
|
||||||
|
if (this.isCode) {
|
||||||
|
return this.msgDetail.content;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
},
|
||||||
|
codeExt() {
|
||||||
|
if (this.isCode) {
|
||||||
|
return this.msgDetail.msg.ext;
|
||||||
|
}
|
||||||
|
return 'txt'
|
||||||
|
},
|
||||||
|
|
||||||
|
isOffice() {
|
||||||
|
return this.msgDetail.type == 'file' && this.msgDetail.file_mode == 2;
|
||||||
|
},
|
||||||
|
officeContent() {
|
||||||
|
return {
|
||||||
|
id: this.isOffice ? this.msgDetail.id : 0,
|
||||||
|
type: this.msgDetail.msg.ext,
|
||||||
|
name: this.title
|
||||||
|
}
|
||||||
|
},
|
||||||
|
officeCode() {
|
||||||
|
if (this.isOffice) {
|
||||||
|
return "msgFile_" + this.msgDetail.id;
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
},
|
||||||
|
|
||||||
|
isPreview() {
|
||||||
|
return this.msgDetail.type == 'file' && this.msgDetail.file_mode == 3;
|
||||||
|
},
|
||||||
|
previewUrl() {
|
||||||
|
if (this.isPreview) {
|
||||||
|
return $A.apiUrl("../fileview/onlinePreview?url=" + encodeURIComponent(this.msgDetail.url))
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getInfo() {
|
||||||
|
let msg_id = $A.runNum(this.$route.params.id);
|
||||||
|
if (msg_id <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.loadIng++;
|
||||||
|
this.$store.dispatch("call", {
|
||||||
|
url: 'dialog/msg/detail',
|
||||||
|
data: {
|
||||||
|
msg_id,
|
||||||
|
},
|
||||||
|
}).then(({data}) => {
|
||||||
|
this.loadIng--;
|
||||||
|
this.msgDetail = data;
|
||||||
|
}).catch(({msg}) => {
|
||||||
|
this.loadIng--;
|
||||||
|
$A.modalError({
|
||||||
|
content: msg,
|
||||||
|
onOk: () => {
|
||||||
|
if (this.$Electron) {
|
||||||
|
window.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
151
resources/assets/js/pages/single/fileTask.vue
Normal file
151
resources/assets/js/pages/single/fileTask.vue
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
<template>
|
||||||
|
<div class="single-file-task">
|
||||||
|
<PageTitle :title="title"/>
|
||||||
|
<Loading v-if="loadIng > 0"/>
|
||||||
|
<template v-else>
|
||||||
|
<AceEditor v-if="isCode" v-model="codeContent" :ext="codeExt" class="view-editor" readOnly/>
|
||||||
|
<OnlyOffice v-else-if="isOffice" v-model="officeContent" :code="officeCode" readOnly/>
|
||||||
|
<iframe v-else-if="isPreview" class="preview-iframe" :src="previewUrl"/>
|
||||||
|
<div v-else class="no-support">{{$L('不支持单独查看此消息')}}</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.single-file-task {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
.preview-iframe,
|
||||||
|
.ace_editor,
|
||||||
|
.no-support {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border: 0;
|
||||||
|
margin: 0;
|
||||||
|
outline: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.preview-iframe {
|
||||||
|
background: 0 0;
|
||||||
|
float: none;
|
||||||
|
max-width: none;
|
||||||
|
}
|
||||||
|
.view-editor,
|
||||||
|
.no-support {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<style lang="scss">
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
import AceEditor from "../../components/AceEditor";
|
||||||
|
import OnlyOffice from "../../components/OnlyOffice";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {OnlyOffice, AceEditor},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loadIng: 0,
|
||||||
|
|
||||||
|
fileDetail: {},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
//
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
'$route': {
|
||||||
|
handler() {
|
||||||
|
this.getInfo();
|
||||||
|
},
|
||||||
|
immediate: true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
title() {
|
||||||
|
const {name} = this.fileDetail;
|
||||||
|
if (name) {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
return "Loading..."
|
||||||
|
},
|
||||||
|
|
||||||
|
isCode() {
|
||||||
|
return this.fileDetail.file_mode == 1;
|
||||||
|
},
|
||||||
|
codeContent() {
|
||||||
|
if (this.isCode) {
|
||||||
|
return this.fileDetail.content;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
},
|
||||||
|
codeExt() {
|
||||||
|
if (this.isCode) {
|
||||||
|
return this.fileDetail.ext;
|
||||||
|
}
|
||||||
|
return 'txt'
|
||||||
|
},
|
||||||
|
|
||||||
|
isOffice() {
|
||||||
|
return this.fileDetail.file_mode == 2;
|
||||||
|
},
|
||||||
|
officeContent() {
|
||||||
|
return {
|
||||||
|
id: this.isOffice ? this.fileDetail.id : 0,
|
||||||
|
type: this.fileDetail.ext,
|
||||||
|
name: this.title
|
||||||
|
}
|
||||||
|
},
|
||||||
|
officeCode() {
|
||||||
|
if (this.isOffice) {
|
||||||
|
return "taskFile_" + this.fileDetail.id;
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
},
|
||||||
|
|
||||||
|
isPreview() {
|
||||||
|
return this.fileDetail.file_mode == 3;
|
||||||
|
},
|
||||||
|
previewUrl() {
|
||||||
|
if (this.isPreview) {
|
||||||
|
return $A.apiUrl("../fileview/onlinePreview?url=" + encodeURIComponent(this.fileDetail.url))
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getInfo() {
|
||||||
|
let file_id = $A.runNum(this.$route.params.id);
|
||||||
|
if (file_id <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.loadIng++;
|
||||||
|
this.$store.dispatch("call", {
|
||||||
|
url: 'project/task/filedetail',
|
||||||
|
data: {
|
||||||
|
file_id,
|
||||||
|
},
|
||||||
|
}).then(({data}) => {
|
||||||
|
this.loadIng--;
|
||||||
|
this.fileDetail = data;
|
||||||
|
}).catch(({msg}) => {
|
||||||
|
this.loadIng--;
|
||||||
|
$A.modalError({
|
||||||
|
content: msg,
|
||||||
|
onOk: () => {
|
||||||
|
if (this.$Electron) {
|
||||||
|
window.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
@ -38,18 +38,25 @@
|
|||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
import TaskDetail from "../manage/components/TaskDetail";
|
import TaskDetail from "../manage/components/TaskDetail";
|
||||||
|
import {mapState} from "vuex";
|
||||||
export default {
|
export default {
|
||||||
components: {TaskDetail},
|
components: {TaskDetail},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
loadIng: 0,
|
loadIng: 0,
|
||||||
|
taskId: 0,
|
||||||
taskInfo: {},
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
//
|
//
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(['cacheTasks']),
|
||||||
|
|
||||||
|
taskInfo() {
|
||||||
|
return this.cacheTasks.find(({id}) => id === this.taskId) || {}
|
||||||
|
}
|
||||||
|
},
|
||||||
watch: {
|
watch: {
|
||||||
'$route': {
|
'$route': {
|
||||||
handler() {
|
handler() {
|
||||||
@ -60,20 +67,19 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getInfo() {
|
getInfo() {
|
||||||
let task_id = $A.runNum(this.$route.params.id);
|
this.taskId = $A.runNum(this.$route.params.id);
|
||||||
if (task_id <= 0) {
|
if (this.taskId <= 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.loadIng++;
|
this.loadIng++;
|
||||||
this.$store.dispatch("getTaskOne", {
|
this.$store.dispatch("getTaskOne", {
|
||||||
task_id,
|
task_id: this.taskId,
|
||||||
archived: 'all'
|
archived: 'all'
|
||||||
}).then(({data}) => {
|
}).then(() => {
|
||||||
this.loadIng--;
|
this.loadIng--;
|
||||||
this.taskInfo = data;
|
this.$store.dispatch("getTaskContent", this.taskId);
|
||||||
this.$store.dispatch("getTaskContent", task_id);
|
this.$store.dispatch("getTaskFiles", this.taskId);
|
||||||
this.$store.dispatch("getTaskFiles", task_id);
|
this.$store.dispatch("getTaskForParent", this.taskId).catch(() => {})
|
||||||
this.$store.dispatch("getTaskForParent", task_id).catch(() => {})
|
|
||||||
}).catch(({msg}) => {
|
}).catch(({msg}) => {
|
||||||
this.loadIng--;
|
this.loadIng--;
|
||||||
$A.modalError({
|
$A.modalError({
|
||||||
|
10
resources/assets/js/routes.js
vendored
10
resources/assets/js/routes.js
vendored
@ -75,6 +75,16 @@ export default [
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'single-file-msg',
|
||||||
|
path: '/single/file/msg/:id',
|
||||||
|
component: () => import('./pages/single/fileMsg.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'single-file-task',
|
||||||
|
path: '/single/file/task/:id',
|
||||||
|
component: () => import('./pages/single/fileTask.vue'),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'single-file',
|
name: 'single-file',
|
||||||
path: '/single/file/:id',
|
path: '/single/file/:id',
|
||||||
|
4
resources/assets/js/store/actions.js
vendored
4
resources/assets/js/store/actions.js
vendored
@ -273,11 +273,11 @@ export default {
|
|||||||
dispatch("call", {
|
dispatch("call", {
|
||||||
url: 'users/basic',
|
url: 'users/basic',
|
||||||
data: {
|
data: {
|
||||||
userid: array.map(({userid}) => userid)
|
userid: [...new Set(array.map(({userid}) => userid))]
|
||||||
},
|
},
|
||||||
}).then(result => {
|
}).then(result => {
|
||||||
time = $A.Time();
|
time = $A.Time();
|
||||||
array.forEach((value) => {
|
array.forEach(value => {
|
||||||
let data = result.data.find(({userid}) => userid == value.userid) || Object.assign(value, {email: ""});
|
let data = result.data.find(({userid}) => userid == value.userid) || Object.assign(value, {email: ""});
|
||||||
data._time = time;
|
data._time = time;
|
||||||
dispatch("saveUserBasic", data);
|
dispatch("saveUserBasic", data);
|
||||||
|
4
resources/assets/sass/components/report.scss
vendored
4
resources/assets/sass/components/report.scss
vendored
@ -145,6 +145,10 @@
|
|||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,6 +45,7 @@
|
|||||||
line-height: 22px;
|
line-height: 22px;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
.ivu-tag {
|
.ivu-tag {
|
||||||
|
flex-shrink: 0;
|
||||||
margin: 0 6px 0 0;
|
margin: 0 6px 0 0;
|
||||||
padding: 0 5px;
|
padding: 0 5px;
|
||||||
&.ivu-tag-success {
|
&.ivu-tag-success {
|
||||||
@ -110,19 +111,50 @@
|
|||||||
height: 30px;
|
height: 30px;
|
||||||
}
|
}
|
||||||
.dialog-view {
|
.dialog-view {
|
||||||
max-width: 70%;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
margin: 0 0 0 8px;
|
margin: 0 0 0 8px;
|
||||||
|
position: relative;
|
||||||
|
&.text {
|
||||||
|
max-width: 70%;
|
||||||
|
}
|
||||||
.dialog-content {
|
.dialog-content {
|
||||||
color: #333333;
|
color: #333333;
|
||||||
background-color: #F4F5F7;
|
background-color: #F4F5F7;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
min-width: 32px;
|
min-width: 32px;
|
||||||
border-radius: 6px 6px 6px 0;
|
border-radius: 6px 6px 6px 0;
|
||||||
.dialog-file {
|
.dialog-file-menu {
|
||||||
cursor: pointer;
|
opacity: 0;
|
||||||
|
transition: all 0.3s;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
bottom: -8px;
|
||||||
|
.file-menu-warp {
|
||||||
|
width: 100%;
|
||||||
|
height: 12px;
|
||||||
|
}
|
||||||
|
.file-menu-icon {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
> i {
|
||||||
|
flex: 1;
|
||||||
|
display: inline-block;
|
||||||
|
padding: 4px 6px;
|
||||||
|
color: #999;
|
||||||
|
font-size: 13px;
|
||||||
|
cursor: pointer;
|
||||||
|
&:hover {
|
||||||
|
color: #777;
|
||||||
|
}
|
||||||
|
&+i {
|
||||||
|
border-left: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
> pre {
|
> pre {
|
||||||
display: block;
|
display: block;
|
||||||
@ -133,6 +165,11 @@
|
|||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
|
&:hover {
|
||||||
|
.dialog-file-menu {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
&.loading {
|
&.loading {
|
||||||
display: flex;
|
display: flex;
|
||||||
.common-loading {
|
.common-loading {
|
||||||
@ -187,6 +224,7 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
.file-img {
|
.file-img {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&.unknown {
|
&.unknown {
|
||||||
|
@ -179,6 +179,16 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.task-add-assist {
|
||||||
|
margin-top: 6px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
.ivu-icon {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #999999;
|
||||||
|
}
|
||||||
|
}
|
||||||
.ivu-modal-footer {
|
.ivu-modal-footer {
|
||||||
padding: 26px 0 22px !important;
|
padding: 26px 0 22px !important;
|
||||||
}
|
}
|
||||||
|
@ -251,10 +251,6 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
cursor: pointer;
|
|
||||||
&:hover {
|
|
||||||
color: $primary-color;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.file-size {
|
.file-size {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
@ -263,27 +259,38 @@
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: #bbbbbb;
|
color: #bbbbbb;
|
||||||
}
|
}
|
||||||
.file-delete {
|
.file-menu {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
opacity: 0;
|
||||||
|
transition: all 0.3s;
|
||||||
padding-left: 12px;
|
padding-left: 12px;
|
||||||
.taskfont {
|
&.show {
|
||||||
display: none;
|
opacity: 1;
|
||||||
|
}
|
||||||
|
i {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #aaaaaa;
|
color: #aaaaaa;
|
||||||
transition: color 0.3s;
|
transition: color 0.3s;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
padding: 0 4px;
|
||||||
&:hover {
|
&:hover {
|
||||||
color: #ff0000;
|
color: #777777;
|
||||||
}
|
}
|
||||||
&.deling {
|
&.del {
|
||||||
display: inline-block;
|
font-size: 13px;
|
||||||
|
&:hover {
|
||||||
|
color: #ff0000;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&:hover {
|
&:hover {
|
||||||
.file-delete {
|
.file-name {
|
||||||
.taskfont {
|
color: $primary-title-color;
|
||||||
display: inline-block;
|
}
|
||||||
}
|
.file-menu {
|
||||||
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -593,6 +600,33 @@
|
|||||||
height: 32px;
|
height: 32px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.drag-over {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 3;
|
||||||
|
background-color: rgba(255, 255, 255, 0.78);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
&:before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 16px;
|
||||||
|
left: 16px;
|
||||||
|
right: 16px;
|
||||||
|
bottom: 16px;
|
||||||
|
border: 2px dashed #7b7b7b;
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
.drag-text {
|
||||||
|
padding: 12px;
|
||||||
|
font-size: 18px;
|
||||||
|
color: #666666;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&.open-dialog {
|
&.open-dialog {
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
7
resources/assets/sass/pages/page-manage.scss
vendored
7
resources/assets/sass/pages/page-manage.scss
vendored
@ -298,6 +298,13 @@
|
|||||||
}
|
}
|
||||||
.manage-mini-menu {
|
.manage-mini-menu {
|
||||||
display: none;
|
display: none;
|
||||||
|
position: relative;
|
||||||
|
.ivu-badge {
|
||||||
|
position: absolute;
|
||||||
|
top: -6px;
|
||||||
|
left: 30px;
|
||||||
|
transform: scale(0.9);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user