Browse Source

优化项目试图

master
陈裕财 5 years ago
parent
commit
cd20c597a1
  1. 24
      src/router/routes_xm.js
  2. 19
      src/views/xm/core/xmIteration/XmIterationForProjectComplex.vue
  3. 444
      src/views/xm/core/xmIteration/XmIterationInfo.vue
  4. 61
      src/views/xm/core/xmIteration/XmIterationInfoRoute.vue
  5. 6
      src/views/xm/core/xmMenu/XmMenuMng.vue
  6. 0
      src/views/xm/core/xmProduct/XmProductForProjectComplex.vue
  7. 445
      src/views/xm/core/xmProduct/XmProductInfo.vue
  8. 61
      src/views/xm/core/xmProduct/XmProductInfoRoute.vue
  9. 156
      src/views/xm/core/xmProduct/XmProductMng.vue
  10. 888
      src/views/xm/core/xmProduct/XmProductOverview.vue
  11. 130
      src/views/xm/core/xmProduct/XmProductOverviewComplex.vue
  12. 59
      src/views/xm/core/xmProject/XmProjectForLink.vue
  13. 12
      src/views/xm/core/xmProject/XmProjectInfo.vue
  14. 2
      src/views/xm/core/xmTask/XmTaskMng.vue

24
src/router/routes_xm.js

