Compare commits
37 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
9eaa575d1a | ||
|
996ed78a0e | ||
|
1759572b2e | ||
|
0a9f9eea90 | ||
|
223fb540b1 | ||
|
c5f1c95f7b | ||
|
3bbcbca926 | ||
|
63c1deb630 | ||
|
424e2428fe | ||
|
2fdef45156 | ||
|
4cd4550a36 | ||
|
16af625aef | ||
|
8e72794c07 | ||
|
d9cf6d7e1b | ||
|
f4e4252227 | ||
|
7107409b1b | ||
|
6c086fab6f | ||
|
d027d67e08 | ||
|
a7d9e635eb | ||
|
e7196efaea | ||
|
0a2a903c74 | ||
|
8104c26b19 | ||
|
101d5c7eb0 | ||
|
cad253b85f | ||
|
f17009a988 | ||
|
1f76278d2b | ||
|
e032d29c91 | ||
|
31d1b0c994 | ||
|
611c6d415c | ||
|
e3b7ac00fd | ||
|
7c952822db | ||
|
b9e435c0e2 | ||
|
356d40e640 | ||
|
123ffd4e66 | ||
|
c952659620 | ||
|
ea8e1e9c57 | ||
|
478d63893b |
@ -17,10 +17,13 @@ use App\Models\ProjectUser;
|
|||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Models\WebSocketDialog;
|
use App\Models\WebSocketDialog;
|
||||||
use App\Module\Base;
|
use App\Module\Base;
|
||||||
|
use App\Module\BillExport;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Illuminate\Support\Arr;
|
use Illuminate\Support\Arr;
|
||||||
|
use Madzipper;
|
||||||
use Request;
|
use Request;
|
||||||
use Response;
|
use Response;
|
||||||
|
use Session;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @apiDefine project
|
* @apiDefine project
|
||||||
@ -604,13 +607,14 @@ class ProjectController extends AbstractController
|
|||||||
if (!is_array($item['task'])) continue;
|
if (!is_array($item['task'])) continue;
|
||||||
$index = 0;
|
$index = 0;
|
||||||
foreach ($item['task'] as $task_id) {
|
foreach ($item['task'] as $task_id) {
|
||||||
ProjectTask::whereId($task_id)->whereProjectId($project->id)->update([
|
if (ProjectTask::whereId($task_id)->whereProjectId($project->id)->whereCompleteAt(null)->update([
|
||||||
'column_id' => $item['id'],
|
'column_id' => $item['id'],
|
||||||
'sort' => $index
|
'sort' => $index
|
||||||
]);
|
])) {
|
||||||
ProjectTask::whereParentId($task_id)->whereProjectId($project->id)->update([
|
ProjectTask::whereParentId($task_id)->whereProjectId($project->id)->update([
|
||||||
'column_id' => $item['id'],
|
'column_id' => $item['id'],
|
||||||
]);
|
]);
|
||||||
|
}
|
||||||
$index++;
|
$index++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -976,6 +980,154 @@ class ProjectController extends AbstractController
|
|||||||
return Base::retSuccess('success', $list);
|
return Base::retSuccess('success', $list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @api {get} api/project/task/export 18. 导出任务(限管理员)
|
||||||
|
*
|
||||||
|
* @apiDescription 导出指定范围任务(已完成、未完成、已归档),返回下载地址,需要token身份
|
||||||
|
* @apiVersion 1.0.0
|
||||||
|
* @apiGroup project
|
||||||
|
* @apiName task__export
|
||||||
|
*
|
||||||
|
* @apiParam {Array} [userid] 指定会员,如:[1, 2]
|
||||||
|
* @apiParam {Array} [time] 指定时间范围,如:['2020-12-12', '2020-12-30']
|
||||||
|
*
|
||||||
|
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||||
|
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||||
|
* @apiSuccess {Object} data 返回数据
|
||||||
|
*/
|
||||||
|
public function task__export()
|
||||||
|
{
|
||||||
|
$user = User::auth('admin');
|
||||||
|
//
|
||||||
|
$userid = Base::arrayRetainInt(Request::input('userid'), true);
|
||||||
|
$time = Request::input('time');
|
||||||
|
if (empty($userid) || empty($time)) {
|
||||||
|
return Base::retError('参数错误');
|
||||||
|
}
|
||||||
|
if (count($userid) > 20) {
|
||||||
|
return Base::retError('导出会员限制最多20个');
|
||||||
|
}
|
||||||
|
if (!(is_array($time) && Base::isDateOrTime($time[0]) && Base::isDateOrTime($time[1]))) {
|
||||||
|
return Base::retError('时间选择错误');
|
||||||
|
}
|
||||||
|
if (Carbon::parse($time[1])->timestamp - Carbon::parse($time[0])->timestamp > 90 * 86400) {
|
||||||
|
return Base::retError('时间范围限制最大90天');
|
||||||
|
}
|
||||||
|
//
|
||||||
|
$headings = [];
|
||||||
|
$headings[] = '任务ID';
|
||||||
|
$headings[] = '任务标题';
|
||||||
|
$headings[] = '负责人';
|
||||||
|
$headings[] = '创建人';
|
||||||
|
$headings[] = '是否完成';
|
||||||
|
$headings[] = '完成时间';
|
||||||
|
$headings[] = '是否归档';
|
||||||
|
$headings[] = '归档时间';
|
||||||
|
$headings[] = '任务开始时间';
|
||||||
|
$headings[] = '任务结束时间';
|
||||||
|
$headings[] = '结束剩余';
|
||||||
|
$headings[] = '所属项目';
|
||||||
|
$headings[] = '父级任务ID';
|
||||||
|
$datas = [];
|
||||||
|
//
|
||||||
|
$builder = ProjectTask::select(['project_tasks.*', 'project_task_users.userid as ownerid'])
|
||||||
|
->join('project_task_users', 'project_tasks.id', '=', 'project_task_users.task_id')
|
||||||
|
->where('project_task_users.owner', 1)
|
||||||
|
->whereIn('project_task_users.userid', $userid)
|
||||||
|
->betweenTime(Carbon::parse($time[0])->startOfDay(), Carbon::parse($time[1])->endOfDay());
|
||||||
|
$builder->orderByDesc('project_tasks.id')->chunk(100, function($tasks) use (&$datas) {
|
||||||
|
/** @var ProjectTask $task */
|
||||||
|
foreach ($tasks as $task) {
|
||||||
|
if ($task->complete_at) {
|
||||||
|
$a = Carbon::parse($task->complete_at)->timestamp;
|
||||||
|
$b = Carbon::parse($task->end_at)->timestamp;
|
||||||
|
if ($b > $a) {
|
||||||
|
$endSurplus = Base::timeDiff($a, $b);
|
||||||
|
} else {
|
||||||
|
$endSurplus = "-" . Base::timeDiff($b, $a);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$endSurplus = '-';
|
||||||
|
}
|
||||||
|
$datas[] = [
|
||||||
|
$task->id,
|
||||||
|
Base::filterEmoji($task->name),
|
||||||
|
Base::filterEmoji(User::userid2nickname($task->ownerid)) . " (ID: {$task->ownerid})",
|
||||||
|
Base::filterEmoji(User::userid2nickname($task->userid)) . " (ID: {$task->userid})",
|
||||||
|
$task->complete_at ? '已完成' : '-',
|
||||||
|
$task->complete_at ?: '-',
|
||||||
|
$task->archived_at ? '已归档' : '-',
|
||||||
|
$task->archived_at ?: '-',
|
||||||
|
$task->start_at ?: '-',
|
||||||
|
$task->end_at ?: '-',
|
||||||
|
$endSurplus,
|
||||||
|
Base::filterEmoji($task->project?->name) ?: '-',
|
||||||
|
$task->parent_id ?: '-',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
//
|
||||||
|
$fileName = User::userid2nickname($userid[0]) ?: $userid[0];
|
||||||
|
if (count($userid) > 1) {
|
||||||
|
$fileName .= "等" . count($userid) . "位成员";
|
||||||
|
}
|
||||||
|
$fileName .= '任务统计_' . Base::time() . '.xls';
|
||||||
|
$filePath = "temp/task/export/" . date("Ym", Base::time());
|
||||||
|
$res = BillExport::create()->setHeadings($headings)->setData($datas)->store($filePath . "/" . $fileName);
|
||||||
|
if ($res != 1) {
|
||||||
|
return Base::retError('导出失败,' . $fileName . '!');
|
||||||
|
}
|
||||||
|
$xlsPath = storage_path("app/" . $filePath . "/" . $fileName);
|
||||||
|
$zipFile = "app/" . $filePath . "/" . Base::rightDelete($fileName, '.xls'). ".zip";
|
||||||
|
$zipPath = storage_path($zipFile);
|
||||||
|
if (file_exists($zipPath)) {
|
||||||
|
Base::deleteDirAndFile($zipPath, true);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Madzipper::make($zipPath)->add($xlsPath)->close();
|
||||||
|
} catch (\Exception) { }
|
||||||
|
//
|
||||||
|
if (file_exists($zipPath)) {
|
||||||
|
$base64 = base64_encode(Base::array2string([
|
||||||
|
'file' => $zipFile,
|
||||||
|
]));
|
||||||
|
Session::put('task::export:userid', $user->userid);
|
||||||
|
return Base::retSuccess('success', [
|
||||||
|
'size' => Base::twoFloat(filesize($zipPath) / 1024, true),
|
||||||
|
'url' => Base::fillUrl('api/project/task/down?key=' . urlencode($base64)),
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
return Base::retError('打包失败,请稍后再试...');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @api {get} api/project/task/down 18. 导出任务(限管理员)
|
||||||
|
*
|
||||||
|
* @apiDescription 导出指定范围任务(已完成、未完成、已归档),返回下载地址,需要token身份
|
||||||
|
* @apiVersion 1.0.0
|
||||||
|
* @apiGroup project
|
||||||
|
* @apiName task__down
|
||||||
|
*
|
||||||
|
* @apiParam {String} key 通过export接口得到的下载钥匙
|
||||||
|
*
|
||||||
|
* @apiSuccess {File} 文件下载
|
||||||
|
*/
|
||||||
|
public function task__down()
|
||||||
|
{
|
||||||
|
$userid = Session::get('task::export:userid');
|
||||||
|
if (empty($userid)) {
|
||||||
|
return Base::ajaxError("请求已过期,请重新导出!", [], 0, 502);
|
||||||
|
}
|
||||||
|
//
|
||||||
|
$array = Base::string2array(base64_decode(urldecode(Request::input('key'))));
|
||||||
|
$file = $array['file'];
|
||||||
|
if (empty($file) || !file_exists(storage_path($file))) {
|
||||||
|
return Base::ajaxError("文件不存在!", [], 0, 502);
|
||||||
|
}
|
||||||
|
return response()->download(storage_path($file));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @api {get} api/project/task/one 19. 获取单个任务信息
|
* @api {get} api/project/task/one 19. 获取单个任务信息
|
||||||
*
|
*
|
||||||
|
@ -153,9 +153,10 @@ class AbstractModel extends Model
|
|||||||
* @param $where
|
* @param $where
|
||||||
* @param array $update 存在时更新的内容
|
* @param array $update 存在时更新的内容
|
||||||
* @param array $insert 不存在时插入的内容,如果没有则插入更新内容
|
* @param array $insert 不存在时插入的内容,如果没有则插入更新内容
|
||||||
|
* @param bool $isInsert 是否是插入数据
|
||||||
* @return AbstractModel|\Illuminate\Database\Eloquent\Builder|Model|object|static|null
|
* @return AbstractModel|\Illuminate\Database\Eloquent\Builder|Model|object|static|null
|
||||||
*/
|
*/
|
||||||
public static function updateInsert($where, $update = [], $insert = [])
|
public static function updateInsert($where, $update = [], $insert = [], &$isInsert = true)
|
||||||
{
|
{
|
||||||
$row = static::where($where)->first();
|
$row = static::where($where)->first();
|
||||||
if (empty($row)) {
|
if (empty($row)) {
|
||||||
@ -165,8 +166,10 @@ class AbstractModel extends Model
|
|||||||
unset($array[$row->primaryKey]);
|
unset($array[$row->primaryKey]);
|
||||||
}
|
}
|
||||||
$row->updateInstance($array);
|
$row->updateInstance($array);
|
||||||
|
$isInsert = true;
|
||||||
} elseif ($update) {
|
} elseif ($update) {
|
||||||
$row->updateInstance($update);
|
$row->updateInstance($update);
|
||||||
|
$isInsert = false;
|
||||||
}
|
}
|
||||||
if (!$row->save()) {
|
if (!$row->save()) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -275,15 +275,16 @@ class File extends AbstractModel
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 格式化内容数据
|
* 格式化内容数据
|
||||||
* @param array $data [path, ext, size, name]
|
* @param array $data [path, size, ext, name]
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public static function formatFileData(array $data)
|
public static function formatFileData(array $data)
|
||||||
{
|
{
|
||||||
$filePath = $data['path'];
|
$filePath = $data['path'];
|
||||||
$fileExt = $data['ext'];
|
|
||||||
$fileSize = $data['size'];
|
$fileSize = $data['size'];
|
||||||
$fileName = $data['name'];
|
$fileExt = $data['ext'];
|
||||||
|
$fileDotExt = '.' . $fileExt;
|
||||||
|
$fileName = Base::rightDelete($data['name'], $fileDotExt) . $fileDotExt;
|
||||||
$publicPath = public_path($filePath);
|
$publicPath = public_path($filePath);
|
||||||
//
|
//
|
||||||
switch ($fileExt) {
|
switch ($fileExt) {
|
||||||
|
@ -376,6 +376,7 @@ class Project extends AbstractModel
|
|||||||
$idc = [];
|
$idc = [];
|
||||||
$hasStart = false;
|
$hasStart = false;
|
||||||
$hasEnd = false;
|
$hasEnd = false;
|
||||||
|
$upTaskList = [];
|
||||||
foreach ($flows as $item) {
|
foreach ($flows as $item) {
|
||||||
$id = intval($item['id']);
|
$id = intval($item['id']);
|
||||||
$turns = Base::arrayRetainInt($item['turns'] ?: [], true);
|
$turns = Base::arrayRetainInt($item['turns'] ?: [], true);
|
||||||
@ -403,7 +404,7 @@ class Project extends AbstractModel
|
|||||||
'userids' => $userids,
|
'userids' => $userids,
|
||||||
'usertype' => trim($item['usertype']),
|
'usertype' => trim($item['usertype']),
|
||||||
'userlimit' => $userlimit,
|
'userlimit' => $userlimit,
|
||||||
]);
|
], [], $isInsert);
|
||||||
if ($flow) {
|
if ($flow) {
|
||||||
$ids[] = $flow->id;
|
$ids[] = $flow->id;
|
||||||
if ($flow->id != $id) {
|
if ($flow->id != $id) {
|
||||||
@ -415,6 +416,9 @@ class Project extends AbstractModel
|
|||||||
if ($flow->status == 'end') {
|
if ($flow->status == 'end') {
|
||||||
$hasEnd = true;
|
$hasEnd = true;
|
||||||
}
|
}
|
||||||
|
if (!$isInsert) {
|
||||||
|
$upTaskList[$flow->id] = $flow->status . "|" . $flow->name;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!$hasStart) {
|
if (!$hasStart) {
|
||||||
@ -429,6 +433,12 @@ class Project extends AbstractModel
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
//
|
//
|
||||||
|
foreach ($upTaskList as $id => $value) {
|
||||||
|
ProjectTask::whereFlowItemId($id)->update([
|
||||||
|
'flow_item_name' => $value
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
//
|
||||||
$projectFlow = ProjectFlow::with(['projectFlowItem'])->whereProjectId($this->id)->find($projectFlow->id);
|
$projectFlow = ProjectFlow::with(['projectFlowItem'])->whereProjectId($this->id)->find($projectFlow->id);
|
||||||
$itemIds = $projectFlow->projectFlowItem->pluck('id')->toArray();
|
$itemIds = $projectFlow->projectFlowItem->pluck('id')->toArray();
|
||||||
foreach ($projectFlow->projectFlowItem as $item) {
|
foreach ($projectFlow->projectFlowItem as $item) {
|
||||||
|
@ -585,6 +585,14 @@ class ProjectTask extends AbstractModel
|
|||||||
'flow' => $flowData,
|
'flow' => $flowData,
|
||||||
'change' => [$currentFlowItem?->name, $newFlowItem->name]
|
'change' => [$currentFlowItem?->name, $newFlowItem->name]
|
||||||
]);
|
]);
|
||||||
|
ProjectTaskFlowChange::createInstance([
|
||||||
|
'task_id' => $this->id,
|
||||||
|
'userid' => User::userid(),
|
||||||
|
'before_flow_item_id' => $flowData['flow_item_id'],
|
||||||
|
'before_flow_item_name' => $flowData['flow_item_name'],
|
||||||
|
'after_flow_item_id' => $this->flow_item_id,
|
||||||
|
'after_flow_item_name' => $this->flow_item_name,
|
||||||
|
])->save();
|
||||||
}
|
}
|
||||||
// 状态
|
// 状态
|
||||||
if (Arr::exists($data, 'complete_at')) {
|
if (Arr::exists($data, 'complete_at')) {
|
||||||
|
34
app/Models/ProjectTaskFlowChange.php
Normal file
34
app/Models/ProjectTaskFlowChange.php
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* App\Models\ProjectTaskFlowChange
|
||||||
|
*
|
||||||
|
* @property int $id
|
||||||
|
* @property int|null $task_id 任务ID
|
||||||
|
* @property int|null $userid 会员ID
|
||||||
|
* @property int|null $before_flow_item_id (变化前)工作流状态ID
|
||||||
|
* @property string|null $before_flow_item_name (变化前)工作流状态名称
|
||||||
|
* @property int|null $after_flow_item_id (变化后)工作流状态ID
|
||||||
|
* @property string|null $after_flow_item_name (变化后)工作流状态名称
|
||||||
|
* @property \Illuminate\Support\Carbon|null $created_at
|
||||||
|
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskFlowChange newModelQuery()
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskFlowChange newQuery()
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskFlowChange query()
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskFlowChange whereAfterFlowItemId($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskFlowChange whereAfterFlowItemName($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskFlowChange whereBeforeFlowItemId($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskFlowChange whereBeforeFlowItemName($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskFlowChange whereCreatedAt($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskFlowChange whereId($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskFlowChange whereTaskId($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskFlowChange whereUpdatedAt($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskFlowChange whereUserid($value)
|
||||||
|
* @mixin \Eloquent
|
||||||
|
*/
|
||||||
|
class ProjectTaskFlowChange extends AbstractModel
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
@ -11,7 +11,7 @@ use App\Module\Base;
|
|||||||
* @property int|null $project_id 项目ID
|
* @property int|null $project_id 项目ID
|
||||||
* @property int|null $userid 成员ID
|
* @property int|null $userid 成员ID
|
||||||
* @property int|null $owner 是否负责人
|
* @property int|null $owner 是否负责人
|
||||||
* @property \Illuminate\Support\Carbon|null $top_at 置顶时间
|
* @property string|null $top_at 置顶时间
|
||||||
* @property \Illuminate\Support\Carbon|null $created_at
|
* @property \Illuminate\Support\Carbon|null $created_at
|
||||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||||
* @property-read \App\Models\Project|null $project
|
* @property-read \App\Models\Project|null $project
|
||||||
@ -22,6 +22,7 @@ use App\Module\Base;
|
|||||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectUser whereId($value)
|
* @method static \Illuminate\Database\Eloquent\Builder|ProjectUser whereId($value)
|
||||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectUser whereOwner($value)
|
* @method static \Illuminate\Database\Eloquent\Builder|ProjectUser whereOwner($value)
|
||||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectUser whereProjectId($value)
|
* @method static \Illuminate\Database\Eloquent\Builder|ProjectUser whereProjectId($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder|ProjectUser whereTopAt($value)
|
||||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectUser whereUpdatedAt($value)
|
* @method static \Illuminate\Database\Eloquent\Builder|ProjectUser whereUpdatedAt($value)
|
||||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectUser whereUserid($value)
|
* @method static \Illuminate\Database\Eloquent\Builder|ProjectUser whereUserid($value)
|
||||||
* @mixin \Eloquent
|
* @mixin \Eloquent
|
||||||
|
@ -70,11 +70,7 @@ class WebSocketDialogMsg extends AbstractModel
|
|||||||
public function getPercentageAttribute()
|
public function getPercentageAttribute()
|
||||||
{
|
{
|
||||||
if (!isset($this->appendattrs['percentage'])) {
|
if (!isset($this->appendattrs['percentage'])) {
|
||||||
if ($this->read > $this->send || empty($this->send)) {
|
$this->generatePercentage();
|
||||||
$this->appendattrs['percentage'] = 100;
|
|
||||||
} else {
|
|
||||||
$this->appendattrs['percentage'] = intval($this->read / $this->send * 100);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return $this->appendattrs['percentage'];
|
return $this->appendattrs['percentage'];
|
||||||
}
|
}
|
||||||
@ -98,6 +94,22 @@ class WebSocketDialogMsg extends AbstractModel
|
|||||||
return $value;
|
return $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取占比
|
||||||
|
* @param bool $increment 是否新增阅读数
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function generatePercentage($increment = false) {
|
||||||
|
if ($increment) {
|
||||||
|
$this->increment('read');
|
||||||
|
}
|
||||||
|
if ($this->read > $this->send || empty($this->send)) {
|
||||||
|
return $this->appendattrs['percentage'] = 100;
|
||||||
|
} else {
|
||||||
|
return $this->appendattrs['percentage'] = intval($this->read / $this->send * 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 标记已送达 同时 告诉发送人已送达
|
* 标记已送达 同时 告诉发送人已送达
|
||||||
* @param $userid
|
* @param $userid
|
||||||
@ -127,13 +139,17 @@ class WebSocketDialogMsg extends AbstractModel
|
|||||||
if (!$msgRead->read_at) {
|
if (!$msgRead->read_at) {
|
||||||
$msgRead->read_at = Carbon::now();
|
$msgRead->read_at = Carbon::now();
|
||||||
$msgRead->save();
|
$msgRead->save();
|
||||||
$this->increment('read');
|
$this->generatePercentage(true);
|
||||||
PushTask::push([
|
PushTask::push([
|
||||||
'userid' => $this->userid,
|
'userid' => $this->userid,
|
||||||
'msg' => [
|
'msg' => [
|
||||||
'type' => 'dialog',
|
'type' => 'dialog',
|
||||||
'mode' => 'update',
|
'mode' => 'readed',
|
||||||
'data' => $this->toArray(),
|
'data' => [
|
||||||
|
'id' => $this->id,
|
||||||
|
'read' => $this->read,
|
||||||
|
'percentage' => $this->percentage,
|
||||||
|
],
|
||||||
]
|
]
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ namespace App\Models;
|
|||||||
* @property int $id
|
* @property int $id
|
||||||
* @property int|null $dialog_id 对话ID
|
* @property int|null $dialog_id 对话ID
|
||||||
* @property int|null $userid 会员ID
|
* @property int|null $userid 会员ID
|
||||||
* @property \Illuminate\Support\Carbon|null $top_at 置顶时间
|
* @property string|null $top_at 置顶时间
|
||||||
* @property \Illuminate\Support\Carbon|null $created_at
|
* @property \Illuminate\Support\Carbon|null $created_at
|
||||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser newModelQuery()
|
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser newModelQuery()
|
||||||
@ -17,6 +17,7 @@ namespace App\Models;
|
|||||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereCreatedAt($value)
|
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereCreatedAt($value)
|
||||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereDialogId($value)
|
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereDialogId($value)
|
||||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereId($value)
|
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereId($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereTopAt($value)
|
||||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereUpdatedAt($value)
|
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereUpdatedAt($value)
|
||||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereUserid($value)
|
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereUserid($value)
|
||||||
* @mixin \Eloquent
|
* @mixin \Eloquent
|
||||||
|
@ -342,19 +342,15 @@ class Base
|
|||||||
{
|
{
|
||||||
if (strtolower($charset) == 'utf-8') {
|
if (strtolower($charset) == 'utf-8') {
|
||||||
if (Base::getStrlen($string) <= $length) return $string;
|
if (Base::getStrlen($string) <= $length) return $string;
|
||||||
$strcut = str_replace(array('&', '"', '<', '>'), array('&', '"', '<', '>'), $string);
|
$strcut = Base::utf8Substr($string, $length, $start);
|
||||||
$strcut = Base::utf8Substr($strcut, $length, $start);
|
|
||||||
$strcut = str_replace(array('&', '"', '<', '>'), array('&', '"', '<', '>'), $strcut);
|
|
||||||
return $strcut . $dot;
|
return $strcut . $dot;
|
||||||
} else {
|
} else {
|
||||||
$length = $length * 2;
|
$length = $length * 2;
|
||||||
if (strlen($string) <= $length) return $string;
|
if (strlen($string) <= $length) return $string;
|
||||||
$string = str_replace(array('&', '"', '<', '>'), array('&', '"', '<', '>'), $string);
|
|
||||||
$strcut = '';
|
$strcut = '';
|
||||||
for ($i = 0; $i < $length; $i++) {
|
for ($i = 0; $i < $length; $i++) {
|
||||||
$strcut .= ord($string[$i]) > 127 ? $string[$i] . $string[++$i] : $string[$i];
|
$strcut .= ord($string[$i]) > 127 ? $string[$i] . $string[++$i] : $string[$i];
|
||||||
}
|
}
|
||||||
$strcut = str_replace(array('&', '"', '<', '>'), array('&', '"', '<', '>'), $strcut);
|
|
||||||
}
|
}
|
||||||
return $strcut . $dot;
|
return $strcut . $dot;
|
||||||
}
|
}
|
||||||
@ -2967,4 +2963,19 @@ class Base
|
|||||||
$matrix = array_unique($matrix, SORT_REGULAR);
|
$matrix = array_unique($matrix, SORT_REGULAR);
|
||||||
return array_merge($matrix);
|
return array_merge($matrix);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 去除emoji表情
|
||||||
|
* @param $str
|
||||||
|
* @return string|string[]|null
|
||||||
|
*/
|
||||||
|
public static function filterEmoji($str)
|
||||||
|
{
|
||||||
|
return preg_replace_callback(
|
||||||
|
'/./u',
|
||||||
|
function (array $match) {
|
||||||
|
return strlen($match[0]) >= 4 ? '' : $match[0];
|
||||||
|
},
|
||||||
|
$str);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
144
app/Module/BillExport.php
Normal file
144
app/Module/BillExport.php
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Module;
|
||||||
|
|
||||||
|
use Excel;
|
||||||
|
use Maatwebsite\Excel\Concerns\FromCollection;
|
||||||
|
use Maatwebsite\Excel\Concerns\WithEvents;
|
||||||
|
use Maatwebsite\Excel\Concerns\WithHeadings;
|
||||||
|
use Maatwebsite\Excel\Concerns\WithStrictNullComparison;
|
||||||
|
use Maatwebsite\Excel\Concerns\WithTitle;
|
||||||
|
use Maatwebsite\Excel\Events\AfterSheet;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Cell\DataValidation;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Writer\Exception;
|
||||||
|
|
||||||
|
class BillExport implements WithHeadings, WithEvents, FromCollection, WithTitle, WithStrictNullComparison
|
||||||
|
{
|
||||||
|
public $title;
|
||||||
|
public $headings = [];
|
||||||
|
public $data = [];
|
||||||
|
public $typeLists = [];
|
||||||
|
public $typeNumber = 0;
|
||||||
|
|
||||||
|
public function __construct($title, array $data)
|
||||||
|
{
|
||||||
|
$this->title = $title;
|
||||||
|
$this->data = $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function create($data = [], $title = "Sheet1") {
|
||||||
|
if (is_string($data)) {
|
||||||
|
list($title, $data) = [$data, $title];
|
||||||
|
}
|
||||||
|
if (!is_array($data)) {
|
||||||
|
$data = [];
|
||||||
|
}
|
||||||
|
return new BillExport($title, $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setTitle($title) {
|
||||||
|
$this->title = $title;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setHeadings(array $headings) {
|
||||||
|
$this->headings = $headings;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setData(array $data) {
|
||||||
|
$this->data = $data;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setTypeList(array $typeList, $number = 0) {
|
||||||
|
$this->typeLists = $typeList;
|
||||||
|
$this->typeNumber = $number;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function store($fileName = '') {
|
||||||
|
if (empty($fileName)) {
|
||||||
|
$fileName = date("YmdHis") . '.xls';
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return Excel::store($this, $fileName);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return "导出错误:" . $e->getMessage();
|
||||||
|
} catch (\PhpOffice\PhpSpreadsheet\Exception $e) {
|
||||||
|
return "导出错误:" . $e->getMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function download($fileName = '') {
|
||||||
|
if (empty($fileName)) {
|
||||||
|
$fileName = date("YmdHis") . '.xls';
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return Excel::download($this, $fileName);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return "导出错误:" . $e->getMessage();
|
||||||
|
} catch (\PhpOffice\PhpSpreadsheet\Exception $e) {
|
||||||
|
return "导出错误:" . $e->getMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出的文件标题
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function title(): string
|
||||||
|
{
|
||||||
|
return $this->title;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标题行
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function headings(): array
|
||||||
|
{
|
||||||
|
return $this->headings;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出的内容
|
||||||
|
* @return \Illuminate\Support\Collection
|
||||||
|
*/
|
||||||
|
public function collection()
|
||||||
|
{
|
||||||
|
return collect($this->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置单元格事件
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function registerEvents(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
AfterSheet::Class => function (AfterSheet $event) {
|
||||||
|
$count = count($this->data);
|
||||||
|
foreach ($this->typeLists AS $cell => $typeList) {
|
||||||
|
if ($cell && $typeList) {
|
||||||
|
$p = $this->headings ? 1 : 0;
|
||||||
|
for ($i = 1 + $p; $i <= max($count, $this->typeNumber) + $p; $i++) {
|
||||||
|
$validation = $event->sheet->getDelegate()->getCell($cell . $i)->getDataValidation();
|
||||||
|
$validation->setType(DataValidation::TYPE_LIST);
|
||||||
|
$validation->setErrorStyle(DataValidation::STYLE_WARNING);
|
||||||
|
$validation->setAllowBlank(false);
|
||||||
|
$validation->setShowDropDown(true);
|
||||||
|
$validation->setShowInputMessage(true);
|
||||||
|
$validation->setShowErrorMessage(true);
|
||||||
|
$validation->setErrorTitle('输入的值不合法');
|
||||||
|
$validation->setError('选择的值不在列表中,请选择列表中的值');
|
||||||
|
$validation->setPromptTitle('从列表中选择');
|
||||||
|
$validation->setPrompt('请选择下拉列表中的值');
|
||||||
|
$validation->setFormula1('"' . implode(',', $typeList) . '"');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
16
app/Module/BillImport.php
Normal file
16
app/Module/BillImport.php
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace App\Module;
|
||||||
|
|
||||||
|
|
||||||
|
use Maatwebsite\Excel\Concerns\ToArray;
|
||||||
|
|
||||||
|
class BillImport implements ToArray
|
||||||
|
{
|
||||||
|
public function Array(Array $tables)
|
||||||
|
{
|
||||||
|
return $tables;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -127,9 +127,11 @@ class WebSocketService implements WebSocketHandlerInterface
|
|||||||
case 'readMsg':
|
case 'readMsg':
|
||||||
$ids = is_array($data['id']) ? $data['id'] : [$data['id']];
|
$ids = is_array($data['id']) ? $data['id'] : [$data['id']];
|
||||||
$userid = $this->getUserid($frame->fd);
|
$userid = $this->getUserid($frame->fd);
|
||||||
$list = WebSocketDialogMsg::whereIn('id', $ids)->get();
|
WebSocketDialogMsg::whereIn('id', $ids)->chunkById(20, function($list) use ($userid) {
|
||||||
$list->transform(function(WebSocketDialogMsg $item) use ($userid) {
|
/** @var WebSocketDialogMsg $item */
|
||||||
$item->readSuccess($userid);
|
foreach ($list as $item) {
|
||||||
|
$item->readSuccess($userid);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
37
cmd
37
cmd
@ -13,6 +13,7 @@ Error="${Red}[错误]${Font}"
|
|||||||
|
|
||||||
cur_path="$(pwd)"
|
cur_path="$(pwd)"
|
||||||
cur_arg=$@
|
cur_arg=$@
|
||||||
|
COMPOSE="docker-compose"
|
||||||
|
|
||||||
judge() {
|
judge() {
|
||||||
if [[ 0 -eq $? ]]; then
|
if [[ 0 -eq $? ]]; then
|
||||||
@ -55,20 +56,24 @@ check_docker() {
|
|||||||
echo -e "${Error} ${RedBG} 未安装 Docker!${Font}"
|
echo -e "${Error} ${RedBG} 未安装 Docker!${Font}"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
docker-compose --version &> /dev/null
|
docker-compose version &> /dev/null
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
echo -e "${Error} ${RedBG} 未安装 Docker-compose!${Font}"
|
docker compose version &> /dev/null
|
||||||
exit 1
|
if [ $? -ne 0 ]; then
|
||||||
|
echo -e "${Error} ${RedBG} 未安装 Docker-compose!${Font}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
COMPOSE="docker compose"
|
||||||
fi
|
fi
|
||||||
if [[ -n `docker-compose --version | grep "docker-compose" | grep -E "\sv*1"` ]]; then
|
if [[ -n `$COMPOSE version | grep -E "\sv*1"` ]]; then
|
||||||
docker-compose --version
|
$COMPOSE version
|
||||||
echo -e "${Error} ${RedBG} Docker-compose 版本过低,请升级至v2+!${Font}"
|
echo -e "${Error} ${RedBG} Docker-compose 版本过低,请升级至v2+!${Font}"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
check_node() {
|
check_node() {
|
||||||
npm --version > /dev/null
|
npm --version &> /dev/null
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
echo -e "${Error} ${RedBG} 未安装nodejs!${Font}"
|
echo -e "${Error} ${RedBG} 未安装nodejs!${Font}"
|
||||||
exit 1
|
exit 1
|
||||||
@ -76,7 +81,7 @@ check_node() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
docker_name() {
|
docker_name() {
|
||||||
echo `docker-compose ps | awk '{print $1}' | grep "\-$1\-"`
|
echo `$COMPOSE ps | awk '{print $1}' | grep "\-$1\-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
run_compile() {
|
run_compile() {
|
||||||
@ -272,7 +277,7 @@ if [ $# -gt 0 ]; then
|
|||||||
chmod -R 775 "${cur_path}/docker/mysql/data"
|
chmod -R 775 "${cur_path}/docker/mysql/data"
|
||||||
# 启动容器
|
# 启动容器
|
||||||
[[ "$(arg_get port)" -gt 0 ]] && env_set APP_PORT "$(arg_get port)"
|
[[ "$(arg_get port)" -gt 0 ]] && env_set APP_PORT "$(arg_get port)"
|
||||||
docker-compose up php -d
|
$COMPOSE up php -d
|
||||||
# 安装composer依赖
|
# 安装composer依赖
|
||||||
run_exec php "composer install"
|
run_exec php "composer install"
|
||||||
if [ ! -f "${cur_path}/vendor/autoload.php" ]; then
|
if [ ! -f "${cur_path}/vendor/autoload.php" ]; then
|
||||||
@ -300,7 +305,7 @@ if [ $# -gt 0 ]; then
|
|||||||
run_exec php "php artisan migrate --seed"
|
run_exec php "php artisan migrate --seed"
|
||||||
# 设置初始化密码
|
# 设置初始化密码
|
||||||
res=`run_exec mariadb "sh /etc/mysql/repassword.sh"`
|
res=`run_exec mariadb "sh /etc/mysql/repassword.sh"`
|
||||||
docker-compose up -d
|
$COMPOSE up -d
|
||||||
supervisorctl_restart php
|
supervisorctl_restart php
|
||||||
echo -e "${OK} ${GreenBG} 安装完成 ${Font}"
|
echo -e "${OK} ${GreenBG} 安装完成 ${Font}"
|
||||||
echo -e "地址: http://${GreenBG}127.0.0.1:$(env_get APP_PORT)${Font}"
|
echo -e "地址: http://${GreenBG}127.0.0.1:$(env_get APP_PORT)${Font}"
|
||||||
@ -314,7 +319,7 @@ if [ $# -gt 0 ]; then
|
|||||||
run_exec php "composer update"
|
run_exec php "composer update"
|
||||||
run_exec php "php artisan migrate"
|
run_exec php "php artisan migrate"
|
||||||
supervisorctl_restart php
|
supervisorctl_restart php
|
||||||
docker-compose up -d
|
$COMPOSE up -d
|
||||||
elif [[ "$1" == "uninstall" ]]; then
|
elif [[ "$1" == "uninstall" ]]; then
|
||||||
shift 1
|
shift 1
|
||||||
read -rp "确定要卸载(含:删除容器、数据库、日志)吗?(y/n): " uninstall
|
read -rp "确定要卸载(含:删除容器、数据库、日志)吗?(y/n): " uninstall
|
||||||
@ -328,7 +333,7 @@ if [ $# -gt 0 ]; then
|
|||||||
exit 2
|
exit 2
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
docker-compose down
|
$COMPOSE down
|
||||||
rm -rf "./docker/mysql/data"
|
rm -rf "./docker/mysql/data"
|
||||||
rm -rf "./docker/log/supervisor"
|
rm -rf "./docker/log/supervisor"
|
||||||
find "./storage/logs" -name "*.log" | xargs rm -rf
|
find "./storage/logs" -name "*.log" | xargs rm -rf
|
||||||
@ -341,7 +346,7 @@ if [ $# -gt 0 ]; then
|
|||||||
elif [[ "$1" == "port" ]]; then
|
elif [[ "$1" == "port" ]]; then
|
||||||
shift 1
|
shift 1
|
||||||
env_set APP_PORT "$1"
|
env_set APP_PORT "$1"
|
||||||
docker-compose up -d
|
$COMPOSE up -d
|
||||||
echo -e "${OK} ${GreenBG} 修改成功 ${Font}"
|
echo -e "${OK} ${GreenBG} 修改成功 ${Font}"
|
||||||
echo -e "地址: http://${GreenBG}127.0.0.1:$(env_get APP_PORT)${Font}"
|
echo -e "地址: http://${GreenBG}127.0.0.1:$(env_get APP_PORT)${Font}"
|
||||||
elif [[ "$1" == "repassword" ]]; then
|
elif [[ "$1" == "repassword" ]]; then
|
||||||
@ -414,11 +419,11 @@ if [ $# -gt 0 ]; then
|
|||||||
e="./vendor/bin/phpunit $@" && run_exec php "$e"
|
e="./vendor/bin/phpunit $@" && run_exec php "$e"
|
||||||
elif [[ "$1" == "restart" ]]; then
|
elif [[ "$1" == "restart" ]]; then
|
||||||
shift 1
|
shift 1
|
||||||
docker-compose stop "$@"
|
$COMPOSE stop "$@"
|
||||||
docker-compose start "$@"
|
$COMPOSE start "$@"
|
||||||
else
|
else
|
||||||
docker-compose "$@"
|
$COMPOSE "$@"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
docker-compose ps
|
$COMPOSE ps
|
||||||
fi
|
fi
|
||||||
|
@ -17,7 +17,7 @@ class CreateProjectLogsTable extends Migration
|
|||||||
$table->bigIncrements('id');
|
$table->bigIncrements('id');
|
||||||
$table->bigInteger('project_id')->nullable()->default(0)->comment('项目ID');
|
$table->bigInteger('project_id')->nullable()->default(0)->comment('项目ID');
|
||||||
$table->bigInteger('column_id')->nullable()->default(0)->comment('列表ID');
|
$table->bigInteger('column_id')->nullable()->default(0)->comment('列表ID');
|
||||||
$table->bigInteger('task_id')->nullable()->default(0)->comment('项目ID');
|
$table->bigInteger('task_id')->nullable()->default(0)->comment('任务ID');
|
||||||
$table->bigInteger('userid')->nullable()->default(0)->comment('会员ID');
|
$table->bigInteger('userid')->nullable()->default(0)->comment('会员ID');
|
||||||
$table->string('detail', 500)->nullable()->default('')->comment('详细信息');
|
$table->string('detail', 500)->nullable()->default('')->comment('详细信息');
|
||||||
$table->timestamps();
|
$table->timestamps();
|
||||||
|
@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class CreateProjectTaskFlowChangesTable extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::create('project_task_flow_changes', function (Blueprint $table) {
|
||||||
|
$table->bigIncrements('id');
|
||||||
|
$table->bigInteger('task_id')->nullable()->default(0)->comment('任务ID');
|
||||||
|
$table->bigInteger('userid')->nullable()->default(0)->comment('会员ID');
|
||||||
|
$table->bigInteger('before_item_id')->nullable()->default(0)->comment('(变化前)工作流状态ID');
|
||||||
|
$table->string('before_item_name', 50)->nullable()->default('')->comment('(变化前)工作流状态名称');
|
||||||
|
$table->bigInteger('after_item_id')->nullable()->default(0)->comment('(变化后)工作流状态ID');
|
||||||
|
$table->string('after_item_name', 50)->nullable()->default('')->comment('(变化后)工作流状态名称');
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('project_task_flow_changes');
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class RenamePreProjectTaskFlowChangesItem extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('project_task_flow_changes', function (Blueprint $table) {
|
||||||
|
if (Schema::hasColumn('project_task_flow_changes', 'before_item_id')) {
|
||||||
|
$table->renameColumn('before_item_id', 'before_flow_item_id');
|
||||||
|
$table->renameColumn('before_item_name', 'before_flow_item_name');
|
||||||
|
$table->renameColumn('after_item_id', 'after_flow_item_id');
|
||||||
|
$table->renameColumn('after_item_name', 'after_flow_item_name');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::table('project_task_flow_changes', function (Blueprint $table) {
|
||||||
|
//
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "DooTask",
|
"name": "DooTask",
|
||||||
"version": "0.9.68",
|
"version": "0.10.5",
|
||||||
"description": "DooTask is task management system.",
|
"description": "DooTask is task management system.",
|
||||||
"main": "electron.js",
|
"main": "electron.js",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "DooTask",
|
"name": "DooTask",
|
||||||
"version": "0.9.68",
|
"version": "0.10.5",
|
||||||
"description": "DooTask is task management system.",
|
"description": "DooTask is task management system.",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "./cmd dev",
|
"start": "./cmd dev",
|
||||||
@ -53,7 +53,7 @@
|
|||||||
"less-loader": "^10.2.0",
|
"less-loader": "^10.2.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"moment": "^2.29.1",
|
"moment": "^2.29.1",
|
||||||
"node-sass": "^4.11.0",
|
"node-sass": "^6.0.1",
|
||||||
"notification-koro1": "^1.1.1",
|
"notification-koro1": "^1.1.1",
|
||||||
"postcss": "^8.4.5",
|
"postcss": "^8.4.5",
|
||||||
"resolve-url-loader": "^4.0.0",
|
"resolve-url-loader": "^4.0.0",
|
||||||
|
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/js/app.js
vendored
2
public/js/app.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/206.js
vendored
2
public/js/build/206.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/304.js
vendored
Normal file
2
public/js/build/304.js
vendored
Normal file
File diff suppressed because one or more lines are too long
2
public/js/build/33.js
vendored
2
public/js/build/33.js
vendored
File diff suppressed because one or more lines are too long
2
public/js/build/400.js
vendored
2
public/js/build/400.js
vendored
File diff suppressed because one or more lines are too long
1
public/js/build/422.js
vendored
Normal file
1
public/js/build/422.js
vendored
Normal file
File diff suppressed because one or more lines are too long
2
public/js/build/486.js
vendored
2
public/js/build/486.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/540.js
vendored
1
public/js/build/540.js
vendored
File diff suppressed because one or more lines are too long
2
public/js/build/544.js
vendored
2
public/js/build/544.js
vendored
File diff suppressed because one or more lines are too long
2
public/js/build/601.js
vendored
2
public/js/build/601.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/747.js
vendored
2
public/js/build/747.js
vendored
File diff suppressed because one or more lines are too long
2
public/js/build/836.js
vendored
2
public/js/build/836.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
@ -21,6 +21,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
|
tocObj.reset()
|
||||||
this.init();
|
this.init();
|
||||||
this.createEditor();
|
this.createEditor();
|
||||||
},
|
},
|
||||||
|
@ -1,8 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="quick-edit" :class="[alwaysIcon ? 'quick-always' : '']">
|
<div class="quick-edit" :class="[alwaysIcon ? 'quick-always' : '']">
|
||||||
<div v-if="isEdit" v-clickoutside="onClickOut" class="quick-input">
|
<div v-if="isEdit" v-clickoutside="onClickOut" class="quick-input">
|
||||||
<TagInput v-if="isTag" ref="input" v-model="content" :disabled="isLoad" @on-enter="onEnter" @on-blur="onBlur"/>
|
<TagInput
|
||||||
<Input v-else ref="input" v-model="content" :disabled="isLoad" @on-enter="onEnter" @on-blur="onBlur"/>
|
v-if="isTag"
|
||||||
|
ref="input"
|
||||||
|
v-model="content"
|
||||||
|
:disabled="isLoad"
|
||||||
|
@on-keyup="onKeyup"
|
||||||
|
@on-blur="onBlur"/>
|
||||||
|
<Input
|
||||||
|
v-else
|
||||||
|
ref="input"
|
||||||
|
v-model="content"
|
||||||
|
:disabled="isLoad"
|
||||||
|
@on-keyup="onKeyup"
|
||||||
|
@on-blur="onBlur"/>
|
||||||
<div v-if="isLoad" class="quick-loading"><Loading/></div>
|
<div v-if="isLoad" class="quick-loading"><Loading/></div>
|
||||||
</div>
|
</div>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
@ -54,9 +66,6 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
isEdit(val) {
|
|
||||||
this.$emit("on-edit-change", val);
|
|
||||||
},
|
|
||||||
autoEdit(val) {
|
autoEdit(val) {
|
||||||
if (val === true) {
|
if (val === true) {
|
||||||
setTimeout(this.onEdit, 0)
|
setTimeout(this.onEdit, 0)
|
||||||
@ -65,9 +74,14 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
onEditChange(val) {
|
||||||
|
this.isEdit = val;
|
||||||
|
this.$emit("on-edit-change", val);
|
||||||
|
},
|
||||||
|
|
||||||
onEdit() {
|
onEdit() {
|
||||||
this.content = this.value;
|
this.content = this.value;
|
||||||
this.isEdit = true;
|
this.onEditChange(true);
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.$refs.input.focus({
|
this.$refs.input.focus({
|
||||||
cursor: 'all'
|
cursor: 'all'
|
||||||
@ -75,9 +89,18 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onKeyup(e) {
|
||||||
|
if (e.keyCode === 13) {
|
||||||
|
this.onEnter();
|
||||||
|
} else if (e.keyCode === 27) {
|
||||||
|
this.isEdit = false;
|
||||||
|
this.isLoad = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
onEnter() {
|
onEnter() {
|
||||||
if (this.content == this.value) {
|
if (this.content == this.value) {
|
||||||
this.isEdit = false;
|
this.onEditChange(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this.isLoad) {
|
if (this.isLoad) {
|
||||||
@ -86,7 +109,7 @@ export default {
|
|||||||
this.isLoad = true;
|
this.isLoad = true;
|
||||||
this.$emit("input", this.content);
|
this.$emit("input", this.content);
|
||||||
this.$emit("on-update", this.content, () => {
|
this.$emit("on-update", this.content, () => {
|
||||||
this.isEdit = false;
|
this.onEditChange(false);
|
||||||
this.isLoad = false;
|
this.isLoad = false;
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@ -99,7 +122,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
onBlur() {
|
onBlur() {
|
||||||
if (this.clickOutSide) {
|
if (this.clickOutSide || !this.isEdit) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.onEnter();
|
this.onEnter();
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
{{ $L('使用 SSO 登录') }}
|
{{ $L('使用 SSO 登录') }}
|
||||||
</div>
|
</div>
|
||||||
<template v-if="showDown">
|
<template v-if="showDown">
|
||||||
<div v-if="$Electron" class="common-right-bottom-link" @click="releasesNotification">
|
<div v-if="$Electron" class="common-right-bottom-link" @click="updateWinShow=true">
|
||||||
<Icon type="md-download"/>
|
<Icon type="md-download"/>
|
||||||
{{ $L(repoTitle) }}
|
{{ $L(repoTitle) }}
|
||||||
</div>
|
</div>
|
||||||
@ -14,20 +14,32 @@
|
|||||||
{{ $L(repoTitle) }}
|
{{ $L(repoTitle) }}
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
|
<Modal
|
||||||
|
v-model="updateWinShow"
|
||||||
|
:ok-text="$L('立即升级')"
|
||||||
|
:closable="false"
|
||||||
|
:mask-closable="false"
|
||||||
|
@on-ok="installApplication"
|
||||||
|
@on-cancel="repoStatus=2"
|
||||||
|
class-name="common-right-bottom-notification">
|
||||||
|
<div slot="header" class="notification-head">
|
||||||
|
<div class="notification-title">{{$L('发现新版本')}}</div>
|
||||||
|
<Tag color="volcano">{{repoReleases.tag_name}}</Tag>
|
||||||
|
</div>
|
||||||
|
<MarkdownPreview class="notification-body overlay-y" :initialValue="repoReleases.body"/>
|
||||||
|
</Modal>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Vue from 'vue'
|
|
||||||
import MarkdownPreview from "./MDEditor/components/preview";
|
import MarkdownPreview from "./MDEditor/components/preview";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
Vue.component('MarkdownPreview', MarkdownPreview)
|
|
||||||
|
|
||||||
import {mapState} from "vuex";
|
import {mapState} from "vuex";
|
||||||
import {Store} from "le5le-store";
|
import {Store} from "le5le-store";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'RightBottom',
|
name: 'RightBottom',
|
||||||
|
components: {MarkdownPreview},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
loadIng: 0,
|
loadIng: 0,
|
||||||
@ -37,6 +49,7 @@ export default {
|
|||||||
repoStatus: 0, // 0 没有,1有客户端,2客户端有新版本
|
repoStatus: 0, // 0 没有,1有客户端,2客户端有新版本
|
||||||
repoReleases: {},
|
repoReleases: {},
|
||||||
|
|
||||||
|
updateWinShow: false,
|
||||||
downloadResult: {},
|
downloadResult: {},
|
||||||
|
|
||||||
subscribe: null,
|
subscribe: null,
|
||||||
@ -46,14 +59,15 @@ export default {
|
|||||||
this.getReleases();
|
this.getReleases();
|
||||||
//
|
//
|
||||||
this.subscribe = Store.subscribe('releasesNotification', () => {
|
this.subscribe = Store.subscribe('releasesNotification', () => {
|
||||||
this.releasesNotification();
|
this.updateWinShow = true;
|
||||||
});
|
});
|
||||||
//
|
//
|
||||||
if (this.$Electron) {
|
if (this.$Electron) {
|
||||||
this.$Electron.registerMsgListener('downloadDone', ({result}) => {
|
this.$Electron.registerMsgListener('downloadDone', ({result}) => {
|
||||||
if (result.name == this.repoData.name && this.repoStatus !== 2) {
|
if (result.name == this.repoData.name && this.repoStatus !== 2) {
|
||||||
|
this.$store.state.clientNewVersion = this.repoReleases.tag_name
|
||||||
this.downloadResult = result;
|
this.downloadResult = result;
|
||||||
this.releasesNotification()
|
this.updateWinShow = true;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -213,44 +227,6 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
releasesNotification() {
|
|
||||||
const {tag_name, body} = this.repoReleases;
|
|
||||||
this.$store.state.clientNewVersion = tag_name
|
|
||||||
$A.modalConfirm({
|
|
||||||
okText: this.$L('立即更新'),
|
|
||||||
onOk: () => {
|
|
||||||
this.installApplication();
|
|
||||||
},
|
|
||||||
onCancel: () => {
|
|
||||||
this.repoStatus = 2;
|
|
||||||
},
|
|
||||||
render: (h) => {
|
|
||||||
return h('div', {
|
|
||||||
class: 'common-right-bottom-notification'
|
|
||||||
}, [
|
|
||||||
h('div', {
|
|
||||||
class: "notification-head"
|
|
||||||
}, [
|
|
||||||
h('div', {
|
|
||||||
class: "notification-title"
|
|
||||||
}, this.$L('发现新版本')),
|
|
||||||
h('Tag', {
|
|
||||||
props: {
|
|
||||||
color: 'volcano'
|
|
||||||
}
|
|
||||||
}, tag_name)
|
|
||||||
]),
|
|
||||||
h('MarkdownPreview', {
|
|
||||||
class: 'notification-body',
|
|
||||||
props: {
|
|
||||||
initialValue: body
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
installApplication() {
|
installApplication() {
|
||||||
if (!this.$Electron) {
|
if (!this.$Electron) {
|
||||||
return;
|
return;
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<div class="teditor-wrapper">
|
<div class="teditor-wrapper">
|
||||||
<div class="teditor-box" :class="[!inline && spinShow ? 'teditor-loadstyle' : 'teditor-loadedstyle']">
|
<div class="teditor-box" :class="[!inline && spinShow ? 'teditor-loadstyle' : 'teditor-loadedstyle']">
|
||||||
<template v-if="inline">
|
<template v-if="inline">
|
||||||
<div ref="myTextarea" :id="id" v-html="content"></div>
|
<div ref="myTextarea" :id="id" v-html="spinShow ? '' : content"></div>
|
||||||
<Icon v-if="spinShow" type="ios-loading" :size="18" class="icon-loading icon-inline"></Icon>
|
<Icon v-if="spinShow" type="ios-loading" :size="18" class="icon-loading icon-inline"></Icon>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
@ -181,11 +181,7 @@
|
|||||||
newValue = "";
|
newValue = "";
|
||||||
}
|
}
|
||||||
if (!this.isTyping) {
|
if (!this.isTyping) {
|
||||||
if (this.getEditor() !== null) {
|
this.setContent(newValue);
|
||||||
this.getEditor().setContent(newValue);
|
|
||||||
} else{
|
|
||||||
this.content = newValue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
readOnly(value) {
|
readOnly(value) {
|
||||||
@ -459,6 +455,14 @@
|
|||||||
return this.getEditor().getContent();
|
return this.getEditor().getContent();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setContent(content) {
|
||||||
|
if (this.getEditor() === null) {
|
||||||
|
this.content = content;
|
||||||
|
} else if (content != this.getEditor().getContent()){
|
||||||
|
this.getEditor().setContent(content);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
insertImage(src) {
|
insertImage(src) {
|
||||||
this.insertContent('<img src="' + src + '">');
|
this.insertContent('<img src="' + src + '">');
|
||||||
},
|
},
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
:placeholder="tis || placeholderText"
|
:placeholder="tis || placeholderText"
|
||||||
@keydown.enter="downEnter($event)"
|
@keydown.enter="downEnter($event)"
|
||||||
@keydown.delete="delTag(false)"
|
@keydown.delete="delTag(false)"
|
||||||
@keyup="addTag($event, content)"
|
@keyup="onKeyup"
|
||||||
@focus="onFocus"
|
@focus="onFocus"
|
||||||
@blur="onBlur"
|
@blur="onBlur"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
@ -158,19 +158,22 @@
|
|||||||
this.addTag(false, this.content)
|
this.addTag(false, this.content)
|
||||||
this.$emit("on-blur", e)
|
this.$emit("on-blur", e)
|
||||||
},
|
},
|
||||||
|
onKeyup(e) {
|
||||||
|
this.addTag(e, this.content);
|
||||||
|
//
|
||||||
|
this.$emit("on-keyup", e)
|
||||||
|
if (e.keyCode === 13) {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$emit("on-enter", e)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
addTag(e, content) {
|
addTag(e, content) {
|
||||||
|
|
||||||
if (e === false || e.keyCode === 13) {
|
if (e === false || e.keyCode === 13) {
|
||||||
if (content.trim() != '' && this.disSource.indexOf(content.trim()) === -1) {
|
if (content.trim() != '' && this.disSource.indexOf(content.trim()) === -1) {
|
||||||
this.disSource.push(content.trim());
|
this.disSource.push(content.trim());
|
||||||
}
|
}
|
||||||
this.content = '';
|
this.content = '';
|
||||||
//
|
|
||||||
if (e.keyCode === 13) {
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.$emit("on-enter", e)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this.max > 0 && this.disSource.length >= this.max) {
|
if (this.max > 0 && this.disSource.length >= this.max) {
|
||||||
|
6
resources/assets/js/functions/web.js
vendored
6
resources/assets/js/functions/web.js
vendored
@ -102,7 +102,7 @@
|
|||||||
* @returns {*|string}
|
* @returns {*|string}
|
||||||
*/
|
*/
|
||||||
formatTime(date) {
|
formatTime(date) {
|
||||||
let time = Math.round($A.Date(date).getTime() / 1000),
|
let time = $A.Date(date, true),
|
||||||
string = '';
|
string = '';
|
||||||
if ($A.formatDate('Ymd') === $A.formatDate('Ymd', time)) {
|
if ($A.formatDate('Ymd') === $A.formatDate('Ymd', time)) {
|
||||||
string = $A.formatDate('H:i', time)
|
string = $A.formatDate('H:i', time)
|
||||||
@ -157,8 +157,10 @@
|
|||||||
let time = Math.round(this.Date(date).getTime() / 1000) - nowTime;
|
let time = Math.round(this.Date(date).getTime() / 1000) - nowTime;
|
||||||
if (time < 86400 * 7 && time > 0 ) {
|
if (time < 86400 * 7 && time > 0 ) {
|
||||||
return this.formatSeconds(time);
|
return this.formatSeconds(time);
|
||||||
} else if (time <= 0) {
|
} else if (time < 0) {
|
||||||
return '-' + this.formatSeconds(time * -1);
|
return '-' + this.formatSeconds(time * -1);
|
||||||
|
} else if (time == 0) {
|
||||||
|
return 0 + 's';
|
||||||
}
|
}
|
||||||
return this.formatTime(date)
|
return this.formatTime(date)
|
||||||
},
|
},
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
<i class="taskfont"></i>
|
<i class="taskfont"></i>
|
||||||
</div>
|
</div>
|
||||||
<Dropdown-menu slot="list" class="login-setting-menu">
|
<Dropdown-menu slot="list" class="login-setting-menu">
|
||||||
<Dropdown placement="right-start" @on-click="setTheme">
|
<Dropdown placement="right" @on-click="setTheme">
|
||||||
<DropdownItem>
|
<DropdownItem>
|
||||||
<div class="login-setting-item">
|
<div class="login-setting-item">
|
||||||
{{$L('主题皮肤')}}
|
{{$L('主题皮肤')}}
|
||||||
@ -48,7 +48,7 @@
|
|||||||
<Dropdown-item v-for="(item, key) in themeList" :key="key" :name="item.value" :selected="themeMode === item.value">{{$L(item.name)}}</Dropdown-item>
|
<Dropdown-item v-for="(item, key) in themeList" :key="key" :name="item.value" :selected="themeMode === item.value">{{$L(item.name)}}</Dropdown-item>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
<Dropdown placement="right-start" @on-click="setLanguage">
|
<Dropdown placement="right" @on-click="setLanguage">
|
||||||
<DropdownItem divided>
|
<DropdownItem divided>
|
||||||
<div class="login-setting-item">
|
<div class="login-setting-item">
|
||||||
{{currentLanguage}}
|
{{currentLanguage}}
|
||||||
@ -114,6 +114,9 @@ export default {
|
|||||||
this.subscribe = null;
|
this.subscribe = null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
activated() {
|
||||||
|
this.loginType = 'login'
|
||||||
|
},
|
||||||
deactivated() {
|
deactivated() {
|
||||||
this.loginJump = false;
|
this.loginJump = false;
|
||||||
this.password = "";
|
this.password = "";
|
||||||
|
@ -19,46 +19,93 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<DropdownMenu slot="list">
|
<DropdownMenu slot="list">
|
||||||
<DropdownItem
|
<template v-for="item in menu">
|
||||||
v-for="(item, key) in menu"
|
<!-- 团队管理 -->
|
||||||
v-if="item.visible !== false"
|
<Dropdown
|
||||||
:key="key"
|
v-if="item.path === 'team'"
|
||||||
:divided="!!item.divided"
|
placement="right-start">
|
||||||
:name="item.path">
|
<DropdownItem divided>
|
||||||
{{$L(item.name)}}
|
<div class="manage-menu-flex">
|
||||||
<Badge v-if="item.path === 'version'" class="manage-menu-report-badge" :text="clientNewVersion"/>
|
{{$L(item.name)}}
|
||||||
<Badge v-if="item.path === 'workReport'" class="manage-menu-report-badge" :count="reportUnreadNumber"/>
|
<Badge v-if="reportUnreadNumber > 0" class="manage-menu-report-badge" :count="reportUnreadNumber"/>
|
||||||
</DropdownItem>
|
<Icon v-else type="ios-arrow-forward"></Icon>
|
||||||
<Dropdown placement="right-start" @on-click="setTheme">
|
</div>
|
||||||
<DropdownItem divided>
|
</DropdownItem>
|
||||||
<div class="manage-menu-language">
|
<DropdownMenu slot="list">
|
||||||
{{$L('主题皮肤')}}
|
<DropdownItem name="allUser">{{$L('团队管理')}}</DropdownItem>
|
||||||
<Icon type="ios-arrow-forward"></Icon>
|
<DropdownItem name="workReport">
|
||||||
</div>
|
<div class="manage-menu-flex">
|
||||||
|
{{$L('工作报告')}}
|
||||||
|
<Badge v-if="reportUnreadNumber > 0" class="manage-menu-report-badge" :count="reportUnreadNumber"/>
|
||||||
|
</div>
|
||||||
|
</DropdownItem>
|
||||||
|
<DropdownItem name="exportTask">{{$L('导出任务统计')}}</DropdownItem>
|
||||||
|
</DropdownMenu>
|
||||||
|
</Dropdown>
|
||||||
|
<!-- 主题皮肤 -->
|
||||||
|
<Dropdown
|
||||||
|
v-else-if="item.path === 'theme'"
|
||||||
|
placement="right-start"
|
||||||
|
@on-click="setTheme">
|
||||||
|
<DropdownItem divided>
|
||||||
|
<div class="manage-menu-flex">
|
||||||
|
{{$L(item.name)}}
|
||||||
|
<Icon type="ios-arrow-forward"></Icon>
|
||||||
|
</div>
|
||||||
|
</DropdownItem>
|
||||||
|
<DropdownMenu slot="list">
|
||||||
|
<DropdownItem
|
||||||
|
v-for="(item, key) in themeList"
|
||||||
|
:key="key"
|
||||||
|
:name="item.value"
|
||||||
|
:selected="themeMode === item.value">{{$L(item.name)}}</DropdownItem>
|
||||||
|
</DropdownMenu>
|
||||||
|
</Dropdown>
|
||||||
|
<!-- 语言设置 -->
|
||||||
|
<Dropdown
|
||||||
|
v-else-if="item.path === 'language'"
|
||||||
|
placement="right-start"
|
||||||
|
@on-click="setLanguage">
|
||||||
|
<DropdownItem divided>
|
||||||
|
<div class="manage-menu-flex">
|
||||||
|
{{currentLanguage}}
|
||||||
|
<Icon type="ios-arrow-forward"></Icon>
|
||||||
|
</div>
|
||||||
|
</DropdownItem>
|
||||||
|
<DropdownMenu slot="list">
|
||||||
|
<DropdownItem
|
||||||
|
v-for="(item, key) in languageList"
|
||||||
|
:key="key"
|
||||||
|
:name="key"
|
||||||
|
:selected="getLanguage() === key">{{item}}</DropdownItem>
|
||||||
|
</DropdownMenu>
|
||||||
|
</Dropdown>
|
||||||
|
<!-- 其他菜单 -->
|
||||||
|
<DropdownItem
|
||||||
|
v-else-if="item.visible !== false"
|
||||||
|
:divided="!!item.divided"
|
||||||
|
:name="item.path"
|
||||||
|
:style="item.style || {}">
|
||||||
|
{{$L(item.name)}}
|
||||||
|
<Badge
|
||||||
|
v-if="item.path === 'version'"
|
||||||
|
class="manage-menu-report-badge"
|
||||||
|
:text="clientNewVersion"/>
|
||||||
|
<Badge
|
||||||
|
v-else-if="item.path === 'workReport' && reportUnreadNumber > 0"
|
||||||
|
class="manage-menu-report-badge"
|
||||||
|
:count="reportUnreadNumber"/>
|
||||||
</DropdownItem>
|
</DropdownItem>
|
||||||
<DropdownMenu slot="list">
|
</template>
|
||||||
<Dropdown-item v-for="(item, key) in themeList" :key="key" :name="item.value" :selected="themeMode === item.value">{{$L(item.name)}}</Dropdown-item>
|
|
||||||
</DropdownMenu>
|
|
||||||
</Dropdown>
|
|
||||||
<Dropdown placement="right-start" @on-click="setLanguage">
|
|
||||||
<DropdownItem divided>
|
|
||||||
<div class="manage-menu-language">
|
|
||||||
{{currentLanguage}}
|
|
||||||
<Icon type="ios-arrow-forward"></Icon>
|
|
||||||
</div>
|
|
||||||
</DropdownItem>
|
|
||||||
<DropdownMenu slot="list">
|
|
||||||
<Dropdown-item v-for="(item, key) in languageList" :key="key" :name="key" :selected="getLanguage() === key">{{item}}</Dropdown-item>
|
|
||||||
</DropdownMenu>
|
|
||||||
</Dropdown>
|
|
||||||
<DropdownItem divided name="signout" style="color:#f40">{{$L('退出登录')}}</DropdownItem>
|
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
<ul :class="overlayClass" @scroll="handleClickTopOperateOutside">
|
<ul :class="overlayClass" @scroll="handleClickTopOperateOutside">
|
||||||
<li @click="toggleRoute('dashboard')" :class="classNameRoute('dashboard')">
|
<li @click="toggleRoute('dashboard')" :class="classNameRoute('dashboard')">
|
||||||
<i class="taskfont"></i>
|
<i class="taskfont"></i>
|
||||||
<div class="menu-title">{{$L('仪表盘')}}</div>
|
<div class="menu-title">{{$L('仪表盘')}}</div>
|
||||||
<Badge class="menu-badge" :type="dashboardTask.overdue.length > 0 ? 'error' : 'primary'" :count="dashboardTotal"></Badge>
|
<Badge v-if="dashboardTask.overdue.length > 0" class="menu-badge" type="error" :count="dashboardTask.overdue.length"/>
|
||||||
|
<Badge v-else-if="dashboardTask.today.length > 0" class="menu-badge" type="info" :count="dashboardTask.today.length"/>
|
||||||
|
<Badge v-else-if="dashboardTask.all.length > 0" class="menu-badge" type="primary" :count="dashboardTask.all.length"/>
|
||||||
</li>
|
</li>
|
||||||
<li @click="toggleRoute('calendar')" :class="classNameRoute('calendar')">
|
<li @click="toggleRoute('calendar')" :class="classNameRoute('calendar')">
|
||||||
<i class="taskfont"></i>
|
<i class="taskfont"></i>
|
||||||
@ -67,7 +114,7 @@
|
|||||||
<li @click="toggleRoute('messenger')" :class="classNameRoute('messenger')">
|
<li @click="toggleRoute('messenger')" :class="classNameRoute('messenger')">
|
||||||
<i class="taskfont"></i>
|
<i class="taskfont"></i>
|
||||||
<div class="menu-title">{{$L('消息')}}</div>
|
<div class="menu-title">{{$L('消息')}}</div>
|
||||||
<Badge class="menu-badge" :count="msgAllUnread"></Badge>
|
<Badge class="menu-badge" :count="msgAllUnread"/>
|
||||||
</li>
|
</li>
|
||||||
<li @click="toggleRoute('file')" :class="classNameRoute('file')">
|
<li @click="toggleRoute('file')" :class="classNameRoute('file')">
|
||||||
<i class="taskfont"></i>
|
<i class="taskfont"></i>
|
||||||
@ -184,18 +231,42 @@
|
|||||||
<TaskAdd ref="addTask" v-model="addTaskShow"/>
|
<TaskAdd ref="addTask" v-model="addTaskShow"/>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
|
<!--导出任务统计-->
|
||||||
|
<Modal
|
||||||
|
v-model="exportTaskShow"
|
||||||
|
:title="$L('导出任务统计')"
|
||||||
|
:mask-closable="false">
|
||||||
|
<Form ref="exportTask" :model="exportData" label-width="auto" @submit.native.prevent>
|
||||||
|
<FormItem :label="$L('导出会员')">
|
||||||
|
<UserInput v-model="exportData.userid" :multiple-max="20" :placeholder="$L('请选择会员')"/>
|
||||||
|
</FormItem>
|
||||||
|
<FormItem :label="$L('时间范围')">
|
||||||
|
<DatePicker
|
||||||
|
v-model="exportData.time"
|
||||||
|
type="daterange"
|
||||||
|
format="yyyy/MM/dd"
|
||||||
|
style="width:100%"
|
||||||
|
:placeholder="$L('请选择时间')"/>
|
||||||
|
</FormItem>
|
||||||
|
</Form>
|
||||||
|
<div slot="footer" class="adaption">
|
||||||
|
<Button type="default" @click="exportTaskShow=false">{{$L('取消')}}</Button>
|
||||||
|
<Button type="primary" :loading="exportLoadIng > 0" @click="onExportTask">{{$L('导出')}}</Button>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
<!--任务详情-->
|
<!--任务详情-->
|
||||||
<Modal
|
<Modal
|
||||||
:value="taskId > 0"
|
:value="taskId > 0"
|
||||||
:mask-closable="false"
|
|
||||||
:styles="{
|
:styles="{
|
||||||
width: '90%',
|
width: '90%',
|
||||||
maxWidth: taskData.dialog_id ? '1200px' : '700px'
|
maxWidth: taskData.dialog_id ? '1200px' : '700px'
|
||||||
}"
|
}"
|
||||||
@on-visible-change="taskVisibleChange"
|
:mask-closable="false"
|
||||||
footer-hide>
|
:footer-hide="true"
|
||||||
|
@on-visible-change="taskVisibleChange">
|
||||||
<div class="page-manage-task-modal" :style="taskStyle">
|
<div class="page-manage-task-modal" :style="taskStyle">
|
||||||
<TaskDetail :task-id="taskId" :open-task="taskData"/>
|
<TaskDetail ref="taskDetail" :task-id="taskId" :open-task="taskData"/>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
@ -248,20 +319,27 @@
|
|||||||
import { mapState, mapGetters } from 'vuex'
|
import { mapState, mapGetters } from 'vuex'
|
||||||
import TaskDetail from "./manage/components/TaskDetail";
|
import TaskDetail from "./manage/components/TaskDetail";
|
||||||
import ProjectArchived from "./manage/components/ProjectArchived";
|
import ProjectArchived from "./manage/components/ProjectArchived";
|
||||||
import notificationKoro from "notification-koro1";
|
|
||||||
import TeamManagement from "./manage/components/TeamManagement";
|
import TeamManagement from "./manage/components/TeamManagement";
|
||||||
import ProjectManagement from "./manage/components/ProjectManagement";
|
import ProjectManagement from "./manage/components/ProjectManagement";
|
||||||
import DrawerOverlay from "../components/DrawerOverlay";
|
import DrawerOverlay from "../components/DrawerOverlay";
|
||||||
import DragBallComponent from "../components/DragBallComponent";
|
import DragBallComponent from "../components/DragBallComponent";
|
||||||
import TaskAdd from "./manage/components/TaskAdd";
|
import TaskAdd from "./manage/components/TaskAdd";
|
||||||
import Report from "./manage/components/Report";
|
import Report from "./manage/components/Report";
|
||||||
|
import notificationKoro from "notification-koro1";
|
||||||
import {Store} from "le5le-store";
|
import {Store} from "le5le-store";
|
||||||
|
import UserInput from "../components/UserInput";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
UserInput,
|
||||||
TaskAdd,
|
TaskAdd,
|
||||||
|
TaskDetail,
|
||||||
Report,
|
Report,
|
||||||
DragBallComponent, DrawerOverlay, ProjectManagement, TeamManagement, ProjectArchived, TaskDetail},
|
DragBallComponent,
|
||||||
|
DrawerOverlay,
|
||||||
|
ProjectManagement,
|
||||||
|
TeamManagement,
|
||||||
|
ProjectArchived},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
loadIng: 0,
|
loadIng: 0,
|
||||||
@ -280,6 +358,13 @@ export default {
|
|||||||
addTaskShow: false,
|
addTaskShow: false,
|
||||||
addTaskSubscribe: null,
|
addTaskSubscribe: null,
|
||||||
|
|
||||||
|
exportTaskShow: false,
|
||||||
|
exportLoadIng: 0,
|
||||||
|
exportData: {
|
||||||
|
userid: [],
|
||||||
|
time: [],
|
||||||
|
},
|
||||||
|
|
||||||
dialogMsgSubscribe: null,
|
dialogMsgSubscribe: null,
|
||||||
|
|
||||||
projectKeyValue: '',
|
projectKeyValue: '',
|
||||||
@ -380,12 +465,8 @@ export default {
|
|||||||
return num;
|
return num;
|
||||||
},
|
},
|
||||||
|
|
||||||
dashboardTotal() {
|
|
||||||
return this.dashboardTask.today.length + this.dashboardTask.overdue.length
|
|
||||||
},
|
|
||||||
|
|
||||||
unreadTotal() {
|
unreadTotal() {
|
||||||
return this.msgAllUnread + this.dashboardTotal + this.reportUnreadNumber;
|
return this.msgAllUnread + this.dashboardTask.overdue.length + this.reportUnreadNumber;
|
||||||
},
|
},
|
||||||
|
|
||||||
currentLanguage() {
|
currentLanguage() {
|
||||||
@ -399,21 +480,37 @@ export default {
|
|||||||
{path: 'personal', name: '个人设置'},
|
{path: 'personal', name: '个人设置'},
|
||||||
{path: 'password', name: '密码设置'},
|
{path: 'password', name: '密码设置'},
|
||||||
{path: 'clearCache', name: '清除缓存'},
|
{path: 'clearCache', name: '清除缓存'},
|
||||||
|
|
||||||
{path: 'system', name: '系统设置', divided: true},
|
{path: 'system', name: '系统设置', divided: true},
|
||||||
{path: 'version', name: '更新版本', visible: !!this.clientNewVersion},
|
{path: 'version', name: '更新版本', visible: !!this.clientNewVersion},
|
||||||
{path: 'workReport', name: '工作报告', divided: true},
|
|
||||||
{path: 'allUser', name: '团队管理'},
|
{path: 'allProject', name: '所有项目', divided: true},
|
||||||
{path: 'allProject', name: '所有项目'},
|
{path: 'archivedProject', name: '已归档的项目'},
|
||||||
{path: 'archivedProject', name: '已归档的项目'}
|
|
||||||
|
{path: 'team', name: '团队管理', divided: true},
|
||||||
|
|
||||||
|
{path: 'theme', name: '主题皮肤', divided: true},
|
||||||
|
|
||||||
|
{path: 'language', name: this.currentLanguage, divided: true},
|
||||||
|
|
||||||
|
{path: 'logout', name: '退出登录', style: {color: '#f40'}, divided: true},
|
||||||
]
|
]
|
||||||
} else {
|
} else {
|
||||||
return [
|
return [
|
||||||
{path: 'personal', name: '个人设置'},
|
{path: 'personal', name: '个人设置'},
|
||||||
{path: 'password', name: '密码设置'},
|
{path: 'password', name: '密码设置'},
|
||||||
{path: 'clearCache', name: '清除缓存'},
|
{path: 'clearCache', name: '清除缓存'},
|
||||||
|
|
||||||
{path: 'version', name: '更新版本', divided: true, visible: !!this.clientNewVersion},
|
{path: 'version', name: '更新版本', divided: true, visible: !!this.clientNewVersion},
|
||||||
|
|
||||||
{path: 'workReport', name: '工作报告', divided: true},
|
{path: 'workReport', name: '工作报告', divided: true},
|
||||||
{path: 'archivedProject', name: '已归档的项目'}
|
{path: 'archivedProject', name: '已归档的项目'},
|
||||||
|
|
||||||
|
{path: 'theme', name: '主题皮肤', divided: true},
|
||||||
|
|
||||||
|
{path: 'language', name: this.currentLanguage, divided: true},
|
||||||
|
|
||||||
|
{path: 'logout', name: '退出登录', style: {color: '#f40'}, divided: true},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -429,7 +526,7 @@ export default {
|
|||||||
|
|
||||||
projectLists() {
|
projectLists() {
|
||||||
const {projectKeyValue, cacheProjects} = this;
|
const {projectKeyValue, cacheProjects} = this;
|
||||||
const data = cacheProjects.sort((a, b) => {
|
const data = $A.cloneJSON(cacheProjects).sort((a, b) => {
|
||||||
if (a.top_at || b.top_at) {
|
if (a.top_at || b.top_at) {
|
||||||
return $A.Date(b.top_at) - $A.Date(a.top_at);
|
return $A.Date(b.top_at) - $A.Date(a.top_at);
|
||||||
}
|
}
|
||||||
@ -575,6 +672,9 @@ export default {
|
|||||||
case 'archivedProject':
|
case 'archivedProject':
|
||||||
this.archivedProjectShow = true;
|
this.archivedProjectShow = true;
|
||||||
return;
|
return;
|
||||||
|
case 'exportTask':
|
||||||
|
this.exportTaskShow = true;
|
||||||
|
return;
|
||||||
case 'workReport':
|
case 'workReport':
|
||||||
if (this.reportUnreadNumber > 0) {
|
if (this.reportUnreadNumber > 0) {
|
||||||
this.reportTabs = "receive";
|
this.reportTabs = "receive";
|
||||||
@ -592,7 +692,7 @@ export default {
|
|||||||
window.location.reload()
|
window.location.reload()
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
case 'signout':
|
case 'logout':
|
||||||
$A.modalConfirm({
|
$A.modalConfirm({
|
||||||
title: '退出登录',
|
title: '退出登录',
|
||||||
content: '你确定要登出系统?',
|
content: '你确定要登出系统?',
|
||||||
@ -685,10 +785,13 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
shortcutEvent(e) {
|
shortcutEvent(e) {
|
||||||
if (e.keyCode === 75 || e.keyCode === 78) {
|
if (e.metaKey || e.ctrlKey) {
|
||||||
if (e.metaKey || e.ctrlKey) {
|
if (e.keyCode === 75 || e.keyCode === 78) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.onAddTask(0)
|
this.onAddTask(0)
|
||||||
|
} else if (e.keyCode === 83 && this.taskId > 0) {
|
||||||
|
e.preventDefault();
|
||||||
|
this.$refs.taskDetail.checkUpdate(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -756,11 +859,60 @@ export default {
|
|||||||
this.$store.dispatch("call", {
|
this.$store.dispatch("call", {
|
||||||
url: 'report/unread',
|
url: 'report/unread',
|
||||||
}).then(({data}) => {
|
}).then(({data}) => {
|
||||||
this.reportUnreadNumber = data.total ? data.total : 0;
|
this.reportUnreadNumber = data.total || 0;
|
||||||
}).catch(() => {});
|
}).catch(() => {});
|
||||||
}, typeof timeout === "number" ? timeout : 1000)
|
}, typeof timeout === "number" ? timeout : 1000)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
handleRightClick(event, item) {
|
||||||
|
this.handleClickTopOperateOutside();
|
||||||
|
this.topOperateItem = item;
|
||||||
|
this.$nextTick(() => {
|
||||||
|
const projectWrap = this.$refs.projectWrapper;
|
||||||
|
const projectBounding = projectWrap.getBoundingClientRect();
|
||||||
|
this.topOperateStyles = {
|
||||||
|
left: `${event.clientX - projectBounding.left}px`,
|
||||||
|
top: `${event.clientY - projectBounding.top}px`
|
||||||
|
};
|
||||||
|
this.topOperateVisible = true;
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
handleClickTopOperateOutside() {
|
||||||
|
this.topOperateVisible = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
handleTopClick() {
|
||||||
|
this.$store.dispatch("call", {
|
||||||
|
url: 'project/top',
|
||||||
|
data: {
|
||||||
|
project_id: this.topOperateItem.id,
|
||||||
|
},
|
||||||
|
}).then(() => {
|
||||||
|
this.$store.dispatch("getProjects").catch(() => {});
|
||||||
|
}).catch(({msg}) => {
|
||||||
|
$A.modalError(msg, 301);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onExportTask() {
|
||||||
|
if (this.exportLoadIng > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.exportLoadIng++;
|
||||||
|
this.$store.dispatch("call", {
|
||||||
|
url: 'project/task/export',
|
||||||
|
data: this.exportData,
|
||||||
|
}).then(({data}) => {
|
||||||
|
this.exportLoadIng--;
|
||||||
|
this.exportTaskShow = false;
|
||||||
|
$A.downFile(data.url);
|
||||||
|
}).catch(({msg}) => {
|
||||||
|
this.exportLoadIng--;
|
||||||
|
$A.modalError(msg);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
notificationInit() {
|
notificationInit() {
|
||||||
this.notificationClass = new notificationKoro(this.$L("打开通知成功"));
|
this.notificationClass = new notificationKoro(this.$L("打开通知成功"));
|
||||||
if (this.notificationClass.support) {
|
if (this.notificationClass.support) {
|
||||||
@ -821,37 +973,6 @@ export default {
|
|||||||
}
|
}
|
||||||
document.addEventListener(visibilityChangeEvent, visibilityChangeListener);
|
document.addEventListener(visibilityChangeEvent, visibilityChangeListener);
|
||||||
},
|
},
|
||||||
|
|
||||||
handleRightClick(event, item) {
|
|
||||||
this.handleClickTopOperateOutside();
|
|
||||||
this.topOperateItem = item;
|
|
||||||
this.$nextTick(() => {
|
|
||||||
const projectWrap = this.$refs.projectWrapper;
|
|
||||||
const projectBounding = projectWrap.getBoundingClientRect();
|
|
||||||
this.topOperateStyles = {
|
|
||||||
left: `${event.clientX - projectBounding.left}px`,
|
|
||||||
top: `${event.clientY - projectBounding.top}px`
|
|
||||||
};
|
|
||||||
this.topOperateVisible = true;
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
handleClickTopOperateOutside() {
|
|
||||||
this.topOperateVisible = false;
|
|
||||||
},
|
|
||||||
|
|
||||||
handleTopClick() {
|
|
||||||
this.$store.dispatch("call", {
|
|
||||||
url: 'project/top',
|
|
||||||
data: {
|
|
||||||
project_id: this.topOperateItem.id,
|
|
||||||
},
|
|
||||||
}).then(() => {
|
|
||||||
this.$store.dispatch("getProjects").catch(() => {});
|
|
||||||
}).catch(({msg}) => {
|
|
||||||
$A.modalError(msg, 301);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -46,6 +46,7 @@
|
|||||||
<div class="time" :title="msgData.created_at">{{$A.formatTime(msgData.created_at)}}</div>
|
<div class="time" :title="msgData.created_at">{{$A.formatTime(msgData.created_at)}}</div>
|
||||||
<Poptip
|
<Poptip
|
||||||
v-if="msgData.send > 1 || dialogType == 'group'"
|
v-if="msgData.send > 1 || dialogType == 'group'"
|
||||||
|
ref="percent"
|
||||||
class="percent"
|
class="percent"
|
||||||
placement="left-end"
|
placement="left-end"
|
||||||
transfer
|
transfer
|
||||||
@ -134,13 +135,13 @@ export default {
|
|||||||
}
|
}
|
||||||
this.msgData._r = true;
|
this.msgData._r = true;
|
||||||
//
|
//
|
||||||
this.$nextTick(() => {
|
setTimeout(() => {
|
||||||
if (!this.$el.offsetParent) {
|
if (!this.$el.offsetParent) {
|
||||||
this.msgData._r = false;
|
this.msgData._r = false;
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.$store.dispatch("dialogMsgRead", this.msgData);
|
this.$store.dispatch("dialogMsgRead", this.msgData);
|
||||||
})
|
}, 50)
|
||||||
},
|
},
|
||||||
|
|
||||||
popperShow() {
|
popperShow() {
|
||||||
@ -151,6 +152,7 @@ export default {
|
|||||||
},
|
},
|
||||||
}).then(({data}) => {
|
}).then(({data}) => {
|
||||||
this.read_list = data;
|
this.read_list = data;
|
||||||
|
this.$refs.percent.updatePopper();
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
this.read_list = [];
|
this.read_list = [];
|
||||||
});
|
});
|
||||||
|
@ -90,6 +90,7 @@
|
|||||||
<div class="drag-text">{{$L('拖动到这里发送')}}</div>
|
<div class="drag-text">{{$L('拖动到这里发送')}}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!--拖动发送提示-->
|
||||||
<Modal
|
<Modal
|
||||||
v-model="pasteShow"
|
v-model="pasteShow"
|
||||||
:title="$L(pasteTitle)"
|
:title="$L(pasteTitle)"
|
||||||
|
@ -623,10 +623,8 @@ export default {
|
|||||||
column.tasks = this.transforTasks(allTask.filter(task => {
|
column.tasks = this.transforTasks(allTask.filter(task => {
|
||||||
return task.column_id == column.id;
|
return task.column_id == column.id;
|
||||||
})).sort((a, b) => {
|
})).sort((a, b) => {
|
||||||
let at1 = $A.Date(a.complete_at),
|
if (a.complete_at || b.complete_at) {
|
||||||
at2 = $A.Date(b.complete_at);
|
return $A.Date(a.complete_at) - $A.Date(b.complete_at);
|
||||||
if (at1 || at2) {
|
|
||||||
return at1 - at2;
|
|
||||||
}
|
}
|
||||||
if (a.sort != b.sort) {
|
if (a.sort != b.sort) {
|
||||||
return a.sort - b.sort;
|
return a.sort - b.sort;
|
||||||
|
@ -238,7 +238,7 @@ export default {
|
|||||||
const {times} = this.addData;
|
const {times} = this.addData;
|
||||||
let temp = $A.date2string(times, "Y-m-d H:i");
|
let temp = $A.date2string(times, "Y-m-d H:i");
|
||||||
if (temp[0] && temp[1]) {
|
if (temp[0] && temp[1]) {
|
||||||
let d = Math.ceil(($A.Date(temp[1]).getTime() - $A.Date(temp[0]).getTime()) / 86400000);
|
let d = Math.ceil(($A.Date(temp[1], true) - $A.Date(temp[0], true)) / 86400);
|
||||||
if (d > 0) {
|
if (d > 0) {
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
<li v-if="ready && taskDetail.parent_id > 0">
|
<li v-if="ready && taskDetail.parent_id > 0">
|
||||||
<div class="subtask-icon">
|
<div class="subtask-icon">
|
||||||
<TaskMenu
|
<TaskMenu
|
||||||
|
v-if="taskId > 0"
|
||||||
:ref="`taskMenu_${taskDetail.id}`"
|
:ref="`taskMenu_${taskDetail.id}`"
|
||||||
:task="taskDetail"
|
:task="taskDetail"
|
||||||
:load-status="taskDetail.loading === true"
|
:load-status="taskDetail.loading === true"
|
||||||
@ -72,6 +73,7 @@
|
|||||||
<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
|
||||||
|
v-if="taskId > 0"
|
||||||
:ref="`taskMenu_${taskDetail.id}`"
|
:ref="`taskMenu_${taskDetail.id}`"
|
||||||
:task="taskDetail"
|
:task="taskDetail"
|
||||||
class="icon"
|
class="icon"
|
||||||
@ -121,6 +123,7 @@
|
|||||||
</ETooltip>
|
</ETooltip>
|
||||||
<div class="menu">
|
<div class="menu">
|
||||||
<TaskMenu
|
<TaskMenu
|
||||||
|
v-if="taskId > 0"
|
||||||
:task="taskDetail"
|
:task="taskDetail"
|
||||||
icon="ios-more"
|
icon="ios-more"
|
||||||
completed-icon="ios-more"
|
completed-icon="ios-more"
|
||||||
@ -150,7 +153,6 @@
|
|||||||
:option-full="taskOptionFull"
|
:option-full="taskOptionFull"
|
||||||
:placeholder="$L('详细描述...')"
|
:placeholder="$L('详细描述...')"
|
||||||
@on-blur="updateData('content')"
|
@on-blur="updateData('content')"
|
||||||
@editorSave="updateData('content')"
|
|
||||||
inline/>
|
inline/>
|
||||||
</div>
|
</div>
|
||||||
<Form class="items" label-position="left" label-width="auto" @submit.native.prevent>
|
<Form class="items" label-position="left" label-width="auto" @submit.native.prevent>
|
||||||
@ -261,7 +263,7 @@
|
|||||||
<div @click="openTime" class="time">{{taskDetail.end_at ? cutTime : '--'}}</div>
|
<div @click="openTime" class="time">{{taskDetail.end_at ? cutTime : '--'}}</div>
|
||||||
<template v-if="!taskDetail.complete_at && taskDetail.end_at">
|
<template v-if="!taskDetail.complete_at && taskDetail.end_at">
|
||||||
<Tag v-if="within24Hours(taskDetail.end_at)" color="blue"><i class="taskfont"></i>{{expiresFormat(taskDetail.end_at)}}</Tag>
|
<Tag v-if="within24Hours(taskDetail.end_at)" color="blue"><i class="taskfont"></i>{{expiresFormat(taskDetail.end_at)}}</Tag>
|
||||||
<Tag v-if="taskDetail.overdue" color="red">{{$L('超期未完成')}}</Tag>
|
<Tag v-if="isOverdue(taskDetail)" color="red">{{$L('超期未完成')}}</Tag>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</DatePicker>
|
</DatePicker>
|
||||||
@ -307,7 +309,13 @@
|
|||||||
<i class="taskfont"></i>{{$L('子任务')}}
|
<i class="taskfont"></i>{{$L('子任务')}}
|
||||||
</div>
|
</div>
|
||||||
<ul class="item-content subtask">
|
<ul class="item-content subtask">
|
||||||
<TaskDetail v-for="(task, key) in subList" :key="key" :task-id="task.id" :open-task="task" :main-end-at="taskDetail.end_at"/>
|
<TaskDetail
|
||||||
|
v-for="(task, key) in subList"
|
||||||
|
:ref="`subTask_${task.id}`"
|
||||||
|
:key="key"
|
||||||
|
:task-id="task.id"
|
||||||
|
:open-task="task"
|
||||||
|
:main-end-at="taskDetail.end_at"/>
|
||||||
</ul>
|
</ul>
|
||||||
<ul :class="['item-content', subList.length === 0 ? 'nosub' : '']">
|
<ul :class="['item-content', subList.length === 0 ? 'nosub' : '']">
|
||||||
<li>
|
<li>
|
||||||
@ -399,7 +407,10 @@
|
|||||||
@on-input-paste="msgPasteDrag"/>
|
@on-input-paste="msgPasteDrag"/>
|
||||||
<div class="no-send" @click="msgDialog">
|
<div class="no-send" @click="msgDialog">
|
||||||
<Loading v-if="sendLoad > 0"/>
|
<Loading v-if="sendLoad > 0"/>
|
||||||
<Icon v-else type="md-send" />
|
<template v-else>
|
||||||
|
<Badge :count="taskDetail.msg_num"/>
|
||||||
|
<Icon type="md-send" />
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="dialogDrag" class="drag-over" @click="dialogDrag=false">
|
<div v-if="dialogDrag" class="drag-over" @click="dialogDrag=false">
|
||||||
@ -613,8 +624,8 @@ export default {
|
|||||||
|
|
||||||
cutTime() {
|
cutTime() {
|
||||||
const {taskDetail} = this;
|
const {taskDetail} = this;
|
||||||
let start_at = Math.round($A.Date(taskDetail.start_at).getTime() / 1000);
|
let start_at = $A.Date(taskDetail.start_at, true);
|
||||||
let end_at = Math.round($A.Date(taskDetail.end_at).getTime() / 1000);
|
let end_at = $A.Date(taskDetail.end_at, true);
|
||||||
let string = "";
|
let string = "";
|
||||||
if ($A.formatDate('Y/m/d', start_at) == $A.formatDate('Y/m/d', end_at)) {
|
if ($A.formatDate('Y/m/d', start_at) == $A.formatDate('Y/m/d', end_at)) {
|
||||||
string = $A.formatDate('Y/m/d H:i', start_at) + " ~ " + $A.formatDate('H:i', end_at)
|
string = $A.formatDate('Y/m/d H:i', start_at) + " ~ " + $A.formatDate('H:i', end_at)
|
||||||
@ -722,29 +733,27 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
initLanguage() {
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
innerHeightListener() {
|
innerHeightListener() {
|
||||||
this.innerHeight = Math.min(1100, window.innerHeight);
|
this.innerHeight = Math.min(1100, window.innerHeight);
|
||||||
},
|
},
|
||||||
|
|
||||||
within24Hours(date) {
|
within24Hours(date) {
|
||||||
return Math.round($A.Date(date).getTime() / 1000) - this.nowTime < 86400
|
return $A.Date(date, true) - this.nowTime < 86400
|
||||||
},
|
},
|
||||||
|
|
||||||
expiresFormat(date) {
|
expiresFormat(date) {
|
||||||
return $A.countDownFormat(date, this.nowTime)
|
return $A.countDownFormat(date, this.nowTime)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
isOverdue(taskDetail) {
|
||||||
|
if (taskDetail.overdue) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return $A.Date(taskDetail.end_at, true) < this.nowTime;
|
||||||
|
},
|
||||||
|
|
||||||
onNameKeydown(e) {
|
onNameKeydown(e) {
|
||||||
if (e.keyCode === 83) {
|
if (e.keyCode === 13) {
|
||||||
if (e.metaKey || e.ctrlKey) {
|
|
||||||
e.preventDefault();
|
|
||||||
this.updateData('name');
|
|
||||||
}
|
|
||||||
} else if (e.keyCode === 13) {
|
|
||||||
if (!e.shiftKey) {
|
if (!e.shiftKey) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.updateData('name');
|
this.updateData('name');
|
||||||
@ -752,6 +761,40 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
checkUpdate(update) {
|
||||||
|
let isModify = false;
|
||||||
|
if (this.openTask.name != this.taskDetail.name) {
|
||||||
|
isModify = true;
|
||||||
|
if (update) {
|
||||||
|
this.updateData('name');
|
||||||
|
} else if (isModify) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.$refs.desc && this.$refs.desc.getContent() != this.taskContent) {
|
||||||
|
isModify = true;
|
||||||
|
if (update) {
|
||||||
|
this.updateData('content');
|
||||||
|
} else if (isModify) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.addsubShow && this.addsubName) {
|
||||||
|
isModify = true;
|
||||||
|
if (update) {
|
||||||
|
this.onAddsub();
|
||||||
|
} else if (isModify) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.subList.some(({id}) => {
|
||||||
|
if (this.$refs[`subTask_${id}`][0].checkUpdate(update)) {
|
||||||
|
isModify = true;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return isModify;
|
||||||
|
},
|
||||||
|
|
||||||
updateData(action, params) {
|
updateData(action, params) {
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case 'priority':
|
case 'priority':
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</slot>
|
</slot>
|
||||||
<EDropdownMenu slot="dropdown" class="task-menu-more-dropdown">
|
<EDropdownMenu ref="dropdownMenu" slot="dropdown" class="task-menu-more-dropdown">
|
||||||
<li class="task-menu-more-warp" :class="size">
|
<li class="task-menu-more-warp" :class="size">
|
||||||
<ul>
|
<ul>
|
||||||
<EDropdownItem v-if="!flow" class="load-flow" disabled>
|
<EDropdownItem v-if="!flow" class="load-flow" disabled>
|
||||||
@ -218,7 +218,9 @@ export default {
|
|||||||
|
|
||||||
visibleChange(visible) {
|
visibleChange(visible) {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
this.$store.dispatch("getTaskFlow", this.task.id).catch(() => {})
|
this.$store.dispatch("getTaskFlow", this.task.id)
|
||||||
|
.then(this.$refs.dropdownMenu.updatePopper)
|
||||||
|
.catch(this.$refs.dropdownMenu.updatePopper)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -266,7 +266,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
completeAtFormat(date) {
|
completeAtFormat(date) {
|
||||||
let time = Math.round($A.Date(date).getTime() / 1000);
|
let time = $A.Date(date, true);
|
||||||
if ($A.formatDate('Y') === $A.formatDate('Y', time)) {
|
if ($A.formatDate('Y') === $A.formatDate('Y', time)) {
|
||||||
return $A.formatDate('m-d H:i', time)
|
return $A.formatDate('m-d H:i', time)
|
||||||
} else {
|
} else {
|
||||||
|
@ -57,73 +57,82 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="tableMode" class="file-table" @contextmenu.prevent="handleRightClick">
|
<div
|
||||||
<Table
|
class="file-drag"
|
||||||
:columns="columns"
|
@drop.prevent="filePasteDrag($event, 'drag')"
|
||||||
:data="fileList"
|
@dragover.prevent="fileDragOver(true, $event)"
|
||||||
:height="tableHeight"
|
@dragleave.prevent="fileDragOver(false, $event)">
|
||||||
:no-data-text="$L('没有任何文件')"
|
<div v-if="tableMode" class="file-table" @contextmenu.prevent="handleRightClick">
|
||||||
@on-cell-click="clickRow"
|
<Table
|
||||||
@on-contextmenu="handleContextMenu"
|
:columns="columns"
|
||||||
@on-select="handleTableSelect"
|
:data="fileList"
|
||||||
@on-select-cancel="handleTableSelect"
|
:height="tableHeight"
|
||||||
@on-select-all-cancel="handleTableSelect"
|
:no-data-text="$L('没有任何文件')"
|
||||||
@on-select-all="handleTableSelect"
|
@on-cell-click="clickRow"
|
||||||
context-menu
|
@on-contextmenu="handleContextMenu"
|
||||||
stripe/>
|
@on-select="handleTableSelect"
|
||||||
|
@on-select-cancel="handleTableSelect"
|
||||||
|
@on-select-all-cancel="handleTableSelect"
|
||||||
|
@on-select-all="handleTableSelect"
|
||||||
|
context-menu
|
||||||
|
stripe/>
|
||||||
|
</div>
|
||||||
|
<template v-else>
|
||||||
|
<div v-if="fileList.length == 0 && loadIng == 0" class="file-no" @contextmenu.prevent="handleRightClick">
|
||||||
|
<i class="taskfont"></i>
|
||||||
|
<p>{{$L('没有任何文件')}}</p>
|
||||||
|
</div>
|
||||||
|
<div v-else class="file-list" @contextmenu.prevent="handleRightClick">
|
||||||
|
<ul class="clearfix">
|
||||||
|
<li
|
||||||
|
v-for="item in fileList"
|
||||||
|
:class="{
|
||||||
|
shear: shearIds.includes(item.id),
|
||||||
|
highlight: selectIds.includes(item.id),
|
||||||
|
}"
|
||||||
|
@contextmenu.prevent.stop="handleRightClick($event, item)"
|
||||||
|
@click="openFile(item)">
|
||||||
|
<div class="file-check" :class="{'file-checked':selectIds.includes(item.id)}" @click.stop="dropFile(item, 'select')">
|
||||||
|
<Checkbox :value="selectIds.includes(item.id)"/>
|
||||||
|
</div>
|
||||||
|
<div class="file-menu" @click.stop="handleRightClick($event, item)">
|
||||||
|
<Icon type="ios-more" />
|
||||||
|
</div>
|
||||||
|
<div :class="`no-dark-mode-before file-icon ${item.type}`">
|
||||||
|
<template v-if="item.share">
|
||||||
|
<UserAvatar v-if="item.userid != userId" :userid="item.userid" class="share-avatar" :size="20">
|
||||||
|
<p>{{$L('共享权限')}}: {{$L(item.permission == 1 ? '读/写' : '只读')}}</p>
|
||||||
|
</UserAvatar>
|
||||||
|
<div v-else class="share-icon no-dark-mode">
|
||||||
|
<i class="taskfont"></i>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="isParentShare">
|
||||||
|
<UserAvatar :userid="item.created_id" class="share-avatar" :size="20">
|
||||||
|
<p v-if="item.created_id != item.userid"><strong>{{$L('成员创建于')}}: {{item.created_at}}</strong></p>
|
||||||
|
<p v-else>{{$L('所有者创建于')}}: {{item.created_at}}</p>
|
||||||
|
</UserAvatar>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
<div v-if="item._edit" class="file-input">
|
||||||
|
<Input
|
||||||
|
:ref="'input_' + item.id"
|
||||||
|
v-model="item.newname"
|
||||||
|
size="small"
|
||||||
|
:disabled="!!item._load"
|
||||||
|
@on-blur="onBlur(item)"
|
||||||
|
@on-keyup="onKeyup($event, item)"/>
|
||||||
|
<div v-if="item._load" class="file-load"><Loading/></div>
|
||||||
|
</div>
|
||||||
|
<div v-else class="file-name" :title="item.name">{{formatName(item)}}</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div v-if="dialogDrag" class="drag-over" @click="dialogDrag=false">
|
||||||
|
<div class="drag-text">{{$L('拖动到这里发送')}}</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<template v-else>
|
|
||||||
<div v-if="fileList.length == 0 && loadIng == 0" class="file-no" @contextmenu.prevent="handleRightClick">
|
|
||||||
<i class="taskfont"></i>
|
|
||||||
<p>{{$L('没有任何文件')}}</p>
|
|
||||||
</div>
|
|
||||||
<div v-else class="file-list" @contextmenu.prevent="handleRightClick">
|
|
||||||
<ul class="clearfix">
|
|
||||||
<li
|
|
||||||
v-for="item in fileList"
|
|
||||||
:class="{
|
|
||||||
shear: shearIds.includes(item.id),
|
|
||||||
highlight: selectIds.includes(item.id),
|
|
||||||
}"
|
|
||||||
@contextmenu.prevent.stop="handleRightClick($event, item)"
|
|
||||||
@click="openFile(item)">
|
|
||||||
<div class="file-check" :class="{'file-checked':selectIds.includes(item.id)}" @click.stop="dropFile(item, 'select')">
|
|
||||||
<Checkbox :value="selectIds.includes(item.id)"/>
|
|
||||||
</div>
|
|
||||||
<div class="file-menu" @click.stop="handleRightClick($event, item)">
|
|
||||||
<Icon type="ios-more" />
|
|
||||||
</div>
|
|
||||||
<div :class="`no-dark-mode-before file-icon ${item.type}`">
|
|
||||||
<template v-if="item.share">
|
|
||||||
<UserAvatar v-if="item.userid != userId" :userid="item.userid" class="share-avatar" :size="20">
|
|
||||||
<p>{{$L('共享权限')}}: {{$L(item.permission == 1 ? '读/写' : '只读')}}</p>
|
|
||||||
</UserAvatar>
|
|
||||||
<div v-else class="share-icon no-dark-mode">
|
|
||||||
<i class="taskfont"></i>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template v-else-if="isParentShare">
|
|
||||||
<UserAvatar :userid="item.created_id" class="share-avatar" :size="20">
|
|
||||||
<p v-if="item.created_id != item.userid"><strong>{{$L('成员创建于')}}: {{item.created_at}}</strong></p>
|
|
||||||
<p v-else>{{$L('所有者创建于')}}: {{item.created_at}}</p>
|
|
||||||
</UserAvatar>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
<div v-if="item._edit" class="file-input">
|
|
||||||
<Input
|
|
||||||
:ref="'input_' + item.id"
|
|
||||||
v-model="item.newname"
|
|
||||||
size="small"
|
|
||||||
:disabled="!!item._load"
|
|
||||||
@on-blur="onBlur(item)"
|
|
||||||
@on-enter="onEnter(item)"/>
|
|
||||||
<div v-if="item._load" class="file-load"><Loading/></div>
|
|
||||||
</div>
|
|
||||||
<div v-else class="file-name" :title="item.name">{{formatName(item)}}</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<div class="file-menu" :style="contextMenuStyles">
|
<div class="file-menu" :style="contextMenuStyles">
|
||||||
<Dropdown
|
<Dropdown
|
||||||
@ -323,6 +332,21 @@
|
|||||||
<FileContent v-else v-model="fileShow" :file="fileInfo"/>
|
<FileContent v-else v-model="fileShow" :file="fileInfo"/>
|
||||||
</DrawerOverlay>
|
</DrawerOverlay>
|
||||||
|
|
||||||
|
<!--拖动上传提示-->
|
||||||
|
<Modal
|
||||||
|
v-model="pasteShow"
|
||||||
|
:title="$L(pasteTitle)"
|
||||||
|
:cancel-text="$L('取消')"
|
||||||
|
:ok-text="$L('发送')"
|
||||||
|
:enter-ok="true"
|
||||||
|
@on-ok="pasteSend">
|
||||||
|
<div class="dialog-wrapper-paste">
|
||||||
|
<template v-for="item in pasteItem">
|
||||||
|
<img v-if="item.type == 'image'" :src="item.result"/>
|
||||||
|
<div v-else>{{$L('文件')}}: {{item.name}} ({{$A.bytesToSize(item.size)}})</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -457,6 +481,11 @@ export default {
|
|||||||
|
|
||||||
shearIds: [],
|
shearIds: [],
|
||||||
selectIds: [],
|
selectIds: [],
|
||||||
|
|
||||||
|
dialogDrag: false,
|
||||||
|
pasteShow: false,
|
||||||
|
pasteFile: [],
|
||||||
|
pasteItem: [],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -537,6 +566,18 @@ export default {
|
|||||||
const {navigator} = this;
|
const {navigator} = this;
|
||||||
return !!navigator.find(({share}) => share);
|
return !!navigator.find(({share}) => share);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
pasteTitle() {
|
||||||
|
const {pasteItem} = this;
|
||||||
|
let hasImage = pasteItem.find(({type}) => type == 'image')
|
||||||
|
let hasFile = pasteItem.find(({type}) => type != 'image')
|
||||||
|
if (hasImage && hasFile) {
|
||||||
|
return '上传文件/图片'
|
||||||
|
} else if (hasImage) {
|
||||||
|
return '上传图片'
|
||||||
|
}
|
||||||
|
return '上传文件'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
@ -918,7 +959,6 @@ export default {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'rename':
|
case 'rename':
|
||||||
this.$set(item, 'newname', item.name);
|
|
||||||
this.setEdit(item.id, true)
|
this.setEdit(item.id, true)
|
||||||
this.autoBlur(item.id)
|
this.autoBlur(item.id)
|
||||||
break;
|
break;
|
||||||
@ -1113,9 +1153,21 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
onBlur(item) {
|
onBlur(item) {
|
||||||
|
if (this.files.find(({id, _edit}) => id == item.id && !_edit)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.onEnter(item);
|
this.onEnter(item);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onKeyup(e, item) {
|
||||||
|
if (e.keyCode === 13) {
|
||||||
|
this.onEnter(item);
|
||||||
|
} else if (e.keyCode === 27) {
|
||||||
|
this.setLoad(item.id, false)
|
||||||
|
this.setEdit(item.id, false)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
onEnter(item) {
|
onEnter(item) {
|
||||||
let isCreate = !/^\d+$/.test(item.id);
|
let isCreate = !/^\d+$/.test(item.id);
|
||||||
if (!item.newname) {
|
if (!item.newname) {
|
||||||
@ -1163,6 +1215,9 @@ export default {
|
|||||||
let item = this.$store.state.files.find(({id}) => id == fileId)
|
let item = this.$store.state.files.find(({id}) => id == fileId)
|
||||||
if (item) {
|
if (item) {
|
||||||
this.$set(item, '_edit', is);
|
this.$set(item, '_edit', is);
|
||||||
|
if (is) {
|
||||||
|
this.$set(item, 'newname', item.name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -1281,6 +1336,61 @@ export default {
|
|||||||
this.selectIds = [];
|
this.selectIds = [];
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/********************拖动上传部分************************/
|
||||||
|
|
||||||
|
pasteDragNext(e, type) {
|
||||||
|
let files = type === 'drag' ? e.dataTransfer.files : e.clipboardData.files;
|
||||||
|
files = Array.prototype.slice.call(files);
|
||||||
|
if (files.length > 0) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (files.length > 0) {
|
||||||
|
this.pasteFile = [];
|
||||||
|
this.pasteItem = [];
|
||||||
|
files.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
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
filePasteDrag(e, type) {
|
||||||
|
this.dialogDrag = false;
|
||||||
|
this.pasteDragNext(e, type);
|
||||||
|
},
|
||||||
|
|
||||||
|
fileDragOver(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;
|
||||||
|
}
|
||||||
|
this.dialogDrag = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
pasteSend() {
|
||||||
|
this.pasteFile.some(file => {
|
||||||
|
this.$refs.fileUpload.upload(file)
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
/********************文件上传部分************************/
|
/********************文件上传部分************************/
|
||||||
|
|
||||||
uploadUpdate(fileList) {
|
uploadUpdate(fileList) {
|
||||||
|
@ -120,7 +120,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
previewUrl() {
|
previewUrl() {
|
||||||
return $A.apiUrl("../fileview/onlinePreview?url=" + encodeURIComponent(this.msgDetail.url))
|
return $A.apiUrl("../fileview/onlinePreview?url=" + encodeURIComponent(this.msgDetail.content.url))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -120,7 +120,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
previewUrl() {
|
previewUrl() {
|
||||||
return $A.apiUrl("../fileview/onlinePreview?url=" + encodeURIComponent(this.fileDetail.url))
|
return $A.apiUrl("../fileview/onlinePreview?url=" + encodeURIComponent(this.fileDetail.content.url))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
27
resources/assets/js/store/actions.js
vendored
27
resources/assets/js/store/actions.js
vendored
@ -74,8 +74,12 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
params.error = () => {
|
params.error = (xhr, status) => {
|
||||||
reject({data: {}, msg: "System error"})
|
if (window.navigator.onLine === false || (status === 0 && xhr.readyState === 4)) {
|
||||||
|
reject({data: {}, msg: $A.L('网络异常,请稍后再试!')})
|
||||||
|
} else {
|
||||||
|
reject({data: {}, msg: "System error"})
|
||||||
|
}
|
||||||
};
|
};
|
||||||
//
|
//
|
||||||
if (params.websocket === true || params.ws === true) {
|
if (params.websocket === true || params.ws === true) {
|
||||||
@ -1628,6 +1632,7 @@ export default {
|
|||||||
task_id: task_id
|
task_id: task_id
|
||||||
},
|
},
|
||||||
}).then(result => {
|
}).then(result => {
|
||||||
|
let task = state.cacheTasks.find(({id}) => id == task_id)
|
||||||
let {data} = result
|
let {data} = result
|
||||||
data.turns.some(item => {
|
data.turns.some(item => {
|
||||||
let index = state.taskFlowItems.findIndex(({id}) => id == item.id);
|
let index = state.taskFlowItems.findIndex(({id}) => id == item.id);
|
||||||
@ -1636,6 +1641,16 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
state.taskFlowItems.push(item);
|
state.taskFlowItems.push(item);
|
||||||
}
|
}
|
||||||
|
if (task
|
||||||
|
&& task.flow_item_id == item.id
|
||||||
|
&& task.flow_item_name != item.name) {
|
||||||
|
state.cacheTasks.filter(({flow_item_id})=> flow_item_id == item.id).some(task => {
|
||||||
|
dispatch("saveTask", {
|
||||||
|
id: task.id,
|
||||||
|
flow_item_name: `${item.status}|${item.name}`,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
//
|
//
|
||||||
delete data.turns;
|
delete data.turns;
|
||||||
@ -2035,7 +2050,7 @@ export default {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
state.wsReadWaitList = [];
|
state.wsReadWaitList = [];
|
||||||
}, 20);
|
}, 50);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2163,6 +2178,12 @@ export default {
|
|||||||
// 更新最后消息
|
// 更新最后消息
|
||||||
dispatch("updateDialogLastMsg", data);
|
dispatch("updateDialogLastMsg", data);
|
||||||
break;
|
break;
|
||||||
|
case 'readed':
|
||||||
|
// 已读回执
|
||||||
|
if (state.dialogMsgs.find(({id}) => id == data.id)) {
|
||||||
|
dispatch("saveDialogMsg", data)
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
})(msgDetail);
|
})(msgDetail);
|
||||||
break;
|
break;
|
||||||
|
@ -44,7 +44,7 @@
|
|||||||
max-height: 210px;
|
max-height: 210px;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
margin: 18px 0;
|
margin-bottom: 16px;
|
||||||
.markdown-preview {
|
.markdown-preview {
|
||||||
margin: -20px -12px;
|
margin: -20px -12px;
|
||||||
h2 {
|
h2 {
|
||||||
|
@ -476,7 +476,7 @@
|
|||||||
.dialog-send {
|
.dialog-send {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
right: 28px;
|
right: 14px;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
width: 46px;
|
width: 46px;
|
||||||
@ -604,10 +604,6 @@
|
|||||||
.dialog-footer {
|
.dialog-footer {
|
||||||
padding: 0 20px;
|
padding: 0 20px;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
|
|
||||||
.dialog-send {
|
|
||||||
right: 20px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -478,9 +478,9 @@
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 10px 0 0 0;
|
margin: 10px 0 0 0;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
word-break: break-all;
|
text-overflow: ellipsis;
|
||||||
white-space: pre-wrap;
|
white-space: nowrap;
|
||||||
word-wrap: break-word;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.task-tags {
|
.task-tags {
|
||||||
|
@ -77,6 +77,13 @@
|
|||||||
img {
|
img {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
pre {
|
||||||
|
padding: 14px;
|
||||||
|
margin: 7px 0;
|
||||||
|
overflow: auto;
|
||||||
|
background: #f5f2f0;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
&[data-mce-placeholder]:not(.mce-visualblocks)::before {
|
&[data-mce-placeholder]:not(.mce-visualblocks)::before {
|
||||||
color: #bbbbbb;
|
color: #bbbbbb;
|
||||||
}
|
}
|
||||||
|
@ -163,6 +163,13 @@
|
|||||||
img {
|
img {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
pre {
|
||||||
|
padding: 14px;
|
||||||
|
margin: 7px 0;
|
||||||
|
overflow: auto;
|
||||||
|
background: #f5f2f0;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
&[data-mce-placeholder]:not(.mce-visualblocks)::before {
|
&[data-mce-placeholder]:not(.mce-visualblocks)::before {
|
||||||
color: #bbbbbb;
|
color: #bbbbbb;
|
||||||
}
|
}
|
||||||
@ -433,6 +440,7 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
margin-top: 6px;
|
margin-top: 6px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
|
white-space: nowrap;
|
||||||
> i {
|
> i {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
padding-right: 8px;
|
padding-right: 8px;
|
||||||
@ -554,6 +562,12 @@
|
|||||||
}
|
}
|
||||||
.no-send {
|
.no-send {
|
||||||
display: none;
|
display: none;
|
||||||
|
.ivu-badge {
|
||||||
|
position: absolute;
|
||||||
|
transform: scale(0.6);
|
||||||
|
top: 5px;
|
||||||
|
left: 4px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.drag-over {
|
.drag-over {
|
||||||
|
@ -108,8 +108,8 @@
|
|||||||
padding: 8px;
|
padding: 8px;
|
||||||
|
|
||||||
.load-flow-warp {
|
.load-flow-warp {
|
||||||
width: 20px;
|
width: 18px;
|
||||||
height: 20px;
|
height: 18px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,8 +51,6 @@
|
|||||||
&:last-child {
|
&:last-child {
|
||||||
background-color: #98de6e;
|
background-color: #98de6e;
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
cursor: default;
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
}
|
||||||
.block-title {
|
.block-title {
|
||||||
color: rgba(255, 255, 255, 0.6);
|
color: rgba(255, 255, 255, 0.6);
|
||||||
|
437
resources/assets/sass/pages/page-file.scss
vendored
437
resources/assets/sass/pages/page-file.scss
vendored
@ -90,24 +90,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.file-no {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
flex-direction: column;
|
|
||||||
margin-bottom: 5%;
|
|
||||||
opacity: 0.8;
|
|
||||||
> i {
|
|
||||||
font-size: 64px;
|
|
||||||
}
|
|
||||||
> p {
|
|
||||||
margin-top: 18px;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.file-navigator {
|
.file-navigator {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -254,192 +236,245 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.file-table {
|
.file-drag {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
cursor: default;
|
height: 0;
|
||||||
margin: 16px 32px 32px;
|
display: flex;
|
||||||
.ivu-table {
|
flex-direction: column;
|
||||||
&:before {
|
position: relative;
|
||||||
display: none;
|
.file-no {
|
||||||
}
|
flex: 1;
|
||||||
.ivu-table-tip {
|
|
||||||
opacity: 0.8;
|
|
||||||
span {
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
line-height: 1.8;
|
|
||||||
&:before {
|
|
||||||
display: block;
|
|
||||||
content: "\e60b";
|
|
||||||
font-family: "taskfont", "serif" !important;
|
|
||||||
font-size: 64px;
|
|
||||||
font-style: normal;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
-webkit-text-stroke-width: 0.2px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.file-nbox {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
position: relative;
|
justify-content: center;
|
||||||
&.shear {
|
flex-direction: column;
|
||||||
opacity: 0.38;
|
margin-bottom: 5%;
|
||||||
|
opacity: 0.8;
|
||||||
|
> i {
|
||||||
|
font-size: 64px;
|
||||||
}
|
}
|
||||||
.file-name {
|
> p {
|
||||||
flex: 1;
|
margin-top: 18px;
|
||||||
width: 0;
|
font-size: 14px;
|
||||||
display: flex;
|
font-weight: 500;
|
||||||
align-items: center;
|
line-height: 1;
|
||||||
position: relative;
|
|
||||||
margin-right: 46px;
|
|
||||||
&:before {
|
|
||||||
flex-shrink: 0;
|
|
||||||
content: "";
|
|
||||||
width: 22px;
|
|
||||||
height: 22px;
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.permission {
|
|
||||||
padding-left: 6px;
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
.taskfont {
|
|
||||||
color: #aaaaaa;
|
|
||||||
font-size: 16px;
|
|
||||||
margin: 0 3px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
.file-table {
|
||||||
.file-list {
|
flex: 1;
|
||||||
flex: 1;
|
cursor: default;
|
||||||
padding: 0 20px 20px;
|
margin: 16px 32px 32px;
|
||||||
margin-top: 16px;
|
.ivu-table {
|
||||||
overflow: auto;
|
&:before {
|
||||||
> ul {
|
display: none;
|
||||||
margin-top: -12px;
|
}
|
||||||
> li {
|
.ivu-table-tip {
|
||||||
list-style: none;
|
opacity: 0.8;
|
||||||
float: left;
|
span {
|
||||||
margin: 12px;
|
font-size: 14px;
|
||||||
width: 100px;
|
font-weight: 500;
|
||||||
height: 110px;
|
line-height: 1.8;
|
||||||
position: relative;
|
&:before {
|
||||||
border-radius: 5px;
|
display: block;
|
||||||
|
content: "\e60b";
|
||||||
|
font-family: "taskfont", "serif" !important;
|
||||||
|
font-size: 64px;
|
||||||
|
font-style: normal;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-webkit-text-stroke-width: 0.2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.file-nbox {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
position: relative;
|
||||||
cursor: pointer;
|
|
||||||
.file-input {
|
|
||||||
margin: 0 4px 4px;
|
|
||||||
position: relative;
|
|
||||||
input {
|
|
||||||
margin: 0;
|
|
||||||
padding: 1px 5px;
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
.file-load {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
right: 6px;
|
|
||||||
bottom: 0;
|
|
||||||
display: flex;
|
|
||||||
.common-loading {
|
|
||||||
width: 10px;
|
|
||||||
height: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.file-name {
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
height: 20px;
|
|
||||||
line-height: 20px;
|
|
||||||
color: $primary-text-color;
|
|
||||||
font-size: 12px;
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
padding: 0 6px;
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
.file-check {
|
|
||||||
opacity: 0;
|
|
||||||
position: absolute;
|
|
||||||
top: 1px;
|
|
||||||
left: 4px;
|
|
||||||
transition: opacity 0.2s;
|
|
||||||
&.file-checked {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.file-menu {
|
|
||||||
opacity: 0;
|
|
||||||
position: absolute;
|
|
||||||
top: 2px;
|
|
||||||
right: 2px;
|
|
||||||
transition: opacity 0.2s;
|
|
||||||
display: flex;
|
|
||||||
.ivu-icon {
|
|
||||||
font-size: 16px;
|
|
||||||
color: #aaaaaa;
|
|
||||||
transition: color 0.2s;
|
|
||||||
padding: 2px 5px;
|
|
||||||
&:hover {
|
|
||||||
color: $primary-text-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.file-icon {
|
|
||||||
display: inline-block;
|
|
||||||
width: 64px;
|
|
||||||
height: 64px;
|
|
||||||
margin-top: 12px;
|
|
||||||
position: relative;
|
|
||||||
&:before {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
}
|
|
||||||
.share-icon,
|
|
||||||
.share-avatar {
|
|
||||||
position: absolute;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
background-color: #9ACD7B;
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
border-radius: 50%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
transform: scale(0.9);
|
|
||||||
.taskfont {
|
|
||||||
font-size: 18px;
|
|
||||||
color: #ffffff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
&.shear {
|
&.shear {
|
||||||
opacity: 0.38;
|
opacity: 0.38;
|
||||||
}
|
}
|
||||||
&.highlight {
|
.file-name {
|
||||||
background-color: #f4f5f7;
|
flex: 1;
|
||||||
}
|
width: 0;
|
||||||
&:hover {
|
display: flex;
|
||||||
background-color: #f4f5f7;
|
align-items: center;
|
||||||
.file-menu,
|
position: relative;
|
||||||
.file-check {
|
margin-right: 46px;
|
||||||
opacity: 1;
|
&:before {
|
||||||
|
flex-shrink: 0;
|
||||||
|
content: "";
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.permission {
|
||||||
|
padding-left: 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
.taskfont {
|
||||||
|
color: #aaaaaa;
|
||||||
|
font-size: 16px;
|
||||||
|
margin: 0 3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.file-list {
|
||||||
|
flex: 1;
|
||||||
|
padding: 0 20px 20px;
|
||||||
|
margin-top: 16px;
|
||||||
|
overflow: auto;
|
||||||
|
> ul {
|
||||||
|
margin-top: -12px;
|
||||||
|
> li {
|
||||||
|
list-style: none;
|
||||||
|
float: left;
|
||||||
|
margin: 12px;
|
||||||
|
width: 100px;
|
||||||
|
height: 110px;
|
||||||
|
position: relative;
|
||||||
|
border-radius: 5px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
cursor: pointer;
|
||||||
|
.file-input {
|
||||||
|
margin: 0 4px 4px;
|
||||||
|
position: relative;
|
||||||
|
input {
|
||||||
|
margin: 0;
|
||||||
|
padding: 1px 5px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
.file-load {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 6px;
|
||||||
|
bottom: 0;
|
||||||
|
display: flex;
|
||||||
|
.common-loading {
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.file-name {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 20px;
|
||||||
|
line-height: 20px;
|
||||||
|
color: $primary-text-color;
|
||||||
|
font-size: 12px;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
padding: 0 6px;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
.file-check {
|
||||||
|
opacity: 0;
|
||||||
|
position: absolute;
|
||||||
|
top: 1px;
|
||||||
|
left: 4px;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
&.file-checked {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.file-menu {
|
||||||
|
opacity: 0;
|
||||||
|
position: absolute;
|
||||||
|
top: 2px;
|
||||||
|
right: 2px;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
display: flex;
|
||||||
|
.ivu-icon {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #aaaaaa;
|
||||||
|
transition: color 0.2s;
|
||||||
|
padding: 2px 5px;
|
||||||
|
&:hover {
|
||||||
|
color: $primary-text-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.file-icon {
|
||||||
|
display: inline-block;
|
||||||
|
width: 64px;
|
||||||
|
height: 64px;
|
||||||
|
margin-top: 12px;
|
||||||
|
position: relative;
|
||||||
|
&:before {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
.share-icon,
|
||||||
|
.share-avatar {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: #9ACD7B;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transform: scale(0.9);
|
||||||
|
.taskfont {
|
||||||
|
font-size: 18px;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
&.shear {
|
||||||
|
opacity: 0.38;
|
||||||
|
}
|
||||||
|
&.highlight {
|
||||||
|
background-color: #f4f5f7;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
background-color: #f4f5f7;
|
||||||
|
.file-menu,
|
||||||
|
.file-check {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.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;
|
||||||
|
margin: 16px 32px 32px;
|
||||||
|
&:before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
border: 2px dashed #7b7b7b;
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
.drag-text {
|
||||||
|
padding: 12px;
|
||||||
|
font-size: 18px;
|
||||||
|
color: #666666;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -450,7 +485,7 @@
|
|||||||
.file-upload-list {
|
.file-upload-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 380px;
|
width: 380px;
|
||||||
padding: 14px 26px 14px 13px;
|
padding: 14px 26px 14px 26px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
border: 1px solid #ebeef5;
|
border: 1px solid #ebeef5;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@ -462,8 +497,7 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
.upload-wrap {
|
.upload-wrap {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
margin-left: 13px;
|
width: 100%;
|
||||||
margin-right: 8px;
|
|
||||||
.title {
|
.title {
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
@ -481,16 +515,18 @@
|
|||||||
.content {
|
.content {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
line-height: 21px;
|
line-height: 21px;
|
||||||
margin: 12px -16px 0 0;
|
margin: 12px 0 0;
|
||||||
color: #606266;
|
color: #606266;
|
||||||
max-height: 500px;
|
max-height: 500px;
|
||||||
|
max-width: 100%;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
> li {
|
> li {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
padding: 4px 16px 4px 0;
|
padding: 4px 0;
|
||||||
position: relative;
|
position: relative;
|
||||||
.file-name {
|
.file-name {
|
||||||
line-height: 18px;
|
line-height: 18px;
|
||||||
|
padding-right: 16px;
|
||||||
}
|
}
|
||||||
.file-error {
|
.file-error {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
@ -498,8 +534,9 @@
|
|||||||
}
|
}
|
||||||
.file-close {
|
.file-close {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
font-size: 14px;
|
||||||
top: 7px;
|
top: 7px;
|
||||||
right: 0;
|
right: -1px;
|
||||||
display: none;
|
display: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
@ -620,8 +657,10 @@
|
|||||||
.file-navigator {
|
.file-navigator {
|
||||||
margin: 0 24px 0;
|
margin: 0 24px 0;
|
||||||
}
|
}
|
||||||
.file-table {
|
.file-drag {
|
||||||
margin: 16px 24px 24px;
|
.file-table {
|
||||||
|
margin: 16px 24px 24px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2
resources/assets/sass/pages/page-manage.scss
vendored
2
resources/assets/sass/pages/page-manage.scss
vendored
@ -87,7 +87,7 @@
|
|||||||
transform: scale(0.9);
|
transform: scale(0.9);
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
.manage-menu-language {
|
.manage-menu-flex {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user