perf: 添加任务模板

This commit is contained in:
kuaifan 2024-12-02 07:17:14 +08:00
parent 6bafa0a6dd
commit e3312c97a7
5 changed files with 89 additions and 30 deletions

View File

@ -2718,7 +2718,6 @@ class ProjectController extends AbstractController
return Base::retError('参数错误'); return Base::retError('参数错误');
} }
$templates = ProjectTaskTemplate::where('project_id', $projectId) $templates = ProjectTaskTemplate::where('project_id', $projectId)
->orderBy('sort')
->orderByDesc('id') ->orderByDesc('id')
->get(); ->get();
return Base::retSuccess('success', $templates); return Base::retSuccess('success', $templates);
@ -2782,7 +2781,6 @@ class ProjectController extends AbstractController
if ($templateCount >= 20) { if ($templateCount >= 20) {
return Base::retError('每个项目最多添加20个模板'); return Base::retError('每个项目最多添加20个模板');
} }
$data['sort'] = ProjectTaskTemplate::where('project_id', $projectId)->max('sort') + 1;
$template = ProjectTaskTemplate::create($data); $template = ProjectTaskTemplate::create($data);
} }
return Base::retSuccess('保存成功', $template); return Base::retSuccess('保存成功', $template);

View File

@ -10,7 +10,7 @@ namespace App\Models;
* @property string $name 模板名称 * @property string $name 模板名称
* @property string|null $title 任务标题 * @property string|null $title 任务标题
* @property string|null $content 任务内容 * @property string|null $content 任务内容
* @property int $sort 排序 * @property int $sort 排序(预留)
* @property bool $is_default 是否默认模板 * @property bool $is_default 是否默认模板
* @property int $userid 创建人 * @property int $userid 创建人
* @property \Illuminate\Support\Carbon|null $created_at * @property \Illuminate\Support\Carbon|null $created_at

View File

