点击项目流程图,弹出弹框,包含tab页。爷组件>父组件>子组件
爷:
父组件:
子组件:
代码段
爷组件:
<template>
<div>
<div class="project-process" @click="changeCardShowStatus">
<img src="" />
</div>
<!-- 项目流程管理 -->
<project-process-management ref="projectProcessManagement" />
</div>
</template>
export default {
methods: {
changeCardShowStatus() {
this.$refs.projectProcessManagement.projectProcessShow = true
},
}
}
父组件:
<template>
<div>
<van-popup
v-model="projectProcessShow"
position="bottom"
:lock-scroll="true"
:style="{ height: '500px' }"
:closeable="true"
class="process-table-popup"
round
>
<div class="process-table-title">项目流程图</div>
<div class="device-tab">
<van-tabs class="project-tab" v-model="activeName" @click="onTabClick">
<van-tab title="实验进度" name="testSchedule">
<test-schedule ref="testSchedule" />
</van-tab>
<van-tab class="project-tab" title="基础信息" name="basicInfo">
<basic-info ref="basicInfo" />
</van-tab>
<van-tab class="project-tab" title="人员信息" name="peopleInfo">
<people-info ref="peopleInfo" />
</van-tab>
<van-tab class="project-tab" title="测试项目" name="testProject">
<test-project ref="testProject" />
</van-tab>
<van-tab class="project-tab" title="主要节点" name="mainNode">
<main-node ref="mainNode" />
</van-tab>
</van-tabs>
</div>
</van-popup>
</div>
</template>
<script>
import testSchedule from "./components/testSchedule.vue";
import testProject from "./components/testProject.vue";
import peopleInfo from "./components/peopleInfo.vue";
import mainNode from "./components/mainNode.vue";
import basicInfo from "./components/basicInfo.vue";
export default {
components: {
testSchedule,
testProject,
mainNode,
basicInfo,
peopleInfo,
},
data() {
return {
projectProcessShow: false,
activeName: this.$store.state.deviceTab,
};
},
created() {
if (
![
"testSchedule",
"testProject",
"peopleInfo",
"mainNode",
"basicInfo",
].includes(this.$store.state.deviceTab)
) {
this.$store.commit("SET_DEVICE_TAB", "testSchedule")
}
},
mounted() { },
watch: { },
methods: {
onTabClick(name, title) {
this.$nextTick( () => {
this.$refs[name].getList()
})
this.$store.commit("SET_DEVICE_TAB", name);
},
},
};
</script>
<style lang="less">
.project-tab > .van-tabs__wrap {
width: 100vw;
height: 44px;
// position: fixed;
left: 0;
top: 100px;
z-index: 99;
}
.project-tab > .van-tabs__wrap > .van-tabs__nav > .van-tab--active {
font-weight: 600;
color: #323233;
}
.project-tab .van-tab {
font-weight: 400;
color: #666666;
}
.project-tab .van-tabs__line {
background: #4381f1;
border-radius: 2px;
}
.process-table-popup {
overflow: auto;
.process-table-title {
width: 100%;
text-align: center;
font-size: 16px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #333333;
margin: 16px 0 0 0;
}
}
</style>
子组件:
<template>
<div>
<div class="schcontain">
<div class="scheduletag">
<div class="tagitem">
<div class="tag" style="background:#ffc000" />
<span class="tagtext">11</span>
</div>
<div class="tagitem">
<div class="tag" style="background:#99639d" />
<span class="tagtext">22</span>
</div>
<div class="tagitem">
<div class="tag" style="background:#feded4" />
<span class="tagtext">33</span>
</div>
</div>
</div>
<div class="process-table-content">
<table @scroll="scrollEvent">
<thead :class="{'box-shadow': showBoxShadow}">
<th>序号</th>
<th>项目(区段)名称</th>
<th>一月</th>
<th>一月</th>
</thead>
<tbody>
<tr v-for="(item, index) in list" :key="index">
<td>{{ index + 1 }}</td>
<td>{{ item.projectName }}</td>
<td class="date-cell">
<div class="month">
<div v-for="num in januaryDays" :key="num" class="day">
<div class="tip-wrapper">
<div
class="tip-card"
v-show="getDayContent(item.createYear, '1', num, index).testTrainColorArray[0]"
:style="{width:'100%',background: '#' + getDayContent(item.createYear, '1', num, index).testTrainColorArray[0] }"
></div>
<div
class="tip-card"
v-show="getDayContent(item.createYear, '1', num, index).testTrainColorArray[1]"
:style="{width:'100%',background: '#' + getDayContent(item.createYear, '1', num, index).testTrainColorArray[1] }"
></div>
<div
class="tip-card"
v-show="getDayContent(item.createYear, '1', num, index).testTrainColorArray[2]"
:style="{width:'100%',background: '#' + getDayContent(item.createYear, '1', num, index).testTrainColorArray[2] }"
></div>
</div>
</div>
</div>
</td>
<td class="date-cell">
<div class="month">
<div v-for="num in nextYearJDays" :key="num" class="day">
<div class="tip-wrapper">
<div
class="tip-card"
v-show="getDayContent(item.createYear + 1, '1', num, index).testTrainColorArray[0]"
:style="{width:'100%',background: '#' + getDayContent(item.createYear + 1, '1', num, index).testTrainColorArray[0] }"
></div>
<div
class="tip-card"
v-show="getDayContent(item.createYear + 1, '1', num, index).testTrainColorArray[1]"
:style="{width:'100%',background: '#' + getDayContent(item.createYear + 1, '1', num, index).testTrainColorArray[1] }"
></div>
<div
class="tip-card"
v-show="getDayContent(item.createYear + 1, '1', num, index).testTrainColorArray[2]"
:style="{width:'100%',background: '#' + getDayContent(item.createYear + 1, '1', num, index).testTrainColorArray[2] }"
></div>
</div>
</div>
</div>
</td>
</tr>
</tbody>
<tfoot>
<th>合计</th>
<th>{{ countObj.projectName }}</th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
</tfoot>
</table>
</div>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import { findProjectSummarizeList } from '@/api/project.js'
export default {
data() {
return {
projectProcessShow: false,
list: [],
countObj: {},
januaryDays: 31, // new Date(new Date().getFullYear(), 1, 0).getDate(), // 一月
februaryDays: new Date(new Date().getFullYear(), 2, 0).getDate(), // 二月
marchDays: 31, // new Date(new Date().getFullYear(), 3, 0).getDate(), // 三月
aprilDays: 30, // new Date(new Date().getFullYear(), 4, 0).getDate(), // 四月
mayDays: 31, // new Date(new Date().getFullYear(), 5, 0).getDate(), // 五月
juneDays: 30, // new Date(new Date().getFullYear(), 6, 0).getDate(), // 六月
julyDays: 31, // new Date(new Date().getFullYear(), 7, 0).getDate(), // 七月
augustDays: 31, // new Date(new Date().getFullYear(), 8, 0).getDate(), // 八月
septemberDays: 30, // new Date(new Date().getFullYear(), 9, 0).getDate(), // 九月
octoberDays: 31, // new Date(new Date().getFullYear(), 10, 0).getDate(), // 十月
novemberDays: 30, // new Date(new Date().getFullYear(), 11, 0).getDate(), // 十一月
decemberDays: 31, // new Date(new Date().getFullYear(), 12, 0).getDate(), // 十二月
nextYearJDays: 31, // new Date(new Date().getFullYear() + 1, 1, 0).getDate(), // 下一年一月
showBoxShadow: false
}
},
computed: {
...mapGetters([
'year'
])
},
created() {
this.getList()
},
methods: {
getList() {
let year = this.year
if (year === '所有') {
year = ''
}
findProjectSummarizeList({ year: year, tabType: "1" }).then(res => {
if (res.code === 200) {
if (res.data.length > 0) {
// 合计行返回到了列表的最后一个元素,因为有些字段与列表不同,所以自定义了合计行
// 需要删除列表中的合计行,以免重复显示
this.countObj = res.data[res.data.length - 1]
res.data.splice(res.data.length - 1, 1)
}
this.list = res.data
}
})
},
getDayContent(year, month, day, index) {
if (month < 10){
month = '0' + month
}
if (day < 10) {
day = '0' + day
}
let date = year + '-' + month + '-' + day
let dateData = this.list[index].testProgress.find(item => {
return item.planDate === date
})
if (dateData && dateData.testTrainColorArray.length) {
return dateData
}else {
return {
planDate: day + '日',
testTrainColorArray: []
}
}
},
scrollEvent() {
const tbody = this.$el.querySelector('table')
if (tbody.scrollLeft > 0) {
this.showBoxShadow = true
} else {
this.showBoxShadow = false
}
}
}
}
</script>
<style lang="less" scoped>
.project-process {
width: 100%;
margin: 10px 0;
height: 69px;
img {
padding: 0 16px;
}
}
.process-table-popup {
overflow: auto;
.process-table-title {
width: 100%;
text-align: center;
font-size: 16px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #333333;
margin: 16px 0;
}
.schcontain {
// padding: 0px 22px ;
box-sizing: border-box;
background: #ffffff;
// color: #EBEDF0;;
font-size: 8px;
.scheduletag {
box-sizing: border-box;
display: flex;
justify-content: center;
.tagitem {
display: flex;
align-items: center;
margin-right: 15px;
.tag {
width: 24px;
height: 12px;
margin-right: 5px;
border-radius: 4px;
}
}
}
}
.process-table-content {
width: 100%;
height: calc(100vh - 220px);
table {
width: 100%;
height: 100%;
display: flex;
table-layout: fixed;
overflow: auto;
scroll-snap-type: x mandatory;
thead {
display: block;
float: left;
position: sticky;
left: 0;
z-index: 2;
th {
font-size: 12px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #666666;
width: 33vw;
padding: 3px 0;
display: block;
text-align: center;
height: 60px;
line-height: 60px;
background: #F1F3F8;
// background: #FFFFFF;
border-top: 1px solid #EBEDF0;
border-right: 1px solid #EBEDF0;
z-index: 2;
}
// th:first-child {
// background: #f1f3f8;
// }
th:last-child {
border-bottom: 1px solid #EBEDF0;
}
}
.box-shadow {
th {
box-shadow: 2px 10px 10px rgba(0, 0, 0, .12);
}
}
tbody {
display: flex;
float: left;
background: #FFFFFF;
tr td {
font-size: 12px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #666666;
width: 67vw;
padding: 3px 0;
display: block;
text-align: center;
height: 60px;
line-height: 60px;
border-top: 1px solid #EBEDF0;
border-left: 1px solid #EBEDF0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
scroll-snap-align: end;
.van-icon {
font-size: 16px;
}
}
tr:first-child td {
border-left: 0;
}
tr td:last-child {
border-bottom: 1px solid #EBEDF0;
}
tr .date-cell {
padding: 0;
.month {
height: 100%;
width: 100%;
display: flex;
.day {
flex: 1;
height: 60px;
border-right: 1px solid #dfe6ec;
}
.tip-wrapper {
display: flex;
flex-direction: column;
height: 60px;
.tip-card {
flex: 1;
}
}
.day:last-child {
border-right: 0;
}
.today {
background: red;
}
.weituo {
background: rgb(0, 176, 240);
}
.dagang {
background: rgb(146, 208, 80);
}
.qidonghui {
background: rgb(255, 0, 0);
}
.lieche {
background: rgb(0, 176, 80);
}
.dongyan {
background: rgb(255, 192, 0);
}
.kaitong {
background: rgb(112, 48, 160);
}
}
}
tr {
td:first-child {
background: #f1f3f8;
}
}
}
tfoot {
display: block;
float: left;
background: #FFFFFF;
th {
font-size: 12px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #666666;
width: 33vw;
padding: 3px 0;
display: block;
text-align: center;
height: 60px;
line-height: 60px;
border-left: 1px solid #EBEDF0;
border-top: 1px solid #EBEDF0;
scroll-snap-align: end;
}
th:first-child {
background: #f1f3f8;
}
th:last-child {
border-bottom: 1px solid #EBEDF0;
}
}
// 隐藏滚动条
&::-webkit-scrollbar {
display: none;
}
}
}
}
</style>
vuex:
import Vue from 'vue'
import Vuex from 'vuex'
import router from '@/router'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
deviceTab: '',
},
mutations: {
//线路信息
SET_DEVICE_TAB: (state, tabName) => {
state.deviceTab = tabName;
},
},
actions: {
},
})
export default store
这是修改完的,可以运行起来的代码。
简述下小插曲:
1、因为第一个页签是默认加载,在父组件的created中进行了判断,但是一直获取不到第一个子组件的请求方法。后来发现问题是没有在第一个子组件中的created中自动调取获取数据的方法。加上就可以了。
2、使用ref操作或者说切换dom的显示隐藏,我一般使用v-if