perf: 添加任务窗口

This commit is contained in:
kuaifan 2021-12-24 23:49:09 +08:00
parent 7171d1d6b2
commit e65c80962d
7 changed files with 255 additions and 94 deletions

View File

@ -32,6 +32,9 @@ class ProjectController extends AbstractController
* - all全部 * - all全部
* - no未归档默认 * - no未归档默认
* - yes已归档 * - yes已归档
* @apiParam {String} [andcolumn] 同时取项目列表
* - no不取默认
* - yes取列表
* @apiParam {Object} [keys] 搜索条件 * @apiParam {Object} [keys] 搜索条件
* - keys.name 项目名称 * - keys.name 项目名称
* *
@ -44,6 +47,7 @@ class ProjectController extends AbstractController
// //
$all = Request::input('all'); $all = Request::input('all');
$archived = Request::input('archived', 'no'); $archived = Request::input('archived', 'no');
$andcolumn = Request::input('andcolumn', 'no');
// //
if ($all) { if ($all) {
$user->identity('admin'); $user->identity('admin');
@ -52,6 +56,10 @@ class ProjectController extends AbstractController
$builder = Project::select(Project::projectSelect)->authData(); $builder = Project::select(Project::projectSelect)->authData();
} }
// //
if ($andcolumn == 'yes') {
$builder->with(['projectColumn']);
}
//
if ($archived == 'yes') { if ($archived == 'yes') {
$builder->whereNotNull('projects.archived_at'); $builder->whereNotNull('projects.archived_at');
} elseif ($archived == 'no') { } elseif ($archived == 'no') {

View File

@ -58,7 +58,7 @@
"stylus-loader": "^6.2.0", "stylus-loader": "^6.2.0",
"tinymce": "^5.10.2", "tinymce": "^5.10.2",
"tui-calendar-hi": "^1.15.1-1", "tui-calendar-hi": "^1.15.1-1",
"view-design-hi": "^4.7.0-3", "view-design-hi": "^4.7.0-5",
"vue": "^2.6.14", "vue": "^2.6.14",
"vue-clipboard2": "^0.3.3", "vue-clipboard2": "^0.3.3",
"vue-emoji-picker": "^1.0.3", "vue-emoji-picker": "^1.0.3",

View File

@ -126,12 +126,11 @@
<!--添加任务--> <!--添加任务-->
<Modal <Modal
v-model="addTaskShow" v-model="addTaskShow"
:title="$L('添加任务')" :mask-closable="false"
:styles="{ :styles="{
width: '90%', width: '90%',
maxWidth: '640px' maxWidth: '640px'
}" }"
:mask-closable="false"
footer-hide> footer-hide>
<TaskAdd ref="addTask" v-model="addTaskShow"/> <TaskAdd ref="addTask" v-model="addTaskShow"/>
</Modal> </Modal>
@ -256,9 +255,6 @@ export default {
'column_id': data, 'column_id': data,
}); });
this.addTaskShow = true; this.addTaskShow = true;
this.$nextTick(() => {
this.$refs.addTask.$refs.input.focus();
})
}); });
// //
document.addEventListener('keydown', this.shortcutEvent); document.addEventListener('keydown', this.shortcutEvent);

View File

@ -815,7 +815,7 @@ export default {
this.$store.dispatch("saveColumn", data); this.$store.dispatch("saveColumn", data);
}).catch(({msg}) => { }).catch(({msg}) => {
this.$set(this.columnLoad, column.id, false); this.$set(this.columnLoad, column.id, false);
this.$store.dispatch("getColumns", {project_id: this.projectId}) this.$store.dispatch("getColumns", this.projectId)
$A.modalError(msg); $A.modalError(msg);
}); });
}, },

View File

