Merge branch 'main' of http://120.76.84.145:10101/gitblit/r/H5/smartBuilding
| | |
| | | addWebpackAlias, |
| | | } = require("customize-cra"); |
| | | const path = require("path"); |
| | | const webpack = require("webpack"); |
| | | |
| | | function resolve(dir) { |
| | | return path.join(__dirname, dir); |
| | | } |
| | |
| | | if (config.resolve) { |
| | | config.resolve.extensions.push(".jsx"); |
| | | } |
| | | |
| | | // 添加 webpack 插件来定义 process.env |
| | | config.plugins.push( |
| | | new webpack.DefinePlugin({ |
| | | 'process.env': JSON.stringify(process.env) |
| | | }) |
| | | ); |
| | | |
| | | return config; |
| | | }; |
| | | module.exports = override( |
| | |
| | | const isHover = hoverIndex === idx; |
| | | return ( |
| | | <div |
| | | key={item.title} |
| | | key={`summary-${idx}`} |
| | | onMouseEnter={() => setHoverIndex(idx)} |
| | | onMouseLeave={() => setHoverIndex(-1)} |
| | | style={{ |
| | |
| | | // 空白行 |
| | | return ( |
| | | <div |
| | | key={item.key || `blank-${idx}`} |
| | | key={`blank-item-${idx}`} |
| | | className="major-hazard-item" |
| | | style={{ |
| | | minHeight: ITEM_HEIGHT, |
| | |
| | | } |
| | | return ( |
| | | <div |
| | | key={idx} |
| | | key={`hazard-${idx}`} |
| | | className="major-hazard-item" |
| | | style={{ |
| | | color: '#fff', |
| | |
| | | date: clipText(row.date, 10), |
| | | total: (row.total !== undefined && row.total !== null && !isNaN(row.total)) ? Number(row.total).toFixed(1) : row.total, |
| | | })), |
| | | { key: 'blank1', name: '', client: '', plan: '', date: '', total: '' }, |
| | | { key: 'blank2', name: '', client: '', plan: '', date: '', total: '' }, |
| | | { key: 'blank3', name: '', client: '', plan: '', date: '', total: '' }, |
| | | { key: `blank-${dataSource.length + 1}`, name: '', client: '', plan: '', date: '', total: '' }, |
| | | { key: `blank-${dataSource.length + 2}`, name: '', client: '', plan: '', date: '', total: '' }, |
| | | { key: `blank-${dataSource.length + 3}`, name: '', client: '', plan: '', date: '', total: '' }, |
| | | ]; |
| | | |
| | | // 跑马灯滚动效果(到底停3秒再回顶部) |
| New file |
| | |
| | | /** |
| | | * 风险源映射配置 |
| | | * 用于将后端返回的 code 映射为对应的中文名称 |
| | | */ |
| | | |
| | | // 超危大工程映射 |
| | | export const projectSuperDangerousTypeMap = { |
| | | DEEP_FOUNDATION_PIT: '深基坑工程', |
| | | TOOL_TEMPLATE: '工具式模板工程', |
| | | TEMPLATE_SUPPORT: '模板支撑工程', |
| | | STEEL_STRUCTURE_INSTALLATION: '钢结构安装满堂支撑体系', |
| | | LIFTING_HOISTING: '起重吊装工程', |
| | | CRANE_INSTALLATION_REMOVAL: '起重机械安装和拆卸工程', |
| | | GROUND_STEEL_PIPE_SCAFFOLD: '落地式钢管脚手架工程', |
| | | SUSPENDED_ASCENDING_SCAFFOLD: '附着式升降脚手架工程', |
| | | CANTILEVER_SCAFFOLD: '悬挑式脚手架工程', |
| | | DEMOLITION: '拆除工程', |
| | | MINING_UNDERGROUND: '矿山法暗挖', |
| | | SHIELD_TUNNELING: '盾构', |
| | | PIPE_ROOFING: '顶管', |
| | | BUILDING_CURTAIN_WALL: '建筑幕墙安装工程', |
| | | STEEL_FRAME_TRUSS_TENSION: '钢结构网架和索膜结构安装工程', |
| | | MANUAL_BORED_PILE: '人工挖孔桩工程', |
| | | LARGE_STRUCTURE_MOVE: '大型结构整体顶升平移转体', |
| | | UNDERWATER_OPERATION: '水下作业工程', |
| | | FOUR_NEW_TECHNOLOGIES: '采用四新技术' |
| | | } |
| | | |
| | | // 危大工程映射 |
| | | export const projectDangerousTypeMap = { |
| | | FOUNDATION_PIT: '基坑工程', |
| | | FORMWORK_SUPPORT: '模板支撑工程', |
| | | STEEL_STRUCTURE_INSTALLATION_SUPPORT_SYSTEM: '钢结构安装满堂支撑体系', |
| | | HOISTING_AND_LIFTING: '起重吊装工程', |
| | | CRANE_INSTALLATION: '起重机械进行安装的工程', |
| | | CRANE_INSTALLATION_AND_DISMANTLING: '起重机械安装和拆卸工程', |
| | | GROUND_PIPE_SCAFFOLD: '落地式钢管脚手架工程', |
| | | SUSPENDED_SCAFFOLD: '附着式升降脚手架工程', |
| | | CANTILEVER_SCAFFOLD: '悬挑式脚手架工程', |
| | | HIGH_ALTITUDE_OPERATION_GONDOLA: '高处作业吊篮', |
| | | UNLOADING_PLATFORM_OPERATION_PLATFORM: '卸料平台操作平台工程', |
| | | IRREGULAR_SCAFFOLD: '异型脚手架工程', |
| | | DEMOLITION: '拆除工程', |
| | | BUILDING_CURTAIN_WALL_INSTALLATION: '建筑幕墙安装工程', |
| | | STEEL_STRUCTURE_GRID_AND_MEMBRANE_STRUCTURE_INSTALLATION: '钢结构网架和索膜结构安装工程', |
| | | MANUAL_BORED_PILE: '人工挖孔桩工程', |
| | | PREFABRICATED_COMPONENT_INSTALLATION: '装配式建筑混凝土预制构件安装工程' |
| | | } |
| | | |
| | | // 风险源映射 |
| | | export const projectHazardSourceMap = { |
| | | HIGH_ALTITUDE_OPERATION: '高处作业', |
| | | FIRE_OPERATION: '动火作业', |
| | | MASS_CONCRETE_POURING: '大体积混凝土浇筑', |
| | | CONFINED_SPACE_OPERATION: '有限空间作业', |
| | | ROAD_OCCUPATION_CONSTRUCTION: '占道施工', |
| | | SINGLE_PERSON_OPERATION: '单人操作及作业', |
| | | SPACE_CROSS_OPERATION: '空间交叉作业' |
| | | } |
| | | |
| | | /** |
| | | * 根据类型和 code 获取对应的中文名称 |
| | | * @param {string} type - 类型:'superDangerous' | 'dangerous' | 'hazardSource' |
| | | * @param {string} code - 风险源代码 |
| | | * @returns {string} 中文名称,如果找不到则返回原 code |
| | | */ |
| | | export const getRiskSourceName = (type, code) => { |
| | | const maps = { |
| | | superDangerous: projectSuperDangerousTypeMap, |
| | | dangerous: projectDangerousTypeMap, |
| | | hazardSource: projectHazardSourceMap |
| | | } |
| | | |
| | | const map = maps[type] |
| | | return map ? (map[code] || code) : code |
| | | } |
| | | |
| | | /** |
| | | * 批量转换风险源数据 |
| | | * @param {Array} dataArray - 风险源数据数组 |
| | | * @param {string} type - 类型:'superDangerous' | 'dangerous' | 'hazardSource' |
| | | * @param {string} riskScale - 风险规模名称 |
| | | * @returns {Array} 转换后的数据数组 |
| | | */ |
| | | export const transformRiskSourceData = (dataArray, type, riskScale) => { |
| | | if (!Array.isArray(dataArray)) return [] |
| | | |
| | | return dataArray.map(item => { |
| | | const content = getRiskSourceName(type, item) |
| | | return { |
| | | riskScale, |
| | | content, |
| | | status: '存在' |
| | | } |
| | | }) |
| | | } |
| | | |
| | |
| | | <img src="../../assets/img/down-circle@2x.png" class="w--15 h--16 shrink0" /> |
| | | </div> |
| | | <div class="flex a-center j-between pl--18 pr--15 h--40 relative w--300 bgcolor1 searchBox"> |
| | | <el-select filterable v-model="type" placeholder="请选择设备类型" class="flex1"> |
| | | <el-select filterable v-model="type" placeholder="请选择设备类型" class="flex1" @change="handleSearch" clearable> |
| | | <el-option v-for="item in deviceTypeList" :key="item.code" :label="item.name" :value="item.code"> |
| | | </el-option> |
| | | </el-select> |
| | | <img src="../../assets/img/down-circle@2x.png" class="w--15 h--16 shrink0" /> |
| | | </div> |
| | | <div @click="handleClick(1)" class="flex a-center j-between px--20 w--300 h--40 relative bgcolor1 picker"> |
| | | <el-date-picker v-model="dateTime" ref="datetime1" type="datetime" placeholder=""> |
| | | <el-date-picker v-model="dateTime" ref="datetime1" type="datetime" placeholder="" @change="handleSearch" clearable> |
| | | </el-date-picker> |
| | | <div class="absolute w100 h100 flex a-center c-fff fs--14 SHS" style="width: calc(100% - 20px)" |
| | | v-if="startTime"> |
| | |
| | | <img src="../../assets/img/home/Shape@2x (6).png" class="w--13 h--13 shrink0" /> |
| | | </div> |
| | | <div @click="handleClick(2)" class="flex a-center j-between px--20 w--300 h--40 relative bgcolor1 picker"> |
| | | <el-date-picker v-model="dateTime2" ref="datetime2" type="datetime" placeholder=""> |
| | | <el-date-picker v-model="dateTime2" ref="datetime2" type="datetime" placeholder="" @change="handleSearch" clearable> |
| | | </el-date-picker> |
| | | <div class="absolute w100 h100 flex a-center c-fff fs--14 SHS" style="width: calc(100% - 20px)" |
| | | v-if="endTime"> |
| | |
| | | <img src="../../assets/img/home/Shape@2x (6).png" class="w--13 h--13 shrink0" /> |
| | | </div> |
| | | <div class="flex a-center j-between w--300 pl--20 h--40 relative bgcolor1 searchBox"> |
| | | <el-input v-model="content" placeholder="请输入搜索内容" class="flex1"></el-input> |
| | | <div class="btn SHSR">搜索</div> |
| | | <el-input v-model="content" placeholder="请输入搜索内容" class="flex1" @keyup.enter="handleSearch"></el-input> |
| | | <div class="btn SHSR" @click="handleSearch">搜索</div> |
| | | </div> |
| | | </div> |
| | | <!-- 表格 --> |
| | | <CustomTable :data="tableList" height="800" |
| | | style="border: 2px solid #3d7fff"> |
| | | <el-table-column v-if="!projectId" label="项目名称" prop="projectName" /> |
| | | <el-table-column label="设备类型" prop="type" /> |
| | | <el-table-column label="设备类型" prop="type"> |
| | | <template #default="{ row }"> |
| | | <div>{{ getDeviceTypeName(row.type) }}</div> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="设备编号" prop="name" /> |
| | | <el-table-column label="设备出厂日期" prop="createTime" /> |
| | | <el-table-column label="使用单位" prop="projectName" /> |
| | | <el-table-column label="工程起重机械生产单位" prop="projectName" /> |
| | | <el-table-column label="工程起重机械租赁单位" prop="projectName" /> |
| | | <el-table-column label="工程起重机械维保单位" prop="projectName" /> |
| | | <el-table-column label="工程起重机械安拆单位" prop="projectName" /> |
| | | <el-table-column label="设备出厂日期" prop="createTime"> |
| | | <template #default="{ row }"> |
| | | <div>{{ getPropertyValue(row, '设备出厂日期') || row.createTime || '-' }}</div> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="使用单位" prop="projectName"> |
| | | <template #default="{ row }"> |
| | | <div class="flex flex-column gap--4"> |
| | | <div v-for="(item, index) in getPropertyValues(row, '使用单位')" :key="index"> |
| | | {{ item }} |
| | | </div> |
| | | </div> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="工程起重机械生产单位"> |
| | | <template #default="{ row }"> |
| | | <div class="flex flex-column gap--4"> |
| | | <div v-for="(item, index) in getPropertyValues(row, '工程起重机械生产单位')" :key="index"> |
| | | {{ item }} |
| | | </div> |
| | | </div> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="工程起重机械租赁单位"> |
| | | <template #default="{ row }"> |
| | | <div class="flex flex-column gap--4"> |
| | | <div v-for="(item, index) in getPropertyValues(row, '工程起重机械租赁单位')" :key="index"> |
| | | {{ item }} |
| | | </div> |
| | | </div> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="工程起重机械维保单位"> |
| | | <template #default="{ row }"> |
| | | <div class="flex flex-column gap--4"> |
| | | <div v-for="(item, index) in getPropertyValues(row, '工程起重机械维保单位')" :key="index"> |
| | | {{ item }} |
| | | </div> |
| | | </div> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="工程起重机械安拆单位"> |
| | | <template #default="{ row }"> |
| | | <div class="flex flex-column gap--4"> |
| | | <div v-for="(item, index) in getPropertyValues(row, '工程起重机械安拆单位')" :key="index"> |
| | | {{ item }} |
| | | </div> |
| | | </div> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="本月检查情况" prop="checkResult"> |
| | | <template #default="{ row, $index }"> |
| | | <template #default="{ row }"> |
| | | <div class="flex a-center j-center"> |
| | | <div class="ripple"> |
| | | <div class="ripple-wave wave-1" :class="{ bgColor3: $index === 0 }"></div> |
| | | <div class="ripple-wave wave-2" :class="{ bgColor4: $index === 0 }"></div> |
| | | <div class="ripple-wave wave-3" :class="{ bgColor5: $index === 0 }"></div> |
| | | <div class="ripple-wave wave-1" :class="{ bgColor3: row.checkResult === '已检查' }"></div> |
| | | <div class="ripple-wave wave-2" :class="{ bgColor4: row.checkResult === '已检查' }"></div> |
| | | <div class="ripple-wave wave-3" :class="{ bgColor5: row.checkResult === '已检查' }"></div> |
| | | </div> |
| | | <div class="ml--6 SHSR">{{ $index }}</div> |
| | | <div class="ml--6 SHSR">{{ row.checkResult }}</div> |
| | | </div> |
| | | </template> |
| | | </el-table-column> |
| | |
| | | </el-table-column> |
| | | </CustomTable> |
| | | <CustomPagination @size-change="handleSizeChange" @current-change="handleCurrentChange" |
| | | :current-page="currentPage" :page-sizes="[100, 200, 300, 400]" :page-size="10" :total="total" /> |
| | | :current-page="currentPage" :page-sizes="[10, 20, 50, 100]" :page-size="pageSize" :total="total" /> |
| | | </div> |
| | | <!-- 项目列表弹窗 --> |
| | | <CustomDialog title="项目" :visible.sync="projectVisible" width="1420px"> |
| | |
| | | <script> |
| | | import HeaderBox from "../header/index.vue"; |
| | | import moment from "moment"; |
| | | import { deviceTypes, getDeviceList } from './service.js' |
| | | import { deviceTypes, getDeviceList,getDeviceDetailCheckList } from './service.js' |
| | | export default { |
| | | components: { HeaderBox }, |
| | | props: {}, |
| | |
| | | handler(newVal, oldVal) { |
| | | if (newVal) { |
| | | this.startTime = moment(newVal).format("YYYY年MM月DD日 HH:mm:ss"); |
| | | } else { |
| | | this.startTime = ''; |
| | | } |
| | | }, |
| | | immediate: true, |
| | |
| | | handler(newVal, oldVal) { |
| | | if (newVal) { |
| | | this.endTime = moment(newVal).format("YYYY年MM月DD日 HH:mm:ss"); |
| | | } else { |
| | | this.endTime = ''; |
| | | } |
| | | }, |
| | | immediate: true, |
| | |
| | | deviceTypes().then(res => { |
| | | this.deviceTypeList = res |
| | | }) |
| | | }, |
| | | // 根据设备类型下标获取设备类型名称 |
| | | getDeviceTypeName(typeIndex) { |
| | | if (typeIndex === null || typeIndex === undefined || typeIndex === '') { |
| | | return '-' |
| | | } |
| | | // 如果 typeIndex 是数字下标,从设备类型列表中获取对应的名称 |
| | | if (typeof typeIndex === 'number' && this.deviceTypeList[typeIndex]) { |
| | | return this.deviceTypeList[typeIndex].name |
| | | } |
| | | // 如果 typeIndex 是 code,从设备类型列表中查找对应的名称 |
| | | const deviceType = this.deviceTypeList.find(item => item.code === typeIndex) |
| | | return deviceType ? deviceType.name : typeIndex |
| | | }, |
| | | toHiddenDanger(row, type) { |
| | | this.$router.push({ |
| | |
| | | } |
| | | getDeviceList({ ...params }).then(res => { |
| | | console.log('设备列表', res) |
| | | this.tableList = res.records |
| | | // 处理 properties 字段 |
| | | this.tableList = res.records.map(item => { |
| | | return { |
| | | ...item, |
| | | parsedProperties: this.parseProperties(item.properties) |
| | | } |
| | | }) |
| | | this.total = res.total |
| | | }) |
| | | }, |
| | | // 解析 properties 字段 |
| | | parseProperties(properties) { |
| | | if (!properties) return [] |
| | | try { |
| | | // 如果是字符串,先解析为数组 |
| | | const propertiesArray = typeof properties === 'string' ? JSON.parse(properties) : properties |
| | | // 确保返回的是数组 |
| | | return Array.isArray(propertiesArray) ? propertiesArray : [propertiesArray] |
| | | } catch (error) { |
| | | console.error('解析 properties 失败:', error) |
| | | return [] |
| | | } |
| | | }, |
| | | // 获取 properties 中某个字段的所有值(数组形式) |
| | | getPropertyValues(row, fieldName) { |
| | | if (!row.parsedProperties || row.parsedProperties.length === 0) { |
| | | return ['-'] |
| | | } |
| | | // 从所有 properties 对象中提取指定字段的值 |
| | | const values = row.parsedProperties |
| | | .map(prop => prop[fieldName]) |
| | | .filter(val => val && val.trim() !== '') // 过滤空值 |
| | | |
| | | return values.length > 0 ? values : ['-'] |
| | | }, |
| | | // 获取 properties 中某个字段的第一个值(单个值) |
| | | getPropertyValue(row, fieldName) { |
| | | if (!row.parsedProperties || row.parsedProperties.length === 0) { |
| | | return '-' |
| | | } |
| | | // 从第一个 properties 对象中获取指定字段的值 |
| | | const value = row.parsedProperties[0][fieldName] |
| | | return value && value.trim() !== '' ? value : '-' |
| | | }, |
| | | |
| | | handleSizeChange(val) { |
| | | console.log(`每页 ${val} 条`); |
| | | this.pageSize = val; |
| | | this.currentPage = 1; // 改变每页条数时重置到第一页 |
| | | this.getList(); |
| | | }, |
| | | |
| | | handleCurrentChange(val) { |
| | | console.log(`当前页: ${val}`); |
| | | this.currentPage = val; |
| | | this.getList(); |
| | | }, |
| | | |
| | | // 搜索方法 |
| | | handleSearch() { |
| | | this.currentPage = 1; // 搜索时重置到第一页 |
| | | this.getList(); |
| | | }, |
| | | |
| | | handleClick(type) { |
| | |
| | | data |
| | | }) |
| | | } |
| | | // 获取设备详情检查列表 |
| | | export const getDeviceDetailCheckList = async (data) => { |
| | | return request(`/project/getDeviceDetailCheckList`, { |
| | | method: 'post', |
| | | data |
| | | }) |
| | | } |
| | |
| | | <div class="danger-info"> |
| | | <div class="info-header"> |
| | | <span>隐患类型</span> |
| | | <div class="header-line"> |
| | | <div class="header-line" @click="changeDangerSort(1)"> |
| | | <span>隐患数量</span> |
| | | <img src="../../assets/img/project/up.png" /> |
| | | <img v-if="dangerSortType === 1 && dangerSortDesc" src="../../assets/img/project/up.png" /> |
| | | <img v-else-if="dangerSortType === 1 && !dangerSortDesc" src="../../assets/img/project/down.png" /> |
| | | <img v-else src="../../assets/img/project/down.png" style="opacity: 0.3;" /> |
| | | </div> |
| | | <div class="header-line"> |
| | | <div class="header-line" @click="changeDangerSort(2)"> |
| | | <span>趋势</span> |
| | | <img src="../../assets/img/project/down.png" /> |
| | | <img v-if="dangerSortType === 2 && dangerSortDesc" src="../../assets/img/project/up.png" /> |
| | | <img v-else-if="dangerSortType === 2 && !dangerSortDesc" src="../../assets/img/project/down.png" /> |
| | | <img v-else src="../../assets/img/project/down.png" style="opacity: 0.3;" /> |
| | | </div> |
| | | </div> |
| | | <div class="info-body"> |
| | | <div class="info-item" v-for="item in 12" :key="item"> |
| | | <div class="item-type">文明施工</div> |
| | | <div class="info-item" v-for="(item, index) in dangerTrendList" :key="index"> |
| | | <div class="item-type">{{ item.name || '-' }}</div> |
| | | <div class="item-number"> |
| | | <div class="flex a--center"> |
| | | <div class="flex a--center"> |
| | | <span>本月</span> |
| | | <div class="number">25</div> |
| | | <div class="number">{{ item.thisMonth || 0 }}</div> |
| | | </div> |
| | | <div class="now"></div> |
| | | <div class="now" :style="{ width: getBarWidth(item.thisMonth, item.lastMonth) }"></div> |
| | | </div> |
| | | <div class="flex a--center"> |
| | | <div class="flex a--center"> |
| | | <span>上月</span> |
| | | <div class="number">25</div> |
| | | <div class="number">{{ item.lastMonth || 0 }}</div> |
| | | </div> |
| | | <div class="before"></div> |
| | | <div class="before" :style="{ width: getBarWidth(item.lastMonth, item.thisMonth) }"></div> |
| | | </div> |
| | | </div> |
| | | <div class="item-trend">50%</div> |
| | | <div class="item-trend" :class="{ 'down': item.rate < 0 }"> |
| | | <i v-if="item.rate > 0" class="trend-arrow trend-arrow-up"></i> |
| | | <i v-else-if="item.rate < 0" class="trend-arrow trend-arrow-down"></i> |
| | | <span>{{ Math.abs(item.rate) }}%</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | |
| | | <template #content> |
| | | <div class="people-info"> |
| | | <div class="people-card"> |
| | | <div class="people-item"> |
| | | <div class="people-item" v-if="peopleList.length > 0"> |
| | | <div class="people-chart"> |
| | | <el-progress :width="74" type="circle" :show-text="false" :percentage="25" define-back-color="#000" |
| | | color="#32C5FF" stroke-linecap="square"></el-progress> |
| | | <div class="progress-card"> |
| | | <div class="progress-num">25.4</div> |
| | | <div class="progress-num">{{ peopleList[0].rate }}</div> |
| | | <div class="progress-unit">%</div> |
| | | </div> |
| | | </div> |
| | | <div class="people-name">项目经理</div> |
| | | <div class="people-name">{{ peopleList[0].postName }}</div> |
| | | </div> |
| | | <div class="people-item"> |
| | | <div class="people-item" v-if="peopleList.length > 1"> |
| | | <div class="people-chart"> |
| | | <el-progress :width="74" type="circle" :show-text="false" :percentage="25" define-back-color="#000" |
| | | color="#32C5FF" stroke-linecap="square"></el-progress> |
| | | <div class="progress-card"> |
| | | <div class="progress-num">25.4</div> |
| | | <div class="progress-num">{{ peopleList[1].rate }}</div> |
| | | <div class="progress-unit">%</div> |
| | | </div> |
| | | </div> |
| | | <div class="people-name">项目经理</div> |
| | | <div class="people-name">{{ peopleList[1].name }}</div> |
| | | </div> |
| | | <div class="people-item"> |
| | | <div class="people-item" v-if="peopleList.length > 2"> |
| | | <div class="people-chart"> |
| | | <el-progress :width="74" type="circle" :show-text="false" :percentage="25" define-back-color="#000" |
| | | color="#32C5FF" stroke-linecap="square"></el-progress> |
| | | <div class="progress-card"> |
| | | <div class="progress-num">25.4</div> |
| | | <div class="progress-num">{{ peopleList[2].rate }}</div> |
| | | <div class="progress-unit">%</div> |
| | | </div> |
| | | </div> |
| | | <div class="people-name">项目经理</div> |
| | | <div class="people-name">{{ peopleList[1].name }}</div> |
| | | </div> |
| | | </div> |
| | | <div class="people-card2"> |
| | | <div class="people-item mr--67"> |
| | | <div class="people-item mr--67" v-if="peopleList.length > 3"> |
| | | <div class="people-chart"> |
| | | <el-progress :width="74" type="circle" :show-text="false" :percentage="25" define-back-color="#000" |
| | | color="#32C5FF" stroke-linecap="square"></el-progress> |
| | | <div class="progress-card"> |
| | | <div class="progress-num">25.4</div> |
| | | <div class="progress-num">{{ peopleList[3].rate }}</div> |
| | | <div class="progress-unit">%</div> |
| | | </div> |
| | | </div> |
| | | <div class="people-name">项目经理</div> |
| | | <div class="people-name">{{ peopleList[1].name }}</div> |
| | | </div> |
| | | <div class="people-item"> |
| | | <div class="people-item" v-if="peopleList.length > 4"> |
| | | <div class="people-chart"> |
| | | <el-progress :width="74" type="circle" :show-text="false" :percentage="25" define-back-color="#000" |
| | | color="#32C5FF" stroke-linecap="square"></el-progress> |
| | | <div class="progress-card"> |
| | | <div class="progress-num">25.4</div> |
| | | <div class="progress-num">{{ peopleList[4].rate }}</div> |
| | | <div class="progress-unit">%</div> |
| | | </div> |
| | | </div> |
| | | <div class="people-name">项目经理</div> |
| | | <div class="people-name">{{ peopleList[1].name }}</div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | |
| | | <div class="label flex1">{{ item.content }}</div> |
| | | <div class="label card4">{{ item.status }}</div> |
| | | </div> |
| | | |
| | | </div> |
| | | |
| | | </div> |
| | | |
| | | </template> |
| | |
| | | |
| | | <script> |
| | | import HeaderBox from "../header/index.vue"; |
| | | import { getLoginInfo, getProjectDetail, getProjectDeviceCount, getDangerCount, deviceTypes } from './service.js' |
| | | import { |
| | | getLoginInfo, |
| | | getProjectDetail, |
| | | getProjectDeviceCount, |
| | | getDangerCount, |
| | | deviceTypes, |
| | | getProjectPersonInfo, |
| | | getProjectSource, |
| | | getDangerTrend |
| | | } from './service.js' |
| | | import { |
| | | transformRiskSourceData |
| | | } from '@/utils/riskSourceMap.js' |
| | | import moment from "moment"; |
| | | import * as echarts from "echarts"; |
| | | export default { |
| | |
| | | dangerCount: {},//危险源统计 |
| | | deviceTypeList: [],//设备类型列表 |
| | | equipment: 0,//设备数量 |
| | | peopleList: [],//人员列表 |
| | | |
| | | // pie数据 |
| | | legendData: [], |
| | | dateTxt: '', |
| | | riskTrandList: [], |
| | | dangerTrendList: [], //分项隐患趋势数据 |
| | | dangerSortType: 1, // 排序类型:1-隐患数量,2-趋势 |
| | | dangerSortDesc: true, // 是否倒序 |
| | | // 风险源数据 |
| | | riskSourceData: [ |
| | | { riskScale: '一般', content: '项目风险源内容1', status: '未开始' }, |
| | |
| | | this.projectName = res.name |
| | | }) |
| | | this.getDetail() |
| | | this.getDangerTrendList() |
| | | } |
| | | }, |
| | | mounted() { |
| | |
| | | this.dangerCount = res |
| | | console.log('getDangerCount getDangerCount', res) |
| | | }) |
| | | // 项目人员信息 |
| | | getProjectPersonInfo({ projectId: this.projectId }).then(res => { |
| | | this.peopleList = res |
| | | }) |
| | | // 获取项目风险源 |
| | | getProjectSource({ projectId: this.projectId }).then(res => { |
| | | const riskSourceList = [] |
| | | |
| | | // 处理超危大工程 |
| | | if (res.superDangerousOptions) { |
| | | try { |
| | | const superDangerousArray = JSON.parse(res.superDangerousOptions) |
| | | const transformedData = transformRiskSourceData(superDangerousArray, 'superDangerous', '超危大工程') |
| | | riskSourceList.push(...transformedData) |
| | | } catch (e) { |
| | | console.error('解析超危大工程数据失败:', e) |
| | | } |
| | | } |
| | | |
| | | // 处理危大工程 |
| | | if (res.dangerousOptions) { |
| | | try { |
| | | const dangerousArray = JSON.parse(res.dangerousOptions) |
| | | const transformedData = transformRiskSourceData(dangerousArray, 'dangerous', '危大工程') |
| | | riskSourceList.push(...transformedData) |
| | | } catch (e) { |
| | | console.error('解析危大工程数据失败:', e) |
| | | } |
| | | } |
| | | |
| | | // 处理风险源 |
| | | if (res.hazardSource) { |
| | | try { |
| | | const hazardSourceArray = JSON.parse(res.hazardSource) |
| | | const transformedData = transformRiskSourceData(hazardSourceArray, 'hazardSource', '风险源') |
| | | riskSourceList.push(...transformedData) |
| | | } catch (e) { |
| | | console.error('解析风险源数据失败:', e) |
| | | } |
| | | } |
| | | |
| | | this.riskSourceData = riskSourceList |
| | | }) |
| | | }, |
| | | // 获取隐患趋势 |
| | | getDangerTrendList() { |
| | | let params = { |
| | | projectId: this.projectId, |
| | | type: this.dangerSortType,// 1数量 2 趋势 |
| | | desc: this.dangerSortDesc, // 是否倒序 |
| | | } |
| | | getDangerTrend(params).then(res => { |
| | | this.dangerTrendList = res || [] |
| | | }) |
| | | }, |
| | | // 切换隐患排序方式 |
| | | changeDangerSort(type) { |
| | | if (this.dangerSortType === type) { |
| | | // 如果点击的是当前排序类型,则切换升序/降序 |
| | | this.dangerSortDesc = !this.dangerSortDesc |
| | | } else { |
| | | // 如果点击的是不同的排序类型,则切换类型并默认倒序 |
| | | this.dangerSortType = type |
| | | this.dangerSortDesc = true |
| | | } |
| | | // 重新请求数据 |
| | | this.getDangerTrendList() |
| | | }, |
| | | // 计算进度条宽度 |
| | | getBarWidth(current, compare) { |
| | | const max = Math.max(current, compare) |
| | | if (max === 0) return '0px' |
| | | const percentage = (current / max) * 100 |
| | | const maxWidth = 114 // 最大宽度 |
| | | return `${(percentage / 100) * maxWidth}px` |
| | | }, |
| | | // 人员在岗率 |
| | | toPersonnelOnDuty() { |
| | | this.$router.push({ |
| | | path: "/personnelOnDuty", |
| | | query: { |
| | | projectId: 111, |
| | | projectName: '项目1', |
| | | projectId: this.projectId, |
| | | projectName: this.projectName, |
| | | }, |
| | | }); |
| | | }, |
| | | // 跳转隐患列表 |
| | | toHiddenDanger(type) { |
| | | this.$router.push({ |
| | | path: "/hiddenDanger", |
| | | query: { |
| | | projectId: 111, |
| | | projectName: '项目1', |
| | | projectId: this.projectId, |
| | | projectName: this.projectName, |
| | | activeTab: type, |
| | | }, |
| | | }); |
| | | }, |
| | | // 跳转设备列表 |
| | | toEquipmentInfo() { |
| | | this.$router.push({ |
| | | path: "/equipmentList", |
| | | query: { |
| | | projectId: 111, |
| | | projectName: '项目1', |
| | | projectId: this.projectId, |
| | | projectName: this.projectName, |
| | | }, |
| | | }); |
| | | }, |
| | | // 跳转重大危险源 |
| | | toMajorRiskList() { |
| | | this.$router.push({ |
| | | path: "/majorRisk", |
| | | query: { |
| | | projectId: 111, |
| | | projectName: '项目1', |
| | | projectId: this.projectId, |
| | | projectName: this.projectName, |
| | | }, |
| | | }); |
| | | }, |
| | |
| | | this.$router.push({ |
| | | path: "/planOfAudit", |
| | | query: { |
| | | projectId: 111, |
| | | projectName: '项目1', |
| | | projectId: this.projectId, |
| | | projectName: this.projectName, |
| | | }, |
| | | }); |
| | | }, |
| | |
| | | .header-line { |
| | | display: flex; |
| | | align-items: center; |
| | | cursor: pointer; |
| | | transition: opacity 0.3s; |
| | | |
| | | &:hover { |
| | | opacity: 0.8; |
| | | } |
| | | |
| | | img { |
| | | width: 7px; |
| | |
| | | .item-trend { |
| | | width: 53px; |
| | | text-align: right; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: flex-end; |
| | | |
| | | font-weight: 800; |
| | | font-size: 12px; |
| | | color: #66ffff; |
| | | line-height: 14px; |
| | | font-style: normal; |
| | | } |
| | | |
| | | .down { |
| | | color: rgba(253, 66, 62, 1); |
| | | .trend-arrow { |
| | | width: 0; |
| | | height: 0; |
| | | border-left: 4px solid transparent; |
| | | border-right: 4px solid transparent; |
| | | margin-right: 4px; |
| | | display: inline-block; |
| | | } |
| | | |
| | | .trend-arrow-up { |
| | | border-bottom: 6px solid #66ffff; |
| | | } |
| | | |
| | | .trend-arrow-down { |
| | | border-top: 6px solid rgba(253, 66, 62, 1); |
| | | } |
| | | |
| | | &.down { |
| | | color: rgba(253, 66, 62, 1); |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | .risk-body { |
| | | max-height: 180px; // 5行数据的高度:36px * 5 = 180px |
| | | overflow-y: auto; |
| | | overflow-x: hidden; |
| | | |
| | | // 自定义滚动条样式 |
| | | &::-webkit-scrollbar { |
| | | width: 4px; |
| | | } |
| | | |
| | | &::-webkit-scrollbar-track { |
| | | background: rgba(255, 255, 255, 0.1); |
| | | border-radius: 2px; |
| | | } |
| | | |
| | | &::-webkit-scrollbar-thumb { |
| | | background: rgba(50, 197, 255, 0.6); |
| | | border-radius: 2px; |
| | | |
| | | &:hover { |
| | | background: rgba(50, 197, 255, 0.8); |
| | | } |
| | | } |
| | | |
| | | .risk-item { |
| | | display: flex; |
| | | align-items: stretch; |
| | |
| | | return request(`/project/getProjectDeviceCount/${id}`, { |
| | | method: 'get' |
| | | }) |
| | | }// 各分项隐患趋势 |
| | | export const getDangerTrend = async (params) => { |
| | | return request(`/project/getProjectHiddenDangerTrend`, { |
| | | method: 'get', params |
| | | }) |
| | | } |
| | | |
| | | // 获取项目隐患数量 |
| | | export const getDangerCount = async (id) => { |
| | | return request(`/project/getProjectHiddenDangerCount/${id}`, { |
| | | method: 'get' |
| | | }) |
| | | } |
| | | // 获取大屏人员在岗信息 |
| | | export const getProjectPersonInfo = async (params) => { |
| | | return request(`/project/getProjectPersonInfo`, { |
| | | method: 'get', params |
| | | }) |
| | | } |
| | | // 获取项目大屏最新重大风险源 |
| | | export const getProjectSource = async (params) => { |
| | | return request(`/project/getProjectDangerousSource`, { |
| | | method: 'get', params |
| | | }) |
| | | } |