@ -77,6 +77,17 @@ export default {
]
},
{
path: '/xm/core/xmProduct/XmProductInfoRoute',
component: _import('xm/core/xmProduct/XmProductInfoRoute'),
name: 'XmProductInfoRoute',
iconCls: 'fa el-icon-menu',
meta: {
title: '产品管理',
icon: 'project',
hidden:true
},
},
{
path: '/xm/core',
component: Layout,
@ -89,13 +100,23 @@ export default {
// leaf: true,//只有一个节点
children: [
{ path: 'xmProduct/XmProductComplex', component: _import('xm/core/xmProduct/XmProductComplex'), name: 'XmProductComplex', meta: { title: '产品总览' }},
{ path: 'xmProduct/XmProductMng', component: _import('xm/core/xmProduct/XmProductMng'), name: 'XmProductMng', meta: { title: '产品管理' }},
{ path: 'xmMenu/XmMenuMng', component: _import('xm/core/xmMenu/XmMenuMng'), name: 'XmMenuMng', meta: { title: '故事管理' }},
{ path: 'xmMenuExchange/XmMenuExchangeMng', component: _import('xm/core/xmMenuExchange/XmMenuExchangeMng'), name: 'XmMenuExchangeMng', meta: { title: '互动评论' }},
{ path: 'xmMenuTemplate/XmMenuTemplateMng', component: _import('xm/core/xmMenuTemplate/XmMenuTemplateMng'), name: 'XmMenuTemplateMng', meta: { title: '故事模板' }},
]
},,
{
path: '/xm/core/xmIteration/XmIterationInfoRoute',
component: _import('xm/core/xmIteration/XmIterationInfoRoute'),
name: 'XmIterationInfoRoute',
iconCls: 'fa el-icon-menu',
meta: {
title: '迭代管理',
icon: 'project',
hidden:true
},
},
{
path: '/xm/core',
@ -108,7 +129,6 @@ export default {
},
// leaf: true,//只有一个节点
children: [
{ path: 'xmIteration/XmIterationComplex', component: _import('xm/core/xmIteration/XmIterationComplex'), name: 'XmIterationComplex', meta: { title: '迭代总览' }},
{ path: 'xmIteration/XmIterationMng', component: _import('xm/core/xmIteration/XmIterationMng'), name: 'XmIterationMng', meta: { title: '迭代管理' }},
{ path: 'xmIterationMenu/XmIterationMenuMng', component: _import('xm/core/xmIterationMenu/XmIterationMenuMng'), name: 'XmIterationMenuMng', meta: { title: '故事迭代' }},
]

src/views/xm/core/xmIteration/XmIterationComplex.vue → src/views/xm/core/xmIteration/XmIterationForProjectComplex.vue

444
src/views/xm/core/xmIteration/XmIterationInfo.vue

@ -0,0 +1,444 @@
<template>
<section class="page-container page-full-height">
<el-row>
<el-menu :default-active="infotype" mode="horizontal" @select="setInfotype" class="menus" background-color="#545c64" text-color="#fff" active-text-color="#ffd04b">
<el-menu-item index="项目概览" >
<span slot="title" >
<span slot="title" @click.stop="goBack"><i class="el-icon-back" ></i></span>
<i class="el-icon-s-data"></i>
<font v-if="selProject.name.length>=10">{{selProject.name.substring(0,10)}}</font>
<font type="danger" v-else>{{selProject.name}}</font>
概览
</span>
</el-menu-item>
<el-menu-item index="迭代">
<span slot="title"><i class="el-icon-document-copy"></i>迭代</span>
</el-menu-item>
<el-menu-item index="产品">
<span slot="title"><i class="el-icon-s-flag"></i>产品</span>
</el-menu-item>
<el-menu-item label="故事" index="用户故事">
<span slot="title"><i class="el-icon-document"></i>故事</span>
</el-menu-item>
<el-menu-item index="任务">
<span slot="title"><i class="el-icon-s-operation"></i>任务</span>
</el-menu-item>
<el-menu-item index="缺陷">
<span slot="title"><i class="el-icon-question"></i>缺陷</span>
</el-menu-item>
<el-menu-item index="团队">
<span slot="title"><i class="el-icon-user-solid"></i>团队</span>
</el-menu-item>
<el-menu-item index="文档">
<span slot="title"><i class="el-icon-document"></i>文档</span>
</el-menu-item>
<el-submenu index="规划与里程碑">
<template slot="title">规划与里程碑 </template>
<el-menu-item index="阶段计划及里程碑">
<span slot="title"><i class="el-icon-odometer"></i>阶段计划及里程碑</span>
</el-menu-item>
<el-menu-item index="测试计划">
<span slot="title"><i class="el-icon-odometer"></i>测试计划</span>
</el-menu-item>
</el-submenu >
<el-menu-item index="项目监控" class="hidden-lg-and-down">
<span slot="title"><i class="el-icon-video-camera"></i>项目监控</span>
</el-menu-item>
<el-menu-item index="故事监控" class="hidden-lg-and-down">
<span slot="title"><i class="el-icon-video-camera"></i>故事监控</span>
</el-menu-item>
<el-menu-item index="合同管理" class="hidden-md-and-down">
<span slot="title"><i class="el-icon-s-data"></i>合同管理</span>
</el-menu-item>
<el-menu-item index="预算" class="hidden-md-and-down">
<span slot="title"><i class="el-icon-coin"></i>预算</span>
</el-menu-item>
<el-menu-item index="费用" class="hidden-md-and-down">
<span slot="title"><i class="el-icon-coin"></i>费用</span>
</el-menu-item>
<el-menu-item index="考核" class="hidden-lg-and-down">
<span slot="title"><i class="el-icon-view"></i>考核</span>
</el-menu-item>
<el-menu-item index="日志" class="hidden-lg-and-down">
<span slot="title"><i class="el-icon-edit-outline"></i>日志</span>
</el-menu-item>
<el-menu-item index="环境清单" class="hidden-lg-and-down">
<span slot="title"><i class="el-icon-setting"></i>环境清单</span>
</el-menu-item>
<el-menu-item index="风险" class="hidden-lg-and-down">
<span slot="title"><i class="el-icon-question"></i>风险</span>
</el-menu-item>
<el-submenu index="更多">
<template slot="title">更多 </template>
<el-menu-item index="故事监控" >
<span slot="title"><i class="el-icon-video-camera"></i>故事监控</span>
</el-menu-item>
<el-menu-item index="项目监控">
<span slot="title"><i class="el-icon-video-camera"></i>项目监控</span>
</el-menu-item>
<el-menu-item index="预算">
<span slot="title"><i class="el-icon-coin"></i>预算</span>
</el-menu-item>
<el-menu-item index="费用">
<span slot="title"><i class="el-icon-coin"></i>费用</span>
</el-menu-item>
<el-menu-item index="考核">
<span slot="title"><i class="el-icon-view"></i>考核</span>
</el-menu-item>
<el-menu-item index="日志">
<span slot="title"><i class="el-icon-edit-outline"></i>日志</span>
</el-menu-item>
<el-menu-item index="合同管理">
<span slot="title"><i class="el-icon-s-data"></i>合同管理</span>
</el-menu-item>
<el-menu-item index="环境清单">
<span slot="title"><i class="el-icon-setting"></i>环境清单</span>
</el-menu-item>
<el-menu-item index="风险">
<span slot="title"><i class="el-icon-question"></i>风险</span>
</el-menu-item>
<el-menu-item index="论坛">
<span slot="title"><i class="el-icon-date"></i>论坛</span>
</el-menu-item>
<el-menu-item index="即聊">
<span slot="title"><i class="el-icon-date"></i>即聊</span>
</el-menu-item>
<el-menu-item index="客服">
<span slot="title"><i class="el-icon-date"></i>客服</span>
</el-menu-item>
</el-submenu>
</el-menu>
<xm-project-overview-complex v-if="infotype=='项目概览'" :sel-project="selProject"></xm-project-overview-complex>
<xm-iteration-for-project-complex v-if="infotype=='迭代'" ref="xmIterationMng" :sel-project="selProject"></xm-iteration-for-project-complex>
<xm-product-for-project-complex v-if="infotype=='产品'" ref="xmProductComplex" :sel-project="selProject"></xm-product-for-project-complex>
<xm-menu-mng v-if="infotype=='用户故事'" :sel-project="selProject"></xm-menu-mng>
<xm-task-mng v-if="infotype=='任务'" ref="xmTaskMng" :sel-project="selProject" ></xm-task-mng>
<xm-question v-if="infotype=='缺陷'" :qtype="'bug'" :sel-project='selProject' ref="xmQuestion"></xm-question>
<xm-group-mng v-if="infotype=='团队'" :sel-project="selProject"></xm-group-mng>
<xm-file-mng v-if="infotype=='文档'" :sel-project="selProject"></xm-file-mng>
<xm-project-phase-mng v-if="infotype=='阶段计划及里程碑'" ref="xmProjectPhaseMng" :sel-project="selProject" ></xm-project-phase-mng>
<xm-test-case-exec-mng v-if="infotype=='测试计划'" :visible="infotype=='测试计划'" :sel-project='selProject' ref="xmQuestion"></xm-test-case-exec-mng>
<xm-menu-with-plan v-if="infotype=='故事监控'" ref="xmMenuWithPlan" :sel-project="selProject"></xm-menu-with-plan>
<xm-project-state-mng v-if="infotype=='项目监控'" :sel-project="selProject"></xm-project-state-mng>
<xm-budget v-if="infotype=='预算'" :sel-project="selProject"></xm-budget>
<xm-cost v-if="infotype=='费用'" :sel-project="selProject"></xm-cost>
<xm-project-kpi v-if="infotype=='考核'" :sel-project="selProject"></xm-project-kpi>
<xm-record v-if="infotype=='日志'" :visible="infotype=='日志'" :sel-project="selProject"></xm-record>
<xm-contract v-if="infotype=='合同管理'" :sel-project="selProject"></xm-contract>
<xm-env-list v-if="infotype=='环境清单'" :sel-project="selProject"></xm-env-list>
<xm-question v-if="infotype=='风险'" :qtype="'risk'" :sel-project='selProject' ref="xmRisk"></xm-question>
<el-drawer title="选中团队成员" :visible.sync="groupUserVisible" size="50%" append-to-body :close-on-click-modal="false">
<xm-project-group-select :sel-project="selProject" :visible="groupUserVisible" is-select-multi-user="1" @user-confirm="onUserSelected"></xm-project-group-select>
</el-drawer>
</el-row>
</section>
</template>
<script>
import util from '@/common/js/util';//
//import Sticky from '@/components/Sticky' // header
//import { listOption } from '@/api/mdp/meta/itemOption';//
import { listXmProject,editStatus , delXmProject, batchDelXmProject } from '@/api/xm/core/xmProject';
import XmProjectAdd from '../xmProject/XmProjectAdd';//
import XmProjectEdit from '../xmProject/XmProjectEdit';//
import { mapGetters } from 'vuex';
import xmTaskMng from '../xmTask/XmTaskMng';
import xmGroupMng from '../xmProjectGroup/XmProjectGroupMng';
import xmProjectGroupSelect from '../xmProjectGroup/XmProjectGroupSelect';
import xmExchange from '../xmExchange/XmExchangeMng';
import xmQuestion from '../xmQuestion/XmQuestionMng';
import xmFileMng from '../xmFile/XmFileMng';
import xmDetail from '../xmProject/XmProjectDetail';
import xmProjectKpi from '../xmProjectKpi/XmProjectKpiMng';
import xmRecord from '../xmRecord/XmRecordMng';
import xmCost from '../xmProject/XmProjectCost';
import xmBudget from '../xmProject/XmProjectBudgetCost';
import xmContract from '../xmProjectContract/XmProjectContractMng';
import xmEnvList from '../xmProjectEnvList/XmProjectEnvListMng';
import xmProjectPhaseMng from '../xmProjectPhase/XmProjectPhaseMng';
import xmMenuMng from '../xmMenu/XmMenuMng';
import xmMenuWithPlan from '../xmMenu/XmMenuWithPlan';
import xmProjectStateMng from '../xmProjectState/XmProjectStateMng';
import xmTestCaseExecMng from '../xmTestCaseExec/XmTestCaseExecMng';
import XmIterationForProjectComplex from '../xmIteration/XmIterationForProjectComplex.vue';
import XmProjectOverviewComplex from '../xmProject/XmProjectOverviewComplex.vue';
import XmProductForProjectComplex from '../xmProduct/XmProductForProjectComplex.vue';
export default {
props: ["selProject","visible"],
computed: {
...mapGetters([
'userInfo','roles'
]),
},
watch:{
selProject:function(selProject){
var oldInfotype=this.infotype
this.infotype=''
this.$nextTick(()=>{
this.infotype=oldInfotype
})
}
},
data() {
return {
platformViewVisible:false,
tabPosition:'left',
infotype:"项目概览",
load:{list:false,edit:false},
groupUserVisible:false,
exportArr: ['任务', '阶段计划', '故事监控']
/**end 自定义属性请在上面加 请加备注**/
}
},//end data
methods: {
afterEditSubmit:function(project){
this.$emit("submit",project)
},
toArchive:function(){
this.$router.push({
path: "/mdp/arc/mate/archive/ArchiveMng"
});
},
toIm:function(){
this.groupUserVisible=true
},
toHelpMe:function(){
this.$router.push({
path: "/mdp/im/messages/crmChat",
query:{
categoryId:'css',
sendContent:'咨询'
}
});
},
handleMenuSelect(menuId){
this.infotype=menuId
},
onUserSelected:function(users){
if(this.groupUserVisible==true){
var query={}
if(users){
if( users.length==1 ){
var user=users[0]
query.toUserid=user.userid
query.toUsername=user.username
query.msgType="prichat"
}else if( users.length >=2 ){
query.users=JSON.stringify(users.map(i=>{return {userid:i.userid,username:i.username}}))
query.categoryId="common"
query.msgType="group"
}
}
this.$router.push({
path: "/mdp/im/messages/messageChat",
query: query
});
}
},
setInfotype(infotype){
this.infotype=infotype;
},
handleExport() {
this.downloadLoading = true
let list = [];
let header = [];
let keyList = [];
let pageNum = 1;
let infotypeKey = '';
if (this.infotype === '任务') {
header = ['序号', '任务名称', '故事', '预算(万)', '工作量(人时)', '执行人', '进度', '任务开始时间', '任务结束时间', '任务技能需求'];
keyList = ['sortLevel', 'name', 'menuName', 'budgetCost', 'budgetWorkload', 'exeUsernames', 'rate', 'startTime', 'endTime', 'taskSkillNames'];
list = this.$refs.xmTaskMng.tasksTreeData;
pageNum = this.$refs.xmTaskMng.pageInfo.pageNum;
} else if (this.infotype === '阶段计划') {
header = ['序号', '阶段名称', '开始时间', '结束时间', '进度(%)', '状态', '计划人数', '实际人数', '计划工期', '实际工期', '计划工作量(人时)', '实际工作量(人时)', '计划非人力成本(元)', '实际非人力成本(元)', '计划内购人力成本(元)', '实际内购人力成本(元)', '计划外购人力成本(元)', '实际外购人力成本(元)', '计划成本合计(元)', '实际成本合计(元)', '审批状态', '备注'];
keyList = ['seqNo', 'phaseName', 'beginDate', 'endDate', 'actRate', 'phaseStatus', 'phaseBudgetOutUserCnt', 'actStaffNu', 'phaseBudgetHours', 'actHours', 'phaseBudgetWorkload', 'phaseActWorkload', 'phaseBudgetNouserAt', 'actNouserAt', 'phaseBudgetInnerUserAt', 'actInnerUserAt', 'phaseBudgetOutUserAt', 'actOutUserAt', 'phaseBudgetCostAt', 'actCostAt', 'bizFlowState', 'remark'];
list = this.$refs.xmProjectPhaseMng.projectPhaseTreeData;
pageNum = this.$refs.xmProjectPhaseMng.pageInfo.pageNum;
} else if (this.infotype === '故事监控') {
header = ['序号', '故事名称', '计划状态', '负责人', '上线时间', '计划开始时间', '实际开始时间', '计划结束时间', '实际结束时间', '计划工作量(人时)', '实际工作量(人时)', '计划成本(元)', '实际成本(元)', '总体完成比例%', '需求完成比例%', '设计完成比例%', '开发完成比例%', 'sit完成比例%', 'uat完成比例%', '上线状态'];
keyList = ['seqNo', 'menuName', 'planStatus', 'chargeUsername', 'onlineTime', 'planStartTime', 'actStartTime', 'planEndTime', 'actEndTime', 'planWorkload', 'actWorkload', 'planCostAmount', 'actCostAmount', 'finishRate', 'demandRate', 'designRate', 'devRate', 'sitRate', 'uatRate', 'onlineStatus'];
list = this.$refs.xmMenuWithPlan.xmMenusTreeData;
pageNum = this.$refs.xmMenuWithPlan.pageInfo.pageNum;
}
const filename = `${this.selProject.name}_${this.infotype}_第${pageNum}`;
const data = this.formatJson(keyList, list);
import('@/vendor/Export2Excel').then(excel => {
excel.export_json_to_excel({
header,
data,
filename,
// autoWidth: this.autoWidth,
bookType: 'xlsx'
})
this.downloadLoading = false
})
},
formatJson(filterVal, jsonData, dataList = []) {
if (this.infotype == '任务') {
jsonData.forEach(v => {
const row = filterVal.map(j => {
let key = '';
return v[j];
})
dataList.push(row);
if (v.children && v.children.length) {
dataList = this.formatJson(filterVal, v.children, dataList);
}
})
return dataList;
} else if (this.infotype == '阶段计划') {
const bizFlowStateDict = {
0: '未发审',
1: '审核中',
2: '已通过',
3: '未通过',
4: '已取消'
}
jsonData.forEach(v => {
const row = filterVal.map(j => {
let key = '';
if(j == 'phaseStatus') {
return this.$refs.xmProjectPhaseMng.formateOption('xmPhaseStatus', v.phaseStatus);
} else if(j == 'bizFlowState') {
return `${bizFlowStateDict[parseInt(v[j]) || 0]}`;
} else {
return v[j];
}
})
dataList.push(row);
if (v.children && v.children.length) {
dataList = this.formatJson(filterVal, v.children, dataList);
}
})
return dataList;
} else if (this.infotype == '故事监控') {
jsonData.forEach(v => {
const row = filterVal.map(j => {
let key = '';
if(j == 'planStatus') {
key = 'xmMenuPlanStatus';
} else if(j == 'onlineStatus') {
return parseInt(v[j]) ? '已上线' : '未上线';
} else {
return v[j];
}
const options = this.$refs.xmMenuWithPlan.options;
if(options[key]==undefined || options[key]==null || options[key].length==0 ){
return v[j];
}
var rowData=options[key].filter(i=>i.optionValue==v[j])
if(rowData.length>0){
return rowData[0].optionName
}else{
return v[j];
}
});
dataList.push(row);
if (v.children && v.children.length) {
dataList = this.formatJson(filterVal, v.children, dataList);
}
})
return dataList;
}
},
getDateString(dateStr){
if(dateStr==null || dateStr=="" || dateStr==undefined){
return ""
}else{
return dateStr.substr(0,10);
}
},
goBack(){
this.$router.back()
}
},//end methods
components: {
xmTaskMng,
xmProjectPhaseMng,
xmGroupMng,
xmExchange,
xmQuestion,
xmFileMng,
xmDetail,
xmProjectKpi,
xmRecord,
xmCost,
xmBudget,
xmContract,
xmEnvList,
xmMenuMng,
xmMenuWithPlan,
xmProjectStateMng,
xmTestCaseExecMng,
xmProjectGroupSelect,
XmIterationForProjectComplex,
XmProjectOverviewComplex,
XmProductForProjectComplex,
//
},
mounted() {
this.$nextTick(() => {
});
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.menus{
.el-menu-item{
padding-left: 0px !important;
}
}
/* 超过宽度则用...代替 */
.truncate{
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
</style>

61
src/views/xm/core/xmIteration/XmIterationInfoRoute.vue

@ -0,0 +1,61 @@
<template>
<section>
<xm-iteration-info v-if="xmIteration" :xm-iteration="xmIteration" :visible="showInfo"></xm-iteration-info>
</section>
</template>
<script>
import util from '@/common/js/util';//
import { mapGetters } from 'vuex';
import XmIterationInfo from './XmIterationInfo'
export default {
computed: {
...mapGetters([
'userInfo','roles'
]),
},
watch:{
},
data() {
return {
xmIteration:null,
showInfo:false,
}
},//end data
methods: {
},//end methods
components: {
XmIterationInfo,
//
},
activated(){
if(this.$route.params && this.$route.params.id){
this.xmIteration=this.$route.params
this.showInfo=true;
localStorage.setItem('xm-iteration-info-route',JSON.stringify(this.xmIteration));
}else{
this.xmIteration=JSON.parse(localStorage.getItem("xm-iteration-info-route"))
this.showInfo=true;
}
},
mounted() {
if(this.$route.params && this.$route.params.id){
this.xmIteration=this.$route.params
this.showInfo=true;
localStorage.setItem('xm-iteration-info-route',JSON.stringify(this.xmIteration));
}else{
this.xmIteration=JSON.parse(localStorage.getItem("xm-iteration-info-route"))
this.showInfo=true;
}
}
}
</script>
<style scoped>
</style>

6
src/views/xm/core/xmMenu/XmMenuMng.vue

@ -27,8 +27,8 @@
</el-input>
<el-button type="primary" v-loading="load.list" :disabled="load.list==true" v-on:click="searchXmMenus" icon="el-icon-search"></el-button>
<el-button type="primary" @click="showAdd" icon="el-icon-plus">故事</el-button>
<el-button @click="toBatchEdit" icon="el-icon-edit">修改</el-button>
<el-button v-if="!selProject&&!xmIteration" type="primary" @click="showAdd" icon="el-icon-plus">故事</el-button>
<el-button v-if="!selProject&&!xmIteration" @click="toBatchEdit" icon="el-icon-edit">修改</el-button>
<el-popover
placement="top-start"
title=""
@ -110,7 +110,7 @@
<el-tag slot="reference" icon="el-icon-chat-line-square">描述</el-tag>
</el-popover>
<el-button size="mini" type="primary" @click="showSubAdd( scope.row,scope.$index)" icon="el-icon-plus" circle></el-button>
<el-button v-if="!selProject&&!xmIteration" size="mini" type="primary" @click="showSubAdd( scope.row,scope.$index)" icon="el-icon-plus" circle></el-button>
<el-popover style="padding-left:10px;"
v-if="isPmUser"

src/views/xm/core/xmProduct/XmProductComplex.vue → src/views/xm/core/xmProduct/XmProductForProjectComplex.vue

445
src/views/xm/core/xmProduct/XmProductInfo.vue

@ -0,0 +1,445 @@
<template>
<section class="page-container page-full-height">
<el-row>
<el-menu :default-active="infotype" mode="horizontal" @select="setInfotype" class="menus" background-color="#545c64" text-color="#fff" active-text-color="#ffd04b">
<el-menu-item index="产品概览" >
<span slot="title" >
<span slot="title" @click.stop="goBack"><i class="el-icon-back" ></i></span>
<i class="el-icon-s-data"></i>
<font v-if="xmProduct.productName.length>=10">{{xmProduct.productName.substring(0,10)}}</font>
<font type="danger" v-else>{{xmProduct.productName}}</font>
概览
</span>
</el-menu-item>
<el-menu-item index="迭代">
<span slot="title"><i class="el-icon-document-copy"></i>迭代</span>
</el-menu-item>
<el-menu-item label="项目" index="项目">
<span slot="title"><i class="el-icon-document"></i>项目</span>
</el-menu-item>
<el-menu-item label="故事" index="用户故事">
<span slot="title"><i class="el-icon-document"></i>故事</span>
</el-menu-item>
<el-menu-item index="任务">
<span slot="title"><i class="el-icon-s-operation"></i>任务</span>
</el-menu-item>
<el-menu-item index="缺陷">
<span slot="title"><i class="el-icon-question"></i>缺陷</span>
</el-menu-item>
<el-menu-item index="团队">
<span slot="title"><i class="el-icon-user-solid"></i>团队</span>
</el-menu-item>
<el-menu-item index="文档">
<span slot="title"><i class="el-icon-document"></i>文档</span>
</el-menu-item>
<el-submenu index="规划与里程碑">
<template slot="title">规划与里程碑 </template>
<el-menu-item index="阶段计划及里程碑">
<span slot="title"><i class="el-icon-odometer"></i>阶段计划及里程碑</span>
</el-menu-item>
<el-menu-item index="测试计划">
<span slot="title"><i class="el-icon-odometer"></i>测试计划</span>
</el-menu-item>
</el-submenu >
<el-menu-item index="项目监控" class="hidden-lg-and-down">
<span slot="title"><i class="el-icon-video-camera"></i>项目监控</span>
</el-menu-item>
<el-menu-item index="故事监控" class="hidden-lg-and-down">
<span slot="title"><i class="el-icon-video-camera"></i>故事监控</span>
</el-menu-item>
<el-menu-item index="合同管理" class="hidden-md-and-down">
<span slot="title"><i class="el-icon-s-data"></i>合同管理</span>
</el-menu-item>
<el-menu-item index="预算" class="hidden-md-and-down">
<span slot="title"><i class="el-icon-coin"></i>预算</span>
</el-menu-item>
<el-menu-item index="费用" class="hidden-md-and-down">
<span slot="title"><i class="el-icon-coin"></i>费用</span>
</el-menu-item>
<el-menu-item index="考核" class="hidden-lg-and-down">
<span slot="title"><i class="el-icon-view"></i>考核</span>
</el-menu-item>
<el-menu-item index="日志" class="hidden-lg-and-down">
<span slot="title"><i class="el-icon-edit-outline"></i>日志</span>
</el-menu-item>
<el-menu-item index="环境清单" class="hidden-lg-and-down">
<span slot="title"><i class="el-icon-setting"></i>环境清单</span>
</el-menu-item>
<el-menu-item index="风险" class="hidden-lg-and-down">
<span slot="title"><i class="el-icon-question"></i>风险</span>
</el-menu-item>
<el-submenu index="更多">
<template slot="title">更多 </template>
<el-menu-item index="故事监控" >
<span slot="title"><i class="el-icon-video-camera"></i>故事监控</span>
</el-menu-item>
<el-menu-item index="项目监控">
<span slot="title"><i class="el-icon-video-camera"></i>项目监控</span>
</el-menu-item>
<el-menu-item index="预算">
<span slot="title"><i class="el-icon-coin"></i>预算</span>
</el-menu-item>
<el-menu-item index="费用">
<span slot="title"><i class="el-icon-coin"></i>费用</span>
</el-menu-item>
<el-menu-item index="考核">
<span slot="title"><i class="el-icon-view"></i>考核</span>
</el-menu-item>
<el-menu-item index="日志">
<span slot="title"><i class="el-icon-edit-outline"></i>日志</span>
</el-menu-item>
<el-menu-item index="合同管理">
<span slot="title"><i class="el-icon-s-data"></i>合同管理</span>
</el-menu-item>
<el-menu-item index="环境清单">
<span slot="title"><i class="el-icon-setting"></i>环境清单</span>
</el-menu-item>
<el-menu-item index="风险">
<span slot="title"><i class="el-icon-question"></i>风险</span>
</el-menu-item>
<el-menu-item index="论坛">
<span slot="title"><i class="el-icon-date"></i>论坛</span>
</el-menu-item>
<el-menu-item index="即聊">
<span slot="title"><i class="el-icon-date"></i>即聊</span>
</el-menu-item>
<el-menu-item index="客服">
<span slot="title"><i class="el-icon-date"></i>客服</span>
</el-menu-item>
</el-submenu>
</el-menu>
<xm-product-overview-complex v-if="infotype=='产品概览'" :xm-product="xmProduct"></xm-product-overview-complex>
<xm-iteration-for-project-complex v-if="infotype=='迭代'" ref="xmIterationMng" :xm-product="xmProduct"></xm-iteration-for-project-complex>
<xm-project-for-link v-if="infotype=='项目'" ref="xmProjectForLink" :xm-product="xmProduct"></xm-project-for-link>
<xm-menu-mng v-if="infotype=='用户故事'" :xm-product="xmProduct"></xm-menu-mng>
<xm-task-mng v-if="infotype=='任务'" ref="xmTaskMng" :xm-product="xmProduct" ></xm-task-mng>
<xm-question v-if="infotype=='缺陷'" :qtype="'bug'" :xm-product='xmProduct' ref="xmQuestion"></xm-question>
<xm-group-mng v-if="infotype=='团队'" :xm-product="xmProduct"></xm-group-mng>
<xm-file-mng v-if="infotype=='文档'" :xm-product="xmProduct"></xm-file-mng>
<xm-project-phase-mng v-if="infotype=='阶段计划及里程碑'" ref="xmProjectPhaseMng" :xm-product="xmProduct" ></xm-project-phase-mng>
<xm-test-case-exec-mng v-if="infotype=='测试计划'" :visible="infotype=='测试计划'" :xm-product='xmProduct' ref="xmQuestion"></xm-test-case-exec-mng>
<xm-menu-with-plan v-if="infotype=='故事监控'" ref="xmMenuWithPlan" :xm-product="xmProduct"></xm-menu-with-plan>
<xm-project-state-mng v-if="infotype=='项目监控'" :xm-product="xmProduct"></xm-project-state-mng>
<xm-budget v-if="infotype=='预算'" :xm-product="xmProduct"></xm-budget>
<xm-cost v-if="infotype=='费用'" :xm-product="xmProduct"></xm-cost>
<xm-project-kpi v-if="infotype=='考核'" :xm-product="xmProduct"></xm-project-kpi>
<xm-record v-if="infotype=='日志'" :visible="infotype=='日志'" :xm-product="xmProduct"></xm-record>
<xm-contract v-if="infotype=='合同管理'" :xm-product="xmProduct"></xm-contract>
<xm-env-list v-if="infotype=='环境清单'" :xm-product="xmProduct"></xm-env-list>
<xm-question v-if="infotype=='风险'" :qtype="'risk'" :xm-product='xmProduct' ref="xmRisk"></xm-question>
<el-drawer title="选中团队成员" :visible.sync="groupUserVisible" size="50%" append-to-body :close-on-click-modal="false">
<xm-project-group-select :xm-product="xmProduct" :visible="groupUserVisible" is-select-multi-user="1" @user-confirm="onUserSelected"></xm-project-group-select>
</el-drawer>
</el-row>
</section>
</template>
<script>
import util from '@/common/js/util';//
//import Sticky from '@/components/Sticky' // header
//import { listOption } from '@/api/mdp/meta/itemOption';//
import { listXmProject,editStatus , delXmProject, batchDelXmProject } from '@/api/xm/core/xmProject';
import XmProjectAdd from '../xmProject/XmProjectAdd';//
import XmProjectEdit from '../xmProject/XmProjectEdit';//
import { mapGetters } from 'vuex';
import xmTaskMng from '../xmTask/XmTaskMng';
import xmGroupMng from '../xmProjectGroup/XmProjectGroupMng';
import xmProjectGroupSelect from '../xmProjectGroup/XmProjectGroupSelect';
import xmExchange from '../xmExchange/XmExchangeMng';
import xmQuestion from '../xmQuestion/XmQuestionMng';
import xmFileMng from '../xmFile/XmFileMng';
import xmDetail from '../xmProject/XmProjectDetail';
import xmProjectKpi from '../xmProjectKpi/XmProjectKpiMng';
import xmRecord from '../xmRecord/XmRecordMng';
import xmCost from '../xmProject/XmProjectCost';
import xmBudget from '../xmProject/XmProjectBudgetCost';
import xmContract from '../xmProjectContract/XmProjectContractMng';
import xmEnvList from '../xmProjectEnvList/XmProjectEnvListMng';
import xmProjectPhaseMng from '../xmProjectPhase/XmProjectPhaseMng';
import xmMenuMng from '../xmMenu/XmMenuMng';
import xmMenuWithPlan from '../xmMenu/XmMenuWithPlan';
import xmProjectStateMng from '../xmProjectState/XmProjectStateMng';
import xmTestCaseExecMng from '../xmTestCaseExec/XmTestCaseExecMng';
import XmIterationForProjectComplex from '../xmIteration/XmIterationForProjectComplex.vue';
import XmProductOverviewComplex from '../xmProduct/XmProductOverviewComplex.vue';
import XmProductForProjectComplex from './XmProductForProjectComplex.vue';
import XmProjectForLink from '../xmProject/XmProjectForLink.vue';
export default {
props: ["xmProduct","visible"],
computed: {
...mapGetters([
'userInfo','roles'
]),
},
watch:{
xmProduct:function(xmProduct){
var oldInfotype=this.infotype
this.infotype=''
this.$nextTick(()=>{
this.infotype=oldInfotype
})
}
},
data() {
return {
platformViewVisible:false,
tabPosition:'left',
infotype:"项目概览",
load:{list:false,edit:false},
groupUserVisible:false,
exportArr: ['任务', '阶段计划', '故事监控']
/**end 自定义属性请在上面加 请加备注**/
}
},//end data
methods: {
afterEditSubmit:function(project){
this.$emit("submit",project)
},
toArchive:function(){
this.$router.push({
path: "/mdp/arc/mate/archive/ArchiveMng"
});
},
toIm:function(){
this.groupUserVisible=true
},
toHelpMe:function(){
this.$router.push({
path: "/mdp/im/messages/crmChat",
query:{
categoryId:'css',
sendContent:'咨询'
}
});
},
handleMenuSelect(menuId){
this.infotype=menuId
},
onUserSelected:function(users){
if(this.groupUserVisible==true){
var query={}
if(users){
if( users.length==1 ){
var user=users[0]
query.toUserid=user.userid
query.toUsername=user.username
query.msgType="prichat"
}else if( users.length >=2 ){
query.users=JSON.stringify(users.map(i=>{return {userid:i.userid,username:i.username}}))
query.categoryId="common"
query.msgType="group"
}
}
this.$router.push({
path: "/mdp/im/messages/messageChat",
query: query
});
}
},
setInfotype(infotype){
this.infotype=infotype;
},
handleExport() {
this.downloadLoading = true
let list = [];
let header = [];
let keyList = [];
let pageNum = 1;
let infotypeKey = '';
if (this.infotype === '任务') {
header = ['序号', '任务名称', '故事', '预算(万)', '工作量(人时)', '执行人', '进度', '任务开始时间', '任务结束时间', '任务技能需求'];
keyList = ['sortLevel', 'name', 'menuName', 'budgetCost', 'budgetWorkload', 'exeUsernames', 'rate', 'startTime', 'endTime', 'taskSkillNames'];
list = this.$refs.xmTaskMng.tasksTreeData;
pageNum = this.$refs.xmTaskMng.pageInfo.pageNum;
} else if (this.infotype === '阶段计划') {
header = ['序号', '阶段名称', '开始时间', '结束时间', '进度(%)', '状态', '计划人数', '实际人数', '计划工期', '实际工期', '计划工作量(人时)', '实际工作量(人时)', '计划非人力成本(元)', '实际非人力成本(元)', '计划内购人力成本(元)', '实际内购人力成本(元)', '计划外购人力成本(元)', '实际外购人力成本(元)', '计划成本合计(元)', '实际成本合计(元)', '审批状态', '备注'];
keyList = ['seqNo', 'phaseName', 'beginDate', 'endDate', 'actRate', 'phaseStatus', 'phaseBudgetOutUserCnt', 'actStaffNu', 'phaseBudgetHours', 'actHours', 'phaseBudgetWorkload', 'phaseActWorkload', 'phaseBudgetNouserAt', 'actNouserAt', 'phaseBudgetInnerUserAt', 'actInnerUserAt', 'phaseBudgetOutUserAt', 'actOutUserAt', 'phaseBudgetCostAt', 'actCostAt', 'bizFlowState', 'remark'];
list = this.$refs.xmProjectPhaseMng.projectPhaseTreeData;
pageNum = this.$refs.xmProjectPhaseMng.pageInfo.pageNum;
} else if (this.infotype === '故事监控') {
header = ['序号', '故事名称', '计划状态', '负责人', '上线时间', '计划开始时间', '实际开始时间', '计划结束时间', '实际结束时间', '计划工作量(人时)', '实际工作量(人时)', '计划成本(元)', '实际成本(元)', '总体完成比例%', '需求完成比例%', '设计完成比例%', '开发完成比例%', 'sit完成比例%', 'uat完成比例%', '上线状态'];
keyList = ['seqNo', 'menuName', 'planStatus', 'chargeUsername', 'onlineTime', 'planStartTime', 'actStartTime', 'planEndTime', 'actEndTime', 'planWorkload', 'actWorkload', 'planCostAmount', 'actCostAmount', 'finishRate', 'demandRate', 'designRate', 'devRate', 'sitRate', 'uatRate', 'onlineStatus'];
list = this.$refs.xmMenuWithPlan.xmMenusTreeData;
pageNum = this.$refs.xmMenuWithPlan.pageInfo.pageNum;
}
const filename = `${this.xmProduct.productName}_${this.infotype}_第${pageNum}`;
const data = this.formatJson(keyList, list);
import('@/vendor/Export2Excel').then(excel => {
excel.export_json_to_excel({
header,
data,
filename,
// autoWidth: this.autoWidth,
bookType: 'xlsx'
})
this.downloadLoading = false
})
},
formatJson(filterVal, jsonData, dataList = []) {
if (this.infotype == '任务') {
jsonData.forEach(v => {
const row = filterVal.map(j => {
let key = '';
return v[j];
})
dataList.push(row);
if (v.children && v.children.length) {
dataList = this.formatJson(filterVal, v.children, dataList);
}
})
return dataList;
} else if (this.infotype == '阶段计划') {
const bizFlowStateDict = {
0: '未发审',
1: '审核中',
2: '已通过',
3: '未通过',
4: '已取消'
}
jsonData.forEach(v => {
const row = filterVal.map(j => {
let key = '';
if(j == 'phaseStatus') {
return this.$refs.xmProjectPhaseMng.formateOption('xmPhaseStatus', v.phaseStatus);
} else if(j == 'bizFlowState') {
return `${bizFlowStateDict[parseInt(v[j]) || 0]}`;
} else {
return v[j];
}
})
dataList.push(row);
if (v.children && v.children.length) {
dataList = this.formatJson(filterVal, v.children, dataList);
}
})
return dataList;
} else if (this.infotype == '故事监控') {
jsonData.forEach(v => {
const row = filterVal.map(j => {
let key = '';
if(j == 'planStatus') {
key = 'xmMenuPlanStatus';
} else if(j == 'onlineStatus') {
return parseInt(v[j]) ? '已上线' : '未上线';
} else {
return v[j];
}
const options = this.$refs.xmMenuWithPlan.options;
if(options[key]==undefined || options[key]==null || options[key].length==0 ){
return v[j];
}
var rowData=options[key].filter(i=>i.optionValue==v[j])
if(rowData.length>0){
return rowData[0].optionName
}else{
return v[j];
}
});
dataList.push(row);
if (v.children && v.children.length) {
dataList = this.formatJson(filterVal, v.children, dataList);
}
})
return dataList;
}
},
getDateString(dateStr){
if(dateStr==null || dateStr=="" || dateStr==undefined){
return ""
}else{
return dateStr.substr(0,10);
}
},
goBack(){
this.$router.back()
}
},//end methods
components: {
xmTaskMng,
xmProjectPhaseMng,
xmGroupMng,
xmExchange,
xmQuestion,
xmFileMng,
xmDetail,
xmProjectKpi,
xmRecord,
xmCost,
xmBudget,
xmContract,
xmEnvList,
xmMenuMng,
xmMenuWithPlan,
xmProjectStateMng,
xmTestCaseExecMng,
xmProjectGroupSelect,
XmIterationForProjectComplex,
XmProductOverviewComplex,
XmProductForProjectComplex,
XmProjectForLink,
//
},
mounted() {
this.$nextTick(() => {
});
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.menus{
.el-menu-item{
padding-left: 0px !important;
}
}
/* 超过宽度则用...代替 */
.truncate{
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
</style>

61
src/views/xm/core/xmProduct/XmProductInfoRoute.vue

@ -0,0 +1,61 @@
<template>
<section>
<xm-product-info v-if="xmProduct" :xm-product="xmProduct" :visible="showInfo"></xm-product-info>
</section>
</template>
<script>
import util from '@/common/js/util';//
import { mapGetters } from 'vuex';
import XmProductInfo from './XmProductInfo'
export default {
computed: {
...mapGetters([
'userInfo','roles'
]),
},
watch:{
},
data() {
return {
xmProduct:null,
showInfo:false,
}
},//end data
methods: {
},//end methods
components: {
XmProductInfo,
//
},
activated(){
if(this.$route.params && this.$route.params.id){
this.xmProduct=this.$route.params
this.showInfo=true;
localStorage.setItem('xm-product-info-route',JSON.stringify(this.xmProduct));
}else{
this.xmProduct=JSON.parse(localStorage.getItem("xm-product-info-route"))
this.showInfo=true;
}
},
mounted() {
if(this.$route.params && this.$route.params.id){
this.xmProduct=this.$route.params
this.showInfo=true;
localStorage.setItem('xm-product-info-route',JSON.stringify(this.xmProduct));
}else{
this.xmProduct=JSON.parse(localStorage.getItem("xm-product-info-route"))
this.showInfo=true;
}
}
}
</script>
<style scoped>
</style>

156
src/views/xm/core/xmProduct/XmProductMng.vue

@ -8,6 +8,7 @@
</el-select>
<el-input v-if="filters.queryScope=='productId'" style="width:20%;" v-model="filters.id" placeholder="输入产品编号" @keyup.enter.native="searchXmProducts">
</el-input>
<el-date-picker v-show="!selProject&&filters.queryScope!='productId'"
v-model="dateRanger"
type="daterange"
@ -27,11 +28,12 @@
<el-button v-loading="load.list" :disabled="load.list==true" v-on:click="searchXmProducts" icon="el-icon-search"></el-button>
</template>
</el-input>
<el-button type="primary" @click="showAdd" icon="el-icon-plus">产品</el-button>
<el-button v-if="xmIteration" icon="el-icon-plus" @click="productSelectVisible=true">将更多产品加入迭代<strong>{{xmIteration.iterationName}}</strong></el-button>
<el-button type="primary" @click="showAdd" icon="el-icon-plus" v-if="!xmIteration">产品</el-button>
<el-popover
placement="top-start"
title=""
width="400"
width="500"
trigger="click" >
<el-divider content-position="left"><strong>查询条件</strong></el-divider>
<el-row>
@ -104,6 +106,42 @@
<el-button slot="reference" icon="el-icon-more" circle></el-button>
</el-popover>
</el-row>
<el-row class="page-main page-height-80">
<!--列表 XmProject xm_project-->
<el-row v-show="showType" v-loading="load.list">
<el-col v-cloak v-for="(p,i) in xmProducts" :key="i" :xl="4" :lg="6" :md="8" :sm="12">
<el-card @click.native="intoInfo(p,i)" class="project-card" shadow="always">
<div class="project-name" title="这是产品名称">{{p.productName}}</div>
<div class="project-id eui-text-truncate">{{p.code}}</div>
<div class="project-info">
<div class="info-item">
<span class="item-total">{{p.totalBugCnt==null?0:p.totalBugCnt}}</span>
<span class="item-type">缺陷</span>
</div>
<div class="info-item">
<span class="item-total">{{p.totalFileCnt==null?0:p.totalFileCnt}}</span>
<span class="item-type">文档</span>
</div>
<div class="info-task">
<span>
<span class="item-total finish-task">{{p.totalCompleteTaskCnt==null?0:p.totalCompleteTaskCnt}}</span>
<span style="margin: 0 .25rem !important;">/</span>
<span class="item-type total-task">{{p.totalTaskCnt==null?0:p.totalTaskCnt}}</span>
</span>
<span class="item-type">任务完成</span>
</div>
</div>
<div class="project-rate">
<el-progress :percentage="(p.totalProgress==null?0:p.totalProgress)"></el-progress>
</div>
<div class="project-footer">
<div class="project-type">{{p.xmType}}</div>
<!--<div class="project-period">{{p.startTime.substr(0,10)}} ~{{p.endTime.substr(0,10)}}</div>-->
</div>
</el-card>
</el-col>
</el-row>
</el-row>
<el-row class="padding-top">
<!--列表 XmProduct 产品表-->
<el-table ref="table" :height="tableHeight" :data="xmProducts" @sort-change="sortChange" highlight-current-row v-loading="load.list" border @selection-change="selsChange" @row-click="rowClick" style="width: 100%;">
@ -273,6 +311,7 @@ import XmProductSelect from './XmProductSelect.vue';
pickerOptions: util.pickerOptions('datarange'),
projectVisible:false,
productSelectVisible:false,
showType:true,
/**begin 自定义属性请在下面加 请加备注**/
/**end 自定义属性请在上面加 请加备注**/
@ -411,6 +450,13 @@ import XmProductSelect from './XmProductSelect.vue';
this.editFormVisible=false;
this.getXmProducts()
},
//info
intoInfo(row) {
this.editForm = row;
this.$router.push({ name:'XmProductInfoRoute', params: row })
//this.showInfo = true;
},
//xmProduct
selsChange: function (sels) {
this.sels = sels;
@ -504,6 +550,9 @@ import XmProductSelect from './XmProductSelect.vue';
},
onProjectSelected(projects){
},
onXmIterationSelect(){
},
/**end 自定义函数请在上面加**/
onXmProductSelect:function(row){
@ -575,3 +624,106 @@ XmProductSelect,
float: right;
}
</style>
<style scoped>
.project-card{
font-size: 12px;
color: #999;
margin: 10px 12px;
}
.project-card:hover{
border-color: #00abfc;
}
.project-card >>> .el-card__body{
padding: 20px 15px 10px;
}
.project-name{
font-size: 16px;
font-weight: 700;
color: #333;
height: 24px;
}
.project-id{
margin-top: 4px;
height: 18px;
}
.project-info{
display: flex;
margin-top: 8px;
}
.project-info>div{
display: flex;
flex-direction: column;
}
.info-item{
width: 15%;
text-align: center;
}
.info-item >>> span{
display: block;
}
.item-total{
font-size: 18px;
color: #666;
}
.info-task{
padding-left: 20px;
width: 70%;
border-left: 1px solid #efefef;
}
.finish-task{
color: #00abfc !important;
}
.project-rate{
margin: 15px 0;
}
.project-rate>.el-progress{
display: flex;
align-items: center;
}
.project-rate >>> .el-progress-bar{
padding-right: 0;
margin-right: 0;
}
.project-rate >>> .el-progress__text{
margin-left: 5px;
}
.project-footer{
display: flex;
}
.project-footer>div{
width: 30%;
}
.project-footer>div:not(:first-child){
width: 70%;
}
.project-period{
text-align: right;
}
@media only screen and (max-height: 2400px) {
.project-box{
max-height: 1600x;
overflow-y: auto;
}
}
@media only screen and (max-height: 1200px) {
.project-box{
max-height: 800px;
overflow-y: auto;
}
}
@media only screen and (max-height: 980px) {
.project-box{
max-height: 600px;
overflow-y: auto;
}
}
@media only screen and (max-height: 640px) {
.project-box{
max-height: 300px;
overflow-y: auto;
}
}
</style>

888
src/views/xm/core/xmProduct/XmProductOverview.vue

@ -1,251 +1,369 @@
<template>
<section>
<section class="page-container padding">
<!-- <el-row class="page-header page-height-10">
<el-col :xs="22" :sm="22" :md="23" :lg="23" :xl="23">
<span >项目总览</span>
</el-col>
</el-row>-->
<el-row class="page-main page-height-75" style="overflow-x: hidden;">
<el-row :gutter="10" style="margin-bottom:10px">
<el-col :span="8" >
<el-card class="box-card" style="padding:0px ;height:425px">
<div slot="header" class="clearfix">
<span>项目信息</span>
</div>
<el-row style="margin-bottom:18px">
<el-row>
<!--列表 XmProduct 产品表-->
<el-table ref="table" :height="tableHeight" :data="xmProducts" @sort-change="sortChange" highlight-current-row v-loading="load.list" border @selection-change="selsChange" @row-click="rowClick" style="width: 100%;">
<el-table-column prop="productName" label="产品名称" min-width="150" >
<template slot="header" slot-scope="scope">
产品名称
<el-popover
placement="top-start"
title=""
width="400"
trigger="click" >
<span v-text="taskMng"></span>
</el-row>
<el-row>
<el-col :span="24" style="padding-top:5px;">
<font class="more-label-font">
产品查询范围
</font>
<el-select size="mini" v-model="filters.queryScope" style="width:100%;" placeholder="产品查询范围">
<el-option :label="userInfo.branchName+'机构下所有的产品'" value="branchId"></el-option>
<el-option label="我相关的产品" value="compete"></el-option>
<el-option label="按产品编号精确查找" value="productId"></el-option>
<el-option label="后台智能匹配" value=""></el-option>
</el-select>
<span>项目负责人</span>
</el-row>
</el-row>
<el-row style="margin-bottom:18px">
<el-col :span="8" @click="">
<div class="item">
<div class="icon" style="background-color: rgb(79, 140, 255);">
<i class="el-icon-right"></i>
</div>
<div class="info">
<div v-text="totalTask"></div>
<div class="title">总任务量</div>
</div>
</div>
</el-col>
<el-col :span="24" style="padding-top:5px;">
<el-input v-if="filters.queryScope=='productId'" size="mini" v-model="filters.id" style="width:100%;" placeholder="输入产品编号" @keyup.enter.native="searchXmProducts">
</el-input>
<el-col :span="8">
<div class="item">
<div class="icon" style="background-color: rgb(255, 153, 51);">
<i class="el-icon-loading"></i>
</div>
<div class="info">
<div v-text="notStart">
</div>
<div class="title">待完成</div>
</div>
</div>
</el-col>
<el-col v-show="!selProject&&filters.queryScope!='productId'" :span="24" style="padding-top:5px;">
<font class="more-label-font">创建时间:</font>
<el-date-picker
v-model="dateRanger"
type="daterange"
align="right"
unlink-panels
range-separator="至"
start-placeholder="开始日期"
end-placeholder="完成日期"
value-format="yyyy-MM-dd"
:default-time="['00:00:00','23:59:59']"
:picker-options="pickerOptions"
></el-date-picker>
<el-col :span="8">
<div class="item">
<div class="icon" style="background-color: rgb(0, 153, 51);">
<i class="el-icon-check"></i>
</div>
<div class="info">
<div v-text="finish" >
</div>
<div class="title">已完成</div>
</div>
</div>
</el-col>
<el-col :span="24" style="padding-top:5px;">
<font class="more-label-font">
产品名称:
</font>
<el-input size="mini" v-model="filters.key" style="width:100%;" placeholder="输入产品名字关键字">
</el-input>
</el-row>
<el-row style="margin-bottom:18px">
<div class="item">
<div class="icon2" style="background-color: rgb(204, 204, 204);">
<i class="el-icon-date"></i>
</div>
<div class="info">
<div v-text="taskStartTime+'~'+taskEndTime">
</div>
<div class="title">项目计划周期</div>
</div>
</div>
</el-row>
<el-row style="margin-bottom:18px">
<div class="item">
<div class="icon2" style="background-color: rgb(204, 204, 204);">
<i class="el-icon-star-off"></i>
</div>
<div class="info">
<div class="title"> 需求数 {{this.xmProjectState.menuCnt}}</div>
</div>
</div>
</el-row>
<el-row style="margin-bottom:18px">
<div class="item">
<div class="icon2" style="background-color: rgb(204, 204, 204);">
<i class="el-icon-refresh"></i>
</div>
<div class="info">
<div class="title"> 迭代数 {{(this.xmProjectState.iterationCnt==null?0:this.xmProjectState.iterationCnt)}} </div>
</div>
</div>
</el-row>
<el-row style="margin-bottom:18px">
<div class="item">
<div class="icon2" style="background-color: rgb(204, 204, 204);">
<i class="el-icon-alarm-clock"></i>
</div>
<div>
<div class="info">
<el-progress v-if="progress1" :percentage="progress1"></el-progress>
</div>
<div class="title">任务进度</div>
</div>
</div>
</el-row>
</el-card>
</el-col>
<el-col :span="24" style="padding-top:5px;">
<font class="more-label-font">
产品经理:
</font>
<el-tag v-if="filters.pmUser" closable @click="selectFiltersPmUser" @close="clearFiltersPmUser()">{{filters.pmUser.username}}</el-tag>
<el-button size="mini" v-else @click="selectFiltersPmUser()">选责任人</el-button>
<el-button size="mini" @click="setFiltersPmUserAsMySelf()">我的</el-button>
<el-col :span="8" >
<el-card class="box-card" style="height:425px">
<div slot="header" class="clearfix">
<span>所有工作项及其完成情况</span>
</div>
<div>
<div id="allChart" :style="{width: '425px', height: '350px'}"></div>
</div>
</el-card>
</el-col>
<el-col :span="24" style="padding-top:5px;">
<el-button type="primary" size="mini" @click="searchXmProducts" >查询</el-button>
<el-col :span="8" >
<el-card class="box-card" style="height:425px">
<div slot="header" class="clearfix">
<span>缺陷情况</span>
</div>
<div>
<div id="bugPieChart" :style="{width: '440px', height: '400px'}"></div>
</div>
</el-card>
</el-col>
</el-row>
<el-button slot="reference" size="mini" icon="el-icon-more" circle></el-button>
</el-popover>
</template>
<template slot-scope="scope">
<font>{{scope.row.productName}}</font>
<font class="align-right"><el-tag :type="scope.row.finishRate>=100?'success':'warning'">{{scope.row.finishRate}}%</el-tag>
<el-tooltip content="产品经理"><el-tag v-if="scope.row.pmUsername">{{scope.row.pmUsername}}</el-tag></el-tooltip>
<el-tooltip content="点击统计进度,由任务进度汇总而成"><el-button size="mini" icon="el-icon-video-play" @click.stop="loadTasksToXmProductState( scope.row)"></el-button></el-tooltip>
<el-row :gutter="10" style="margin-bottom:10px">
<el-col :span="12" >
<el-card class="box-card" style="padding:0px ;height:425px">
<div slot="header" class="clearfix">
<span>任务每日状态趋势</span>
</div>
<div>
<div id="taskChart" :style="{width: '630px', height: '320px'}"></div>
</div>
</el-card>
</el-col>
<el-col :span="12" >
<el-card class="box-card" style="padding:0px ;height:425px">
<div slot="header" class="clearfix">
<span>项目工时</span>
</div>
<div>
<el-row style="padding:25px;">
<div class="item">
<el-col :span="8">
<div>
<div style="text-align:center;">
<span style="font-size:24px;" v-text="this.xmProjectState.totalPlanWorkload"></span>
<span style="font-size:5px;">h</span>
</div>
<div style="text-align:center;font-size:5px;">预估工时</div>
</div>
</el-col>
<el-col :span="8">
<div>
<div style="text-align:center;">
<span style="font-size:24px;" v-text="this.xmProjectState.totalActWorkload"></span>
<span style="font-size:5px;">h</span>
</div>
<div style="text-align:center;font-size:5px;">登记工时</div>
</div>
</el-col>
<el-col :span="8">
<div>
<div style="text-align:center;">
<span style="font-size:24px;" v-text="workloadProgress"></span>
<span style="font-size:5px;">%</span>
</div>
<div style="text-align:center;font-size:5px;">工时进度</div>
</div>
</el-col>
</div>
</el-row>
<el-row style="padding:25px;">
<div class="item">
<el-col :span="8">
<div>
<div style="text-align:center;">
<span style="font-size:24px;" v-text="remainWorkload"></span>
<span style="font-size:5px;">h</span>
</div>
<div style="text-align:center;font-size:5px;">剩余工时</div>
</div>
</el-col>
<el-col :span="8">
<div>
<div style="text-align:center;">
<span style="font-size:24px;" v-text="deviation"></span>
<span style="font-size:5px;">h</span>
</div>
<div style="text-align:center;font-size:5px;">预估偏差</div>
</div>
</el-col>
<el-col :span="8">
<div>
<div style="text-align:center;">
<span style="font-size:24px;" v-text="deviationRate"></span>
<span style="font-size:5px;">%</span>
</div>
<div style="text-align:center;font-size:5px;">预估偏差率</div>
</div>
</el-col>
</div>
</el-row>
<el-row>
<span style="margin-left:20px;">项目预计进度</span>
<el-progress style="width: 600px;margin-left:20px;margin-top: 10px;margin-bottom: 20px;" :text-inside="true" :stroke-width="24" :percentage="planProgress"></el-progress>
</el-row>
<el-row>
<span style="margin-left:20px;">项目实际进度</span>
<el-progress style="width: 600px;margin-left:20px;margin-top: 10px;" :text-inside="true" :stroke-width="24" :percentage="realProgress"></el-progress>
</el-row>
</font>
</template>
</el-table-column>
<el-table-column v-if="isSelectProduct==true" label="操作" width="100" fixed="right" >
<template slot-scope="scope">
<el-button type="primary" @click="selectedProduct( scope.row,scope.$index)">选择</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination layout="total, prev, next" @current-change="handleCurrentChange" @size-change="handleSizeChange" :page-sizes="[10,20, 50, 100, 500]" :current-page="pageInfo.pageNum" :page-size="pageInfo.pageSize" :total="pageInfo.total" style="float:right;"></el-pagination>
</div>
</el-card>
</el-col>
</el-row>
<el-row style="margin-bottom:10px">
<el-card class="box-card" style="padding:0px ;height:200px">
<div slot="header" class="clearfix" style="margin-bottom:10px">
<span>项目阶段</span>
</div>
<div>
<el-row style="padding:10px">
<el-steps :active="calcProjectStatusStep" finish-status="success">
<el-step v-for="(i,index) in options['projectStatus']" :title="i.optionName" :key="index">
<el-row slot="title" @click.native.stop="editForm.status=i.optionValue">
{{i.optionName}}
</el-row>
</el-step>
</el-steps>
</el-row>
</div>
</el-card>
</el-row>
</el-row>
<el-drawer title="选择员工" :visible.sync="selectFiltersPmUserVisible" size="60%" append-to-body>
<users-select @confirm="onFiltersPmUserSelected" ref="usersSelect"></users-select>
</el-drawer>
</section>
</template>
<script>
import util from '@/common/js/util';//
//import Sticky from '@/components/Sticky' // header
//import { listOption } from '@/api/mdp/meta/itemOption';//
import { listXmProductWithState } from '@/api/xm/core/xmProduct';
import { mapGetters } from 'vuex'
import UsersSelect from "@/views/mdp/sys/user/UsersSelect";
import { loadTasksToXmProductState } from '@/api/xm/core/xmProductState';
import util from "@/common/js/util"; //
//import Sticky from "@/components/Sticky"; // header
import { mapGetters } from "vuex";
import { listXmProjectState } from '@/api/xm/core/xmProjectState';
import { listOption } from '@/api/mdp/meta/itemOption';//
export default {
props:['isSelectProduct','selProject','xmIteration'],
computed: {
...mapGetters([
'userInfo','roles'
])
...mapGetters(["userInfo"]),
finish: function (){
return this.xmProjectState.totalCompleteTaskCnt;
},
watch:{
xmIteration(){
this.getXmProducts();
}
notStart: function() {
return this.xmProjectState.totalTaskCnt-this.xmProjectState.totalCompleteTaskCnt;
},
data() {
const beginDate = new Date();
const endDate = new Date();
beginDate.setTime(beginDate.getTime() - 3600 * 1000 * 24 * 7 * 4 * 12 );
return {
filters: {
key: '',
queryScope:'compete',
id:'',//
pmUser:null,//
},
xmProducts: [],//
pageInfo:{//
total:0,//0>0
pageSize:10,//
count:false,//
pageNum:1,//1
orderFields:[],// ['sex','student_id']
orderDirs:[]// asc,desc ['asc','desc']
},
load:{ list: false, edit: false, del: false, add: false },//...
sels: [],//
options:{},// params=[{categoryId:'0001',itemCode:'sex'}] {'sex':[{optionValue:'1',optionName:'',seqOrder:'1',fp:'',isDefault:'0'},{optionValue:'2',optionName:'',seqOrder:'2',fp:'',isDefault:'0'}]}
addFormVisible: false,//xmProduct
//xmProduct
addForm: {
id:'',productName:'',branchId:'',remark:''
},
editFormVisible: false,//
//xmProduct
editForm: {
id:'',productName:'',branchId:'',remark:''
},
iterationVisible:false,
productStateVisible:false,
selectFiltersPmUserVisible:false,
tableHeight:300,
dateRanger: [
util.formatDate.format(beginDate, "yyyy-MM-dd"),
util.formatDate.format(endDate, "yyyy-MM-dd")
],
pickerOptions: util.pickerOptions('datarange'),
/**begin 自定义属性请在下面加 请加备注**/
/**end 自定义属性请在上面加 请加备注**/
totalTask: function() {
return this.xmProjectState.totalTaskCnt;
},
progress1: function (){
return Math.round(this.xmProjectState.totalCompleteTaskCnt/this.xmProjectState.totalTaskCnt*100);
},
taskStartTime: function (){
return this.selProject.startTime.substring(0,10);
},
taskEndTime: function (){
return this.selProject.endTime.substring(0,10);
},
taskMng: function (){
return this.selProject.createUsername;
},
workloadProgress:function (){
return Math.round(this.xmProjectState.totalActWorkload/this.xmProjectState.totalPlanWorkload*100);
},
deviation:function (){
let now = new Date();
let taskStartTime = new Date(this.selProject.startTime);
let taskEndTime = new Date(this.selProject.endTime);
if(now<=taskEndTime){
let allDays=taskEndTime-taskStartTime;
return this.xmProjectState.totalActWorkload - Math.round((now-taskStartTime)/allDays*this.xmProjectState.totalPlanWorkload)
}else{
return this.xmProjectState.totalActWorkload - this.xmProjectState.totalPlanWorkload;
}
},//end data
methods: {
handleSizeChange(pageSize) {
this.pageInfo.pageSize=pageSize;
this.getXmProducts();
},
handleCurrentChange(pageNum) {
this.pageInfo.pageNum = pageNum;
this.getXmProducts();
},
// obj.order=ascending/descending, asc/desc ; obj.prop=,
sortChange( obj ){
var dir='asc';
if(obj.order=='ascending'){
dir='asc'
},
deviationRate:function (){
return Math.round(this.deviation/this.xmProjectState.totalPlanWorkload*100);
},
remainWorkload:function (){
return this.xmProjectState.totalPlanWorkload - this.xmProjectState.totalActWorkload;
},
planProgress:function (){
let now = new Date();
let taskStartTime = new Date(this.selProject.startTime);
let taskEndTime = new Date(this.selProject.endTime);
if(now<=taskEndTime){
let allDays=taskEndTime-taskStartTime;
return Math.round((now-taskStartTime)/allDays*100)
}else{
dir='desc';
return 100;
}
if(obj.prop=='xxx'){
this.pageInfo.orderFields=['xxx'];
this.pageInfo.orderDirs=[dir];
},
realProgress:function (){
if(this.xmProjectState.totalActWorkload < this.xmProjectState.totalPlanWorkload){
return Math.round(this.xmProjectState.totalActWorkload/this.xmProjectState.totalPlanWorkload*100)
}else{
return 100;
}
this.getXmProducts();
},
searchXmProducts(){
this.pageInfo.count=true;
this.getXmProducts();
xmProjectStateCpd(){
return this.xmProjectState
},
// XmProduct
getXmProducts() {
let params = {
pageSize: this.pageInfo.pageSize,
pageNum: this.pageInfo.pageNum,
total: this.pageInfo.total,
count:this.pageInfo.count
};
if(this.pageInfo.orderFields!=null && this.pageInfo.orderFields.length>0){
let orderBys=[];
for(var i=0;i<this.pageInfo.orderFields.length;i++){
orderBys.push(this.pageInfo.orderFields[i]+" "+this.pageInfo.orderDirs[i])
}
params.orderBy= orderBys.join(",")
calcProjectStatusStep(){
if(this.options['projectStatus'] && this.selProject){
var index=this.options['projectStatus'].findIndex(i=>{
if(i.optionValue==this.selProject.status){
return true;
}else{
return false;
}
if(this.filters.key!==""){
params.key="%"+this.filters.key+"%"
})
return index+1;
}else{
//params.xxx=xxxxx
return 0;
}
if(this.selProject){
params.projectId=this.selProject.id
}
if(this.xmIteration){
params.iterationId=this.xmIteration.id
}
params.queryScope=this.filters.queryScope
if(this.filters.queryScope=='productId'){
if(!this.filters.id){
this.$message({showClose: true, message:"您选择了按产品编号精确查找模式,请输入产品编号", type: 'error' });
return;
}
params.id=this.filters.id
},
props:['selProject'],
watch:{
xmProjectStateCpd:function(){
this.drawAllBar();
this.drawTaskByDate();
this.drawPieBug();
}
if(this.filters.queryScope=="branchId"){
params.branchId=this.userInfo.branchId
params.projectId=null;
}
if(!this.selProject && !this.xmIteration && this.filters.queryScope!='productId'){
if(!this.dateRanger || this.dateRanger.length==0){
this.$message({showClose: true, message: "创建日期范围不能为空", type: 'error' });
return;
}
params.ctimeStart=this.dateRanger[0]+" 00:00:00"
params.ctimeEnd=this.dateRanger[1]+" 23:59:59"
}
},
data() {
return {
isActive: true,
load:{ list: false},
xmProjectState: [],//
options:{
projectType:[],
urgencyLevel:[],
priority:[],
projectStatus:[],
},// params=[{categoryId:'0001',itemCode:'sex'}] {'sex':[{optionValue:'1',optionName:'',seqOrder:'1',fp:'',isDefault:'0'},{optionValue:'2',optionName:'',seqOrder:'2',fp:'',isDefault:'0'}]}
};
},
methods:{
//xmProjectsTate
getXmProjectState(){
let params = {
projectId:this.selProject.id,
branchId:this.userInfo.branchId
};
this.load.list = true;
listXmProductWithState(params).then((res) => {
var tips=res.data.tips;
listXmProjectState(params).then((res) => {
let tips=res.data.tips;
if(tips.isOk){
this.pageInfo.total = res.data.total;
this.pageInfo.count=false;
this.xmProducts = res.data.data;
this.xmProjectState = res.data.data[0];
}else{
this.$message({showClose: true, message: tips.msg, type: 'error' });
}
@ -253,79 +371,329 @@
}).catch( err => this.load.list = false );
},
//xmProduct
selsChange: function (sels) {
this.sels = sels;
drawAllBar() {
// domecharts
let allChart = this.$echarts.init(document.getElementById("allChart"));
let option = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
rowClick: function(row, event, column){
this.$emit('row-click',row, event, column);// @row-click="rowClick"
grid: {
left: '3%',
right: '4%',
bottom: '10%',
containLabel: true
},
selectedProduct:function(row){
this.$emit('selected',row);
yAxis: {
type: 'value'
},
/**begin 自定义函数请在下面加**/
clearFiltersPmUser:function(){
this.filters.pmUser=null;
this.searchXmProducts();
xAxis: {
type: 'category',
data: ['需求', '任务', '缺陷']
},
selectFiltersPmUser(){
this.selectFiltersPmUserVisible=true;
series: [
{
data: [
{
value: this.xmProjectState.menuCnt,
itemStyle: {
normal:{
color: '#99CCFF'
}
}
},
onFiltersPmUserSelected(users){
if(users && users.length>0){
this.filters.pmUser=users[0]
}else{
this.filters.pmUser=null;
{
value: this.xmProjectState.totalTaskCnt,
itemStyle: {
normal:{
color: '#99CCFF'
}
}
this.selectFiltersPmUserVisible=false;
this.searchXmProducts();
},
setFiltersPmUserAsMySelf(){
this.filters.pmUser=this.userInfo;
this.searchXmProducts();
{
value: this.xmProjectState.totalBugCnt,
itemStyle: {
normal:{
color: '#99CCFF'
}
}
},
],
type: 'bar'
}
]
};
loadTasksToXmProductState: function (row) {
this.load.edit=true;
//
allChart.setOption(option);
},
drawTaskByDate() {
let taskChart = this.$echarts.init(document.getElementById("taskChart"));
let option = {
tooltip: {
trigger: 'axis'
},
color:['rgb(0, 153, 255)','#6699CC'],
legend: {
data: ['未开始', '进行中']
},
grid: {
left: '1%',
right: '3%',
bottom: '5%',
containLabel: true
},
toolbox: {
feature: {
saveAsImage: {}
}
},
xAxis: {
type: 'category',
boundaryGap: false,
data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
},
yAxis: {
type: 'value'
},
series: [
{
name: '未开始',
type: 'line',
//stack: '',
data: [120, 132, 101, 134, 90, 230, 210],
areaStyle: {
normal:{
color:"rgb(153, 204, 255)" , //线
}
},
lineStyle:{
normal:{
color:'rgb(0, 153, 255)'
}
},
},
{
name: '进行中',
type: 'line',
//stack: '',
data: [220, 182, 191, 234, 290, 330, 310],
areaStyle: {
normal:{
color:"rgb(153, 204, 255)", //线
}
},
lineStyle:{
normal:{
color:'#6699CC'
}
},
},
],
};
let params = { productId: row.id };
loadTasksToXmProductState(params).then((res) => {
this.load.edit=false;
var tips=res.data.tips;
if(tips.isOk){
this.pageInfo.count=true;
this.getXmProducts();
//
taskChart.setOption(option);
},
drawPieBug() {
let bugPieChart = this.$echarts.init(document.getElementById("bugPieChart"));
let option = {
tooltip: {
trigger: 'item',
formatter: '{b} : {c} ({d}%)'
},
legend: {
orient: 'vertical',
left: 'left',
},
series: [
{
center:['55%','40%'],
type: 'pie',
radius: '68%',
data: [
{value: this.xmProjectState.totalClosedBugCnt,
itemStyle: {
normal:{
color: '#5470C6'
}
},
name: '已关闭'},
{value: this.xmProjectState.totalResolvedBugCnt,
itemStyle: {
normal:{
color: '#91CC75'
}
this.$message({showClose: true, message: tips.msg, type: tips.isOk?'success':'error' });
}).catch( err => this.load.edit=false );
},
},//end methods
components: {
UsersSelect,
//
name: '已解决'},
{value: this.xmProjectState.totalActiveBugCnt,
itemStyle: {
normal:{
color: '#FAC858'
}
},
name: '已激活'},
{value: this.xmProjectState.totalConfirmedBugCnt,
itemStyle: {
normal:{
color: '#EE6666'
}
},
name: '已确认'},
],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
};
//
bugPieChart.setOption(option);
}
},
mounted() {
this.$nextTick(() => {
var clientRect=this.$refs.table.$el.getBoundingClientRect();
var subHeight=70/1000 * window.innerHeight;
this.tableHeight = window.innerHeight -clientRect.y - this.$refs.table.$el.offsetTop-subHeight;
this.getXmProducts();
this.getXmProjectState();
});
listOption([{categoryId:'all',itemCode:'projectType'},{categoryId:'all',itemCode:'urgencyLevel'},{categoryId:'all',itemCode:'priority'},{categoryId:'all',itemCode:'projectStatus'}] ).then(res=>{
if(res.data.tips.isOk){
this.options['projectType']=res.data.data.projectType
this.options['urgencyLevel']=res.data.data.urgencyLevel
this.options['priority']=res.data.data.priority
this.options['projectStatus']=res.data.data.projectStatus
}
}
});
},
};
</script>
<style scoped>
<style scoped lang="scss">
.container {
margin: 10px;
}
.header {
display: flex;
justify-content: flex-start;
padding: 10px;
span {
padding-right: 15px;
}
}
.col {
margin-bottom: 20px;
}
.icon {
color: #fff;
height: 30px;
width: 30px;
border-radius: 15px;
text-align: center;
line-height: 30px;
font-size: 20px;
display: inline-block;
margin-right: 5px;
}
.icon2 {
color: #000000;
height: 30px;
width: 30px;
border-radius: 15px;
text-align: center;
line-height: 30px;
font-size: 20px;
display: inline-block;
margin-right: 5px;
margin-left: 5px;
}
.item {
display: flex;
justify-content: flex-start;
}
.card-font {
color: #000000;
font-size: 12px;
.el-col {
margin-bottom: 20px;
}
}
.calendar-header {
display: flex;
justify-content: space-between;
.cal-header-boxs {
flex: 1;
display: flex;
justify-content: flex-end;
.more-label-font{
.cal-header-box {
padding: 5px;
height: 45px;
margin-left: 10px;
}
.box-icon {
text-align: center;
}
.box-info {
text-align: center;
float:left;
padding-top:5px;
font-size: 12px;
color: #000000;
}
}
}
.el-tag:hover {
cursor: pointer;
}
.el-progress {
width: 350px;
}
.align-right{
float: right;
.value {
cursor: pointer;
}
.reference {
margin-top: 10px;
font-size: 12px;
}
.click {
background: #e9f7ff;
}
.calendar-box {
display: flex;
justify-content: flex-start;
}
</style>
<style>
.app-container{
padding: 20px;
padding-bottom: 0;
}
</style>

130
src/views/xm/core/xmProduct/XmProductOverviewComplex.vue

@ -0,0 +1,130 @@
<template>
<section class="page-container page-height-90 padding-left padding-right">
<el-menu mode="horizontal" default-active="overiew" @select="onMenuToolBarSelect">
<el-menu-item index="overiew">
<span slot="title">项目概览</span>
</el-menu-item>
<el-menu-item index="detail">
<span slot="title">项目详情</span>
</el-menu-item>
<el-submenu index="project-change">
<template slot="title">
<span slot="title">项目变更</span>
</template>
<el-menu-item index="project-change-base-info">
<span slot="title">基础信息修改</span>
</el-menu-item>
<el-menu-item index="project-change-start">
<span slot="title">立项申请</span>
</el-menu-item>
<el-menu-item index="project-change-budget">
<span slot="title">预算+-</span>
</el-menu-item>
<el-menu-item index="project-change-cost">
<span slot="title">成本+-</span>
</el-menu-item>
<el-menu-item index="project-change-collection">
<span slot="title">收款</span>
</el-menu-item>
<el-menu-item index="project-change-conclusion">
<span slot="title">结项申请</span>
</el-menu-item>
<el-menu-item index="project-change-suspend">
<span slot="title">暂停申请</span>
</el-menu-item>
<el-menu-item index="project-change-activation">
<span slot="title">激活申请</span>
</el-menu-item>
<el-menu-item index="project-change-to-after-sales">
<span slot="title">转售后申请</span>
</el-menu-item>
<el-menu-item index="project-change-new-pm">
<span slot="title">项目经理变更申请</span>
</el-menu-item>
<el-menu-item index="project-change-new-leader">
<span slot="title">组长变更申请</span>
</el-menu-item>
</el-submenu>
<el-submenu index="menu-change">
<template slot="title">
<span slot="title">需求变更</span>
</template>
<el-menu-item index="menu-change-review">
<span slot="title">需求评审</span>
</el-menu-item>
<el-menu-item index="menu-change-border-review">
<span slot="title">需求边界异动</span>
</el-menu-item>
</el-submenu>
<el-submenu index="iteration-change">
<template slot="title">
<span slot="title">迭代变更</span>
</template>
<el-menu-item index="iteration-change-review">
<span slot="title">迭代计划评审</span>
</el-menu-item>
<el-menu-item index="iteration-change-border-review">
<span slot="title">迭代上线申请</span>
</el-menu-item>
</el-submenu>
<el-submenu index="phase-change">
<template slot="title">
<span slot="title">阶段计划变更</span>
</template>
<el-menu-item index="phase-change-review">
<span slot="title">计划评审</span>
</el-menu-item>
<el-menu-item index="phase-change-border-review">
<span slot="title">预算变更</span>
</el-menu-item>
</el-submenu>
</el-menu>
<xm-product-overview v-if="showPanelName=='overiew'" :xm-product="xmProduct"></xm-product-overview>
<xm-product-edit v-if="showPanelName=='detail'" :xm-product="xmProduct"></xm-product-edit>
</section>
</template>
<script>
import util from "@/common/js/util"; //
//import Sticky from "@/components/Sticky"; // header
import { mapGetters } from "vuex";
import { listXmProjectState } from '@/api/xm/core/xmProductState';
import XmProductOverview from './XmProductOverview.vue';
import XmProductEdit from './XmProductEdit.vue';
export default {
components: { XmProductOverview, XmProductEdit },
computed: {
...mapGetters(["userInfo"]),
},
props:['selProject'],
watch:{
},
data() {
return {
showPanelName:'overiew'
};
},
methods:{
onMenuToolBarSelect(menuIndex){
this.showPanelName=menuIndex;
}
},
mounted() {
this.$nextTick(() => {
});
},
};
</script>
<style scoped lang="scss">
</style>

59
src/views/xm/core/xmProject/XmProjectForLink.vue

@ -6,6 +6,10 @@
<el-button @click="searchXmProjects" icon="el-icon-search"></el-button>
</template>
</el-input>
<el-button icon="el-icon-plus" @click="xmProjectListVisible=true" v-if="!xmIteration">
加入更多项目到产品中
</el-button>
</el-row>
<el-row class="page-main page-height-90">
<el-table ref="table" :height="tableHeight" stripe :data="xmProjects" highlight-current-row v-loading="load.list" style="width: 100%;">
@ -15,9 +19,8 @@
<el-table-column label="操作" width="245" fixed="right">
<template slot-scope="scope">
<el-button-group>
<el-button size="mini" type="primary" @click.stop="selectProject(scope.row)" >选中</el-button>
<el-button v-if="!xmIteration" size="mini" type="primary" @click.stop="doDelXmProductProjectLink(scope.row)" >移出产品</el-button>
</el-button-group>
<!-- <el-button style="width:100%;" slot="reference" class="see-more" type="text" icon="el-icon-more"></el-button>
</el-popover> -->
</template>
@ -25,6 +28,10 @@
</el-table>
<el-pagination layout="total, sizes, prev, pager, next" @current-change="handleCurrentChange" @size-change="handleSizeChange" :page-sizes="[10,20, 50, 100, 500]" :current-page="pageInfo.pageNum" :page-size="pageInfo.pageSize" :total="pageInfo.total" style="float:right;"></el-pagination>
</el-row>
<el-drawer title="选择项目" :visible.sync="xmProjectListVisible" size="60%" append-to-body>
<xm-project-list @project-confirm="onXmProjectSelect"></xm-project-list>
</el-drawer>
</section>
</template>
@ -37,11 +44,13 @@
//import { listOption } from '@/api/mdp/meta/itemOption';//
import { listXmProject, } from '@/api/xm/core/xmProject';
import { mapGetters } from 'vuex'
import { delXmProductProjectLink, addXmProductProjectLink,batchDelXmProductProjectLink } from '@/api/xm/core/xmProductProjectLink';
import XmProjectList from './XmProjectList.vue';
export default {
props:['xmProduct'],
props:['xmProduct','xmIteration'],
computed: {
...mapGetters([
'userInfo','roles'
@ -50,6 +59,9 @@
watch:{
xmProduct:function(){
this.getXmProjects();
},
xmIteration:function(){
this.getXmProjects();
}
},
data() {
@ -82,7 +94,7 @@
editForm: {
id:'',code:'',name:'',xmType:'',startTime:'',endTime:'',urgent:'',priority:'',description:'',createUserid:'',createUsername:'',createTime:'',assess:'',assessRemarks:'',status:'',branchId:'',planTotalCost:'',bizProcInstId:'',bizFlowState:'',planNouserAt:'',planInnerUserAt:'',planOutUserAt:'',locked:'',baseTime:'',baseRemark:'',baselineId:'',planWorkload:'',totalReceivables:'',budgetMarginRate:'',contractAmt:'',planInnerUserPrice:'',budgetOutUserPrice:'',planOutUserCnt:'',planInnerUserCnt:'',planWorkingHours:''
},
xmProjectListVisible:false,
/**end 自定义属性请在上面加 请加备注**/
}
@ -125,6 +137,10 @@
if(this.xmProduct){
params.productId=this.xmProduct.id
}
if(this.xmIteration){
params.iterationId=this.xmIteration.id
}
if(this.filters.key){
params.key='%'+this.filters.key+'%'
}
@ -153,11 +169,46 @@
selectProject:function(row){
this.editForm=row
this.$emit('project-confirm',this.editForm);
},
/**end 自定义函数请在上面加**/
onXmProjectSelect:function(row){
var xmProject=row;
var xmProduct=this.xmProduct;
this.$confirm('确认将项目【'+xmProject.name+'】加入产品【'+xmProduct.productName+'】吗?', '提示', {
type: 'warning'
}).then(()=>{
addXmProductProjectLink({projectId:xmProject.id,productId:xmProduct.id}).then(res=>{
var tips =res.data.tips;
if(tips.isOk){
this.getXmProjects();
}
this.$message({showClose: true, message: tips.msg, type: tips.isOk?'success':'error'});
})
})
},
doDelXmProductProjectLink(row){
var xmProduct=this.xmProduct;
var selProject=row;
this.$confirm('确认将项目【'+selProject.projectName+'】从产品【'+xmProduct.productName+'】移出吗?移出后,项目试图中将看不到该产品信息', '提示', {
type: 'warning'
}).then(()=>{
var params={productId:xmProduct.id,projectId:selProject.id}
delXmProductProjectLink(params).then(res=>{
var tips = res.data.tips;
if(tips.isOk){
this.getXmProjects();
this.$message({showClose: true, message: "移出成功", type: 'success' });
}
});
})
}
/**end 自定义函数请在上面加**/
},//end methods
components: {
XmProjectList
//
},

12
src/views/xm/core/xmProject/XmProjectInfo.vue

@ -142,8 +142,8 @@
</el-menu>
<xm-project-overview-complex v-if="infotype=='项目概览'" :sel-project="selProject"></xm-project-overview-complex>
<xm-iteration-complex v-if="infotype=='迭代'" ref="xmIterationMng" :sel-project="selProject" ></xm-iteration-complex>
<xm-product-complex v-if="infotype=='产品'" ref="xmProductComplex" :sel-project="selProject" ></xm-product-complex>
<xm-iteration-for-project-complex v-if="infotype=='迭代'" ref="xmIterationMng" :sel-project="selProject"></xm-iteration-for-project-complex>
<xm-product-for-project-complex v-if="infotype=='产品'" ref="xmProductComplex" :sel-project="selProject"></xm-product-for-project-complex>
<xm-menu-mng v-if="infotype=='用户故事'" :sel-project="selProject"></xm-menu-mng>
<xm-task-mng v-if="infotype=='任务'" ref="xmTaskMng" :sel-project="selProject" ></xm-task-mng>
<xm-question v-if="infotype=='缺陷'" :qtype="'bug'" :sel-project='selProject' ref="xmQuestion"></xm-question>
@ -194,9 +194,9 @@
import xmMenuWithPlan from '../xmMenu/XmMenuWithPlan';
import xmProjectStateMng from '../xmProjectState/XmProjectStateMng';
import xmTestCaseExecMng from '../xmTestCaseExec/XmTestCaseExecMng';
import XmIterationComplex from '../xmIteration/XmIterationComplex.vue';
import XmIterationForProjectComplex from '../xmIteration/XmIterationForProjectComplex.vue';
import XmProjectOverviewComplex from './XmProjectOverviewComplex.vue';
import XmProductComplex from '../xmProduct/XmProductComplex.vue';
import XmProductForProjectComplex from '../xmProduct/XmProductForProjectComplex.vue';
export default {
@ -416,9 +416,9 @@ import XmProductComplex from '../xmProduct/XmProductComplex.vue';
xmProjectStateMng,
xmTestCaseExecMng,
xmProjectGroupSelect,
XmIterationComplex,
XmIterationForProjectComplex,
XmProjectOverviewComplex,
XmProductComplex,
XmProductForProjectComplex,
//
},
mounted() {

2
src/views/xm/core/xmTask/XmTaskMng.vue

@ -56,7 +56,7 @@
<el-button v-if=" isTaskCenter!='1' && isMy!='1'" @click="showAdd" icon="el-icon-plus">直接创建</el-button>
</el-col>
</el-row>
<el-button slot="reference" v-if=" isTaskCenter!='1' && isMy!='1'" type="primary" icon="el-icon-plus" circle></el-button>
<el-button slot="reference" v-if=" isTaskCenter!='1' && isMy!='1' && !xmProduct && !xmIteration" type="primary" icon="el-icon-plus" circle></el-button>
</el-popover>
<el-popover

Loading…
Cancel
Save