From 5672f352d0ba114e2ae96c8cefad6c74ae6d2934 Mon Sep 17 00:00:00 2001 From: unknown <280848880@qq.com> Date: 星期一, 23 十月 2023 10:38:28 +0800 Subject: [PATCH] 苏州-web:智能安全帽提交 --- web/src/components/element/Table.vue | 300 +- web/src/views/ProjectManage/SectionManage.vue | 1219 ++++++------ web/src/views/SecureManage/SmartHelmet/AlarmRecord.vue | 728 +++++++ web/src/assets/markerIcon.png | 0 web/src/plugins/public.js | 363 ++- web/src/style/layout-main.scss | 959 +++++----- /dev/null | 126 - web/src/api/modules/safety.js | 517 +++-- web/src/components/element/Form.vue | 64 web/src/views/SecureManage/SmartHelmet/TrackBack.vue | 741 ++++++++ web/package.json | 2 web/src/views/SecureManage/SmartHelmet/SafeHat.vue | 132 + web/src/views/SecureManage/SmartHelmet/PhoneManage.vue | 253 ++ web/package-lock.json | 10 14 files changed, 3,676 insertions(+), 1,738 deletions(-) diff --git a/web/package-lock.json b/web/package-lock.json index c1c1899..a2b3711 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -4,6 +4,11 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@amap/amap-jsapi-loader": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@amap/amap-jsapi-loader/-/amap-jsapi-loader-1.0.1.tgz", + "integrity": "sha512-nPyLKt7Ow/ThHLkSvn2etQlUzqxmTVgK7bIgwdBRTg2HK5668oN7xVxkaiRe3YZEzGzfV2XgH5Jmu2T73ljejw==" + }, "@ampproject/remapping": { "version": "2.1.1", "resolved": "https://registry.npmmirror.com/@ampproject/remapping/-/remapping-2.1.1.tgz", @@ -4977,6 +4982,11 @@ } } }, + "echarts-liquidfill": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/echarts-liquidfill/-/echarts-liquidfill-3.1.0.tgz", + "integrity": "sha512-5Dlqs/jTsdTUAsd+K5LPLLTgrbbNORUSBQyk8PSy1Mg2zgHDWm83FmvA4s0ooNepCJojFYRITTQ4GU1UUSKYLw==" + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmmirror.com/ee-first/-/ee-first-1.1.1.tgz", diff --git a/web/package.json b/web/package.json index 472926c..1f87330 100644 --- a/web/package.json +++ b/web/package.json @@ -8,12 +8,14 @@ "lint": "vue-cli-service lint" }, "dependencies": { + "@amap/amap-jsapi-loader": "^1.0.1", "@vue/composition-api": "^1.4.9", "@wangeditor/editor": "^5.1.23", "@wangeditor/editor-for-vue": "^1.0.2", "axios": "^0.26.0", "core-js": "^3.6.5", "echarts": "^5.4.2", + "echarts-liquidfill": "^3.1.0", "element-ui": "^2.15.6", "ezuikit-js": "^7.7.5", "node-sass": "^4.14.1", diff --git a/web/src/api/modules/safety.js b/web/src/api/modules/safety.js index 637f94b..963b9c4 100644 --- a/web/src/api/modules/safety.js +++ b/web/src/api/modules/safety.js @@ -1,238 +1,281 @@ -/** - * 安全管理模块 - */ - import axios from '../request'; - export default{ - /** - * 安全公告模块 - */ - // 查询安全公告信息列表 - searchSafetyAfficheList: (params) => - axios({ - method: 'post', - url: '/secure/secureNotice/findList', - headers: { - pageNum: params.pageNum, - pageSize: params.pageSize - }, - data: { - noticeName: params.noticeName, - startTime: params.startTime, - endTime: params.endTime - } - }), - // 添加 修改 安全公告信息 - insertSafetyAfficheInfo: (params) => - axios.post('/secure/secureNotice/addSecureNotice', params), - // 删除安全公告信息 - deleteSafetyAfficheInfo: (params) => - axios.post('secure/secureNotice/delete', params), - // 上架 下架安全公告信息 - upAndDownSafetyAfficheInfo: (params) => - axios.post('/secure/secureNotice/updown', params), - - - /** - * 劳务档案 - */ - //奖惩记录列表 - recordAwardsLists: (params) => - axios.post('/secure/encourage/encourageRecordWebList', params), - //安全码列表 - codeSafeLists: (params) => - axios.post('/secure/encourage/encourageSecurityCode', params), - - /** - * 安全考核模块 - */ - // 根据类型获取类型名称(安全培训和日常培训制定名称) - getAllSecureTrainPull: (params) => - axios.post('/secure/secureTrain/secureTrainPull', params), - // 查询安全考核信息 - searchSafetyExamineList: (params) => - axios({ - method: 'post', - url: '/secure/exam/findList', - headers: { - pageNum: params.pageNum, - pageSize: params.pageSize - }, - data: { - examName: params.examName, - startDay: params.startDay, - endDay: params.endDay - } - }), - // 添加 修改 安全考核信息 - insertSafetyExamineInfo: (params) => - axios.post('/secure/exam/addExam', params), - // 删除安全考核信息 - deleteSafetyExamineInfo: (params) => - axios.post('/secure/exam/delete', params), - // 获取安全考核统计信息 - getExamRecordStatistical: (params) => - axios.post('/secure/examRecord/recordStat', params), - // 查询安全考核记录 - searchSafetyCheckRecord: (params) => - axios({ - method: 'post', - url: '/secure/examRecord/recordList', - headers: { - pageNum: params.pageNum, - pageSize: params.pageSize - }, - data: { - examId: params.examId, - examResult: params.examResult, - departId: params.departId, - groupId: params.groupId, - startTime: params.startTime, - endTime: params.endTime - } - }), - - - // 安全考题信息列表 - searchSafetyTopicList: (params) => - axios({ - method: 'post', - url: '/secure/question/findList', - headers: { - pageNum: params.pageNum, - pageSize: params.pageSize - }, - data: { - trainName: params.trainName - } - }), - // 新增 修改安全考题信息 - insertSafetyTopicInfo: (params) => - axios.post('/secure/question/addQuestion', params), - // 删除安全考题信息 - deleteSafetyTopicInfo: (params) => - axios.post('/secure/question/delete', params), - // 获取答案列表信息 - getAnswerlIstInfo: (params) => - axios.post('secure/question/answerList', params), - - /** - * 消防器材模块 - */ - // 查询消防器材信息列表 - searchFireequipmentList: (params) => - axios({ - method: 'post', - url: '/secure/secureGood/findList', - headers: { - pageNum: params.pageNum, - pageSize: params.pageSize - }, - data: { - goodName: params.goodName, - startTime: params.startTime, - endTime: params.endTime - } - }), - // 添加 修改 消防器材信息 - insertFireequipmentInfo: (params) => - axios.post('/secure/secureGood/addSecureGood', params), - // 删除消防器材信息 - deleteFireequipmentInfo: (params) => - axios.post('secure/secureGood/delete', params), - // 导出二维码 - getFireequipmentCode: (params) => - axios({ - method: 'get', - url: '/secure/secureGood/viewcode', - params: params, - responseType: 'blob' - }), - // 获取消防器材图片列表 - getFireequipmentImageInfo: (params) => - axios.post('/secure/secureGood/fileList', params), - - /** - * 安全培训和日常培训制定 - */ - // 列表信息 - searchEnactLists: params => - axios.post('/secure/secureTrain/secureTrainList', params), - // 添加信息 - insertEnactInfo: params => - axios.post('/secure/secureTrain/secureTrainInsert', params), - // 修改信息 - updateEnactInfo: params => - axios.post('/secure/secureTrain/secureTrainUpdate', params), - // 信息详情 - detailsEnactInfo: params => - axios.post('/secure/secureTrain/secureTrainInfo', params), - // 删除信息 - deleteEnactInfo: params => - axios.post('/secure/secureTrain/secureTrainDel', params), - //查询记录 - getEnactRecords: params => - axios.post('/secure/secureTrain/secureTrainRecord', params), - - /** - * 安全资料模块 - */ - // 列表信息 - searchDatumLists: params => - axios.post('/secure/material/materialList', params), - // 添加信息 - insertDatumInfo: params => - axios.post('/secure/material/materialInsert', params), - // 修改信息 - updateDatumInfo: params => - axios.post('/secure/material/materialUpdate', params), - // 信息详情 - detailsDatumInfo: params => - axios.post('/secure/material/materialInfo', params), - - /** - * 奖惩标准模块 - */ - // 列表信息 - searchPunishLists: params => - axios.post('/secure/encourage/encourageList', params), - // 添加信息 - insertPunishInfo: params => - axios.post('/secure/encourage/encourageInsert', params), - // 修改信息 - updatePunishInfo: params => - axios.post('/secure/encourage/encourageUpdate', params), - // 信息详情 - detailsPunishInfo: params => - axios.post('/secure/encourage/encourageInfo', params), - // 信息删除 - deletePunishInfo: params => - axios.post('/secure/encourage/encourageDel', params), - - /** - * 风险分级管控 - */ - RiskGrad: { - // 危险源告知 - warning : { - getLists: params => - axios.post('/secure/regionHazardInform/findAll', params), - insert: params => - axios.post('/secure/regionHazardInform/insert', params), - update: params => - axios.post('/secure/regionHazardInform/update', params), - delete: params => - axios.get('/secure/regionHazardInform/delete', { params }), - }, - // 区域包保 - allocation : { - getLists: params => - axios.post('/secure/tRegionWarranty/findAll', params), - insert: params => - axios.post('/secure/tRegionWarranty/insert', params), - update: params => - axios.post('/secure/tRegionWarranty/update', params), - delete: params => - axios.get('/secure/tRegionWarranty/delete', { params }), - } - } +/** + * 安全管理模块 + */ + import axios from '../request'; + export default{ + /** + * 安全公告模块 + */ + // 查询安全公告信息列表 + searchSafetyAfficheList: (params) => + axios({ + method: 'post', + url: '/secure/secureNotice/findList', + headers: { + pageNum: params.pageNum, + pageSize: params.pageSize + }, + data: { + noticeName: params.noticeName, + startTime: params.startTime, + endTime: params.endTime + } + }), + // 添加 修改 安全公告信息 + insertSafetyAfficheInfo: (params) => + axios.post('/secure/secureNotice/addSecureNotice', params), + // 删除安全公告信息 + deleteSafetyAfficheInfo: (params) => + axios.post('secure/secureNotice/delete', params), + // 上架 下架安全公告信息 + upAndDownSafetyAfficheInfo: (params) => + axios.post('/secure/secureNotice/updown', params), + + + /** + * 劳务档案 + */ + //奖惩记录列表 + recordAwardsLists: (params) => + axios.post('/secure/encourage/encourageRecordWebList', params), + //安全码列表 + codeSafeLists: (params) => + axios.post('/secure/encourage/encourageSecurityCode', params), + + /** + * 安全考核模块 + */ + // 根据类型获取类型名称(安全培训和日常培训制定名称) + getAllSecureTrainPull: (params) => + axios.post('/secure/secureTrain/secureTrainPull', params), + // 查询安全考核信息 + searchSafetyExamineList: (params) => + axios({ + method: 'post', + url: '/secure/exam/findList', + headers: { + pageNum: params.pageNum, + pageSize: params.pageSize + }, + data: { + examName: params.examName, + startDay: params.startDay, + endDay: params.endDay + } + }), + // 添加 修改 安全考核信息 + insertSafetyExamineInfo: (params) => + axios.post('/secure/exam/addExam', params), + // 删除安全考核信息 + deleteSafetyExamineInfo: (params) => + axios.post('/secure/exam/delete', params), + // 获取安全考核统计信息 + getExamRecordStatistical: (params) => + axios.post('/secure/examRecord/recordStat', params), + // 查询安全考核记录 + searchSafetyCheckRecord: (params) => + axios({ + method: 'post', + url: '/secure/examRecord/recordList', + headers: { + pageNum: params.pageNum, + pageSize: params.pageSize + }, + data: { + examId: params.examId, + examResult: params.examResult, + departId: params.departId, + groupId: params.groupId, + startTime: params.startTime, + endTime: params.endTime + } + }), + + + // 安全考题信息列表 + searchSafetyTopicList: (params) => + axios({ + method: 'post', + url: '/secure/question/findList', + headers: { + pageNum: params.pageNum, + pageSize: params.pageSize + }, + data: { + trainName: params.trainName + } + }), + // 新增 修改安全考题信息 + insertSafetyTopicInfo: (params) => + axios.post('/secure/question/addQuestion', params), + // 删除安全考题信息 + deleteSafetyTopicInfo: (params) => + axios.post('/secure/question/delete', params), + // 获取答案列表信息 + getAnswerlIstInfo: (params) => + axios.post('secure/question/answerList', params), + + /** + * 消防器材模块 + */ + // 查询消防器材信息列表 + searchFireequipmentList: (params) => + axios({ + method: 'post', + url: '/secure/secureGood/findList', + headers: { + pageNum: params.pageNum, + pageSize: params.pageSize + }, + data: { + goodName: params.goodName, + startTime: params.startTime, + endTime: params.endTime + } + }), + // 添加 修改 消防器材信息 + insertFireequipmentInfo: (params) => + axios.post('/secure/secureGood/addSecureGood', params), + // 删除消防器材信息 + deleteFireequipmentInfo: (params) => + axios.post('secure/secureGood/delete', params), + // 导出二维码 + getFireequipmentCode: (params) => + axios({ + method: 'get', + url: '/secure/secureGood/viewcode', + params: params, + responseType: 'blob' + }), + // 获取消防器材图片列表 + getFireequipmentImageInfo: (params) => + axios.post('/secure/secureGood/fileList', params), + + /** + * 安全培训和日常培训制定 + */ + // 列表信息 + searchEnactLists: params => + axios.post('/secure/secureTrain/secureTrainList', params), + // 添加信息 + insertEnactInfo: params => + axios.post('/secure/secureTrain/secureTrainInsert', params), + // 修改信息 + updateEnactInfo: params => + axios.post('/secure/secureTrain/secureTrainUpdate', params), + // 信息详情 + detailsEnactInfo: params => + axios.post('/secure/secureTrain/secureTrainInfo', params), + // 删除信息 + deleteEnactInfo: params => + axios.post('/secure/secureTrain/secureTrainDel', params), + //查询记录 + getEnactRecords: params => + axios.post('/secure/secureTrain/secureTrainRecord', params), + + /** + * 安全资料模块 + */ + // 列表信息 + searchDatumLists: params => + axios.post('/secure/material/materialList', params), + // 添加信息 + insertDatumInfo: params => + axios.post('/secure/material/materialInsert', params), + // 修改信息 + updateDatumInfo: params => + axios.post('/secure/material/materialUpdate', params), + // 信息详情 + detailsDatumInfo: params => + axios.post('/secure/material/materialInfo', params), + + /** + * 奖惩标准模块 + */ + // 列表信息 + searchPunishLists: params => + axios.post('/secure/encourage/encourageList', params), + // 添加信息 + insertPunishInfo: params => + axios.post('/secure/encourage/encourageInsert', params), + // 修改信息 + updatePunishInfo: params => + axios.post('/secure/encourage/encourageUpdate', params), + // 信息详情 + detailsPunishInfo: params => + axios.post('/secure/encourage/encourageInfo', params), + // 信息删除 + deletePunishInfo: params => + axios.post('/secure/encourage/encourageDel', params), + + /** + * 风险分级管控 + */ + RiskGrad: { + // 危险源告知 + warning : { + getLists: params => + axios.post('/secure/regionHazardInform/findAll', params), + insert: params => + axios.post('/secure/regionHazardInform/insert', params), + update: params => + axios.post('/secure/regionHazardInform/update', params), + delete: params => + axios.get('/secure/regionHazardInform/delete', { params }), + }, + // 区域包保 + allocation : { + getLists: params => + axios.post('/secure/tRegionWarranty/findAll', params), + insert: params => + axios.post('/secure/tRegionWarranty/insert', params), + update: params => + axios.post('/secure/tRegionWarranty/update', params), + delete: params => + axios.get('/secure/tRegionWarranty/delete', { params }), + } + }, + /** + * 智能安全帽 + */ + SmartHelmet: { + // 人员列表(智能安全帽下其他页面共用此接口) + getLists: params => + axios.post('/materials/helmet/helmetList', params), + // ---轨迹回放--- + trackBack: { + // 获取用户在线时长 + getOnlineTime: params => + axios.post('/materials/helmet/helmetTrajectoryList', params), + // 轨迹数据 + getTrackLists: params => + axios.post('/materials/helmet/helmetMotionList', params), + }, + // ---照片管理--- + pic: { + // 图片 + getPics: params => + axios.post('/materials/helmet/helmetPictureList', params), + }, + // ---报警记录--- + warning: { + // 报警数 + getWarning: params => + axios.post('/materials/helmet/helmetReportTotal', params), + // 报警详情 + getDetailWarning: params => + axios.post('/materials/helmet/helmetReportUser', params), + }, + // 区域包保 + allocation : { + getLists: params => + axios.post('/secure/tRegionWarranty/findAll', params), + insert: params => + axios.post('/secure/tRegionWarranty/insert', params), + update: params => + axios.post('/secure/tRegionWarranty/update', params), + delete: params => + axios.get('/secure/tRegionWarranty/delete', { params }), + } + } } \ No newline at end of file diff --git a/web/src/assets/markerIcon.png b/web/src/assets/markerIcon.png new file mode 100644 index 0000000..39347ed --- /dev/null +++ b/web/src/assets/markerIcon.png Binary files differ diff --git a/web/src/components/element/Form.vue b/web/src/components/element/Form.vue new file mode 100644 index 0000000..a955305 --- /dev/null +++ b/web/src/components/element/Form.vue @@ -0,0 +1,64 @@ +<template> + <div class="role"> + <!-- https://juejin.cn/post/7042597964618924069?searchId=202310071305486BA29870D97939288832#heading-8 --> + <!-- https://juejin.cn/post/7139110255748710436?searchId=2023092815420483B1983D44854DBE6608#comment --> + <el-form + ref="ruleForm" + class="rule-form" + :model="formConfig.formModels" + :rules="formConfig.formRules" + :label-width="formConfig.labelWidth || 'auto'"> + <el-row> + <el-col v-for="item in formConfig.formItems" :key="item.label" :span="item.span || 24"> + <el-form-item :label="item.label" :style="formConfig.itemStyle || null"> + + <template v-if="item.type === 'input' || item.type === 'password'"> + <el-input :placeholder="item.placeholder || `请输入${item.label}`" + :show-password="item.type === 'password'"></el-input> + </template> + + <template v-else-if="item.type === 'select'"> + <el-select :placeholder="item.placeholder || `请选择${item.label}`"> + <el-option v-for="option in item.options" :key="option.label" :label="option.label" + :value="option.value"></el-option> + </el-select> + </template> + + <template v-else> + <el-date-picker v-bind="item.otherOptions"></el-date-picker> + </template> + + </el-form-item> + </el-col> + </el-row> + </el-form> + </div> +</template> +<script> +export default { + data() { + return {} + }, + props: { + formConfig: { + type: Object, + default: () => ({}) + } + } +} +</script> +<style scoped lang="scss"> +.el-form { + padding-right: 15px; + max-height: 550px; + overflow-y: auto; +} + +::v-deep .el-form-item__label { + color: #fff +} + +.el-select { + width: 100%; +} +</style> \ No newline at end of file diff --git a/web/src/components/element/Table.vue b/web/src/components/element/Table.vue index b266c54..e5d56bd 100644 --- a/web/src/components/element/Table.vue +++ b/web/src/components/element/Table.vue @@ -1,151 +1,151 @@ -<template> - <div> - <!-- elTable --> - <el-table v-loading="tableLoading" :data="tableData" :ref="tableRef" :size="tableSize" @row-click="rowClick" - @row-dblclick="rowDblClick" @selection-change="handleSelectionChange" border stripe> - <template v-for="(col, index) in tableColumns"> - <!-- 选择框 --> - <el-table-column v-if="col.selection" :key="`selection${index}`" width="50" type="selection" align="center"> - </el-table-column> - - <!-- 序号 --> - <el-table-column v-else-if="col.index" :key="`index${index}`" width="50" :label="col.name || '序号'" type="index" - align="center"> - </el-table-column> - - <!-- 操作 --> - <el-table-column v-else-if="col.operation" :key="`operation${col.name}`" :width="col.width" :label="col.name" - align="center"> - <template #default="{ row }"> - <el-button v-for="subCol in col.value" :key="subCol.name" :class="subCol.class || ''" :icon="subCol.icon || ''" - v-permission="subCol.permission || ''" @click="subCol.handleRow ? subCol.handleRow(row) : emptyFn"> - {{ subCol.name }} - </el-button> - </template> - </el-table-column> - - <!-- 常规col --> - <el-table-column v-else :key="col.name" :width="col.width" :label="col.name" :prop="col.key" - :show-overflow-tooltip="col.showOverflowTip || false" :align="col.align || 'center'"> - <template #default="{ row }"> - <!-- slotのcol --> - <template v-if="col.slot"> - <slot :name="col.slot" :row="row"></slot> - </template> - <!-- 需处理数据のcol --> - <template v-else-if="col.formatter"> - {{ col.formatter(row) }} - </template> - <!-- 普通のcol --> - <template v-else> - {{ row[col.key] }} - </template> - </template> - </el-table-column> - </template> - </el-table> - <!-- 分页组件 --> - <cPagination :total="pageTotal" :page-num.sync="pageNum" :page-size.sync="pageSize" @change-page-num="changePageNum" - @change-page-size="changePageSize" /> - </div> -</template> -<script> -import cPagination from "./Pagination" -export default { - name: "cTable", - data() { - return { - time: null, - emptyFn: () => { } - }; - }, - props: { - tableData: { - type: Array, - default() { - return []; - } - }, - tableHeight: { - type: Number, - default: null - }, - tableLoading: { - type: Boolean, - default: false - }, - tableRef: { - type: String, - default: "multipleTable" - }, - tableSize: { - type: String, - default: "mini" - }, - tableColumns: { - type: Array, - default() { - return []; - } - }, - pageTotal: { - type: Number - }, - pageNum: { - type: Number, - default: 1 - }, - pageSize: { - type: Number, - default: 10 - }, - pageChange: { - type: Function, - default: () => { } - }, - handleSelection: { - type: Function, - default: () => { } - } - }, - components: { - cPagination - }, - methods: { - changePageNum(val) { - // console.log(val) - // this.pageNum = val - this.$emit("update:pageNum", val); - this.pageChange(); - }, - changePageSize(val) { - this.$emit("update:pageSize", val); - if (val * (this.pageNum - 1) <= this.pageTotal) { - this.pageChange(); - } - }, - // 单击 - rowClick() { - // this.time && clearTimeout(this.time); - // this.time = setTimeout(() => { - // this.$refs[this.tableRef].toggleRowSelection(row); - // }, 200); - }, - // 双击 - rowDblClick() { - // this.time && clearTimeout(this.time); - }, - clearSelection() { - this.$refs[this.tableRef].clearSelection(); - }, - handleSelectionChange(val) { - this.handleSelection(val); - } - } -}; -</script> -<style lang="scss" scoped> -.el-table { - border: none; -} +<template> + <div class="box"> + <!-- elTable --> + <el-table v-loading="tableLoading" :data="tableData" :ref="tableRef" :size="tableSize" @row-click="handleRowClick" + @row-dblclick="handleRowDblClick" @selection-change="handleSelectionChange" border stripe> + <template v-for="(col, index) in tableColumns"> + <!-- 选择框 --> + <el-table-column v-if="col.selection" :key="`selection${index}`" width="50" type="selection" align="center"> + </el-table-column> + + <!-- 序号 --> + <el-table-column v-else-if="col.index" :key="`index${index}`" width="50" :label="col.name || '序号'" type="index" + align="center"> + </el-table-column> + + <!-- 操作 --> + <el-table-column v-else-if="col.operation" :key="`operation${col.name}`" :width="col.width" :label="col.name" + align="center"> + <template #default="{ row }"> + <el-button v-for="subCol in col.value" :key="subCol.name" :class="subCol.class || ''" + :icon="subCol.icon || ''" v-permission="subCol.permission || ''" + @click="subCol.handleRow ? subCol.handleRow(row) : emptyFn"> + {{ subCol.name }} + </el-button> + </template> + </el-table-column> + + <!-- 常规col --> + <el-table-column v-else :key="col.name" :width="col.width" :label="col.name" :prop="col.key" + :show-overflow-tooltip="col.showOverflowTip || false" :align="col.align || 'center'"> + <template #default="{ row }"> + <!-- slotのcol --> + <template v-if="col.slot"> + <slot :name="col.slot" :row="row"></slot> + </template> + <!-- 需处理数据のcol --> + <template v-else-if="col.formatter"> + {{ col.formatter(row) }} + </template> + <!-- 普通のcol --> + <template v-else> + {{ row[col.key] }} + </template> + </template> + </el-table-column> + </template> + </el-table> + <!-- 分页组件 --> + <cPagination v-if="pageTotal" :total="pageTotal" :page-num.sync="pageNum" :page-size.sync="pageSize" + @change-page-num="changePageNum" @change-page-size="changePageSize" /> + </div> +</template> +<script> +import cPagination from "./Pagination" +export default { + name: "cTable", + data() { + return { + time: null, + emptyFn: () => { } + }; + }, + props: { + tableData: { + type: Array, + default() { + return []; + } + }, + tableHeight: { + type: Number, + default: null + }, + tableLoading: { + type: Boolean, + default: false + }, + tableRef: { + type: String, + default: "multipleTable" + }, + tableSize: { + type: String, + default: "mini" + }, + tableColumns: { + type: Array, + default() { + return []; + } + }, + pageTotal: { + type: Number + }, + pageNum: { + type: Number, + default: 1 + }, + pageSize: { + type: Number, + default: 10 + }, + pageChange: { + type: Function, + default: () => { } + }, + handleSelection: { + type: Function, + default: () => { } + }, + rowClick: { + type: Function, + default: () => { } + }, + }, + components: { + cPagination + }, + methods: { + changePageNum(val) { + this.$emit("update:pageNum", val) + this.pageChange() + }, + changePageSize(val) { + this.$emit("update:pageSize", val) + if (val * (this.pageNum - 1) <= this.pageTotal) { + this.pageChange() + } + }, + // 单击 + handleRowClick(row) { + this.rowClick(row) + }, + // 双击 + handleRowDblClick() { }, + handleSelectionChange(val) { + this.handleSelection(val) + } + } +}; +</script> +<style lang="scss" scoped> +.box { + height: 100%; +} + +.el-table { + border: none; + height: 100%; +} </style> \ No newline at end of file diff --git a/web/src/components/table/Pagination.vue b/web/src/components/table/Pagination.vue deleted file mode 100644 index 46cfa39..0000000 --- a/web/src/components/table/Pagination.vue +++ /dev/null @@ -1,78 +0,0 @@ -<template> - <div class="pagination-container"> - <el-pagination @current-change="handleCurrentChange" @size-change="handleSizeChange" :page-sizes="[10, 20, 30]" - :total="total" :current-page="pageNum" :page-size="pageSize" - layout="total, sizes, prev, pager, next, jumper"></el-pagination> - </div> -</template> -<script> -export default { - name: "pagination", - data() { - return {}; - }, - props: { - // 总页数 - total: { - type: Number, - }, - // 当前页 - pageNum: { - type: Number, - }, - // 每页显示条数 - pageSize: { - type: Number, - }, - }, - methods: { - // 当前页码变化 - handleCurrentChange(val) { - this.$emit("change-page-num", val); - }, - // 每页查看条数变化 - handleSizeChange(val) { - this.$emit("change-page-size", val); - }, - }, - // watch: { - // pageSize: { - // // immediate: true, - // handler(newValue, oldValue) { - // this.page._pageSize = this.newValue; - // console.log("pageSize", newValue, oldValue); - // }, - // }, - // pageNum: { - // // immediate: true, - // handler(newValue, oldValue) { - // this.page._currentPage = newValue; - // console.log("pageNum", newValue, oldValue); - // }, - // }, - // }, -}; -</script> -<style lang="scss" scoped> -// 主体底部样式 -::v-deep.el-pagination .btn-prev, -::v-deep.el-pagination .btn-next, -::v-deep.el-pagination .el-pager li { - background-color: rgba(20, 25, 58, 0.4); - border: 1px solid rgba(255, 255, 255, 0.12); - font-weight: 400; - color: #E2E4E9; - border-radius: 4px; -} - -::v-deep.el-pagination .el-pager li:not(.disabled).active { - color: #fff; - border: 1px solid #39B5FE; - background-color: #0B3562; - font-weight: 400; -} - -.pagination-container { - margin-top: 30px; -} -</style> \ No newline at end of file diff --git a/web/src/components/table/Table.vue b/web/src/components/table/Table.vue deleted file mode 100644 index ce3e9a8..0000000 --- a/web/src/components/table/Table.vue +++ /dev/null @@ -1,126 +0,0 @@ -<template> - <div> - <el-table v-loading="tableLoading" :data="tableData" :ref="tableRef" :size="tableSize" @row-click="rowClick" - @row-dblclick="rowDblClick" @selection-change="handleSelectionChange" border stripe> - <template v-for="(col, index) in tableColumnsConfig"> - <!-- 选择框 --> - <el-table-column v-if="col.selection" width="50" :key="`selection_${index}`" type="selection" align="center"> - </el-table-column> - <!-- 序号 --> - <el-table-column v-else-if="col.index" width="50" label="序号" :key="`index_${index}`" type="index" align="center"> - </el-table-column> - <!-- 自定义 --> - <slot v-else-if="col.slot" :name="col.slot" show-overflow-tooltip></slot> - <!-- 常规col --> - <el-table-column v-else :key="`col_${index}`" :width="col.width" :label="col.name" :prop="col.key" - :show-overflow-tooltip="col.showOverflowTip || false" :align="col.align || 'center'"> - <template #default="{ row }"> - {{ row[col.key] }} - </template> - </el-table-column> - </template> - </el-table> - <!-- 分页组件 --> - <cPagination :total="pageTotal" :page-num.sync="pageNum" :page-size.sync="pageSize" @change-page-num="changePageNum" - @change-page-size="changePageSize" /> - </div> -</template> -<script> -import cPagination from "@/components/table/Pagination" -export default { - name: "cTable", - data() { - return { - time: null - }; - }, - props: { - tableData: { - type: Array, - default() { - return []; - } - }, - tableHeight: { - type: Number, - default: null - }, - tableLoading: { - type: Boolean, - default: false - }, - tableRef: { - type: String, - default: "multipleTable" - }, - tableSize: { - type: String, - default: "mini" - }, - tableColumnsConfig: { - type: Array, - default() { - return []; - } - }, - pageTotal: { - type: Number - }, - pageNum: { - type: Number, - default: 1 - }, - pageSize: { - type: Number, - default: 10 - }, - pageChange: { - type: Function, - default: () => { } - }, - handleSelection: { - type: Function, - default: () => { } - } - }, - components: { - cPagination - }, - methods: { - changePageNum(val) { - // console.log(val) - // this.pageNum = val - this.$emit("update:pageNum", val); - this.pageChange(); - }, - changePageSize(val) { - this.$emit("update:pageSize", val); - if (val * (this.pageNum - 1) <= this.pageTotal) { - this.pageChange(); - } - }, - // 单击 - rowClick() { - // this.time && clearTimeout(this.time); - // this.time = setTimeout(() => { - // this.$refs[this.tableRef].toggleRowSelection(row); - // }, 200); - }, - // 双击 - rowDblClick() { - // this.time && clearTimeout(this.time); - }, - clearSelection() { - this.$refs[this.tableRef].clearSelection(); - }, - handleSelectionChange(val) { - this.handleSelection(val); - } - } -}; -</script> -<style lang="scss" scoped> -.el-table { - border: none; -} -</style> \ No newline at end of file diff --git a/web/src/plugins/public.js b/web/src/plugins/public.js index 10fb130..cd06262 100644 --- a/web/src/plugins/public.js +++ b/web/src/plugins/public.js @@ -1,167 +1,196 @@ -import axios from 'axios' -import Vue from 'vue' -/** - * 防抖函数 - */ - export const debounce = (fun, time) => { - let timer = null - return function() { - if(timer) { - clearTimeout(timer) - } - let args = arguments - timer = setTimeout(() => { - fun.apply(this, args) - }, time) - } -} - -/** - * 节流函数 - */ -export const throttle = (fun, time) => { - let strTime = 0 - return function() { - let endTime = new Date() - let args = arguments - if(endTime - strTime > time) { - strTime = endTime - fun.apply(this, args) - } - } -} - -/** - * 动态切换组件尺寸 -*/ -export const changeSize = () => { - // let size = "" - // let viewWidth = document.documentElement.clientWidth - // switch(true) { - // case viewWidth <= 1024: - // size = "mini" - // break; - // case viewWidth <= 1280: - // size = "small" - // break; - // case viewWidth <= 1600: - // size = "medium" - // break; - // default: - // size = "" - // } - // return size - return 'small' -} - -/** - * 存入/更新cookie信息 - * name: cookie 名称 - * values: cookie 值 - * times: 过期时间 -*/ -export const setCookie = (name, values, times) => { - let date = new Date(); - let params = JSON.stringify(values) - date.setTime(date.getTime() + (times * 24 * 60 * 60 * 1000)); - document.cookie = `${name}=${params};expires=${date.toGMTString()}`; -} - -/** - * 获取cookie信息 - * name: 存入的cookie名 -*/ -export const getCookie = (name) => { - let data = ""; - let list = document.cookie.split(';'); - list.forEach(item => { - item = item.split('=') - if(item[0].replace(/^\s+|\s+$/g,"") === name) { - data = JSON.parse(item[item.length - 1]) - } - }) - return data -} - -/** - * 处理时间戳 -*/ -export const changeTime = (time) => { - const date = new Date(time); - console.log(date,'---'); - let year = date.getFullYear(); - let month = date.getMonth() + 1; - month = month > 9 ? month : `0${month}`; - let day = date.getDate() > 9 ? date.getDate() : `0${date.getDate()}`; - let hours = date.getHours() > 9 ? date.getHours() : `0${date.getHours()}`; - let min = date.getMinutes() > 9 ? date.getMinutes() : `0${date.getMinutes()}`; - let sec = date.getSeconds() > 9 ? date.getSeconds() : `0${date.getSeconds()}`; - return `${year}-${month}-${day} ${hours}:${min}:${sec}` -} - -/** - * 下载文件公共方法 -*/ - -export const downLoadFile = (data, url) => { - let link = document.createElement("a") - link.style.display = "none"; - link.href = `${process.env.VUE_APP_BASE_URL}${url}?authcode=${data}`; - document.body.appendChild(link) - link.click(); - URL.revokeObjectURL(link.href) // 释放URL 对象 -} - -export const downFiles = (data, fileName = 'downLoad', fileType) => { - let url = URL.createObjectURL(new Blob([data])) - let link = document.createElement("a") - link.style.display = "none"; - link.href = url; - link.setAttribute("download", `${fileName}.${fileType}`) - document.body.appendChild(link) - link.click(); - URL.revokeObjectURL(link.href) // 释放URL 对象 -} - -/** - * 转二维码(blob流转地址) - */ -export const tranQr = (val) => { - let imageUrl = ''; - Vue.prototype.$api.Print.getPrintPhone({num:val}).then(res=>{ - imageUrl = URL.createObjectURL(new Blob([res.data])); - }) - return imageUrl -} - - -export const downLoadQR = (data, url) => { - axios({ - method: 'get', - url: `${process.env.VUE_APP_BASE_URL}${url}?num=${data}`, - responseType: 'blob' - }).then((res) => { - downFiles(res.data, '二维码图片','png') - }) - // let link = document.createElement("a") - // link.style.display = "none"; - // link.href = ; - // document.body.appendChild(link) - // link.click(); - // URL.revokeObjectURL(link.href) // 释放URL 对象 - - // link.href = `${process.env.VUE_APP_BASE_URL}${url}?num=${data}`; - // link.setAttribute("download", '二维码图片.png') - // document.body.appendChild(link) - // link.click(); -} -//通过扫描二维码跳转详情页面 -export const downLoadQRDetails = (data,id,typePage, url) => { - axios({ - method: 'get', - url: `${process.env.VUE_APP_BASE_URL}${url}?num=${data}&bigDeviceId=${id}&type=${typePage}`, - responseType: 'blob' - }).then((res) => { - downFiles(res.data, '二维码图片','png') - }) -} \ No newline at end of file +import axios from 'axios' +import Vue from 'vue' +/** + * 防抖函数 + */ + export const debounce = (fun, time) => { + let timer = null + return function() { + if(timer) { + clearTimeout(timer) + } + let args = arguments + timer = setTimeout(() => { + fun.apply(this, args) + }, time) + } +} + +/** + * 节流函数 + */ +export const throttle = (fun, time) => { + let strTime = 0 + return function() { + let endTime = new Date() + let args = arguments + if(endTime - strTime > time) { + strTime = endTime + fun.apply(this, args) + } + } +} + +/** + * 动态切换组件尺寸 +*/ +export const changeSize = () => { + // let size = "" + // let viewWidth = document.documentElement.clientWidth + // switch(true) { + // case viewWidth <= 1024: + // size = "mini" + // break; + // case viewWidth <= 1280: + // size = "small" + // break; + // case viewWidth <= 1600: + // size = "medium" + // break; + // default: + // size = "" + // } + // return size + return 'small' +} + +/** + * 存入/更新cookie信息 + * name: cookie 名称 + * values: cookie 值 + * times: 过期时间 +*/ +export const setCookie = (name, values, times) => { + let date = new Date(); + let params = JSON.stringify(values) + date.setTime(date.getTime() + (times * 24 * 60 * 60 * 1000)); + document.cookie = `${name}=${params};expires=${date.toGMTString()}`; +} + +/** + * 获取cookie信息 + * name: 存入的cookie名 +*/ +export const getCookie = (name) => { + let data = ""; + let list = document.cookie.split(';'); + list.forEach(item => { + item = item.split('=') + if(item[0].replace(/^\s+|\s+$/g,"") === name) { + data = JSON.parse(item[item.length - 1]) + } + }) + return data +} + +/** + * 处理时间戳 +*/ +export const changeTime = (time) => { + const date = new Date(time); + console.log(date,'---'); + let year = date.getFullYear(); + let month = date.getMonth() + 1; + month = month > 9 ? month : `0${month}`; + let day = date.getDate() > 9 ? date.getDate() : `0${date.getDate()}`; + let hours = date.getHours() > 9 ? date.getHours() : `0${date.getHours()}`; + let min = date.getMinutes() > 9 ? date.getMinutes() : `0${date.getMinutes()}`; + let sec = date.getSeconds() > 9 ? date.getSeconds() : `0${date.getSeconds()}`; + return `${year}-${month}-${day} ${hours}:${min}:${sec}` +} + +/** + * 下载文件公共方法 +*/ + +export const downLoadFile = (data, url) => { + let link = document.createElement("a") + link.style.display = "none"; + link.href = `${process.env.VUE_APP_BASE_URL}${url}?authcode=${data}`; + document.body.appendChild(link) + link.click(); + URL.revokeObjectURL(link.href) // 释放URL 对象 +} + +export const downFiles = (data, fileName = 'downLoad', fileType) => { + let url = URL.createObjectURL(new Blob([data])) + let link = document.createElement("a") + link.style.display = "none"; + link.href = url; + link.setAttribute("download", `${fileName}.${fileType}`) + document.body.appendChild(link) + link.click(); + URL.revokeObjectURL(link.href) // 释放URL 对象 +} + +/** + * 转二维码(blob流转地址) + */ +export const tranQr = (val) => { + let imageUrl = ''; + Vue.prototype.$api.Print.getPrintPhone({num:val}).then(res=>{ + imageUrl = URL.createObjectURL(new Blob([res.data])); + }) + return imageUrl +} + + +export const downLoadQR = (data, url) => { + axios({ + method: 'get', + url: `${process.env.VUE_APP_BASE_URL}${url}?num=${data}`, + responseType: 'blob' + }).then((res) => { + downFiles(res.data, '二维码图片','png') + }) + // let link = document.createElement("a") + // link.style.display = "none"; + // link.href = ; + // document.body.appendChild(link) + // link.click(); + // URL.revokeObjectURL(link.href) // 释放URL 对象 + + // link.href = `${process.env.VUE_APP_BASE_URL}${url}?num=${data}`; + // link.setAttribute("download", '二维码图片.png') + // document.body.appendChild(link) + // link.click(); +} +//通过扫描二维码跳转详情页面 +export const downLoadQRDetails = (data,id,typePage, url) => { + axios({ + method: 'get', + url: `${process.env.VUE_APP_BASE_URL}${url}?num=${data}&bigDeviceId=${id}&type=${typePage}`, + responseType: 'blob' + }).then((res) => { + downFiles(res.data, '二维码图片','png') + }) +} + +/* Date原型添加format方法 */ +Date.prototype.format = function (fmt) { + fmt = fmt || 'yyyy-MM-dd' + var o = { + 'M+': this.getMonth() + 1, //月份 + 'd+': this.getDate(), //日 + 'h+': this.getHours(), //小时 + 'm+': this.getMinutes(), //分 + 's+': this.getSeconds(), //秒 + 'q+': Math.floor((this.getMonth() + 3) / 3), //季度 + S: this.getMilliseconds() //毫秒 + } + if (/(y+)/.test(fmt)) { + fmt = fmt.replace( + RegExp.$1, + (this.getFullYear() + '').substr(4 - RegExp.$1.length) + ) + } + for (var k in o) { + if (new RegExp('(' + k + ')').test(fmt)) { + fmt = fmt.replace( + RegExp.$1, + RegExp.$1.length == 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length) + ) + } + } + return fmt +} \ No newline at end of file diff --git a/web/src/style/layout-main.scss b/web/src/style/layout-main.scss index 3eb82ea..e6479f6 100644 --- a/web/src/style/layout-main.scss +++ b/web/src/style/layout-main.scss @@ -1,464 +1,497 @@ -// 页面主体样式 -.main { - position: absolute; - display: flex; - flex-direction: column; - width: calc(100% - 30px); - height: calc(100% - 30px); - border-radius: 4px; - background: url('~@/assets/main-bg.png') no-repeat; - background-size: 100% 100%; - // background: linear-gradient(170deg, rgba(14, 54, 120, .3) 20%, #0E3678 35%, rgba(14, 54, 120, .3) 55%); - box-shadow: 0 2px 12px 0 rgba(57, 181, 254, 0.6); - - // - .main_tabs { - padding: 20px 0 10px; - - /deep/ .el-tabs__nav-wrap { - padding: 0 20px; - font-size: 20px; - - &::after { - height: 1px; - background-color: #1B428F; - } - } - - /deep/ .el-tabs__active-bar { - background-color: #39B5FE; - } - } - - // 主体头部样式 - .main_header { - display: flex; - margin: 9px 24px 24px; - font-size: 1rem; - flex-wrap: wrap; - - .header_item { - display: flex; - align-items: center; - margin-right: 24px; - margin-top: 15px; - } - - .header_label { - color: #EAEAEA; - white-space: nowrap; - text-align: right; - // min-width: 100px; - } - } - - // - .main_functional { - display: flex; - margin: 10px 10px 20px; - } - - // 主体内容样式 - .main_content { - position: relative; - flex: 1; - margin: 0 24px; - overflow: hidden; - } - - // 主体底部样式 - .main_footer { - height: 50px; - margin: 10px; - text-align: right; - margin-top: 35px; - ::v-deep.el-input__inner{ - border:1px solid #294366; - background-color: #102448; - } - ::v-deep .el-pagination__jump{ - color: #bfbfbf; - } - } - - ::v-deep .search_btn { - border: 0 none; - color: #fff; - background: url('~@/assets/search_bg.png') no-repeat; - background-size: 100% 100%; - &:hover { - color: #F69C42; - } - } - - ::v-deep .table_btn { - border: 0 none; - color: #39B5FE; - background: url('~@/assets/table_btn.png') no-repeat; - background-size: 100% 100%; - &:hover { - color: rgba($color: #39B5FE, $alpha: .7); - } - } - - ::v-deep .delete_btn { - border: 0 none; - color: #F94550; - background: url('~@/assets/error_btn.png') no-repeat; - background-size: 100% 100%; - &:hover { - color: rgba($color: #F94550, $alpha: .7); - } - } - - ::v-deep .el-tabs__item { - color: #fff; - } - ::v-deep .el-loading-mask { - background-color: rgba($color: #132a5c, $alpha: .8); - } - ::v-deep .el-tabs__item.is-active { - color: #39B5FE; - } - ::v-deep .el-tabs__active-bar { - background-color: #39B5FE; - } - ::v-deep .el-tabs__nav-wrap::after { - background-color: #1c50ae; - } - ::v-deep .el-table { - background: transparent - } - ::v-deep .el-input__inner { - border: 1px solid #39B5FE; - background-color: transparent; - color: #fff; - } - ::v-deep .el-textarea__inner { - color: #fff; - background-color: transparent; - border: 1px solid #39B5FE; - } - ::v-deep .el-dialog__body .el-input__inner { - background-color: transparent; - border: 1px solid rgba(57, 181, 254, 1); - color: #fff; - } - ::v-deep .el-table tr { - background: transparent; - } - ::v-deep .el-table__expand-icon { - color: #22fffe; - } - ::v-deep .el-table--enable-row-hover .el-table__body tr:hover > td.el-table__cell { - background: rgba($color: #34AFED, $alpha: .5); - } - ::v-deep .el-table .warning-row { - background: transparent; - } - ::v-deep .el-table .success-row { - background: #031a46; - } - ::v-deep .el-table__body .el-table__row.hover-row td { - background-color: #0d3271; - } - ::v-deep .el-date-editor .el-range-input { - background-color: #0b2f66; - color: #fff; - } - ::v-deep .el-table tr { - &:nth-of-type(even) { - background: linear-gradient( - 90deg, - transparent 3%, - rgba(57, 166, 254, 0.4) 55%, - transparent 95%); - } - } - ::v-deep .el-date-editor .el-range-separator { - // color: #606266; - color: #fff; - } - ::v-deep .el-upload--picture-card { - background-color: #0b2c64; - border-color: #39B5FE; - } - ::v-deep .el-radio { - padding-bottom: 16px; - color: #fff; - } - ::v-deep .el-radio__inner { - border-color: #39B5FE; - background-color: #061c48; - } - ::v-deep .el-input-group__append, - .el-input-group__prepend { - background-color: #0B2559; - border-color: #39B5FE; - color: #fff; - padding: 0 10px; - } - /deep/.el-date-editor .el-range-input{ - background-color: transparent; - } -} - -// 列表按钮侧边竖线 -.line { - position: relative; - - &::before { - content: ""; - position: absolute; - top: 50%; - right: -6px; - transform: translateY(-50%); - width: 1px; - height: 40%; - background-color: #eaeaea; - } -} -.preview_dialog{ - ::v-deep .el-dialog__headerbtn{ - font-size: 24px!important; - } -} - -.scan_dialog{ - ::v-deep .el-dialog{ - min-width: 200px!important; - margin-top: 30vh!important; - } - ::v-deep .el-dialog__header{ - padding-bottom: 10px!important; - } - ::v-deep .el-dialog__body{ - padding: 10px 10px 26px!important; - } -} -// 弹出框标题蓝线 -.prop_dialog { - ::v-deep .el-dialog__header { - position: relative; - padding: 20px 20px 20px 24px; - } - - ::v-deep .el-dialog__headerbtn .el-dialog__close { - color: #BFBFBF; - } - - ::v-deep .el-dialog__body { - padding: 30px 10px 10px 24px; - } - ::v-deep .el-dialog__title { - color: #39B5FE; - font-weight: 600; - } - ::v-deep .el-dialog__footer { - padding: 20px 20px 20px; - } - .submit_btn { - border: 0 none; - color: #39B5FE; - background: url('~@/assets/submit_bg.png') no-repeat; - background-size: 100% 100%; - &:hover { - color: #39B5FE; - } - } -} - -// form表单样式 -.rule_form { - padding-right: 15px; - max-height: 550px; - overflow-y: auto; - - ::v-deep .el-select { - width: 100%; - } - ::v-deep .el-date-editor.el-input, - .el-date-editor.el-input__inner { - width: 100%; - color: #fff; - background-color: #102351; - } - ::v-deep.el-form-item__label-wrap .el-form-item__label { - color: #fff; - } - .text_ranage { - width: 210px; - padding: 0px 15px; - margin-left: 15px; - background-color: #102351; - color: #fff; - } - ::v-deep .el-form-item__label { - color: #fff; - } - ::v-deep .el-form-item__content { - display: flex; - flex-direction: row; - .marks_text { - margin-left: 20px; - padding: 0px 10px; - width: 18rem; - border: 1px solid #deeaff; - background-color: #deeaff; - - .el-icon-time:before { - color: #2c77ff; - padding-right: 10px; - } - } - .el-switch { - padding-top: 15px; - } - } - ::v-deep .el-textarea__inner { - font-family: Avenir, Helvetica, Arial, sans-serif; - background-color: #102351; - } - - ::v-deep .el-cascader { - width: 100%; - } - ::v-deep .el-input-number { - width: 100%; - } - ::v-deep .el-input-number .el-input__inner { - text-align: left; - background-color: #102351; - } -} - -.tabs_main { - margin: 0; - width: 100%; - height: 100%; - box-shadow: none; -} - -/deep/ .el-textarea__inner{ - background-color: transparent; - border: 1px solid #39B5FE; -} -.inspection_content { - display: flex; - flex-direction: column; - padding: 0 20px; - height: 500px; - overflow: auto; - - .inspection_content_item { - display: flex; - flex-direction: column; - margin-bottom: 20px; - width: 100%; - color: #E1E3E9; - - .inspection_content_item--header { - position: relative; - display: flex; - justify-content: space-between; - padding: 0 0 5px 15px; - border-bottom: 1px solid #39B5FE; - - &::after { - content: ""; - position: absolute; - top: 50%; - left: 0; - transform: translateY(-50%); - width: 3px; - height: 70%; - background: #19F7F7; - } - - .label { - color: #19F7F7; - } - } - - .inspection_content_item--content { - display: flex; - flex-direction: column; - padding: 10px 0; - - .info_image { - margin: 10px 0; - width: 150px; - height: 140px; - border: 1px solid #39B5FE; - border-radius: 6px; - cursor: pointer; - } - } - } -} - -.prop_image { - padding: 0 15px 20px 0; - width: 100%; - height: 450px; - background: transparent; -} -//预览按钮样式 -.preview_content{ - padding: 24px 20px; - box-shadow: 2px 2px 12px 0 rgba(0, 0, 0, 0.06); - .preview_titles{ - text-align: center; - font-size: 28px; - font-weight: 600; - color: #39B5FE; - } - .preview_times{ - text-align: center; - font-size: 14px; - color: #999999; - margin-top: 15px; - padding-bottom: 15px; - border-bottom: 1px dashed #eaeaea; - } - .preview_main{ - padding: 24px 20px 0px; - overflow: auto; - } -} - -/deep/.el-dialog.is-fullscreen{ - overflow-x: hidden; - overflow-y: auto; - width: 64%; - height: 92%; - margin-top: 40px; - background: #f6f6f6!important; -} - -/deep/.el-input-number__decrease{ - border-right-color: #39B5FE; - .el-icon-minus:before{ - color: #19F6F8; - } -} - -/deep/.el-input-number__increase{ - border-left-color: #39B5FE; - .el-icon-plus:before{ - color: #19F6F8; - } -} - -/deep/.el-input-number__increase{ - background-color: transparent; -} - -/deep/.el-input-number__decrease{ - background-color: transparent; -} - -/deep/.el-textarea.is-disabled .el-textarea__inner{ - background-color: #0D2657; - border-color: #39B5FE; +// 页面主体样式 +.main { + position: absolute; + display: flex; + flex-direction: column; + width: calc(100% - 30px); + height: calc(100% - 30px); + border-radius: 4px; + background: url('~@/assets/main-bg.png') no-repeat; + background-size: 100% 100%; + // background: linear-gradient(170deg, rgba(14, 54, 120, .3) 20%, #0E3678 35%, rgba(14, 54, 120, .3) 55%); + box-shadow: 0 2px 12px 0 rgba(57, 181, 254, 0.6); + + // + .main_tabs { + padding: 20px 0 10px; + + /deep/ .el-tabs__nav-wrap { + padding: 0 20px; + font-size: 20px; + + &::after { + height: 1px; + background-color: #1B428F; + } + } + + /deep/ .el-tabs__active-bar { + background-color: #39B5FE; + } + } + + // 主体头部样式 + .main_header { + display: flex; + margin: 9px 24px 24px; + font-size: 1rem; + flex-wrap: wrap; + + .header_item { + display: flex; + align-items: center; + margin-right: 24px; + margin-top: 15px; + } + + .header_label { + color: #EAEAEA; + white-space: nowrap; + text-align: right; + // min-width: 100px; + } + } + + // + .main_functional { + display: flex; + margin: 10px 10px 20px; + } + + // 主体内容样式 + .main_content { + position: relative; + flex: 1; + margin: 0 24px; + overflow: hidden; + } + + // 主体底部样式 + .main_footer { + height: 50px; + margin: 10px; + text-align: right; + margin-top: 35px; + ::v-deep.el-input__inner{ + border:1px solid #294366; + background-color: #102448; + } + ::v-deep .el-pagination__jump{ + color: #bfbfbf; + } + } + + ::v-deep .search_btn { + border: 0 none; + color: #fff; + background: url('~@/assets/search_bg.png') no-repeat; + background-size: 100% 100%; + &:hover { + color: #F69C42; + } + } + + ::v-deep .table_btn { + border: 0 none; + color: #39B5FE; + background: url('~@/assets/table_btn.png') no-repeat; + background-size: 100% 100%; + &:hover { + color: rgba($color: #39B5FE, $alpha: .7); + } + } + + ::v-deep .delete_btn { + border: 0 none; + color: #F94550; + background: url('~@/assets/error_btn.png') no-repeat; + background-size: 100% 100%; + &:hover { + color: rgba($color: #F94550, $alpha: .7); + } + } + + ::v-deep .el-checkbox__inner { + border: 1px solid #3DC8FF; + background-color: transparent; + &::after{ + border: 1px solid #3DC8FF; + border-left: 0; + border-top: 0; + } + } + ::v-deep .el-tabs__item { + color: #fff; + } + ::v-deep .el-loading-mask { + background-color: rgba($color: #132a5c, $alpha: .8); + } + ::v-deep .el-tabs__item.is-active { + color: #39B5FE; + } + ::v-deep .el-tabs__active-bar { + background-color: #39B5FE; + } + ::v-deep .el-tabs__nav-wrap::after { + background-color: #1c50ae; + } + ::v-deep .el-table { + background: transparent + } + ::v-deep .el-input__inner { + border: 1px solid #39B5FE; + background-color: transparent; + color: #fff; + } + ::v-deep .el-textarea__inner { + color: #fff; + background-color: transparent; + border: 1px solid #39B5FE; + } + ::v-deep .el-dialog__body .el-input__inner { + background-color: transparent; + border: 1px solid rgba(57, 181, 254, 1); + color: #fff; + } + ::v-deep .el-table tr { + background: transparent; + } + ::v-deep .el-table__expand-icon { + color: #22fffe; + } + ::v-deep .el-table--enable-row-hover .el-table__body tr:hover > td.el-table__cell { + background: rgba($color: #34AFED, $alpha: .5); + } + ::v-deep .el-table .warning-row { + background: transparent; + } + ::v-deep .el-table .success-row { + background: #031a46; + } + ::v-deep .el-table__body .el-table__row.hover-row td { + background-color: #0d3271; + } + ::v-deep .el-date-editor .el-range-input { + background-color: #0b2f66; + color: #fff; + } + ::v-deep .el-table tr { + &:nth-of-type(even) { + background: linear-gradient( + 90deg, + transparent 3%, + rgba(57, 166, 254, 0.4) 55%, + transparent 95%); + } + } + ::v-deep .el-date-editor .el-range-separator { + // color: #606266; + color: #fff; + } + ::v-deep .el-upload--picture-card { + background-color: #0b2c64; + border-color: #39B5FE; + } + ::v-deep .el-radio { + padding-bottom: 16px; + color: #fff; + } + ::v-deep .el-radio__inner { + border-color: #39B5FE; + background-color: #061c48; + } + ::v-deep .el-input-group__append, + .el-input-group__prepend { + background-color: #0B2559; + border-color: #39B5FE; + color: #fff; + padding: 0 10px; + } + /deep/.el-date-editor .el-range-input{ + background-color: transparent; + } +} + +// 列表按钮侧边竖线 +.line { + position: relative; + + &::before { + content: ""; + position: absolute; + top: 50%; + right: -6px; + transform: translateY(-50%); + width: 1px; + height: 40%; + background-color: #eaeaea; + } +} +.preview_dialog{ + ::v-deep .el-dialog__headerbtn{ + font-size: 24px!important; + } +} + +.scan_dialog{ + ::v-deep .el-dialog{ + min-width: 200px!important; + margin-top: 30vh!important; + } + ::v-deep .el-dialog__header{ + padding-bottom: 10px!important; + } + ::v-deep .el-dialog__body{ + padding: 10px 10px 26px!important; + } +} +// 弹出框标题蓝线 +.prop_dialog { + ::v-deep .el-dialog__header { + position: relative; + padding: 20px 20px 20px 24px; + } + + ::v-deep .el-dialog__headerbtn .el-dialog__close { + color: #BFBFBF; + } + + ::v-deep .el-dialog__body { + padding: 30px 10px 10px 24px; + } + ::v-deep .el-dialog__title { + color: #39B5FE; + font-weight: 600; + } + ::v-deep .el-dialog__footer { + padding: 20px 20px 20px; + } + .submit_btn { + border: 0 none; + color: #39B5FE; + background: url('~@/assets/submit_bg.png') no-repeat; + background-size: 100% 100%; + &:hover { + color: #39B5FE; + } + } +} + +// form表单样式 +.rule_form { + padding-right: 15px; + max-height: 550px; + overflow-y: auto; + + ::v-deep .el-select { + width: 100%; + } + ::v-deep .el-date-editor.el-input, + .el-date-editor.el-input__inner { + width: 100%; + color: #fff; + background-color: #102351; + } + ::v-deep.el-form-item__label-wrap .el-form-item__label { + color: #fff; + } + .text_ranage { + width: 210px; + padding: 0px 15px; + margin-left: 15px; + background-color: #102351; + color: #fff; + } + ::v-deep .el-form-item__label { + color: #fff; + } + ::v-deep .el-form-item__content { + display: flex; + flex-direction: row; + .marks_text { + margin-left: 20px; + padding: 0px 10px; + width: 18rem; + border: 1px solid #deeaff; + background-color: #deeaff; + + .el-icon-time:before { + color: #2c77ff; + padding-right: 10px; + } + } + .el-switch { + padding-top: 15px; + } + } + ::v-deep .el-textarea__inner { + font-family: Avenir, Helvetica, Arial, sans-serif; + background-color: #102351; + } + + ::v-deep .el-cascader { + width: 100%; + } + ::v-deep .el-input-number { + width: 100%; + } + ::v-deep .el-input-number .el-input__inner { + text-align: left; + background-color: #102351; + } +} + +.tabs_main { + margin: 0; + width: 100%; + height: 100%; + box-shadow: none; +} + +/deep/ .el-textarea__inner{ + background-color: transparent; + border: 1px solid #39B5FE; +} +.inspection_content { + display: flex; + flex-direction: column; + padding: 0 20px; + height: 500px; + overflow: auto; + + .inspection_content_item { + display: flex; + flex-direction: column; + margin-bottom: 20px; + width: 100%; + color: #E1E3E9; + + .inspection_content_item--header { + position: relative; + display: flex; + justify-content: space-between; + padding: 0 0 5px 15px; + border-bottom: 1px solid #39B5FE; + + &::after { + content: ""; + position: absolute; + top: 50%; + left: 0; + transform: translateY(-50%); + width: 3px; + height: 70%; + background: #19F7F7; + } + + .label { + color: #19F7F7; + } + } + + .inspection_content_item--content { + display: flex; + flex-direction: column; + padding: 10px 0; + + .info_image { + margin: 10px 0; + width: 150px; + height: 140px; + border: 1px solid #39B5FE; + border-radius: 6px; + cursor: pointer; + } + } + } +} + +.prop_image { + padding: 0 15px 20px 0; + width: 100%; + height: 450px; + background: transparent; +} +//预览按钮样式 +.preview_content{ + padding: 24px 20px; + box-shadow: 2px 2px 12px 0 rgba(0, 0, 0, 0.06); + .preview_titles{ + text-align: center; + font-size: 28px; + font-weight: 600; + color: #39B5FE; + } + .preview_times{ + text-align: center; + font-size: 14px; + color: #999999; + margin-top: 15px; + padding-bottom: 15px; + border-bottom: 1px dashed #eaeaea; + } + .preview_main{ + padding: 24px 20px 0px; + overflow: auto; + } +} + +/deep/.el-dialog.is-fullscreen{ + overflow-x: hidden; + overflow-y: auto; + width: 64%; + height: 92%; + margin-top: 40px; + background: #f6f6f6!important; +} + +/deep/.el-input-number__decrease{ + border-right-color: #39B5FE; + .el-icon-minus:before{ + color: #19F6F8; + } +} + +/deep/.el-input-number__increase{ + border-left-color: #39B5FE; + .el-icon-plus:before{ + color: #19F6F8; + } +} + +/deep/.el-input-number__increase{ + background-color: transparent; +} + +/deep/.el-input-number__decrease{ + background-color: transparent; +} + +/deep/.el-textarea.is-disabled .el-textarea__inner{ + background-color: #0D2657; + border-color: #39B5FE; +} + +/* 滚动条统一样式修改 */ +/deep/ { + &::-webkit-scrollbar { + width: 8px; + height: 8px; + } + + &::-webkit-scrollbar-corner { + background-color: transparent; + } + + &::-webkit-scrollbar-thumb { + border-radius: 10px; + box-shadow: inset 0 0 5px transparent; + background: #39B5FE; + } + + &::-webkit-scrollbar-track { + box-shadow: inset 0 0 5px transparent; + border-radius: 10px; + background: rgba(76, 188, 254, .3); + } } \ No newline at end of file diff --git a/web/src/views/ProjectManage/SectionManage.vue b/web/src/views/ProjectManage/SectionManage.vue index 09e277f..dbae05b 100644 --- a/web/src/views/ProjectManage/SectionManage.vue +++ b/web/src/views/ProjectManage/SectionManage.vue @@ -1,584 +1,637 @@ -<template> - <!-- 工程项目管理 ==> 单位工程管理--> - <div class="main"> - <div class="main_header"> - <div class="header_item"> - <span class="header_label">标段名称:</span> - <el-input v-model="queryInfo.segmentName" clearable placeholder="请输入标段名称"></el-input> - </div> - <div class="header_item"> - <span class="header_label">项目名称:</span> - <el-select v-model="queryInfo.proId" placeholder="请选择项目名称" clearable> - <el-option v-for="item in optionsProject" :key="item.proId" :label="item.proName" :value="item.proId"> - </el-option> - </el-select> - </div> - <div class="header_item"> - <el-button icon="el-icon-search" v-permission="'search'" @click="queryReset">查询</el-button> - <el-button class="search_btn" icon="el-icon-plus" v-permission="'insert'" @click="addRow">新增</el-button> - </div> - </div> - <div class="main_content"> - <cpnTable :table-index="true" :table-data="dataList" :table-columns="tableColumns" :page-total="total" - :page-num.sync="queryInfo.pageNum" :page-size.sync="queryInfo.pageSize" :page-change="pageChange"> - - <template #finished="{ row }"> - <div style="cursor: pointer;" @click="showDetail(row)">{{ row.completedQuantity }}</div> - </template> - </cpnTable> - </div> - <!-- dialog --> - <el-dialog class="prop_dialog" v-if="isRender" :title="dialogTitle" :visible.sync="asyncVisible" width="660px" - @close="closeForm"> - <el-form ref="ruleForm" :model="ruleForm" :rules="rules" label-width="auto" class="rule_form"> - <el-form-item label="项目名称:" prop="proId"> - <el-select v-model="ruleForm.proId" placeholder="请选择" @change="changeNeed"> - <el-option v-for="item in optionsProject" :key="item.proId" :label="item.proName" :value="item.proId"> - </el-option> - </el-select> - </el-form-item> - <el-form-item label="起讫里程:" prop="mileage"> - <el-input v-model="ruleForm.mileage" clearable placeholder="请输入起讫里程"></el-input> - </el-form-item> - <el-form-item label="单位工程名称:" prop="unitProjectName"> - <el-input v-model="ruleForm.unitProjectName" clearable placeholder="请输入单位工程名称"></el-input> - </el-form-item> - <el-form-item label="需求负责人:" prop="segmentAdmin"> - <el-select v-model="ruleForm.segmentAdmin" placeholder="请选择求负责人"> - <el-option v-for="item in optionsUser" :key="item.userId" :label="item.realName" :value="item.userId"> - </el-option> - </el-select> - </el-form-item> - <el-form-item label="盾构单位:" prop="shieldEnp"> - <el-input v-model="ruleForm.shieldEnp" clearable placeholder="请输入盾构单位"></el-input> - </el-form-item> - <el-form-item label="开始时间:" prop="startTime"> - <el-date-picker v-model="ruleForm.startTime" value-format="yyyy-MM-dd" placeholder="请选择开始日期"></el-date-picker> - </el-form-item> - <el-form-item label="结束时间:" prop="endTime"> - <el-date-picker v-model="ruleForm.endTime" value-format="yyyy-MM-dd" placeholder="请选择结束日期"></el-date-picker> - </el-form-item> - <el-form-item label="工期:" prop="duration"> - <el-input v-model="ruleForm.duration" clearable placeholder="请输入工期"></el-input> - </el-form-item> - <el-form-item label="站点:" prop="station"> - <el-input v-model="ruleForm.station" clearable placeholder="请输入站点"></el-input> - </el-form-item> - <div class="section_needs">标段需求</div> - <div class="section_needs_content"> - <div v-for="item in ruleForm.segmentList" :key="item.dictId" class="needs_items"> - <div class="needs_text">{{ item.dictName }}</div> - <el-input placeholder="请输入数量" type="number" v-model="item.needNum" class="needs_num"> - <template slot="append">块</template> - </el-input> - </div> - </div> - </el-form> - <div slot="footer"> - <el-button @click="asyncVisible = false">取 消</el-button> - <el-button class="submit_btn" @click="onSubmit('ruleForm')">提 交</el-button> - </div> - </el-dialog> - <!-- detail dialog --> - <el-dialog class="prop_dialog" v-if="detail.isRenderDetail" title="管片数量" :visible.sync="detail.asyncVisible" - width="1000px"> - <el-card> - <div class="titles" v-for="item in detail.infoMap" :key="item.key"> - {{ item.name }} : - <template v-if="item.key === 'waterproofType'"> - <span>{{ detail.infos[item.key] === 0 ? '有' : '无' }} </span> - </template> - <template v-else-if="item.key === 'proHas'"> - <span v-for="val in detail.infos[item.key]" :key="val.hasSteel"> - {{ val.dictName }} - </span> - </template> - <template v-else> - <span>{{ detail.infos[item.key] }}</span> - </template> - </div> - </el-card> - <cpnTable :table-index="true" :table-data="detail.tableData" :table-columns="detail.tableColumns" - :page-total="detail.total" :page-num.sync="detail.pageNum" :page-size.sync="detail.pageSize" - :page-change="detailPageChange"> - </cpnTable> - </el-dialog> - </div> -</template> - -<script> -import {throttle} from '../../plugins/public'; // 导入节流、动态切换组件尺寸方法 -import cpnTable from '@/components/element/Table' -export default { - data() { - return { - isRender: false, - loading: false, - asyncVisible: false, - submitMode: '', // add update - total: 0, - queryInfo: { - pageNum: 1, - pageSize: 10, - segmentName: '', - proId: '', - }, - dataList: [], - tableColumns: [], - optionsUser: [], //需求负责人 - optionsProject: [], // 项目名称 - ruleForm: {}, // 按钮表单 - rules: { - proId: [{required: true, message: '请选择', trigger: 'change'}], - mileage: [{required: true, message: '请输入', trigger: 'blur'}], - unitProjectName: [{required: true, message: '请输入', trigger: 'blur'}], - segmentAdmin: [{required: true, message: '请选择', trigger: 'change'}], - shieldEnp: [{required: true, message: '请输入', trigger: 'blur'}], - startTime: [{required: true, message: '请选择', trigger: 'change'}], - endTime: [{required: true, message: '请选择', trigger: 'change'}], - duration: [{required: true, message: '请输入', trigger: 'blur'}], - station: [{required: true, message: '请输入', trigger: 'blur'}], - }, - detail: { - rowId: '', - isRenderDetail: false, - asyncVisible: false, - pageNum: 1, - pageSize: 10, - total: 0, - tableData: [], - tableColumns: [], - infos: [], - infoMap: [ - { - name: '项目名称', - key: 'proName', - }, - { - name: '单位工程名称', - key: 'createUnit', - }, - { - name: '外径', - key: 'outsideDiameter', - }, - { - name: '內径', - key: 'innerDiameter', - }, - { - name: '厚度', - key: 'thickness', - }, - { - name: '环宽', - key: 'ringWidth', - }, - { - name: '混凝土强度等级', - key: 'concreteStrengthGrade', - }, - { - name: '抗渗等级', - key: 'impermeabilityLevel', - }, - { - name: '配筋型号', - key: 'proHas', - }, - { - name: '有无外弧面防水', - key: 'waterproofType', - } - ] - } - } - }, - components: { - cpnTable - }, - computed: { - isUpdate() { - return this.submitMode === 'update' - }, - dialogTitle() { - return this.isUpdate ? '修改新增单位工程' : '新增单位工程' - }, - }, - created() { - this.setFormProps() - this.setTableColumn() - this.getLists() - this.getAllProjects() - this.getAllPersons() - // this.getAllBlocks() // 暂时没用,先屏蔽 - }, - methods: { - // 获取table列表数据 - getLists() { - this.loading = true - let params = this.queryInfo - this.$api.Engineer.searchSegment(params).then(res => { - if (res.statusMsg === 'ok') { - this.total = res.data.total - this.dataList = res.data.list - } - this.loading = false - }) - }, - //获得所有人员 - getAllPersons() { - this.$api.Engineer.getPersonsList({}).then(res => { - if (res.statusMsg === 'ok') { - this.optionsUser = res.data - } else { - this.$message.warning('人员名称获取失败') - } - }) - }, - //获得所有项目名称 - getAllProjects() { - let obj = { - pageNum: 1, - pageSize: 100000000 - } - this.$api.Engineer.searchProjects(obj).then(res => { - if (res.statusMsg === 'ok') { - this.optionsProject = res.data.list - } else { - this.$message.warning('项目名称获取失败') - } - }) - }, - //字典里获取所有标段块号 - getAllBlocks() { - let params = { - pageNum: 1, - pageSize: 100000000 - } - this.$api.Dictionary.searchDictionary(params).then(res => { - if (res.statusMsg === 'ok') { - const segmentList = [] - res.data.list.forEach(item => { - if (item.dictType === '5') { - segmentList.push({ - dictName: item.dictName, - needType: item.dictId, - needNum: 0, - segmentId: '' - }) - } - }) - this.ruleForm.segmentList = segmentList - } else { - this.$message.warning('标段获取失败') - } - }) - }, - // 已完成 table信息 - getDetailLists() { - let detailData = this.detail - this.$api.DuctpiecePLM.searchDuctpiecePLMList({ - segmentId: detailData.rowId, - pageNum: detailData.pageNum, - pageSize: detailData.pageSize - }).then(res => { - if (res.success) { - detailData.total = res.data.total - detailData.tableData = res.data.list - } - }) - }, - // 已完成 title信息 - getDetailInfos(id) { - this.$api.Engineer.detailsProjects({proId: id}).then(res => { - if (res.success) { - this.detail.infos = res.data - } - }) - }, - // 初始化 ruleform - setFormProps(options = {}) { - let _form = { - proId: '', // 项目名称id - mileage: '', // 起讫里程 - unitProjectName: '', // 单位工程名称 - segmentAdmin: '', // 需求负责人 - shieldEnp: '', // 盾构单位 - startTime: null, // 开始时间 - endTime: null, // 结束时间 - duration: '', // 工期 - station: '', // 站点 - segmentList: [], // 标段需求 - } - this.ruleForm = Object.keys(options).length ? options : _form - }, - // 初始化 table 配置 - setTableColumn() { - this.tableColumns = [ - {index: true}, - {slot: "finished", name: "已完成(块)"}, - {name: "项目名称", key: "proName", width: 160}, - {name: "起讫里程", key: "mileage"}, - {name: "单位工程名称", key: "unitProjectName"}, - {name: "盾构单位", key: "shieldEnp"}, - {name: "开始时间", key: "startTime", width: 100}, - {name: "结束时间", key: "endTime", width: 100}, - {name: "工期", key: "duration"}, - {name: "站点", key: "station"}, - {name: "负责人", key: "realName"}, - { - operation: true, name: "操作", width: 140, value: [ - {name: "修改", class: "table_btn", permission: "update", handleRow: this.updateRow}, - {name: "删除", class: "delete_btn", permission: "delete", handleRow: this.deleteRow}, - ] - }, - ] - this.detail.tableColumns = [ - {index: true}, - {name: "环号", key: "ringNum"}, - {name: "管片编号", key: "pipeNum", width: 140}, - {name: "转向", key: "turnName", width: 106}, - {name: "配筋", key: "reinforcementName"}, - {name: "注浆孔", key: "groutingHolesName"}, - {name: "块号", key: "blockNumName"}, - {name: "模具", key: "mouldNum"}, - {name: "入模时间", key: "intoModTime", width: 136}, - {name: "浇筑时间", key: "pouringTime"}, - {name: "质检时间", key: "checkTime", width: 136}, - {name: "生产班组", key: "groupName"}, - {name: "项目", key: "proName", width: 240}, - {name: "质量标注", key: "checkResultStr"}, - ] - }, - // 重置表单 - resetForm(formName) { - this.$refs[formName].resetFields() - }, - // 显示表单 - showForm() { - !this.isRender && (this.isRender = true) - this.asyncVisible = true - }, - // 隐藏表单 - closeForm() { - this.asyncVisible = false - this.resetForm('ruleForm') - this.setFormProps() - }, - // 查询按钮列表信息 - queryReset() { - this.queryInfo.pageNum = 1 - this.queryInfo.pageSize = 10 - this.getLists() - }, - // 添加数据 - addRow() { - this.submitMode = 'add' - this.showForm() - }, - // 更新数据 - async updateRow(row) { - this.submitMode = 'update' - this.showForm() - const segmentList = await this.getProjectBlocks(row.segmentId) - Object.keys(this.ruleForm).forEach(item => { - if (row.hasOwnProperty.call(row, item)) { - this.ruleForm[item] = row[item] - } - }) - this.ruleForm.segmentId = row.segmentId - this.ruleForm.segmentList = segmentList - }, - // 删除数据 - deleteRow(row) { - this.$confirm("该操作将删除该信息,是否继续删除?", "提示", { - confirmButtonText: "确定", - cancelButtonText: "取消", - type: "warning" - }).then(() => { - this.$api.Engineer.deleteSegment({segmentId: row.segmentId}).then(res => { - if (res.statusMsg === 'ok') { - this.queryReset(); - this.$message.success("删除成功!") - } else { - this.$message.warning(res.statusMsg) - } - }) - }).catch(() => { - this.$message.warning("您已取消") - }) - }, - //获取项目标段 - getProjectBlocks(id) { - return new Promise(resolve => { - this.$api.Engineer.detailsSegment({segmentId: id}).then(res => { - let outData = [] - if (res.statusMsg === 'ok') { - outData.push(...res.data.segmentNeeds) - } - resolve(outData) - }) - }) - }, - //通过不同的项目展示不同的标段需求 - changeNeed(val) { - this.$api.Reinforce.searchProjectBears({proId: val}).then(res => { - if (res.statusMsg === 'ok') { - const segmentList = [] - res.data.blokDtos.forEach(item => { - segmentList.push({ - dictName: item.blockName, - needType: item.blockNum, - needNum: 0, - segmentId: '' - }) - }) - this.ruleForm.segmentList = segmentList - } else { - this.$message.warning('标段获取失败') - } - }) - }, - // 提交表单 - onSubmit: throttle(function () { - this.$refs.ruleForm.validate(valid => { - if (!valid) return - const params = this.ruleForm - if (this.isUpdate) { - // 更新 - this.$api.Engineer.updateSegment(params).then((res) => { - if (res.statusMsg === 'ok') { - this.closeForm() - this.getLists() - this.$message.success('更新成功!') - } else { - this.$message.warning(res.statusMsg) - } - }) - } else { - // 添加 - this.$api.Engineer.insertSegment(params).then((res) => { - if (res.statusMsg === 'ok') { - this.closeForm() - this.getLists() - this.$message.success('添加成功!') - } else { - this.$message.warning(res.statusMsg) - } - }) - } - }) - }, 1000), - showDetail(row) { - let detailData = this.detail - !detailData.isRenderDetail && (detailData.isRenderDetail = true) - detailData.asyncVisible = true - detailData.rowId = row.segmentId - detailData.total = 0 - detailData.tableData = [] - detailData.infos = [] - this.getDetailInfos(row.proId) - this.getDetailLists() - }, - // 分页改变 - pageChange() { - this.getLists(); - }, - // 已完成の分页改变 - detailPageChange() { - this.getDetailLists(); - }, - } -} -</script> - -<style lang="scss" scoped> -@import '../../style/layout-main.scss'; - -/deep/ { - &::-webkit-scrollbar { - width: 8px; - height: 8px; - } - - &::-webkit-scrollbar-corner { - background-color: transparent; - } - - &::-webkit-scrollbar-thumb { - border-radius: 10px; - box-shadow: inset 0 0 5px transparent; - background: #39B5FE; - } - - &::-webkit-scrollbar-track { - box-shadow: inset 0 0 5px transparent; - border-radius: 10px; - background: rgba(76, 188, 254, .3); - } -} - -.el-card { - margin-bottom: 20px; - border: none; - color: #fff; - background: rgba(9, 64, 101, 1); - - .titles { - float: left; - width: 25%; - min-height: 36px; - padding-right: 6px; - margin-bottom: 6px; - line-height: 18px; - box-sizing: border-box; - } - - span { - color: #39B5FE; - } -} - -.section_needs { - position: relative; - color: #18F5F7; - padding: 20px 20px 10px 15px; - border-bottom: 1px solid #1949A3; - - &::before { - position: absolute; - content: ""; - width: 2px; - height: 20px; - background-color: #18F5F7; - top: 20px; - left: 5px; - } -} - -.section_needs_content { - display: flex; - flex-wrap: wrap; - - .needs_items { - max-width: 190px; - min-width: 142px; - padding: 15px; - display: flex; - - .needs_text { - // width: 50px; - flex: none; - align-self: center; - text-align: center; - padding-right: 15px; - color: #E1E3E9; - } - - .needs_num { - align-self: center; - } - } -} +<template> + <!-- 工程项目管理 ==> 单位工程管理--> + <div class="main"> + <div class="main_header"> + <div class="header_item"> + <span class="header_label">标段名称:</span> + <el-input v-model="queryInfo.segmentName" clearable placeholder="请输入标段名称"></el-input> + </div> + <div class="header_item"> + <span class="header_label">项目名称:</span> + <el-select v-model="queryInfo.proId" placeholder="请选择项目名称" clearable> + <el-option v-for="item in optionsProject" :key="item.proId" :label="item.proName" :value="item.proId"> + </el-option> + </el-select> + </div> + <div class="header_item"> + <el-button icon="el-icon-search" v-permission="'search'" @click="queryReset">查询</el-button> + <el-button class="search_btn" icon="el-icon-plus" v-permission="'insert'" @click="addRow">新增</el-button> + </div> + </div> + <div class="main_content"> + <cpnTable :table-index="true" :table-loading="loading" :table-data="dataList" :table-columns="tableColumns" + :page-total="total" :page-num.sync="queryInfo.pageNum" :page-size.sync="queryInfo.pageSize" + :page-change="pageChange"> + + <template #finished="{ row }"> + <div style="cursor: pointer;" @click="showDetail(row)">{{ row.completedQuantity }}</div> + </template> + </cpnTable> + </div> + <!-- dialog --> + <el-dialog class="prop_dialog" v-if="isRender" :title="dialogTitle" :visible.sync="asyncVisible" width="660px" + @close="closeForm"> + + <cpnForm :form-config="formConfig"></cpnForm> + + <el-form ref="ruleForm" :model="ruleForm" :rules="rules" label-width="auto" class="rule_form"> + <el-form-item label="项目名称:" prop="proId"> + <el-select v-model="ruleForm.proId" placeholder="请选择" @change="changeNeed"> + <el-option v-for="item in optionsProject" :key="item.proId" :label="item.proName" :value="item.proId"> + </el-option> + </el-select> + </el-form-item> + <el-form-item label="起讫里程:" prop="mileage"> + <el-input v-model="ruleForm.mileage" clearable placeholder="请输入起讫里程"></el-input> + </el-form-item> + <el-form-item label="单位工程名称:" prop="unitProjectName"> + <el-input v-model="ruleForm.unitProjectName" clearable placeholder="请输入单位工程名称"></el-input> + </el-form-item> + <el-form-item label="需求负责人:" prop="segmentAdmin"> + <el-select v-model="ruleForm.segmentAdmin" placeholder="请选择求负责人"> + <el-option v-for="item in optionsUser" :key="item.userId" :label="item.realName" :value="item.userId"> + </el-option> + </el-select> + </el-form-item> + <el-form-item label="盾构单位:" prop="shieldEnp"> + <el-input v-model="ruleForm.shieldEnp" clearable placeholder="请输入盾构单位"></el-input> + </el-form-item> + <el-form-item label="开始时间:" prop="startTime"> + <el-date-picker v-model="ruleForm.startTime" value-format="yyyy-MM-dd" placeholder="请选择开始日期"></el-date-picker> + </el-form-item> + <el-form-item label="结束时间:" prop="endTime"> + <el-date-picker v-model="ruleForm.endTime" value-format="yyyy-MM-dd" placeholder="请选择结束日期"></el-date-picker> + </el-form-item> + <el-form-item label="工期:" prop="duration"> + <el-input v-model="ruleForm.duration" clearable placeholder="请输入工期"></el-input> + </el-form-item> + <el-form-item label="站点:" prop="station"> + <el-input v-model="ruleForm.station" clearable placeholder="请输入站点"></el-input> + </el-form-item> + <div class="section_needs">标段需求</div> + <div class="section_needs_content"> + <div v-for="item in ruleForm.segmentList" :key="item.dictId" class="needs_items"> + <div class="needs_text">{{ item.dictName }}</div> + <el-input placeholder="请输入数量" type="number" v-model="item.needNum" class="needs_num"> + <template slot="append">块</template> + </el-input> + </div> + </div> + </el-form> + <div slot="footer"> + <el-button @click="asyncVisible = false">取 消</el-button> + <el-button class="submit_btn" @click="onSubmit('ruleForm')">提 交</el-button> + </div> + </el-dialog> + <!-- detail dialog --> + <el-dialog class="prop_dialog" v-if="detail.isRenderDetail" title="管片数量" :visible.sync="detail.asyncVisible" + width="1000px"> + <el-card> + <div class="titles" v-for="item in detail.infoMap" :key="item.key"> + {{ item.name }} : + <template v-if="item.key === 'waterproofType'"> + <span>{{ detail.infos[item.key] === 0 ? '有' : '无' }} </span> + </template> + <template v-else-if="item.key === 'proHas'"> + <span v-for="val in detail.infos[item.key]" :key="val.hasSteel"> + {{ val.dictName }} + </span> + </template> + <template v-else> + <span>{{ detail.infos[item.key] }}</span> + </template> + </div> + </el-card> + <cpnTable :table-index="true" :table-data="detail.tableData" :table-columns="detail.tableColumns" + :page-total="detail.total" :page-num.sync="detail.pageNum" :page-size.sync="detail.pageSize" + :page-change="detailPageChange"> + </cpnTable> + </el-dialog> + </div> +</template> + +<script> +import {throttle} from '../../plugins/public'; // 导入节流、动态切换组件尺寸方法 +import cpnTable from '@/components/element/Table' +import cpnForm from '@/components/element/Form' +export default { + data() { + return { + isRender: false, + loading: false, + asyncVisible: false, + submitMode: '', // add update + total: 0, + queryInfo: { + pageNum: 1, + pageSize: 10, + segmentName: '', + proId: '', + }, + dataList: [], + tableColumns: [], + optionsUser: [], //需求负责人 + optionsProject: [], // 项目名称 + ruleForm: {}, // 按钮表单 + rules: { + proId: [{required: true, message: '请选择', trigger: 'change'}], + mileage: [{required: true, message: '请输入', trigger: 'blur'}], + unitProjectName: [{required: true, message: '请输入', trigger: 'blur'}], + segmentAdmin: [{required: true, message: '请选择', trigger: 'change'}], + shieldEnp: [{required: true, message: '请输入', trigger: 'blur'}], + startTime: [{required: true, message: '请选择', trigger: 'change'}], + endTime: [{required: true, message: '请选择', trigger: 'change'}], + duration: [{required: true, message: '请输入', trigger: 'blur'}], + station: [{required: true, message: '请输入', trigger: 'blur'}], + }, + detail: { + rowId: '', + isRenderDetail: false, + asyncVisible: false, + pageNum: 1, + pageSize: 10, + total: 0, + tableData: [], + tableColumns: [], + infos: [], + infoMap: [ + { + name: '项目名称', + key: 'proName', + }, + { + name: '单位工程名称', + key: 'createUnit', + }, + { + name: '外径', + key: 'outsideDiameter', + }, + { + name: '內径', + key: 'innerDiameter', + }, + { + name: '厚度', + key: 'thickness', + }, + { + name: '环宽', + key: 'ringWidth', + }, + { + name: '混凝土强度等级', + key: 'concreteStrengthGrade', + }, + { + name: '抗渗等级', + key: 'impermeabilityLevel', + }, + { + name: '配筋型号', + key: 'proHas', + }, + { + name: '有无外弧面防水', + key: 'waterproofType', + } + ] + }, + + formConfig: { + formModels: { + proId: '', // 项目名称id + mileage: '', // 起讫里程 + unitProjectName: '', // 单位工程名称 + segmentAdmin: '', // 需求负责人 + shieldEnp: '', // 盾构单位 + startTime: null, // 开始时间 + endTime: null, // 结束时间 + duration: '', // 工期 + station: '', // 站点 + segmentList: [], // 标段需求 + }, + formItems: [ + { + type: 'input', label: '用户id' + }, + { + type: 'input', label: '用户名' + }, + + { + type: 'input', label: '真实姓名' + }, + { + type: 'input', label: '电话号码' + }, + { + type: 'select', label: '用户状态', options: [ + {label: '禁用', value: 0}, + {label: '启用', value: 1} + ] + }, + { + type: 'datepicker', label: '创建时间', otherOptions: { + startPlaceholder: '开始时间', + endPlaceholder: '结束时间', + type: 'daterange', + 'unlink-panels': true + } + } + ], + itemColLayout: { + span: 8 + } + } + } + }, + components: { + cpnTable, + cpnForm + }, + computed: { + isUpdate() { + return this.submitMode === 'update' + }, + dialogTitle() { + return this.isUpdate ? '修改新增单位工程' : '新增单位工程' + }, + }, + created() { + this.setFormProps() + this.setTableColumn() + this.getLists() + this.getAllProjects() + this.getAllPersons() + // this.getAllBlocks() // 暂时没用,先屏蔽 + }, + methods: { + // 获取table列表数据 + getLists() { + this.loading = true + let params = this.queryInfo + this.$api.Engineer.searchSegment(params).then(res => { + if (res.statusMsg === 'ok') { + this.total = res.data.total + this.dataList = res.data.list + } + this.loading = false + }) + }, + //获得所有人员 + getAllPersons() { + this.$api.Engineer.getPersonsList({}).then(res => { + if (res.statusMsg === 'ok') { + this.optionsUser = res.data + } else { + this.$message.warning('人员名称获取失败') + } + }) + }, + //获得所有项目名称 + getAllProjects() { + let obj = { + pageNum: 1, + pageSize: 100000000 + } + this.$api.Engineer.searchProjects(obj).then(res => { + if (res.statusMsg === 'ok') { + this.optionsProject = res.data.list + } else { + this.$message.warning('项目名称获取失败') + } + }) + }, + //字典里获取所有标段块号 + getAllBlocks() { + let params = { + pageNum: 1, + pageSize: 100000000 + } + this.$api.Dictionary.searchDictionary(params).then(res => { + if (res.statusMsg === 'ok') { + const segmentList = [] + res.data.list.forEach(item => { + if (item.dictType === '5') { + segmentList.push({ + dictName: item.dictName, + needType: item.dictId, + needNum: 0, + segmentId: '' + }) + } + }) + this.ruleForm.segmentList = segmentList + } else { + this.$message.warning('标段获取失败') + } + }) + }, + // 已完成 table信息 + getDetailLists() { + let detailData = this.detail + this.$api.DuctpiecePLM.searchDuctpiecePLMList({ + segmentId: detailData.rowId, + pageNum: detailData.pageNum, + pageSize: detailData.pageSize + }).then(res => { + if (res.success) { + detailData.total = res.data.total + detailData.tableData = res.data.list + } + }) + }, + // 已完成 title信息 + getDetailInfos(id) { + this.$api.Engineer.detailsProjects({proId: id}).then(res => { + if (res.success) { + this.detail.infos = res.data + } + }) + }, + // 初始化 ruleform + setFormProps(options = {}) { + let _form = { + proId: '', // 项目名称id + mileage: '', // 起讫里程 + unitProjectName: '', // 单位工程名称 + segmentAdmin: '', // 需求负责人 + shieldEnp: '', // 盾构单位 + startTime: null, // 开始时间 + endTime: null, // 结束时间 + duration: '', // 工期 + station: '', // 站点 + segmentList: [], // 标段需求 + } + this.ruleForm = Object.keys(options).length ? options : _form + }, + // 初始化 table 配置 + setTableColumn() { + this.tableColumns = [ + {index: true}, + {slot: "finished", name: "已完成(块)"}, + {name: "项目名称", key: "proName", width: 160}, + {name: "起讫里程", key: "mileage"}, + {name: "单位工程名称", key: "unitProjectName"}, + {name: "盾构单位", key: "shieldEnp"}, + {name: "开始时间", key: "startTime", width: 100}, + {name: "结束时间", key: "endTime", width: 100}, + {name: "工期", key: "duration"}, + {name: "站点", key: "station"}, + {name: "负责人", key: "realName"}, + { + operation: true, name: "操作", width: 140, value: [ + {name: "修改", class: "table_btn", permission: "update", handleRow: this.updateRow}, + {name: "删除", class: "delete_btn", permission: "delete", handleRow: this.deleteRow}, + ] + }, + ] + this.detail.tableColumns = [ + {index: true}, + {name: "环号", key: "ringNum"}, + {name: "管片编号", key: "pipeNum", width: 140}, + {name: "转向", key: "turnName", width: 106}, + {name: "配筋", key: "reinforcementName"}, + {name: "注浆孔", key: "groutingHolesName"}, + {name: "块号", key: "blockNumName"}, + {name: "模具", key: "mouldNum"}, + {name: "入模时间", key: "intoModTime", width: 136}, + {name: "浇筑时间", key: "pouringTime"}, + {name: "质检时间", key: "checkTime", width: 136}, + {name: "生产班组", key: "groupName"}, + {name: "项目", key: "proName", width: 240}, + {name: "质量标注", key: "checkResultStr"}, + ] + }, + // 重置表单 + resetForm(formName) { + this.$refs[formName].resetFields() + }, + // 显示表单 + showForm() { + !this.isRender && (this.isRender = true) + this.asyncVisible = true + }, + // 隐藏表单 + closeForm() { + this.asyncVisible = false + this.resetForm('ruleForm') + this.setFormProps() + }, + // 查询按钮列表信息 + queryReset() { + this.queryInfo.pageNum = 1 + this.queryInfo.pageSize = 10 + this.getLists() + }, + // 添加数据 + addRow() { + this.submitMode = 'add' + this.showForm() + }, + // 更新数据 + async updateRow(row) { + this.submitMode = 'update' + this.showForm() + const segmentList = await this.getProjectBlocks(row.segmentId) + Object.keys(this.ruleForm).forEach(item => { + if (row.hasOwnProperty.call(row, item)) { + this.ruleForm[item] = row[item] + } + }) + this.ruleForm.segmentId = row.segmentId + this.ruleForm.segmentList = segmentList + }, + // 删除数据 + deleteRow(row) { + this.$confirm("该操作将删除该信息,是否继续删除?", "提示", { + confirmButtonText: "确定", + cancelButtonText: "取消", + type: "warning" + }).then(() => { + this.$api.Engineer.deleteSegment({segmentId: row.segmentId}).then(res => { + if (res.statusMsg === 'ok') { + this.queryReset(); + this.$message.success("删除成功!") + } else { + this.$message.warning(res.statusMsg) + } + }) + }).catch(() => { + this.$message.warning("您已取消") + }) + }, + //获取项目标段 + getProjectBlocks(id) { + return new Promise(resolve => { + this.$api.Engineer.detailsSegment({segmentId: id}).then(res => { + let outData = [] + if (res.statusMsg === 'ok') { + outData.push(...res.data.segmentNeeds) + } + resolve(outData) + }) + }) + }, + //通过不同的项目展示不同的标段需求 + changeNeed(val) { + this.$api.Reinforce.searchProjectBears({proId: val}).then(res => { + if (res.statusMsg === 'ok') { + const segmentList = [] + res.data.blokDtos.forEach(item => { + segmentList.push({ + dictName: item.blockName, + needType: item.blockNum, + needNum: 0, + segmentId: '' + }) + }) + this.ruleForm.segmentList = segmentList + } else { + this.$message.warning('标段获取失败') + } + }) + }, + // 提交表单 + onSubmit: throttle(function () { + this.$refs.ruleForm.validate(valid => { + if (!valid) return + const params = this.ruleForm + if (this.isUpdate) { + // 更新 + this.$api.Engineer.updateSegment(params).then((res) => { + if (res.statusMsg === 'ok') { + this.closeForm() + this.getLists() + this.$message.success('更新成功!') + } else { + this.$message.warning(res.statusMsg) + } + }) + } else { + // 添加 + this.$api.Engineer.insertSegment(params).then((res) => { + if (res.statusMsg === 'ok') { + this.closeForm() + this.getLists() + this.$message.success('添加成功!') + } else { + this.$message.warning(res.statusMsg) + } + }) + } + }) + }, 1000), + showDetail(row) { + let detailData = this.detail + !detailData.isRenderDetail && (detailData.isRenderDetail = true) + detailData.asyncVisible = true + detailData.rowId = row.segmentId + detailData.total = 0 + detailData.tableData = [] + detailData.infos = [] + this.getDetailInfos(row.proId) + this.getDetailLists() + }, + // 分页改变 + pageChange() { + this.getLists(); + }, + // 已完成の分页改变 + detailPageChange() { + this.getDetailLists(); + }, + } +} +</script> + +<style lang="scss" scoped> +@import '../../style/layout-main.scss'; + +/deep/ { + &::-webkit-scrollbar { + width: 8px; + height: 8px; + } + + &::-webkit-scrollbar-corner { + background-color: transparent; + } + + &::-webkit-scrollbar-thumb { + border-radius: 10px; + box-shadow: inset 0 0 5px transparent; + background: #39B5FE; + } + + &::-webkit-scrollbar-track { + box-shadow: inset 0 0 5px transparent; + border-radius: 10px; + background: rgba(76, 188, 254, .3); + } +} + +.el-card { + margin-bottom: 20px; + border: none; + color: #fff; + background: rgba(9, 64, 101, 1); + + .titles { + float: left; + width: 25%; + min-height: 36px; + padding-right: 6px; + margin-bottom: 6px; + line-height: 18px; + box-sizing: border-box; + } + + span { + color: #39B5FE; + } +} + +.section_needs { + position: relative; + color: #18F5F7; + padding: 20px 20px 10px 15px; + border-bottom: 1px solid #1949A3; + + &::before { + position: absolute; + content: ""; + width: 2px; + height: 20px; + background-color: #18F5F7; + top: 20px; + left: 5px; + } +} + +.section_needs_content { + display: flex; + flex-wrap: wrap; + + .needs_items { + max-width: 190px; + min-width: 142px; + padding: 15px; + display: flex; + + .needs_text { + // width: 50px; + flex: none; + align-self: center; + text-align: center; + padding-right: 15px; + color: #E1E3E9; + } + + .needs_num { + align-self: center; + } + } +} </style> \ No newline at end of file diff --git a/web/src/views/SecureManage/SmartHelmet/AlarmRecord.vue b/web/src/views/SecureManage/SmartHelmet/AlarmRecord.vue index 668cc49..9c82ec0 100644 --- a/web/src/views/SecureManage/SmartHelmet/AlarmRecord.vue +++ b/web/src/views/SecureManage/SmartHelmet/AlarmRecord.vue @@ -1,3 +1,725 @@ -<template> - <div>报警记录</div> -</template> \ No newline at end of file +<template> + <!-- 安全管理 ==> 智能安全帽 => 报警记录 --> + <div class="main"> + <!-- main_left --> + <div class="main_left" v-show="!isRenderDetail"> + <div class="main_left_search"> + <div class="search_item"> + <span>姓名:</span> + <el-input size="mini" v-model="queryInfo.userName" placeholder="请输入姓名" clearable></el-input> + </div> + <div class="search_item"> + <span>设备编号:</span> + <el-input size="mini" v-model="queryInfo.deviceNum" placeholder="请输入设备编号" clearable></el-input> + <el-button icon="el-icon-search" @click="queryTable">查询</el-button> + </div> + </div> + <div class="main_left_table"> + <cpnTable :table-loading="loading" :table-data="dataList" :table-columns="tableColumns"> + </cpnTable> + </div> + </div> + <!-- main_right --> + <div class="main_right" v-show="!isRenderDetail"> + <div class="echart_options"> + <el-radio-group v-model="chart.radioGroupVal" @change="loadChart"> + <el-radio-button label="7">近7天报警数</el-radio-button> + <el-radio-button label="31">近31天报警数</el-radio-button> + </el-radio-group> + </div> + <div ref="echart_bar" class="echart_bar"></div> + <div class="echart_drop"> + <div ref="echart_drop_1" :class="{ show: chart.isShowDrop1, hide: !chart.isShowDrop1 }"></div> + <div ref="echart_drop_2" :class="{ show: chart.isShowDrop2, hide: !chart.isShowDrop2 }"></div> + </div> + </div> + <!-- detail --> + <div class="detail" v-if="isRenderDetail"> + <div class="detail_option"> + <div class="search_item"> + <span>日期:</span> + <el-date-picker class="elDatePicker" type="daterange" v-model="detail.date" value-format="yyyy-MM-dd" + start-placeholder="开始日期" end-placeholder="结束日期" clearable></el-date-picker> + </div> + <el-button icon="el-icon-search" @click="queryDetail">查询</el-button> + <el-button style="float:right" @click="hideDetail">返回</el-button> + </div> + <div class="detail_botbox"> + <div class="detail_table"> + <cpnTable :table-data="detail.dataList" :table-columns="detail.tableColumns"> + </cpnTable> + </div> + <div class="detail_map"> + <div id="container"></div> + <div class="detail_map_info_l" v-show="detail.isShowInfo"> + <h2>静默报警</h2> + <p>原因: <span>{{ detail.info.cause }}</span></p> + <p>姓名: <span>{{ detail.info.name }}</span></p> + <p>设备: <span>{{ detail.info.device }}</span></p> + <p>位置: <span>{{ detail.info.position }}</span></p> + <p>时间: <span>{{ detail.info.time }}</span></p> + <el-button style="float: right;" @click="() => { detail.isShowInfo = false }">确定</el-button> + </div> + <div class="detail_map_info_r" v-show="detail.isShowInfo"> + <h2>报警设备信息</h2> + <p>姓名: <span>{{ detail.info.name }}</span></p> + <p>时间: <span>{{ detail.info.time }}</span></p> + </div> + </div> + </div> + </div> + </div> +</template> + +<script> +import AMapLoader from '@amap/amap-jsapi-loader'; +import cpnTable from '@/components/element/Table' +import 'echarts-liquidfill' // 水滴图形 +export default { + data() { + return { + loading: false, + isRenderDetail: false, // 是否查看轨迹地图 + queryInfo: { + pageNum: 1, + pageSize: 9999, + userName: '', + deviceNum: '', + }, + dataList: [], + tableColumns: [], + userId: '', // 用户id + userName: '', // 用户名称 + deviceNum: '', // 设备编号 + // 图表数据 + chart: { + date: '', + radioGroupVal: '7', // (7 31)默认近7天 + isShowDrop1: false, + isShowDrop2: false, + chartInstanceLine: null, // 柱状图实例 + chartInstanceDrop1: null, // 水滴图实例 + chartInstanceDrop2: null, + }, + detail: { // 详情 + date: [], // 日期查询 + dataList: [], + tableColumns: [], + isShowInfo: false, + // 报警信息 + info: { + cause: '', + name: '', + device: '', + position: '', + time: '', + }, + // 报警类型原因 + sosTypes: { + 1: '长时间静止' + }, + }, + $http: '', // 接口api路径 + } + }, + components: { + cpnTable + }, + created() { + // 添加密钥,获取高德地址使用 + window._AMapSecurityConfig = { + securityJsCode: "aa90b00e27025d1f57286675cc8b232e", + } + this.$http = this.$api.Safety.SmartHelmet.warning + this.setTableColumn() + this.getLists() + }, + mounted() { + this.initChart() + window.addEventListener("resize", this.screenAdapter) + }, + beforeDestroy() { + this.clearMap() + this.clearChart() + window.removeEventListener("resize", this.screenAdapter) + }, + methods: { + setTableColumn() { + this.tableColumns = [ + {name: "用户ID", key: "userId"}, + {name: "姓名", key: "userName"}, + {name: "设备编号", key: "deviceNum"}, + { + operation: true, name: "操作", width: 140, value: [ + {name: "查看", class: "table_btn", handleRow: this.warchRow}, + {name: "详情", class: "table_btn", handleRow: this.detailRow}, + ] + }, + ] + }, + // 获取报警信息 + async getWarning() { + let out = {} + const params = { + userId: this.userId, + strTime: this.chart.date?.[0], + endTime: this.chart.date?.[1], + } + const {data, statusMsg} = await this.$http.getWarning(params) + if (statusMsg === 'ok') { + out = data + } + return out + }, + // 获取table数据 + getLists() { + this.loading = true + this.$api.Safety.SmartHelmet.getLists(this.queryInfo).then(res => { + if (res.statusMsg === 'ok') { + this.dataList = res.data.list + } + this.loading = false + }) + }, + // table-详情按钮 + detailRow(row) { + this.userId = row.userId + this.deviceNum = row.deviceNum + this.renderDetail() + }, + // table-查看按钮 + warchRow(row) { + if (this.userId === row.userId) return + this.userId = row.userId + this.userName = row.userName + console.log(row) + this.chart.isShowDrop1 = false + this.chart.isShowDrop2 = false + this.loadChart() + }, + // 加载图表 + loadChart() { + this.changeDay() + this.showDrop() + this.renderChart() + }, + // 水滴图表根据 7/31 切换显示 + showDrop() { + if (this.chart.radioGroupVal === '7') { + this.chart.isShowDrop1 = true + } else if (this.chart.radioGroupVal === '31') { + this.chart.isShowDrop2 = true + } + }, + // 近 7/31 天切换 + changeDay() { + const nowDate = new Date().format() + const beforeDate7 = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).format() + const beforeDate31 = new Date(Date.now() - 31 * 24 * 60 * 60 * 1000).format() + const beforeDate = this.chart.radioGroupVal === '7' ? beforeDate7 : (this.chart.radioGroupVal === '31' ? beforeDate31 : '') + + this.chart.date = [beforeDate, nowDate] + }, + // 初始化图表 + initChart() { + this.chart.chartInstanceLine = this.$echarts.init(this.$refs.echart_bar) + this.chart.chartInstanceDrop1 = this.$echarts.init(this.$refs.echart_drop_1) + this.chart.chartInstanceDrop2 = this.$echarts.init(this.$refs.echart_drop_2) + this.renderBaseChart() + }, + // 渲染chart基础配置 + renderBaseChart() { + const baseBarOptions = { + animationDuration: 1500, + tooltip: { + trigger: "axis", + formatter: "日期: {b} <div></div> 在线时长: {c}分", + }, + grid: { + top: "10%", + right: "0", + left: "2%", + bottom: "14", + containLabel: true, + }, + xAxis: [{ + type: 'category', + axisLine: { + lineStyle: { + width: 2, + color: "#B7E4F7", + }, + }, + axisTick: { + show: false, + }, + }], + yAxis: [{ + type: 'value', + axisLabel: { + formatter: "{value}", + color: "#CAD3E0", + }, + splitLine: { + lineStyle: { + width: 2, + type: "dashed", + color: "#5A7E9D", + }, + }, + }], + series: [{ + type: 'line', + smooth: true, + barWidth: 14, + color: new this.$echarts.graphic.LinearGradient(0, 0, 0, 1, [ + {offset: 0, color: "#C15B3C"}, + {offset: 1, color: "#FACD91"}, + ]), + }], + } + const baseDropOptions1 = { + title: { + text: '近一周报警天数', + left: '32%', + top: 30, + textStyle: { + color: "#fff", + }, + }, + series: [{ + type: 'liquidFill', + data: [0], + radius: '60%', //图表的大小 值是圆的直径 + shape: 'circle', //水填充图的形状 circle默认圆形 rect圆角矩形 triangle三角形 diamond菱形 pin水滴状 arrow箭头状 还可以是svg的path + center: ['50%', '50%'], //图表相对于盒子的位置 + outline: { + show: false + }, + backgroundStyle: { // 背景配置 + color: 'transparent', //背景颜色 + borderWidth: '2',//边框大小 + borderColor: 'rgba(17, 94, 176, 0.4)', // 边框颜色 + }, + itemStyle: { + color: new this.$echarts.graphic.LinearGradient( + 0, 0, 0, 1, //4个参数用于配置渐变色的起止位置, 这4个参数依次对应右/下/左/上四个方位. 而0 0 0 1则代表渐变色从正上方开始 + [ + {offset: 0, color: '#2790F4'}, + {offset: 1, color: '#165190'} + ] //数组, 用于配置颜色的渐变过程. 每一项为一个对象, 包含offset和color两个参数. offset的范围是0 ~ 1, 用于表示位置 + ) + }, + }] + } + const baseDropOptions2 = { + title: { + text: '近一个月(31天)报警天数', + right: '24%', + top: 30, + textStyle: { + color: "#fff", + }, + }, + series: [{ + type: 'liquidFill', + data: [0], + radius: '60%', //图表的大小 值是圆的直径 + shape: 'circle', //水填充图的形状 circle默认圆形 rect圆角矩形 triangle三角形 diamond菱形 pin水滴状 arrow箭头状 还可以是svg的path + center: ['50%', '50%'], //图表相对于盒子的位置 + outline: { + show: false + }, + backgroundStyle: { // 背景配置 + color: 'transparent', //背景颜色 + borderWidth: '2',//边框大小 + borderColor: 'rgba(17, 94, 176, 0.4)', // 边框颜色 + }, + itemStyle: { + color: new this.$echarts.graphic.LinearGradient( + 0, 0, 0, 1, //4个参数用于配置渐变色的起止位置, 这4个参数依次对应右/下/左/上四个方位. 而0 0 0 1则代表渐变色从正上方开始 + [ + {offset: 0, color: '#2790F4'}, + {offset: 1, color: '#165190'} + ] //数组, 用于配置颜色的渐变过程. 每一项为一个对象, 包含offset和color两个参数. offset的范围是0 ~ 1, 用于表示位置 + ) + }, + }] + } + this.chart.chartInstanceLine.setOption(baseBarOptions) + this.chart.chartInstanceDrop1.setOption(baseDropOptions1) + this.chart.chartInstanceDrop2.setOption(baseDropOptions2) + }, + // 渲染chart内容数据(x,y,series) + async renderChart() { + const obj = await this.getWarning() + const day = obj.day // 报警天数 + const percentDay = (day / this.chart.radioGroupVal).toFixed(2) //百分比 + const x = [] // x轴数据 + const y = [] // y轴数据 + obj.reportNumDtos.forEach(item => { + x.push(item.smTime) + y.push(item.num) + }) + const lineOptions = { + title: { + text: `${this.userName} 近${this.chart.radioGroupVal}天报警天数`, + left: '42%', + textStyle: { + color: "#E2E5EA", + fontSize: 12, + }, + }, + xAxis: { + index: 0, + data: x + }, + series: { + index: 0, + data: y + } + } + const dropOptions = { + series: { + index: 0, + data: [percentDay], + label: { + color: '#fff', + formatter: () => day, + } + } + } + this.chart.chartInstanceLine.setOption(lineOptions) + if (this.chart.radioGroupVal === '7') { + this.chart.chartInstanceDrop1.setOption(dropOptions) + + } else if (this.chart.radioGroupVal === '31') { + this.chart.chartInstanceDrop2.setOption(dropOptions) + } + }, + screenAdapter() { + this.chart.chartInstanceLine?.resize() + this.chart.chartInstanceDrop1?.resize() + this.chart.chartInstanceDrop2?.resize() + }, + queryTable() { + this.getLists() + }, + /* --------详情页面-s */ + renderDetail() { + this.isRenderDetail = true + this.setTableDetailColumn() + this.getDetailLists() + this.renderMap() + }, + setTableDetailColumn() { + if (this.detail.tableColumns.length) return + this.detail.tableColumns = [ + {name: "时间", key: "smTime"}, + {name: "姓名", key: "userName", width: 70}, + {name: "原因", key: "sosType", width: 80, formatter: (row) => this.detail.sosTypes[row.sosType] || ''}, + { + operation: true, name: "操作", width: 100, value: [ + {name: "查看", class: "table_btn", handleRow: this.warchDetailRow}, + ] + }, + ] + }, + getDetailLists() { + const params = { + userId: this.userId, + strTime: this.detail.date?.[0], + endTime: this.detail.date?.[1], + } + this.detail.dataList = [] + this.$http.getDetailWarning(params).then(res => { + if (res.statusMsg === 'ok') { + this.detail.dataList = res.data + } + }) + }, + // 查看具体报警信息 + async warchDetailRow(row) { + const _position = await this.getAdress(row.xpoint, row.ypoint) + const info = { + cause: this.detail.sosTypes[row.sosType], + name: row.userName, + device: this.deviceNum, + position: `${_position}(经度:${row.ypoint}, 纬度${row.xpoint})`, + time: row.smTime, + } + this.detail.info = info + this.detail.isShowInfo = true + }, + renderMap() { + AMapLoader.load({ + key: "1dc3895771d98745fa27bbcc3280464f", // 申请好的Web端开发者Key,首次调用 load 时必填 + version: "2.0", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15 + }).then(AMap => { + this.detail.AMap = AMap + this.detail.map = new AMap.Map("container", { + resizeEnable: true, + viewMode: "2D", // 是否为3D地图模式 + zoom: 13, // 初始化地图级别 + }) + }) + }, + createMapMarker(lnglatXY) { + if (!this.detail.marker) { + const AMap = this.detail.AMap + this.detail.marker = new AMap.Marker() + } + const marker = this.detail.marker + marker.setMap(null) + marker.setPosition(lnglatXY) + this.detail.map.add(marker) + this.detail.map.setFitView() + }, + // 获取地址 + getAdress(x, y) { + return new Promise(resolve => { + let address = '' + const AMap = this.detail.AMap + const lnglatXY = [y, x] // 位置的经纬度 + this.createMapMarker(lnglatXY) + AMap.plugin('AMap.Geocoder', () => { + const geocoder = new AMap.Geocoder({ + radius: 1000, + extensions: "all" + }) + geocoder.getAddress(lnglatXY, function (status, result) { + if (status === 'complete' && result.info === 'OK') { + address = result.regeocode.formattedAddress + } + resolve(address) + }) + }) + }) + }, + hideDetail() { + this.clearMap() + this.detail.date = [] + this.detail.isShowInfo = false + this.isRenderDetail = !this.isRenderDetail + }, + queryDetail() { + this.getDetailLists() + }, + /* --------详情页面-e */ + clearMap() { + this.detail.map?.destroy() + }, + clearChart() { + this.chart.chartInstanceLine?.clear() + this.chart.chartInstanceDrop1?.clear() + this.chart.chartInstanceDrop2?.clear() + }, + } +} +</script> + +<style lang="scss" scoped> +@import '@/style/layout-main.scss'; + +::v-deep .el-radio-button__inner { + border: 1px solid #39B5FE; + color: #fff; + background: transparent; +} + +.hide { + opacity: 0; +} + +.show { + opacity: 1; +} + +#container { + width: 100%; + height: 100%; +} + +.main { + display: flex; + flex-direction: row; + color: #EBEBEB; + + .main_left { + display: flex; + flex-direction: column; + width: 380px; + padding: 14px; + margin-right: 18px; + border: 1px solid #39B5FE; + border-radius: 10px; + box-shadow: rgba(0, 145, 255, 0.7) 0 0 18px inset; + box-sizing: content-box; + + .search_item { + span { + display: inline-block; + min-width: 54px; + margin-right: 6px; + text-align: right; + } + + .el-input { + width: 220px; + margin: 0 28px 16px 0; + } + } + + ::v-deep.main_left_table { + overflow: hidden; + + .el-table { + display: flex; + flex-direction: column; + + .el-table__body-wrapper { + overflow-y: auto; + flex: 1; + } + } + } + } + + .main_right { + overflow: hidden; + display: flex; + flex-direction: column; + flex: 1; + padding: 20px; + border: 1px solid #39B5FE; + background-color: rgba(0, 52, 121, 0.3); + border-radius: 10px; + box-shadow: rgba(0, 145, 255, 0.7) 0 0 18px inset; + box-sizing: content-box; + + .echart_options { + margin-bottom: 10px; + + + } + + .echart_bar { + flex: 1; + } + + .echart_drop { + flex: 1.1; + + > div { + float: left; + width: 50%; + height: 100%; + } + } + } + + .detail { + display: flex; + flex-direction: column; + position: absolute; + width: 100%; + height: 100%; + padding: 20px; + + .detail_option { + .search_item { + float: left; + + span { + display: inline-block; + margin-right: 6px; + } + + .el-input { + width: 220px; + margin: 0 28px 16px 0; + } + + .elDatePicker { + margin: 0 10px 14px 0; + } + } + } + + .detail_botbox { + overflow: hidden; + flex: 1; + } + + ::v-deep.detail_table { + float: left; + width: 400px; + height: 100%; + margin-right: 20px; + + .el-table { + display: flex; + flex-direction: column; + + .el-table__body-wrapper { + overflow-y: auto; + flex: 1; + } + } + } + + .detail_map { + position: relative; + overflow: hidden; + min-height: 200px; + height: 100%; + + p { + display: flex; + width: 300px; + padding-left: 10px; + line-height: 32px; + word-break: break-all; + border-radius: 3px; + background: #143164; + + span { + padding: 0 6px; + color: #39B5FE; + flex: 1; + } + + } + + .detail_map_info_l { + position: absolute; + left: 44px; + top: 160px; + padding: 0 20px 10px 20px; + border-radius: 5px; + background: #0D1846; + + h2 { + margin-bottom: 30px; + color: #F94550; + } + } + + .detail_map_info_r { + position: absolute; + right: 36px; + top: 260px; + padding: 0 20px; + border-radius: 5px; + background: #0D1846; + + h2 { + margin-bottom: 30px; + color: #39B5FE; + } + } + } + + } +} +</style> \ No newline at end of file diff --git a/web/src/views/SecureManage/SmartHelmet/PhoneManage.vue b/web/src/views/SecureManage/SmartHelmet/PhoneManage.vue index f377ec1..300e799 100644 --- a/web/src/views/SecureManage/SmartHelmet/PhoneManage.vue +++ b/web/src/views/SecureManage/SmartHelmet/PhoneManage.vue @@ -1,3 +1,250 @@ -<template> - <div>照片管理</div> -</template> \ No newline at end of file +<template> + <!-- 安全管理 ==> 智能安全帽 => 照片管理 --> + <div class="main"> + <!-- main_left --> + <div class="main_left"> + <div class="main_left_search"> + <div class="search_item"> + <span>姓名:</span> + <el-input size="mini" v-model="queryInfo.userName" placeholder="请输入姓名" clearable></el-input> + </div> + <div class="search_item"> + <span>设备编号:</span> + <el-input size="mini" v-model="queryInfo.deviceNum" placeholder="请输入设备编号" clearable></el-input> + <el-button icon="el-icon-search" @click="queryTable">查询</el-button> + </div> + </div> + <div class="main_left_table"> + <cpnTable :table-loading="loading" :table-data="dataList" :table-columns="tableColumns" + :row-click="rowClick"> + </cpnTable> + </div> + </div> + <!-- main_right --> + <div class="main_right"> + <div class="search_item"> + <span>日期:</span> + <el-date-picker type="daterange" v-model="pics.date" value-format="yyyy-MM-dd" start-placeholder="开始日期" + end-placeholder="结束日期" clearable></el-date-picker> + <!-- <el-select v-model="pics.type"> + <el-option :value="1">所有照片</el-option> + <el-option :value="2">安全带取证</el-option> + </el-select> --> + <el-button icon="el-icon-search" @click="queryPic">查询</el-button> + </div> + <div class="pic_wrap"> + <div class="pic_item" v-for="item in pics.data" :key="item.iid"> + <el-image :src="item.imageUrl" :preview-src-list="[item.imageUrl]" lazy></el-image> + <div class="pic_item_info"> + <div class="pic_item_info_time">{{ item.smTime }}</div> + <div class="pic_item_info_des"></div> + <div class="pic_item_info_btn"> + </div> + </div> + </div> + <div class="nopic" v-if="!pics.data.length">暂无图片</div> + </div> + </div> + </div> +</template> + +<script> +import cpnTable from '@/components/element/Table' +export default { + data() { + return { + loading: false, + queryInfo: { + pageNum: 1, + pageSize: 9999, + userName: '', + deviceNum: '', + }, + dataList: [], + tableColumns: [], + userId: '', // 用户id + pics: { + date: [], + type: '', + data: [], + }, + $http: '', // 接口api路径 + } + }, + components: { + cpnTable + }, + created() { + this.$http = this.$api.Safety.SmartHelmet.pic + this.setTableColumn() + this.getLists() + }, + methods: { + setTableColumn() { + this.tableColumns = [ + {selection: true}, + {name: "用户ID", key: "userId"}, + {name: "姓名", key: "userName"}, + {name: "设备编号", key: "deviceNum"}, + ] + }, + getLists() { + this.loading = true + this.$api.Safety.SmartHelmet.getLists(this.queryInfo).then(res => { + if (res.statusMsg === 'ok') { + this.dataList = res.data.list + } + }).finally(() => this.loading = false) + }, + getPics() { + const params = { + userId: this.userId, + pageNum: 1, + pageSize: 9999, + strTime: this.pics.date?.[0], + endTime: this.pics.date?.[1], + } + this.$http.getPics(params).then(res => { + if (res.statusMsg === 'ok') { + this.pics.data = res.data.list + } + }) + }, + rowClick(row) { + this.userId = row.userId + this.getPics() + }, + queryTable() { + this.getLists() + }, + queryPic() { + this.getPics() + }, + } +} +</script> + +<style lang="scss" scoped> +@import '@/style/layout-main.scss'; + +.main { + display: flex; + flex-direction: row; + color: #EBEBEB; + + .main_left { + display: flex; + flex-direction: column; + width: 380px; + padding: 14px; + margin-right: 18px; + border: 1px solid #39B5FE; + border-radius: 10px; + box-shadow: rgba(0, 145, 255, 0.7) 0 0 18px inset; + box-sizing: content-box; + + .search_item { + span { + display: inline-block; + min-width: 54px; + margin-right: 6px; + text-align: right; + } + + .el-input { + width: 220px; + margin: 0 28px 16px 0; + } + } + + ::v-deep.main_left_table { + overflow: hidden; + + .el-table__row { + cursor: pointer + } + + .el-table { + display: flex; + flex-direction: column; + + .el-table__body-wrapper { + overflow-y: auto; + flex: 1; + } + } + } + } + + .main_right { + overflow: hidden; + display: flex; + flex-direction: column; + flex: 1; + padding: 20px; + border: 1px solid #39B5FE; + background-color: rgba(0, 52, 121, 0.3); + border-radius: 10px; + box-shadow: rgba(0, 145, 255, 0.7) 0 0 18px inset; + box-sizing: content-box; + + .search_item { + + .el-button, + .el-input, + .el-select { + margin: 0 12px 14px 6px; + } + } + + .pic_wrap { + flex: 1; + display: flex; + flex-wrap: wrap; + justify-content: space-between; + align-items: center; + overflow-y: auto; + + .nopic { + width: 100%; + text-align: center; + font-size: 28px; + } + + .pic_item { + display: flex; + flex-direction: column; + width: 380px; + height: 300px; + border: 1px solid green; + margin: 10px; + + .el-image { + border: 1px solid #4EA7DD; + } + + .pic_item_info { + position: relative; + flex: 1; + margin-top: 3px; + border: 1px solid #3DC8FF; + background: rgba(21, 66, 107, .6); + + .pic_item_info_time { + position: absolute; + top: 40%; + font-size: 13px; + text-indent: 20px; + } + + .pic_item_info_btn { + position: absolute; + top: 34px; + right: 20px; + } + } + } + } + } +} +</style> \ No newline at end of file diff --git a/web/src/views/SecureManage/SmartHelmet/SafeHat.vue b/web/src/views/SecureManage/SmartHelmet/SafeHat.vue index ac93bb0..06f4ff5 100644 --- a/web/src/views/SecureManage/SmartHelmet/SafeHat.vue +++ b/web/src/views/SecureManage/SmartHelmet/SafeHat.vue @@ -1,3 +1,129 @@ -<template> - <div>安全帽设备</div> -</template> \ No newline at end of file +<template> + <!-- 安全管理 ==> 智能安全帽 => 安全帽设备 --> + <div class="main"> + <!-- main_left --> + <div class="main_left"> + <div class="main_left_search"> + <div class="search_item"> + <span>姓名:</span> + <el-input size="mini" v-model="queryInfo.userName" placeholder="请输入姓名" clearable></el-input> + </div> + <div class="search_item"> + <span>设备编号:</span> + <el-input size="mini" v-model="queryInfo.deviceNum" placeholder="请输入设备编号" clearable></el-input> + <el-button icon="el-icon-search" v-permission="'search'" @click="queryTable">查询</el-button> + </div> + </div> + <div class="main_left_table"> + <cpnTable :table-loading="loading" :table-data="dataList" :table-columns="tableColumns" + :row-click="rowClick"> + </cpnTable> + </div> + </div> + </div> +</template> + +<script> +import cpnTable from '@/components/element/Table' +export default { + data() { + return { + loading: false, + queryInfo: { + pageNum: 1, + pageSize: 9999, + userName: '', + deviceNum: '', + }, + dataList: [], + tableColumns: [], + userId: '', // 用户id + } + }, + components: { + cpnTable + }, + created() { + this.setTableColumn() + this.getLists() + }, + methods: { + setTableColumn() { + this.tableColumns = [ + {selection: true}, + {name: "用户ID", key: "userId"}, + {name: "姓名", key: "userName"}, + {name: "设备编号", key: "deviceNum"}, + ] + }, + getLists() { + this.loading = true + this.$api.Safety.SmartHelmet.getLists(this.queryInfo).then(res => { + if (res.statusMsg === 'ok') { + this.dataList = res.data.list + } + }).finally(() => this.loading = false) + }, + rowClick(row) { + this.userId = row.userId + }, + queryTable() { + this.getLists() + }, + } +} +</script> + +<style lang="scss" scoped> +@import '@/style/layout-main.scss'; + +.main { + display: flex; + flex-direction: row; + color: #EBEBEB; + + .main_left { + display: flex; + flex-direction: column; + width: 380px; + padding: 14px; + margin-right: 18px; + border: 1px solid #39B5FE; + border-radius: 10px; + box-shadow: rgba(0, 145, 255, 0.7) 0 0 18px inset; + box-sizing: content-box; + + .search_item { + span { + display: inline-block; + min-width: 54px; + margin-right: 6px; + text-align: right; + } + + .el-input { + width: 220px; + margin: 0 28px 16px 0; + } + } + + ::v-deep.main_left_table { + overflow: hidden; + + .el-table__row { + cursor: pointer + } + + .el-table { + display: flex; + flex-direction: column; + + .el-table__body-wrapper { + overflow-y: auto; + flex: 1; + } + } + } + } +} +</style> \ No newline at end of file diff --git a/web/src/views/SecureManage/SmartHelmet/TrackBack.vue b/web/src/views/SecureManage/SmartHelmet/TrackBack.vue index 1939462..a943fa9 100644 --- a/web/src/views/SecureManage/SmartHelmet/TrackBack.vue +++ b/web/src/views/SecureManage/SmartHelmet/TrackBack.vue @@ -1,3 +1,738 @@ -<template> - <div>轨迹回放</div> -</template> \ No newline at end of file +<template> + <!-- 安全管理 ==> 智能安全帽 => 轨迹回放 --> + <div class="main"> + <!-- table --> + <div class="main_left"> + <div class="main_left_search"> + <div class="search_item"> + <span>姓名:</span> + <el-input size="mini" v-model="queryInfo.userName" placeholder="请输入姓名" clearable></el-input> + </div> + <div class="search_item"> + <span>设备编号:</span> + <el-input size="mini" v-model="queryInfo.deviceNum" placeholder="请输入设备编号" clearable></el-input> + <el-button icon="el-icon-search" @click="queryTable">查询</el-button> + </div> + </div> + <div class="main_left_table"> + <cpnTable :table-loading="loading" :table-data="dataList" :table-columns="tableColumns" + :row-click="rowClick"> + </cpnTable> + </div> + </div> + <!-- chart --> + <div class="main_right_echart" v-show="!isShowMap"> + <div class="echart_options"> + <span>日期:</span> + <el-date-picker class="elDatePicker" type="daterange" v-model="chart.date" value-format="yyyy-MM-dd" + start-placeholder="开始日期" end-placeholder="结束日期" clearable></el-date-picker> + <el-button icon="el-icon-search" @click="queryEchart">查询</el-button> + <el-button style="float:right" @click="watchMap">查看</el-button> + </div> + <div ref="echart_bar" class="echart_bar"></div> + <div ref="echart_drop" class="echart_drop"></div> + </div> + <!-- map --> + <div class="main_right_map" v-show="isShowMap"> + <div class="map_option"> + <span>日期:</span> + <el-date-picker class="elDatePicker" type="daterange" v-model="chart.date" value-format="yyyy-MM-dd" + start-placeholder="开始日期" end-placeholder="结束日期" clearable></el-date-picker> + <el-button icon="el-icon-search" @click="queryEchart">查询</el-button> + <el-button style="float:right" @click="hideMap">返回</el-button> + </div> + <div class="map_view"> + <div class="map_view_main"> + <div id="container"></div> + <div id="trackDetail" v-show="isShowTrackDetail"> + <h2>位置信息统计</h2> + <div class="ul_box"> + <ul> + <li :class="{ active: index === maps.trackListIndex }" + v-for="(val, index) in maps.trackLists " :key="index" @click="trackListClick(index)"> + {{ val }} + </li> + </ul> + </div> + </div> + </div> + <div class="map_view_btn"> + <el-button @click="startAnimation">开始动画</el-button> + <el-button @click="pauseAnimation">停止动画</el-button> + <el-button @click="resumeAnimation">继续动画</el-button> + <el-button @click="stopAnimation">停止动画</el-button> + <el-button @click="showTrackDetail">轨迹详情</el-button> + </div> + </div> + </div> + </div> +</template> + +<script> +import AMapLoader from '@amap/amap-jsapi-loader'; +import cpnTable from '@/components/element/Table' +import icon from '@/assets/markerIcon.png' +import 'echarts-liquidfill' // 水滴图形 +export default { + data() { + return { + loading: false, + isShowMap: false, // 是否查看轨迹地图 + isShowTrackDetail: false, // 是否渲染轨迹详情 + queryInfo: { + pageNum: 1, + pageSize: 9999, + userName: '', + deviceNum: '', + }, + dataList: [], + tableColumns: [], + userId: '', // 用户id + // 图表数据 + chart: { + date: [new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).format(), new Date().format()], // 轨迹图表日期.默认近一个月 + totaltime: '', // 安全帽在线总时长 + }, + maps: { // 地图数据 + date: '', + trackLists: [], // 轨迹详情 + trackListIndex: '', + }, + } + }, + components: { + cpnTable + }, + created() { + // 添加密钥,获取高德地址使用 + window._AMapSecurityConfig = { + securityJsCode: "aa90b00e27025d1f57286675cc8b232e", + } + // 存储全局变量.(不响应式) + this.maps.times = [] + this.maps.lineArr = [] + this.maps.AMap = null + this.maps.map = null + this.maps.mapMarker = null + this.chart.chartInstanceBar = null + this.chart.chartInstanceDrop = null + this.$http = this.$api.Safety.SmartHelmet.trackBack // 接口api路径 + + this.setTableColumn() + this.getLists() + }, + mounted() { + window.addEventListener("resize", this.screenAdapter) + }, + beforeDestroy() { + this.clearMap() + this.clearChart() + window.removeEventListener("resize", this.screenAdapter) + }, + methods: { + setTableColumn() { + this.tableColumns = [ + {selection: true}, + {name: "用户ID", key: "userId"}, + {name: "姓名", key: "userName"}, + {name: "设备编号", key: "deviceNum"}, + ] + }, + getLists() { + this.loading = true + this.$api.Safety.SmartHelmet.getLists(this.queryInfo).then(res => { + if (res.statusMsg === 'ok') { + this.dataList = res.data.list + } + this.loading = false + }) + }, + // 获取用户在线时长 + async getOnlineTime() { + let out = {} + const params = { + userId: this.userId, + // strTime: this.chart.date?.[0], + // endTime: this.chart.date?.[1], + strTime: '2023-06-01', + endTime: '2023-06-31', + } + const {data, statusMsg} = await this.$http.getOnlineTime(params) + if (statusMsg === 'ok') { + out = { + list: data.tHelmetTrajectory, + total: data.total, + } + } + return out + }, + // 获取用户轨迹 + async getTrackLists() { + let out = { + positions: [], // 经纬度 + times: [], // 时间 + } + const params = { + userId: this.userId, + // strTime: this.chart.date?.[0], + // endTime: this.chart.date?.[1], + strTime: '2023-06-01', + endTime: '2023-06-31', + } + const {data, statusMsg} = await this.$http.getTrackLists(params) + if (statusMsg === 'ok') { + data.forEach(item => { + out.positions.push([+item.ypoint, +item.xpoint]) + out.times.push(item.smTime) + }) + } + return out + }, + // 初始化图表 + initChart() { + this.chart.chartInstanceBar = this.$echarts.init(this.$refs.echart_bar) + this.chart.chartInstanceDrop = this.$echarts.init(this.$refs.echart_drop) + this.renderBaseChart() + }, + // 渲染chart基础配置 + renderBaseChart() { + const baseBarOptions = { + animationDuration: 1500, + tooltip: { + trigger: "axis", + }, + legend: { + itemWidth: 35, + itemHeight: 12, + x: 'right', + y: 'top', + textStyle: { + color: "#fff", + }, + }, + grid: { + top: "10%", + right: "0", + left: "2%", + bottom: "14", + containLabel: true, + }, + xAxis: [{ + type: 'category', + axisLine: { + lineStyle: { + width: 2, + color: "#B7E4F7", + }, + }, + axisTick: { + show: false, + }, + }], + yAxis: [{ + type: 'value', + axisLabel: { + formatter: "{value}", + color: "#CAD3E0", + }, + splitLine: { + lineStyle: { + width: 2, + type: "dashed", + color: "#5A7E9D", + }, + }, + }], + series: [{ + type: 'bar', + name: "在线时长", + barWidth: 14, + color: new this.$echarts.graphic.LinearGradient(0, 0, 0, 1, [ + {offset: 0, color: "#C15B3C"}, + {offset: 1, color: "#FACD91"}, + ]), + }], + } + const baseDropOptions = { + series: [{ + type: 'liquidFill', + data: [0], + radius: '70%', //图表的大小 值是圆的直径 + shape: 'circle', //水填充图的形状 circle默认圆形 rect圆角矩形 triangle三角形 diamond菱形 pin水滴状 arrow箭头状 还可以是svg的path + center: ['50%', '50%'], //图表相对于盒子的位置 + outline: { + show: false + }, + backgroundStyle: { // 背景配置 + color: 'transparent', //背景颜色 + borderWidth: '2',//边框大小 + borderColor: 'rgba(17, 94, 176, 0.4)', // 边框颜色 + }, + itemStyle: { + color: new this.$echarts.graphic.LinearGradient( + 0, 0, 0, 1, //4个参数用于配置渐变色的起止位置, 这4个参数依次对应右/下/左/上四个方位. 而0 0 0 1则代表渐变色从正上方开始 + [ + {offset: 0, color: '#2790F4'}, + {offset: 1, color: '#165190'} + ] //数组, 用于配置颜色的渐变过程. 每一项为一个对象, 包含offset和color两个参数. offset的范围是0 ~ 1, 用于表示位置 + ) + }, + }] + } + this.chart.chartInstanceBar.setOption(baseBarOptions) + this.chart.chartInstanceDrop.setOption(baseDropOptions) + }, + // 渲染chart内容数据(x,y,series) + async renderChart() { + const {list, total} = await this.getOnlineTime() + const x = [] // x轴数据 + const y = [] // y轴数据 + list.forEach(item => { + x.push(item.ctime) + y.push(item.ltime) + }) + const baseBarOptions = { + xAxis: { + index: 0, + data: x + }, + series: { + index: 0, + data: y + } + } + const baseDropOptions = { + series: { + index: 0, + data: [0.3], + label: { + color: '#fff', + fontSize: 28, + formatter: () => this.handleTime(total), + } + } + } + this.chart.chartInstanceBar.setOption(baseBarOptions) + this.chart.chartInstanceDrop.setOption(baseDropOptions) + }, + // 处理秒=>天时分 + handleTime(num) { + let second = num * 60 + let day = 0 + let hour = 0 + let minutes = 0 + if (second < 86400) { + day = 0; + if (second < 3600) { + hour = 0; + if (second < 60) { + minutes = 0; + } else { + minutes = Math.floor(second / 60); + } + } else { + hour = Math.floor(second / 3600); + if (second - hour * 3600 < 60) { + minutes = 0; + } else { + minutes = Math.floor((second - hour * 3600) / 60); + } + } + } else { + day = Math.floor(second / 86400); + if (second - 86400 * day < 3600) { + hour = 0; + minutes = Math.floor((second - 86400 * day) / 60); + } else { + hour = Math.floor((second - 86400 * day) / 3600); + if (second - 86400 * day - 3600 * hour < 60) { + minutes = 0; + } else { + minutes = Math.floor((second - 86400 * day - 3600 * hour) / 60); + } + } + } + day = day ? day + "天" : '' + hour = hour ? hour + "小时" : '' + minutes = minutes && minutes + "分" + return day + hour + minutes + }, + rowClick(row) { + this.userId = row.userId + if (!this.chart.chartInstanceBar) { + this.initChart() + } + this.hideMap() + this.renderChart() + }, + queryTable() { + this.getLists() + }, + queryEchart() { + this.renderChart() + }, + queryMap() { + + }, + // chart 查看按钮 + watchMap() { + if (!this.userId) { + this.$message.warning('请先选择用户'); + return + } + this.renderMap() + }, + async renderMap() { + this.isShowMap = !this.isShowMap + let {positions, times} = await this.getTrackLists() + this.maps.times = times + this.maps.lineArr = positions + AMapLoader.load({ + key: "1dc3895771d98745fa27bbcc3280464f", // 申请好的Web端开发者Key,首次调用 load 时必填 + version: "2.0", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15 + }).then(AMap => { + this.maps.AMap = AMap + AMap.plugin('AMap.MoveAnimation', () => { + // 创建地图 + this.maps.map = new AMap.Map("container", { + resizeEnable: true, + viewMode: "2D", // 是否为3D地图模式 + zoom: 17, // 初始化地图级别 + }) + if (positions.length) { + // 创建一个 (小人) Icon + var personIcon = new AMap.Icon({ + image: icon, // 图标的取图地址 + imageSize: new AMap.Size(40, 40), // 图标所用图片大小 + }) + // 创建marker实例 + let mapMarker = this.maps.mapMarker = new AMap.Marker({ + map: this.maps.map, + position: positions[0], + icon: personIcon, + zIndex: 10, + offset: new AMap.Pixel(-20, -36), + }) + // marker提示框 + let infoWindow = new AMap.InfoWindow({offset: new AMap.Pixel(0, -30)}); + let markerMouseover = async (e) => { + let index = e.target.content + let adress = await this.getAdress(positions[index]) + let time = times[index] + let el = ` + <p><t>地址:</t><span>${adress}</span></p> + <p><t>时间:</t><span>${time}</span></p> + <p><t>经纬度:</t><span>${positions[index][0]}, ${positions[index][1]}</span></p> + ` + infoWindow.setContent(el) + infoWindow.open(this.maps.map, e.target.getPosition()) + } + // 给折线图添加拐点和绑定事件 + for (var i = 0; i < positions.length; i += 1) { + let center = positions[i] + let icon = new AMap.Icon({ + size: new AMap.Size(6, 11), // 图标尺寸 + image: 'https://webapi.amap.com/theme/v1.3/markers/n/mark_b.png', // Icon的图像 + imageSize: new AMap.Size(6, 11) + }) + let marker = new AMap.Marker({ + map: this.maps.map, + position: center, + icon: icon, + offset: new AMap.Pixel(-3, -10) + }) + marker.content = i + marker.on("mouseover", markerMouseover) + } + // 绘制轨迹 + new AMap.Polyline({ + map: this.maps.map, + path: positions, + // showDir: true, + strokeColor: "#28F", //线颜色 + strokeWeight: 3, //线宽 + }) + // 已通过轨迹 + let passedPolyline = new AMap.Polyline({ + map: this.maps.map, + strokeColor: "red", //线颜色 + strokeWeight: 3, //线宽 + }) + mapMarker.on('moving', (e) => { + passedPolyline.setPath(e.passedPath); + this.maps.map.setCenter(e.target.getPosition(), true) + }) + this.maps.map.setFitView() + } + }) + }) + }, + // 获取地址 + getAdress(lnglats) { + return new Promise(resolve => { + const AMap = this.maps.AMap + AMap.plugin('AMap.Geocoder', () => { + const geocoder = new AMap.Geocoder({ + radius: 1000, + extensions: "all" + }) + geocoder.getAddress(lnglats, (status, result) => { + let address = '' + if (status === 'complete' && result.info === 'OK') { + address = result.regeocodes ? result.regeocodes : result.regeocode.formattedAddress + } + resolve(address) + }) + }) + }) + }, + hideMap() { + this.clearMap() + this.isShowMap = false + this.maps.trackLists = [] + this.maps.trackListIndex = '' + }, + startAnimation() { + this.maps.mapMarker?.moveAlong(this.maps.lineArr, { + // 每一段的时长 + duration: 500,//可根据实际采集时间间隔设置 + // JSAPI2.0 是否延道路自动设置角度在 moveAlong 里设置 + autoRotation: true, + }) + }, + pauseAnimation() { + this.maps.mapMarker?.pauseMove() + }, + resumeAnimation() { + this.maps.mapMarker?.resumeMove() + }, + stopAnimation() { + this.maps.mapMarker?.stopMove() + }, + // 显示轨迹详情 + async showTrackDetail() { + this.isShowTrackDetail = !this.isShowTrackDetail + if (this.maps.trackLists.length) { + return + } + if (this.maps.times.length && this.maps.lineArr.length) { + let tracks = [] + let adress = await this.getAdress([...this.maps.lineArr]) + adress.forEach((item, index) => { + tracks.push(`位置: ${this.maps.times[index]}(${item.formattedAddress})`) + }) + this.maps.trackLists = tracks + } + }, + trackListClick(index) { + this.maps.trackListIndex = index + }, + screenAdapter() { + this.chart.chartInstanceBar?.resize() + this.chart.chartInstanceDrop?.resize() + }, + clearMap() { + this.maps.mapMarker?.stopMove() + this.maps.map?.destroy() + }, + clearChart() { + this.chart.chartInstanceBar?.clear() + this.chart.chartInstanceDrop?.clear() + }, + } +} +</script> + +<style lang="scss" scoped> +@import '@/style/layout-main.scss'; + +::v-deep .amap-info-content { + flex-direction: row; + width: 220px; + padding: 14px; + line-height: 20px; + border-radius: 5px; + background: #0D1846; + + p { + display: flex; + width: 100%; + } + + t { + width: 40px; + } + + span { + flex: 1; + color: #39B5FE; + } +} + +::v-deep .bottom-center .amap-info-sharp { + border-top: 8px solid #0D1846; +} + +.active { + color: #39B5FE; +} + +#container { + min-height: 400px; + flex: 1.6; +} + +#trackDetail { + overflow: hidden; + display: flex; + flex-direction: column; + flex: 1; + + h2 { + text-indent: 20px; + } + + .ul_box { + overflow-y: auto; + flex: 1; + + ul { + margin: 0; + padding: 0 16px; + } + + li { + padding: 10px 0; + font-size: 15px; + line-height: 24px; + list-style: none; + border-bottom: 1px dashed #fff; + cursor: pointer; + } + } +} + +.main { + display: flex; + flex-direction: row; + color: #EBEBEB; + + .main_left { + display: flex; + flex-direction: column; + width: 380px; + padding: 14px; + margin-right: 18px; + border: 1px solid #39B5FE; + border-radius: 10px; + box-shadow: rgba(0, 145, 255, 0.7) 0 0 18px inset; + box-sizing: content-box; + + .search_item { + span { + display: inline-block; + min-width: 54px; + margin-right: 6px; + text-align: right; + } + + .el-input { + width: 220px; + margin: 0 28px 16px 0; + } + } + + ::v-deep.main_left_table { + overflow: hidden; + + .el-table__row { + cursor: pointer + } + + .el-table { + display: flex; + flex-direction: column; + + .el-table__body-wrapper { + overflow-y: auto; + flex: 1; + } + } + } + } + + .main_right_echart { + overflow: hidden; + display: flex; + flex-direction: column; + flex: 1; + padding: 20px; + border: 1px solid #39B5FE; + background-color: rgba(0, 52, 121, 0.3); + border-radius: 10px; + box-shadow: rgba(0, 145, 255, 0.7) 0 0 18px inset; + box-sizing: content-box; + + .echart_options { + span { + display: inline-block; + margin-right: 6px; + text-align: right; + } + + .elDatePicker { + width: 260px; + margin: 0 10px 14px 0; + } + } + + .echart_bar { + flex: 1; + } + + .echart_drop { + flex: 1.1; + } + } + + .main_right_map { + display: flex; + flex-direction: column; + position: absolute; + right: 0; + left: 430px; + top: 0; + bottom: 0; + padding: 20px; + + .map_option { + span { + display: inline-block; + margin-right: 6px; + } + + .elDatePicker { + width: 260px; + margin: 0 10px 14px 0; + } + } + + .map_view { + overflow: hidden; + display: flex; + flex-direction: column; + flex: 1; + + .map_view_main { + overflow: hidden; + display: flex; + flex: 1; + } + + .map_view_btn { + margin-top: 10px; + } + } + } +} +</style> \ No newline at end of file -- Gitblit v1.9.3