整合前端
This commit is contained in:
135
ruoyi-ui/src/views/oa/project/pace/components/FormDialog.vue
Normal file
135
ruoyi-ui/src/views/oa/project/pace/components/FormDialog.vue
Normal file
@@ -0,0 +1,135 @@
|
||||
<template>
|
||||
<el-dialog :visible.sync="visible" custom-class="add-dialog" title="新增项目进度" width="800px" @close="reset">
|
||||
<el-form :model="form" label-width="80px" :rules="rules" ref="progressForm">
|
||||
<!-- ================= 主体区域 ================= -->
|
||||
<el-row :gutter="20">
|
||||
<!-- ---------- 左侧:项目选择 + 信息 ---------- -->
|
||||
<el-col :span="24">
|
||||
<el-form-item label="项目" prop="projectId">
|
||||
<ProjectSelect v-model="form.projectId" style="width: 100%" placeholder="选择项目" filterable
|
||||
@change="handleProjectChange" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="进度模板" prop="templateType">
|
||||
<el-select v-model="form.templateType" style="width: 100%" placeholder="选择进度模板">
|
||||
<el-option v-for="t in templateTypeOptions" :key="t.value" :label="t.label" :value="t.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 选中项目后展示详情 -->
|
||||
<ProjectInfo :info="projectDetail" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- ---------- 底部按钮 ---------- -->
|
||||
<el-form-item style="margin-top: 20px; text-align: right">
|
||||
<el-button @click="visible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submit">保存</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getProject } from '@/api/oa/project';
|
||||
|
||||
import ProjectInfo from "@/components/fad-service/ProjectInfo/index.vue";
|
||||
import ProjectSelect from "@/components/fad-service/ProjectSelect/index.vue";
|
||||
|
||||
export default {
|
||||
name: 'FormDialog',
|
||||
components: { ProjectInfo, ProjectSelect },
|
||||
props: {
|
||||
value: { type: Boolean, required: true },
|
||||
projects: { type: Array, default: () => [] },
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
visible: false,
|
||||
projectDetail: null,
|
||||
availableSteps: [],
|
||||
form: { projectId: '', mode: 'custom', templateType: 'automation', steps: [] },
|
||||
rules: { projectId: [{ required: true, message: '请选择项目', trigger: 'change' }] },
|
||||
stepParams: { pageNum: 1, pageSize: 9999, stepName: '' },
|
||||
templateTypeOptions: [
|
||||
{ label: '信息化', value: 'software' },
|
||||
{ label: '自动化', value: 'automation' }
|
||||
]
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value: {
|
||||
immediate: true, handler (v) {
|
||||
this.visible = v
|
||||
}
|
||||
},
|
||||
visible (v) {
|
||||
this.$emit('input', v)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleProjectChange (projectId) {
|
||||
getProject(projectId).then(r => {
|
||||
this.projectDetail = r.data
|
||||
})
|
||||
},
|
||||
/* -------- 提交 -------- */
|
||||
submit () {
|
||||
this.$refs.progressForm.validate(valid => {
|
||||
if (!valid) return
|
||||
const payload = { ...this.form }
|
||||
this.$emit('save', payload)
|
||||
this.visible = false
|
||||
this.$message.success('保存成功')
|
||||
})
|
||||
},
|
||||
reset () {
|
||||
this.visible = false
|
||||
this.projectDetail = null
|
||||
this.form = { projectId: '', mode: 'preset', templateType: 'automation' }
|
||||
},
|
||||
/* 工具函数 */
|
||||
parseTime (time, fmt) {
|
||||
const date = new Date(time)
|
||||
const o = { '{y}': date.getFullYear(), '{m}': date.getMonth() + 1, '{d}': date.getDate() }
|
||||
return fmt.replace(/\{[ymd]\}/g, k => String(o[k]).padStart(2, '0'))
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.add-dialog ::v-deep .el-dialog__body {
|
||||
padding-top: 12px
|
||||
}
|
||||
|
||||
.project-detail {
|
||||
font-size: 13px;
|
||||
line-height: 1.6
|
||||
}
|
||||
|
||||
.detail-row+.detail-row {
|
||||
margin-top: 4px
|
||||
}
|
||||
|
||||
.box-title {
|
||||
font-weight: 600;
|
||||
margin-bottom: 4px
|
||||
}
|
||||
|
||||
.step-box {
|
||||
min-height: 120px
|
||||
}
|
||||
|
||||
.clickable {
|
||||
margin: 4px;
|
||||
cursor: pointer
|
||||
}
|
||||
|
||||
.empty-hint {
|
||||
color: #c0c4cc;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
padding: 20px 0
|
||||
}
|
||||
</style>
|
||||
170
ruoyi-ui/src/views/oa/project/pace/components/MenuSelect.vue
Normal file
170
ruoyi-ui/src/views/oa/project/pace/components/MenuSelect.vue
Normal file
@@ -0,0 +1,170 @@
|
||||
<template>
|
||||
<div class="two-level-filter">
|
||||
<!-- 第一级:进度类别 -->
|
||||
<div class="filter-panel first-level">
|
||||
<h3 class="panel-title">进度类别</h3>
|
||||
<ul class="option-list">
|
||||
<li v-for="item in tabOption" :key="item.value" :class="{ 'active': defaultTabNode === item.value }"
|
||||
@click="handleTabChange(item.value)" class="option-item">
|
||||
{{ item.label }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- 第二级:一级分类 -->
|
||||
<div class="filter-panel second-level">
|
||||
<h3 class="panel-title">一级分类</h3>
|
||||
<div class="second-level-content">
|
||||
<template v-if="defaultTabNode">
|
||||
<ul class="option-list" v-if="renderFirstLevelOption.length">
|
||||
<li v-for="item in renderFirstLevelOption" :key="item.value"
|
||||
:class="{ 'active': defaultFirstLevelNode === item.value }" @click="handleFirstLevelChange(item.value)"
|
||||
class="option-item">
|
||||
{{ item.label }}
|
||||
</li>
|
||||
</ul>
|
||||
<p class="empty-tip" v-else>当前进度类别下无一级分类</p>
|
||||
</template>
|
||||
<p class="empty-tip" v-else>请先选择进度类别</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
tabOption: {
|
||||
// 格式: { label: 'xxx', value: 'xxx' }
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
firstLevelOption: {
|
||||
// 格式: { label: 'xxx', value: 'xxx', tabNode: 'xxx' }
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
defaultTabNode: "",
|
||||
defaultFirstLevelNode: ""
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
// 根据选中的进度类别过滤一级分类
|
||||
renderFirstLevelOption () {
|
||||
if (this.defaultTabNode) {
|
||||
return this.firstLevelOption.filter(
|
||||
item => item.tabNode === this.defaultTabNode
|
||||
);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 处理进度类别变更
|
||||
handleTabChange (tabNode) {
|
||||
this.defaultTabNode = tabNode;
|
||||
this.defaultFirstLevelNode = ""; // 重置二级选中值
|
||||
this.$emit("change", {
|
||||
tabNode: this.defaultTabNode,
|
||||
firstLevelNode: this.defaultFirstLevelNode
|
||||
});
|
||||
},
|
||||
// 处理一级分类变更
|
||||
handleFirstLevelChange (firstLevelNode) {
|
||||
this.defaultFirstLevelNode = firstLevelNode;
|
||||
this.$emit("change", {
|
||||
tabNode: this.defaultTabNode,
|
||||
firstLevelNode: this.defaultFirstLevelNode
|
||||
});
|
||||
},
|
||||
clear () {
|
||||
this.defaultTabNode = "";
|
||||
this.defaultFirstLevelNode = "";
|
||||
this.$emit("change", {
|
||||
tabNode: this.defaultTabNode,
|
||||
firstLevelNode: this.defaultFirstLevelNode
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.two-level-filter {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 540px;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.filter-panel {
|
||||
flex: 1;
|
||||
min-height: 200px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.first-level {
|
||||
border-right: 1px solid #e5e7eb;
|
||||
}
|
||||
|
||||
.panel-title {
|
||||
margin: 0;
|
||||
padding: 12px 16px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
background-color: #f9fafb;
|
||||
border-bottom: 1px solid #e5e7eb;
|
||||
}
|
||||
|
||||
.option-list {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.option-item {
|
||||
padding: 12px 16px;
|
||||
font-size: 14px;
|
||||
color: #374151;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.option-item:hover {
|
||||
background-color: #f3f4f6;
|
||||
}
|
||||
|
||||
.option-item.active {
|
||||
background-color: #eff6ff;
|
||||
color: #2563eb;
|
||||
font-weight: 500;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.option-item.active::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
width: 3px;
|
||||
background-color: #2563eb;
|
||||
}
|
||||
|
||||
.second-level-content {
|
||||
height: calc(100% - 45px);
|
||||
}
|
||||
|
||||
.empty-tip {
|
||||
color: #9ca3af;
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
padding: 0 16px;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
744
ruoyi-ui/src/views/oa/project/pace/components/StepTable.vue
Normal file
744
ruoyi-ui/src/views/oa/project/pace/components/StepTable.vue
Normal file
@@ -0,0 +1,744 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-row style="margin-bottom: 10px;">
|
||||
<el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="addInnerData">新增</el-button>
|
||||
<slot name="extra-buttons"></slot>
|
||||
</el-col>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<vxe-table size="mini" height="500" ref="tableRef" border show-overflow :edit-config="editConfig" :data="innerData"
|
||||
:row-config="{ 'isCurrent': true }" :column-config="{ 'isCurrent': true }" :sort-config="sortConfig">
|
||||
<vxe-column field="sortNum" title="顺序" :edit-render="{ name: 'input' }" width="70" sortable></vxe-column>
|
||||
<vxe-column field="secondLevelNode" title="步骤名称" :edit-render="{ name: 'input' }"></vxe-column>
|
||||
<vxe-column field="tabNode" title="进度类别" :edit-render="{ name: 'input' }"></vxe-column>
|
||||
<vxe-column field="firstLevelNode" title="一级分类" :edit-render="{ name: 'input' }"></vxe-column>
|
||||
<vxe-column field="nodeHeader" title="负责人" :edit-render="{}">
|
||||
<template slot-scope="{ row }" slot="default">
|
||||
{{ row.nodeHeader }}
|
||||
</template>
|
||||
<template slot-scope="{ row }" slot="edit">
|
||||
<el-select v-model="row.nodeHeader" placeholder="请选择负责人" filterable clearable>
|
||||
<el-option v-for="item in users" :key="item.key" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="relatedDocs" title="成果资料" width="60">
|
||||
<template slot-scope="{ row }" slot="default">
|
||||
<vxe-button type="primary" @click="previewFiles(row)" style="cursor: pointer;">
|
||||
{{ row.relatedDocs ? row.relatedDocs.split(',').length : 0 }}个
|
||||
</vxe-button>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<!-- 资料上传和图片上传也需要定制 -->
|
||||
<vxe-column field="relatedImages" title="相关图片" width="60">
|
||||
<template slot-scope="{ row }" slot="default">
|
||||
<vxe-button type="primary" @click="previewImages(row)" style="cursor: pointer;">
|
||||
{{ row.relatedImages ? row.relatedImages.split(',').length : 0 }}张
|
||||
</vxe-button>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<!-- 结束时间的渲染需要增强 -->
|
||||
<!-- 首先状态改为待验收的时候就自动填写真实结束时间 -->
|
||||
<!-- 与原定结束时间和当前时间做对比, 如果当前时间接近真实时间则提示临期或逾期 -->
|
||||
<!-- 可以申请逾期 -->
|
||||
<!-- <vxe-column field="planEnd" title="计划结束" v-if="isCEO"
|
||||
:edit-render="{ name: 'ElDatePicker', props: { type: 'datetime', valueFormat: 'yyyy-MM-dd HH:mm:ss' } }">
|
||||
</vxe-column> -->
|
||||
<vxe-column title="剩余时间" width="120">
|
||||
<template slot-scope="{ row }" slot="default">
|
||||
<div class="remain-time-container">
|
||||
<vxe-button v-if="row.planEnd && row.useFlag == 1 && row.status == 0 && isSelf(row)" size="mini" type="text"
|
||||
@click="handleApplyDelay(row)" class="delay-btn">
|
||||
申请延期
|
||||
</vxe-button>
|
||||
<!-- 剩余时间显示 -->
|
||||
<span :class="{
|
||||
'text-red': remainStatus(row) === 'overdue',
|
||||
'text-orange': remainStatus(row) === 'warning',
|
||||
'text-green': remainStatus(row) === 'enough',
|
||||
'text-gray': !row.planEnd,
|
||||
'text-blue': row.useFlag == 0
|
||||
}">
|
||||
{{ getRemainText(row) }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column title="结束时间" :edit-render="{}" width="140">
|
||||
<template slot="header">
|
||||
<span title="当前状态为进行中时,表示计划结束时间,此时若字体颜色为蓝色则表示有延期申请,其他状态下表示实际结束时间">
|
||||
结束时间
|
||||
</span>
|
||||
</template>
|
||||
<template slot-scope="{ row }" slot="default">
|
||||
<span v-if="row.status == 0">{{ row.planEnd }}</span>
|
||||
<span v-else>{{ row.endTime }}</span>
|
||||
</template>
|
||||
<template slot-scope="{ row }" slot="edit">
|
||||
<el-date-picker v-if="row.status == 0" v-model="row.planEnd" type="datetime"
|
||||
value-format="yyyy-MM-dd HH:mm:ss" placeholder="选择计划结束时间" />
|
||||
<el-date-picker v-else v-model="row.endTime" type="datetime" value-format="yyyy-MM-dd HH:mm:ss"
|
||||
placeholder="选择结束时间" />
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="status" title="状态">
|
||||
<template slot-scope="{ row }">
|
||||
<!-- 对于总经办的人,可以任意变更状态0,null进行中,1待验收,2已完成 -->
|
||||
<el-select v-if="isCEO" :value="row.status" placeholder="请选择状态" @change="changeStatus(row, $event)">
|
||||
<el-option label="进行中" :value="0"></el-option>
|
||||
<el-option label="待验收" :value="1"></el-option>
|
||||
<el-option label="已完成" :value="2"></el-option>
|
||||
</el-select>
|
||||
<!-- 对于nodeHeader是自己的, 可以变更为待验收状态 -->
|
||||
<template v-else-if="isSelf(row)">
|
||||
<div v-if="row.status === 1">待验收</div>
|
||||
<el-button v-else-if="row.status === 0" type="primary" @click="changeStatus(row, 1)">提交</el-button>
|
||||
<div v-else>已完成</div>
|
||||
</template>
|
||||
<!-- 对于其他人只能查看状态 -->
|
||||
<template v-else>
|
||||
<div>{{ row.status === 0 ? '进行中' : (row.status === 1 ? '待验收' : '已完成') }}</div>
|
||||
</template>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column title="更多信息" show-overflow="tooltip">
|
||||
<template slot-scope="{ row }" slot="default">
|
||||
<el-tooltip v-if="row.specification" class="item" :content="row.specification" placement="top">
|
||||
<div title="点击查看更多信息" @click="handleMore(row)"
|
||||
style="cursor: pointer; width: 100%; min-width: 80px; height: 100%">
|
||||
{{ row.specification || '暂无更多' }}
|
||||
</div>
|
||||
</el-tooltip>
|
||||
<div v-else title="点击查看更多信息" @click="handleMore(row)"
|
||||
style="cursor: pointer; width: 100%; min-width: 80px; height: 100%; min-height: 30px;">
|
||||
{{ row.specification || '暂无更多' }}
|
||||
</div>
|
||||
<!-- <vxe-button>更多</vxe-button> -->
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column title="操作" width="200" v-if="editable && isCEO">
|
||||
<template v-slot:default="{ row }">
|
||||
<template v-if="showEdit(row)">
|
||||
<template v-if="row.trackId">
|
||||
<template v-if="hasEditStatus(row)">
|
||||
<vxe-button @click="saveRowEvent(row)">保存</vxe-button>
|
||||
<vxe-button @click="cancelRowEvent()">取消</vxe-button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<!-- <vxe-button v-if="row.useFlag == 0" @click="agreeDelay(row)">
|
||||
同意延期
|
||||
</vxe-button> -->
|
||||
<vxe-button @click="editRowEvent(row)">编辑</vxe-button>
|
||||
<vxe-button @click="handleDelete(row)">删除</vxe-button>
|
||||
</template>
|
||||
</template>
|
||||
<template v-else>
|
||||
<vxe-button @click="handleAdd(row)">新增</vxe-button>
|
||||
</template>
|
||||
</template>
|
||||
</template>
|
||||
</vxe-column>
|
||||
</vxe-table>
|
||||
|
||||
<el-dialog :visible.sync="dialogDocsVisible" title="成果资料上传" append-to-body>
|
||||
<file-upload v-model="dialogRelatedDocs" @success="handleFileSuccess" @delete="handleFileDelete"></file-upload>
|
||||
<el-button type="primary" @click="uploadRelatedDocs">保存</el-button>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog :visible.sync="dialogImagesVisible" title="相关图片上传" append-to-body>
|
||||
<image-upload v-model="dialogRelatedImages"></image-upload>
|
||||
<el-button type="primary" @click="uploadRelatedImages">保存</el-button>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 延期申请对话框 -->
|
||||
<el-dialog :visible.sync="dialogApplyDelayVisible" title="申请延期" append-to-body>
|
||||
<el-form :model="dialogApplyDelayForm" ref="formRef" label-width="120px">
|
||||
<el-form-item label="延期到" prop="delayTo">
|
||||
<el-date-picker v-model="dialogApplyDelayForm.delayTo" type="date" value-format="yyyy-MM-dd"
|
||||
placeholder="选择延期到的日期" />
|
||||
</el-form-item>
|
||||
<el-form-item label="申请理由" prop="applyReason">
|
||||
<el-input type="textarea" v-model="dialogApplyDelayForm.applyReason" placeholder="请输入申请理由"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-button type="primary" @click="submitApplyDelay">确认申请</el-button>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog :visible.sync="addDialogVisible" title="新增进度" append-to-body>
|
||||
<el-form :model="dialogAddForm" ref="formRef" label-width="120px">
|
||||
<el-form-item label="进度类别" prop="tabNode">
|
||||
<el-input v-model="dialogAddForm.tabNode" placeholder="请输入进度类别"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="一级分类" prop="firstLevelNode">
|
||||
<el-input v-model="dialogAddForm.firstLevelNode" placeholder="请输入一级分类"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="步骤名称" prop="secondLevelNode">
|
||||
<el-input v-model="dialogAddForm.secondLevelNode" placeholder="请输入进度描述"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="计划结束时间" prop="planEnd">
|
||||
<el-date-picker v-model="dialogAddForm.planEnd" type="datetime" value-format="yyyy-MM-dd HH:mm:ss"
|
||||
placeholder="选择计划结束时间" />
|
||||
</el-form-item>
|
||||
<el-form-item label="进度规范" prop="specification">
|
||||
<el-input type="textarea" v-model="dialogAddForm.specification" placeholder="进度规范"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-button type="primary" @click="handleAdd">确认新增</el-button>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog :visible.sync="dialogMoreVisible" title="更多信息" append-to-body v-loading="buttonLoading">
|
||||
<el-row align="middle" justify="space-between" type="flex">
|
||||
<el-col :span="16">
|
||||
<el-alert title="可以任意添加所需的信息,例如:需求资料,需求描述" type="info"></el-alert>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-button icon="el-icon-plus" @click="handleAddMore" plain size="mini">新增</el-button>
|
||||
<el-button type="primary" icon="el-icon-check" @click="handleMoreSave" plain size="mini">保存</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-descriptions :column="1" border style="margin-top: 20px;">
|
||||
<el-descriptions-item label="规格需求">
|
||||
<el-input v-model="dialogMoreForm.specification" placeholder="规格需求" type="textarea"></el-input>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="需求资料">
|
||||
<file-upload v-model="dialogMoreForm.requirementFile"></file-upload>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="供应商">
|
||||
<el-select v-model="dialogMoreForm.supplierId" placeholder="请选择供应商">
|
||||
<el-option v-for="item in supplierList" :key="item.value" :label="item.label"
|
||||
:value="item.value"></el-option>
|
||||
</el-select>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<el-descriptions :column="1" border style="margin-top: 20px;" :labelStyle="{ width: '150px' }">
|
||||
<el-descriptions-item v-for="item in dialogMoreForm.other" :key="item.label">
|
||||
<template slot="label">
|
||||
<el-input v-model="item.label" placeholder="请输入信息名称"></el-input>
|
||||
</template>
|
||||
<el-input v-model="item.value" placeholder="请输入" type="textarea"></el-input>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { addFileOperationRecord } from '@/api/oa/fileOperationRecord';
|
||||
import { applyProjectScheduleDelay } from "@/api/oa/projectScheduleDelay";
|
||||
import { updateProjectScheduleStep } from "@/api/oa/projectScheduleStep";
|
||||
import { listSupplier } from "@/api/oa/supplier";
|
||||
import { listUser } from "@/api/system/user";
|
||||
|
||||
export default {
|
||||
name: "StepTable",
|
||||
props: {
|
||||
stepList: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
master: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
editable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
defaultTabNode: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
defaultFirstLevelNode: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
innerData: [],
|
||||
buttonLoading: false,
|
||||
editConfig: { trigger: 'manual', mode: 'row' },
|
||||
sortConfig: {
|
||||
defaultSort: {
|
||||
field: 'sortNum',
|
||||
order: 'asc'
|
||||
}
|
||||
},
|
||||
dialogRelatedDocs: "",
|
||||
dialogRelatedImages: "",
|
||||
dialogDocsVisible: false,
|
||||
dialogImagesVisible: false,
|
||||
currentRow: {},
|
||||
// 延期对话框控制
|
||||
dialogApplyDelayVisible: false,
|
||||
dialogApplyDelayForm: {
|
||||
trackId: '',
|
||||
delayTo: '',
|
||||
applyReason: '',
|
||||
},
|
||||
dialogMoreVisible: false,
|
||||
// 新增对话框控制
|
||||
addDialogVisible: false,
|
||||
dialogMoreForm: {
|
||||
specification: '',
|
||||
requirementFile: '',
|
||||
supplierId: '',
|
||||
other: [{
|
||||
label: '',
|
||||
value: '',
|
||||
}]
|
||||
},
|
||||
dialogAddForm: {
|
||||
stepOrder: '',
|
||||
secondLevelNode: '',
|
||||
tabNode: '',
|
||||
firstLevelNode: '',
|
||||
specification: '',
|
||||
nodeHeader: '',
|
||||
relatedDocs: '',
|
||||
relatedImages: '',
|
||||
startTime: '',
|
||||
endTime: '',
|
||||
status: '',
|
||||
},
|
||||
users: [],
|
||||
supplierList: [],
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
stepList: {
|
||||
handler (newVal) {
|
||||
this.innerData = newVal;
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
nickName () {
|
||||
return this.$store.getters.nickName;
|
||||
},
|
||||
isCEO () {
|
||||
// console.log(this.$store.getters.roles);
|
||||
// 拥有ceo权限或者admin权限或者是项目负责人
|
||||
return this.$store.getters.roles.includes('ceo') ||
|
||||
this.$store.getters.roles.includes('doctor') ||
|
||||
this.$store.getters.roles.includes('admin') ||
|
||||
this.$store.getters.roles.includes('13') ||
|
||||
this.master === this.nickName;
|
||||
}
|
||||
},
|
||||
|
||||
mounted () {
|
||||
this.getUsers();
|
||||
this.getSupplierList();
|
||||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* 获取所有用户
|
||||
*/
|
||||
getUsers () {
|
||||
listUser({ pageNum: 1, pageSize: 1000 }).then(res => {
|
||||
console.log(res.rows);
|
||||
this.users = res.rows.map(item => ({
|
||||
key: item.userId,
|
||||
label: item.nickName,
|
||||
value: item.nickName,
|
||||
}));
|
||||
});
|
||||
},
|
||||
|
||||
getSupplierList () {
|
||||
listSupplier({ pageNum: 1, pageSize: 1000 }).then(res => {
|
||||
this.supplierList = res.rows.map(item => ({
|
||||
label: item.supplierName,
|
||||
value: item.supplierId,
|
||||
}));
|
||||
});
|
||||
},
|
||||
|
||||
handleFileSuccess (resList, res) {
|
||||
addFileOperationRecord({
|
||||
fileId: res.ossId,
|
||||
fileName: res.name,
|
||||
type: 1,
|
||||
projectId: this.currentRow.projectId,
|
||||
trackId: this.currentRow.trackId,
|
||||
})
|
||||
console.log(this.currentRow, this.dialogRelatedDocs);
|
||||
updateProjectScheduleStep({
|
||||
...this.currentRow,
|
||||
relatedDocs: this.dialogRelatedDocs,
|
||||
})
|
||||
},
|
||||
|
||||
handleFileDelete (res) {
|
||||
addFileOperationRecord({
|
||||
fileId: res.ossId,
|
||||
fileName: res.name,
|
||||
type: 2,
|
||||
projectId: this.currentRow.projectId,
|
||||
trackId: this.currentRow.trackId,
|
||||
})
|
||||
console.log(this.currentRow, this.dialogRelatedDocs);
|
||||
updateProjectScheduleStep({
|
||||
...this.currentRow,
|
||||
relatedDocs: this.dialogRelatedDocs,
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 计算剩余时间状态
|
||||
* @param {Object} row 行数据
|
||||
* @returns {String} 状态标识(overdue/warning/enough)
|
||||
*/
|
||||
remainStatus (row) {
|
||||
if (!row.planEnd) return '';
|
||||
|
||||
// 处理时间格式,将计划结束时间设为当天23:59:59
|
||||
const endTime = new Date(row.planEnd);
|
||||
endTime.setHours(23, 59, 59, 999);
|
||||
const currentTime = new Date();
|
||||
|
||||
// 计算剩余天数(向上取整)
|
||||
const diffMs = endTime - currentTime;
|
||||
const diffDays = Math.ceil(diffMs / (24 * 60 * 60 * 1000));
|
||||
|
||||
if (diffDays < 0) return 'overdue'; // 逾期
|
||||
if (diffDays <= 3) return 'warning'; // 警告(0-3天)
|
||||
return 'enough'; // 充足(>3天)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取剩余时间显示文本
|
||||
* @param {Object} row 行数据
|
||||
* @returns {String} 显示文本
|
||||
*/
|
||||
getRemainText (row) {
|
||||
if (!row.planEnd) return '未设置';
|
||||
|
||||
// 不修改planEnd的原始时间,直接用row.planEnd
|
||||
const endTime = new Date(row.planEnd);
|
||||
const currentTime = new Date();
|
||||
const diffMs = endTime - currentTime;
|
||||
const diffDays = Math.floor(diffMs / (24 * 60 * 60 * 1000)); // 改为向下取整
|
||||
|
||||
if (row.status == 1) return '等待验收';
|
||||
if (row.status == 2) return '已完成';
|
||||
|
||||
if (diffMs < 0) {
|
||||
// 逾期:计算逾期天数(绝对值向下取整)
|
||||
const overdueDays = Math.floor(Math.abs(diffMs) / (24 * 60 * 60 * 1000));
|
||||
return overdueDays > 0 ? `逾期${overdueDays}天` : '已逾期(不足1天)';
|
||||
} else if (diffDays === 0) {
|
||||
// 剩余不足1天
|
||||
return '剩余不足1天';
|
||||
} else {
|
||||
// 剩余≥1天
|
||||
return `剩余${diffDays}天`;
|
||||
}
|
||||
},
|
||||
|
||||
handleMore (row) {
|
||||
// 初始化表单数据
|
||||
// 中文键json转数组
|
||||
// this.dialogMoreForm.other = row.other || [];
|
||||
const other = []
|
||||
const otherJson = JSON.parse(row.other || '{}');
|
||||
if (row.other) {
|
||||
for (const key in otherJson) {
|
||||
other.push({
|
||||
label: key,
|
||||
value: otherJson[key],
|
||||
})
|
||||
}
|
||||
}
|
||||
this.dialogMoreForm.trackId = row.trackId;
|
||||
this.dialogMoreForm.other = other
|
||||
this.dialogMoreForm.specification = row.specification;
|
||||
this.dialogMoreForm.requirementFile = row.requirementFile;
|
||||
this.dialogMoreForm.supplierId = row.supplierId;
|
||||
// 显示对话框
|
||||
this.dialogMoreVisible = true;
|
||||
},
|
||||
|
||||
handleAddMore () {
|
||||
// 初始化表单数据
|
||||
this.dialogMoreForm.other.push({
|
||||
label: '',
|
||||
value: '',
|
||||
})
|
||||
},
|
||||
|
||||
handleMoreSave () {
|
||||
// 数组转中文键json
|
||||
const o = {}
|
||||
this.dialogMoreForm.other.forEach(item => {
|
||||
if (item.label) {
|
||||
o[item.label] = item.value
|
||||
}
|
||||
})
|
||||
// this.buttonLoading = true;
|
||||
const payload = { ...this.dialogMoreForm, other: JSON.stringify(o) }
|
||||
console.log(payload)
|
||||
// this.loading = true;
|
||||
this.buttonLoading = true;
|
||||
updateProjectScheduleStep(payload).then(res => {
|
||||
this.buttonLoading = false;
|
||||
// this.loading = false;
|
||||
this.$emit("refresh", this.innerData);
|
||||
this.$modal.msgSuccess("更新成功");
|
||||
this.dialogMoreVisible = false;
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 处理申请延期逻辑
|
||||
* @param {Object} row 行数据
|
||||
*/
|
||||
handleApplyDelay (row) {
|
||||
// 这里添加申请延期的逻辑,例如打开弹窗
|
||||
console.log('申请延期:', row);
|
||||
// 初始化表单数据
|
||||
this.dialogApplyDelayForm.trackId = row.trackId;
|
||||
this.dialogApplyDelayForm.delayTo = row.planEnd;
|
||||
this.dialogApplyDelayForm.originalEndTime = row.planEnd;
|
||||
this.dialogApplyDelayForm.applyReason = '';
|
||||
// 显示对话框
|
||||
this.dialogApplyDelayVisible = true;
|
||||
},
|
||||
/**
|
||||
* 提交延期申请
|
||||
*/
|
||||
submitApplyDelay () {
|
||||
// 修改计划结束时间为申请的时间,并更新计划延期申请状态
|
||||
const payload = {
|
||||
trackId: this.dialogApplyDelayForm.trackId,
|
||||
planEnd: this.dialogApplyDelayForm.delayTo,
|
||||
useFlag: 0, // 计划延期申请状态(0:申请中,1:已处理)
|
||||
}
|
||||
// 调用接口更新数据
|
||||
this.loading = true;
|
||||
this.buttonLoading = true;
|
||||
updateProjectScheduleStep(payload).then(response => {
|
||||
this.$emit("refresh", this.innerData);
|
||||
applyProjectScheduleDelay({
|
||||
trackId: this.dialogApplyDelayForm.trackId,
|
||||
expectEndTime: this.dialogApplyDelayForm.delayTo + ' 23:59:59',
|
||||
applyReason: this.dialogApplyDelayForm.applyReason,
|
||||
originalEndTime: this.dialogApplyDelayForm.originalEndTime,
|
||||
})
|
||||
this.$modal.msgSuccess("延期申请已提交");
|
||||
this.dialogApplyDelayVisible = false;
|
||||
}).catch(() => {
|
||||
this.$modal.msgError("提交失败");
|
||||
}).finally(() => {
|
||||
this.buttonLoading = false;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
isSelf (row) {
|
||||
return row.nodeHeader === this.nickName;
|
||||
},
|
||||
changeStatus (row, status) {
|
||||
const payload = { ...row, status };
|
||||
// 如果从0直接改为2,或者从0改为1,需要设置endTime
|
||||
if ((status === 2 || status === 1) && (row.status == 0 || row.status == null)) {
|
||||
payload.endTime = new Date().toISOString().substring(0, 19).replace('T', ' ');
|
||||
}
|
||||
this.loading = true;
|
||||
this.buttonLoading = true;
|
||||
updateProjectScheduleStep(payload).then(response => {
|
||||
this.$emit("refresh", this.innerData);
|
||||
this.$modal.msgSuccess("提交成功");
|
||||
this.dialogImagesVisible = false;
|
||||
}).catch(() => {
|
||||
this.$modal.msgError("提交失败");
|
||||
}).finally(() => {
|
||||
this.buttonLoading = false;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
previewFiles (row) {
|
||||
this.dialogRelatedDocs = row.relatedDocs ? row.relatedDocs : '';
|
||||
this.currentRow = row;
|
||||
this.dialogDocsVisible = true;
|
||||
},
|
||||
uploadRelatedDocs () {
|
||||
this.loading = true;
|
||||
this.buttonLoading = true;
|
||||
updateProjectScheduleStep({ ...this.currentRow, relatedDocs: this.dialogRelatedDocs }).then(response => {
|
||||
this.$emit("refresh", this.innerData);
|
||||
this.$modal.msgSuccess("保存成功");
|
||||
this.dialogDocsVisible = false;
|
||||
}).catch(() => {
|
||||
this.$modal.msgError("保存失败");
|
||||
}).finally(() => {
|
||||
this.buttonLoading = false;
|
||||
this.loading = false;
|
||||
});
|
||||
|
||||
},
|
||||
previewImages (row) {
|
||||
this.dialogRelatedImages = row.relatedImages ? row.relatedImages : '';
|
||||
this.currentRow = row;
|
||||
this.dialogImagesVisible = true;
|
||||
},
|
||||
uploadRelatedImages () {
|
||||
this.loading = true;
|
||||
this.buttonLoading = true;
|
||||
updateProjectScheduleStep({ ...this.currentRow, relatedImages: this.dialogRelatedImages }).then(response => {
|
||||
this.$emit("refresh", this.innerData);
|
||||
this.$modal.msgSuccess("保存成功");
|
||||
this.dialogImagesVisible = false;
|
||||
}).catch(() => {
|
||||
this.$modal.msgError("保存失败");
|
||||
}).finally(() => {
|
||||
this.buttonLoading = false;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
hasEditStatus (row) {
|
||||
const $table = this.$refs.tableRef;
|
||||
return $table ? $table.isEditByRow(row) : false;
|
||||
},
|
||||
showEdit (row) {
|
||||
return !row.isAggregate;
|
||||
},
|
||||
editRowEvent (row) {
|
||||
const $table = this.$refs.tableRef;
|
||||
if ($table) {
|
||||
$table.setEditRow(row);
|
||||
}
|
||||
},
|
||||
agreeDelay (row) {
|
||||
// 同意延期,设置计划延期申请状态为已处理
|
||||
this.loading = true;
|
||||
this.buttonLoading = true;
|
||||
updateProjectScheduleStep({
|
||||
...row,
|
||||
useFlag: 1, // 计划延期申请状态(0:申请中,1:已处理)
|
||||
}).then(response => {
|
||||
this.$emit("refresh", this.innerData);
|
||||
this.loading = false;
|
||||
this.$modal.msgSuccess("同意延期成功");
|
||||
}).catch(() => {
|
||||
this.loading = false;
|
||||
this.$modal.msgError("同意延期失败");
|
||||
}).finally(() => {
|
||||
this.buttonLoading = false;
|
||||
});
|
||||
},
|
||||
saveRowEvent (row) {
|
||||
const $table = this.$refs.tableRef;
|
||||
this.currentRow = row;
|
||||
if ($table) {
|
||||
$table.clearEdit().then(() => {
|
||||
this.loading = true;
|
||||
this.buttonLoading = true;
|
||||
updateProjectScheduleStep({
|
||||
...row,
|
||||
// useFlag: 1, // 计划延期申请状态(0:申请中,1:已处理)
|
||||
}).then(response => {
|
||||
this.$emit("refresh", this.innerData);
|
||||
this.loading = false;
|
||||
this.$modal.msgSuccess("保存成功");
|
||||
}).catch(() => {
|
||||
this.loading = false;
|
||||
}).finally(() => {
|
||||
this.buttonLoading = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
cancelRowEvent () {
|
||||
const $table = this.$refs.tableRef;
|
||||
if ($table) {
|
||||
$table.clearEdit();
|
||||
}
|
||||
},
|
||||
|
||||
addInnerData () {
|
||||
this.addDialogVisible = true;
|
||||
this.dialogAddForm = {
|
||||
stepOrder: this.innerData.length + 1,
|
||||
secondLevelNode: "",
|
||||
tabNode: this.defaultTabNode,
|
||||
firstLevelNode: this.defaultFirstLevelNode,
|
||||
specification: "",
|
||||
nodeHeader: "",
|
||||
relatedDocs: "",
|
||||
relatedImages: "",
|
||||
startTime: "",
|
||||
endTime: "",
|
||||
status: "",
|
||||
};
|
||||
// this.innerData.push({
|
||||
// stepOrder: this.innerData.length + 1,
|
||||
// secondLevelNode: "",
|
||||
// tabNode: this.defaultTabNode,
|
||||
// firstLevelNode: this.defaultFirstLevelNode,
|
||||
// specification: "",
|
||||
// nodeHeader: "",
|
||||
// relatedDocs: "",
|
||||
// relatedImages: "",
|
||||
// startTime: "",
|
||||
// endTime: "",
|
||||
// status: "",
|
||||
// });
|
||||
// this.editRowEvent(this.innerData[this.innerData.length - 1]);
|
||||
},
|
||||
handleAdd () {
|
||||
this.addDialogVisible = false;
|
||||
this.$emit("add", this.dialogAddForm);
|
||||
},
|
||||
handleDelete (row) {
|
||||
this.$emit('delete', row.trackId);
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* 样式部分 */
|
||||
.remain-time-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.text-red {
|
||||
color: #f56c6c;
|
||||
/* 红色:逾期 */
|
||||
}
|
||||
|
||||
.text-orange {
|
||||
color: #e6a23c;
|
||||
/* 橙色:警告(0-3天) */
|
||||
}
|
||||
|
||||
.text-green {
|
||||
color: #67c23a;
|
||||
/* 绿色:充足(>3天) */
|
||||
}
|
||||
|
||||
.text-gray {
|
||||
color: #909399;
|
||||
/* 灰色:未设置 */
|
||||
}
|
||||
|
||||
.delay-btn {
|
||||
color: #409eff;
|
||||
/* 按钮蓝色 */
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
.text-blue {
|
||||
color: #409eff !important;
|
||||
/* 蓝色:申请中 */
|
||||
}
|
||||
</style>
|
||||
111
ruoyi-ui/src/views/oa/project/pace/components/postpone.vue
Normal file
111
ruoyi-ui/src/views/oa/project/pace/components/postpone.vue
Normal file
@@ -0,0 +1,111 @@
|
||||
<template>
|
||||
<div v-loading="loading" class="app-container">
|
||||
<el-table :data="list">
|
||||
<el-table-column label="相关项目" align="center" prop="projectName" show-overflow-tooltip />
|
||||
<el-table-column label="进度步骤" align="center" prop="trackId" show-overflow-tooltip>
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.tabNode }} / {{ scope.row.firstLevelNode }} / {{ scope.row.secondLevelNode }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="申请人姓名" align="center" prop="applyUserName" />
|
||||
<el-table-column label="原时间" align="center" prop="originalEndTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.originalEndTime, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="申请至" align="center" prop="expectEndTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.expectEndTime, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="延期原因" align="center" prop="applyReason" />
|
||||
<el-table-column label="审批人" align="center" prop="approveUserName" />
|
||||
<el-table-column label="审批时间" align="center" prop="approveTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.approveTime, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审批结果" align="center" prop="approveResult">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="scope.row.approveResult == 1">已同意</span>
|
||||
<span v-else-if="scope.row.approveResult == 2">已拒绝</span>
|
||||
<span v-else>待审批</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-check" @click="handleAgree(scope.row)"
|
||||
v-if="scope.row.approveResult == 0">同意延期</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
|
||||
@pagination="getList" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { agreeProjectScheduleDelay, listProjectScheduleDelay } from "@/api/oa/projectScheduleDelay";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
scheduleId: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
queryParams: {
|
||||
scheduleId: this.scheduleId,
|
||||
pageNum: 1,
|
||||
pageSize: 10
|
||||
},
|
||||
list: [],
|
||||
total: 0,
|
||||
loading: true
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
scheduleId: {
|
||||
handler (newVal, oldVal) {
|
||||
if (newVal !== oldVal) {
|
||||
this.queryParams.scheduleId = newVal;
|
||||
this.getList();
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getList () {
|
||||
this.loading = true;
|
||||
listProjectScheduleDelay(this.queryParams).then(res => {
|
||||
this.list = res.rows;
|
||||
this.total = res.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleAgree (row) {
|
||||
this.$modal.confirm("确认同意延期吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(() => {
|
||||
agreeProjectScheduleDelay({
|
||||
delayId: row.delayId,
|
||||
approveResult: 1
|
||||
}).then(response => {
|
||||
this.$modal.success("同意成功");
|
||||
this.$message({
|
||||
message: "已同意延期",
|
||||
type: "success"
|
||||
});
|
||||
this.getList();
|
||||
});
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
421
ruoyi-ui/src/views/oa/project/pace/components/step.vue
Normal file
421
ruoyi-ui/src/views/oa/project/pace/components/step.vue
Normal file
@@ -0,0 +1,421 @@
|
||||
<template>
|
||||
<div v-loading="loading">
|
||||
<el-row>
|
||||
<div>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<div style="font-size: small;">
|
||||
<span style="color:#d0d0d0 ">项目名:</span>
|
||||
<el-popover placement="bottom" trigger="hover" width="800">
|
||||
<template slot="reference">
|
||||
<span style="color: #409eff;">{{ projectName }}</span>
|
||||
</template>
|
||||
<ProjectInfo :info="projectDetail" />
|
||||
</el-popover>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div style="font-size: small;">
|
||||
<span style="color:#d0d0d0 ">项目负责人:</span>
|
||||
<span style="">{{ master }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12">
|
||||
<div style="font-size: small;">
|
||||
<span style="color:#d0d0d0 ">当前进度:</span>
|
||||
<span style="">{{ scheduleSummary }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12">
|
||||
<div style="font-size: small;">
|
||||
<span style="color:#d0d0d0 ">
|
||||
项目状态:
|
||||
</span>
|
||||
<span v-if="isTop" style="color: #ff4d4f;">重点关注</span>
|
||||
<span v-else style="">一般项目</span>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-row>
|
||||
|
||||
<el-divider></el-divider>
|
||||
<div style="position: relative;">
|
||||
<el-radio-group v-model="viewMode" style="position: absolute; top: -40px; left: 0; z-index: 9999;">
|
||||
<el-radio-button label="xmind">思维导图</el-radio-button>
|
||||
<el-radio-button label="table">表格</el-radio-button>
|
||||
</el-radio-group>
|
||||
<el-row v-show="viewMode === 'xmind'">
|
||||
<xmind :list="projectScheduleStepList" @refresh="getList"></xmind>
|
||||
</el-row>
|
||||
<el-row :gutter="20" v-show="viewMode === 'table'">
|
||||
<el-col :span="4">
|
||||
<menu-select ref="menuSelectRef" :tabOption="tabOption" :firstLevelOption="firstLevelOption"
|
||||
@change="handleChange"></menu-select>
|
||||
</el-col>
|
||||
<el-col :span="20">
|
||||
<step-table ref="stepTableRef" :defaultTabNode="defaultTabNode" :defaultFirstLevelNode="defaultFirstLevelNode"
|
||||
:stepList="filterList" @refresh="getList" @add="submitForm" @delete="handleDelete" :editable="true"
|
||||
:master="master">
|
||||
<template slot="extra-buttons">
|
||||
<el-button type="primary" plain icon="el-icon-camera" size="mini" @click="handleOverview">总览</el-button>
|
||||
<el-button type="primary" plain icon="el-icon-refresh" size="mini" @click="getList">刷新</el-button>
|
||||
<el-checkbox style="margin-left: 10px;" v-model="filterParams.onlyMy">只看我的</el-checkbox>
|
||||
<el-checkbox v-model="filterParams.onlyUnfinished">只看未完成</el-checkbox>
|
||||
</template>
|
||||
</step-table>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getProject } from "@/api/oa/project";
|
||||
import { addProjectScheduleStep, delProjectScheduleStep, getProjectScheduleStep, listPage, updateProjectScheduleStep } from "@/api/oa/projectScheduleStep";
|
||||
import ProjectInfo from "@/components/fad-service/ProjectInfo/index.vue";
|
||||
import MenuSelect from "@/views/oa/project/pace/components/MenuSelect.vue";
|
||||
import StepTable from "@/views/oa/project/pace/components/StepTable.vue";
|
||||
import Xmind from "./xmind.vue";
|
||||
|
||||
export default {
|
||||
name: "ProjectScheduleStep",
|
||||
props: {
|
||||
scheduleId: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
projectId: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
master: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
projectName: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
projectStatus: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
isTop: {
|
||||
type: Boolean | Number,
|
||||
default: false
|
||||
},
|
||||
},
|
||||
components: {
|
||||
StepTable,
|
||||
MenuSelect,
|
||||
Xmind,
|
||||
ProjectInfo,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
viewMode: 'xmind',
|
||||
defaultTabNode: "",
|
||||
defaultFirstLevelNode: "",
|
||||
// 按钮loading
|
||||
buttonLoading: false,
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 选中数组
|
||||
ids: [],
|
||||
// 非单个禁用
|
||||
single: true,
|
||||
// 非多个禁用
|
||||
multiple: true,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 项目进度步骤跟踪表格数据
|
||||
projectScheduleStepList: [],
|
||||
// 弹出层标题
|
||||
title: "",
|
||||
// 是否显示弹出层
|
||||
open: false,
|
||||
projectDetail: {},
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 999,
|
||||
accessory: undefined,
|
||||
scheduleId: undefined,
|
||||
stepOrder: undefined,
|
||||
stepName: undefined,
|
||||
planStart: undefined,
|
||||
planEnd: undefined,
|
||||
actualStart: undefined,
|
||||
actualEnd: undefined,
|
||||
status: undefined,
|
||||
},
|
||||
// 筛选参数
|
||||
filterParams: {
|
||||
onlyMy: false,
|
||||
onlyUnfinished: false,
|
||||
},
|
||||
// 表单参数
|
||||
form: {},
|
||||
gridOptions: {
|
||||
// columns: [
|
||||
// { title: '步骤序号', field: 'stepOrder', editRender: { name: 'el-input-number' } },
|
||||
// { title: '步骤名称', field: 'stepName', editRender: { name: 'el-input' } },
|
||||
// { title: '计划开始', field: 'planStart', editRender: { name: 'el-date-picker' } },
|
||||
// { title: '计划完成', field: 'planEnd', editRender: { name: 'el-date-picker' } },
|
||||
// { title: '实际开始', field: 'actualStart', editRender: { name: 'el-date-picker' } },
|
||||
// { title: '实际完成', field: 'actualEnd', editRender: { name: 'el-date-picker' } },
|
||||
// ],
|
||||
// data: this.projectScheduleStepList
|
||||
columns: [
|
||||
{ type: 'seq', width: 70 },
|
||||
{ field: 'name', title: 'Name' },
|
||||
{ field: 'sex', title: 'Sex' },
|
||||
{ field: 'age', title: 'Age' }
|
||||
],
|
||||
data: [
|
||||
{ id: 10001, name: 'Test1', role: 'Develop', sex: 'Man', age: 28, address: 'test abc' },
|
||||
{ id: 10002, name: 'Test2', role: 'Test', sex: 'Women', age: 22, address: 'Guangzhou' },
|
||||
{ id: 10003, name: 'Test3', role: 'PM', sex: 'Man', age: 32, address: 'Shanghai' },
|
||||
{ id: 10004, name: 'Test4', role: 'Designer', sex: 'Women', age: 24, address: 'Shanghai' }
|
||||
]
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
tabOption: {
|
||||
get () {
|
||||
const seen = new Set(); // 用于记录已出现的tabNode
|
||||
const tabNodes = [];
|
||||
// 遍历原数组,只保留首次出现的tabNode,保持原有顺序
|
||||
for (const item of this.projectScheduleStepList) {
|
||||
const currentTabNode = item.tabNode;
|
||||
if (!seen.has(currentTabNode)) {
|
||||
seen.add(currentTabNode);
|
||||
tabNodes.push(currentTabNode);
|
||||
}
|
||||
}
|
||||
return tabNodes.map(item => ({ label: item, value: item }));
|
||||
}
|
||||
},
|
||||
scheduleSummary: {
|
||||
get () {
|
||||
// 统计总数,和状态为2以及1的数量(为2表示已完成,为1表示待验收)
|
||||
const totalCount = this.projectScheduleStepList.length;
|
||||
const completedCount = this.projectScheduleStepList.filter(item => item.status === 2).length;
|
||||
const pendingCount = this.projectScheduleStepList.filter(item => item.status === 1).length;
|
||||
return `已完成(${completedCount})+ 待验收(${pendingCount}) / 总节点数(${totalCount})`;
|
||||
// return this.projectScheduleStepList.find(item => item.scheduleId === this.scheduleId);
|
||||
}
|
||||
},
|
||||
firstLevelOption: {
|
||||
get () {
|
||||
// 用 "firstLevelNode-tabNode" 作为组合键,确保唯一
|
||||
const uniqueMap = {};
|
||||
|
||||
this.projectScheduleStepList.forEach(item => {
|
||||
// 生成组合键(确保 firstLevelNode 和 tabNode 都相同时才重复)
|
||||
const uniqueKey = `${item.firstLevelNode}-${item.tabNode}`;
|
||||
// 存储唯一键对应的完整对象(重复键会覆盖,保留最后一个;若需保留第一个可加判断)
|
||||
uniqueMap[uniqueKey] = item;
|
||||
});
|
||||
|
||||
// 转换为数组,再映射成目标格式
|
||||
const firstLevelNodes = Object.values(uniqueMap).map(item => ({
|
||||
label: item.firstLevelNode, // 显示文本
|
||||
value: item.firstLevelNode, // 选中值
|
||||
tabNode: item.tabNode // 关联的 tabNode(此时必然正确)
|
||||
}));
|
||||
|
||||
return firstLevelNodes;
|
||||
}
|
||||
},
|
||||
filterList: {
|
||||
get () {
|
||||
// 筛选参数
|
||||
const { onlyMy, onlyUnfinished } = this.filterParams;
|
||||
if (!this.defaultTabNode || !this.defaultFirstLevelNode) {
|
||||
return this.projectScheduleStepList.filter(item => {
|
||||
if (onlyMy) {
|
||||
console.log(item.nodeHeader, this.$store.getters);
|
||||
return item.nodeHeader === this.$store.getters.nickName;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.filter(item => {
|
||||
if (onlyUnfinished) {
|
||||
return item.status !== 2;
|
||||
}
|
||||
return true;
|
||||
});;
|
||||
}
|
||||
|
||||
console.log(onlyMy, onlyUnfinished);
|
||||
return this.projectScheduleStepList.filter(item => item.tabNode === this.defaultTabNode && item.firstLevelNode === this.defaultFirstLevelNode)
|
||||
.filter(item => {
|
||||
if (onlyMy) {
|
||||
console.log(item.nodeHeader, this.$store.getters);
|
||||
return item.nodeHeader === this.$store.getters.nickName;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.filter(item => {
|
||||
if (onlyUnfinished) {
|
||||
return item.status !== 2;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
scheduleId: {
|
||||
handler (newVal) {
|
||||
this.queryParams.scheduleId = newVal;
|
||||
this.getList();
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
projectId: {
|
||||
handler (newVal) {
|
||||
getProject(newVal).then(r => {
|
||||
console.log(r);
|
||||
this.projectDetail = r.data
|
||||
})
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
/** 查询项目进度步骤跟踪列表 */
|
||||
getList () {
|
||||
this.loading = true;
|
||||
listPage(this.queryParams).then(response => {
|
||||
this.projectScheduleStepList = response.rows;
|
||||
this.total = response.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleOverview () {
|
||||
this.$refs.menuSelectRef.clear();
|
||||
},
|
||||
handleChange (val) {
|
||||
this.defaultTabNode = val.tabNode;
|
||||
this.defaultFirstLevelNode = val.firstLevelNode;
|
||||
},
|
||||
// 取消按钮
|
||||
cancel () {
|
||||
this.open = false;
|
||||
this.reset();
|
||||
},
|
||||
// 表单重置
|
||||
reset () {
|
||||
this.form = {
|
||||
trackId: undefined,
|
||||
accessory: undefined,
|
||||
scheduleId: this.scheduleId,
|
||||
stepOrder: undefined,
|
||||
stepName: undefined,
|
||||
planStart: undefined,
|
||||
planEnd: undefined,
|
||||
actualStart: undefined,
|
||||
actualEnd: undefined,
|
||||
status: undefined,
|
||||
createBy: undefined,
|
||||
createTime: undefined,
|
||||
updateBy: undefined,
|
||||
updateTime: undefined,
|
||||
delFlag: undefined
|
||||
};
|
||||
this.resetForm("form");
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery () {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery () {
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
// 多选框选中数据
|
||||
handleSelectionChange (selection) {
|
||||
this.ids = selection.map(item => item.trackId)
|
||||
this.single = selection.length !== 1
|
||||
this.multiple = !selection.length
|
||||
},
|
||||
/** 新增按钮操作 */
|
||||
handleAdd () {
|
||||
this.reset();
|
||||
this.open = true;
|
||||
this.title = "添加项目进度步骤跟踪";
|
||||
},
|
||||
/** 修改按钮操作 */
|
||||
handleUpdate (row) {
|
||||
this.loading = true;
|
||||
this.reset();
|
||||
const trackId = row.trackId || this.ids
|
||||
getProjectScheduleStep(trackId).then(response => {
|
||||
this.loading = false;
|
||||
this.form = response.data;
|
||||
this.open = true;
|
||||
this.title = "修改项目进度步骤跟踪";
|
||||
});
|
||||
},
|
||||
/** 提交按钮 */
|
||||
submitForm (row) {
|
||||
this.buttonLoading = true;
|
||||
this.loading = true;
|
||||
if (this.form.trackId != null) {
|
||||
updateProjectScheduleStep({
|
||||
...row,
|
||||
scheduleId: this.scheduleId,
|
||||
}).then(response => {
|
||||
this.$modal.msgSuccess("修改成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
}).finally(() => {
|
||||
this.buttonLoading = false;
|
||||
});
|
||||
} else {
|
||||
addProjectScheduleStep({
|
||||
...row,
|
||||
originalEndTime: row.planEnd,
|
||||
scheduleId: this.scheduleId,
|
||||
}).then(response => {
|
||||
this.$modal.msgSuccess("新增成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
}).finally(() => {
|
||||
this.buttonLoading = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
/** 删除按钮操作 */
|
||||
handleDelete (trackIds) {
|
||||
this.$modal.confirm('是否确认删除项目进度步骤跟踪编号为"' + trackIds + '"的数据项?').then(() => {
|
||||
this.loading = true;
|
||||
return delProjectScheduleStep(trackIds);
|
||||
}).then(() => {
|
||||
this.loading = false;
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("删除成功");
|
||||
}).catch(() => {
|
||||
}).finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
/** 导出按钮操作 */
|
||||
handleExport () {
|
||||
this.download('oa/projectScheduleStep/export', {
|
||||
...this.queryParams
|
||||
}, `projectScheduleStep_${new Date().getTime()}.xlsx`)
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
365
ruoyi-ui/src/views/oa/project/pace/components/xmind.vue
Normal file
365
ruoyi-ui/src/views/oa/project/pace/components/xmind.vue
Normal file
@@ -0,0 +1,365 @@
|
||||
<template>
|
||||
<div class="xmind-box">
|
||||
<div class='action-panel'>
|
||||
<!-- <el-button type="primary" icon="el-icon-plus" @click="handleAdd">新增</el-button> -->
|
||||
<el-button type="primary" icon="el-icon-refresh" @click="handleRefresh">刷新</el-button>
|
||||
<!-- <el-button type="primary" icon="el-icon-view" @click="handleRefresh">详情</el-button>
|
||||
<el-button type="primary" icon="el-icon-edit" @click="handleRefresh">编辑</el-button>
|
||||
<el-button type="primary" icon="el-icon-folder" @click="previewFiles(currentNode)">文件</el-button>
|
||||
<el-button type="primary" icon="el-icon-picture" @click="previewImages(currentNode)">图片</el-button> -->
|
||||
</div>
|
||||
<div class="xmind-container" ref="chart" style="width: 100%; height: 800px;"></div>
|
||||
<!-- 新增:三级节点点击弹窗-查看完整信息 -->
|
||||
<el-dialog title="节点详情信息" :visible.sync="dialogVisible" width="1200px" center append-to-body>
|
||||
<el-form>
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="进度类别" prop="name">
|
||||
<el-input v-model="currentNode.tabNode" placeholder="请输入进度类型"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="一级分类" prop="firstLevelNode">
|
||||
<el-input v-model="currentNode.firstLevelNode" placeholder="请输入一级分类"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="步骤名称" prop="secondLevelNode">
|
||||
<el-input v-model="currentNode.secondLevelNode" placeholder="请输入步骤名称"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12">
|
||||
<el-form-item label="负责人" prop="nodeHeader">
|
||||
<el-input v-model="currentNode.nodeHeader" placeholder="请输入负责人"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12">
|
||||
<el-form-item label="规格需求" prop="specification">
|
||||
<el-input v-model="currentNode.specification" placeholder="规格需求"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="24">
|
||||
<el-form-item label="成果资料" prop="relatedDocs">
|
||||
<file-upload @success="handleFileSuccess" @delete="handleFileDelete" v-model="currentNode.relatedDocs"
|
||||
placeholder="成果资料"></file-upload>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="24">
|
||||
<el-form-item label="相关图片" prop="relatedImages">
|
||||
<image-upload v-model="currentNode.relatedImages" placeholder="相关图片"></image-upload>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="24">
|
||||
<el-form-item label="需求资料" prop="requirementFile">
|
||||
<file-upload @success="handleFileSuccess" @delete="handleFileDelete" v-model="currentNode.requirementFile"
|
||||
placeholder="需求资料"></file-upload>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<!-- <div class="dialog-content">
|
||||
<div class="node-title">{{ currentNode.name }}</div>
|
||||
<div class="node-info-item" v-for="(val, key) in currentNode.value" :key="key">
|
||||
<span class="label">{{ key }}:</span>
|
||||
<span class="value">{{ val || '无' }}</span>
|
||||
</div>
|
||||
</div> -->
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button type="primary" @click="handleSubmit">提交修改</el-button>
|
||||
<el-button type="primary" @click="dialogVisible = false">关闭</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { addFileOperationRecord } from '@/api/oa/fileOperationRecord';
|
||||
import { updateProjectScheduleStep } from "@/api/oa/projectScheduleStep";
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
export default {
|
||||
name: "Xmind",
|
||||
props: {
|
||||
list: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
chartInstance: null, // 保存图表实例,用于后续重绘/销毁
|
||||
dialogVisible: false, // 新增:弹窗显示隐藏开关
|
||||
currentNode: {}, // 新增:存储当前点击的三级节点完整数据
|
||||
clickEvent: null, // 新增:存储点击事件句柄,用于销毁解绑
|
||||
users: [],
|
||||
supplierList: [],
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
// 监听列表数据变化,自动更新图表
|
||||
list: {
|
||||
deep: true,
|
||||
handler () {
|
||||
if (this.chartInstance) {
|
||||
this.initChart();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.initChart();
|
||||
// 监听窗口大小变化,自适应重绘
|
||||
window.addEventListener('resize', this.resizeChart);
|
||||
},
|
||||
beforeDestroy () {
|
||||
window.removeEventListener('resize', this.resizeChart);
|
||||
// 新增:解绑Echarts点击事件,防止内存泄漏
|
||||
if (this.chartInstance && this.clickEvent) {
|
||||
this.chartInstance.off('click', this.clickEvent);
|
||||
}
|
||||
// 销毁图表实例,防止内存泄漏
|
||||
this.chartInstance?.dispose();
|
||||
},
|
||||
methods: {
|
||||
// 优化:增加防抖处理-窗口自适应,避免频繁触发
|
||||
resizeChart () {
|
||||
this.chartInstance?.resize()
|
||||
},
|
||||
|
||||
handleSubmit () {
|
||||
updateProjectScheduleStep(this.currentNode).then(response => {
|
||||
this.$modal.msgSuccess("修改成功");
|
||||
this.dialogVisible = false;
|
||||
this.handleRefresh();
|
||||
});
|
||||
},
|
||||
|
||||
handleRefresh () {
|
||||
this.$emit('refresh')
|
||||
},
|
||||
|
||||
handleFileSuccess (resList, res) {
|
||||
addFileOperationRecord({
|
||||
fileId: res.ossId,
|
||||
fileName: res.name,
|
||||
type: 1,
|
||||
projectId: this.currentNode.projectId,
|
||||
trackId: this.currentNode.trackId,
|
||||
})
|
||||
console.log(this.currentNode, this.currentNode.relatedDocs);
|
||||
updateProjectScheduleStep({
|
||||
...this.currentNode,
|
||||
relatedDocs: this.currentNode.relatedDocs,
|
||||
})
|
||||
},
|
||||
|
||||
handleFileDelete (res) {
|
||||
addFileOperationRecord({
|
||||
fileId: res.ossId,
|
||||
fileName: res.name,
|
||||
type: 2,
|
||||
projectId: this.currentNode.projectId,
|
||||
trackId: this.currentNode.trackId,
|
||||
})
|
||||
console.log(this.currentNode, this.currentNode.relatedDocs);
|
||||
updateProjectScheduleStep({
|
||||
...this.currentNode,
|
||||
relatedDocs: this.currentNode.relatedDocs,
|
||||
})
|
||||
},
|
||||
|
||||
// 核心方法:把扁平数组 转为 ECharts树图需要的嵌套树形结构
|
||||
transformToTreeData (list) {
|
||||
if (!list.length) return { name: '暂无项目数据', children: [] };
|
||||
|
||||
// 1. 获取项目名称(所有数据是同一个项目,取第一条即可)
|
||||
const projectName = list[0].projectName || '项目进度树图';
|
||||
// 2. 构建层级Map,去重+归集子节点
|
||||
const levelMap = new Map();
|
||||
list.forEach(item => {
|
||||
const firstLevel = item.firstLevelNode || '未分类一级节点';
|
||||
const secondLevel = item.secondLevelNode || '未分类二级节点';
|
||||
// 状态映射:0=未开始(蓝色) 2=已完成(绿色) 其他=进行中(橙色),可根据业务调整
|
||||
const statusText = item.status === 0 ? '待开始' : item.status === 2 ? '✅已完成' : '🔵进行中';
|
||||
const statusColor = item.status === 0 ? '#409EFF' : item.status === 2 ? '#67C23A' : '#E6A23C';
|
||||
|
||||
// 组装节点数据:显示名称+业务信息+样式
|
||||
const nodeData = {
|
||||
name: secondLevel,
|
||||
itemStyle: { color: statusColor },
|
||||
label: { color: statusColor },
|
||||
// 自定义业务数据,鼠标悬浮时显示
|
||||
value: {
|
||||
...item,
|
||||
负责人: item.nodeHeader || '无',
|
||||
状态: statusText,
|
||||
计划完成: item.planEnd || '无',
|
||||
说明: item.specification || '无'
|
||||
}
|
||||
};
|
||||
|
||||
// 归集一级节点和二级节点
|
||||
if (!levelMap.has(firstLevel)) {
|
||||
levelMap.set(firstLevel, []);
|
||||
}
|
||||
levelMap.get(firstLevel).push(nodeData);
|
||||
});
|
||||
|
||||
// 3. 组装最终的树形结构
|
||||
const treeChildren = Array.from(levelMap).map(([firstName, children]) => ({
|
||||
name: firstName,
|
||||
itemStyle: { color: '#303133' }, // 一级节点统一深灰色
|
||||
children: children
|
||||
}));
|
||||
|
||||
return {
|
||||
name: projectName,
|
||||
itemStyle: { color: '#1890FF' }, // 根节点(项目名)蓝色高亮
|
||||
children: treeChildren
|
||||
};
|
||||
},
|
||||
|
||||
// 初始化图表
|
||||
initChart () {
|
||||
// 初始化图表实例
|
||||
if (!this.chartInstance) {
|
||||
this.chartInstance = echarts.init(this.$refs.chart);
|
||||
}
|
||||
// 重要:先解绑已有点击事件,防止多次绑定导致弹窗多次触发
|
||||
if (this.clickEvent) {
|
||||
this.chartInstance.off('click', this.clickEvent);
|
||||
}
|
||||
// 转换数据格式
|
||||
const treeData = this.transformToTreeData(this.list);
|
||||
// 设置图表配置项
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: ({ data }) => {
|
||||
// 鼠标悬浮展示完整业务信息
|
||||
let tip = `<div style="font-size:14px"><b>${data.name}</b></div>`;
|
||||
if (data.value) {
|
||||
// Object.keys(data.value).forEach(key => {
|
||||
// tip += `<div>${key}:${data.value[key]}</div>`;
|
||||
// });
|
||||
tip += `<div>负责人:${data.value.nodeHeader || '无'}</div>`;
|
||||
tip += `<div>规格需求:${data.value.specification || '无'}</div>`;
|
||||
tip += `<div>任务状态:${data.value.statusText || '无'}</div>`;
|
||||
tip += `<div>计划完成:${data.value.planEnd || '无'}</div>`;
|
||||
}
|
||||
return tip;
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'tree', // 树图核心类型
|
||||
data: [treeData],
|
||||
symbol: 'circle', // 节点形状:圆点
|
||||
symbolSize: 6, // 节点大小
|
||||
orient: 'LR', // 树图展开方向:LR=从左到右(脑图样式),可选 TB(从上到下)
|
||||
initialTreeDepth: 2, // 默认展开层级:2级
|
||||
roam: true, // 开启鼠标拖拽+滚轮缩放
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: 12,
|
||||
fontWeight: 500,
|
||||
position: 'left', // 文字在节点左侧
|
||||
verticalAlign: 'middle'
|
||||
},
|
||||
lineStyle: {
|
||||
width: 1.2,
|
||||
curveness: 0.3, // 连接线曲率,0=直线,0.3=轻微曲线
|
||||
color: '#ccc'
|
||||
},
|
||||
emphasis: {
|
||||
focus: 'descendant' // 鼠标悬浮时高亮当前节点及子节点
|
||||
},
|
||||
expandAndCollapse: true, // 开启节点折叠/展开功能
|
||||
animationDuration: 300 // 展开折叠动画时长
|
||||
}
|
||||
]
|
||||
};
|
||||
// 渲染图表
|
||||
this.chartInstance?.setOption(option, true);
|
||||
|
||||
// ========== 核心新增:绑定ECharts点击事件,只对三级节点生效 ==========
|
||||
this.clickEvent = (params) => {
|
||||
console.log(params);
|
||||
const { data, treeAncestors } = params;
|
||||
// ✅ 核心判断:treeAncestors是当前节点的「所有上级节点数组」
|
||||
// 根节点(项目名) → 一级节点 → 三级节点 :treeAncestors.length = 2 → 精准匹配第三级节点
|
||||
// 层级对应关系:根节点(0级) → 一级分类(1级) → 业务节点(3级/你要的三级)
|
||||
if (treeAncestors.length === 4) {
|
||||
console.log(data);
|
||||
this.currentNode = { ...data.value }; // 深拷贝当前节点完整数据
|
||||
this.dialogVisible = true; // 打开弹窗
|
||||
}
|
||||
};
|
||||
// 绑定点击事件
|
||||
this.chartInstance.on('click', this.clickEvent);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.xmind-box {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.action-panel {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.xmind-container {
|
||||
background: #fafafa;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
/* 新增:弹窗内部样式美化 */
|
||||
:deep(.dialog-content) {
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
:deep(.node-title) {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #1890FF;
|
||||
text-align: center;
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
:deep(.node-info-item) {
|
||||
display: flex;
|
||||
padding: 6px 0;
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
:deep(.label) {
|
||||
width: 80px;
|
||||
color: #666;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
:deep(.value) {
|
||||
flex: 1;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
:deep(.dialog-footer) {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
237
ruoyi-ui/src/views/oa/project/pace/file.vue
Normal file
237
ruoyi-ui/src/views/oa/project/pace/file.vue
Normal file
@@ -0,0 +1,237 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="80px">
|
||||
<el-form-item label="文件标识" prop="fileId">
|
||||
<el-input v-model="queryParams.fileId" placeholder="请输入文件标识" clearable @keyup.enter.native="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="文件名" prop="fileName">
|
||||
<el-input v-model="queryParams.fileName" placeholder="请输入文件名" clearable @keyup.enter.native="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="操作人" prop="operatorName">
|
||||
<el-input v-model="queryParams.operatorName" placeholder="请输入操作人姓名" clearable
|
||||
@keyup.enter.native="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="相关项目" prop="projectName">
|
||||
<project-select v-model="queryParams.projectId" placeholder="请选择相关项目" clearable
|
||||
@keyup.enter.native="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table v-loading="loading" :data="fileOperationRecordList" @selection-change="handleSelectionChange">
|
||||
<el-table-column label="文件标识" align="center" prop="fileId">
|
||||
<template slot-scope="scope">
|
||||
<el-tag type="primary" @click="previewFile(scope.row)">{{ scope.row.fileId }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="文件名" align="center" prop="fileName" />
|
||||
<el-table-column label="操作人姓名" align="center" prop="operatorName" />
|
||||
<el-table-column label="操作时间" align="center" prop="createTime" />
|
||||
<el-table-column label="相关项目" align="center" prop="projectName" />
|
||||
<!-- 相关进度 -->
|
||||
<el-table-column label="相关进度" align="center" show-overflow-tooltip>
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.tabNode }} / {{ scope.row.firstLevelNode }} / {{ scope.row.secondLevelNode }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作类型" align="center" prop="type">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.type === 1 ? '上传' : '删除' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" show-overflow-tooltip>
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" size="mini" icon="el-icon-view" @click="previewFile(scope.row)">预览</el-button>
|
||||
<el-button type="text" size="mini" icon="el-icon-download" @click="handleDownload(scope.row)">下载</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
|
||||
@pagination="getList" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { addFileOperationRecord, delFileOperationRecord, getFileOperationRecord, listFileOperationRecord, updateFileOperationRecord } from "@/api/oa/fileOperationRecord";
|
||||
import { listByIds } from "@/api/system/oss";
|
||||
import ProjectSelect from "@/components/fad-service/ProjectSelect/index.vue";
|
||||
|
||||
export default {
|
||||
name: "FileOperationRecord",
|
||||
components: {
|
||||
ProjectSelect,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
// 按钮loading
|
||||
buttonLoading: false,
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 选中数组
|
||||
ids: [],
|
||||
// 非单个禁用
|
||||
single: true,
|
||||
// 非多个禁用
|
||||
multiple: true,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// OA文件操作记录表格数据
|
||||
fileOperationRecordList: [],
|
||||
// 弹出层标题
|
||||
title: "",
|
||||
// 是否显示弹出层
|
||||
open: false,
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
fileId: undefined,
|
||||
fileName: undefined,
|
||||
operatorName: undefined,
|
||||
type: undefined,
|
||||
projectId: undefined,
|
||||
},
|
||||
// 表单参数
|
||||
form: {},
|
||||
// 表单校验
|
||||
rules: {
|
||||
}
|
||||
};
|
||||
},
|
||||
created () {
|
||||
this.getList();
|
||||
},
|
||||
inject: ['$folder'],
|
||||
methods: {
|
||||
/** 查询OA文件操作记录列表 */
|
||||
getList () {
|
||||
this.loading = true;
|
||||
listFileOperationRecord(this.queryParams).then(response => {
|
||||
this.fileOperationRecordList = response.rows;
|
||||
this.total = response.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
previewFile (row) {
|
||||
this.loading = true;
|
||||
const folder = this.$folder();
|
||||
listByIds(row.fileId).then(res => {
|
||||
folder.previewSimple(res.data[0]);
|
||||
}).finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleDownload (row) {
|
||||
this.$download.oss(row.fileId)
|
||||
},
|
||||
// 取消按钮
|
||||
cancel () {
|
||||
this.open = false;
|
||||
this.reset();
|
||||
},
|
||||
// 表单重置
|
||||
reset () {
|
||||
this.form = {
|
||||
recordId: undefined,
|
||||
fileId: undefined,
|
||||
fileName: undefined,
|
||||
operatorName: undefined,
|
||||
type: undefined,
|
||||
remark: undefined,
|
||||
createBy: undefined,
|
||||
createTime: undefined,
|
||||
updateBy: undefined,
|
||||
updateTime: undefined,
|
||||
delFlag: undefined
|
||||
};
|
||||
this.resetForm("form");
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery () {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery () {
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
// 多选框选中数据
|
||||
handleSelectionChange (selection) {
|
||||
this.ids = selection.map(item => item.recordId)
|
||||
this.single = selection.length !== 1
|
||||
this.multiple = !selection.length
|
||||
},
|
||||
/** 新增按钮操作 */
|
||||
handleAdd () {
|
||||
this.reset();
|
||||
this.open = true;
|
||||
this.title = "添加OA文件操作记录";
|
||||
},
|
||||
/** 修改按钮操作 */
|
||||
handleUpdate (row) {
|
||||
this.loading = true;
|
||||
this.reset();
|
||||
const recordId = row.recordId || this.ids
|
||||
getFileOperationRecord(recordId).then(response => {
|
||||
this.loading = false;
|
||||
this.form = response.data;
|
||||
this.open = true;
|
||||
this.title = "修改OA文件操作记录";
|
||||
});
|
||||
},
|
||||
/** 提交按钮 */
|
||||
submitForm () {
|
||||
this.$refs["form"].validate(valid => {
|
||||
if (valid) {
|
||||
this.buttonLoading = true;
|
||||
if (this.form.recordId != null) {
|
||||
updateFileOperationRecord(this.form).then(response => {
|
||||
this.$modal.msgSuccess("修改成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
}).finally(() => {
|
||||
this.buttonLoading = false;
|
||||
});
|
||||
} else {
|
||||
addFileOperationRecord(this.form).then(response => {
|
||||
this.$modal.msgSuccess("新增成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
}).finally(() => {
|
||||
this.buttonLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
/** 删除按钮操作 */
|
||||
handleDelete (row) {
|
||||
const recordIds = row.recordId || this.ids;
|
||||
this.$modal.confirm('是否确认删除OA文件操作记录编号为"' + recordIds + '"的数据项?').then(() => {
|
||||
this.loading = true;
|
||||
return delFileOperationRecord(recordIds);
|
||||
}).then(() => {
|
||||
this.loading = false;
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("删除成功");
|
||||
}).catch(() => {
|
||||
}).finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
/** 导出按钮操作 */
|
||||
handleExport () {
|
||||
this.download('oa/fileOperationRecord/export', {
|
||||
...this.queryParams
|
||||
}, `fileOperationRecord_${new Date().getTime()}.xlsx`)
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
383
ruoyi-ui/src/views/oa/project/pace/index.vue
Normal file
383
ruoyi-ui/src/views/oa/project/pace/index.vue
Normal file
@@ -0,0 +1,383 @@
|
||||
<template>
|
||||
<div class="app-container" v-loading="loading">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
|
||||
<el-form-item label="项目名称" prop="projectId">
|
||||
<project-select v-model="queryParams.projectId" placeholder="请选择项目" clearable />
|
||||
<!-- <el-select v-model="queryParams.projectId" filterable placeholder="请选择">
|
||||
<el-option v-for="item in projects" :key="item.projectId" :label="item.projectName" :value="item.projectId">
|
||||
</el-option>
|
||||
</el-select> -->
|
||||
</el-form-item>
|
||||
<el-form-item label="项目编号" prop="projectNum">
|
||||
<el-input v-model="queryParams.projectNum" placeholder="请输入项目编号" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="贸易类型" prop="tradeType">
|
||||
<el-select v-model="queryParams.tradeType" placeholder="请选择项目类型" clearable>
|
||||
<el-option v-for="dict in dict.type.sys_trade_type" :key="dict.value" :label="dict.label"
|
||||
:value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="项目代号" prop="projectCode">
|
||||
<el-select v-model="queryParams.projectCode" placeholder="请选择代号类型" style="width: 100%" filterable
|
||||
@change="handleQuery">
|
||||
<el-option v-for="dict in dict.type.sys_project_code" :key="dict.value" :label="dict.label"
|
||||
:value="dict.value">
|
||||
<span style="float: left">{{ dict.label }}</span>
|
||||
<span style="float: right; color: #8492a6; font-size: 13px">{{ dict.value }}</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
|
||||
<el-form-item label="项目周期">
|
||||
<el-date-picker v-model="searchTime" type="daterange" start-placeholder="开始日期" end-placeholder="结束日期"
|
||||
:default-time="['00:00:00', '23:59:59']">
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="负责人">
|
||||
<el-select filterable allow-add v-model="queryParams.steward" @change="handleStatusChange(scope.row)">
|
||||
<el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.nickName">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="优质筛选">
|
||||
<el-switch v-model="queryParams.prePay" active-text="开" active-value="0.1" inactive-value="0"
|
||||
@change="selectPrePay" inactive-text="关">
|
||||
</el-switch>
|
||||
</el-form-item> -->
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd">绑定进度
|
||||
</el-button>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="1.5">
|
||||
<el-button type="danger" plain icon="el-icon-delete" size="mini" :disabled="multiple" @click="handleDelete">删除
|
||||
</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-table v-loading="loading" :data="scheduleList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="代号" prop="projectCode" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-tag v-if="scope.row.projectCode == null" type="danger">无</el-tag>
|
||||
<el-tag v-else>{{ scope.row.projectCode }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="项目名称" prop="projectName" show-overflow-tooltip>
|
||||
<template slot-scope="scope">
|
||||
<span v-if="scope.row.prePay > 0">⭐</span>
|
||||
<span>{{ scope.row.projectName }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="项目编号" prop="projectNum"></el-table-column>
|
||||
<el-table-column label="负责人" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-select filterable allow-add v-model="scope.row.steward" @change="handleStatusChange(scope.row)">
|
||||
<el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.nickName">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" align="center">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="scope.row.isTop" style="color: #ff4d4f;">重点关注</span>
|
||||
<span v-else style="">一般项目</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="延期进度数" prop="delayCount" />
|
||||
<el-table-column label="未完成进度" prop="unFinishCount" />
|
||||
<el-table-column label="完成状态" align="center" prop="sortNum">
|
||||
<template slot-scope="scope">
|
||||
<el-select size="mini" v-model="scope.row.status" placeholder="请选择完成状态"
|
||||
@change="handleStatusChange(scope.row)">
|
||||
<el-option label="进行中" :value="1"></el-option>
|
||||
<el-option label="已完成" :value="2"></el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
|
||||
</el-table-column>
|
||||
<el-table-column label="开始时间" align="center" prop="startTime">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.startTime, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleDetail(scope.row)">进度详情
|
||||
</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-time" @click="handlePostpone(scope.row)">延期记录
|
||||
</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-check"
|
||||
v-if="scope.row.schedulePercentage === 100 && scope.row.status !== 2"
|
||||
@click="handleComplete(scope.row)">完成任务
|
||||
</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)">删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
|
||||
@pagination="getList" />
|
||||
</el-row>
|
||||
|
||||
<el-drawer title="进度详情" :visible.sync="detailDrawer" direction="btt" size="90%" :before-close="closeDetailShow">
|
||||
<div style="padding:0 20px">
|
||||
<project-schedule-step :scheduleId="scheduleDetail.scheduleId" :master="scheduleDetail.functionary"
|
||||
:projectName="scheduleDetail.projectName" :projectStatus="scheduleDetail.projectStatus"
|
||||
:isTop="scheduleDetail.isTop" :projectId="scheduleDetail.projectId" />
|
||||
</div>
|
||||
</el-drawer>
|
||||
|
||||
<FormDialog v-model="addDialog" :projects="projects" @save="handleSave" />
|
||||
|
||||
<el-drawer title="延期记录" :visible.sync="postponeDrawer" direction="btt" size="90%">
|
||||
<postpone :scheduleId="scheduleDetail.scheduleId" />
|
||||
</el-drawer>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { listProject } from "@/api/oa/project";
|
||||
import { addByProjectId, delProjectSchedule, listProjectSchedule, updateProjectSchedule } from "@/api/oa/projectSchedule";
|
||||
import { listUser } from "@/api/system/user";
|
||||
import ProjectSelect from "@/components/fad-service/ProjectSelect/index.vue";
|
||||
import UserSelect from "@/components/UserSelect/index.vue";
|
||||
import FormDialog from "./components/FormDialog.vue";
|
||||
import Postpone from "./components/postpone.vue";
|
||||
import ProjectScheduleStep from "./components/step.vue";
|
||||
|
||||
export default {
|
||||
name: "Schedule",
|
||||
dicts: ['sys_project_status', 'sys_trade_type', 'sys_project_type', 'sys_project_code'],
|
||||
components: {
|
||||
UserSelect,
|
||||
ProjectScheduleStep,
|
||||
FormDialog,
|
||||
ProjectSelect,
|
||||
Postpone
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
loading: false,
|
||||
detailDrawer: false,
|
||||
fileShow: false,
|
||||
addDialog: false,
|
||||
multiple: true,
|
||||
form: {},
|
||||
projects: [],
|
||||
scheduleList: [],
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
startTime: '',
|
||||
endTime: '',
|
||||
steward: ''
|
||||
},
|
||||
total: 0,
|
||||
searchTime: [],
|
||||
showSearch: true,
|
||||
recentProjects: [],
|
||||
scheduleDetail: {},
|
||||
userList: [],
|
||||
postponeDrawer: false
|
||||
};
|
||||
|
||||
},
|
||||
mounted () {
|
||||
this.currentUser = this.$store.state.user
|
||||
this.getList();
|
||||
this.getProjectList();
|
||||
this.getAllUser();
|
||||
const cache = localStorage.getItem('oa_recent_projects');
|
||||
if (cache) {
|
||||
this.recentProjects = JSON.parse(cache);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 关闭细节窗口
|
||||
closeDetailShow (done) {
|
||||
this.getList();
|
||||
done()
|
||||
},
|
||||
getAllUser () {
|
||||
listUser({ pageNum: 1, pageSize: 999 }).then(res => {
|
||||
this.userList = res.rows
|
||||
})
|
||||
},
|
||||
handleStatusChange (row) {
|
||||
updateProjectSchedule(row).then(res => {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("操作成功")
|
||||
})
|
||||
},
|
||||
// 绑定进度
|
||||
handleAdd () {
|
||||
this.addDialog = true;
|
||||
},
|
||||
selectPrePay () {
|
||||
this.handleQuery()
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery () {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
getDateStr (date) {
|
||||
if (!date) {
|
||||
return ''
|
||||
}
|
||||
return this.parseTime(date, '{y}-{m}-{d} {h}:{i}:{s}')
|
||||
},
|
||||
getList () {
|
||||
this.loading = true
|
||||
|
||||
console.log(this.queryParams, this.searchTime)
|
||||
/* 日期搜索条件 */
|
||||
if (this.searchTime && this.searchTime.length) {
|
||||
this.queryParams.startTime = this.getDateStr(this.searchTime[0])
|
||||
this.queryParams.endTime = this.getDateStr(this.searchTime[1])
|
||||
}
|
||||
|
||||
listProjectSchedule(this.queryParams).then(res => {
|
||||
this.scheduleList = res.rows
|
||||
this.total = res.total
|
||||
let cache = JSON.parse(localStorage.getItem('oa_recent_projects') || '[]')
|
||||
const id2idx = new Map()
|
||||
cache.forEach((item, idx) => id2idx.set(item.scheduleId, idx))
|
||||
for (const row of res.rows) {
|
||||
const hit = id2idx.get(row.scheduleId)
|
||||
if (hit !== undefined) {
|
||||
cache[hit] = row
|
||||
} else {
|
||||
cache.unshift(row)
|
||||
}
|
||||
}
|
||||
cache = cache.slice(0, 2)
|
||||
/* 2‑5 回写缓存 + 更新响应式数据 */
|
||||
localStorage.setItem('oa_recent_projects', JSON.stringify(cache))
|
||||
this.recentProjects = cache
|
||||
/* 3. 结束 loading */
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
getProjectList () {
|
||||
let params = {
|
||||
pageNum: 1,
|
||||
pageSize: 999,
|
||||
}
|
||||
listProject(params).then(res => {
|
||||
this.projects = res.rows
|
||||
})
|
||||
},
|
||||
/** 保存处理*/
|
||||
handleSave (payload) {
|
||||
addByProjectId(payload).then(response => {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("绑定成功")
|
||||
|
||||
})
|
||||
},
|
||||
handleDetail (row) {
|
||||
// 把当前项目放到数组最前面,去重
|
||||
const list = [row, ...this.recentProjects.filter(p => p.projectId !== row.projectId)];
|
||||
// 只保留前 2 条
|
||||
this.recentProjects = list.slice(0, 2);
|
||||
// 持久化
|
||||
localStorage.setItem('oa_recent_projects', JSON.stringify(this.recentProjects));
|
||||
|
||||
this.getScheduleDetail(row)
|
||||
},
|
||||
handlePostpone (row) {
|
||||
// 打开延期记录弹窗
|
||||
this.postponeDrawer = true
|
||||
this.scheduleDetail = row
|
||||
},
|
||||
getScheduleDetail (row) {
|
||||
this.scheduleDetail = row
|
||||
this.detailDrawer = true
|
||||
},
|
||||
|
||||
/* ========= 左侧主列表删除(支持单删或批量 ids) ========= */
|
||||
handleDelete (row) {
|
||||
/* 支持:row.scheduleId 或 this.ids = '1,2,3' */
|
||||
const scheduleIds = row.scheduleId || this.ids
|
||||
|
||||
this.$modal.confirm(`将会删除进度编号为 "${scheduleIds}" 的数据项, 同时会删除所有的子进度且无法找回 !!! 是否继续? `)
|
||||
.then(() => {
|
||||
this.loading = true
|
||||
return delProjectSchedule(scheduleIds)
|
||||
})
|
||||
.then(() => {
|
||||
/* 刷新左侧列表并提示 */
|
||||
this.getList()
|
||||
this.$modal.msgSuccess('删除成功')
|
||||
})
|
||||
},
|
||||
|
||||
// 多选框选中数据
|
||||
handleSelectionChange (selection) {
|
||||
this.ids = selection.map(item => item.scheduleId)
|
||||
this.single = selection.length !== 1
|
||||
this.multiple = !selection.length
|
||||
},
|
||||
|
||||
/** 重置按钮操作 */
|
||||
resetQuery () {
|
||||
this.searchTime = [];
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.uploader {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.file-item {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.file-row {
|
||||
display: flex;
|
||||
font-size: small;
|
||||
color: #414141;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.file-icon {
|
||||
width: 40px;
|
||||
text-align: center;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.file-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.file-meta {
|
||||
color: #909399;
|
||||
font-size: 12px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.file-actions el-button+el-button {
|
||||
margin-left: 4px;
|
||||
}
|
||||
</style>
|
||||
306
ruoyi-ui/src/views/oa/project/pace/myStep.vue
Normal file
306
ruoyi-ui/src/views/oa/project/pace/myStep.vue
Normal file
@@ -0,0 +1,306 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="100px">
|
||||
<!-- 状态 -->
|
||||
<!-- <el-form-item label="状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择状态">
|
||||
<el-option label="进行中" value="0" />
|
||||
<el-option label="待验收" value="1" />
|
||||
<el-option label="已完成" value="2" />
|
||||
</el-select>
|
||||
</el-form-item> -->
|
||||
<!-- 计划结束时间 -->
|
||||
<el-form-item label="计划结束时间" prop="planEnd">
|
||||
<el-date-picker v-model="queryParams.planEndRange" type="daterange" value-format="yyyy-MM-dd"
|
||||
placeholder="选择计划结束时间" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport">导出</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="projectScheduleStepList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="所属项目" align="center" prop="projectName" />
|
||||
<el-table-column label="步骤名称" align="center" prop="tabNode">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.tabNode }}/ {{ scope.row.firstLevelNode }} / {{ scope.row.secondLevelNode }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="计划完成" align="center" prop="planEnd" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.planEnd, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="剩余时间" align="center" prop="endTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="scope.row.status == 2" style="color: #36d399">{{ parseTime(scope.row.endTime, '{y}-{m}-{d}') }}
|
||||
已完成</span>
|
||||
<span v-else-if="scope.row.status == 1" style="color: #4096ff">待验收</span>
|
||||
<span v-else>
|
||||
<!-- 调用计算方法获取剩余天数 -->
|
||||
<template v-if="scope.row.planEnd">
|
||||
<span v-if="calcRemainingDays(scope.row.planEnd) < 0" style="color: #f56c6c">已逾期 {{
|
||||
-calcRemainingDays(scope.row.planEnd) }} 天</span>
|
||||
<span v-else-if="calcRemainingDays(scope.row.planEnd) <= 3" style="color: #e6a23c">临期 | 还剩 {{
|
||||
calcRemainingDays(scope.row.planEnd) }} 天</span>
|
||||
<span v-else style="color: #67c23a">还剩 {{ calcRemainingDays(scope.row.planEnd) }} 天</span>
|
||||
</template>
|
||||
<span v-else>未设置计划日期</span>
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" align="center" prop="status">
|
||||
<!-- 0进行中,1待验收,2已完成 -->
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.status === 0 ? '进行中' : (scope.row.status === 1 ? '待验收' : '已完成') }}</span>
|
||||
</template>
|
||||
</el-table-column>>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
|
||||
@pagination="getList" />
|
||||
|
||||
<!-- 添加或修改项目进度步骤跟踪对话框 -->
|
||||
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { addProjectScheduleStep, delProjectScheduleStep, getProjectScheduleStep, listMyPage as listProjectScheduleStep, updateProjectScheduleStep } from "@/api/oa/projectScheduleStep";
|
||||
import { listUser } from "@/api/system/user";
|
||||
|
||||
export default {
|
||||
name: "ProjectScheduleStep",
|
||||
data () {
|
||||
return {
|
||||
// 按钮loading
|
||||
buttonLoading: false,
|
||||
// 用户列表loading
|
||||
userLoading: false,
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 选中数组
|
||||
ids: [],
|
||||
// 非单个禁用
|
||||
single: true,
|
||||
// 非多个禁用
|
||||
multiple: true,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 项目进度步骤跟踪表格数据
|
||||
projectScheduleStepList: [],
|
||||
// 弹出层标题
|
||||
title: "",
|
||||
// 是否显示弹出层
|
||||
open: false,
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
scheduleId: undefined,
|
||||
stepName: undefined,
|
||||
planEnd: undefined,
|
||||
status: undefined,
|
||||
tabNode: undefined,
|
||||
firstLevelNode: undefined,
|
||||
secondLevelNode: undefined,
|
||||
endTime: undefined,
|
||||
nodeHeader: undefined,
|
||||
relatedDocs: undefined,
|
||||
relatedImages: undefined,
|
||||
supplierId: undefined,
|
||||
requirementFile: undefined,
|
||||
specification: undefined,
|
||||
planEndRange: undefined,
|
||||
},
|
||||
// 表单参数
|
||||
form: {},
|
||||
// 表单校验
|
||||
rules: {
|
||||
},
|
||||
userList: [],
|
||||
};
|
||||
},
|
||||
created () {
|
||||
this.getList();
|
||||
this.getListUser();
|
||||
},
|
||||
methods: {
|
||||
/** 查询项目进度步骤跟踪列表 */
|
||||
/** 计算剩余天数(当前日期 - 计划完成日期) */
|
||||
calcRemainingDays (planEnd) {
|
||||
if (!planEnd) return '无计划日期';
|
||||
// 转换计划日期为时间戳(忽略时分秒,按日期当天结束计算)
|
||||
const planEndTime = new Date(planEnd).setHours(23, 59, 59, 999);
|
||||
const currentTime = new Date().getTime();
|
||||
// 计算天数差值(向上取整)
|
||||
const diffDays = Math.ceil((planEndTime - currentTime) / (1000 * 60 * 60 * 24));
|
||||
return diffDays;
|
||||
},
|
||||
/** 查询用户列表 */
|
||||
getListUser () {
|
||||
this.userLoading = true;
|
||||
listUser({ pageSize: 999 }).then(response => {
|
||||
this.userList = response.rows;
|
||||
this.userLoading = false;
|
||||
});
|
||||
},
|
||||
getList () {
|
||||
this.loading = true;
|
||||
const endTime = this.queryParams.planEndRange ? this.queryParams.planEndRange[1] + ' 23:59:59' : undefined;
|
||||
const startTime = this.queryParams.planEndRange ? this.queryParams.planEndRange[0] + ' 00:00:00' : undefined;
|
||||
const { planEndRange, ...querys } = {
|
||||
...this.queryParams,
|
||||
startTime,
|
||||
endTime,
|
||||
}
|
||||
listProjectScheduleStep(querys).then(response => {
|
||||
this.projectScheduleStepList = response.rows;
|
||||
this.total = response.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
// 取消按钮
|
||||
cancel () {
|
||||
this.open = false;
|
||||
this.reset();
|
||||
},
|
||||
// 表单重置
|
||||
reset () {
|
||||
this.form = {
|
||||
trackId: undefined,
|
||||
accessory: undefined,
|
||||
scheduleId: undefined,
|
||||
stepOrder: undefined,
|
||||
stepName: undefined,
|
||||
planStart: undefined,
|
||||
planEnd: undefined,
|
||||
actualStart: undefined,
|
||||
actualEnd: undefined,
|
||||
status: undefined,
|
||||
createBy: undefined,
|
||||
createTime: undefined,
|
||||
updateBy: undefined,
|
||||
updateTime: undefined,
|
||||
delFlag: undefined,
|
||||
header: undefined,
|
||||
useFlag: undefined,
|
||||
batchId: undefined,
|
||||
tabNode: undefined,
|
||||
firstLevelNode: undefined,
|
||||
secondLevelNode: undefined,
|
||||
startTime: undefined,
|
||||
originalEndTime: undefined,
|
||||
endTime: undefined,
|
||||
nodeHeader: undefined,
|
||||
relatedDocs: undefined,
|
||||
relatedImages: undefined,
|
||||
supplierId: undefined,
|
||||
requirementFile: undefined,
|
||||
other: undefined,
|
||||
specification: undefined,
|
||||
sortNum: undefined
|
||||
};
|
||||
this.resetForm("form");
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery () {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery () {
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
// 多选框选中数据
|
||||
handleSelectionChange (selection) {
|
||||
this.ids = selection.map(item => item.trackId)
|
||||
this.single = selection.length !== 1
|
||||
this.multiple = !selection.length
|
||||
},
|
||||
/** 新增按钮操作 */
|
||||
handleAdd () {
|
||||
this.reset();
|
||||
this.open = true;
|
||||
this.title = "添加项目进度步骤跟踪";
|
||||
},
|
||||
/** 修改按钮操作 */
|
||||
handleUpdate (row) {
|
||||
this.loading = true;
|
||||
this.reset();
|
||||
const trackId = row.trackId || this.ids
|
||||
getProjectScheduleStep(trackId).then(response => {
|
||||
this.loading = false;
|
||||
this.form = response.data;
|
||||
this.open = true;
|
||||
this.title = "修改项目进度步骤跟踪";
|
||||
});
|
||||
},
|
||||
/** 提交按钮 */
|
||||
submitForm () {
|
||||
this.$refs["form"].validate(valid => {
|
||||
if (valid) {
|
||||
this.buttonLoading = true;
|
||||
if (this.form.trackId != null) {
|
||||
updateProjectScheduleStep(this.form).then(response => {
|
||||
this.$modal.msgSuccess("修改成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
}).finally(() => {
|
||||
this.buttonLoading = false;
|
||||
});
|
||||
} else {
|
||||
addProjectScheduleStep(this.form).then(response => {
|
||||
this.$modal.msgSuccess("新增成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
}).finally(() => {
|
||||
this.buttonLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
/** 删除按钮操作 */
|
||||
handleDelete (row) {
|
||||
const trackIds = row.trackId || this.ids;
|
||||
this.$modal.confirm('是否确认删除项目进度步骤跟踪编号为"' + trackIds + '"的数据项?').then(() => {
|
||||
this.loading = true;
|
||||
return delProjectScheduleStep(trackIds);
|
||||
}).then(() => {
|
||||
this.loading = false;
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("删除成功");
|
||||
}).catch(() => {
|
||||
}).finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
/** 导出按钮操作 */
|
||||
handleExport () {
|
||||
this.download('oa/projectScheduleStep/export', {
|
||||
...this.queryParams
|
||||
}, `projectScheduleStep_${new Date().getTime()}.xlsx`)
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
301
ruoyi-ui/src/views/oa/project/pace/postpone.vue
Normal file
301
ruoyi-ui/src/views/oa/project/pace/postpone.vue
Normal file
@@ -0,0 +1,301 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="100px">
|
||||
<el-form-item label="申请人姓名">
|
||||
<el-input v-model="queryParams.applyUserName" placeholder="请输入申请人姓名" clearable />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="相关项目">
|
||||
<project-select v-model="queryParams.projectId" placeholder="请选择相关项目" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table v-loading="loading" :data="projectScheduleDelayList">
|
||||
<el-table-column label="相关项目" align="center" prop="projectName" show-overflow-tooltip />
|
||||
<el-table-column label="进度步骤" align="center" prop="trackId" show-overflow-tooltip>
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.tabNode }} / {{ scope.row.firstLevelNode }} / {{ scope.row.secondLevelNode }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="申请人姓名" align="center" prop="applyUserName" />
|
||||
<el-table-column label="原时间" align="center" prop="originalEndTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.originalEndTime, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="申请至" align="center" prop="expectEndTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.expectEndTime, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="延期原因" align="center" prop="applyReason" />
|
||||
<el-table-column label="审批人" align="center" prop="approveUserName" />
|
||||
<el-table-column label="审批时间" align="center" prop="approveTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.approveTime, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审批结果" align="center" prop="approveResult">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="scope.row.approveResult == 1">已同意</span>
|
||||
<span v-else-if="scope.row.approveResult == 2">已拒绝</span>
|
||||
<span v-else>待审批</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-check" @click="handleAgree(scope.row)"
|
||||
v-if="scope.row.approveResult == 0">同意延期</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
|
||||
@pagination="getList" />
|
||||
|
||||
<!-- 添加或修改项目进度步骤延期记录对话框 -->
|
||||
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item label="关联的进度步骤跟踪记录主键" prop="trackId">
|
||||
<el-input v-model="form.trackId" placeholder="请输入关联的进度步骤跟踪记录主键" />
|
||||
</el-form-item>
|
||||
<el-form-item label="申请人姓名" prop="applyUserName">
|
||||
<el-input v-model="form.applyUserName" placeholder="请输入申请人姓名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="申请提交时间" prop="applyTime">
|
||||
<el-date-picker clearable v-model="form.applyTime" type="datetime" value-format="yyyy-MM-dd HH:mm:ss"
|
||||
placeholder="请选择申请提交时间">
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item label="延期申请原因" prop="applyReason">
|
||||
<el-input v-model="form.applyReason" type="textarea" placeholder="请输入内容" />
|
||||
</el-form-item>
|
||||
<el-form-item label="原计划结束时间" prop="originalEndTime">
|
||||
<el-date-picker clearable v-model="form.originalEndTime" type="datetime" value-format="yyyy-MM-dd HH:mm:ss"
|
||||
placeholder="请选择原计划结束时间">
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item label="申请延期后的预计结束时间" prop="expectEndTime">
|
||||
<el-date-picker clearable v-model="form.expectEndTime" type="datetime" value-format="yyyy-MM-dd HH:mm:ss"
|
||||
placeholder="请选择申请延期后的预计结束时间">
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item label="审批人姓名" prop="approveUserName">
|
||||
<el-input v-model="form.approveUserName" placeholder="请输入审批人姓名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="审批完成时间" prop="approveTime">
|
||||
<el-date-picker clearable v-model="form.approveTime" type="datetime" value-format="yyyy-MM-dd HH:mm:ss"
|
||||
placeholder="请选择审批完成时间">
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item label="审批结果:0-待审批 1-审批通过 2-审批驳回" prop="approveResult">
|
||||
<el-input v-model="form.approveResult" placeholder="请输入审批结果:0-待审批 1-审批通过 2-审批驳回" />
|
||||
</el-form-item>
|
||||
<el-form-item label="审批备注/驳回原因" prop="approveRemark">
|
||||
<el-input v-model="form.approveRemark" type="textarea" placeholder="请输入内容" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注(如需)" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { addProjectScheduleDelay, agreeProjectScheduleDelay, delProjectScheduleDelay, getProjectScheduleDelay, listProjectScheduleDelay, updateProjectScheduleDelay } from "@/api/oa/projectScheduleDelay";
|
||||
import ProjectSelect from "@/components/fad-service/ProjectSelect";
|
||||
|
||||
export default {
|
||||
name: "ProjectScheduleDelay",
|
||||
components: {
|
||||
ProjectSelect
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
// 按钮loading
|
||||
buttonLoading: false,
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 选中数组
|
||||
ids: [],
|
||||
// 非单个禁用
|
||||
single: true,
|
||||
// 非多个禁用
|
||||
multiple: true,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 项目进度步骤延期记录表格数据
|
||||
projectScheduleDelayList: [],
|
||||
// 弹出层标题
|
||||
title: "",
|
||||
// 是否显示弹出层
|
||||
open: false,
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
applyUserName: undefined,
|
||||
projectId: undefined,
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
},
|
||||
// 表单参数
|
||||
form: {},
|
||||
// 表单校验
|
||||
rules: {
|
||||
}
|
||||
};
|
||||
},
|
||||
created () {
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
/** 查询项目进度步骤延期记录列表 */
|
||||
getList () {
|
||||
this.loading = true;
|
||||
listProjectScheduleDelay(this.queryParams).then(response => {
|
||||
this.projectScheduleDelayList = response.rows;
|
||||
this.total = response.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleAgree (row) {
|
||||
this.$modal.confirm("确认同意延期吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(() => {
|
||||
agreeProjectScheduleDelay({
|
||||
delayId: row.delayId,
|
||||
approveResult: 1
|
||||
}).then(response => {
|
||||
this.$modal.success("同意成功");
|
||||
this.$message({
|
||||
message: "已同意延期",
|
||||
type: "success"
|
||||
});
|
||||
this.getList();
|
||||
});
|
||||
});
|
||||
},
|
||||
// 取消按钮
|
||||
cancel () {
|
||||
this.open = false;
|
||||
this.reset();
|
||||
},
|
||||
// 表单重置
|
||||
reset () {
|
||||
this.form = {
|
||||
delayId: undefined,
|
||||
trackId: undefined,
|
||||
applyUserName: undefined,
|
||||
applyTime: undefined,
|
||||
applyReason: undefined,
|
||||
originalEndTime: undefined,
|
||||
expectEndTime: undefined,
|
||||
approveUserName: undefined,
|
||||
approveTime: undefined,
|
||||
approveResult: undefined,
|
||||
approveRemark: undefined,
|
||||
delayStatus: undefined,
|
||||
createBy: undefined,
|
||||
createTime: undefined,
|
||||
updateBy: undefined,
|
||||
updateTime: undefined,
|
||||
delFlag: undefined,
|
||||
remark: undefined
|
||||
};
|
||||
this.resetForm("form");
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery () {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery () {
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
// 多选框选中数据
|
||||
handleSelectionChange (selection) {
|
||||
this.ids = selection.map(item => item.delayId)
|
||||
this.single = selection.length !== 1
|
||||
this.multiple = !selection.length
|
||||
},
|
||||
/** 新增按钮操作 */
|
||||
handleAdd () {
|
||||
this.reset();
|
||||
this.open = true;
|
||||
this.title = "添加项目进度步骤延期记录";
|
||||
},
|
||||
/** 修改按钮操作 */
|
||||
handleUpdate (row) {
|
||||
this.loading = true;
|
||||
this.reset();
|
||||
const delayId = row.delayId || this.ids
|
||||
getProjectScheduleDelay(delayId).then(response => {
|
||||
this.loading = false;
|
||||
this.form = response.data;
|
||||
this.open = true;
|
||||
this.title = "修改项目进度步骤延期记录";
|
||||
});
|
||||
},
|
||||
/** 提交按钮 */
|
||||
submitForm () {
|
||||
this.$refs["form"].validate(valid => {
|
||||
if (valid) {
|
||||
this.buttonLoading = true;
|
||||
if (this.form.delayId != null) {
|
||||
updateProjectScheduleDelay(this.form).then(response => {
|
||||
this.$modal.msgSuccess("修改成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
}).finally(() => {
|
||||
this.buttonLoading = false;
|
||||
});
|
||||
} else {
|
||||
addProjectScheduleDelay(this.form).then(response => {
|
||||
this.$modal.msgSuccess("新增成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
}).finally(() => {
|
||||
this.buttonLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
/** 删除按钮操作 */
|
||||
handleDelete (row) {
|
||||
const delayIds = row.delayId || this.ids;
|
||||
this.$modal.confirm('是否确认删除项目进度步骤延期记录编号为"' + delayIds + '"的数据项?').then(() => {
|
||||
this.loading = true;
|
||||
return delProjectScheduleDelay(delayIds);
|
||||
}).then(() => {
|
||||
this.loading = false;
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("删除成功");
|
||||
}).catch(() => {
|
||||
}).finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
/** 导出按钮操作 */
|
||||
handleExport () {
|
||||
this.download('oa/projectScheduleDelay/export', {
|
||||
...this.queryParams
|
||||
}, `projectScheduleDelay_${new Date().getTime()}.xlsx`)
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
342
ruoyi-ui/src/views/oa/project/pace/step.vue
Normal file
342
ruoyi-ui/src/views/oa/project/pace/step.vue
Normal file
@@ -0,0 +1,342 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="100px">
|
||||
<el-form-item label="节点负责人" prop="nodeHeader">
|
||||
<el-select v-loading="userLoading" v-model="queryParams.nodeHeader" placeholder="请选择节点负责人" clearable filterable>
|
||||
<el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.nickName" />
|
||||
</el-select>
|
||||
<!-- <el-input v-model="queryParams.nodeHeader" placeholder="请输入节点负责人" clearable @keyup.enter.native="handleQuery" /> -->
|
||||
</el-form-item>
|
||||
<!-- 状态 -->
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择状态">
|
||||
<el-option label="进行中" value="0" />
|
||||
<el-option label="待验收" value="1" />
|
||||
<el-option label="已完成" value="2" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- 计划结束时间 -->
|
||||
<el-form-item label="计划结束时间" prop="planEnd">
|
||||
<el-date-picker v-model="queryParams.planEndRange" type="daterange" value-format="yyyy-MM-dd"
|
||||
placeholder="选择计划结束时间" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<!-- <el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="success" plain icon="el-icon-edit" size="mini" :disabled="single"
|
||||
@click="handleUpdate">修改</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="danger" plain icon="el-icon-delete" size="mini" :disabled="multiple"
|
||||
@click="handleDelete">删除</el-button>
|
||||
</el-col> -->
|
||||
<el-col :span="1.5">
|
||||
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport">导出</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="projectScheduleStepList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<!-- <el-table-column label="跟踪记录主键" align="center" prop="trackId" v-if="false" /> -->
|
||||
<el-table-column label="所属项目" align="center" prop="projectName" />
|
||||
<el-table-column label="步骤名称" align="center" prop="tabNode">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.tabNode }}/ {{ scope.row.firstLevelNode }} / {{ scope.row.secondLevelNode }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- <el-table-column label="一级节点" align="center" prop="firstLevelNode" />
|
||||
<el-table-column label="二级节点" align="center" prop="secondLevelNode" /> -->
|
||||
<el-table-column label="计划完成" align="center" prop="planEnd" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ parseTime(scope.row.planEnd, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="剩余时间" align="center" prop="endTime" width="180">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="scope.row.status == 2" style="color: #36d399">{{ parseTime(scope.row.endTime, '{y}-{m}-{d}') }}
|
||||
已完成</span>
|
||||
<span v-else-if="scope.row.status == 1" style="color: #4096ff">待验收</span>
|
||||
<span v-else>
|
||||
<!-- 调用计算方法获取剩余天数 -->
|
||||
<template v-if="scope.row.planEnd">
|
||||
<span v-if="calcRemainingDays(scope.row.planEnd) < 0" style="color: #f56c6c">已逾期 {{
|
||||
-calcRemainingDays(scope.row.planEnd) }} 天</span>
|
||||
<span v-else-if="calcRemainingDays(scope.row.planEnd) <= 3" style="color: #e6a23c">临期 | 还剩 {{
|
||||
calcRemainingDays(scope.row.planEnd) }} 天</span>
|
||||
<span v-else style="color: #67c23a">还剩 {{ calcRemainingDays(scope.row.planEnd) }} 天</span>
|
||||
</template>
|
||||
<span v-else>未设置计划日期</span>
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" align="center" prop="status">
|
||||
<!-- 0进行中,1待验收,2已完成 -->
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.status === 0 ? '进行中' : (scope.row.status === 1 ? '待验收' : '已完成') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="负责人" align="center" prop="nodeHeader" />
|
||||
<!-- <el-table-column label="相关资料" align="center" prop="relatedDocs" /> -->
|
||||
<!-- <el-table-column label="相关图片" align="center" prop="relatedImages" width="100">
|
||||
<template slot-scope="scope">
|
||||
<image-preview :src="scope.row.relatedImages" :width="50" :height="50" />
|
||||
</template>
|
||||
</el-table-column> -->
|
||||
<!-- <el-table-column label="供应商ID" align="center" prop="supplierName" /> -->
|
||||
<!-- <el-table-column label="需求文件" align="center" prop="requirementFile" /> -->
|
||||
<!-- <el-table-column label="规范说明" align="center" prop="specification" /> -->
|
||||
<!-- <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)">修改</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column> -->
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
|
||||
@pagination="getList" />
|
||||
|
||||
<!-- 添加或修改项目进度步骤跟踪对话框 -->
|
||||
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { addProjectScheduleStep, delProjectScheduleStep, getProjectScheduleStep, listPage as listProjectScheduleStep, updateProjectScheduleStep } from "@/api/oa/projectScheduleStep";
|
||||
import { listUser } from "@/api/system/user";
|
||||
|
||||
export default {
|
||||
name: "ProjectScheduleStep",
|
||||
data () {
|
||||
return {
|
||||
// 按钮loading
|
||||
buttonLoading: false,
|
||||
// 用户列表loading
|
||||
userLoading: false,
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 选中数组
|
||||
ids: [],
|
||||
// 非单个禁用
|
||||
single: true,
|
||||
// 非多个禁用
|
||||
multiple: true,
|
||||
// 显示搜索条件
|
||||
showSearch: true,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 项目进度步骤跟踪表格数据
|
||||
projectScheduleStepList: [],
|
||||
// 弹出层标题
|
||||
title: "",
|
||||
// 是否显示弹出层
|
||||
open: false,
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
scheduleId: undefined,
|
||||
stepName: undefined,
|
||||
planEnd: undefined,
|
||||
status: undefined,
|
||||
tabNode: undefined,
|
||||
firstLevelNode: undefined,
|
||||
secondLevelNode: undefined,
|
||||
endTime: undefined,
|
||||
nodeHeader: undefined,
|
||||
relatedDocs: undefined,
|
||||
relatedImages: undefined,
|
||||
supplierId: undefined,
|
||||
requirementFile: undefined,
|
||||
specification: undefined,
|
||||
planEndRange: undefined,
|
||||
},
|
||||
// 表单参数
|
||||
form: {},
|
||||
// 表单校验
|
||||
rules: {
|
||||
},
|
||||
userList: [],
|
||||
};
|
||||
},
|
||||
created () {
|
||||
this.getList();
|
||||
this.getListUser();
|
||||
},
|
||||
methods: {
|
||||
/** 查询项目进度步骤跟踪列表 */
|
||||
/** 计算剩余天数(当前日期 - 计划完成日期) */
|
||||
calcRemainingDays (planEnd) {
|
||||
if (!planEnd) return '无计划日期';
|
||||
// 转换计划日期为时间戳(忽略时分秒,按日期当天结束计算)
|
||||
const planEndTime = new Date(planEnd).setHours(23, 59, 59, 999);
|
||||
const currentTime = new Date().getTime();
|
||||
// 计算天数差值(向上取整)
|
||||
const diffDays = Math.ceil((planEndTime - currentTime) / (1000 * 60 * 60 * 24));
|
||||
return diffDays;
|
||||
},
|
||||
/** 查询用户列表 */
|
||||
getListUser () {
|
||||
this.userLoading = true;
|
||||
listUser({ pageSize: 999 }).then(response => {
|
||||
this.userList = response.rows;
|
||||
this.userLoading = false;
|
||||
});
|
||||
},
|
||||
getList () {
|
||||
this.loading = true;
|
||||
const endTime = this.queryParams.planEndRange ? this.queryParams.planEndRange[1] + ' 23:59:59' : undefined;
|
||||
const startTime = this.queryParams.planEndRange ? this.queryParams.planEndRange[0] + ' 00:00:00' : undefined;
|
||||
const { planEndRange, ...querys } = {
|
||||
...this.queryParams,
|
||||
startTime,
|
||||
endTime,
|
||||
}
|
||||
listProjectScheduleStep(querys).then(response => {
|
||||
this.projectScheduleStepList = response.rows;
|
||||
this.total = response.total;
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
// 取消按钮
|
||||
cancel () {
|
||||
this.open = false;
|
||||
this.reset();
|
||||
},
|
||||
// 表单重置
|
||||
reset () {
|
||||
this.form = {
|
||||
trackId: undefined,
|
||||
accessory: undefined,
|
||||
scheduleId: undefined,
|
||||
stepOrder: undefined,
|
||||
stepName: undefined,
|
||||
planStart: undefined,
|
||||
planEnd: undefined,
|
||||
actualStart: undefined,
|
||||
actualEnd: undefined,
|
||||
status: undefined,
|
||||
createBy: undefined,
|
||||
createTime: undefined,
|
||||
updateBy: undefined,
|
||||
updateTime: undefined,
|
||||
delFlag: undefined,
|
||||
header: undefined,
|
||||
useFlag: undefined,
|
||||
batchId: undefined,
|
||||
tabNode: undefined,
|
||||
firstLevelNode: undefined,
|
||||
secondLevelNode: undefined,
|
||||
startTime: undefined,
|
||||
originalEndTime: undefined,
|
||||
endTime: undefined,
|
||||
nodeHeader: undefined,
|
||||
relatedDocs: undefined,
|
||||
relatedImages: undefined,
|
||||
supplierId: undefined,
|
||||
requirementFile: undefined,
|
||||
other: undefined,
|
||||
specification: undefined,
|
||||
sortNum: undefined
|
||||
};
|
||||
this.resetForm("form");
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery () {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
/** 重置按钮操作 */
|
||||
resetQuery () {
|
||||
this.resetForm("queryForm");
|
||||
this.handleQuery();
|
||||
},
|
||||
// 多选框选中数据
|
||||
handleSelectionChange (selection) {
|
||||
this.ids = selection.map(item => item.trackId)
|
||||
this.single = selection.length !== 1
|
||||
this.multiple = !selection.length
|
||||
},
|
||||
/** 新增按钮操作 */
|
||||
handleAdd () {
|
||||
this.reset();
|
||||
this.open = true;
|
||||
this.title = "添加项目进度步骤跟踪";
|
||||
},
|
||||
/** 修改按钮操作 */
|
||||
handleUpdate (row) {
|
||||
this.loading = true;
|
||||
this.reset();
|
||||
const trackId = row.trackId || this.ids
|
||||
getProjectScheduleStep(trackId).then(response => {
|
||||
this.loading = false;
|
||||
this.form = response.data;
|
||||
this.open = true;
|
||||
this.title = "修改项目进度步骤跟踪";
|
||||
});
|
||||
},
|
||||
/** 提交按钮 */
|
||||
submitForm () {
|
||||
this.$refs["form"].validate(valid => {
|
||||
if (valid) {
|
||||
this.buttonLoading = true;
|
||||
if (this.form.trackId != null) {
|
||||
updateProjectScheduleStep(this.form).then(response => {
|
||||
this.$modal.msgSuccess("修改成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
}).finally(() => {
|
||||
this.buttonLoading = false;
|
||||
});
|
||||
} else {
|
||||
addProjectScheduleStep(this.form).then(response => {
|
||||
this.$modal.msgSuccess("新增成功");
|
||||
this.open = false;
|
||||
this.getList();
|
||||
}).finally(() => {
|
||||
this.buttonLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
/** 删除按钮操作 */
|
||||
handleDelete (row) {
|
||||
const trackIds = row.trackId || this.ids;
|
||||
this.$modal.confirm('是否确认删除项目进度步骤跟踪编号为"' + trackIds + '"的数据项?').then(() => {
|
||||
this.loading = true;
|
||||
return delProjectScheduleStep(trackIds);
|
||||
}).then(() => {
|
||||
this.loading = false;
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("删除成功");
|
||||
}).catch(() => {
|
||||
}).finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
/** 导出按钮操作 */
|
||||
handleExport () {
|
||||
this.download('oa/projectScheduleStep/export', {
|
||||
...this.queryParams
|
||||
}, `projectScheduleStep_${new Date().getTime()}.xlsx`)
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
Reference in New Issue
Block a user