工程统计页面完善
This commit is contained in:
parent
fc3d74c997
commit
d7965c45ff
|
|
@ -0,0 +1,10 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 综合查询 获取工程统计列表
|
||||
export const getProjectCountListAPI = (data) => {
|
||||
return request({
|
||||
url: '/bmw/workerLight/xxx',
|
||||
method: 'GET',
|
||||
params: data,
|
||||
})
|
||||
}
|
||||
|
|
@ -74,6 +74,21 @@ export const constantRoutes = [
|
|||
permissions: ['home:page:query'],
|
||||
meta: { title: '首页', icon: 'dashboard', affix: true },
|
||||
},
|
||||
|
||||
// 工程统计详情页面
|
||||
{
|
||||
path: 'project-count-detail',
|
||||
name: 'ProjectCountDetail',
|
||||
hidden: true,
|
||||
component: () =>
|
||||
import(
|
||||
'@/views/synthesize-query/project-count/detail/index'
|
||||
),
|
||||
meta: {
|
||||
title: '工程统计详情',
|
||||
activeMenu: '/synthesize-query/project-count',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
export const formLabel = [
|
||||
{
|
||||
isShow: false, // 是否展示label
|
||||
f_type: 'ipt',
|
||||
f_label: '工程名称',
|
||||
f_model: 'proName',
|
||||
},
|
||||
{
|
||||
isShow: false, // 是否展示label
|
||||
f_type: 'sel',
|
||||
f_label: '工程状态',
|
||||
f_model: 'proStatus',
|
||||
},
|
||||
]
|
||||
|
||||
export const columnsList = [
|
||||
{ t_props: 'mainProName', t_label: '分公司' },
|
||||
{ t_label: '工程名称', t_props: 'volLevel' },
|
||||
|
||||
{
|
||||
t_label: '分包数量',
|
||||
t_props: 'volLevel',
|
||||
},
|
||||
{ t_slot: 'proStatus', t_label: '班组数量' },
|
||||
{ t_slot: 'proStatus', t_label: '在场' },
|
||||
{ t_slot: 'proStatus', t_label: '工程类型' },
|
||||
{ t_slot: 'proStatus', t_label: '电压等级' },
|
||||
{ t_slot: 'proStatus', t_label: '状态' },
|
||||
{ t_slot: 'proStatus', t_label: '工程地址' },
|
||||
]
|
||||
|
|
@ -0,0 +1,337 @@
|
|||
<template>
|
||||
<!-- 考勤机信息卡片 -->
|
||||
<div class="sub-team-card-container">
|
||||
<!-- 考勤机信息表格 -->
|
||||
<div class="section-container">
|
||||
<div class="section-header">
|
||||
<div class="section-title">
|
||||
<div class="title-indicator"></div>
|
||||
<span>考勤机明细</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-container" ref="attMachineCardRef">
|
||||
<el-table
|
||||
:data="subcontractorData"
|
||||
style="width: 100%"
|
||||
:header-cell-style="tableHeaderStyle"
|
||||
:cell-style="tableCellStyle"
|
||||
stripe
|
||||
border
|
||||
>
|
||||
<el-table-column
|
||||
prop="subcontractorName"
|
||||
label="考勤机编号"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column prop="contractCode" label="考勤机名称" />
|
||||
|
||||
<el-table-column
|
||||
prop="entryExitStatus"
|
||||
label="状态"
|
||||
width="120"
|
||||
align="center"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<div class="status-cell">
|
||||
<span
|
||||
class="status-dot"
|
||||
:class="
|
||||
scope.row.entryExitStatus === '在场'
|
||||
? 'status-online'
|
||||
: 'status-offline'
|
||||
"
|
||||
></span>
|
||||
{{ scope.row.entryExitStatus }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="totalEntryCount"
|
||||
label="绑定人"
|
||||
width="120"
|
||||
align="center"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="currentOnsiteCount"
|
||||
label="绑定时间"
|
||||
width="120"
|
||||
align="center"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="entryTime"
|
||||
label="绑定人"
|
||||
width="130"
|
||||
/>
|
||||
|
||||
<el-table-column
|
||||
prop="exitTime"
|
||||
label="解绑时间"
|
||||
width="130"
|
||||
/>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'SubTeamCard',
|
||||
data() {
|
||||
return {
|
||||
// 分包信息数据
|
||||
subcontractorData: [
|
||||
{
|
||||
subcontractorName: '重庆市万州水电建筑工程有限公司',
|
||||
contractCode: 'SXFGS-YH-001',
|
||||
contractName: '220kV永和(云纺)变电站建筑工程专业分包合同1',
|
||||
entryExitStatus: '在场',
|
||||
totalEntryCount: 80,
|
||||
currentOnsiteCount: 56,
|
||||
entryTime: '2025-04-06',
|
||||
authorizationLetter: '已上传',
|
||||
exitTime: '/',
|
||||
wageCommitment: '/',
|
||||
},
|
||||
{
|
||||
subcontractorName: '深圳市铁越电气有限公司',
|
||||
contractCode: 'SXFGS-YH-005',
|
||||
contractName:
|
||||
'220kV 永和(云纺)输变电工程架线施工劳务分包框架子合同',
|
||||
entryExitStatus: '离场',
|
||||
totalEntryCount: 45,
|
||||
currentOnsiteCount: 0,
|
||||
entryTime: '2025-04-06',
|
||||
authorizationLetter: '已上传',
|
||||
exitTime: '2025-10-06',
|
||||
wageCommitment: '已上传',
|
||||
},
|
||||
],
|
||||
// 班组信息数据
|
||||
teamData: [
|
||||
{
|
||||
teamName: '组塔一班',
|
||||
teamLeader: '颜静宁 13875286111',
|
||||
subcontractorName: '重庆市万州水电建筑工程有限公司',
|
||||
entryExitStatus: '在场',
|
||||
totalEntryCount: 80,
|
||||
currentOnsiteCount: 56,
|
||||
entryTime: '2025-04-06',
|
||||
exitTime: '/',
|
||||
commitmentLetter: '/',
|
||||
},
|
||||
{
|
||||
teamName: '架线三班',
|
||||
teamLeader: '颜静宁 13875286111',
|
||||
subcontractorName: '重庆市万州水电建筑工程有限公司',
|
||||
entryExitStatus: '离场',
|
||||
totalEntryCount: 45,
|
||||
currentOnsiteCount: 0,
|
||||
entryTime: '2025-04-06',
|
||||
exitTime: '2025-12-06',
|
||||
commitmentLetter: '已上传',
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 表格头部样式
|
||||
tableHeaderStyle() {
|
||||
return {
|
||||
backgroundColor: '#f5f7fa',
|
||||
color: '#606266',
|
||||
fontWeight: '500',
|
||||
fontSize: '14px',
|
||||
textAlign: 'center',
|
||||
}
|
||||
},
|
||||
// 表格单元格样式
|
||||
tableCellStyle() {
|
||||
return {
|
||||
fontSize: '14px',
|
||||
color: '#606266',
|
||||
padding: '12px 8px',
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
console.log(this.$refs.attMachineCardRef, '9996')
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '@/assets/styles/variables.scss';
|
||||
|
||||
.sub-team-card-container {
|
||||
margin-bottom: 20px;
|
||||
|
||||
.section-container {
|
||||
background: #ffffff;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
margin-bottom: 20px;
|
||||
overflow: hidden;
|
||||
|
||||
.section-header {
|
||||
padding: 20px 24px 16px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
|
||||
.section-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #1f2937;
|
||||
|
||||
.title-indicator {
|
||||
width: 4px;
|
||||
height: 20px;
|
||||
background: #3b82f6;
|
||||
border-radius: 2px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.table-container {
|
||||
padding: 0;
|
||||
|
||||
::v-deep .el-table {
|
||||
border: none;
|
||||
|
||||
.el-table__header-wrapper {
|
||||
.el-table__header {
|
||||
th {
|
||||
border-bottom: 1px solid #e4e7ed;
|
||||
border-right: 1px solid #e4e7ed;
|
||||
|
||||
&:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-table__body-wrapper {
|
||||
.el-table__body {
|
||||
tr {
|
||||
&:hover {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
td {
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
border-right: 1px solid #f0f0f0;
|
||||
|
||||
&:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.status-cell {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.status-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
margin-right: 6px;
|
||||
display: inline-block;
|
||||
|
||||
&.status-online {
|
||||
background-color: #10b981;
|
||||
}
|
||||
|
||||
&.status-offline {
|
||||
background-color: #ef4444;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.upload-status {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
|
||||
&.uploaded {
|
||||
color: #10b981;
|
||||
}
|
||||
|
||||
&.not-uploaded {
|
||||
color: #9ca3af;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 响应式设计
|
||||
@media (max-width: 1200px) {
|
||||
.sub-team-card-container {
|
||||
.section-container {
|
||||
.table-container {
|
||||
::v-deep .el-table {
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.sub-team-card-container {
|
||||
.section-container {
|
||||
.section-header {
|
||||
padding: 16px 20px 12px;
|
||||
|
||||
.section-title {
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.table-container {
|
||||
::v-deep .el-table {
|
||||
font-size: 12px;
|
||||
|
||||
.el-table__header-wrapper {
|
||||
.el-table__header {
|
||||
th {
|
||||
padding: 8px 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-table__body-wrapper {
|
||||
.el-table__body {
|
||||
td {
|
||||
padding: 8px 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.status-cell {
|
||||
.status-dot {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.upload-status {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,547 @@
|
|||
<template>
|
||||
<!-- 统计卡片组件 -->
|
||||
<div class="card-count-container">
|
||||
<!-- 加载状态 -->
|
||||
<div v-if="loading" class="loading-container">
|
||||
<div class="cards-grid">
|
||||
<div v-for="index in 7" :key="index" class="card-item">
|
||||
<div class="statistics-card loading-card">
|
||||
<div class="card-content">
|
||||
<el-skeleton :rows="2" animated />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 正常显示状态 -->
|
||||
<div v-else class="cards-display">
|
||||
<div class="cards-grid">
|
||||
<div
|
||||
v-for="(card, index) in cardList"
|
||||
:key="index"
|
||||
class="card-item"
|
||||
>
|
||||
<div
|
||||
class="statistics-card"
|
||||
:class="`card-${card.type}`"
|
||||
@click="handleCardClick(card)"
|
||||
:title="`点击查看${card.label}详情`"
|
||||
>
|
||||
<!-- 左侧彩色条 -->
|
||||
<div
|
||||
class="card-indicator"
|
||||
:style="{ backgroundColor: card.color }"
|
||||
></div>
|
||||
|
||||
<!-- 卡片内容 -->
|
||||
<div class="card-content">
|
||||
<!-- 数字 -->
|
||||
<div class="card-number">
|
||||
{{ formatNumber(card.value) }}
|
||||
</div>
|
||||
|
||||
<!-- 标签 -->
|
||||
<div class="card-label">
|
||||
<span
|
||||
class="label-dot"
|
||||
:style="{
|
||||
backgroundColor:
|
||||
card.dotColor || card.color,
|
||||
}"
|
||||
>
|
||||
</span>
|
||||
{{ card.label }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 悬停效果 -->
|
||||
<div class="card-hover-effect"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getProjectCountListAPI } from '@/api/synthesize-query/project-count'
|
||||
|
||||
export default {
|
||||
name: 'CardCount',
|
||||
props: {
|
||||
// 项目ID,用于获取特定项目的统计数据
|
||||
projectId: {
|
||||
type: [String, Number],
|
||||
default: null,
|
||||
},
|
||||
// 是否自动加载数据
|
||||
autoLoad: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
cardList: [
|
||||
{
|
||||
type: 'subcontractor',
|
||||
label: '分包单位',
|
||||
value: 0,
|
||||
color: '#EF4444',
|
||||
dotColor: '#EF4444',
|
||||
dataKey: 'subcontractorCount',
|
||||
},
|
||||
{
|
||||
type: 'team',
|
||||
label: '施工班组',
|
||||
value: 0,
|
||||
color: '#3B82F6',
|
||||
dotColor: '#3B82F6',
|
||||
dataKey: 'teamCount',
|
||||
},
|
||||
{
|
||||
type: 'entry',
|
||||
label: '累计入场',
|
||||
value: 0,
|
||||
color: '#8B5CF6',
|
||||
dotColor: '#8B5CF6',
|
||||
dataKey: 'totalEntryCount',
|
||||
},
|
||||
{
|
||||
type: 'onsite',
|
||||
label: '当前在场',
|
||||
value: 0,
|
||||
color: '#F59E0B',
|
||||
dotColor: '#F59E0B', // 注意:图片中这个点的颜色是紫色
|
||||
dataKey: 'currentOnsiteCount',
|
||||
},
|
||||
{
|
||||
type: 'attendance',
|
||||
label: '累计考勤',
|
||||
value: 0,
|
||||
color: '#06B6D4',
|
||||
dotColor: '#06B6D4',
|
||||
dataKey: 'totalAttendanceCount',
|
||||
},
|
||||
{
|
||||
type: 'wage',
|
||||
label: '累计工资',
|
||||
value: 0,
|
||||
color: '#EC4899',
|
||||
dotColor: '#EC4899', // 注意:图片中这个点的颜色是绿色
|
||||
dataKey: 'totalWageCount',
|
||||
},
|
||||
{
|
||||
type: 'machine',
|
||||
label: '考勤机',
|
||||
value: 0,
|
||||
color: '#10B981',
|
||||
dotColor: '#10B981',
|
||||
dataKey: 'attendanceMachineCount',
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (this.autoLoad) {
|
||||
this.loadData()
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
projectId: {
|
||||
handler() {
|
||||
if (this.autoLoad) {
|
||||
this.loadData()
|
||||
}
|
||||
},
|
||||
immediate: false,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 加载统计数据
|
||||
*/
|
||||
async loadData() {
|
||||
if (this.loading) return
|
||||
|
||||
this.loading = true
|
||||
try {
|
||||
// 这里应该调用实际的API接口
|
||||
// const response = await getProjectCountListAPI({
|
||||
// projectId: this.projectId
|
||||
// })
|
||||
|
||||
// 模拟API响应数据
|
||||
const mockData = {
|
||||
subcontractorCount: 5,
|
||||
teamCount: 12,
|
||||
totalEntryCount: 1200,
|
||||
currentOnsiteCount: 200,
|
||||
totalAttendanceCount: 9600,
|
||||
totalWageCount: 745635,
|
||||
attendanceMachineCount: 14,
|
||||
}
|
||||
|
||||
// 更新卡片数据
|
||||
this.updateCardData(mockData)
|
||||
|
||||
this.$emit('data-loaded', mockData)
|
||||
} catch (error) {
|
||||
console.error('加载统计数据失败:', error)
|
||||
this.$message.error('加载统计数据失败')
|
||||
this.$emit('data-error', error)
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 更新卡片数据
|
||||
* @param {Object} data 统计数据
|
||||
*/
|
||||
updateCardData(data) {
|
||||
this.cardList.forEach((card) => {
|
||||
if (data.hasOwnProperty(card.dataKey)) {
|
||||
card.value = data[card.dataKey] || 0
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 刷新数据
|
||||
*/
|
||||
refresh() {
|
||||
this.loadData()
|
||||
},
|
||||
|
||||
/**
|
||||
* 格式化数字显示
|
||||
* @param {Number} value 数值
|
||||
* @returns {String} 格式化后的字符串
|
||||
*/
|
||||
formatNumber(value) {
|
||||
if (value >= 1000000) {
|
||||
return (value / 1000000).toFixed(1) + 'M'
|
||||
} else if (value >= 1000) {
|
||||
return (value / 1000).toFixed(1) + 'K'
|
||||
} else {
|
||||
return value.toLocaleString()
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 处理卡片点击事件
|
||||
* @param {Object} card 卡片数据
|
||||
*/
|
||||
handleCardClick(card) {
|
||||
// 添加点击反馈效果
|
||||
// const cardElement = event.currentTarget
|
||||
// cardElement.style.transform = 'scale(0.95)'
|
||||
// setTimeout(() => {
|
||||
// cardElement.style.transform = ''
|
||||
// }, 150)
|
||||
|
||||
this.$emit('cardClick', card)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '@/assets/styles/variables.scss';
|
||||
|
||||
.card-count-container {
|
||||
margin-bottom: 20px;
|
||||
|
||||
.cards-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(7, 1fr);
|
||||
gap: 20px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.card-item {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.statistics-card {
|
||||
position: relative;
|
||||
background: #ffffff;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
overflow: hidden;
|
||||
height: 120px;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 20px 0 rgba(0, 0, 0, 0.15);
|
||||
|
||||
.card-hover-effect {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: translateY(-1px) scale(0.98);
|
||||
box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
// 加载状态卡片
|
||||
&.loading-card {
|
||||
cursor: default;
|
||||
background: #f8f9fa;
|
||||
|
||||
&:hover {
|
||||
transform: none;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.card-content {
|
||||
padding: 20px;
|
||||
|
||||
::v-deep .el-skeleton {
|
||||
.el-skeleton__item {
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
#f2f2f2 25%,
|
||||
#e6e6e6 37%,
|
||||
#f2f2f2 63%
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 左侧彩色指示条
|
||||
.card-indicator {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 4px;
|
||||
height: 100%;
|
||||
border-radius: 0 2px 2px 0;
|
||||
}
|
||||
|
||||
// 卡片内容
|
||||
.card-content {
|
||||
padding: 20px 20px 20px 24px;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
|
||||
.card-number {
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
color: #374151;
|
||||
margin-bottom: 8px;
|
||||
line-height: 1.2;
|
||||
font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.card-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
color: #6b7280;
|
||||
font-weight: 500;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
.label-dot {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
margin-right: 6px;
|
||||
flex-shrink: 0;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 悬停效果层
|
||||
.card-hover-effect {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
rgba(255, 255, 255, 0.1) 0%,
|
||||
rgba(255, 255, 255, 0.05) 100%
|
||||
);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
// 不同卡片类型的背景色
|
||||
&.card-subcontractor {
|
||||
background: linear-gradient(135deg, #fef2f2 0%, #fce7f3 100%);
|
||||
}
|
||||
|
||||
&.card-team {
|
||||
background: linear-gradient(135deg, #eff6ff 0%, #dbeafe 100%);
|
||||
}
|
||||
|
||||
&.card-entry {
|
||||
background: linear-gradient(135deg, #f3e8ff 0%, #e9d5ff 100%);
|
||||
}
|
||||
|
||||
&.card-onsite {
|
||||
background: linear-gradient(135deg, #fffbeb 0%, #fef3c7 100%);
|
||||
}
|
||||
|
||||
&.card-attendance {
|
||||
background: linear-gradient(135deg, #ecfeff 0%, #cffafe 100%);
|
||||
}
|
||||
|
||||
&.card-wage {
|
||||
background: linear-gradient(135deg, #fdf2f8 0%, #fce7f3 100%);
|
||||
}
|
||||
|
||||
&.card-machine {
|
||||
background: linear-gradient(135deg, #ecfdf5 0%, #d1fae5 100%);
|
||||
}
|
||||
}
|
||||
|
||||
// 响应式设计
|
||||
@media (max-width: 1400px) {
|
||||
.card-count-container {
|
||||
.cards-grid {
|
||||
grid-template-columns: repeat(6, 1fr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
.card-count-container {
|
||||
.cards-grid {
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
.statistics-card {
|
||||
height: 110px;
|
||||
|
||||
.card-content {
|
||||
padding: 18px 18px 18px 22px;
|
||||
|
||||
.card-number {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.card-label {
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 992px) {
|
||||
.card-count-container {
|
||||
.cards-grid {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.card-count-container {
|
||||
.cards-grid {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.statistics-card {
|
||||
height: 100px;
|
||||
|
||||
.card-content {
|
||||
padding: 16px 16px 16px 20px;
|
||||
|
||||
.card-number {
|
||||
font-size: 20px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.card-label {
|
||||
font-size: 12px;
|
||||
|
||||
.label-dot {
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 576px) {
|
||||
.card-count-container {
|
||||
.cards-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.statistics-card {
|
||||
height: 90px;
|
||||
|
||||
.card-content {
|
||||
padding: 14px 14px 14px 18px;
|
||||
|
||||
.card-number {
|
||||
font-size: 18px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.card-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.card-count-container {
|
||||
.cards-grid {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.statistics-card {
|
||||
height: 80px;
|
||||
|
||||
.card-content {
|
||||
padding: 12px 12px 12px 16px;
|
||||
|
||||
.card-number {
|
||||
font-size: 16px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.card-label {
|
||||
font-size: 10px;
|
||||
|
||||
.label-dot {
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,261 @@
|
|||
<template>
|
||||
<!-- 头部工程详情信息 -->
|
||||
<div class="header-info">
|
||||
<el-card class="project-info-card">
|
||||
<div class="project-header">
|
||||
<!-- 项目图标区域 -->
|
||||
<div class="project-icon">
|
||||
<div class="icon-placeholder">
|
||||
<!-- 图标占位符,后续可替换为实际图标 -->
|
||||
<i class="el-icon-setting"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 项目信息区域 -->
|
||||
<div class="project-details">
|
||||
<div class="project-title">
|
||||
{{ projectInfo.projectName }}
|
||||
</div>
|
||||
<div class="company-name">
|
||||
{{ projectInfo.companyName }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 项目属性信息 -->
|
||||
<div class="project-attributes">
|
||||
<div class="attribute-row">
|
||||
<div
|
||||
class="attribute-item"
|
||||
v-for="item in labelList"
|
||||
:key="item.valueKey"
|
||||
>
|
||||
<span class="attribute-label">{{ item.label }}</span>
|
||||
<span
|
||||
class="attribute-value"
|
||||
:class="{
|
||||
'status-active':
|
||||
item.valueKey === 'currentStatus',
|
||||
}"
|
||||
>
|
||||
<span class="status-dot"></span>
|
||||
{{ projectInfo[item.valueKey] }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'HeaderInfo',
|
||||
data() {
|
||||
return {
|
||||
projectInfo: {
|
||||
projectName: '220kV永和(云纺)输变电工程(变电部分)',
|
||||
companyName: '输电一公司',
|
||||
currentStatus: '在建',
|
||||
projectType: '基建线路',
|
||||
voltageLevel: '220kV',
|
||||
plannedStartDate: '2025-02-01',
|
||||
plannedCompletionDate: '2025-11-30',
|
||||
projectLocation:
|
||||
'广东省-东莞市广东省-东莞市广东省-东莞市广东省-东莞市广东省-东莞市广东省-东莞市',
|
||||
},
|
||||
|
||||
labelList: [
|
||||
{
|
||||
label: '当前状态',
|
||||
value: '在建',
|
||||
valueKey: 'currentStatus',
|
||||
},
|
||||
{
|
||||
label: '工程类型',
|
||||
value: '基建线路',
|
||||
valueKey: 'projectType',
|
||||
},
|
||||
{
|
||||
label: '电压等级',
|
||||
value: '220kV',
|
||||
valueKey: 'voltageLevel',
|
||||
},
|
||||
{
|
||||
label: '计划开工时间',
|
||||
value: '2025-02-01',
|
||||
valueKey: 'plannedStartDate',
|
||||
},
|
||||
{
|
||||
label: '计划完工时间',
|
||||
value: '2025-11-30',
|
||||
valueKey: 'plannedCompletionDate',
|
||||
},
|
||||
{
|
||||
label: '工程地址',
|
||||
value: '广东省-东莞市',
|
||||
valueKey: 'projectLocation',
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '@/assets/styles/variables.scss';
|
||||
|
||||
.header-info {
|
||||
margin-bottom: 20px;
|
||||
|
||||
.project-info-card {
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
border: none;
|
||||
|
||||
::v-deep .el-card__body {
|
||||
padding: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.project-header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 24px;
|
||||
|
||||
.project-icon {
|
||||
margin-right: 20px;
|
||||
flex-shrink: 0;
|
||||
|
||||
.icon-placeholder {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
background: linear-gradient(135deg, #fce7f3 0%, #f3e8ff 100%);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 4px 12px rgba(236, 72, 153, 0.2);
|
||||
|
||||
i {
|
||||
font-size: 28px;
|
||||
color: #ec4899;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.project-details {
|
||||
flex: 1;
|
||||
|
||||
.project-title {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
color: #1f2937;
|
||||
margin-bottom: 8px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.company-name {
|
||||
font-size: 14px;
|
||||
color: #6b7280;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.project-attributes {
|
||||
margin-left: 80px; // 与项目图标区域对齐,确保与项目名称左对齐
|
||||
|
||||
.attribute-row {
|
||||
display: flex;
|
||||
gap: 70px;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.attribute-item {
|
||||
// flex: 1;
|
||||
// min-width: 120px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start; // 改为左对齐
|
||||
text-align: left; // 改为左对齐
|
||||
|
||||
.attribute-label {
|
||||
font-size: 12px;
|
||||
color: #9ca3af;
|
||||
margin-bottom: 16px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.attribute-value {
|
||||
font-size: 14px;
|
||||
color: #374151;
|
||||
font-weight: 500;
|
||||
word-break: break-all;
|
||||
|
||||
&.status-active {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start; // 改为左对齐
|
||||
|
||||
.status-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background-color: $green;
|
||||
border-radius: 50%;
|
||||
margin-right: 6px;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 响应式设计
|
||||
@media (max-width: 1200px) {
|
||||
.header-info {
|
||||
.project-attributes {
|
||||
margin-left: 80px; // 保持对齐
|
||||
|
||||
.attribute-row {
|
||||
.attribute-item {
|
||||
min-width: 100px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.header-info {
|
||||
.project-header {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
|
||||
.project-icon {
|
||||
margin-right: 0;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.project-attributes {
|
||||
margin-left: 0; // 移动端取消左边距,居中对齐
|
||||
text-align: center;
|
||||
|
||||
.attribute-row {
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
|
||||
.attribute-item {
|
||||
min-width: auto;
|
||||
width: 100%;
|
||||
align-items: center; // 移动端居中对齐
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,624 @@
|
|||
<template>
|
||||
<!-- 人员信息卡片 -->
|
||||
<div class="person-info-container">
|
||||
<!-- 出入场信息 -->
|
||||
<div class="info-section">
|
||||
<div class="section-header">
|
||||
<div class="section-title">
|
||||
<div class="title-indicator"></div>
|
||||
<span>出入场信息</span>
|
||||
</div>
|
||||
<div class="view-more">
|
||||
<span>查看更多></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-grid">
|
||||
<div class="data-item">
|
||||
<div class="data-label">累计入场人数</div>
|
||||
<div class="data-value">{{
|
||||
personData.entry.totalEntryCount
|
||||
}}</div>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<div class="data-label">累计出场人数</div>
|
||||
<div class="data-value">{{
|
||||
personData.entry.totalExitCount
|
||||
}}</div>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<div class="data-label">离场人员工资结算确认单</div>
|
||||
<div class="data-value">
|
||||
<div class="status-item">
|
||||
<span class="status-dot uploaded"></span>
|
||||
<span
|
||||
>已上传:
|
||||
{{
|
||||
personData.entry.salaryConfirmationUploaded
|
||||
}}</span
|
||||
>
|
||||
</div>
|
||||
<div class="status-item">
|
||||
<span class="status-dot not-uploaded"></span>
|
||||
<span
|
||||
>未上传:
|
||||
{{
|
||||
personData.entry
|
||||
.salaryConfirmationNotUploaded
|
||||
}}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<div class="data-label">首次人员入场时间</div>
|
||||
<div class="data-value">{{
|
||||
personData.entry.firstEntryTime
|
||||
}}</div>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<div class="data-label">当前在场人数</div>
|
||||
<div class="data-value">{{
|
||||
personData.entry.currentOnsiteCount
|
||||
}}</div>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<div class="data-label">在场人数状态</div>
|
||||
<div class="data-value">
|
||||
<div class="status-item">
|
||||
<span class="status-dot yellow"></span>
|
||||
<span
|
||||
>黄灯:
|
||||
{{ personData.entry.yellowLightCount }}</span
|
||||
>
|
||||
</div>
|
||||
<div class="status-item">
|
||||
<span class="status-dot green"></span>
|
||||
<span
|
||||
>绿灯:
|
||||
{{ personData.entry.greenLightCount }}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<div class="data-label">黄灯7天人数</div>
|
||||
<div class="data-value">{{
|
||||
personData.entry.yellowLight7DaysCount
|
||||
}}</div>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<div class="data-label">末次人员出场时间</div>
|
||||
<div class="data-value">{{
|
||||
personData.entry.lastExitTime
|
||||
}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 考勤信息 -->
|
||||
<div class="info-section">
|
||||
<div class="section-header">
|
||||
<div class="section-title">
|
||||
<div class="title-indicator"></div>
|
||||
<span>考勤信息</span>
|
||||
</div>
|
||||
<div class="view-more">
|
||||
<span>查看更多></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-grid">
|
||||
<div class="data-item">
|
||||
<div class="data-label">累计考勤天数(人次)</div>
|
||||
<div class="data-value">{{
|
||||
personData.attendance.totalAttendanceDays
|
||||
}}</div>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<div class="data-label">累计补卡申请次数(次)</div>
|
||||
<div class="data-value">
|
||||
{{
|
||||
personData.attendance
|
||||
.totalCardReplacementApplications
|
||||
}}
|
||||
<span
|
||||
class="view-link"
|
||||
@click="handleViewCardReplacement"
|
||||
>查看</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<div class="data-label">累计补卡人数(人)</div>
|
||||
<div class="data-value">{{
|
||||
personData.attendance.totalCardReplacementPersons
|
||||
}}</div>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<div class="data-label">累计补卡天数(人次)</div>
|
||||
<div class="data-value">{{
|
||||
personData.attendance.totalCardReplacementDays
|
||||
}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 工资信息 -->
|
||||
<div class="info-section">
|
||||
<div class="section-header">
|
||||
<div class="section-title">
|
||||
<div class="title-indicator"></div>
|
||||
<span>工资信息</span>
|
||||
</div>
|
||||
<div class="view-more">
|
||||
<span>查看更多></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-grid">
|
||||
<div class="data-item">
|
||||
<div class="data-label">累计发放工资(元)</div>
|
||||
<div class="data-value salary-amount">{{
|
||||
formatCurrency(personData.salary.totalSalaryIssued)
|
||||
}}</div>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<div class="data-label">工资册数量(次)</div>
|
||||
<div class="data-value">{{
|
||||
personData.salary.payrollBookCount
|
||||
}}</div>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<div class="data-label">累计发放人数</div>
|
||||
<div class="data-value">{{
|
||||
personData.salary.totalIssuedPersons
|
||||
}}</div>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<div class="data-label">累计发放人次</div>
|
||||
<div class="data-value">{{
|
||||
personData.salary.totalIssuedPersonTimes
|
||||
}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'PersonInfoCard',
|
||||
props: {
|
||||
// 项目ID,用于获取特定项目的人员信息
|
||||
projectId: {
|
||||
type: [String, Number],
|
||||
default: null,
|
||||
},
|
||||
// 是否自动加载数据
|
||||
autoLoad: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
personData: {
|
||||
entry: {
|
||||
totalEntryCount: 1200,
|
||||
totalExitCount: 800,
|
||||
salaryConfirmationUploaded: 390,
|
||||
salaryConfirmationNotUploaded: 10,
|
||||
firstEntryTime: '2025-09-02 10:12:13',
|
||||
currentOnsiteCount: 400,
|
||||
yellowLightCount: 10,
|
||||
greenLightCount: 390,
|
||||
yellowLight7DaysCount: 2,
|
||||
lastExitTime: '2025-10-02 19:12:13',
|
||||
},
|
||||
attendance: {
|
||||
totalAttendanceDays: 9600,
|
||||
totalCardReplacementApplications: 10,
|
||||
totalCardReplacementPersons: 35,
|
||||
totalCardReplacementDays: 156,
|
||||
},
|
||||
salary: {
|
||||
totalSalaryIssued: 745635,
|
||||
payrollBookCount: 10,
|
||||
totalIssuedPersons: 253,
|
||||
totalIssuedPersonTimes: 1352,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (this.autoLoad) {
|
||||
this.loadData()
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
projectId: {
|
||||
handler() {
|
||||
if (this.autoLoad) {
|
||||
this.loadData()
|
||||
}
|
||||
},
|
||||
immediate: false,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 加载人员信息数据
|
||||
*/
|
||||
async loadData() {
|
||||
if (this.loading) return
|
||||
|
||||
this.loading = true
|
||||
try {
|
||||
// 这里应该调用实际的API接口
|
||||
// const response = await getPersonInfoAPI({
|
||||
// projectId: this.projectId
|
||||
// })
|
||||
|
||||
// 模拟API响应数据
|
||||
const mockData = {
|
||||
entry: {
|
||||
totalEntryCount: 1200,
|
||||
totalExitCount: 800,
|
||||
salaryConfirmationUploaded: 390,
|
||||
salaryConfirmationNotUploaded: 10,
|
||||
firstEntryTime: '2025-09-02 10:12:13',
|
||||
currentOnsiteCount: 400,
|
||||
yellowLightCount: 10,
|
||||
greenLightCount: 390,
|
||||
yellowLight7DaysCount: 2,
|
||||
lastExitTime: '2025-10-02 19:12:13',
|
||||
},
|
||||
attendance: {
|
||||
totalAttendanceDays: 9600,
|
||||
totalCardReplacementApplications: 10,
|
||||
totalCardReplacementPersons: 35,
|
||||
totalCardReplacementDays: 156,
|
||||
},
|
||||
salary: {
|
||||
totalSalaryIssued: 745635,
|
||||
payrollBookCount: 10,
|
||||
totalIssuedPersons: 253,
|
||||
totalIssuedPersonTimes: 1352,
|
||||
},
|
||||
}
|
||||
|
||||
this.personData = mockData
|
||||
this.$emit('data-loaded', mockData)
|
||||
} catch (error) {
|
||||
console.error('加载人员信息失败:', error)
|
||||
this.$message.error('加载人员信息失败')
|
||||
this.$emit('data-error', error)
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 刷新数据
|
||||
*/
|
||||
refresh() {
|
||||
this.loadData()
|
||||
},
|
||||
|
||||
/**
|
||||
* 格式化货币显示
|
||||
* @param {Number} value 金额
|
||||
* @returns {String} 格式化后的货币字符串
|
||||
*/
|
||||
formatCurrency(value) {
|
||||
return `¥${value.toLocaleString()}`
|
||||
},
|
||||
|
||||
/**
|
||||
* 处理查看补卡申请点击事件
|
||||
*/
|
||||
handleViewCardReplacement() {
|
||||
this.$emit('view-card-replacement')
|
||||
console.log('查看补卡申请')
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '@/assets/styles/variables.scss';
|
||||
|
||||
.person-info-container {
|
||||
.info-section {
|
||||
background: #ffffff;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
margin-bottom: 20px;
|
||||
overflow: hidden;
|
||||
|
||||
.section-header {
|
||||
padding: 20px 24px 16px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.section-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #1f2937;
|
||||
|
||||
.title-indicator {
|
||||
width: 4px;
|
||||
height: 20px;
|
||||
background: #3b82f6;
|
||||
border-radius: 2px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.view-more {
|
||||
color: #3b82f6;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
transition: color 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
color: #1d4ed8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.data-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 0;
|
||||
padding: 0;
|
||||
|
||||
.data-item {
|
||||
padding: 20px 24px;
|
||||
border-right: 1px solid #f0f0f0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
background: #f8f9fa;
|
||||
transition: background-color 0.3s ease;
|
||||
|
||||
&:nth-child(4n) {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
&:nth-last-child(-n + 4) {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: #f1f5f9;
|
||||
}
|
||||
|
||||
.data-label {
|
||||
font-size: 14px;
|
||||
color: #6b7280;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 500;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.data-value {
|
||||
font-size: 18px;
|
||||
color: #1f2937;
|
||||
font-weight: 600;
|
||||
line-height: 1.3;
|
||||
|
||||
&.salary-amount {
|
||||
color: #059669;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.status-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 4px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.status-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
margin-right: 6px;
|
||||
display: inline-block;
|
||||
|
||||
&.uploaded {
|
||||
background-color: #10b981;
|
||||
}
|
||||
|
||||
&.not-uploaded {
|
||||
background-color: #f59e0b;
|
||||
}
|
||||
|
||||
&.yellow {
|
||||
background-color: #f59e0b;
|
||||
}
|
||||
|
||||
&.green {
|
||||
background-color: #10b981;
|
||||
}
|
||||
}
|
||||
|
||||
span:last-child {
|
||||
font-size: 14px;
|
||||
color: #374151;
|
||||
}
|
||||
}
|
||||
|
||||
.view-link {
|
||||
color: #3b82f6;
|
||||
font-size: 14px;
|
||||
margin-left: 8px;
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
transition: color 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
color: #1d4ed8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 响应式设计
|
||||
@media (max-width: 1200px) {
|
||||
.person-info-container {
|
||||
.info-section {
|
||||
.data-grid {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
|
||||
.data-item {
|
||||
&:nth-child(3n) {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
&:nth-child(4n) {
|
||||
border-right: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
&:nth-last-child(-n + 3) {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 992px) {
|
||||
.person-info-container {
|
||||
.info-section {
|
||||
.data-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
|
||||
.data-item {
|
||||
&:nth-child(2n) {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
&:nth-child(3n) {
|
||||
border-right: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
&:nth-last-child(-n + 2) {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.person-info-container {
|
||||
.info-section {
|
||||
.section-header {
|
||||
padding: 16px 20px 12px;
|
||||
|
||||
.section-title {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.view-more {
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
.data-grid {
|
||||
grid-template-columns: 1fr;
|
||||
|
||||
.data-item {
|
||||
border-right: none !important;
|
||||
border-bottom: 1px solid #f0f0f0 !important;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none !important;
|
||||
}
|
||||
|
||||
.data-label {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.data-value {
|
||||
font-size: 16px;
|
||||
|
||||
&.salary-amount {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.status-item {
|
||||
.status-dot {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
span:last-child {
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
.view-link {
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 576px) {
|
||||
.person-info-container {
|
||||
.info-section {
|
||||
.section-header {
|
||||
padding: 14px 16px 10px;
|
||||
|
||||
.section-title {
|
||||
font-size: 14px;
|
||||
|
||||
.title-indicator {
|
||||
width: 3px;
|
||||
height: 16px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.view-more {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.data-grid {
|
||||
.data-item {
|
||||
padding: 16px;
|
||||
|
||||
.data-label {
|
||||
font-size: 12px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.data-value {
|
||||
font-size: 15px;
|
||||
|
||||
&.salary-amount {
|
||||
font-size: 17px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,479 @@
|
|||
<template>
|
||||
<!-- 分包班组卡片 -->
|
||||
<div class="sub-team-card-container">
|
||||
<!-- 分包信息表格 -->
|
||||
<div class="section-container">
|
||||
<div class="section-header">
|
||||
<div class="section-title">
|
||||
<div class="title-indicator"></div>
|
||||
<span>分包信息</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-container">
|
||||
<el-table
|
||||
:data="subcontractorData"
|
||||
style="width: 100%"
|
||||
:header-cell-style="tableHeaderStyle"
|
||||
:cell-style="tableCellStyle"
|
||||
stripe
|
||||
border
|
||||
>
|
||||
<el-table-column
|
||||
prop="subcontractorName"
|
||||
label="分包商名称"
|
||||
min-width="200"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
prop="contractCode"
|
||||
label="分包合同编码"
|
||||
width="150"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="contractName"
|
||||
label="分包合同名称"
|
||||
min-width="250"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
prop="entryExitStatus"
|
||||
label="出入场状态"
|
||||
width="120"
|
||||
align="center"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<div class="status-cell">
|
||||
<span
|
||||
class="status-dot"
|
||||
:class="
|
||||
scope.row.entryExitStatus === '在场'
|
||||
? 'status-online'
|
||||
: 'status-offline'
|
||||
"
|
||||
></span>
|
||||
{{ scope.row.entryExitStatus }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="totalEntryCount"
|
||||
label="累计入场人数"
|
||||
width="120"
|
||||
align="center"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="currentOnsiteCount"
|
||||
label="当前在场人数"
|
||||
width="120"
|
||||
align="center"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="entryTime"
|
||||
label="分包入场时间"
|
||||
width="130"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="authorizationLetter"
|
||||
label="授权委托书"
|
||||
width="120"
|
||||
align="center"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<span
|
||||
v-if="
|
||||
scope.row.authorizationLetter === '已上传'
|
||||
"
|
||||
class="upload-status uploaded"
|
||||
>
|
||||
{{ scope.row.authorizationLetter }}
|
||||
</span>
|
||||
<span v-else class="upload-status not-uploaded">
|
||||
{{ scope.row.authorizationLetter }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="exitTime"
|
||||
label="分包出场时间"
|
||||
width="130"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="wageCommitment"
|
||||
label="工资已支付完成承诺书"
|
||||
width="180"
|
||||
align="center"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<span
|
||||
v-if="scope.row.wageCommitment === '已上传'"
|
||||
class="upload-status uploaded"
|
||||
>
|
||||
{{ scope.row.wageCommitment }}
|
||||
</span>
|
||||
<span v-else class="upload-status not-uploaded">
|
||||
{{ scope.row.wageCommitment }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 班组信息表格 -->
|
||||
<div class="section-container">
|
||||
<div class="section-header">
|
||||
<div class="section-title">
|
||||
<div class="title-indicator"></div>
|
||||
<span>班组信息</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-container">
|
||||
<el-table
|
||||
:data="teamData"
|
||||
style="width: 100%"
|
||||
:header-cell-style="tableHeaderStyle"
|
||||
:cell-style="tableCellStyle"
|
||||
stripe
|
||||
border
|
||||
>
|
||||
<el-table-column
|
||||
prop="teamName"
|
||||
label="班组名称"
|
||||
width="150"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="teamLeader"
|
||||
label="班组长及联系方式"
|
||||
width="180"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
prop="subcontractorName"
|
||||
label="分包商名称"
|
||||
min-width="200"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
prop="entryExitStatus"
|
||||
label="出入场状态"
|
||||
width="120"
|
||||
align="center"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<div class="status-cell">
|
||||
<span
|
||||
class="status-dot"
|
||||
:class="
|
||||
scope.row.entryExitStatus === '在场'
|
||||
? 'status-online'
|
||||
: 'status-offline'
|
||||
"
|
||||
></span>
|
||||
{{ scope.row.entryExitStatus }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="totalEntryCount"
|
||||
label="累计入场人数"
|
||||
width="120"
|
||||
align="center"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="currentOnsiteCount"
|
||||
label="当前在场人数"
|
||||
width="120"
|
||||
align="center"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="entryTime"
|
||||
label="入场时间"
|
||||
width="120"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="exitTime"
|
||||
label="出场时间"
|
||||
width="120"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="commitmentLetter"
|
||||
label="施工班组承诺书"
|
||||
width="150"
|
||||
align="center"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<span
|
||||
v-if="scope.row.commitmentLetter === '已上传'"
|
||||
class="upload-status uploaded"
|
||||
>
|
||||
{{ scope.row.commitmentLetter }}
|
||||
</span>
|
||||
<span v-else class="upload-status not-uploaded">
|
||||
{{ scope.row.commitmentLetter }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'SubTeamCard',
|
||||
data() {
|
||||
return {
|
||||
// 分包信息数据
|
||||
subcontractorData: [
|
||||
{
|
||||
subcontractorName: '重庆市万州水电建筑工程有限公司',
|
||||
contractCode: 'SXFGS-YH-001',
|
||||
contractName: '220kV永和(云纺)变电站建筑工程专业分包合同1',
|
||||
entryExitStatus: '在场',
|
||||
totalEntryCount: 80,
|
||||
currentOnsiteCount: 56,
|
||||
entryTime: '2025-04-06',
|
||||
authorizationLetter: '已上传',
|
||||
exitTime: '/',
|
||||
wageCommitment: '/',
|
||||
},
|
||||
{
|
||||
subcontractorName: '深圳市铁越电气有限公司',
|
||||
contractCode: 'SXFGS-YH-005',
|
||||
contractName:
|
||||
'220kV 永和(云纺)输变电工程架线施工劳务分包框架子合同',
|
||||
entryExitStatus: '离场',
|
||||
totalEntryCount: 45,
|
||||
currentOnsiteCount: 0,
|
||||
entryTime: '2025-04-06',
|
||||
authorizationLetter: '已上传',
|
||||
exitTime: '2025-10-06',
|
||||
wageCommitment: '已上传',
|
||||
},
|
||||
],
|
||||
// 班组信息数据
|
||||
teamData: [
|
||||
{
|
||||
teamName: '组塔一班',
|
||||
teamLeader: '颜静宁 13875286111',
|
||||
subcontractorName: '重庆市万州水电建筑工程有限公司',
|
||||
entryExitStatus: '在场',
|
||||
totalEntryCount: 80,
|
||||
currentOnsiteCount: 56,
|
||||
entryTime: '2025-04-06',
|
||||
exitTime: '/',
|
||||
commitmentLetter: '/',
|
||||
},
|
||||
{
|
||||
teamName: '架线三班',
|
||||
teamLeader: '颜静宁 13875286111',
|
||||
subcontractorName: '重庆市万州水电建筑工程有限公司',
|
||||
entryExitStatus: '离场',
|
||||
totalEntryCount: 45,
|
||||
currentOnsiteCount: 0,
|
||||
entryTime: '2025-04-06',
|
||||
exitTime: '2025-12-06',
|
||||
commitmentLetter: '已上传',
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 表格头部样式
|
||||
tableHeaderStyle() {
|
||||
return {
|
||||
backgroundColor: '#f5f7fa',
|
||||
color: '#606266',
|
||||
fontWeight: '500',
|
||||
fontSize: '14px',
|
||||
textAlign: 'center',
|
||||
}
|
||||
},
|
||||
// 表格单元格样式
|
||||
tableCellStyle() {
|
||||
return {
|
||||
fontSize: '14px',
|
||||
color: '#606266',
|
||||
padding: '12px 8px',
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '@/assets/styles/variables.scss';
|
||||
|
||||
.sub-team-card-container {
|
||||
margin-bottom: 20px;
|
||||
|
||||
.section-container {
|
||||
background: #ffffff;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
margin-bottom: 20px;
|
||||
overflow: hidden;
|
||||
|
||||
.section-header {
|
||||
padding: 20px 24px 16px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
|
||||
.section-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #1f2937;
|
||||
|
||||
.title-indicator {
|
||||
width: 4px;
|
||||
height: 20px;
|
||||
background: #3b82f6;
|
||||
border-radius: 2px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.table-container {
|
||||
padding: 0;
|
||||
|
||||
::v-deep .el-table {
|
||||
border: none;
|
||||
|
||||
.el-table__header-wrapper {
|
||||
.el-table__header {
|
||||
th {
|
||||
border-bottom: 1px solid #e4e7ed;
|
||||
border-right: 1px solid #e4e7ed;
|
||||
|
||||
&:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-table__body-wrapper {
|
||||
.el-table__body {
|
||||
tr {
|
||||
&:hover {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
td {
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
border-right: 1px solid #f0f0f0;
|
||||
|
||||
&:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.status-cell {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.status-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
margin-right: 6px;
|
||||
display: inline-block;
|
||||
|
||||
&.status-online {
|
||||
background-color: #10b981;
|
||||
}
|
||||
|
||||
&.status-offline {
|
||||
background-color: #ef4444;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.upload-status {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
|
||||
&.uploaded {
|
||||
color: #10b981;
|
||||
}
|
||||
|
||||
&.not-uploaded {
|
||||
color: #9ca3af;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 响应式设计
|
||||
@media (max-width: 1200px) {
|
||||
.sub-team-card-container {
|
||||
.section-container {
|
||||
.table-container {
|
||||
::v-deep .el-table {
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.sub-team-card-container {
|
||||
.section-container {
|
||||
.section-header {
|
||||
padding: 16px 20px 12px;
|
||||
|
||||
.section-title {
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.table-container {
|
||||
::v-deep .el-table {
|
||||
font-size: 12px;
|
||||
|
||||
.el-table__header-wrapper {
|
||||
.el-table__header {
|
||||
th {
|
||||
padding: 8px 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-table__body-wrapper {
|
||||
.el-table__body {
|
||||
td {
|
||||
padding: 8px 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.status-cell {
|
||||
.status-dot {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.upload-status {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
<template>
|
||||
<!-- 综合查询 ---- 工程统计详情 -->
|
||||
<div class="app-container">
|
||||
<HeaderInfo />
|
||||
<CardCount
|
||||
:auto-load="true"
|
||||
:project-id="projectId"
|
||||
@cardClick="handleCardClick"
|
||||
/>
|
||||
<SubTeamCard ref="subTeamCardRef" />
|
||||
<PersonInfoCard ref="personInfoCardRef" />
|
||||
<AttMachineCard ref="attMachineCardRef" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import HeaderInfo from './components/header-info.vue'
|
||||
import CardCount from './components/card-count.vue'
|
||||
import SubTeamCard from './components/subTeam-card.vue'
|
||||
import PersonInfoCard from './components/personInfo-card.vue'
|
||||
import AttMachineCard from './components/attMachine-card.vue'
|
||||
|
||||
export default {
|
||||
name: 'ProjectCountDetail',
|
||||
components: {
|
||||
HeaderInfo,
|
||||
CardCount,
|
||||
SubTeamCard,
|
||||
PersonInfoCard,
|
||||
AttMachineCard,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
projectId: null, // 从路由参数或其他地方获取项目ID
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// 从路由参数获取项目ID
|
||||
this.projectId = this.$route.params.id || this.$route.query.id
|
||||
},
|
||||
methods: {
|
||||
// 处理卡片点击事件
|
||||
handleCardClick(card) {
|
||||
this.scrollToTarget(card.type)
|
||||
// const attMachineCard_1 = document.getElementById('attMachineCard_1')
|
||||
},
|
||||
|
||||
// 平滑滚动到目标组件
|
||||
scrollToTarget(cardType) {
|
||||
// 卡片类型到目标组件的映射关系
|
||||
const cardTypeMap = {
|
||||
subcontractor: 'subTeamCardRef', // 分包单位 -> 分包信息
|
||||
team: 'subTeamCardRef', // 施工班组 -> 班组信息
|
||||
entry: 'personInfoCardRef', // 累计入场 -> 出入场信息
|
||||
onsite: 'personInfoCardRef', // 当前在场 -> 出入场信息
|
||||
attendance: 'personInfoCardRef', // 累计考勤 -> 考勤信息
|
||||
wage: 'personInfoCardRef', // 累计工资 -> 工资信息
|
||||
machine: 'attMachineCardRef', // 考勤机 -> 考勤机明细
|
||||
}
|
||||
|
||||
const targetRef = cardTypeMap[cardType]
|
||||
|
||||
console.log('targetRef', targetRef)
|
||||
|
||||
if (!targetRef) {
|
||||
console.warn('未找到对应的目标组件:', cardType)
|
||||
return
|
||||
}
|
||||
|
||||
// 使用 nextTick 确保 DOM 更新完成
|
||||
this.$nextTick(() => {
|
||||
const targetElement = this.$refs[targetRef].$el
|
||||
|
||||
if (!targetElement) {
|
||||
console.warn('目标组件引用不存在:', targetRef)
|
||||
return
|
||||
}
|
||||
|
||||
// 平滑滚动到目标位置
|
||||
this.smoothScrollToElement(targetElement)
|
||||
})
|
||||
},
|
||||
|
||||
smoothScrollToElement(element) {
|
||||
console.log('element', element)
|
||||
|
||||
if (element) {
|
||||
// 获取位置信息
|
||||
const rect = element.getBoundingClientRect()
|
||||
console.log('元素位置信息:', {
|
||||
top: rect.top, // 元素顶部距离视口顶部的距离
|
||||
left: rect.left, // 元素左侧距离视口左侧的距离
|
||||
right: rect.right, // 元素右侧距离视口左侧的距离
|
||||
bottom: rect.bottom, // 元素底部距离视口顶部的距离
|
||||
width: rect.width, // 元素宽度
|
||||
height: rect.height, // 元素高度
|
||||
})
|
||||
|
||||
// 计算目标滚动位置 - 使用更简单的方法
|
||||
const targetPosition = rect.top + window.pageYOffset - 80 // 减去80px的偏移量,避免被固定头部遮挡
|
||||
|
||||
console.log('当前滚动位置:', window.pageYOffset)
|
||||
console.log('目标滚动位置:', targetPosition)
|
||||
console.log('滚动距离:', targetPosition - window.pageYOffset)
|
||||
|
||||
// 使用原生 smooth scroll 行为
|
||||
element.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'start',
|
||||
inline: 'nearest',
|
||||
})
|
||||
|
||||
// 或者使用 window.scrollTo 的 smooth 行为
|
||||
// window.scrollTo({
|
||||
// top: targetPosition,
|
||||
// behavior: 'smooth'
|
||||
// })
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
@ -1,239 +1,73 @@
|
|||
<template>
|
||||
<div class="not-dev-container app-container">
|
||||
<div class="not-dev-content">
|
||||
<div class="not-dev-image">
|
||||
<svg
|
||||
width="400"
|
||||
height="400"
|
||||
viewBox="0 0 400 400"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
<!-- 综合查询 ---- 工程统计 -->
|
||||
<div class="app-container">
|
||||
<TableModel
|
||||
:formLabel="formLabel"
|
||||
:showOperation="true"
|
||||
:showRightTools="true"
|
||||
ref="projectCountTableRef"
|
||||
:columnsList="columnsList"
|
||||
:testTableList="testTableList"
|
||||
:request-api="getProjectCountListAPI"
|
||||
>
|
||||
<template slot="handle" slot-scope="{ queryParams }">
|
||||
<el-button
|
||||
plain
|
||||
size="mini"
|
||||
type="primary"
|
||||
icon="el-icon-view"
|
||||
@click="onHandleViewDetail(queryParams)"
|
||||
>
|
||||
<!-- 背景圆形 -->
|
||||
<circle
|
||||
cx="200"
|
||||
cy="200"
|
||||
r="180"
|
||||
fill="#F8FAFC"
|
||||
stroke="#E2E8F0"
|
||||
stroke-width="2"
|
||||
/>
|
||||
|
||||
<!-- 主要图标:建筑工地或开发中的图标 -->
|
||||
<rect
|
||||
x="120"
|
||||
y="140"
|
||||
width="160"
|
||||
height="120"
|
||||
rx="8"
|
||||
fill="#3B82F6"
|
||||
opacity="0.1"
|
||||
/>
|
||||
<rect
|
||||
x="130"
|
||||
y="150"
|
||||
width="140"
|
||||
height="100"
|
||||
rx="4"
|
||||
fill="#3B82F6"
|
||||
opacity="0.2"
|
||||
/>
|
||||
|
||||
<!-- 脚手架结构 -->
|
||||
<rect
|
||||
x="110"
|
||||
y="120"
|
||||
width="8"
|
||||
height="160"
|
||||
fill="#94A3B8"
|
||||
/>
|
||||
<rect
|
||||
x="282"
|
||||
y="120"
|
||||
width="8"
|
||||
height="160"
|
||||
fill="#94A3B8"
|
||||
/>
|
||||
<rect
|
||||
x="110"
|
||||
y="120"
|
||||
width="180"
|
||||
height="8"
|
||||
fill="#94A3B8"
|
||||
/>
|
||||
<rect
|
||||
x="110"
|
||||
y="200"
|
||||
width="180"
|
||||
height="8"
|
||||
fill="#94A3B8"
|
||||
/>
|
||||
<rect
|
||||
x="110"
|
||||
y="280"
|
||||
width="180"
|
||||
height="8"
|
||||
fill="#94A3B8"
|
||||
/>
|
||||
|
||||
<!-- 工具图标 -->
|
||||
<circle cx="160" cy="180" r="15" fill="#F59E0B" />
|
||||
<circle cx="240" cy="180" r="15" fill="#10B981" />
|
||||
<circle cx="200" cy="220" r="15" fill="#EF4444" />
|
||||
|
||||
<!-- 进度条 -->
|
||||
<rect
|
||||
x="120"
|
||||
y="320"
|
||||
width="160"
|
||||
height="8"
|
||||
rx="4"
|
||||
fill="#E2E8F0"
|
||||
/>
|
||||
<rect
|
||||
x="120"
|
||||
y="320"
|
||||
width="80"
|
||||
height="8"
|
||||
rx="4"
|
||||
fill="#3B82F6"
|
||||
/>
|
||||
|
||||
<!-- 装饰性元素 -->
|
||||
<circle
|
||||
cx="150"
|
||||
cy="80"
|
||||
r="4"
|
||||
fill="#3B82F6"
|
||||
opacity="0.6"
|
||||
/>
|
||||
<circle
|
||||
cx="250"
|
||||
cy="90"
|
||||
r="3"
|
||||
fill="#10B981"
|
||||
opacity="0.6"
|
||||
/>
|
||||
<circle
|
||||
cx="180"
|
||||
cy="70"
|
||||
r="2"
|
||||
fill="#F59E0B"
|
||||
opacity="0.6"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="not-dev-text">
|
||||
<h2 class="title">即将上线</h2>
|
||||
<p class="subtitle">敬请期待...</p>
|
||||
</div>
|
||||
</div>
|
||||
详情
|
||||
</el-button>
|
||||
</template>
|
||||
</TableModel>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TableModel from '@/components/TableModel'
|
||||
import { formLabel, columnsList } from './config'
|
||||
import { getProjectCountListAPI } from '@/api/synthesize-query/project-count'
|
||||
export default {
|
||||
name: 'NotDev',
|
||||
name: 'ProjectCount',
|
||||
components: {
|
||||
TableModel,
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
return {
|
||||
formLabel,
|
||||
columnsList,
|
||||
getProjectCountListAPI,
|
||||
testTableList: [
|
||||
{
|
||||
mainProName: '分公司1',
|
||||
volLevel: '工程1',
|
||||
subNum: 1,
|
||||
teamNum: 1,
|
||||
teamNum: 1,
|
||||
},
|
||||
{
|
||||
mainProName: '分公司1',
|
||||
volLevel: '工程1',
|
||||
subNum: 1,
|
||||
teamNum: 1,
|
||||
teamNum: 1,
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
onHandleViewDetail(queryParams) {
|
||||
console.log(queryParams)
|
||||
|
||||
this.$router.push({
|
||||
name: 'ProjectCountDetail',
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.not-dev-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
// min-height: 100vh;
|
||||
background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%);
|
||||
|
||||
.not-dev-content {
|
||||
text-align: center;
|
||||
padding: 40px;
|
||||
border-radius: 16px;
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
backdrop-filter: blur(10px);
|
||||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
|
||||
.not-dev-image {
|
||||
margin-bottom: 32px;
|
||||
|
||||
svg {
|
||||
filter: drop-shadow(0 8px 16px rgba(59, 130, 246, 0.15));
|
||||
transition: transform 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-8px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.not-dev-text {
|
||||
.title {
|
||||
font-size: 32px;
|
||||
font-weight: 600;
|
||||
color: #1e3a8a;
|
||||
margin: 0 0 16px 0;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 18px;
|
||||
color: #64748b;
|
||||
margin: 0;
|
||||
font-weight: 400;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 响应式设计
|
||||
@media (max-width: 768px) {
|
||||
.not-dev-container {
|
||||
padding: 20px;
|
||||
|
||||
.not-dev-content {
|
||||
padding: 30px 20px;
|
||||
|
||||
.not-dev-image svg {
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
.not-dev-text {
|
||||
.title {
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.not-dev-container {
|
||||
.not-dev-content {
|
||||
.not-dev-image svg {
|
||||
width: 250px;
|
||||
height: 250px;
|
||||
}
|
||||
|
||||
.not-dev-text {
|
||||
.title {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style></style>
|
||||
|
|
|
|||
Loading…
Reference in New Issue