工程统计页面完善

This commit is contained in:
BianLzhaoMin 2025-09-16 17:08:36 +08:00
parent fc3d74c997
commit d7965c45ff
10 changed files with 2486 additions and 227 deletions

View File

@ -0,0 +1,10 @@
import request from '@/utils/request'
// 综合查询 获取工程统计列表
export const getProjectCountListAPI = (data) => {
return request({
url: '/bmw/workerLight/xxx',
method: 'GET',
params: data,
})
}

View File

@ -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',
},
},
],
},
{

View File

@ -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: '工程地址' },
]

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>