Merge branch 'develop' of ssh://git.gezi.vip:6006/gx/dootask
# Conflicts: # electron/package.json # package.json # public/js/app.js
This commit is contained in:
commit
35b1c12bb5
@ -1747,4 +1747,31 @@ class ProjectController extends AbstractController
|
||||
//
|
||||
return Base::retSuccess('success', $list);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/project/top 37. 项目置顶
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup project
|
||||
* @apiName top
|
||||
*
|
||||
* @apiParam {Number} project_id 项目ID
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function top()
|
||||
{
|
||||
$user = User::auth();
|
||||
$projectId = intval(Request::input('project_id'));
|
||||
$projectUser = ProjectUser::whereUserid($user->userid)->whereProjectId($projectId)->first();
|
||||
if (!$projectUser) {
|
||||
return Base::retError("项目不存在");
|
||||
}
|
||||
$projectUser->top_at = $projectUser->top_at ? null : Carbon::now();
|
||||
$projectUser->save();
|
||||
return Base::retSuccess("success", $projectId);
|
||||
}
|
||||
}
|
||||
|
@ -113,6 +113,7 @@ class Project extends AbstractModel
|
||||
->select([
|
||||
'projects.*',
|
||||
'project_users.owner',
|
||||
'project_users.top_at',
|
||||
])
|
||||
->leftJoin('project_users', function ($leftJoin) use ($userid) {
|
||||
$leftJoin
|
||||
@ -136,6 +137,7 @@ class Project extends AbstractModel
|
||||
->select([
|
||||
'projects.*',
|
||||
'project_users.owner',
|
||||
'project_users.top_at',
|
||||
])
|
||||
->join('project_users', 'projects.id', '=', 'project_users.project_id')
|
||||
->where('project_users.userid', $userid);
|
||||
|
@ -11,6 +11,7 @@ use App\Module\Base;
|
||||
* @property int|null $project_id 项目ID
|
||||
* @property int|null $userid 成员ID
|
||||
* @property int|null $owner 是否负责人
|
||||
* @property \Illuminate\Support\Carbon|null $top_at 置顶时间
|
||||
* @property \Illuminate\Support\Carbon|null $created_at
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
* @property-read \App\Models\Project|null $project
|
||||
|
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class ProjectUsersAddTopAt extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('project_users', function (Blueprint $table) {
|
||||
if (!Schema::hasColumn('project_users', 'top_at')) {
|
||||
$table->timestamp('top_at')->nullable()->after('owner')->comment('置顶时间');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('project_users', function (Blueprint $table) {
|
||||
$table->dropColumn("top_at");
|
||||
});
|
||||
}
|
||||
}
|
2
public/css/app.css
vendored
2
public/css/app.css
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
public/js/language.all.js
vendored
1
public/js/language.all.js
vendored
@ -447,5 +447,6 @@
|
||||
{"CN": "取消置顶","EN": "Cancel","TC": "取消置頂","KM": "លប់ចោល","TH": "ยกเลิก","KO": "취소","JA": "キャンセル"},
|
||||
{"CN": "置顶该聊天","EN": "Top the chat","TC": "置頂該聊天","KM": "បញ្ចូលការជជែក","TH": "ด้านบนการแชท","KO": "채팅 상단","JA": "トップチャット"},
|
||||
{"CN": "请输入正确的邀请码","EN": "Please enter the correct invitation code","TC": "請輸入正確的邀請碼","KM": "សូមបញ្ចូលលេខកូដអញ្ជើញត្រឹមត្រូវ","TH": "โปรดป้อนรหัสเชิญที่ถูกต้อง","KO": "올바른 초대 코드를 입력하십시오","JA": "正しい招待状コードを入力してください"},
|
||||
{"CN": "置顶","EN": "Topping","TC": "置頂","KM": "ការបញ្ចូល","TH": "โรยหน้า","KO": "토핑","JA": "トッピング"},
|
||||
]
|
||||
})(window)
|
||||
|
@ -54,7 +54,7 @@
|
||||
<DropdownItem divided name="signout" style="color:#f40">{{$L('退出登录')}}</DropdownItem>
|
||||
</DropdownMenu>
|
||||
</Dropdown>
|
||||
<ul class="overlay-y">
|
||||
<ul class="overlay-y" @scroll="listScroll()">
|
||||
<li @click="toggleRoute('dashboard')" :class="classNameRoute('dashboard')">
|
||||
<i class="taskfont"></i>
|
||||
<div class="menu-title">{{$L('仪表盘')}}</div>
|
||||
@ -73,13 +73,20 @@
|
||||
<i class="taskfont"></i>
|
||||
<div class="menu-title">{{$L('文件')}}</div>
|
||||
</li>
|
||||
<li class="menu-project">
|
||||
<li class="menu-project" ref="projectWrapper">
|
||||
<ul>
|
||||
<li
|
||||
v-for="(item, key) in projectLists"
|
||||
:key="key"
|
||||
:class="classNameRoute('project/' + item.id, openMenu[item.id])"
|
||||
@click="toggleRoute('project/' + item.id)">
|
||||
@click="toggleRoute('project/' + item.id)"
|
||||
@contextmenu.prevent.stop="handleRightClick($event, item)"
|
||||
>
|
||||
<div
|
||||
:class="{
|
||||
top: item.top_at,
|
||||
operate: item.id == topOperateItem.id && topOperateVisible
|
||||
}">
|
||||
<div class="project-h1">
|
||||
<em @click.stop="toggleOpenMenu(item.id)"></em>
|
||||
<div class="title">{{item.name}}</div>
|
||||
@ -97,9 +104,24 @@
|
||||
<Progress :percent="item.task_percent" :stroke-width="6" />
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<Loading v-if="loadIng > 0"/>
|
||||
<div class="top-operate" :style="topOperateStyles">
|
||||
<Dropdown
|
||||
trigger="custom"
|
||||
:visible="topOperateVisible"
|
||||
transfer-class-name="page-file-dropdown-menu"
|
||||
@on-clickoutside="handleClickTopOperateOutside"
|
||||
transfer>
|
||||
<DropdownMenu slot="list">
|
||||
<DropdownItem @click.native="handleTopClick">
|
||||
{{ $L(topOperateItem.top_at ? '取消置顶' : '置顶该项目') }}
|
||||
</DropdownItem>
|
||||
</DropdownMenu>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<div
|
||||
@ -287,6 +309,9 @@ export default {
|
||||
|
||||
reportTabs: "my",
|
||||
reportUnreadNumber: 0,
|
||||
topOperateStyles: {},
|
||||
topOperateVisible: false,
|
||||
topOperateItem: {},
|
||||
}
|
||||
},
|
||||
|
||||
@ -411,6 +436,9 @@ export default {
|
||||
projectLists() {
|
||||
const {projectKeyValue, cacheProjects} = this;
|
||||
const data = cacheProjects.sort((a, b) => {
|
||||
if (a.top_at || b.top_at) {
|
||||
return $A.Date(b.top_at) - $A.Date(a.top_at);
|
||||
}
|
||||
return b.id - a.id;
|
||||
});
|
||||
if (projectKeyValue) {
|
||||
@ -782,6 +810,40 @@ export default {
|
||||
}
|
||||
document.addEventListener(visibilityChangeEvent, visibilityChangeListener);
|
||||
},
|
||||
handleRightClick(event, item) {
|
||||
this.handleClickTopOperateOutside();
|
||||
this.topOperateItem = $A.isJson(item) ? 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(() => {});
|
||||
this.$Modal.remove();
|
||||
}).catch(({msg}) => {
|
||||
$A.modalError(msg, 301);
|
||||
this.$Modal.remove();
|
||||
});
|
||||
},
|
||||
listScroll() {
|
||||
this.topOperateVisible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -5,6 +5,7 @@
|
||||
<div class="project-titbox">
|
||||
<div class="project-title">
|
||||
<h1>{{projectData.name}}</h1>
|
||||
<label v-if="projectData.top_at" class="top-text">{{$L('置顶')}}</label>
|
||||
<div v-if="projectLoad > 0" class="project-load"><Loading/></div>
|
||||
</div>
|
||||
<ul class="project-icons">
|
||||
|
@ -19,10 +19,10 @@
|
||||
<i class="taskfont"></i>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="block-title">{{$L('参与的项目')}}</div>
|
||||
<li @click="dashboard='all'">
|
||||
<div class="block-title">{{$L('待完成任务')}}</div>
|
||||
<div class="block-data">
|
||||
<div class="block-num">{{cacheProjects.length}}</div>
|
||||
<div class="block-num">{{dashboardTask.all.length}}</div>
|
||||
<i class="taskfont"></i>
|
||||
</div>
|
||||
</li>
|
||||
@ -62,7 +62,7 @@
|
||||
<i class="taskfont"></i>
|
||||
<em>{{item.sub_complete}}/{{item.sub_num}}</em>
|
||||
</div>
|
||||
<ETooltip :content="item.end_at" placement="right">
|
||||
<ETooltip v-if="item.end_at" :content="item.end_at" placement="right">
|
||||
<div :class="['item-icon', item.today ? 'today' : '', item.overdue ? 'overdue' : '']">
|
||||
<i class="taskfont"></i>
|
||||
<em>{{expiresFormat(item.end_at)}}</em>
|
||||
@ -121,6 +121,8 @@ export default {
|
||||
return this.$L('今日任务');
|
||||
case 'overdue':
|
||||
return this.$L('超期任务');
|
||||
case 'all':
|
||||
return this.$L('待完成任务');
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
@ -136,9 +138,12 @@ export default {
|
||||
case 'overdue':
|
||||
data = this.transforTasks(this.dashboardTask.overdue);
|
||||
break
|
||||
case 'all':
|
||||
data = this.transforTasks(this.dashboardTask.all);
|
||||
break
|
||||
}
|
||||
return data.sort((a, b) => {
|
||||
return $A.Date(a.end_at) - $A.Date(b.end_at);
|
||||
return $A.Date(a.end_at || "2099-12-31 23:59:59") - $A.Date(b.end_at || "2099-12-31 23:59:59");
|
||||
});
|
||||
},
|
||||
},
|
||||
|
17
resources/assets/js/store/actions.js
vendored
17
resources/assets/js/store/actions.js
vendored
@ -1116,23 +1116,25 @@ export default {
|
||||
state.cacheLoading["loadDashboardTasks"] = true;
|
||||
//
|
||||
const time = $A.Time()
|
||||
const {today, overdue} = getters.dashboardTask;
|
||||
const {today, overdue,all} = getters.dashboardTask;
|
||||
const currentIds = today.map(({id}) => id)
|
||||
currentIds.push(...overdue.map(({id}) => id))
|
||||
currentIds.push(...all.map(({id}) => id))
|
||||
//
|
||||
let loadIng = 2;
|
||||
let loadIng = 3;
|
||||
let call = () => {
|
||||
if (loadIng <= 0) {
|
||||
state.cacheLoading["loadDashboardTasks"] = false;
|
||||
//
|
||||
const {today, overdue} = getters.dashboardTask;
|
||||
const {today, overdue,all} = getters.dashboardTask;
|
||||
const newIds = today.filter(task => task._time >= time).map(({id}) => id)
|
||||
newIds.push(...overdue.filter(task => task._time >= time).map(({id}) => id))
|
||||
newIds.push(...all.filter(task => task._time >= time).map(({id}) => id))
|
||||
dispatch("forgetTask", currentIds.filter(v => newIds.indexOf(v) == -1))
|
||||
return;
|
||||
}
|
||||
loadIng--;
|
||||
if (loadIng == 1) {
|
||||
if (loadIng == 2) {
|
||||
// 获取今日任务
|
||||
dispatch("getTasks", {
|
||||
complete: "no",
|
||||
@ -1141,12 +1143,17 @@ export default {
|
||||
$A.formatDate("Y-m-d 23:59:59")
|
||||
],
|
||||
}).then(call).catch(call)
|
||||
} else if (loadIng == 0) {
|
||||
} else if (loadIng == 1) {
|
||||
// 获取过期任务
|
||||
dispatch("getTasks", {
|
||||
complete: "no",
|
||||
time_before: $A.formatDate("Y-m-d H:i:s"),
|
||||
}).then(call).catch(call)
|
||||
} else if((loadIng == 0)) {
|
||||
// 获取待处理任务
|
||||
dispatch("getTasks", {
|
||||
complete: "no",
|
||||
}).then(call).catch(call)
|
||||
}
|
||||
}
|
||||
call();
|
||||
|
9
resources/assets/js/store/getters.js
vendored
9
resources/assets/js/store/getters.js
vendored
@ -112,7 +112,7 @@ export default {
|
||||
/**
|
||||
* 仪表盘任务数据
|
||||
* @param state
|
||||
* @returns {{overdue: *, today: *}}
|
||||
* @returns {{overdue: *, today: *,all:*}}
|
||||
*/
|
||||
dashboardTask(state) {
|
||||
const todayStart = $A.Date($A.formatDate("Y-m-d 00:00:00")),
|
||||
@ -125,9 +125,6 @@ export default {
|
||||
if (task.complete_at && chackCompleted === true) {
|
||||
return false;
|
||||
}
|
||||
if (!task.end_at) {
|
||||
return false;
|
||||
}
|
||||
return task.owner;
|
||||
}
|
||||
let array = state.cacheTasks.filter(task => filterTask(task));
|
||||
@ -142,11 +139,13 @@ export default {
|
||||
return (start <= todayStart && todayStart <= end) || (start <= todayEnd && todayEnd <= end) || (start > todayStart && todayEnd > end);
|
||||
})
|
||||
const overdueTasks = array.filter(task => {
|
||||
return $A.Date(task.end_at) <= todayNow;
|
||||
return task.end_at && $A.Date(task.end_at) <= todayNow;
|
||||
})
|
||||
|
||||
return {
|
||||
today: todayTasks,
|
||||
overdue: overdueTasks,
|
||||
all: array
|
||||
}
|
||||
},
|
||||
}
|
||||
|
@ -29,6 +29,17 @@
|
||||
height: 22px;
|
||||
}
|
||||
}
|
||||
.top-text{
|
||||
width: 40px;
|
||||
height: 24px;
|
||||
border-radius:4px;
|
||||
margin-left: 10px;
|
||||
background-color: #8BCF70;
|
||||
color:#FFFFFF;
|
||||
text-align: center;
|
||||
margin-top: 4px;
|
||||
padding-top: 2px
|
||||
}
|
||||
}
|
||||
.project-icons {
|
||||
display: flex;
|
||||
|
13
resources/assets/sass/pages/page-manage.scss
vendored
13
resources/assets/sass/pages/page-manage.scss
vendored
@ -226,6 +226,12 @@
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
.top {
|
||||
background-color: #EEEFF1;
|
||||
}
|
||||
.operate {
|
||||
border:1px solid $primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
.common-loading {
|
||||
@ -233,6 +239,13 @@
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
}
|
||||
.top-operate {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
opacity: 0;
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
&.active {
|
||||
background-color: #ffffff;
|
||||
|
@ -78,6 +78,7 @@
|
||||
width: 100%;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
padding-right: 9px;
|
||||
> ul {
|
||||
&.dialog {
|
||||
> li {
|
||||
@ -192,7 +193,7 @@
|
||||
transform: scale(0.8);
|
||||
}
|
||||
&.top {
|
||||
background-color: #f1f6fb;
|
||||
background-color: #EEEFF1;
|
||||
}
|
||||
&.active {
|
||||
background-color: #F4F5F7;
|
||||
|
@ -447,5 +447,6 @@
|
||||
{"CN": "取消置顶","EN": "Cancel","TC": "取消置頂","KM": "លប់ចោល","TH": "ยกเลิก","KO": "취소","JA": "キャンセル"},
|
||||
{"CN": "置顶该聊天","EN": "Top the chat","TC": "置頂該聊天","KM": "បញ្ចូលការជជែក","TH": "ด้านบนการแชท","KO": "채팅 상단","JA": "トップチャット"},
|
||||
{"CN": "请输入正确的邀请码","EN": "Please enter the correct invitation code","TC": "請輸入正確的邀請碼","KM": "សូមបញ្ចូលលេខកូដអញ្ជើញត្រឹមត្រូវ","TH": "โปรดป้อนรหัสเชิญที่ถูกต้อง","KO": "올바른 초대 코드를 입력하십시오","JA": "正しい招待状コードを入力してください"},
|
||||
{"CN": "置顶","EN": "Topping","TC": "置頂","KM": "ការបញ្ចូល","TH": "โรยหน้า","KO": "토핑","JA": "トッピング"},
|
||||
]
|
||||
})(window)
|
||||
|
Loading…
x
Reference in New Issue
Block a user