init
This commit is contained in:
2025-06-27 17:42:38 +08:00
commit bd6402478b
5317 changed files with 785994 additions and 0 deletions
@@ -0,0 +1,47 @@
<template>
<BasicModal
@register="registerModal"
title="监测数据"
width="80%"
destoryOnClose
@cancel="closeModal"
:bodyStyle="{ height: 'calc(100vh * 0.7)', overflow: 'hidden auto' }"
>
<indoor-page style="height: calc(80vh - 160px)" v-if="typeId == 0 && meterCode" :meter-code="meterCode" />
<outer-page style="height: calc(80vh - 160px)" v-if="typeId == 1 && meterCode" :meterCode="meterCode" />
<water-page style="height: calc(80vh - 160px)" v-if="typeId == 3 && meterCode" :meterCode="meterCode" />
<pollen-page style="height: calc(80vh - 160px)" v-if="typeId == 2 && meterCode" :meterCode="meterCode" />
</BasicModal>
</template>
<script setup lang="ts">
import BasicModal from '/@/components/Modal/src/BasicModal.vue';
import indoorPage from '/@/views/interveneNew/environment/inner/dataAnalysis/indexSon.vue';
import outerPage from '/@/views/interveneNew/environment/outer/dataAnalysis/indexSon.vue';
import waterPage from '/@/views/interveneNew/environment/water/dataAnalysis/indexSon.vue';
import pollenPage from '/@/views/interveneNew/environment/pollen/dataAnalysis/indexSon.vue';
import { useModalInner } from '/@/components/Modal';
import { ref } from 'vue';
const typeId = ref(0);
const meterCode = ref('');
const emit = defineEmits(['closeModal']);
const [registerModal, { setModalProps }] = useModalInner(async (data) => {
setModalProps({ showOkBtn: false, showCancelBtn: false });
typeId.value = data.typeId;
meterCode.value = data.meterCode;
});
function closeModal() {
typeId.value = -1;
emit('closeModal');
}
</script>
<style scoped lang="less">
:deep(.ant-modal-header) {
background-color: red;
}
:deep(.container) {
overflow: hidden !important;
}
</style>
@@ -0,0 +1,235 @@
<template>
<div id="container" style="position: relative"> </div>
</template>
<script setup lang="ts">
import AMapLoader from '@amap/amap-jsapi-loader';
import mapKey from '/@/utils/mapKey';
import { onMounted, ref, watch } from 'vue';
const emit = defineEmits(['getMarkerInfo', 'getDetail']);
const record = ref({});
let BasicMap = null;
let SelfMap = null;
let marker = null;
const props = defineProps({
mapList: {
type: Array<any>,
default: () => [],
},
centerPosition: {
type: Array<any>,
default: () => [108.95, 34.33],
},
markerContent: {
type: String,
default: () =>
'<div><img style="width: 20px;height: 20px" src="//a.amap.com/jsapi_demos/static/demo-center/icons/poi-marker-red.png" alt=""></div>',
},
labelContent: {
type: String,
default: () => '<div></div>',
},
field: {
type: String,
default: () => '',
},
id: {
type: String,
default: () => 'id',
},
});
watch(
() => props.mapList,
(nV) => {
if (SelfMap) {
addMarker(nV);
}
},
{ immediate: true }
);
function initMap(mapList) {
AMapLoader.load({
key: mapKey, // 申请好的Web端开发者Key,首次调用 load 时必填
// version: '1.4.15', // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
version: '2.0', // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
plugins: ['AMap.Geocoder'], // 需要使用的的插件列表,如比例尺'AMap.Scale'等
})
.then((AMap) => {
SelfMap = AMap;
//设置地图容器id
BasicMap = new AMap.Map('container', {
viewMode: '3D', //是否为3D地图模式
zoom: 8, //初始化地图级别
center: getCenterP(mapList), //初始化地图中心点位置
resizeEnable: true,
});
addMarker(mapList);
let logo1 = document.getElementsByClassName('amap-logo')[0];
if (logo1) {
// 将logo位置设置在视野之外
logo1.style.position = 'absolute';
logo1.style.left = '-1000px';
}
let logo2 = document.getElementsByClassName('amap-copyright')[0];
if (logo2) {
// 将logo位置设置在视野之外
logo2.style.position = 'absolute';
logo2.style.left = '-1000px';
}
})
.catch((e) => {
console.log(e);
});
}
function getCenterP(mapList) {
let lng = [];
let lat = [];
mapList.map((item: any) => {
lng.push(item.longitude * 1);
lat.push(item.latitude * 1);
});
if (mapList.length === 1) {
return [lng[0], lat[0]];
}
if (mapList.length > 1) {
return getCenter(
lng.sort((a, b) => {
return a - b;
}),
lat.sort((a, b) => {
return a - b;
})
);
}
return props.centerPosition;
}
function selectInfo(v) {
try {
if (v && Object.keys(JSON.parse(v)).length > 0) {
const { longitude, latitude } = JSON.parse(v);
record.value = JSON.parse(v);
if (latitude && latitude) {
let lngLat = new SelfMap.LngLat(longitude, latitude);
BasicMap.setCenter(lngLat);
BasicMap.setZoom(12);
}
}
} catch (e) {
console.log(e);
}
}
let markerList = [];
function addMarker(list) {
removeMarker();
if (list.length === 0) return;
BasicMap.setCenter(getCenterP(list));
let marker = null;
markerList = list.map((item: any) => {
marker = new SelfMap.Marker({
position: new SelfMap.LngLat(item.longitude * 1, item.latitude * 1),
content: props.markerContent ? props.markerContent.replace('^&', item[props.field]) : item?.markerContent,
// icon: medicalResource,
maxZoom: 8,
});
marker.info = item;
marker.on('click', markerClick);
return marker;
});
BasicMap.add(markerList);
}
window.getDetail = () => {
event?.stopPropagation();
emit('getDetail', record.value);
};
function getCenter(lng, lat) {
let lngCenter = (lng[0] + lng[lng.length - 1]) / 2;
let latCenter = (lat[0] + lat[lat.length - 1]) / 2;
return [lngCenter, latCenter];
// let centerLngLat = new SelfMap.LngLat(lngCenter, latCenter);
// BasicMap.setCenter(centerLngLat); // 设置地图中心点坐标
}
function markerClick(e) {
if (marker?.info[props.id] !== e.target?.info[props.id]) {
if (marker) {
marker.setLabel({ content: ``, direction: 'right' });
marker.setzIndex(1);
}
marker = e.target;
emit('getMarkerInfo', e.target.info);
record.value = e.target.info;
}
console.log(e);
}
function setLabel(labelContent) {
marker.setzIndex(999);
marker.setLabel({
content: labelContent,
direction: 'right',
});
let logo1 = document.getElementsByClassName('amap-marker-label')[0];
if (logo1) {
logo1.style.left = marker._style.width;
}
}
function removeMarker() {
for (let i = 0; i < markerList.length; i++) {
markerList[i].setMap(null);
}
markerList = []; // 清空数组
marker = null;
}
function destroyMap() {
BasicMap.destroy();
}
defineExpose({
selectInfo,
initMap,
setLabel,
destroyMap,
});
</script>
<style scoped lang="less">
#container {
width: 100%;
height: 100%;
}
:deep(.ant-drawer) {
position: absolute !important;
}
:deep(.ant-drawer-header) {
background-color: #b4c7e7;
}
:deep(.jeecg-basic-title) {
font-weight: bold;
}
:deep(.amap-marker-label) {
background-color: transparent !important;
padding: 0 !important;
border: none !important;
cursor: pointer;
top: 0 !important;
}
:deep(.ant-drawer-content-wrapper) {
height: 90%;
margin-top: 2.5%;
}
</style>
@@ -0,0 +1,90 @@
<template>
<BasicModal @register="registerModal" title="咨询数据" width="80%">
<div class="top-d-outer">
<div class="top-d">
<div>医院名称{{ hospitalInfo?.resourceName || '-' }}</div>
<div>医院级别{{ hospitalInfo?.level || '-' }}</div>
<div>专家人数{{ hospitalInfo?.specialistSum }}</div>
<div>当年咨询人次{{ hospitalInfo?.thisYearSessionSum }}</div>
<div>累计咨询人次{{ hospitalInfo?.allSessionSum }}</div>
</div>
</div>
<div style="display: flex; justify-content: flex-end; padding-right: 5px">
<a-radio-group v-model:value="radioValue" button-style="solid" class="radio-group-d" @change="changeRadio">
<a-radio-button value="0">专家数据</a-radio-button>
<a-radio-button value="1">员工咨询数据</a-radio-button>
</a-radio-group>
</div>
<BasicTable @register="registerTable" table-type="1" />
</BasicModal>
</template>
<script setup lang="ts">
import BasicModal from '/@/components/Modal/src/BasicModal.vue';
import { useModalInner } from '/@/components/Modal';
import { ref } from 'vue';
import BasicTable from '/@/components/Table/src/BasicTable.vue';
import { useListPage } from '/@/hooks/system/useListPage';
import { userPageApi, yearPageApi } from '/@/views/archivesManage/institution/timeService/consult/consult.api';
import dayjs from 'dayjs';
import { columns1, columns2 } from '/@/views/archivesManage/institution/timeService/consult/consult.data';
const radioValue = ref('0');
const hospitalInfo = ref({});
const [registerModal, { setModalProps }] = useModalInner(async (data) => {
radioValue.value = '0';
setModalProps({ showCancelBtn: false, showOkBtn: false });
hospitalInfo.value = data.record;
setProps({ api: yearPageApi, columns: columns1, searchInfo: { hospitalId: hospitalInfo.value.id, year: dayjs().format('YYYY') } });
await reload({ page: 1 });
});
const { tableContext } = useListPage({
tableProps: {
canResize: false,
orderFlag: false,
immediate: false,
showIndexColumn: true,
useSearchForm: false,
showTableSetting: false,
showActionColumn: false,
},
});
const [registerTable, { setProps, reload }] = tableContext;
async function changeRadio(v) {
if (v.target.value === '0') {
setProps({ api: yearPageApi, columns: columns1 });
await reload({ page: 1 });
} else {
setProps({ api: userPageApi, columns: columns2 });
await reload({ page: 1 });
}
}
</script>
<style scoped lang="less">
.top-d-outer {
padding: 10px;
.top-d {
border-bottom: 1px dashed #cecece;
display: flex;
padding: 0 0 10px;
> div {
margin-right: 50px;
}
}
}
:deep(.ant-radio-button-wrapper) {
color: #1684fc !important;
border-color: #1684fc !important;
&:before {
background-color: #1684fc !important;
}
}
:deep(.ant-radio-button-wrapper-checked) {
color: #ffffff !important;
}
</style>
@@ -0,0 +1,13 @@
import { defHttp } from '/@/utils/http/axios';
enum Api {
list = '/health-consultation/archives/hospital/geo',
statistics = '/health-consultation/archives/consultation/statistics',
userPage = '/health-consultation/archives/consultation/user-page',
yearPage = '/health-consultation/archives/specialist/page',
}
export const listApi = (params: any) => defHttp.get({ url: Api.list, params });
export const statisticsApi = (params: any) => defHttp.get({ url: Api.statistics, params });
export const userPageApi = (params: any) => defHttp.get({ url: Api.userPage, params });
export const yearPageApi = (params: any) => defHttp.get({ url: Api.yearPage, params });
@@ -0,0 +1,78 @@
import { BasicColumn } from '/@/components/Table';
import dayjs from 'dayjs';
import { render } from '/@/utils/common/renderUtils';
export const columns1: BasicColumn[] = [
{
title: '姓名',
dataIndex: 'doctorName',
},
{
title: '性别',
dataIndex: 'sex',
customRender: ({ text }) => {
return render.renderDict(text, 'sex2');
},
},
{
title: '年龄',
dataIndex: 'age',
},
{
title: '科室',
dataIndex: 'departmentName',
},
{
title: '职称',
dataIndex: 'doctorJob_dictText',
},
{
title: `当年服务人次(${dayjs().format('YYYY')})`,
dataIndex: 'thisYearSessionSum',
},
{
title: '累计服务人次',
dataIndex: 'allSessionSum',
},
];
export const columns2: BasicColumn[] = [
{
title: '单位',
dataIndex: 'secondDeptName',
},
{
title: '部门',
dataIndex: 'thirdDeptName',
},
{
title: '姓名',
dataIndex: 'realName',
width: 100,
},
{
title: '工号',
dataIndex: 'workNo',
width: 100,
},
{
title: '性别',
dataIndex: 'sex',
customRender: ({ text }) => {
return render.renderDict(text, 'sex2');
},
width: 80,
},
{
title: '年龄',
dataIndex: 'age',
width: 80,
},
{
title: '最近咨询时间',
dataIndex: 'lastSessionDate',
},
{
title: '累计咨询次数',
dataIndex: 'sessionCount',
},
];
@@ -0,0 +1,178 @@
<template>
<div class="outer-d">
<div class="top-title"> 长庆油田咨询医院 </div>
<div class="map-d">
<template v-if="!loading">
<service-map
ref="serviceMap"
:marker-content="markerContent"
:label-content="labelContent"
field="resourceName"
@get-marker-info="getMarkerInfo"
@get-detail="getDetail"
/>
<div class="bottom-d">
<div
class="bottom-d-item"
v-for="(item, index) in statisticsInfo"
:key="`bottom-d-item-${index}`"
:style="{ margin: index === 1 ? '0 25px 0 50px' : index === 2 ? '0 50px 0 25px' : 0 }"
>
<div>{{ item.value }}</div>
<div>{{ item.name }}</div>
</div>
</div>
</template>
<template v-else>
<div style="width: 100%; height: 100%; display: flex; align-items: center; justify-content: center">
<a-spin size="large" />
</div>
</template>
</div>
</div>
<consult-modal @register="registerModal" />
</template>
<script setup lang="ts">
import ServiceMap from '/@/views/archivesManage/institution/timeService/components/serviceMap.vue';
import { nextTick, onBeforeUnmount, onMounted, ref } from 'vue';
import { listApi, statisticsApi } from '/@/views/archivesManage/institution/timeService/consult/consult.api';
import ConsultModal from '/@/views/archivesManage/institution/timeService/consult/components/consultModal.vue';
import { useModal } from '/@/components/Modal';
import consult from '/@/assets/images/consult/consult.png';
import dayjs from 'dayjs';
const [registerModal, { openModal }] = useModal();
const loading = ref(true);
const serviceMap = ref();
const statisticsInfo = ref();
const mapList = ref();
const orgCode = ref();
const markerContent = ref(`<div style="white-space: nowrap;display: flex;background-color: #58a55c;padding: 5px 10px;border-radius: 10px;
align-items: center;">
<img src="${consult}" alt="" style="width: 20px;height: 20px; margin-right: 5px"/>
<span style="color: #ffffff">^&</span>
</div>`);
const labelContent = ref('');
onMounted(async () => {
await initList();
await statistics();
});
onBeforeUnmount(() => {
serviceMap.value.destroyMap();
});
async function statistics() {
const info = await statisticsApi({});
statisticsInfo.value = [
{ value: info?.hospitalSum, name: '医院数量' },
{ value: info?.specialistSum, name: '专家人数' },
{ value: info?.thisYearSessionSum, name: `${dayjs().format('YYYY')}年咨询人次` },
{ value: info?.allSessionSum, name: `2023年至今累计咨询人次` },
];
}
async function initList() {
try {
mapList.value = await listApi({ orgCode: orgCode.value === '1' ? '' : orgCode.value });
loading.value = false;
await nextTick(() => {
serviceMap.value.initMap(mapList.value);
});
} catch (e) {
loading.value = false;
await nextTick(() => {
serviceMap.value.initMap([]);
});
console.log(e);
}
}
function getMarkerInfo(e) {
let r = JSON.stringify(e);
console.log(r);
labelContent.value = `
<div style="padding: 10px;background-color: #ffffff;border-radius: 10px">
<div style="padding-top: 8px;font-weight: bold;font-size: 14px">专家人数:${e.specialistSum} </div>
<div style="padding-top: 8px;font-weight: bold;font-size: 14px">当年咨询人次:${e.thisYearSessionSum} </div>
<div style="padding-top: 8px;font-weight: bold;font-size: 14px">累计咨询人次:${e.allSessionSum} </div>
<div style="padding-top: 8px;text-align: center;color: #5087ec" onclick="getDetail()">查看咨询数据></div>
</div>
`;
serviceMap.value.setLabel(labelContent.value);
}
function getDetail(e) {
console.log(e);
openModal(true, {
record: e,
});
}
</script>
<style scoped lang="less">
.outer-d {
position: relative;
width: 100%;
height: 100%;
}
.top-title {
padding: 10px;
text-align: center;
font-size: 20px;
}
.search-d {
box-shadow: 2px 2px 0 0 rgba(0, 0, 0, 0.35);
border-radius: 5px;
background-color: #ffffff;
padding: 10px 20px;
position: absolute;
top: 10px;
left: 10px;
z-index: 99;
> :nth-child(n + 1) {
margin-left: 10px;
}
}
.bottom-d {
border-radius: 5px;
width: 100%;
position: absolute;
bottom: 8%;
left: 0;
z-index: 99;
display: flex;
align-items: center;
justify-content: center;
.bottom-d-item {
border-radius: 5px;
box-shadow: 2px 2px 4px 1px rgba(0, 0, 0, 0.35);
background-color: #ffffff;
font-weight: bold;
padding: 0 50px;
> div {
text-align: center;
&:nth-child(1) {
font-size: 18px;
padding: 10px 0 5px 0;
}
&:nth-child(2) {
padding: 5px 0 10px 0;
}
}
}
}
.map-d {
position: absolute;
width: calc(100% - 20px);
height: calc(100% - 70px);
top: 50px;
left: 10px;
}
</style>
@@ -0,0 +1,62 @@
<template>
<BasicModal @register="registerModal" title="服务数据" width="80%">
<div class="top-d-outer">
<div class="top-d">
<div>应急中心名称{{ hospitalInfo?.centerName || '-' }}</div>
<div>专业人员数量{{ hospitalInfo?.majNum }}</div>
<div>操作人员数量{{ hospitalInfo?.opNum }}</div>
<div>当年服务人次{{ hospitalInfo?.thisYearNum }}</div>
<div>累计服务人次{{ hospitalInfo?.totalYearNum }}</div>
</div>
</div>
<BasicTable @register="registerTable" table-type="1" />
</BasicModal>
</template>
<script setup lang="ts">
import BasicModal from '/@/components/Modal/src/BasicModal.vue';
import { useModalInner } from '/@/components/Modal';
import { ref } from 'vue';
import BasicTable from '/@/components/Table/src/BasicTable.vue';
import { useListPage } from '/@/hooks/system/useListPage';
import dayjs from 'dayjs';
import { listInfoApi } from '/@/views/archivesManage/institution/timeService/emergency/emergency.api';
import { columns } from '/@/views/archivesManage/institution/timeService/emergency/emergency.data';
const hospitalInfo = ref({});
const [registerModal, { setModalProps }] = useModalInner(async (data) => {
setModalProps({ showCancelBtn: false, showOkBtn: false });
hospitalInfo.value = data.record;
setProps({ searchInfo: { centerId: hospitalInfo.value.centerId } });
await reload({ page: 1 });
});
const { tableContext } = useListPage({
tableProps: {
api: listInfoApi,
columns,
canResize: false,
orderFlag: false,
immediate: false,
showIndexColumn: true,
useSearchForm: false,
showTableSetting: false,
showActionColumn: false,
},
});
const [registerTable, { setProps, reload }] = tableContext;
</script>
<style scoped lang="less">
.top-d-outer {
padding: 10px;
.top-d {
border-bottom: 1px dashed #cecece;
display: flex;
padding: 0 0 10px;
> div {
margin-right: 50px;
}
}
}
</style>
@@ -0,0 +1,9 @@
import { defHttp } from '/@/utils/http/axios';
enum Api {
list = '/health-emergency/archives/emergency/centers',
listInfo = '/health-emergency/emergency/order/list',
}
export const listApi = (params: any) => defHttp.get({ url: Api.list, params });
export const listInfoApi = (params: any) => defHttp.get({ url: Api.listInfo, params });
@@ -0,0 +1,170 @@
import { BasicColumn } from '/@/components/Table';
import { render } from '/@/utils/common/renderUtils';
export const columns: BasicColumn[] = [
{
title: '被救助人',
align: 'center',
dataIndex: 'salvageUserName',
fixed: 'left',
},
{
title: '单位',
align: 'center',
dataIndex: 'salvageUserSecondDepart',
},
{
title: '部门',
align: 'center',
dataIndex: 'salvageUserDepart',
},
{
title: '所属应急中心',
dataIndex: 'centerName',
},
{
title: '求助方式',
align: 'center',
dataIndex: 'initiatorType',
customRender: ({ text }) => {
return text == '0' ? '120' : text == '1' ? '应急就医' : '';
},
},
{
title: '求助时间',
align: 'center',
dataIndex: 'createTime',
},
{
title: '员工位置',
align: 'center',
dataIndex: 'initiatorLongitude',
customRender: ({ record }) => {
// @ts-ignore
const { initiatorLongitude, initiatorLatitude } = record;
if (!initiatorLongitude || !initiatorLatitude) {
return '';
}
return `${initiatorLongitude.toFixed(4)}${initiatorLatitude.toFixed(4)}`;
},
},
{
title: '专业人员',
align: 'center',
dataIndex: 'majorUserName',
},
{
title: '操作人员',
align: 'center',
dataIndex: 'operationUserName',
},
{
title: '驻场人员',
align: 'center',
dataIndex: 'stationUserName',
},
{
title: '工单状态',
align: 'center',
dataIndex: 'orderStatus',
customRender: ({ text }) => render.renderDict(text, 'emergency_order_status'),
},
// {
// title: '派单状态',
// align: 'center',
// dataIndex: 'isDispatch',
// customRender: ({ text }) => render.renderDict(text, 'emergency_order_send_status').children,
// },
// {
// title: '操作人员电话',
// align:"center",
// dataIndex: 'operationUserMobile'
// },
// {
// title: '专业人员电话',
// align:"center",
// dataIndex: 'majorUserMobile'
// },
// {
// title: '驻场人员',
// align: 'center',
// dataIndex: 'stationUserName',
// },
// {
// title: '驻场人员部门',
// align:"center",
// dataIndex: 'stationUserDepart'
// },
// {
// title: '驻场人员派单业务',
// align:"center",
// dataIndex: 'stationBusiness'
// },
// {
// title: '操作人员响应时间',
// align:"center",
// dataIndex: 'operationResponseTime',
// customRender:({text}) =>{
// return !text?"":(text.length>10?text.substr(0,10):text)
// },
// },
// {
// title: '操作人员派单时间',
// align:"center",
// dataIndex: 'operationSendOrderTime',
// customRender:({text}) =>{
// return !text?"":(text.length>10?text.substr(0,10):text)
// },
// },
// {
// title: '专业人员响应时间',
// align:"center",
// dataIndex: 'majorResponseTime',
// customRender:({text}) =>{
// return !text?"":(text.length>10?text.substr(0,10):text)
// },
// },
// {
// title: '驻场人员响应时间',
// align:"center",
// dataIndex: 'stationResponseTime',
// customRender:({text}) =>{
// return !text?"":(text.length>10?text.substr(0,10):text)
// },
// },
// {
// title: '驻场人员拒单时间',
// align:"center",
// dataIndex: 'stationRejectionTime',
// customRender:({text}) =>{
// return !text?"":(text.length>10?text.substr(0,10):text)
// },
// },
// {
// title: '专业人员救助意见',
// align:"center",
// dataIndex: 'majorSalvageOpinion'
// },
// {
// title: '派单医院',
// align:"center",
// dataIndex: 'sendOrderHospital'
// },
// {
// title: '应急单状态',
// align:"center",
// dataIndex: 'orderStatus',
// customRender: ({ text }) => {
// return render.renderDict(text, 'emergency_order_status');
// },
// },
// {
// title: '是否派单',
// align:"center",
// dataIndex: 'isDispatch',
// customRender: ({ text }) => {
// return text == false ? '否' : text == true ? '是' : '';
// },
// },
];
@@ -0,0 +1,181 @@
<template>
<div class="outer-d">
<div class="top-title"> 长庆油田应急中心 </div>
<div class="map-d">
<template v-if="!loading">
<service-map
ref="serviceMap"
:marker-content="markerContent"
:label-content="labelContent"
field="centerName"
@get-marker-info="getMarkerInfo"
@get-detail="getDetail"
id="centerId"
/>
<div class="bottom-d">
<div class="bottom-d-item" v-for="(item, index) in statisticsInfo" :key="`bottom-d-item-${index}`">
<div>{{ item.value }}</div>
<div>{{ item.name }}</div>
</div>
</div>
</template>
<template v-else>
<div style="width: 100%; height: 100%; display: flex; align-items: center; justify-content: center">
<a-spin size="large" />
</div>
</template>
</div>
</div>
<consult-modal @register="registerModal" />
</template>
<script setup lang="ts">
import ServiceMap from '/@/views/archivesManage/institution/timeService/components/serviceMap.vue';
import { onMounted, ref, nextTick, onBeforeUnmount } from 'vue';
import { listApi } from '/@/views/archivesManage/institution/timeService/emergency/emergency.api';
import ConsultModal from '/@/views/archivesManage/institution/timeService/emergency/components/emergencyModal.vue';
import { useModal } from '/@/components/Modal';
import emergency from '/@/assets/images/emergency.png';
const [registerModal, { openModal }] = useModal();
const loading = ref(true);
const serviceMap = ref();
const statisticsInfo = ref<any[]>([]);
const mapList = ref();
const markerContent = ref(`<div style="white-space: nowrap;display: flex;background-color: #58a55c;padding: 5px 10px;border-radius: 10px;
align-items: center;">
<img src="${emergency}" alt="" style="width: 20px;height: 20px; margin-right: 5px"/>
<span style="color: #ffffff">^&</span>
</div>`);
const labelContent = ref('');
onMounted(async () => {
await initList();
});
onBeforeUnmount(() => {
serviceMap.value.destroyMap();
});
async function initList() {
try {
let majNum = 0;
let opNum = 0;
let thisYearNum = 0;
let totalYearNum = 0;
mapList.value = (await listApi({})).map((item) => {
majNum += item.majNum;
opNum += item.opNum;
thisYearNum += item.thisYearNum;
totalYearNum += item.totalYearNum;
return item;
});
statisticsInfo.value = [
{ value: mapList.value.length, name: '应急中心数量' },
{ value: majNum, name: '专业人员数量' },
{ value: opNum, name: '操作人员数量' },
{ value: thisYearNum, name: '当年应急人次' },
{ value: totalYearNum, name: '累计应急人次' },
];
loading.value = false;
await nextTick(() => {
serviceMap.value.initMap(mapList.value);
});
} catch (e) {
loading.value = false;
await nextTick(() => {
serviceMap.value.initMap([]);
});
console.log(e);
}
}
function getMarkerInfo(e) {
labelContent.value = `
<div style="padding: 10px;background-color: #ffffff;border-radius: 10px">
<div style="padding-top: 8px;font-weight: bold;font-size: 14px">专业人员数量:${e.majNum} </div>
<div style="padding-top: 8px;font-weight: bold;font-size: 14px">操作人员数量:${e.opNum} </div>
<div style="padding-top: 8px;font-weight: bold;font-size: 14px">当年应急人次:${e.thisYearNum} </div>
<div style="padding-top: 8px;font-weight: bold;font-size: 14px">累计应急人次:${e.totalYearNum} </div>
<div style="padding-top: 8px;text-align: center;color: #5087ec" onclick="getDetail()">查看应急数据></div>
</div>
`;
serviceMap.value.setLabel(labelContent.value);
}
function getDetail(e) {
openModal(true, {
record: e,
});
}
</script>
<style scoped lang="less">
.outer-d {
position: relative;
width: 100%;
height: 100%;
}
.top-title {
padding: 10px;
text-align: center;
font-size: 20px;
}
.search-d {
box-shadow: 2px 2px 0 0 rgba(0, 0, 0, 0.35);
border-radius: 5px;
background-color: #ffffff;
padding: 10px 20px;
position: absolute;
top: 10px;
left: 10px;
z-index: 99;
> :nth-child(n + 1) {
margin-left: 10px;
}
}
.bottom-d {
border-radius: 5px;
width: 100%;
position: absolute;
bottom: 8%;
left: 0;
z-index: 99;
display: flex;
align-items: center;
justify-content: center;
> :nth-child(n + 1) {
margin-left: 50px;
}
.bottom-d-item {
border-radius: 5px;
box-shadow: 2px 2px 4px 1px rgba(0, 0, 0, 0.35);
background-color: #ffffff;
font-weight: bold;
padding: 0 50px;
> div {
text-align: center;
&:nth-child(1) {
font-size: 18px;
padding: 10px 0 5px 0;
}
&:nth-child(2) {
padding: 5px 0 5px 0;
}
}
}
}
.map-d {
position: absolute;
width: calc(100% - 20px);
height: calc(100% - 70px);
top: 50px;
left: 10px;
}
</style>
@@ -0,0 +1,48 @@
<template>
<BasicModal @register="registerModal" :title="title" width="80%">
<BasicTable @register="registerTable" />
</BasicModal>
</template>
<script setup lang="ts">
import BasicModal from '/@/components/Modal/src/BasicModal.vue';
import { useModalInner } from '/@/components/Modal';
import { useListPage } from '/@/hooks/system/useListPages';
import BasicTable from '/@/components/Table/src/BasicTable.vue';
import { abnormalApi } from '/@/views/archivesManage/institution/timeService/environment/environment.api';
import { ref } from 'vue';
import { columns } from '/@/views/archivesManage/institution/timeService/environment/environment.data';
const title = ref('');
const [registerModal, { setModalProps }] = useModalInner(async (data) => {
setModalProps({ showOkBtn: false, cancelText: '关闭' });
setProps({ searchInfo: { type: data?.type, orgCode: data?.orgCode } });
title.value = data.title;
await reload();
});
const { tableContext } = useListPage({
tableProps: {
api: abnormalApi,
pageTitle: '长庆油田已封存员工',
columns,
canResize: false,
immediate: false,
btnArr: ['edit', 'delete'],
btnArrText: { add: '新增封存员工', search: '列表查询', export: '信息导出', print: '信息打印' },
useSearchForm: false,
showIndexColumn: true,
indexColumnProps: {
dataIndex: 'listIndex',
width: 70,
},
actionColumn: {
width: 120,
fixed: 'right',
},
},
});
const [registerTable, { setProps, reload }, {}] = tableContext;
</script>
<style scoped lang="less"></style>
@@ -0,0 +1,11 @@
import { defHttp } from '/@/utils/http/axios';
enum Api {
list = '/health-intervene/archive/environment/map-list',
statistics = '/health-intervene/archive/environment/statistics',
abnormal = '/health-intervene/archive/environment/abnormal/page',
}
export const listApi = (params: any) => defHttp.get({ url: Api.list, params });
export const statisticsApi = (params: any) => defHttp.get({ url: Api.statistics, params });
export const abnormalApi = (params: any) => defHttp.get({ url: Api.abnormal, params });
@@ -0,0 +1,27 @@
import { BasicColumn } from '/@/components/Table';
export const columns: BasicColumn[] = [
{
title: '设备所属单位',
dataIndex: 'orgName',
},
{
title: '设备名称',
dataIndex: 'meterName',
},
{
title: '设备编码',
dataIndex: 'meterCode',
},
{
title: '设备地址',
dataIndex: 'meterAddress',
},
{
title: '最后一次回传数据时间',
dataIndex: 'lastMonitorDate',
customRender: ({ text }) => {
return text || '-';
},
},
];
@@ -0,0 +1,604 @@
<template>
<div class="outer-d">
<div class="top-title"> 长庆油田环境监测设备 </div>
<div class="map-d">
<template v-if="!loading">
<service-map
ref="serviceMap"
:map-list="mapList"
:marker-content="null"
:label-content="labelContent"
@get-marker-info="getMarkerInfo"
@get-detail="getDetail"
id="meterId"
/>
<div class="search-d">
所属单位
<ApiSelect
:value="orgCode"
:api="allSecondaryDepartsNew"
@change="
(v) => {
orgCode = v;
}
"
resultField="result"
labelField="departName"
valueField="orgCode"
:after-fetch="
(data) => {
data.unshift({
orgCode: '1',
departName: '全部单位',
});
return data;
}
"
:allowClear="true"
:show-default-value="false"
:immediate="true"
:showSearch="true"
:filterOption="
(input: string, option: any): boolean => {
const str: string = input.toLowerCase();
return option.label.toLowerCase().indexOf(str) >= 0;
}"
style="width: 260px"
/>
<a-button type="primary" @click="searchB">查询</a-button>
<a-button style="background-color: #a4adb3; color: #ffffff" @click="resetB">重置</a-button>
</div>
<div class="z" v-if="radioValue !== '3'">
<div class="inner" v-if="radioValue === '0'"></div>
<div class="outer" v-if="radioValue === '1'"></div>
<div class="water" v-if="radioValue === '3'"></div>
<div class="pollen" v-if="radioValue === '2'"></div>
<div class="z-d">
<div v-for="(item, index) in describe[radioValue]" :key="`describe${index}`">
{{ item }}
</div>
</div>
</div>
<div class="z" style="height: 100px; flex-direction: column; justify-content: space-around" v-else>
<div style="display: flex; color: #ffffff; font-weight: bold">
<div style="background-color: #00ff1a; width: 20px; height: 20px; border-radius: 6px; margin-right: 10px"></div>
<div>达标</div>
</div>
<div style="display: flex; color: #ffffff; font-weight: bold">
<div style="background-color: #ec0808; width: 20px; height: 20px; border-radius: 6px; margin-right: 10px"></div>
<div>不达标</div>
</div>
</div>
<a-radio-group v-model:value="radioValue" button-style="solid" class="radio-group-d" @change="initList">
<a-radio-button value="0">室内环境</a-radio-button>
<a-radio-button value="1">室外环境</a-radio-button>
<a-radio-button value="3">水质监测</a-radio-button>
<a-radio-button value="2">过敏源监测</a-radio-button>
</a-radio-group>
<div class="bottom-d">
<div
class="bottom-d-item"
v-for="(item, index) in deviceInfo"
:key="`bottom-d-item-${index}`"
:style="{ margin: index === 1 ? '0 25px 0 50px' : index === 2 ? '0 50px 0 25px' : 0 }"
>
<div>
<span>运行正常:{{ item.working }}台</span>
<span
style="color: red; cursor: pointer"
@click="
() => {
openModalA(true, {
type: item?.value,
title: item?.name,
orgCode: orgCode === '1' ? '' : orgCode,
});
}
"
>异常列表</span
>
</div>
<div style="font-size: 30px; font-weight: bold; display: flex; justify-content: center">
<div style="position: relative">
{{ item.sum }}
<span style="font-size: 16px; margin-left: -7px">台</span>
</div>
</div>
<div style="display: flex; justify-content: center; align-items: center">
<div style="position: relative">
<div style="display: flex; align-items: center; position: absolute; top: 0; height: 100%; left: -30px">
<img :src="imgInfos[index]" alt="" style="width: 25px; height: 23px" />
</div>
{{ item?.name }}
</div>
</div>
</div>
</div>
</template>
<template v-else>
<div style="width: 100%; height: 100%; display: flex; align-items: center; justify-content: center">
<a-spin size="large" />
</div>
</template>
</div>
</div>
<environment-modal
@register="registerModal"
@close-modal="
() => {
count++;
}
"
/>
<environment-abnormal-modal @register="registerModalA" />
</template>
<script setup lang="ts">
import ServiceMap from '/@/views/archivesManage/institution/timeService/components/serviceMap.vue';
import { nextTick, onMounted, ref, onBeforeUnmount } from 'vue';
import { listApi, statisticsApi } from '/@/views/archivesManage/institution/timeService/environment/environment.api';
import { getName } from '/@/views/interveneNew/compoents/utils';
import ApiSelect from '/@/components/Form/src/components/ApiSelect.vue';
import { allSecondaryDepartsNew } from '/@/utils/orgSearchInfo';
import EnvironmentModal from '/@/views/archivesManage/institution/timeService/components/environmentModal.vue';
import { useModal } from '/@/components/Modal';
import innerPng from '/@/assets/images/enviroment/institution/inner.png';
import outerPng from '/@/assets/images/enviroment/institution/outer.png';
import waterPng from '/@/assets/images/enviroment/institution/water.png';
import pollenPng from '/@/assets/images/enviroment/institution/pollen.png';
import innerPngS from '/@/assets/images/enviroment/institution/inner-s.png';
import outerPngS from '/@/assets/images/enviroment/institution/outer-s.png';
import waterPngS from '/@/assets/images/enviroment/institution/water-s.png';
import pollenPngS from '/@/assets/images/enviroment/institution/pollen-s.png';
import EnvironmentAbnormalModal from '/@/views/archivesManage/institution/timeService/environment/components/environmentAbnormalModal.vue';
const [registerModal, { openModal }] = useModal();
const [registerModalA, { openModal: openModalA }] = useModal();
const imgInfo = ref({
'0': innerPng,
'1': outerPng,
'3': waterPng,
'2': pollenPng,
});
const imgInfos = ref({
0: innerPngS,
1: outerPngS,
2: waterPngS,
3: pollenPngS,
});
const describe = ref({
'0': ['清洁', '未污染', '轻污染', '中污染', '重污染'],
'1': ['优', '良', '轻度污染', '中度污染', '重度污染', '严重污染'],
'3': ['达标', '不达标'],
'2': ['很低', '低', '中等', '高', '很高'],
});
const colorInfo = ref({
'0': [
{ value: 1, label: '#66c706' },
{ value: 2, label: '#f5cc27' },
{ value: 3, label: '#fe8700' },
{ value: 4, label: '#e60707' },
{ value: 5, label: '#940654' },
],
'1': [
{ value: 1, label: '#66c706' },
{ value: 2, label: '#f5cc27' },
{ value: 3, label: '#fe8700' },
{ value: 4, label: '#e60707' },
{ value: 5, label: '#65001c' },
{ value: 6, label: '#65001c' },
],
'3': [
{ value: 0, label: '#ec0808' },
{ value: 1, label: '#66c706' },
],
'2': [
{ value: 1, label: '#66c706' },
{ value: 2, label: '#f5cc27' },
{ value: 3, label: '#fe8700' },
{ value: 4, label: '#e60707' },
{ value: 5, label: '#65001c' },
],
});
const level = ref({
'0': [
{ value: 1, label: 'I级' },
{ value: 2, label: 'II级' },
{ value: 3, label: 'III级' },
{ value: 4, label: 'IV级' },
{ value: 5, label: 'V级' },
],
'1': [
{ value: 1, label: '一级' },
{ value: 2, label: '二级' },
{ value: 3, label: '三级' },
{ value: 4, label: '四级' },
{ value: 5, label: '五级' },
{ value: 5, label: '六级' },
],
'3': [
{ value: 0, label: '不合格' },
{ value: 1, label: '合格' },
],
});
const loading = ref(true);
const radioValue = ref('0');
const serviceMap = ref();
const mapList = ref<any[]>([]);
const labelContent = ref('');
const deviceInfo = ref({});
const orgCode = ref('1');
const count = ref(0);
function getMarkerInfo(e) {
let color = e.newData ? getName(e.newData.score, colorInfo.value[radioValue.value]) : 'red';
switch (radioValue.value) {
case '0':
labelContent.value = innerDoor(e, color);
break;
case '1':
labelContent.value = outerDoor(e, color);
break;
case '3':
labelContent.value = water(e, color);
break;
case '2':
labelContent.value = pollen(e, color);
break;
}
serviceMap.value.setLabel(labelContent.value);
}
function innerDoor(e, color) {
return `
<div style="background-color: #ffffff; padding: 10px; border-radius: 10px;z-index: 999">
<div style="display: flex; align-items: center">
<div>
<div style="font-size: 18px; font-weight: bold">${e.meterName}</div>
<div style="margin-top: 10px">单位名称:${e.orgName || '-'}</div>
<div style="margin-top: 10px">综合指数:${e.newData ? Math.round(e.newData.compositeIndex * 100) / 100 : '-'}</div>
<div style="margin-top: 10px;">空气质量级别:${e.newData ? getName(e.newData.score, level.value[radioValue.value]) : '-'}</div>
<div style="margin-top: 10px;">更新时间:${e.lastMonitorDate || '-'}</div>
</div>
<div style="margin-left: 10px">
<div style="padding: 30px; border-radius: 50%; border: 5px solid ${color}">
<div style="text-align: center; font-size: 12px">空气质量</div>
<div style="text-align: center; font-size: 19px; font-weight: bold; color: green;margin-top: 10px">
${e.newData?.score_dictText || '-'}
</div>
</div>
</div>
</div>
<div style="padding: 5px 0 0;
border-top: 1px dashed #cecece;
margin: 5px 0 0;">
设备地址:${e?.meterAddress || '-'}
</div>
<div style="text-align: center;height: 30px;line-height: 30px;" onclick="getDetail('${e.meterCode}')">
<span style="cursor: pointer;color: #2189ff">查看监测数据></span>
</div>
</div>
`;
}
function outerDoor(e, color) {
return `
<div style="background-color: #ffffff; padding: 10px; border-radius: 10px;z-index: 999">
<div style="display: flex; align-items: center">
<div>
<div style="font-size: 18px; font-weight: bold">${e.meterName}</div>
<div style="margin-top: 10px">单位名称:${e.orgName || '-'}</div>
<div style="margin-top: 10px">空气质量指数(AQI)${e?.newData?.compositeIndex || '-'}</div>
<div style="margin-top: 10px;">空气质量级别:${e.newData ? getName(e.newData.score, level.value[radioValue.value]) : '-'}</div>
<div style="margin-top: 10px;">更新时间:${e.lastMonitorDate || '-'}</div>
</div>
<div style="margin-left: 10px">
<div style="padding: 30px; border-radius: 50%; border: 5px solid ${color}">
<div style="text-align: center; font-size: 12px">空气质量评价</div>
<div style="text-align: center; font-size: 19px; font-weight: bold; color: green;margin-top: 10px">
${e.newData?.score_dictText || '-'}
</div>
</div>
</div>
</div>
<div style="padding: 5px 0 0;
border-top: 1px dashed #cecece;
margin: 5px 0 0;">
设备地址:${e?.meterAddress || '-'}
</div>
<div style="text-align: center;height: 30px;line-height: 30px;" onclick="getDetail('${e.meterCode}')">
<span style="cursor: pointer;color: #2189ff">查看监测数据></span>
</div>
</div>
`;
}
function water(e, color) {
return `
<div style="background-color: #ffffff; padding: 10px; border-radius: 10px;z-index: 999">
<div style="display: flex; align-items: center">
<div>
<div style="font-size: 18px; font-weight: bold">${e.meterName}</div>
<div style="margin-top: 10px">单位名称:${e.orgName || '-'}</div>
<div style="margin-top: 10px">TDS${e.newData?.tds || '-'}</div>
<div style="margin-top: 10px;">PH${e.newData?.ph || '-'}</div>
<div style="margin-top: 10px;">电导率:${e.newData?.conductivity || '-'}</div>
<div style="margin-top: 10px;">更新时间:${e.lastMonitorDate || '-'}</div>
</div>
<div style="margin-left: 10px">
<div style="padding: 30px; border-radius: 50%; border: 5px solid ${color}">
<div style="text-align: center; font-size: 12px">空气质量评价</div>
<div style="text-align: center; font-size: 19px; font-weight: bold; color: green;margin-top: 10px">
${e.newData?.score_dictText || '-'}
</div>
</div>
</div>
</div>
<div style="padding: 5px 0 0;
border-top: 1px dashed #cecece;
margin: 5px 0 0;">
设备地址:${e?.meterAddress || '-'}
</div>
<div style="text-align: center;height: 30px;line-height: 30px;" onclick="getDetail('${e.meterCode}')">
<span style="cursor: pointer;color: #2189ff">查看监测数据></span>
</div>
</div>
`;
}
function pollen(e, color) {
return `
<div style="background-color: #ffffff; padding: 10px; border-radius: 10px;z-index: 999">
<div style="display: flex; align-items: center">
<div>
<div style="font-size: 18px; font-weight: bold">${e.meterName}</div>
<div style="margin-top: 10px">单位名称:${e.orgName || '-'}</div>
<div style="margin-top: 10px">花粉浓度:${e.newData?.totalPollen || '-'}</div>
<div style="margin-top: 10px;">主要过敏源:${e.newData?.majorAllergens || '-'}</div>
<div style="margin-top: 10px;">更新时间:${e.lastMonitorDate || '-'}</div>
</div>
<div style="margin-left: 10px">
<div style="padding: 30px; border-radius: 50%; border: 5px solid ${color}">
<div style="text-align: center; font-size: 12px">空气质量评价</div>
<div style="text-align: center; font-size: 19px; font-weight: bold; color: green;margin-top: 10px">
${e.newData?.score_dictText || '-'}
</div>
</div>
</div>
</div>
<div style="padding: 5px 0 0;
border-top: 1px dashed #cecece;
margin: 5px 0 0;">
设备地址:${e?.meterAddress || '-'}
</div>
<div style="text-align: center;height: 30px;line-height: 30px;" onclick="getDetail('${e.meterCode}')">
<span style="cursor: pointer;color: #2189ff">查看监测数据></span>
</div>
</div>
`;
}
function getDetail(e) {
openModal(true, {
typeId: radioValue.value,
meterCode: e.meterCode,
});
}
onMounted(async () => {
await initList();
loading.value = false;
await nextTick(() => {
serviceMap.value.initMap(mapList.value);
});
await getStatistics();
});
onBeforeUnmount(() => {
serviceMap.value.destroyMap();
});
async function getStatistics() {
try {
const res = await statisticsApi({ orgCode: orgCode.value === '1' ? '' : orgCode.value });
deviceInfo.value = [
{
sum: res?.indoorMeterSum || 0,
working: res?.indoorMeterWorking || 0,
name: '室内环境',
value: '0',
},
{
sum: res?.outdoorMeterSum || 0,
working: res?.outdoorMeterWorking || 0,
name: '室外环境',
value: '1',
},
{
sum: res?.waterQualityMeterSum || 0,
working: res?.waterQualityMeterWorking || 0,
name: '水质监测设备',
value: '3',
},
{
sum: res?.pollenMeterSum || 0,
working: res?.pollenMeterWorking || 0,
name: '过敏源监测设备',
value: '2',
},
];
} catch (e) {
console.log(e);
}
}
async function initList() {
try {
mapList.value = (await listApi({ type: radioValue.value, orgCode: orgCode.value === '1' ? '' : orgCode.value })).map((item) => {
let color = item.newData ? getName(item.newData.score, colorInfo.value[radioValue.value]) : 'red';
item['img'] = imgInfo.value[radioValue.value];
item[
'markerContent'
] = `<div style="white-space: nowrap;display: flex;background-color: ${color};padding: 5px 10px;border-radius: 10px">
<img src="${imgInfo.value[radioValue.value]}" alt="" style="width: 20px;height: 20px; margin-right: 5px"/>
<span style="color: #ffffff">${
item.newData
? item.newData.score_dictText +
(radioValue.value !== '3' ? '(' + item.newData[radioValue.value !== '2' ? 'compositeIndex' : 'totalPollen'] + ')' : '')
: '暂无数据'
}</span>
</div>`;
return item;
});
} catch (e) {
mapList.value = [];
console.log(e);
}
}
async function searchB() {
await initList();
await getStatistics();
}
async function resetB() {
orgCode.value = '1';
await searchB();
}
</script>
<style scoped lang="less">
.outer-d {
position: relative;
width: 100%;
height: 100%;
}
.top-title {
padding: 10px;
text-align: center;
font-size: 20px;
}
.search-d {
box-shadow: 2px 2px 0 0 rgba(0, 0, 0, 0.35);
border-radius: 5px;
background-color: #ffffff;
padding: 10px 20px;
position: absolute;
top: 10px;
left: 10px;
z-index: 99;
> :nth-child(n + 1) {
margin-left: 10px;
}
}
.radio-group-d {
position: absolute;
top: 20px;
right: 10px;
z-index: 99;
}
.bottom-d {
border-radius: 5px;
width: 100%;
position: absolute;
bottom: 8%;
left: 0;
z-index: 99;
display: flex;
align-items: center;
justify-content: center;
.bottom-d-item {
width: 200px;
border-radius: 5px;
box-shadow: 2px 2px 4px 1px rgba(0, 0, 0, 0.35);
background-color: #ffffff;
> div {
width: 100%;
text-align: center;
&:nth-child(1) {
color: green;
text-align: left;
padding: 5px 15px 0;
display: flex;
justify-content: space-between;
}
&:nth-child(2) {
font-size: 18px;
font-weight: bold;
padding: 3px 0 3px 0;
}
&:nth-child(3) {
font-size: 18px;
font-weight: bold;
padding: 0 0 10px 0;
}
}
}
}
.map-d {
position: absolute;
width: calc(100% - 20px);
height: calc(100% - 70px);
top: 50px;
left: 10px;
}
.z {
position: absolute;
top: 100px;
right: 50px;
background-color: rgba(#8c8c8c, 0.5);
border-radius: 10px;
padding: 10px 30px 10px 10px;
display: flex;
height: 300px;
.inner {
width: 15px;
height: 100%;
border-radius: 7px;
background: linear-gradient(#02fb1b 0, #2ab539 25%, #f7e303 50%, #fe8a02 75%, #ed1507 100%);
}
.outer {
width: 15px;
height: 100%;
border-radius: 7px;
background: linear-gradient(#0efe19 0, #fed302 20%, #fe8702 40%, #ef1b07 60%, #962e91 80%, #3d095f 100%);
}
.pollen {
width: 15px;
height: 100%;
border-radius: 7px;
background: linear-gradient(#01fbe2 0, #36ce20 25%, #f3df04 50%, #fe8902 75%, #ed1008 100%);
}
.z-d {
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
padding-left: 8px;
color: #ffffff;
font-weight: bold;
}
}
:deep(.ant-radio-button-wrapper) {
color: #1684fc !important;
border-color: #1684fc !important;
&:before {
background-color: #1684fc !important;
}
}
:deep(.ant-radio-button-wrapper-checked) {
color: #ffffff !important;
}
</style>
@@ -0,0 +1,9 @@
import { defHttp } from '/@/utils/http/axios';
enum Api {
knowledgePage = '/health-intervene/archive/knowledge/browse-top',
knowledgeStat = '/health-intervene/archive/knowledge/statistics',
}
export const knowledgePageApi = (params) => defHttp.get({ url: Api.knowledgePage, params });
export const knowledgeStatApi = (params) => defHttp.get({ url: Api.knowledgeStat, params });
@@ -0,0 +1,49 @@
import { BasicColumn, FormSchema } from '/@/components/Table';
export const columns: BasicColumn[] = [
{
title: '资源名称',
dataIndex: 'title',
width: 120,
align: 'center',
},
{
title: '内容简介',
dataIndex: 'content',
width: 200,
align: 'center',
},
{
title: '所属类别',
dataIndex: 'type_dictText',
width: 80,
align: 'center',
},
{
title: '主讲人姓名',
dataIndex: 'keynoteSpeaker',
width: 120,
align: 'center',
},
{
title: '主讲人所在机构',
dataIndex: 'speakerDept',
width: 120,
align: 'center',
},
{
title: '职务职称',
dataIndex: 'duty',
width: 120,
align: 'center',
},
{
title: '浏览人次',
dataIndex: 'browseCount',
width: 120,
align: 'center',
sorter: {
compare: (a, b) => a.browseCount - b.browseCount,
},
},
];
@@ -0,0 +1,156 @@
<template>
<BasicTables @register="registerTable">
<template #btnTop>
<div class="all-stat">
<span>
视频知识总数量{{ allData?.videoCount ? allData?.videoCount : '--' }} 浏览总人数{{
allData?.videoBrowse ? allData?.videoBrowse : '--'
}}
</span>
<span>
图文知识总数量{{ allData?.imageCount ? allData?.imageCount : '--' }} 浏览总人数{{
allData?.imageBrowse ? allData?.imageBrowse : '--'
}}
</span>
</div>
<div>
<p class="title-p">知识浏览次数排名</p>
<a-radio-group v-model:value="dataType" button-style="solid" @change="chooseData">
<a-radio-button :value="1">视频知识</a-radio-button>
<a-radio-button :value="0">图文知识</a-radio-button>
<!-- <a-radio-button :value="2">音频知识</a-radio-button>-->
</a-radio-group>
</div>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex == 'title'">
<a-button type="link" @click="handleClick(record)">{{ record.title }}</a-button>
</template>
</template>
</BasicTables>
<div style="display: none">
<ImagePreviewGroup v-if="imgList && imgList.length > 0" :preview="{ visible, onVisibleChange: (vis) => (visible = vis) }">
<Image v-for="(item, index) in imgList" :key="index" :width="200" :fallback="getFamaleDefaultImage()" :src="getFileAccessHttpUrl(item)" />
</ImagePreviewGroup>
</div>
<video-preview ref="videoPreviewRef" :url="videoUrl" />
</template>
<script setup lang="ts">
import BasicTables from '/@/components/Table/src/BasicTables.vue';
import { useListPage } from '/@/hooks/system/useListPages';
import { columns } from '/@/views/archivesManage/institution/timeService/knowledge/knowledge.data';
import { knowledgePageApi, knowledgeStatApi } from '/@/views/archivesManage/institution/timeService/knowledge/knowledge.api';
import { ref, nextTick, onMounted } from 'vue';
import { CaretUpOutlined, CaretDownOutlined } from '@ant-design/icons-vue';
import VideoPreview from '/@/components/Video/VideoPreview.vue';
import { Image, ImagePreviewGroup, message } from 'ant-design-vue';
import { getFamaleDefaultImage, getFileAccessHttpUrl } from '/@/utils/common/compUtils';
const videoPreviewRef = ref();
const dataType = ref(1);
const imgList = ref<string[]>([]);
const visible = ref(false);
const { tableContext } = useListPage({
tableProps: {
pageTitle: '长庆油田知识资源',
btnArr: ['add', 'search', 'edit', 'delete', 'export', 'total', 'print'],
api: knowledgePageApi,
columns: columns,
beforeFetch: (params) => {
params.type = dataType.value;
return params;
},
canResize: false,
showIndexColumn: true,
indexColumnProps: {
dataIndex: 'listIndex',
width: 70,
},
actionColumn: {
width: 120,
fixed: 'right',
},
},
});
const [registerTable, { reload }] = tableContext;
const allData = ref();
onMounted(async () => {
allData.value = await knowledgeStatApi({});
});
function chooseData() {
reload();
}
function handleClick(record) {
console.log(record);
switch (dataType.value) {
case 1:
previewResources(record?.videoUrl);
break;
case 2:
const audio = new Audio('13203.wav');
audio.play();
break;
default:
handleAva(record?.titlePic);
break;
}
}
function handleAva(images) {
nextTick(() => {
imgList.value = images?.split(',');
visible.value = true;
});
}
const videoUrl = ref('');
const showVideo = ref(false);
function previewResources(url: string) {
if (!url) return message.info('未找到文件资源');
videoUrl.value = url && url.indexOf('http') !== -1 ? url : getFileAccessHttpUrl(url);
videoPreviewRef.value && videoPreviewRef.value.showPreview();
showVideo.value = true;
}
</script>
<style lang="less" scoped>
.all-stat {
display: flex;
justify-content: space-around;
background: #fff;
padding: 10px;
border: 1px solid #e9e9e9;
span {
border-right: 1px solid #e9e9e9;
flex: 1;
text-align: center;
&:last-child {
border-right: none;
}
}
}
.title-p {
font-weight: bold;
font-size: 16px;
margin-top: 10px;
}
.count {
position: relative;
.lined-arr {
display: flex;
flex-direction: column;
/* margin-left: 27px; */
right: 10px;
position: absolute;
top: -2px;
}
}
:deep(.ant-radio-button-wrapper) {
color: #1684fc !important;
border-color: #1684fc !important;
&:before {
background-color: #1684fc !important;
}
}
:deep(.ant-radio-button-wrapper-checked) {
color: #ffffff !important;
}
</style>
@@ -0,0 +1,194 @@
<template>
<BasicModal @register="registerModal" title="服务数据" width="80%">
<div class="top-d-outer">
<div class="top-d">
<div>医院名称{{ hospitalInfo?.centerName || '-' }}</div>
<div>医护人员数量{{ hospitalInfo?.medicalCareNum }}</div>
<div>当年服务人次{{ hospitalInfo?.thisYearNum }}</div>
<div>累计服务人次{{ hospitalInfo?.totalYearNum }}</div>
</div>
</div>
<div style="display: flex; justify-content: flex-end; padding-right: 5px">
<a-radio-group v-model:value="radioValue" button-style="solid" class="radio-group-d" @change="changeRadio">
<a-radio-button value="0">就诊服务</a-radio-button>
<a-radio-button value="1">随访服务</a-radio-button>
</a-radio-group>
</div>
<BasicTable @register="registerTable" table-type="1">
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'nr1'">
<a-button
type="link"
@click="
() => {
openDrawer(true, {
// record: res,
userInfo: record,
isUpdate: true,
showFooter: false,
type: '详情',
});
}
"
>查看</a-button
>
</template>
<template v-if="column.dataIndex === 'nr2'">
<a-button type="link" @click="openViewDrawerD(record)">查看</a-button>
</template>
</template>
</BasicTable>
</BasicModal>
<MedicalStaffDrawer @register="registerDrawer" type="1" />
<FollowUpTasksViewDrawer @register="registerViewDrawer" type="1">
<template #taskList>
<div v-if="taskList.length > 0" style="width: 100%; border: 1px dashed #999; padding: 10px; margin-top: 10px">
<div style="font-weight: bold; margin: 0 0 10px 0; font-size: 14px; display: flex; align-items: center">
<span class="circular"></span> 当前关联的任务
</div>
<template v-for="(item, index) in taskList" :key="`taskList-${index}`">
<div style="width: 100%; display: flex; padding: 3px 10px; margin-bottom: 10px; background: #ececec">
<div style="width: 40px; display: flex; align-items: center"> </div>
<div style="width: calc(100% - 40px)">
<div style="display: flex; justify-content: space-between; width: 100%; padding: 2px 0">
<span>{{ getTaskName(item.type) }}</span>
<span
v-if="item.type != 2"
style="color: #ffffff; padding: 1px 5px"
:style="{ background: item.status == 1 ? '#FCA318' : item.status == 2 ? '#FD2121' : '#2AC18B' }"
>
{{ getName(item.status, taskStatus) }}
</span>
</div>
<div style="display: flex; justify-content: space-between; width: 100%; padding: 2px 0">
<span>计划随访日期{{ dayjs(item.expectDate).format('YYYY-MM-DD') }}</span>
<template v-if="item.status === 1">
<template v-if="getStatusName(item.expectDate) === 0"> <span>今日</span> </template>
<template v-else>
<span>
还剩 <span style="color: red">{{ getStatusName(item.expectDate) * -1 }}</span>
</span>
</template>
</template>
<template v-if="item.status === 2">
<span>
逾期 <span style="color: red">{{ getStatusName(item.expectDate) * -1 }}</span>
</span>
</template>
</div>
</div>
</div>
</template>
</div>
</template>
</FollowUpTasksViewDrawer>
</template>
<script setup lang="ts">
import BasicModal from '/@/components/Modal/src/BasicModal.vue';
import { useModalInner } from '/@/components/Modal';
import { ref } from 'vue';
import BasicTable from '/@/components/Table/src/BasicTable.vue';
import { useListPage } from '/@/hooks/system/useListPage';
import { userPageApi, yearPageApi } from '/@/views/archivesManage/institution/timeService/medical/medical.api';
import dayjs from 'dayjs';
import { columns1, columns2 } from '/@/views/archivesManage/institution/timeService/medical/medical.data';
import MedicalStaffDrawer from '/@/views/interveneNew/medicalPoints/employeeTreatment/components/employeeTreatmentDrawer.vue';
import { useDrawer } from '/@/components/Drawer';
import { getName } from '/@/views/interveneNew/compoents/utils';
import FollowShow from '/@/views/interveneNew/medicalPoints/follow/componets/followShow.vue';
import FollowUpTasksViewDrawer from '/@/views/interveneNew/medicalPoints/followUpTasks/components/followUpTasksViewDrawer.vue';
import { queryByIdNewApi } from '/@/views/interveneNew/medicalPoints/follow/follow.api';
const [registerDrawer, { openDrawer }] = useDrawer();
const [registerViewDrawer, { openDrawer: openViewDrawer }] = useDrawer();
async function openViewDrawerD(record: Recordable) {
taskList.value = [];
openViewDrawer(true, {
records: record,
});
try {
taskList.value = await queryByIdNewApi({ recordId: record.id });
} catch (e) {
console.log(e);
}
}
const radioValue = ref('0');
const hospitalInfo = ref({});
const taskList = ref<any[]>([]);
const [registerModal, { setModalProps }] = useModalInner(async (data) => {
radioValue.value = '0';
setModalProps({ showCancelBtn: false, showOkBtn: false });
hospitalInfo.value = data.record;
setProps({
api: yearPageApi,
columns: columns1,
searchInfo: { medicalCenterId: hospitalInfo.value.centerId, departCode: hospitalInfo.value.orgCode, userSign: true },
});
await reload({ page: 1 });
});
const { tableContext } = useListPage({
tableProps: {
canResize: false,
orderFlag: false,
immediate: false,
showIndexColumn: true,
useSearchForm: false,
showTableSetting: false,
showActionColumn: false,
},
});
const [registerTable, { setProps, reload, setTableData, setPagination }] = tableContext;
async function changeRadio(v) {
setTableData([]);
setPagination({ total: 0 });
if (v.target.value === '0') {
setProps({
api: yearPageApi,
columns: columns1,
searchInfo: { medicalCenterId: hospitalInfo.value.centerId, departCode: hospitalInfo.value.orgCode, userSign: true },
});
await reload({ page: 1 });
} else {
setProps({
api: userPageApi,
columns: columns2,
searchInfo: { medicalId: hospitalInfo.value.centerId, medicalOrgCode: hospitalInfo.value.orgCode, type: 0 },
});
await reload({ page: 1 });
}
}
</script>
<style scoped lang="less">
.top-d-outer {
padding: 10px;
.top-d {
border-bottom: 1px dashed #cecece;
display: flex;
padding: 0 0 10px;
> div {
margin-right: 50px;
}
}
}
:deep(.ant-radio-button-wrapper) {
color: #1684fc !important;
border-color: #1684fc !important;
&:before {
background-color: #1684fc !important;
}
}
:deep(.ant-radio-button-wrapper-checked) {
color: #ffffff !important;
}
</style>
@@ -0,0 +1,13 @@
import { defHttp } from '/@/utils/http/axios';
enum Api {
list = '/medical-center/archives/medical-center/centers',
statistics = '/health-consultation/archives/consultation/statistics',
userPage = '/medical-center/medicalCenter/followUpRecord/list',
yearPage = '/medical-center/medicalCenter/dailyDiagnosis/list',
}
export const listApi = (params: any) => defHttp.get({ url: Api.list, params });
export const statisticsApi = (params: any) => defHttp.get({ url: Api.statistics, params });
export const userPageApi = (params: any) => defHttp.get({ url: Api.userPage, params });
export const yearPageApi = (params: any) => defHttp.get({ url: Api.yearPage, params });
@@ -0,0 +1,158 @@
import { BasicColumn } from '/@/components/Table';
import dayjs from 'dayjs';
import { render } from '/@/utils/common/renderUtils';
import { getNotHaveName } from '/@/views/interveneNew/compoents/utils';
export const columns1: BasicColumn[] = [
{
title: '单位',
dataIndex: 'userOrgName',
},
{
title: '部门',
dataIndex: 'userDepartName',
},
{
title: '姓名',
dataIndex: 'userName',
width: 90,
},
{
title: '员工编号',
dataIndex: 'workNo',
width: 120,
},
{
title: '性别',
dataIndex: 'sex',
customRender: ({ text }) => {
return render.renderDict(text, 'sex2');
},
width: 60,
},
{
title: '年龄',
dataIndex: 'age',
width: 60,
},
{
title: '手机号码',
dataIndex: 'phone',
width: 120,
},
{
title: '身份证号',
dataIndex: 'idCard',
width: 160,
},
{
title: '就诊医疗点',
dataIndex: 'medicalCenterName',
},
{
title: '就诊疾病',
dataIndex: 'diagnosisDisease',
},
{
title: '就诊医生',
dataIndex: 'doctorName',
width: 80,
},
{
title: '就诊时间',
dataIndex: 'diagnosisDate',
},
{
title: '就诊内容',
dataIndex: 'nr1',
width: 80,
},
];
export const columns2: BasicColumn[] = [
{
title: '单位',
dataIndex: 'orgName',
},
{
title: '部门',
dataIndex: 'userDepartName',
},
{
title: '姓名',
dataIndex: 'userName',
width: 90,
},
{
title: '员工编号',
dataIndex: 'userNo',
width: 120,
},
{
title: '性别',
dataIndex: 'sex_dictText',
width: 80,
},
{
title: '年龄',
dataIndex: 'age',
width: 80,
},
{
title: '手机号码',
dataIndex: 'telPhone',
width: 120,
},
{
title: '身份证号',
dataIndex: 'idNo',
width: 160,
},
{
title: '随访医疗点',
dataIndex: 'medicalName',
},
{
title: '症状',
dataIndex: 'symptom',
},
{
title: '随访形式',
dataIndex: 'followUp',
customRender: ({ text }) => {
return getNotHaveName(text, followType);
},
width: 80,
},
{
title: '随访医生',
dataIndex: 'doctorName',
width: 80,
},
{
title: '随访时间',
dataIndex: 'followDate',
},
{
title: '随访内容',
dataIndex: 'nr2',
width: 80,
},
];
const followType = [
{
label: '门诊',
value: '1',
},
{
label: '巡诊',
value: '4',
},
{
label: '家庭',
value: '2',
},
{
label: '电话',
value: '3',
},
];
@@ -0,0 +1,230 @@
<template>
<div class="outer-d">
<div class="top-title"> 长庆油田一线医疗点 </div>
<div class="map-d">
<template v-if="!loading">
<service-map
ref="serviceMap"
:marker-content="markerContent"
:label-content="labelContent"
field="centerName"
:map-list="mapList"
@get-marker-info="getMarkerInfo"
@get-detail="getDetail"
id="centerId"
/>
<div class="search-d">
所属单位
<ApiSelect
:value="orgCode"
:api="allSecondaryDepartsNew"
@change="
(v) => {
orgCode = v;
}
"
resultField="result"
labelField="departName"
valueField="orgCode"
:after-fetch="
(data) => {
data.unshift({
orgCode: '1',
departName: '全部单位',
});
return data;
}
"
:allowClear="true"
:show-default-value="false"
:immediate="true"
:showSearch="true"
:filterOption="
(input: string, option: any): boolean => {
const str: string = input.toLowerCase();
return option.label.toLowerCase().indexOf(str) >= 0;
}"
style="width: 260px"
/>
<a-button type="primary" @click="searchB">查询</a-button>
<a-button style="background-color: #a4adb3; color: #ffffff" @click="resetB">重置</a-button>
</div>
<div class="bottom-d">
<div
class="bottom-d-item"
v-for="(item, index) in statisticsInfo"
:key="`bottom-d-item-${index}`"
:style="{ margin: index === 1 ? '0 25px 0 50px' : index === 2 ? '0 50px 0 25px' : 0 }"
>
<div>{{ item.value }}</div>
<div>{{ item.name }}</div>
</div>
</div>
</template>
<template v-else>
<div style="width: 100%; height: 100%; display: flex; align-items: center; justify-content: center">
<a-spin size="large" />
</div>
</template>
</div>
</div>
<consult-modal @register="registerModal" />
</template>
<script setup lang="ts">
import ServiceMap from '/@/views/archivesManage/institution/timeService/components/serviceMap.vue';
import { nextTick, onMounted, ref, onBeforeUnmount } from 'vue';
import { listApi } from '/@/views/archivesManage/institution/timeService/medical/medical.api';
import ConsultModal from '/@/views/archivesManage/institution/timeService/medical/components/medicalModal.vue';
import { useModal } from '/@/components/Modal';
import { allSecondaryDepartsNew } from '/@/utils/orgSearchInfo';
import ApiSelect from '/@/components/Form/src/components/ApiSelect.vue';
import building from '/@/assets/images/archivesManage/building.png';
const [registerModal, { openModal }] = useModal();
const loading = ref(true);
const serviceMap = ref();
const statisticsInfo = ref<any[]>([]);
const orgCode = ref('1');
const mapList = ref();
const markerContent = ref(`<div style="white-space: nowrap;display: flex;background-color: #58a55c;padding: 5px 10px;border-radius: 10px;
align-items: center;">
<img src="${building}" alt="" style="width: 20px;height: 20px; margin-right: 5px"/>
<span style="color: #ffffff">^&</span>
</div>`);
const labelContent = ref('');
onMounted(async () => {
await initList();
loading.value = false;
await nextTick(() => {
serviceMap.value.initMap(mapList.value);
});
});
onBeforeUnmount(() => {
serviceMap.value.destroyMap();
});
async function initList() {
try {
let specialistSum = 0;
let thisYearSessionSum = 0;
let allSessionSum = 0;
mapList.value = (await listApi({ orgCode: orgCode.value === '1' ? '' : orgCode.value })).map((item) => {
specialistSum += item.medicalCareNum;
thisYearSessionSum += item.thisYearNum;
allSessionSum += item.totalYearNum;
return item;
});
statisticsInfo.value = [
{ value: mapList.value.length, name: '医疗点数量' },
{ value: specialistSum, name: '医护人员数量' },
{ value: thisYearSessionSum, name: '当年服务人次' },
{ value: allSessionSum, name: '累计服务人次' },
];
} catch (e) {
mapList.value = [];
statisticsInfo.value = [
{ value: 0, name: '医疗点数量' },
{ value: 0, name: '医护人员数量' },
{ value: 0, name: '当年服务人次' },
{ value: 0, name: '累计服务人次' },
];
console.log(e);
}
}
function getMarkerInfo(e) {
labelContent.value = `
<div style="padding: 10px;background-color: #ffffff;border-radius: 10px">
<div style="padding-top: 8px;font-weight: bold;font-size: 14px">医护人员数量:${e.medicalCareNum} </div>
<div style="padding-top: 8px;font-weight: bold;font-size: 14px">当年服务人次:${e.thisYearNum} </div>
<div style="padding-top: 8px;font-weight: bold;font-size: 14px">累计服务人次:${e.totalYearNum} </div>
<div style="padding-top: 8px;text-align: center;color: #5087ec" onclick="getDetail()">查看服务数据></div>
</div>
`;
serviceMap.value.setLabel(labelContent.value);
}
async function searchB() {
await initList();
}
async function resetB() {
orgCode.value = '1';
await searchB();
}
function getDetail(e) {
openModal(true, {
record: e,
});
}
</script>
<style scoped lang="less">
.outer-d {
position: relative;
width: 100%;
height: 100%;
}
.top-title {
padding: 10px;
text-align: center;
font-size: 20px;
}
.search-d {
box-shadow: 2px 2px 0 0 rgba(0, 0, 0, 0.35);
border-radius: 5px;
background-color: #ffffff;
padding: 10px 20px;
position: absolute;
top: 10px;
left: 10px;
z-index: 99;
> :nth-child(n + 1) {
margin-left: 10px;
}
}
.bottom-d {
border-radius: 5px;
width: 100%;
position: absolute;
bottom: 8%;
left: 0;
z-index: 99;
display: flex;
align-items: center;
justify-content: center;
.bottom-d-item {
border-radius: 5px;
box-shadow: 2px 2px 4px 1px rgba(0, 0, 0, 0.35);
background-color: #ffffff;
font-weight: bold;
padding: 0 50px;
> div {
text-align: center;
&:nth-child(1) {
font-size: 18px;
padding: 10px 0 5px 0;
}
&:nth-child(2) {
padding: 5px 0 10px 0;
}
}
}
}
.map-d {
position: absolute;
width: calc(100% - 20px);
height: calc(100% - 70px);
top: 50px;
left: 10px;
}
</style>
@@ -0,0 +1,28 @@
import { defHttp } from '/@/utils/http/axios';
import { stServerUrl } from '/@/utils/http/stRequestToken/serverUrl';
import { getStToken } from '/@/utils/http/stRequestToken';
export enum Api {
userFoodEnergyInfo = '/foodNourishmentReport/getUserFoodEnergyInfo',
}
/**
* 列表接口
* @param params
*/
export const getUserFoodEnergyInfo = async (params) => {
const token = await getStToken({});
return defHttp.get(
{
url: Api.userFoodEnergyInfo,
params,
headers: {
'X-Access-Token': token,
},
},
{
apiUrl: stServerUrl,
withToken: false,
}
);
};
@@ -0,0 +1,28 @@
import { BasicColumn } from '/@/components/Table';
//列表数据
export const columns: BasicColumn[] = [
{
title: '序号',
align: 'center',
width: 80,
customRender: ({ index }) => {
return index + 1;
},
},
{
title: '餐品名称',
align: 'center',
dataIndex: 'foodName',
},
{
title: '取用量(克)',
align: 'center',
dataIndex: 'eatWeight',
},
{
title: '热量(kcal)',
align: 'center',
dataIndex: 'energy',
},
];
@@ -0,0 +1,58 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" title="摄入食品详情" :footer="null" destroyOnClose :width="800">
<a-descriptions style="padding: 0 10px">
<a-descriptions-item label="姓名">{{ realName }}</a-descriptions-item>
<a-descriptions-item label="餐次">{{ dinnerType }}</a-descriptions-item>
<a-descriptions-item label="用餐时间">{{ eatDate }}</a-descriptions-item>
</a-descriptions>
<BasicTable @register="registerTable" />
</BasicModal>
</template>
<script lang="ts" setup>
import { ref, unref } from 'vue';
import { BasicTable } from '/@/components/Table';
import { useListPage } from '/@/hooks/system/useListPage';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { getUserFoodEnergyInfo } from './info.api';
import { columns } from './info.data';
let userId = ref('');
let realName = ref('');
let dinnerType = ref('');
let eatDate = ref('');
const [registerModal] = useModalInner(async (data) => {
if (data.record?.id) {
realName.value = data.record.realname;
userId.value = data.record.id;
dinnerType.value = data.record.dinnerType;
eatDate.value = data.record.statisticsDay;
}
});
const { tableContext } = useListPage({
tableProps: {
api: getUserFoodEnergyInfo,
beforeFetch: (params) => {
params.userId = unref(userId);
params.dinnerType = unref(dinnerType);
params.eatDate = unref(eatDate);
},
columns,
maxHeight: 500,
canResize: true,
useSearchForm: false,
showTableSetting: false,
showActionColumn: false,
},
});
// BasicTable绑定注册
const [registerTable] = tableContext;
</script>
<style scoped>
:deep(.scrollbar__wrap) {
margin-bottom: 0 !important;
}
</style>
@@ -0,0 +1,50 @@
import { defHttp } from '/@/utils/http/axios';
import { stServerUrl } from '/@/utils/http/stRequestToken/serverUrl';
import { getStToken } from '/@/utils/http/stRequestToken';
export enum Api {
list = '/foodNourishmentReport/canteenInfoDevice',
canteenDepartNameList = '/foodNourishmentReport/getCanteenDepartNameList',
}
/**
* 列表接口
* @param params
*/
export const list = async (params) => {
const token = await getStToken({});
return defHttp.get(
{
url: Api.list,
params,
headers: {
'X-Access-Token': token,
},
},
{
apiUrl: stServerUrl,
withToken: false,
}
);
};
/**
* 部门
* @param params
*/
export const getCanteenDepartNameList = async (params) => {
const token = await getStToken({});
return defHttp.get(
{
url: Api.canteenDepartNameList,
params,
headers: {
'X-Access-Token': token,
},
},
{
apiUrl: stServerUrl,
withToken: false,
}
);
};
@@ -0,0 +1,345 @@
<template>
<div class="mapPage">
<div>
<a-spin :spinning="loading">
<div id="maps"></div>
<div id="search">
所属单位
<a-select v-model:value="canteenId" placeholder="请选择单位" style="width: 260px">
<a-select-option v-for="item in canteenIdList" :label="item.departName" :value="item.departId" :key="item.departId">
{{ item['departName'] }}
</a-select-option>
</a-select>
<a-button style="margin: 0 15px" type="primary" @click="getList">查询</a-button>
<a-button style="background-color: #a4adb3; color: #ffffff" @click="reset">重置</a-button>
</div>
<div id="total">
<div>
<div
><span>{{ canteenNum }}</span
></div
>
<div>营养监控食堂数量</div>
</div>
<div>
<div
><span>{{ deviceNum }}</span
></div
>
<div>设备总数</div>
</div>
<div>
<div
><span>{{ normalNum }}</span
></div
>
<div>设备正常运行数</div>
</div>
</div>
</a-spin>
</div>
</div>
</template>
<script lang="ts" name="timeService-nutrition-map" setup>
import { nextTick, onMounted, ref } from 'vue';
import mapKey from '/@/utils/mapKey';
import AMapLoader from '@amap/amap-jsapi-loader';
import { getCanteenDepartNameList, list } from './mapPage.api';
import canteenIcon from '/@/assets/images/canteenIcon.png';
import { useUserStore } from '/@/store/modules/user';
const emit = defineEmits(['openInfo']);
const loading = ref<any>(true);
const maps = ref<any>(null);
let selfMap = null;
const markerList = ref<any>([]);
const canteenIdList = ref<any>([]);
const canteenId = ref<any>(null);
const canteenNum = ref<any>(0);
const deviceNum = ref<any>(0);
const normalNum = ref<any>(0);
onMounted(() => {
getCanteen();
getList();
});
const getCanteen = () => {
let params = {
orgCode: useUserStore()?.getUserInfo.orgCode,
};
getCanteenDepartNameList(params).then((res) => {
canteenIdList.value = res;
});
};
const reset = () => {
canteenId.value = null;
getList();
};
const getList = () => {
loading.value = true;
let params = {
canteenId: canteenId.value,
};
list(params).then((res) => {
let dinersVos: any = [];
canteenNum.value = res.canteenNum;
deviceNum.value = res.deviceNum;
normalNum.value = res.normalNum;
res.dinersVos.map((item) => {
if (item.addressLongitude && item.addressLatitude) {
dinersVos.push(item);
}
});
markerList.value = dinersVos;
loading.value = false;
nextTick(() => {
initMap();
});
});
};
const initMap = () => {
AMapLoader.load({
key: mapKey,
version: '2.0',
plugins: ['AMap.ToolBar', 'AMap.Scale', 'AMap.Geocoder', 'AMap.Geolocation'],
})
.then((AMap) => {
selfMap = AMap;
let lng: any = [];
let lat: any = [];
let center: any = null;
if (markerList.value && markerList.value.length) {
markerList.value.map((item) => {
lng.push(Number(item.addressLongitude));
lat.push(Number(item.addressLatitude));
});
if (markerList.value.length === 1) {
center = [lng[0], lat[0]];
} else {
center = getCenter(
lng.sort((a, b) => {
return a - b;
}),
lat.sort((a, b) => {
return a - b;
})
);
}
}
maps.value = new AMap.Map('maps', {
center: center,
zoom: 9,
viewMode: '3D',
resizeEnable: true,
pitchEnable: true,
convert: true,
});
const w = window as any;
nextTick(() => {
w.openInfoW = (canteenId: any) => {
let data = markerList.value.filter((item) => {
return item.info.canteenId == canteenId;
});
emit('openInfo', data[0].info);
};
});
maps.value.addControl(new AMap.ToolBar());
maps.value.addControl(new AMap.Scale());
addMarkerList();
})
.catch((e) => {
console.log(e);
});
};
const addMarkerList = () => {
if (markerList.value.length === 0) {
return;
} else {
let marker: any;
markerList.value = markerList.value.map((item) => {
marker = new selfMap.Marker({
position: new selfMap.LngLat(Number(item.addressLongitude), Number(item.addressLatitude)),
icon: new selfMap.Icon({
image: canteenIcon,
size: new selfMap.Size(0, 0),
imageSize: new selfMap.Size(0, 0),
}),
label: {
content: `<div class="labelContent">
<div class="restName">
<img src="${canteenIcon}" style="height: 15px;width: 15px; margin-right: 3px" alt=""/>
<div>${item.restName}</div>
</div>
<div id="Id${item.canteenId}" class="info">
<div>营养监控人数:${item.eatPersonNum}</div>
<div>营养监控设备:${item.deviceNum}</div>
<div onclick="openInfoW(${item.canteenId})">查看营养监控设备&gt;</div>
</div>
</div>`,
icon: null,
offset: new selfMap.Pixel(0, 27),
direction: 'top',
},
maxZoom: 8,
});
marker.info = item;
marker.on('click', markerClick);
return marker;
});
maps.value.add(markerList.value);
}
};
const getCenter = (lng, lat) => {
let lngCenter = (lng[0] + lng[lng.length - 1]) / 2;
let latCenter = (lat[0] + lat[lat.length - 1]) / 2;
return [lngCenter, latCenter];
};
const markerClick = (e) => {
let times = new Date().getTime().toString();
e.target.setzIndex(times.substring(times.length - 8, times.length));
let idDom = document.getElementById('Id' + e.target.info.canteenId);
if (idDom) {
let s = idDom.style.display;
idDom.style.display = s === 'block' ? 'none' : 'block';
}
};
</script>
<style lang="less" scoped>
.mapPage {
position: absolute;
left: 10px;
width: calc(100% - 20px);
height: calc(100% - 95px);
top: 85px;
:deep(.ant-spin-nested-loading),
:deep(.ant-spin-container) {
height: 100%;
}
> div {
width: 100%;
height: 100%;
padding: 8px;
border-radius: 10px;
box-sizing: border-box;
position: relative;
background-color: #ffffff;
#maps {
width: 100%;
height: 100%;
:deep(.amap-marker-label) {
border: none !important;
background-color: rgba(0, 0, 0, 0) !important;
}
:deep(.labelContent) {
position: relative;
.restName {
display: flex;
flex-wrap: nowrap;
background-color: #58a55c;
padding: 5px 10px;
border-radius: 5px;
color: #ffffff;
cursor: pointer;
}
.info {
display: none;
position: absolute;
left: 0;
top: 30px;
padding: 12px 20px;
border-radius: 3px;
background-color: #ffffff;
box-shadow: 0 0 3px 3px rgba(0, 0, 0, 0.1);
> div {
font-size: 14px;
margin-bottom: 8px;
&:last-of-type {
cursor: pointer;
color: #1890ff;
font-size: 12px;
margin-bottom: 0;
text-align: center;
}
}
}
}
}
#search {
position: absolute;
z-index: 999;
top: 10px;
left: 10px;
border-radius: 3px;
padding: 10px 20px;
background-color: #ffffff;
box-shadow: 0 0 3px 3px rgba(0, 0, 0, 0.1);
}
#total {
position: absolute;
z-index: 999;
left: 0;
right: 0;
width: 900px;
bottom: 30px;
margin: auto;
display: flex;
flex-wrap: nowrap;
justify-content: space-around;
> div {
width: 200px;
padding: 25px 0;
text-align: center;
border-radius: 3px;
background-color: #ffffff;
box-shadow: 0 0 3px 3px rgba(0, 0, 0, 0.1);
> div:nth-of-type(1) {
height: 32px;
font-size: 16px;
line-height: 32px;
margin-bottom: 10px;
span {
font-size: 32px;
font-weight: bold;
line-height: 32px;
}
}
> div:nth-of-type(2) {
height: 16px;
font-size: 16px;
line-height: 16px;
}
}
}
}
}
</style>
@@ -0,0 +1,28 @@
import { defHttp } from '/@/utils/http/axios';
import { stServerUrl } from '/@/utils/http/stRequestToken/serverUrl';
import { getStToken } from '/@/utils/http/stRequestToken';
export enum Api {
canteenInfo = '/foodNourishmentReport/userEatInfoByCanteenId',
}
/**
* 列表接口
* @param params
*/
export const list = async (params) => {
const token = await getStToken({});
return defHttp.get(
{
url: Api.canteenInfo,
params,
headers: {
'X-Access-Token': token,
},
},
{
apiUrl: stServerUrl,
withToken: false,
}
);
};
@@ -0,0 +1,76 @@
import { BasicColumn } from '/@/components/Table';
//列表数据
export const columns: BasicColumn[] = [
{
title: '序号',
align: 'center',
dataIndex: 'index',
width: 80,
customRender: ({ index }): any => {
return index + 1;
},
},
{
title: '单位',
align: 'left',
width: 200,
dataIndex: 'secondDepartName',
},
{
title: '部门',
align: 'center',
width: 200,
dataIndex: 'thirdDepartName',
},
{
title: '姓名',
align: 'center',
dataIndex: 'realname',
},
{
title: '工号',
align: 'center',
dataIndex: 'workNo',
},
{
title: '性别',
align: 'center',
dataIndex: 'sex',
},
{
title: '年龄',
align: 'center',
dataIndex: 'age',
},
{
title: '身高(cm)',
align: 'center',
dataIndex: 'height',
},
{
title: '体重(kg)',
align: 'center',
dataIndex: 'weight',
},
{
title: 'BMI',
align: 'center',
dataIndex: 'bmi',
},
{
title: '体力劳动',
align: 'center',
dataIndex: 'workStrength',
},
{
title: '餐次总数量',
align: 'center',
dataIndex: 'dinnerTypeNum',
},
{
title: '平均摄入数量(kcal)',
align: 'center',
dataIndex: 'energy',
},
];
@@ -0,0 +1,83 @@
<template>
<div>
<BasicModal v-bind="$attrs" title="查看营养监控数据" @register="registerModal" :width="1250" destroyOnClose :maskClosable="true" :footer="null">
<BasicTable @register="registerTable">
<template #tableTitle>
<a-descriptions :column="5" v-if="record">
<a-descriptions-item label="食堂名称">{{ record.restName }}</a-descriptions-item>
<a-descriptions-item label="营养监控人数">{{ record.eatPersonNum }}</a-descriptions-item>
<a-descriptions-item label="营养监控设备">{{ record.deviceNum }}</a-descriptions-item>
<a-descriptions-item label="所属单位">{{ record.flatsName }}</a-descriptions-item>
<a-descriptions-item label="管理部门">{{ record.departName }}</a-descriptions-item>
</a-descriptions>
</template>
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" />
</template>
</BasicTable>
</BasicModal>
<meal-modal @register="registerMealModal" />
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { BasicModal, useModal, useModalInner } from '/@/components/Modal';
import { list } from './mapData.api';
import { columns } from './mapData.data';
import { BasicTable, TableAction} from '/@/components/Table';
import { useListPage } from '/@/hooks/system/useListPage';
import { useUserStore } from '/@/store/modules/user';
import MealModal from '../meal/meal.vue';
const record = ref<any>(null);
const [registerModal] = useModalInner(async (data) => {
if (data.record) {
record.value = data.record;
}
});
const { tableContext } = useListPage({
tableProps: {
title: '营养监控数据',
api: list,
beforeFetch: (params) => {
params.canteenId = record.value.canteenId;
params.orgCode = useUserStore()?.getUserInfo.orgCode;
},
columns,
useSearchForm: false,
showTableSetting: false,
clickToRowSelect: false,
actionColumn: {
width: 120,
fixed: 'right',
title: '摄入记录',
},
},
});
const [registerTable] = tableContext;
const [registerMealModal, { openModal: openMealModal }] = useModal();
const openMeal = (record) => {
openMealModal(true, {
record,
isUpdate: true,
});
};
/**
* 操作栏
*/
function getTableAction(record: Recordable) {
return [
{
label: '摄入记录',
onClick: openMeal.bind(null, record),
},
];
}
</script>
@@ -0,0 +1,52 @@
import { defHttp } from '/@/utils/http/axios';
import { stServerUrl } from '/@/utils/http/stRequestToken/serverUrl';
import { getStToken } from '/@/utils/http/stRequestToken';
export enum Api {
userFoodEnergyInfo = '/foodNourishmentReport/getUserInfoAndEatNum',
exportList = '/foodNourishmentReport/getUserInfoAndEatNum2/export',
}
/**
* 列表接口
* @param params
*/
export const getUserFoodEnergyInfo = async (params) => {
const token = await getStToken({});
return defHttp.get(
{
url: Api.userFoodEnergyInfo,
params,
headers: {
'X-Access-Token': token,
},
},
{
apiUrl: stServerUrl,
withToken: false,
}
);
};
/**
* 导出接口
* @param params
*/
export const exportList = async (params) => {
const token = await getStToken({});
return defHttp.get(
{
url: Api.exportList,
params,
headers: {
'X-Access-Token': token,
},
responseType: 'blob',
},
{
apiUrl: stServerUrl,
withToken: false,
isTransformResponse: false,
}
);
};
@@ -0,0 +1,51 @@
import { BasicColumn, FormSchema } from '/@/components/Table';
//列表数据
export const columns: BasicColumn[] = [
{
title: '序号',
align: 'center',
width: 80,
customRender: ({ index }) => {
return index + 1;
},
},
{
title: '用餐时间',
align: 'center',
dataIndex: 'statisticsDay',
},
{
title: '用餐餐次',
align: 'center',
dataIndex: 'dinnerType',
},
{
title: '摄入餐品数',
align: 'center',
dataIndex: 'foodNum',
},
{
title: '摄入热量(kcal)',
align: 'center',
dataIndex: 'energy',
},
];
export const searchFormSchema: FormSchema[] = [
{
label: '用餐时间',
field: 'days',
component: 'RangeDate',
componentProps: () => {
return {
showTime: false,
valueFormat: 'YYYY-MM-DD',
getPopupContainer: () => document.body,
style: {
width: '100%',
},
};
},
},
];
@@ -0,0 +1,128 @@
<template>
<div>
<BasicModal v-bind="$attrs" @register="registerModal" title="摄入记录" :footer="null" destroyOnClose :width="1200">
<template v-if="record">
<a-descriptions :column="6" style="padding: 0 10px">
<a-descriptions-item label="姓名">{{ record.realname }}</a-descriptions-item>
<a-descriptions-item label="性别">{{ record.sex }}</a-descriptions-item>
<a-descriptions-item label="年龄">{{ record.age }}</a-descriptions-item>
<a-descriptions-item label="工号">{{ record.workNo }}</a-descriptions-item>
<a-descriptions-item label="所属单位">{{ record.secondDepartName }}</a-descriptions-item>
<a-descriptions-item label="所属部门">{{ record.thirdDepartName }}</a-descriptions-item>
<a-descriptions-item label="身高(cm)">{{ record.height }}</a-descriptions-item>
<a-descriptions-item label="体重(kg)">{{ record.weight }}</a-descriptions-item>
<a-descriptions-item label="BMI">{{ record.bmi }}</a-descriptions-item>
<a-descriptions-item label="体力劳动">{{ record.workStrength }}</a-descriptions-item>
</a-descriptions>
</template>
<BasicTable @register="registerTable">
<template #tableTitle>
<a-button type="primary" @click="exportExcel">导出</a-button>
</template>
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" />
</template>
</BasicTable>
</BasicModal>
<info-modal @register="registerInfoModal" />
</div>
</template>
<script lang="ts" setup>
import { ref, unref } from 'vue';
import { BasicTable, TableAction} from '/@/components/Table';
import { useListPage } from '/@/hooks/system/useListPage';
import { BasicModal, useModal, useModalInner} from '/@/components/Modal';
import InfoModal from '../info/info.vue';
import { getUserFoodEnergyInfo, exportList } from './meal.api';
import { columns, searchFormSchema } from './meal.data';
import { useUserStore } from '/@/store/modules/user';
let userId = ref('');
let record = ref(null);
const [registerModal] = useModalInner(async (data) => {
if (data.record?.id) {
userId.value = data.record.id;
record.value = data.record;
}
});
const { tableContext } = useListPage({
tableProps: {
api: getUserFoodEnergyInfo,
beforeFetch: (params) => {
params.userId = unref(userId);
},
columns,
maxHeight: 500,
canResize: true,
formConfig: {
labelWidth: '70px',
labelAlign: 'left',
schemas: searchFormSchema,
autoSubmitOnEnter: true,
fieldMapToTime: [['days', ['startDay', 'endDay'], 'YYYY-MM-DD']],
},
showTableSetting: false,
actionColumn: {
width: 120,
fixed: 'right',
title: '食品详情',
},
},
});
// BasicTable绑定注册
const [registerTable, { setLoading, getForm }] = tableContext;
const [registerInfoModal, { openModal: openInfoModal }] = useModal();
const exportExcel = () => {
let { getFieldsValue } = getForm();
setLoading(true);
let params = {
orgCode: useUserStore()?.getUserInfo.orgCode,
userId: unref(userId),
startDay: getFieldsValue().startDay,
endDay: getFieldsValue().endDay,
};
exportList(params).then((res) => {
setLoading(false);
const url = window.URL.createObjectURL(new Blob([res]));
const link = document.createElement('a');
link.style.display = 'none';
link.href = url;
link.setAttribute('download', '摄入记录.xls');
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
});
};
const openInfo = (record) => {
openInfoModal(true, {
record,
isUpdate: true,
});
};
/**
* 操作栏
*/
function getTableAction(record: Recordable) {
return [
{
label: '查看',
onClick: openInfo.bind(null, record),
},
];
}
</script>
<style scoped>
:deep(.scrollbar__wrap) {
margin-bottom: 0 !important;
}
</style>
@@ -0,0 +1,40 @@
<template>
<div class="page">
<div class="pageTitle">长庆油田营养监控食堂</div>
<map-page @openInfo="openInfo" />
<map-data @register="registerSearchModal" />
</div>
</template>
<script lang="ts" name="timeService-nutrition" setup>
import { useModal } from '/@/components/Modal';
import MapPage from './components/map/mapPage.vue';
import MapData from './components/mapData/mapData.vue';
const [registerSearchModal, { openModal: openSearchModal }] = useModal();
const openInfo = (record) => {
openSearchModal(true, {
record: record,
isUpdate: true,
});
};
</script>
<style lang="less" scoped>
.page {
height: 100%;
padding: 10px;
.pageTitle {
color: #252535;
width: 100%;
height: 72px;
font-size: 26px;
line-height: 72px;
text-align: center;
}
}
</style>
@@ -0,0 +1,118 @@
<template>
<BasicModal v-bind="$attrs" destroyOnClose @register="registerModal" title="监测数据" width="80%">
<div style="display: flex; flex-wrap: wrap">
<div style="width: 100%; padding: 0 20px 20px">
<div class="top-info">
<div style="margin-bottom: 10px; font-weight: bold"> 员工姓名{{ infoData.bindRealName || '-' }} </div>
<div style="margin-bottom: 10px; font-weight: bold"> 员工编号{{ infoData.workNo || '-' }} </div>
<div style="margin-bottom: 10px; font-weight: bold"> 工具编码{{ infoData.watchNo || '-' }} </div>
<div style="margin-bottom: 10px; font-weight: bold"> 性别{{ getName(infoData.sex, Dict.getDict('sex2')) || '-' }} </div>
<div style="margin-bottom: 10px; font-weight: bold"> 年龄{{ infoData.age || '-' }} </div>
<div style="margin-bottom: 10px; font-weight: bold"> 单位{{ infoData.userTwoOrgName || '-' }} </div>
<div style="margin-bottom: 10px; font-weight: bold"> 部门{{ infoData.userThreeOrgName || '-' }} </div>
<div style="margin-bottom: 10px; font-weight: bold"> 绑定日期{{ infoData.bindDate }} </div>
</div>
</div>
<div style="width: 100%">
<a-tabs v-model:activeKey="activeKey">
<a-tab-pane key="0" tab="总览" />
<a-tab-pane key="1" tab="心率" />
<a-tab-pane key="2" tab="血氧" />
<a-tab-pane key="3" tab="压力" />
<a-tab-pane key="4" tab="睡眠" />
<a-tab-pane key="5" tab="体温" />
<a-tab-pane key="6" tab="锻炼" />
<a-tab-pane key="7" tab="步数" />
</a-tabs>
<div style="height: 60vh; overflow: auto">
<all-look
@change-params-date="changeParamsDate"
v-if="activeKey === '0'"
:info-data="info"
:userId="infoData.bindUserId"
class="tabs-d"
/>
<heart-rate v-if="activeKey === '1'" :userId="infoData.bindUserId" class="tabs-d" :keyType="activeKey" />
<blood-oxygen v-if="activeKey === '2'" :userId="infoData.bindUserId" class="tabs-d" :keyType="activeKey" />
<pressure v-if="activeKey === '3'" :userId="infoData.bindUserId" class="tabs-d" :keyType="activeKey" />
<sleep v-if="activeKey === '4'" :userId="infoData.bindUserId" class="tabs-d" :keyType="activeKey" />
<temperature v-if="activeKey === '5'" :userId="infoData.bindUserId" class="tabs-d" :keyType="activeKey" />
<motion v-if="activeKey === '6'" :userId="infoData.bindUserId" class="tabs-d" :keyType="activeKey" />
<step-number v-if="activeKey === '7'" :userId="infoData.bindUserId" class="tabs-d" :keyType="activeKey" />
</div>
</div>
</div>
</BasicModal>
</template>
<script setup lang="ts">
import { useModalInner } from '/@/components/Modal';
import BasicModal from '/@/components/Modal/src/BasicModal.vue';
import { ref } from 'vue';
import AllLook from '/@/views/healthMonitor/healMonitorManage/monitorToll/userInfo/componets/allLook.vue';
import HeartRate from '/@/views/healthMonitor/healMonitorManage/monitorToll/userInfo/componets/heartRate.vue';
import BloodOxygen from '/@/views/healthMonitor/healMonitorManage/monitorToll/userInfo/componets/bloodOxygen.vue';
import Pressure from '/@/views/healthMonitor/healMonitorManage/monitorToll/userInfo/componets/pressure.vue';
import Sleep from '/@/views/healthMonitor/healMonitorManage/monitorToll/userInfo/componets/sleep.vue';
import Temperature from '/@/views/healthMonitor/healMonitorManage/monitorToll/userInfo/componets/temperature.vue';
import Motion from '/@/views/healthMonitor/healMonitorManage/monitorToll/userInfo/componets/motion.vue';
import StepNumber from '/@/views/healthMonitor/healMonitorManage/monitorToll/userInfo/componets/stepNumber.vue';
import { bindInfoByUserApi, queryAllStatisticsApi } from '/@/views/healthMonitor/healMonitorManage/monitorToll/userInfo/userInfo.api';
import { getName } from '/@/views/interveneNew/compoents/utils';
import { Dict } from '/@/utils/cache/dict';
const activeKey = ref('0');
const infoData = ref({});
const paramsDate = ref('0');
const info = ref({});
const [registerModal, { setModalProps }] = useModalInner(async (data) => {
infoData.value = {};
activeKey.value = '0';
paramsDate.value = '0';
info.value = {};
setModalProps({ footer: false });
let result: any;
try {
result = await bindInfoByUserApi({ userId: data.record.bindUserId });
} catch {}
infoData.value = { ...data.record, ...{ bindDate: result ? result?.bindDate : '' } };
await getInfo();
});
async function changeParamsDate(v) {
paramsDate.value = v;
await getInfo();
}
async function getInfo() {
await queryAllStatisticsApi({ type: paramsDate.value, userId: infoData.value.bindUserId }).then((res) => {
info.value = res;
});
}
</script>
<style scoped lang="less">
:deep(.ant-tabs-nav-wrap) {
justify-content: center;
}
:deep(.ant-tabs-top > .ant-tabs-nav::before, .ant-tabs-top > div > .ant-tabs-nav::before) {
display: none;
}
:deep(.ant-tabs-tab + .ant-tabs-tab) {
margin-left: calc(100% / 14);
}
:deep(.ant-tabs-nav-list) {
width: 100%;
justify-content: center;
}
.tabs-d {
padding: 20px;
}
.top-info {
flex-wrap: wrap;
border-bottom: 1px dashed #cecece;
display: flex;
> div {
width: 25%;
}
}
</style>
@@ -0,0 +1,75 @@
<template>
<BasicModal @register="registerModal" title="服务数据" width="80%">
<div class="top-d-outer">
<div class="top-d">
<div>所属单位{{ wearInfo?.departName || '-' }}</div>
<div>设备数量{{ wearInfo?.total }}</div>
<div>配发人数{{ wearInfo?.distribution }}</div>
<div>设备正常运行数量{{ wearInfo?.normal }}</div>
</div>
</div>
<BasicTable @register="registerTable" table-type="1">
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'dataInfo'">
<a-button v-if="record?.bindUserId" type="link" @click="lookDataInfo(record)">监测数据</a-button>
<template v-else>-</template>
</template>
</template>
</BasicTable>
</BasicModal>
<user-info-modal @register="registerModal1" />
</template>
<script setup lang="ts">
import BasicModal from '/@/components/Modal/src/BasicModal.vue';
import { useModal, useModalInner } from '/@/components/Modal';
import { ref } from 'vue';
import BasicTable from '/@/components/Table/src/BasicTable.vue';
import { useListPage } from '/@/hooks/system/useListPage';
import { deviceListApi } from '/@/views/archivesManage/institution/timeService/wear/wear.api';
import { columns } from '/@/views/archivesManage/institution/timeService/wear/wear.data';
import UserInfoModal from '/@/views/archivesManage/institution/timeService/wear/components/userInfoModal.vue';
const wearInfo = ref({});
const [registerModal1, { openModal }] = useModal();
const [registerModal, { setModalProps }] = useModalInner(async (data) => {
setModalProps({ showCancelBtn: false, showOkBtn: false });
wearInfo.value = data.record;
setProps({ searchInfo: { deptId: data?.record?.deptId } });
await reload({ page: 1 });
});
const { tableContext } = useListPage({
tableProps: {
api: deviceListApi,
columns,
canResize: false,
orderFlag: false,
immediate: false,
showIndexColumn: true,
useSearchForm: false,
showTableSetting: false,
showActionColumn: false,
},
});
const [registerTable, { reload, setProps }] = tableContext;
function lookDataInfo(record: Recordable) {
openModal(true, {
record,
});
}
</script>
<style scoped lang="less">
.top-d-outer {
padding: 10px;
.top-d {
border-bottom: 1px dashed #cecece;
display: flex;
padding: 0 0 10px;
> div {
margin-right: 50px;
}
}
}
</style>
@@ -0,0 +1,9 @@
import { defHttp } from '/@/utils/http/axios';
enum Api {
list = '/health-watch/archives/watch/device/page',
deviceList = '/health-watch/watch/watchDevice/list',
}
export const listApi = (params: any) => defHttp.get({ url: Api.list, params }, { isTransformResponse: false });
export const deviceListApi = (params: any) => defHttp.get({ url: Api.deviceList, params });
@@ -0,0 +1,68 @@
import { BasicColumn } from '/@/components/Table';
export const columns: BasicColumn[] = [
{
title: '工具编码',
dataIndex: 'watchNo',
},
{
title: '归属单位',
dataIndex: 'departmentName',
},
{
title: '入库日期',
dataIndex: 'createDate',
width: 110,
},
{
title: '绑定状态',
dataIndex: 'watchStatus',
customRender: ({ text }) => {
switch (text) {
case '0':
return '未绑定';
case '1':
return '已绑定';
default:
return;
}
},
width: 100,
},
{
title: '绑定员工',
dataIndex: 'bindRealName',
customRender: ({ text }) => {
return text || '-';
},
width: 100,
},
{
title: '员工编号',
dataIndex: 'workNo',
customRender: ({ text }) => {
return text || '-';
},
width: 120,
},
{
title: '员工所属部门',
dataIndex: 'userThreeOrgName',
customRender: ({ text }) => {
return text || '-';
},
},
{
title: '绑定日期',
dataIndex: 'bindDate',
customRender: ({ text }) => {
return text || '-';
},
width: 110,
},
{
title: '监测数据',
dataIndex: 'dataInfo',
width: 120,
},
];
@@ -0,0 +1,219 @@
<template>
<div class="outer-d">
<div class="top-title"> 长庆油田穿戴设备 </div>
<div class="map-d">
<template v-if="!loading">
<service-map
ref="serviceMap"
:map-list="mapList"
@get-marker-info="getMarkerInfo"
:label-content="labelContent"
:marker-content="markerContent"
@get-detail="getDetail"
field="departName"
id="departCode"
/>
<div class="search-d">
所属单位
<ApiSelect
:value="orgCode"
:api="allSecondaryDepartsNew"
@change="
(v) => {
orgCode = v;
}
"
resultField="result"
labelField="departName"
valueField="orgCode"
:after-fetch="
(data) => {
data.unshift({
orgCode: '1',
departName: '全部单位',
});
return data;
}
"
:allowClear="true"
:show-default-value="false"
:immediate="true"
:showSearch="true"
:filterOption="
(input: string, option: any): boolean => {
const str: string = input.toLowerCase();
return option.label.toLowerCase().indexOf(str) >= 0;
}"
style="width: 260px"
/>
<a-button type="primary" @click="searchB">查询</a-button>
<a-button style="background-color: #a4adb3; color: #ffffff" @click="resetB">重置</a-button>
</div>
<div class="bottom-d">
<div
class="bottom-d-item"
v-for="(item, index) in wearInfo"
:key="`bottom-d-item-${index}`"
:style="{ margin: index === 1 ? '0 25px 0 50px' : index === 2 ? '0 50px 0 25px' : 0 }"
>
<div>{{ item.value }}</div>
<div>{{ item.name }}</div>
</div>
</div>
</template>
<template v-else>
<div style="width: 100%; height: 100%; display: flex; align-items: center; justify-content: center">
<a-spin size="large" />
</div>
</template>
</div>
</div>
<wear-modal @register="registerModal" />
</template>
<script setup lang="ts">
import ServiceMap from '/@/views/archivesManage/institution/timeService/components/serviceMap.vue';
import { nextTick, onMounted, ref, onBeforeUnmount } from 'vue';
import { allSecondaryDepartsNew } from '/@/utils/orgSearchInfo';
import ApiSelect from '/@/components/Form/src/components/ApiSelect.vue';
import { listApi } from '/@/views/archivesManage/institution/timeService/wear/wear.api';
import WearModal from '/@/views/archivesManage/institution/timeService/wear/components/wearModal.vue';
import watch from '/@/assets/images/watch.png';
import { useModal } from '/@/components/Modal';
const [registerModal, { openModal }] = useModal();
const loading = ref(true);
const serviceMap = ref();
const mapList = ref();
const orgCode = ref('1');
const wearInfo = ref<any[]>([]);
const markerContent = ref(`<div style="white-space: nowrap;display: flex;background-color: #58a55c;padding: 5px 10px;border-radius: 10px;
align-items: center;">
<img src="${watch}" alt="" style="width: 20px;height: 20px; margin-right: 5px"/>
<span style="color: #ffffff">^&</span>
</div>`);
const labelContent = ref('');
onMounted(async () => {
await initList();
loading.value = false;
await nextTick(() => {
serviceMap.value.initMap(mapList.value);
});
});
onBeforeUnmount(() => {
serviceMap.value.destroyMap();
});
async function initList() {
try {
const { code, result, total, distribution, wear, normal } = await listApi({ orgCode: orgCode.value === '1' ? '' : orgCode.value });
if (code === 200) {
mapList.value = result;
wearInfo.value = [
{ value: total, name: '穿戴设备总数' },
{ value: distribution, name: '穿戴设备配发人数' },
{ value: wear, name: '佩带人数' },
{ value: normal, name: '运行正常设备数' },
];
}
} catch (e) {
mapList.value = [];
console.log(e);
}
}
function getMarkerInfo(e) {
labelContent.value = `
<div style="padding: 10px;background-color: #ffffff;border-radius: 10px">
<div style="padding-top: 8px;font-weight: bold;font-size: 14px">所属单位:${e.departName || '-'} </div>
<div style="padding-top: 8px;font-weight: bold;font-size: 14px">设备数量:${e.total} </div>
<div style="padding-top: 8px;font-weight: bold;font-size: 14px">配发人数:${e.distribution} </div>
<div style="padding-top: 8px;font-weight: bold;font-size: 14px">佩戴人数:${e.wear} </div>
<div style="padding-top: 8px;font-weight: bold;font-size: 14px">设备运行正常数量:${e.normal} </div>
<div style="padding-top: 8px;text-align: center;color: #5087ec" onclick="getDetail()">查看设备数据></div>
</div>
`;
serviceMap.value.setLabel(labelContent.value);
}
async function searchB() {
await initList();
}
async function resetB() {
orgCode.value = '1';
await searchB();
}
function getDetail(e) {
openModal(true, {
record: e,
});
}
</script>
<style scoped lang="less">
.outer-d {
position: relative;
width: 100%;
height: 100%;
}
.top-title {
padding: 10px;
text-align: center;
font-size: 20px;
}
.search-d {
box-shadow: 2px 2px 0 0 rgba(0, 0, 0, 0.35);
border-radius: 5px;
background-color: #ffffff;
padding: 10px 20px;
position: absolute;
top: 10px;
left: 10px;
z-index: 99;
> :nth-child(n + 1) {
margin-left: 10px;
}
}
.bottom-d {
border-radius: 5px;
width: 100%;
position: absolute;
bottom: 8%;
left: 0;
z-index: 99;
display: flex;
align-items: center;
justify-content: center;
.bottom-d-item {
border-radius: 5px;
box-shadow: 2px 2px 4px 1px rgba(0, 0, 0, 0.35);
background-color: #ffffff;
font-weight: bold;
padding: 0 50px;
> div {
text-align: center;
&:nth-child(1) {
font-size: 18px;
padding: 10px 0 5px 0;
}
&:nth-child(2) {
padding: 5px 0 10px 0;
}
}
}
}
.map-d {
position: absolute;
width: calc(100% - 20px);
height: calc(100% - 70px);
top: 50px;
left: 10px;
}
</style>