@ -12,7 +12,8 @@
<div class="content"> <div class="content">
<div v-if="!templates.length" class="empty"> <div v-if="!templates.length" class="empty">
<div class="empty-text">{{$L('暂无任务模板')}}</div> <div class="empty-text">{{$L('当前项目暂无任务模板')}}</div>
<Button type="primary" icon="md-add" @click="handleAdd">{{$L('新建模板')}}</Button>
</div> </div>
<div v-else class="template-list"> <div v-else class="template-list">
<div v-for="item in templates" :key="item.id" class="template-item"> <div v-for="item in templates" :key="item.id" class="template-item">
@ -53,15 +54,16 @@
v-bind="formOptions" v-bind="formOptions"
@submit.native.prevent> @submit.native.prevent>
<FormItem prop="name" :label="$L('模板名称')"> <FormItem prop="name" :label="$L('模板名称')">
<Input ref="templateName" v-model="editingTemplate.name" :placeholder="$L('请输入模板名称')"/> <Input ref="templateName" v-model="editingTemplate.name" :disabled="systemTemplateIsMultiple" :placeholder="$L('请输入模板名称')"/>
</FormItem> </FormItem>
<FormItem prop="title" :label="$L('任务标题')"> <FormItem prop="title" :label="$L('任务标题')">
<Input v-model="editingTemplate.title" :placeholder="$L('请输入任务标题')"/> <Input v-model="editingTemplate.title" :disabled="systemTemplateIsMultiple" :placeholder="$L('请输入任务标题')"/>
</FormItem> </FormItem>
<FormItem prop="content" :label="$L('任务内容')"> <FormItem prop="content" :label="$L('任务内容')">
<Input <Input
type="textarea" type="textarea"
v-model="editingTemplate.content" v-model="editingTemplate.content"
:disabled="systemTemplateIsMultiple"
:placeholder="$L('请输入任务内容')" :placeholder="$L('请输入任务内容')"
:autosize="{ minRows: 4, maxRows: 12 }"/> :autosize="{ minRows: 4, maxRows: 12 }"/>
</FormItem> </FormItem>
@ -69,14 +71,27 @@
<div class="project-task-template-system"> <div class="project-task-template-system">
<div v-if="!systemTemplateShow" @click="onSystemTemplate" class="tip-title">{{$L('使用示例模板')}}</div> <div v-if="!systemTemplateShow" @click="onSystemTemplate" class="tip-title">{{$L('使用示例模板')}}</div>
<ul v-else> <ul v-else>
<li v-for="(item, index) in systemTemplateData" :key="index" @click="useSystemTemplate(item)">{{item.name}}</li> <li
:class="{selected:systemTemplateIsMultiple}"
@click="systemTemplateIsMultiple=!systemTemplateIsMultiple">
<i class="taskfont" v-html="systemTemplateIsMultiple ? '&#xe627;' : '&#xe625;'"></i>
{{$L('多选')}}
</li>
<li
v-for="(item, index) in systemTemplateData"
:key="index"
:class="{selected:systemTemplateIsMultiple && systemTemplateMultipleData.indexOf(item)!==-1}"
@click="useSystemTemplate(item)">{{item.name}}</li>
</ul> </ul>
</div> </div>
</FormItem> </FormItem>
</Form> </Form>
<div slot="footer" class="adaption"> <div slot="footer" class="adaption">
<Button type="default" @click="showEditModal=false">{{$L('取消')}}</Button> <Button type="default" @click="showEditModal=false">{{$L('取消')}}</Button>
<Button type="primary" :loading="loading" @click="handleSave">{{$L('保存')}}</Button> <Button type="primary" :loading="loading" @click="handleSave">
{{ $L('保存') }}
{{ systemTemplateIsMultiple && systemTemplateMultipleData.length > 0 ? ` (${systemTemplateMultipleData.length})` : '' }}
</Button>
</div> </div>
</Modal> </Modal>
</div> </div>
@ -111,6 +126,8 @@ export default {
systemTemplateShow: false, systemTemplateShow: false,
systemTemplateData: [], systemTemplateData: [],
systemTemplateIsMultiple: false,
systemTemplateMultipleData: [],
} }
}, },
computed: { computed: {
@ -176,22 +193,40 @@ export default {
$A.messageWarning('请输入模板名称') $A.messageWarning('请输入模板名称')
return return
} }
let savePromises = []
if (this.systemTemplateIsMultiple) {
if (this.systemTemplateMultipleData.length === 0) {
$A.messageWarning('请选择示例模板')
return
}
savePromises = this.systemTemplateMultipleData.map(item => {
const template = { ...this.editingTemplate, id: null, name: item.name, title: item.title, content: item.content }
return this.handleSaveCall(template)
})
} else {
savePromises.push(this.handleSaveCall(this.editingTemplate))
}
try { try {
await this.$store.dispatch("call", { const results = await Promise.all(savePromises)
url: 'project/task/template_save', $A.messageSuccess(results.length === 1 ? results[0].msg : '全部保存成功')
data: this.editingTemplate,
method: 'post',
spinner: 300
})
$A.messageSuccess('保存成功')
this.showEditModal = false this.showEditModal = false
this.loadTemplates() this.loadTemplates()
} catch ({msg}) { } catch (error) {
$A.messageError(msg || '保存失败') $A.messageError(error.msg || '保存失败')
} }
}, },
//
async handleSaveCall(data) {
return this.$store.dispatch("call", {
url: 'project/task/template_save',
data,
method: 'post',
spinner: 300
})
},
// //
async handleDelete(template) { async handleDelete(template) {
$A.modalConfirm({ $A.modalConfirm({
@ -199,14 +234,14 @@ export default {
content: '确定要删除该模板吗?', content: '确定要删除该模板吗?',
onOk: async () => { onOk: async () => {
try { try {
await this.$store.dispatch("call", { const {msg} = await this.$store.dispatch("call", {
url: 'project/task/template_delete', url: 'project/task/template_delete',
data: { data: {
id: template.id id: template.id
}, },
spinner: 300 spinner: 300
}) })
$A.messageSuccess('删除成功') $A.messageSuccess(msg || '删除成功')
this.loadTemplates() this.loadTemplates()
} catch ({msg}) { } catch ({msg}) {
$A.messageError(msg || '删除失败') $A.messageError(msg || '删除失败')
@ -218,7 +253,7 @@ export default {
// //
async handleSetDefault(template) { async handleSetDefault(template) {
try { try {
await this.$store.dispatch("call", { const {msg} = await this.$store.dispatch("call", {
url: 'project/task/template_default', url: 'project/task/template_default',
data: { data: {
id: template.id, id: template.id,
@ -226,7 +261,7 @@ export default {
}, },
spinner: 300 spinner: 300
}) })
$A.messageSuccess('设置成功') $A.messageSuccess(msg || '设置成功')
this.loadTemplates() this.loadTemplates()
} catch ({msg}) { } catch ({msg}) {
$A.messageError(msg || '设置失败') $A.messageError(msg || '设置失败')
@ -244,6 +279,15 @@ export default {
this.editingTemplate.name = item.name this.editingTemplate.name = item.name
this.editingTemplate.title = item.title this.editingTemplate.title = item.title
this.editingTemplate.content = item.content this.editingTemplate.content = item.content
//
if (this.systemTemplateIsMultiple) {
const index = this.systemTemplateMultipleData.indexOf(item)
if (index === -1) {
this.systemTemplateMultipleData.push(item)
} else {
this.systemTemplateMultipleData.splice(index, 1)
}
}
} }
} }
} }

View File

@ -631,4 +631,14 @@ body.dark-mode-reverse {
} }
} }
} }
.project-task-template-system {
ul {
> li {
&.selected {
color: black;
}
}
}
}
} }

View File

@ -4,7 +4,8 @@
flex-direction: column; flex-direction: column;
.header { .header {
padding: 20px 20px 10px; height: 60px;
padding: 10px 20px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: flex-start; justify-content: flex-start;
@ -30,15 +31,15 @@
.empty { .empty {
text-align: center; text-align: center;
padding: 40px 0;
color: $primary-text-color;
height: 100%; height: 100%;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
flex-direction: column;
.empty-text { padding: 20px;
font-size: 14px; color: $primary-text-color;
> button {
margin-top: 18px;
} }
} }
} }
@ -86,11 +87,12 @@
} }
.template-actions { .template-actions {
margin-top: 12px; margin-top: 4px;
text-align: right; text-align: right;
> button { > button {
margin-right: 12px; margin-top: 8px;
margin-right: 8px;
height: 28px; height: 28px;
padding: 0 12px; padding: 0 12px;
font-size: 13px; font-size: 13px;
@ -128,6 +130,11 @@
&:hover { &:hover {
background-color: #e0e0e0; background-color: #e0e0e0;
} }
&.selected {
background-color: $primary-color;
color: white;
}
} }
} }
} }