Browse Source

开发地图设计器布局

master
wangxiang 3 years ago
parent
commit
3d4acd06b4
  1. 2
      src/api/platform/common/controller/org.ts
  2. 2
      src/api/platform/common/entity/hospital.ts
  3. 4
      src/api/platform/common/entity/org.ts
  4. 2
      src/api/platform/core/entity/user.ts
  5. 0
      src/components/AMapDesigner/index.ts
  6. 344
      src/components/AMapDesigner/src/Amap.vue
  7. 4
      src/components/AMapDesigner/src/MapDataSettingsModal.vue
  8. 0
      src/components/AMapDesigner/src/MutualTaskModal.vue
  9. 0
      src/components/AMapDesigner/src/TaskModal.vue
  10. 0
      src/components/AMapDesigner/src/map.data.ts
  11. 28
      src/views/common/mapLogistic/MapLogisticModal.vue

2
src/api/platform/common/controller/org.ts

@ -10,7 +10,7 @@ enum Api { @@ -10,7 +10,7 @@ enum Api {
del = '/common_proxy/common/org/remove'
}
export const listOrg = (params: OrgParams) => defHttp.get<OrgResult>({url: Api.list, params}, { isReturnResultResponse: true });
export const listOrg = (params?: Partial<OrgParams>) => defHttp.get<OrgResult>({url: Api.list, params}, { isReturnResultResponse: true });
export const addOrg = (params: Partial<Org>) => defHttp.post({ url: Api.save, data: params });

2
src/api/platform/common/entity/hospital.ts

@ -14,7 +14,7 @@ export interface Hospital extends CommonEntity { @@ -14,7 +14,7 @@ export interface Hospital extends CommonEntity {
contactPhone: string;
contactTitle: string;
payment: string;
addressIds: string;
regionIds: string;
detailAddress: string;
status: string;
mapOrientation: number;

4
src/api/platform/common/entity/org.ts

@ -11,12 +11,12 @@ export interface Org extends CommonEntity{ @@ -11,12 +11,12 @@ export interface Org extends CommonEntity{
contactPhone: string;
contactTitle: string;
addressNames: string;
addressIds: string;
regionIds: string;
detailAddress: string;
status: string;
mapOrientation: number;
mapLng: number;
mapLat: number;
mapNotify: string;
}
export type OrgResult = R<Org[]>;

2
src/api/platform/core/entity/user.ts

@ -37,6 +37,8 @@ export interface User extends CommonEntity { @@ -37,6 +37,8 @@ export interface User extends CommonEntity {
userName: string;
// 昵称
nickName: string;
// 用户类型
userType: string;
// 头像
avatar: string;
// 所属部门ID

0
src/components/AMap/index.ts → src/components/AMapDesigner/index.ts

344
src/components/AMap/src/Amap.vue → src/components/AMapDesigner/src/Amap.vue

@ -1,10 +1,38 @@ @@ -1,10 +1,38 @@
<template>
<PageWrapper v-loading="mapState.loading"
contentClass="flex"
contentFullHeight
fixedHeight
dense
>
<div class="amap-designer">
<div class="headToolbar">
<ATooltip title="保存并发布"
placement="bottom"
:arrowPointAtCenter="true">
<a-button type="primary" @click="">
<Icon icon="fa6-regular:floppy-disk" size="13"/>保存
</a-button>
</ATooltip>
<ATooltip title="放大"
placement="bottom"
:arrowPointAtCenter="true">
<a-button @click="">
<Icon icon="fa6-solid:magnifying-glass-plus" size="13"/>
</a-button>
</ATooltip>
<a-button>
{{ Math.ceil(mapState.defaultZoom * 10 * 10) + "%" }}
</a-button>
<ATooltip title="缩小"
placement="bottom"
:arrowPointAtCenter="true">
<a-button @click="">
<Icon icon="fa6-solid:magnifying-glass-minus" size="13"/>
</a-button>
</ATooltip>
<ATooltip title="重置"
placement="bottom"
:arrowPointAtCenter="true">
<a-button @click="">
<Icon icon="fa-solid:database" size="13"/>
</a-button>
</ATooltip>
</div>
<div id="mapview" ref="mapview">
<div id="mapPanel" ref="mapPanel"/>
</div>
@ -19,13 +47,13 @@ @@ -19,13 +47,13 @@
:model="mapState.modelRef"
>
<AFormItem label="发单点">
<ASelect v-model:value="mapState.modelRef.smallHospitalId"
<ASelect v-model:value="mapState.modelRef.sendOrderId"
:options="mapState.hospitalList"
@select="handleTakeSpecimenSearch"
/>
</AFormItem>
<AFormItem label="起点">
<ASelect v-model:value="mapState.modelRef.takeSpecimenId" :options="mapState.takeSpecimenList"/>
<ASelect v-model:value="mapState.modelRef.courierUserId" :options="mapState.courierUserList"/>
</AFormItem>
<AFormItem label="添加任务">
<a-button type="primary" @click="handleOpenTask">打开面板</a-button>
@ -37,37 +65,45 @@ @@ -37,37 +65,45 @@
</div>
</div>
<TaskModal @register="registerModal" @success="handleRefreshTable"/>
</PageWrapper>
</div>
</template>
<script lang="ts" setup>
import AMapLoader from '@amap/amap-jsapi-loader';
import { reactive, watchEffect, getCurrentInstance, onBeforeMount, onUnmounted } from 'vue';
import { largeHospitalMapList, smallHospitalMapList, specimenMapList, columns } from './map.data';
import { smallHospitalMapList, specimenMapList, columns } from './map.data';
import hospital from '/@/assets/images/hospital.svg';
import medicalKit from '/@/assets/images/medical-kit.svg';
import hospitalTwinkle from '/@/assets/images/hospital-twinkle.gif';
import redFlag from '/@/assets/images/redFlag.svg';
import { PageWrapper } from '/@/components/Page';
import { Form, Select } from 'ant-design-vue';
import { Form, Select, Button, Tooltip } from 'ant-design-vue';
import { BasicTable, useTable } from '/@/components/Table';
import { useModal } from '/@/components/Modal';
import TaskModal from './TaskModal.vue';
import { propTypes } from '/@/utils/propTypes';
import { listHospital } from '/@/api/platform/common/controller/hospital';
import { listUser } from '/@/api/platform/system/controller/user';
import { listOrg } from '/@/api/platform/common/controller/org';
import AButton from "/@/components/Button/src/BasicButton.vue";
import { Icon } from '/@/components/Icon';
const mapProps = defineProps({
sidebarControl: propTypes.bool.def(true)
options: propTypes.object.def({
}),
sidebarControl: propTypes.bool.def(true),
});
let map;
let largeHospitalMarkerCluster;
let smallHospitalMarkerCluster;
let takeSpecimenCluster;
let scanTakeSpecimenCircleRange;
let orgMarkerCluster;
let hospitalMarkerCluster;
let courierUserMarkerCluster;
let scanCourierUserCircleRange;
let driving;
const instance = getCurrentInstance();
const mapState = reactive<any>({
loading: false,
defaultZoom: 1,
toggleOperatePanelClass: {
span: 'none',
p: 'block',
@ -75,15 +111,25 @@ @@ -75,15 +111,25 @@
},
toggleStatus: true,
modelRef: {
smallHospitalId: '',
takeSpecimenId: ''
sendOrderId: '',
courierUserId: ''
},
takeSpecimenList: [],
hospitalList: []
/** 收样员集合收样员集合 */
courierUserList: [],
/** 下级医院集合 */
hospitalList: [],
/** 上级医检集合 */
orgList: [],
/** 地图任务数据 */
mapTask: [],
/** 地图预览点数据 */
mapLogisticPoint: []
});
const AForm = Form;
const AFormItem = Form.Item;
const ASelect = Select;
const AButtonGroup = Button.Group;
const ATooltip = Tooltip;
const [registerTable, { reload }] = useTable({
title: '地图任务列表',
rowKey: 'id',
@ -95,7 +141,7 @@ @@ -95,7 +141,7 @@
taskName: '测试任务2'
}],
columns,
resizeHeightOffset: 5,
resizeHeightOffset: 80,
showTableSetting: true,
tableSetting: {
setting: false,
@ -114,30 +160,48 @@ @@ -114,30 +160,48 @@
pitch:60,
viewMode:'3D',
resizeEnable: true,
center: [112.919043,28.288623],
center: [112.919043, 28.288623],
zoom: 17,
keyboardEnable: true
}
};
mapState.loading = true;
//
//
listHospital({ size: 40 }).then(res => {
mapState.hospitalList = (res.data || []).map(item => ({
mapState.hospitalList = res.data?.map(item => ({
value: item.id,
label: item.name,
mapLat: item.mapLat,
mapLng: item.mapLng,
mapNotify: item.mapNotify,
mapNotify: !!~~item.mapNotify,
mapOrientation: item.mapOrientation
}));
})
});
listUser({ size: 40, userType: '1' }).then(res => {
mapState.courierUserList = res.data?.map(item => ({
value: item.id,
label: item.nickName,
mapOrientation: item.mapOrientation,
// todo: SSEGPS
mapLat: 0,
mapLng: 0
}));
});
listOrg({ size: 40 }).then(res => {
mapState.orgList = res.data?.map(item => ({
value: item.id,
label: item.name,
mapOrientation: item.mapOrientation,
mapLat: item.mapLat,
mapLng: item.mapLng
}))
});
//
AMapLoader.load({
key: mapConfig.amapKey,
version: '2.0',
plugins: ['AMap.MarkerCluster', 'AMap.Driving']
}).then(AMap => {
//
map = new AMap.Map(instance.refs.mapview, mapConfig.options);
map.plugin(['AMap.ToolBar', 'AMap.MapType', 'AMap.ControlBar', 'AMap.Scale'], () => {
@ -158,22 +222,22 @@ @@ -158,22 +222,22 @@
);
});
//
largeHospitalMarkerCluster = new AMap.MarkerCluster(map, [], {
//
orgMarkerCluster = new AMap.MarkerCluster(map, [], {
gridSize: 80,
maxZoom: 14,
renderMarker(ctx) {
const { marker, data } = ctx;
if (Array.isArray(data) && data[0]) {
const { title, orientation } = data[0];
const content = `<img width="30px" height="30px" style="transform: scale(1) rotate(${360 - Number(orientation)}deg);" src='${redFlag}'/>`;
const { label, mapOrientation } = data[0];
const content = `<img width="30px" height="30px" style="transform: scale(1) rotate(${360 - Number(mapOrientation)}deg);" src='${redFlag}'/>`;
marker.setContent(content);
marker.setLabel({
direction: 'bottom',
//
offset: new AMap.Pixel(-4, 0),
//
content: `<div>${title}</div>`
content: `<div>${label}</div>`
});
marker.setOffset(new AMap.Pixel(-18, -10));
}
@ -182,46 +246,46 @@ @@ -182,46 +246,46 @@
const { clusterData, marker, count } = ctx;
const content = `<img width="30px" height="30px" src='${redFlag}'/>`;
marker.setContent(content);
const title = count == 1 ? clusterData[0].title : `上级医检医院数量:${count}`;
const label = count == 1 ? clusterData[0].label : `医检机构数量:${count}`;
marker.setLabel({
direction: 'bottom',
//
offset: new AMap.Pixel(-4, 0),
//
content: `<div>${title}</div>`
content: `<div>${label}</div>`
});
marker.setOffset(new AMap.Pixel(-18, -10));
}
});
largeHospitalMarkerCluster.on('click', ctx => {
orgMarkerCluster.on('click', ctx => {
const { lnglat } = ctx;
map.setZoomAndCenter(18, lnglat);
});
const largeHospitalPoints = largeHospitalMapList.map((v: any) => ({
lnglat: [v.lng, v.lat],
const orgPoints = mapState.orgList.map((v: any) => ({
lnglat: [v.mapLng, v.mapLat],
...v
}));
largeHospitalMarkerCluster?.setData(largeHospitalPoints);
orgMarkerCluster?.setData(orgPoints);
//
smallHospitalMarkerCluster = new AMap.MarkerCluster(map, [], {
//
hospitalMarkerCluster = new AMap.MarkerCluster(map, [], {
gridSize: 80,
maxZoom: 14,
renderMarker(ctx) {
const { marker, data } = ctx;
if (Array.isArray(data) && data[0]) {
const { title, orientation, notify } = data[0];
let content = `<img width="30px" height="30px" style="transform: scale(1) rotate(${360 - Number(orientation)}deg);" src='${hospital}'/>`;
const { label, mapOrientation, mapNotify } = data[0];
let content = `<img width="30px" height="30px" style="transform: scale(1) rotate(${360 - Number(mapOrientation)}deg);" src='${hospital}'/>`;
marker.setLabel({
direction: 'bottom',
//
offset: new AMap.Pixel(-4, 0),
//
content: `<div>${title}</div>`
content: `<div>${label}</div>`
});
marker.setOffset(new AMap.Pixel(-18, -10));
if (notify) {
content = `<img width="30px" height="30px" style="transform: scale(1) rotate(${360 - Number(orientation)}deg);" src='${hospitalTwinkle}'/>`;
if (mapNotify) {
content = `<img width="30px" height="30px" style="transform: scale(1) rotate(${360 - Number(mapOrientation)}deg);" src='${hospitalTwinkle}'/>`;
}
marker.setContent(content);
}
@ -229,47 +293,47 @@ @@ -229,47 +293,47 @@
renderClusterMarker(ctx) {
const { clusterData, marker, count } = ctx;
let content = `<img width="30px" height="30px" src='${hospital}'/>`;
if(clusterData.some(item => item.notify)){
if(clusterData.some(item => item.mapNotify)){
content = `<img width="30px" height="30px" src='${hospitalTwinkle}'/>`;
}
marker.setContent(content);
const title = count == 1 ? clusterData[0].title : `下级医院数量:${count}`;
const label = count == 1 ? clusterData[0].label : `医院数量:${count}`;
marker.setLabel({
direction: 'bottom',
//
offset: new AMap.Pixel(-4, 0),
//
content: `<div>${title}</div>`
content: `<div>${label}</div>`
});
marker.setOffset(new AMap.Pixel(-18, -10));
}
});
smallHospitalMarkerCluster.on('click', ctx => {
hospitalMarkerCluster.on('click', ctx => {
const { lnglat } = ctx;
map.setZoomAndCenter(18, lnglat);
});
const smallHospitalPoints = smallHospitalMapList.map((v: any) => ({
lnglat: [v.lng, v.lat],
const hospitalPoints = mapState.hospitalList.map((v: any) => ({
lnglat: [v.mapLng, v.mapLat],
...v
}));
smallHospitalMarkerCluster?.setData(smallHospitalPoints);
hospitalMarkerCluster?.setData(hospitalPoints);
//
takeSpecimenCluster = new AMap.MarkerCluster(map, [], {
//
courierUserMarkerCluster = new AMap.MarkerCluster(map, [], {
gridSize: 80,
maxZoom: 14,
renderMarker(ctx) {
const { marker, data } = ctx;
if (Array.isArray(data) && data[0]) {
const { title, orientation } = data[0];
const content = `<img width="30px" height="30px" style="transform: scale(1) rotate(${360 - Number(orientation)}deg);" src='${medicalKit}'/>`;
const { label, mapOrientation } = data[0];
const content = `<img width="30px" height="30px" style="transform: scale(1) rotate(${360 - Number(mapOrientation)}deg);" src='${medicalKit}'/>`;
marker.setContent(content);
marker.setLabel({
direction: 'bottom',
//
offset: new AMap.Pixel(-4, 0),
//
content: `<div>${title}</div>`
content: `<div>${label}</div>`
});
marker.setOffset(new AMap.Pixel(-18, -10));
}
@ -278,29 +342,29 @@ @@ -278,29 +342,29 @@
const { clusterData, marker, count } = ctx;
const content = `<img width="30px" height="30px" src='${medicalKit}'/>`;
marker.setContent(content);
const title = count == 1 ? clusterData[0].title : `收样员数量:${count}`;
const label = count == 1 ? clusterData[0].label : `收样员数量:${count}`;
marker.setLabel({
direction: 'bottom',
//
offset: new AMap.Pixel(-4, 0),
//
content: `<div>${title}</div>`
content: `<div>${label}</div>`
});
marker.setOffset(new AMap.Pixel(-18, -10));
}
});
takeSpecimenCluster.on('click', ctx => {
courierUserMarkerCluster.on('click', ctx => {
const { lnglat } = ctx;
map.setZoomAndCenter(18, lnglat);
});
const specimenPoints = specimenMapList.map((v: any) => ({
lnglat: [v.lng, v.lat],
const courierUserPoints = mapState.courierUserList.map((v: any) => ({
lnglat: [v.mapLng, v.mapLat],
...v
}));
takeSpecimenCluster?.setData(specimenPoints);
courierUserMarkerCluster?.setData(courierUserPoints);
//
scanTakeSpecimenCircleRange = new AMap.Circle({
scanCourierUserCircleRange = new AMap.Circle({
radius: 14000,
borderWeight: 3,
strokeColor: '#3600ff',
@ -318,7 +382,6 @@ @@ -318,7 +382,6 @@
map: map,
panel: instance.refs.mapPanel
});
// 线
driving.search(new AMap.LngLat(112.913864, 28.295114), new AMap.LngLat(112.918119, 28.282891), {
waypoints:[new AMap.LngLat(112.919165, 28.289924)]
@ -408,77 +471,110 @@ @@ -408,77 +471,110 @@
openModal(true, { driving, formData: mapState.modelRef });
}
function handleRefreshTable() {
}
</script>
<style lang="less" scoped>
#mapview {
margin-right: v-bind('mapState.toggleOperatePanelClass.siderWidth + "px"');
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
//
.amap-designer {
@headerHeight: 48px;
@siderWidth: v-bind('mapState.toggleOperatePanelClass.siderWidth + "px"');
@borderColor: #e0e0e0;
#mapPanel {
#mapview {
position: absolute;
background-color: white;
max-height: 90%;
overflow-y: auto;
top: 130px;
right: 18px;
width: 260px;
z-index: 1;
}
top: @headerHeight;
left: 0;
right: @siderWidth;
bottom: 0;
#mapPanel :deep(.amap-lib-driving) {
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
overflow: hidden;
#mapPanel {
position: absolute;
background-color: white;
max-height: 90%;
overflow-y: auto;
top: 130px;
right: 18px;
width: 260px;
z-index: 1;
}
#mapPanel :deep(.amap-lib-driving) {
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
overflow: hidden;
}
}
}
.operatePanel {
width: v-bind('mapState.toggleOperatePanelClass.siderWidth + "px"');
height: 100%;
background: #ffffff;
position: absolute;
top: 0;
right: 0;
z-index: 2;
.operatePanel-arrow {
width: 15px;
height: 94px;
margin: -48px 0 0 -15px;
background: #0075ff;
.operatePanel {
border: 1px solid @borderColor;
width: @siderWidth;
height: 100%;
background-color: white;
position: absolute;
top: 50%;
cursor: pointer;
line-height: 88px;
font-size: 36px;
font-weight: 200;
font-family: Times;
text-align: center;
border-radius: 4px 0 0 4px;
color: #fff;
visibility: visible;
left: 0;
box-shadow: 0 2px 10px rgba(0,0,0,.2);
display: block;
span {
display: v-bind('mapState.toggleOperatePanelClass.span');
top: 0;
right: 0;
z-index: 2;
.operatePanel-arrow {
width: 15px;
height: 94px;
margin: -48px 0 0 -15px;
background: #0075ff;
position: absolute;
top: 50%;
cursor: pointer;
line-height: 88px;
font-size: 36px;
font-weight: 200;
font-family: Times;
text-align: center;
border-radius: 4px 0 0 4px;
color: #fff;
visibility: visible;
left: 0;
box-shadow: 0 2px 10px rgba(0,0,0,.2);
display: block;
span {
display: v-bind('mapState.toggleOperatePanelClass.span');
}
p {
display: v-bind('mapState.toggleOperatePanelClass.p');
}
}
p {
display: v-bind('mapState.toggleOperatePanelClass.p');
.operatePanel-form {
margin-top: 10px;
}
}
.operatePanel-form {
margin-top: 10px;
.operatePanel-list {
overflow: hidden;
}
}
.operatePanel-list {
overflow: hidden;
.headToolbar {
padding: 0 20px;
background-color: white;
border: 1px solid @borderColor;
height: @headerHeight;
position: absolute;
top: 0;
left: 0;
right: @siderWidth;
bottom: 0;
text-align: left;
line-height: 50px!important;
height: 50px !important;
.ant-btn {
font-size: 13px;
margin-right: 1px;
}
}
}

4
src/components/AMap/src/MapDataSettingsModal.vue → src/components/AMapDesigner/src/MapDataSettingsModal.vue

@ -2,8 +2,8 @@ @@ -2,8 +2,8 @@
import { reactive, nextTick, ref, h } from 'vue';
import { BasicModal, ModalProps, useModalInner } from '/@/components/Modal';
import { Form, Select, Row, Col } from 'ant-design-vue';
import { AMap } from '/@/components/AMap';
import { formMapDataSettingsColumns } from '/@/components/AMap/src/map.data';
import { AMap } from '/@/components/AMapDesigner';
import { formMapDataSettingsColumns } from '/@/components/AMapDesigner/src/map.data';
import Sortable from 'sortablejs';
import { VxeGridProps } from 'vxe-table';

0
src/components/AMap/src/MutualTaskModal.vue → src/components/AMapDesigner/src/MutualTaskModal.vue

0
src/components/AMap/src/TaskModal.vue → src/components/AMapDesigner/src/TaskModal.vue

0
src/components/AMap/src/map.data.ts → src/components/AMapDesigner/src/map.data.ts

28
src/views/common/mapLogistic/MapLogisticModal.vue

@ -1,7 +1,8 @@ @@ -1,7 +1,8 @@
<template>
<BasicModal v-bind="$attrs"
defaultFullscreen
@ok="handleSubmit"
:showOkBtn="false"
:showCancelBtn="false"
@register="registerModal"
>
<AMap/>
@ -16,7 +17,7 @@ @@ -16,7 +17,7 @@
*/
import { ref, unref } from 'vue';
import { BasicModal, ModalProps, useModalInner } from '/@/components/Modal';
import { AMap } from '/@/components/AMap';
import { AMap } from '/@/components/AMapDesigner';
/** 通用变量统一声明区域 */
const tag = ref<Nullable<string>>('');
@ -44,27 +45,4 @@ @@ -44,27 +45,4 @@
// :
setModalProps(props);
});
/** 处理弹出框提交 */
async function handleSubmit() {
try {
//
setModalProps({ confirmLoading: true });
// tag
switch (unref(tag)) {
case 'add':
break;
case 'edit':
break;
}
//
closeModal();
emit('success');
} finally {
setModalProps({ confirmLoading: false });
}
}
</script>

Loading…
Cancel
Save