@ -1,5 +1,16 @@
<template> <template>
<div class="task-add"> <div class="task-add">
<div class="head" :class="{empty:addData.cascader.length == 0,visible:cascaderShow}">
<Cascader
v-model="addData.cascader"
:data="cascaderData"
:clearable="false"
:placeholder="$L('请选择项目')"
:load-data="cascaderLoadData"
@on-input-change="cascaderInputChange"
@on-visible-change="cascaderShow=!cascaderShow"
filterable/>
</div>
<div class="task-add-form"> <div class="task-add-form">
<div class="title"> <div class="title">
<Input <Input
@ -39,22 +50,6 @@
</div> </div>
<Form v-if="advanced" class="task-add-advanced" label-width="auto" @submit.native.prevent> <Form v-if="advanced" class="task-add-advanced" label-width="auto" @submit.native.prevent>
<FormItem :label="$L('任务列表')">
<Select
v-model="addData.column_id"
:placeholder="$L('选择任务列表')"
:multipleMax="1"
multiple
filterable
transfer
allowCreate
transfer-class-name="task-add-advanced-transfer"
@on-create="columnCreate">
<div slot="drop-prepend" class="task-drop-prepend">{{$L('最多只能选择1项')}}</div>
<Option v-for="(item, key) in columnList" :value="item.id" :key="key">{{ item.name }}</Option>
<Option v-for="(item, key) in columnAdd" :value="item.id" :key="'_' + key">{{ item.name }}</Option>
</Select>
</FormItem>
<FormItem :label="$L('计划时间')"> <FormItem :label="$L('计划时间')">
<DatePicker <DatePicker
v-model="addData.times" v-model="addData.times"
@ -70,7 +65,7 @@
v-model="addData.owner" v-model="addData.owner"
:multiple-max="10" :multiple-max="10"
:placeholder="$L('选择任务负责人')" :placeholder="$L('选择任务负责人')"
:project-id="projectId"/> :project-id="addData.project_id"/>
</FormItem> </FormItem>
<div class="subtasks"> <div class="subtasks">
<div v-if="addData.subtasks.length > 0" class="sublist"> <div v-if="addData.subtasks.length > 0" class="sublist">
@ -102,7 +97,7 @@
v-model="item.owner" v-model="item.owner"
:multiple-max="1" :multiple-max="1"
:placeholder="$L('选择负责人')" :placeholder="$L('选择负责人')"
:project-id="projectId"/> :project-id="addData.project_id"/>
</Col> </Col>
</Row> </Row>
</div> </div>
@ -118,13 +113,13 @@
<div class="ivu-modal-footer"> <div class="ivu-modal-footer">
<Button type="default" @click="close">{{$L('取消')}}</Button> <Button type="default" @click="close">{{$L('取消')}}</Button>
<ButtonGroup class="page-manage-add-task-button-group"> <ButtonGroup class="page-manage-add-task-button-group">
<Button type="primary" :loading="loadIng > 0" @click="onAdd">{{$L('添加')}}</Button> <Button type="primary" :loading="loadIng > 0" @click="onAdd">{{$L('添加任务')}}</Button>
<Dropdown @on-click="onAdd(true)"> <Dropdown @on-click="onAdd(true)">
<Button type="primary" :loading="loadIng > 0"> <Button type="primary" :loading="loadIng > 0">
<Icon type="ios-arrow-down"></Icon> <Icon type="ios-arrow-down"></Icon>
</Button> </Button>
<DropdownMenu slot="list"> <DropdownMenu slot="list">
<DropdownItem>{{$L('添加并继续')}}</DropdownItem> <DropdownItem>{{$L('提交继续添加')}}</DropdownItem>
</DropdownMenu> </DropdownMenu>
</Dropdown> </Dropdown>
</ButtonGroup> </ButtonGroup>
@ -149,9 +144,11 @@ export default {
data() { data() {
return { return {
addData: { addData: {
cascader: [],
name: "", name: "",
content: "", content: "",
owner: 0, owner: 0,
project_id: 0,
column_id: 0, column_id: 0,
times: [], times: [],
subtasks: [], subtasks: [],
@ -160,9 +157,14 @@ export default {
p_color: '', p_color: '',
}, },
cascaderShow: false,
cascaderData: [],
cascaderValue: '',
cascaderLoading: 0,
cascaderAlready: [],
advanced: false, advanced: false,
subName: '', subName: '',
columnAdd: [],
taskPlugins: [ taskPlugins: [
'advlist autolink lists link image charmap print preview hr anchor pagebreak', 'advlist autolink lists link image charmap print preview hr anchor pagebreak',
@ -195,14 +197,29 @@ export default {
} }
}, },
mounted() { mounted() {
//
}, },
computed: { computed: {
...mapState(['userId', 'projectId', 'columns', 'taskPriority']), ...mapState(['userId', 'projects', 'projectId', 'columns', 'taskPriority']),
},
columnList() { watch: {
return this.columns.filter(({project_id}) => project_id == this.projectId) value(val) {
if (val) {
this.initCascaderData();
this.initProjectData();
this.$nextTick(this.$refs.input.focus)
}
}, },
'addData.column_id' () {
const {project_id, column_id} = this.addData;
this.$nextTick(() => {
if (project_id && column_id) {
this.$set(this.addData, 'cascader', [project_id, column_id]);
} else {
this.$set(this.addData, 'cascader', []);
}
})
}
}, },
methods: { methods: {
initLanguage() { initLanguage() {
@ -256,17 +273,55 @@ export default {
}] }]
}; };
}, },
close() {
this.$emit("input", !this.value) initCascaderData() {
}, this.cascaderData = this.projects.map(project => {
columnCreate(val) { const children = this.columns.filter(({project_id}) => project_id == project.id).map(column => {
if (!this.columnAdd.find(({id}) => id == val)) { return {
this.columnAdd.push({ value: column.id,
id: val, label: column.name
name: val }
}); });
let data = {
value: project.id,
label: project.name,
children,
};
if (children.length == 0) {
data.loading = false;
}
return data
});
},
initProjectData() {
let column_id = this.addData.column_id;
if (column_id) {
let column = this.columns.find(({id}) => id == column_id);
if (column) {
this.addData.project_id = column.project_id;
this.addData.column_id = column.id;
}
} else {
let project = this.projects.find(({id}) => id == this.projectId) || this.projects.find(({id}) => id > 0);
if (project) {
let column = this.columns.find(({project_id}) => project_id == project.id);
if (column) {
this.addData.project_id = column.project_id;
this.addData.column_id = column.id;
} else {
this.$store.dispatch("getColumns", project.id).then((data) => {
column = data.find(({id}) => id > 0);
if (column) {
this.addData.project_id = column.project_id;
this.addData.column_id = column.id;
}
});
}
}
} }
}, },
taskTimeChange(times) { taskTimeChange(times) {
let tempc = $A.date2string(times, "Y-m-d H:i"); let tempc = $A.date2string(times, "Y-m-d H:i");
if (tempc[0] && tempc[1]) { if (tempc[0] && tempc[1]) {
@ -275,6 +330,7 @@ export default {
} }
} }
}, },
onKeydown(e) { onKeydown(e) {
if (e.keyCode === 13) { if (e.keyCode === 13) {
if (e.shiftKey) { if (e.shiftKey) {
@ -284,6 +340,7 @@ export default {
this.onAdd(); this.onAdd();
} }
}, },
addSubTask() { addSubTask() {
if (this.subName.trim() !== '') { if (this.subName.trim() !== '') {
this.addData.subtasks.push({ this.addData.subtasks.push({
@ -294,6 +351,7 @@ export default {
this.subName = ''; this.subName = '';
} }
}, },
choosePriority(item) { choosePriority(item) {
let start = new Date(); let start = new Date();
let end = new Date(new Date().setDate(start.getDate() + $A.runNum(item.days))); let end = new Date(new Date().setDate(start.getDate() + $A.runNum(item.days)));
@ -302,6 +360,7 @@ export default {
this.$set(this.addData, 'p_name', item.name) this.$set(this.addData, 'p_name', item.name)
this.$set(this.addData, 'p_color', item.color) this.$set(this.addData, 'p_color', item.color)
}, },
defaultPriority() { defaultPriority() {
if (this.taskPriority.length === 0) { if (this.taskPriority.length === 0) {
return; return;
@ -311,18 +370,59 @@ export default {
} }
this.choosePriority(this.taskPriority[0]); this.choosePriority(this.taskPriority[0]);
}, },
cascaderLoadData(item, callback) {
item.loading = true;
this.$store.dispatch("getColumns", item.value).then((data) => {
item.children = data.map(column => {
return {
value: column.id,
label: column.name
}
});
item.loading = false;
callback();
}).catch(() => {
item.loading = false;
callback();
});
},
cascaderInputChange(key) {
this.cascaderValue = key || "";
//
if (this.cascaderAlready[this.cascaderValue] === true) {
return;
}
this.cascaderAlready[this.cascaderValue] = true;
//
setTimeout(() => {
this.cascaderLoading++;
}, 1000)
this.$store.dispatch("getProjects", {
keys: {
name: this.cascaderValue,
},
andcolumn: 'yes'
}).then(() => {
this.cascaderLoading--;
this.initCascaderData();
}).catch(() => {
this.cascaderLoading--;
});
},
setData(data) { setData(data) {
this.addData = Object.assign({}, this.addData, data); this.addData = Object.assign({}, this.addData, data);
}, },
onAdd(again) { onAdd(again) {
if (!this.addData.name) { if (!this.addData.name) {
$A.messageError("任务描述不能为空"); $A.messageError("任务描述不能为空");
return; return;
} }
this.loadIng++; this.loadIng++;
this.$store.dispatch("taskAdd", Object.assign(this.addData, { this.$store.dispatch("taskAdd", this.addData).then(({msg}) => {
project_id: this.projectId
})).then(({msg}) => {
this.loadIng--; this.loadIng--;
$A.messageSuccess(msg); $A.messageSuccess(msg);
if (again === true) { if (again === true) {
@ -334,6 +434,7 @@ export default {
this.$refs.input.focus(); this.$refs.input.focus();
} else { } else {
this.addData = { this.addData = {
cascader: [],
name: "", name: "",
content: "", content: "",
owner: 0, owner: 0,
@ -350,7 +451,11 @@ export default {
this.loadIng--; this.loadIng--;
$A.modalError(msg); $A.modalError(msg);
}); });
} },
close() {
this.$emit("input", !this.value)
},
} }
} }
</script> </script>

View File

@ -516,6 +516,10 @@ export default {
dispatch("saveProject", project) dispatch("saveProject", project)
}); });
} else if (state.method.isJson(data)) { } else if (state.method.isJson(data)) {
if (typeof data.project_column !== "undefined") {
dispatch("saveColumn", data.project_column)
delete data.project_column;
}
let index = state.projects.findIndex(({id}) => id == data.id); let index = state.projects.findIndex(({id}) => id == data.id);
if (index > -1) { if (index > -1) {
state.projects.splice(index, 1, Object.assign(state.projects[index], data)); state.projects.splice(index, 1, Object.assign(state.projects[index], data));
@ -571,10 +575,10 @@ export default {
dispatch("call", { dispatch("call", {
url: 'project/lists', url: 'project/lists',
data: data || {} data: data || {}
}).then(result => { }).then(({data}) => {
state.projectTotal = result.data.total_all; state.projectTotal = data.total_all;
dispatch("saveProject", result.data.data); dispatch("saveProject", data.data);
resolve(result) resolve(data)
}).catch(e => { }).catch(e => {
console.error(e); console.error(e);
reject(e) reject(e)
@ -747,45 +751,51 @@ export default {
* @param state * @param state
* @param dispatch * @param dispatch
* @param project_id * @param project_id
* @returns {Promise<unknown>}
*/ */
getColumns({state, dispatch}, project_id) { getColumns({state, dispatch}, project_id) {
if (state.userId === 0) { return new Promise(function (resolve, reject) {
state.columns = []; if (state.userId === 0) {
return; state.columns = [];
} reject({msg: 'Parameter error'})
if (state.cacheColumns.length > 0) { return;
state.columns = state.cacheColumns;
}
state.projectLoad++;
dispatch("call", {
url: 'project/column/lists',
data: {
project_id
} }
}).then(result => { if (state.cacheColumns.length > 0) {
state.projectLoad--; state.columns = state.cacheColumns;
const ids = result.data.data.map(({id}) => id)
if (ids.length > 0) {
state.columns = state.columns.filter((item) => item.project_id != project_id || ids.includes(item.id));
} }
dispatch("saveColumn", result.data.data); state.projectLoad++;
// 判断只有1列的时候默认版面为表格模式 dispatch("call", {
if (state.columns.filter(item => item.project_id == project_id).length === 1) { url: 'project/column/lists',
const cache = state.cacheTablePanel.find(item => item.project_id == project_id) || {}; data: {
if (typeof cache.cardInit === "undefined" || cache.cardInit === false) { project_id
dispatch("toggleTablePanel", {
project_id,
key: {
card: false,
cardInit: true,
}
});
} }
} }).then(({data}) => {
}).catch(e => { state.projectLoad--;
console.error(e); const ids = data.data.map(({id}) => id)
state.projectLoad--; if (ids.length > 0) {
}); state.columns = state.columns.filter((item) => item.project_id != project_id || ids.includes(item.id));
}
dispatch("saveColumn", data.data);
// 判断只有1列的时候默认版面为表格模式
if (state.columns.filter(item => item.project_id == project_id).length === 1) {
const cache = state.cacheTablePanel.find(item => item.project_id == project_id) || {};
if (typeof cache.cardInit === "undefined" || cache.cardInit === false) {
dispatch("toggleTablePanel", {
project_id,
key: {
card: false,
cardInit: true,
}
});
}
}
resolve(data.data)
}).catch(e => {
console.error(e);
state.projectLoad--;
reject(e);
});
})
}, },
/** /**

View File

@ -1,4 +1,57 @@
.task-add { .task-add {
.head {
display: flex;
align-items: center;
padding: 8px 0;
margin-bottom: 20px;
position: relative;
.ivu-cascader {
.ivu-cascader-label {
width: auto;
padding: 0;
font-size: 15px;
display: flex;
align-items: center;
&:after {
transition: all 0.2s;
padding: 6px;
font-family: Ionicons, serif;
content: "\f116";
opacity: 1;
}
}
.ivu-cascader-arrow {
display: none;
}
.ivu-input {
padding: 4px 0;
border: 0;
box-shadow: none;
font-size: 15px;
}
.ivu-cascader-not-found-tip {
padding: 6px 12px;
}
}
&.empty {
.ivu-cascader {
.ivu-cascader-label {
&:after {
opacity: 0;
}
}
}
}
&.visible {
.ivu-cascader {
.ivu-cascader-label {
&:after {
transform: rotate(180deg);
}
}
}
}
}
.task-add-form, .task-add-form,
.task-add-advanced { .task-add-advanced {
.title { .title {
@ -95,14 +148,3 @@
padding: 26px 0 22px !important; padding: 26px 0 22px !important;
} }
} }
.task-add-advanced-transfer {
.task-drop-prepend {
text-align: center;
color: #c5c8ce;
line-height: 20px;
padding-bottom: 5px;
font-size: 12px;
border-bottom: 1px solid #f1f1f1;
margin-bottom: 5px;
}
}