Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4c30187435 | ||
|
|
9431553951 | ||
|
|
5293b509b2 | ||
|
|
f55544321b | ||
|
|
ecd1e5cd46 | ||
|
|
a22c59f220 | ||
|
|
c33ad0d807 | ||
|
|
7b6d6e52b2 | ||
|
|
336a881855 | ||
|
|
9f6deb49a0 | ||
|
|
4d9b1ac3cb | ||
|
|
e021bad791 | ||
|
|
8095b6b503 | ||
|
|
8cc8945b68 | ||
|
|
bbb2cdc04e | ||
|
|
c583f85516 | ||
|
|
849fc8778d | ||
|
|
49597a4dd2 | ||
|
|
1c908284e6 | ||
|
|
6241504095 | ||
|
|
4f9d79461c | ||
|
|
bb0536c7e4 | ||
|
|
d95a6942cb | ||
|
|
bbc5685be0 | ||
|
|
f943ee8df3 | ||
|
|
0b67be998a | ||
|
|
482d165651 | ||
|
|
822fecc3b1 | ||
|
|
f2570ef494 | ||
|
|
8c9eafa2fb | ||
|
|
ded845793a | ||
|
|
53195cc5c2 | ||
|
|
87490ae4b5 | ||
|
|
faa0fa991c | ||
|
|
5589f92e72 | ||
|
|
4db07874f5 | ||
|
|
e09f90203f | ||
|
|
593c8bfe0b | ||
|
|
61e83f6c78 | ||
|
|
4e1c4b87c4 | ||
|
|
ea062a4ba9 | ||
|
|
cb8f23e9ab | ||
|
|
9407ba979c | ||
|
|
626fb84a6b | ||
|
|
c7f571399a | ||
|
|
ef86526095 | ||
|
|
f617922cd3 | ||
|
|
f44e372210 | ||
|
|
f12c73a911 | ||
|
|
17f9478233 | ||
|
|
98ff86f89c | ||
|
|
028cc03aeb | ||
|
|
ba91d7ff13 | ||
|
|
5d2249d4c7 | ||
|
|
28c61a0731 | ||
|
|
c9f075b106 | ||
|
|
7775b14bba | ||
|
|
e4d3e3ba1c | ||
|
|
7154314e93 | ||
|
|
f024615260 | ||
|
|
d103785a45 | ||
|
|
c7abcf1f67 | ||
|
|
a97d9c64aa | ||
|
|
1714ff19f5 | ||
|
|
bd2d066314 | ||
|
|
832291570f | ||
|
|
3755349ea8 | ||
|
|
68548362eb | ||
|
|
baec4e1ff0 | ||
|
|
0a59f01616 | ||
|
|
ff78173b25 | ||
|
|
a89cc758f7 | ||
|
|
8a386eba5b | ||
|
|
b56efa9f7f | ||
|
|
269e35d19b | ||
|
|
b61ca7f567 | ||
|
|
b52d46cd76 | ||
|
|
4b8f3ae1ef | ||
|
|
cf129e6d23 | ||
|
|
cd1b085499 | ||
|
|
055e28b457 | ||
|
|
b0a16c6f0b | ||
|
|
91abcfd78d | ||
|
|
f6e5bb78da | ||
|
|
7565056a7e | ||
|
|
726bd19ad3 | ||
|
|
67a2ea75d1 | ||
|
|
b55bc1857a | ||
|
|
8a630dd1cd | ||
|
|
07f14c7703 | ||
|
|
a2da7e4be7 | ||
|
|
01f3339833 | ||
|
|
aa71fd5c7e | ||
|
|
86ed8341e2 | ||
|
|
22c5b89f90 | ||
|
|
7e2346f5fb | ||
|
|
ff493d83d9 | ||
|
|
1cbda373e0 | ||
|
|
0a6843406a |
@@ -1,7 +1,7 @@
|
|||||||
# 王昊地址
|
# 王昊地址
|
||||||
#VITE_GLOB_API_URL=http://192.168.1.27
|
#VITE_GLOB_API_URL=http://192.168.1.98
|
||||||
#开发环境
|
#开发环境
|
||||||
VITE_GLOB_API_URL=http://cqyt.dev.yg.dt.io
|
VITE_GLOB_API_URL=http://api-xj.tri.dt.io
|
||||||
#VITE_GLOB_API_URL=https://api.cqygjk.com
|
#VITE_GLOB_API_URL=https://api.cqygjk.com
|
||||||
#健康监测工具报警-websocket
|
#健康监测工具报警-websocket
|
||||||
#VITE_GLOB_API_YIN_JI=ws://cqyt.dev.yg.dt.io/health-watch/websocket/watchMonitor
|
#VITE_GLOB_API_YIN_JI=ws://cqyt.dev.yg.dt.io/health-watch/websocket/watchMonitor
|
||||||
|
|||||||
@@ -1,12 +1,3 @@
|
|||||||
|
|
||||||
#后台地址
|
#后台地址
|
||||||
VITE_GLOB_API_URL=https://api.cqygjk.com
|
VITE_GLOB_API_URL=http://api-xj.tri.dt.io
|
||||||
#健康监测工具报警-websocket
|
|
||||||
VITE_GLOB_API_YIN_JI=wss://api.cqygjk.com/health-watch/websocket/watchMonitor
|
|
||||||
#应急求助-websocket
|
|
||||||
VITE_GLOB_API_JIAN_CE=wss://api.cqygjk.com/health-emergency/websocket/emergency
|
|
||||||
|
|
||||||
#西安云坐席
|
|
||||||
VITE_GLOB_API_XIAN_YUN=wss://api.cqygjk.com/health-emergency/websocket/emergency3
|
|
||||||
#银川云坐席
|
|
||||||
VITE_GLOB_API_YIN_CHUAN=wss://api.cqygjk.com/health-emergency/websocket/emergency4
|
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
FROM nginx
|
||||||
|
MAINTAINER jeecgos@163.com
|
||||||
|
VOLUME /tmp
|
||||||
|
ENV LANG en_US.UTF-8
|
||||||
|
|
||||||
|
ENV VITE_GLOB_API_URL http://api-xj.tri.dt.io
|
||||||
|
|
||||||
|
RUN echo "server { \
|
||||||
|
#解决Router(mode: 'history')模式下,刷新路由地址不能找到页面的问题 \
|
||||||
|
location / { \
|
||||||
|
root /var/www/html/; \
|
||||||
|
index index.html index.htm; \
|
||||||
|
if (!-e \$request_filename) { \
|
||||||
|
rewrite ^(.*)\$ /index.html?s=\$1 last; \
|
||||||
|
break; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
access_log /var/log/nginx/access.log ; \
|
||||||
|
} " > /etc/nginx/conf.d/default.conf \
|
||||||
|
&& mkdir -p /var/www \
|
||||||
|
&& mkdir -p /var/www/html
|
||||||
|
|
||||||
|
ADD dist/ /var/www/html/
|
||||||
|
|
||||||
|
# 👇 新增:复制启动脚本(用于运行时替换环境变量)
|
||||||
|
COPY entrypoint.sh /entrypoint.sh
|
||||||
|
RUN chmod +x /entrypoint.sh
|
||||||
|
|
||||||
|
# 使用脚本作为入口点
|
||||||
|
ENTRYPOINT ["bash","/entrypoint.sh"]
|
||||||
|
|
||||||
|
EXPOSE 80
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# 设置默认值(构建时 .env 不生效时兜底)
|
||||||
|
: ${VITE_GLOB_API_URL:=https://api.xjygjk.com}
|
||||||
|
|
||||||
|
# 替换 index.html 中的占位符
|
||||||
|
sed -i "s@__VITE_GLOB_API_URL__@$VITE_GLOB_API_URL@g" /var/www/html/index.html
|
||||||
|
|
||||||
|
exec nginx -g 'daemon off;'
|
||||||
@@ -7,11 +7,18 @@
|
|||||||
<title>应急就医服务中心</title>
|
<title>应急就医服务中心</title>
|
||||||
<!-- 全局配置 -->
|
<!-- 全局配置 -->
|
||||||
<script>
|
<script>
|
||||||
window._CONFIG = {};
|
// // 默认值(可选)
|
||||||
|
// window.__APP_ENV__ = {
|
||||||
|
// VITE_GLOB_API_URL: '__VITE_GLOB_API_URL__', // 占位符,Docker 替换
|
||||||
|
// };
|
||||||
window._AMapSecurityConfig = {
|
window._AMapSecurityConfig = {
|
||||||
securityJsCode: "1b3b86585c863d22db1e7f7636a9e495",
|
securityJsCode: "54f1d830d2bb225c173b76f5780b65eb",
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
<!-- __APP_ENV__ 由 Vite 在构建时注入 -->
|
||||||
|
<!-- 开发环境:不注入或注入空值 -->
|
||||||
|
<!-- 生产环境:注入真实值(或占位符供 Docker 替换) -->
|
||||||
|
<!-- __INJECT_APP_ENV__ -->
|
||||||
</head>
|
</head>
|
||||||
<style>
|
<style>
|
||||||
html, body, #app {
|
html, body, #app {
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite --open",
|
"dev": "vite --open",
|
||||||
|
"pro": "vite --open --mode production",
|
||||||
|
"test": "vite --open --mode test",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"build:dev": "vite build --mode dev",
|
"build:dev": "vite build --mode dev",
|
||||||
"build:test": "vite build --mode test",
|
"build:test": "vite build --mode test",
|
||||||
@@ -18,6 +20,7 @@
|
|||||||
"@turf/turf": "^6.5.0",
|
"@turf/turf": "^6.5.0",
|
||||||
"autoprefixer": "^10.4.16",
|
"autoprefixer": "^10.4.16",
|
||||||
"axios": "^1.6.2",
|
"axios": "^1.6.2",
|
||||||
|
"cnpm": "^9.4.0",
|
||||||
"dayjs": "^1.11.10",
|
"dayjs": "^1.11.10",
|
||||||
"echarts": "5.4.3",
|
"echarts": "5.4.3",
|
||||||
"echarts-gl": "^2.0.9",
|
"echarts-gl": "^2.0.9",
|
||||||
@@ -32,6 +35,7 @@
|
|||||||
"postcss-px2rem": "^0.3.0",
|
"postcss-px2rem": "^0.3.0",
|
||||||
"prettier": "^3.1.0",
|
"prettier": "^3.1.0",
|
||||||
"qs": "^6.11.2",
|
"qs": "^6.11.2",
|
||||||
|
"swiper": "^12.0.2",
|
||||||
"vue-router": "^4.2.5",
|
"vue-router": "^4.2.5",
|
||||||
"vue3-seamless-scroll": "^2.0.1"
|
"vue3-seamless-scroll": "^2.0.1"
|
||||||
},
|
},
|
||||||
@@ -42,7 +46,7 @@
|
|||||||
"@typescript-eslint/eslint-plugin": "^6.15.0",
|
"@typescript-eslint/eslint-plugin": "^6.15.0",
|
||||||
"@typescript-eslint/parser": "^6.15.0",
|
"@typescript-eslint/parser": "^6.15.0",
|
||||||
"@vitejs/plugin-vue": "^4.2.3",
|
"@vitejs/plugin-vue": "^4.2.3",
|
||||||
"@vitejs/plugin-vue-jsx": "^4.0.1",
|
"@vitejs/plugin-vue-jsx": "^5.1.1",
|
||||||
"ant-design-vue": "^3.2.20",
|
"ant-design-vue": "^3.2.20",
|
||||||
"eslint": "^8.51.0",
|
"eslint": "^8.51.0",
|
||||||
"eslint-config-prettier": "^8.10.0",
|
"eslint-config-prettier": "^8.10.0",
|
||||||
@@ -53,7 +57,7 @@
|
|||||||
"jest": "^29.7.0",
|
"jest": "^29.7.0",
|
||||||
"less": "^4.2.0",
|
"less": "^4.2.0",
|
||||||
"typescript": "^5.0.2",
|
"typescript": "^5.0.2",
|
||||||
"vite": "^4.4.5",
|
"vite": "^5.4.20",
|
||||||
"vite-plugin-resolve-externals": "^0.2.2",
|
"vite-plugin-resolve-externals": "^0.2.2",
|
||||||
"vue-tsc": "^1.8.5"
|
"vue-tsc": "^1.8.5"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,25 +10,21 @@
|
|||||||
import { onMounted, watch } from 'vue';
|
import { onMounted, watch } from 'vue';
|
||||||
import { getEmergencyCenterApi } from '/@/api/user.ts';
|
import { getEmergencyCenterApi } from '/@/api/user.ts';
|
||||||
import { useUserStore } from '/@/store/modules/user.ts';
|
import { useUserStore } from '/@/store/modules/user.ts';
|
||||||
import { useRouter } from 'vue-router';
|
import { openTestModal } from '/@/utils/utils.ts';
|
||||||
import { getBaseLogin } from '/@/enum/pageEnum.ts';
|
|
||||||
|
|
||||||
dayjs.locale('zh-cn');
|
dayjs.locale('zh-cn');
|
||||||
const locle = zhCN;
|
const locle = zhCN;
|
||||||
|
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
openTestModal();
|
||||||
try {
|
try {
|
||||||
if (userStore.getToken && getBaseLogin() === '/centralScreenLogin') {
|
if (userStore.getToken) {
|
||||||
const { result } = await getEmergencyCenterApi();
|
const { code, result } = await getEmergencyCenterApi();
|
||||||
if (result?.screenPath) {
|
if (code === 200) {
|
||||||
router.replace(result?.screenPath);
|
userStore.setCenterInfo(result);
|
||||||
} else {
|
|
||||||
router.replace('centralScreen');
|
|
||||||
}
|
}
|
||||||
userStore.setCenterInfo(result);
|
|
||||||
}
|
}
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
if (userStore.getUserInfo) {
|
if (userStore.getUserInfo) {
|
||||||
|
|||||||
@@ -31,10 +31,8 @@ service.interceptors.response.use(
|
|||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
if (error.response) {
|
if (error.response) {
|
||||||
console.log('response', error.response);
|
|
||||||
switch (error.response.data.code) {
|
switch (error.response.data.code) {
|
||||||
case 401:
|
case 401:
|
||||||
// debugger;
|
|
||||||
let path = getBaseLogin();
|
let path = getBaseLogin();
|
||||||
if (!path) {
|
if (!path) {
|
||||||
path = toLoginByNowPath(router.currentRoute.value.path);
|
path = toLoginByNowPath(router.currentRoute.value.path);
|
||||||
@@ -64,6 +62,22 @@ export const get = (url, params = {}) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
export const Dictget = (url, params = {}) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
service
|
||||||
|
.get(url, { params: params })
|
||||||
|
.then((res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
resolve(res.data);
|
||||||
|
} else {
|
||||||
|
reject(res.data);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
reject(err.data);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
export const post = (url: string, params: any) =>
|
export const post = (url: string, params: any) =>
|
||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
service
|
service
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { get, post } from '/@/api/request.ts';
|
|||||||
|
|
||||||
enum Api {
|
enum Api {
|
||||||
getInputCode = '/sys/randomImage',
|
getInputCode = '/sys/randomImage',
|
||||||
|
videoApi = '/health-emergency/api/anon/screen-animation/query',
|
||||||
Login = '/sys/login',
|
Login = '/sys/login',
|
||||||
Logout = '/sys/logout',
|
Logout = '/sys/logout',
|
||||||
GetUserInfo = '/sys/user/getUserInfo',
|
GetUserInfo = '/sys/user/getUserInfo',
|
||||||
@@ -9,8 +10,12 @@ enum Api {
|
|||||||
emergencyCenter = '/health-emergency/emergency/center/getCenterDetail',
|
emergencyCenter = '/health-emergency/emergency/center/getCenterDetail',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export const getCodeInfo = (params: any) => get(Api.getInputCode + `/${params}`);
|
export const getCodeInfo = (params: any) => get(Api.getInputCode + `/${params}`);
|
||||||
|
|
||||||
|
export const getVideo = () => get(Api.videoApi);
|
||||||
|
|
||||||
export function loginApi(params: any) {
|
export function loginApi(params: any) {
|
||||||
return post(Api.Login, params);
|
return post(Api.Login, params);
|
||||||
}
|
}
|
||||||
|
|||||||
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 88 KiB |
|
After Width: | Height: | Size: 271 KiB |
|
After Width: | Height: | Size: 538 KiB |
|
After Width: | Height: | Size: 243 KiB |
|
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 180 KiB |
|
After Width: | Height: | Size: 179 KiB |
|
After Width: | Height: | Size: 178 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 23 KiB |
@@ -0,0 +1,29 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||||
|
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||||
|
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="72.000000pt" height="176.000000pt" viewBox="0 0 72.000000 176.000000"
|
||||||
|
preserveAspectRatio="xMidYMid meet">
|
||||||
|
|
||||||
|
<g transform="translate(0.000000,176.000000) scale(0.100000,-0.100000)"
|
||||||
|
fill="#000000" stroke="none">
|
||||||
|
<path d="M269 1715 c-107 -34 -194 -124 -225 -233 -43 -146 37 -312 185 -380
|
||||||
|
45 -22 71 -27 131 -27 130 1 234 66 292 183 63 127 39 271 -62 372 -82 82
|
||||||
|
-218 118 -321 85z m151 -190 c5 -38 7 -40 45 -43 22 -2 43 -6 47 -10 4 -4 8
|
||||||
|
-51 9 -105 1 -53 5 -100 10 -103 5 -3 9 -14 9 -25 0 -18 -9 -19 -165 -20 -91
|
||||||
|
-1 -171 -2 -177 -3 -23 -2 -21 41 1 47 19 5 21 13 21 95 0 49 3 117 6 151 l7
|
||||||
|
62 91 -3 91 -3 5 -40z"/>
|
||||||
|
<path d="M298 1536 c-24 -18 -23 -39 3 -62 22 -19 43 -13 63 19 8 12 5 21 -8
|
||||||
|
36 -22 24 -34 26 -58 7z"/>
|
||||||
|
<path d="M431 1366 c2 -102 18 -126 21 -32 1 36 2 76 2 88 1 12 -5 23 -12 26
|
||||||
|
-10 3 -13 -15 -11 -82z"/>
|
||||||
|
<path d="M267 1424 c-14 -15 6 -24 54 -24 36 0 49 4 47 13 -5 13 -89 23 -101
|
||||||
|
11z"/>
|
||||||
|
<path d="M272 1358 c5 -15 87 -18 91 -3 1 6 -20 11 -47 13 -36 2 -47 0 -44
|
||||||
|
-10z"/>
|
||||||
|
<path d="M283 1313 c-7 -2 -13 -9 -13 -14 0 -12 87 -12 94 0 11 17 -49 27 -81
|
||||||
|
14z"/>
|
||||||
|
<path d="M345 718 c-8 -308 -5 -393 15 -393 12 0 16 16 18 75 5 114 -9 560
|
||||||
|
-18 560 -4 0 -11 -109 -15 -242z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 9.6 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 8.9 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
@@ -0,0 +1,22 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 64 64">
|
||||||
|
<defs>
|
||||||
|
<radialGradient id="a" cx="32" cy="32" r="32" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop offset="0" stop-color="red" stop-opacity="0"/>
|
||||||
|
<stop offset="1" stop-color="red"/>
|
||||||
|
</radialGradient>
|
||||||
|
<radialGradient id="b" cy="32" r="20" xlink:href="#a"/>
|
||||||
|
</defs>
|
||||||
|
<g data-name="图层 2">
|
||||||
|
<g data-name="图层 1">
|
||||||
|
<circle cx="32" cy="32" r="6" style="fill:url(#a);opacity:1">
|
||||||
|
<animate attributeName="r" from="6" to="32" begin="0s" dur="2s" repeatCount="indefinite"/>
|
||||||
|
<animate attributeName="opacity" from="1" to="0" begin="0s" dur="2s" repeatCount="indefinite"/>
|
||||||
|
</circle>
|
||||||
|
<circle cx="32" cy="32" r="6" style="fill:url(#b);opacity:1">
|
||||||
|
<animate attributeName="r" from="6" to="32" begin="1s" dur="2s" repeatCount="indefinite"/>
|
||||||
|
<animate attributeName="opacity" from="1" to="0" begin="1s" dur="2s" repeatCount="indefinite"/>
|
||||||
|
</circle>
|
||||||
|
<circle cx="32" cy="32" r="6" style="fill:red"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 648 B |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 982 B |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 649 B |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 920 B |
@@ -8,7 +8,6 @@ html, body, #app {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.type-time {
|
.type-time {
|
||||||
padding-top: 3%;
|
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: right;
|
justify-content: right;
|
||||||
@@ -116,8 +115,9 @@ html, body, #app {
|
|||||||
|
|
||||||
// 下拉框样式-start
|
// 下拉框样式-start
|
||||||
.ant-select {
|
.ant-select {
|
||||||
color: var(--antSelectFontColor) !important;
|
color: #fff !important;
|
||||||
background-color: var(--antSelectBg);
|
background-color: #082147 !important;
|
||||||
|
border-radius: 4px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-select-dropdown {
|
.ant-select-dropdown {
|
||||||
@@ -127,14 +127,15 @@ html, body, #app {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ant-select-item {
|
.ant-select-item {
|
||||||
color: #1b7ef2 !important;
|
color: #fff !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
//框背景色
|
//框背景色
|
||||||
.ant-select:not(.ant-select-customize-input) .ant-select-selector {
|
.ant-select:not(.ant-select-customize-input) .ant-select-selector {
|
||||||
color: var(--antSelectFontColor) !important;
|
color: var(--antSelectFontColor) !important;
|
||||||
background-color: transparent !important;
|
background-color: transparent !important;
|
||||||
border: 1px solid var(--antSelectDropdownBorderColor) !important;
|
border: 1px solid transparent !important;
|
||||||
|
border-radius: 4px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-select-item-option-selected {
|
.ant-select-item-option-selected {
|
||||||
@@ -214,7 +215,7 @@ html, body, #app {
|
|||||||
|
|
||||||
.ant-modal-title {
|
.ant-modal-title {
|
||||||
color: #576BAD !important;
|
color: #576BAD !important;
|
||||||
background: url('/@/assets/images/dialog-light.png') no-repeat;
|
//background: url('/@/assets/images/dialog-light.png') no-repeat;
|
||||||
background-size: 100% 100%;
|
background-size: 100% 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -230,8 +231,10 @@ html, body, #app {
|
|||||||
|
|
||||||
.ant-modal-title {
|
.ant-modal-title {
|
||||||
color: #ffffff !important;
|
color: #ffffff !important;
|
||||||
background: url('/@/assets/images/dialog.png') no-repeat;
|
//background: url('/@/assets/images/dialog.png') no-repeat;
|
||||||
background-size: 100% 100%;
|
//background-size: 100% 100%;
|
||||||
|
background: #082147;
|
||||||
|
border: 1px solid #00CCFF;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { mapIcon1, mapIcon2, mapIcon3, mapIcon4, mapIcon7 } from '/@/components/chinaMap/chinaHooks.ts';
|
import { mapIcon1, mapIcon11, mapIcon3, mapIcon4, mapIcon41, mapIcon5, mapIcon51, watchIconR } from '/@/components/chinaMap/chinaHooks.ts';
|
||||||
import { watchIcon } from '/@/components/secondaryScreen/secondMap/secondMapHooks.ts';
|
|
||||||
import * as turf from '@turf/turf';
|
import * as turf from '@turf/turf';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -18,29 +17,53 @@ export function createIcon({ icon, iconSize, imageSize }) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc 根据类型创建Icon
|
* @desc 根据类型创建Icon
|
||||||
* @type 1:油田医院,2:合作医院,3:医疗点
|
* @type 1:医院,2:健康小屋,4:AED
|
||||||
*/
|
*/
|
||||||
export function typeToIcon(type: string, centerResource: boolean) {
|
export function typeToIcon(type: string) {
|
||||||
const iconSize = [18, 44];
|
// const iconSize = [28, 49];
|
||||||
const imageSize = [18, 44];
|
// const imageSize = [28, 49];
|
||||||
if (type == '1') {
|
if (type == '1') {
|
||||||
return createIcon({ icon: mapIcon1, iconSize, imageSize });
|
// return createIcon({ icon: mapIcon1, iconSize, imageSize });
|
||||||
}
|
return mapIcon1;
|
||||||
if (type == '2') {
|
|
||||||
return createIcon({ icon: mapIcon4, iconSize, imageSize });
|
|
||||||
}
|
|
||||||
//关联医疗点
|
|
||||||
if (type == '3' && centerResource) {
|
|
||||||
return createIcon({ icon: mapIcon7, iconSize, imageSize });
|
|
||||||
}
|
}
|
||||||
if (type == '3') {
|
if (type == '3') {
|
||||||
return createIcon({ icon: mapIcon2, iconSize, imageSize });
|
// return createIcon({ icon: mapIcon5, iconSize, imageSize });
|
||||||
|
return mapIcon5;
|
||||||
}
|
}
|
||||||
if (type == '5' || type == '4') {
|
if (type == '4') {
|
||||||
|
// return createIcon({ icon: mapIcon4, iconSize, imageSize });
|
||||||
|
return mapIcon4;
|
||||||
|
}
|
||||||
|
if (type == '5') {
|
||||||
|
// return createIcon({ icon: mapIcon3, iconSize, imageSize });
|
||||||
|
return mapIcon3;
|
||||||
|
}
|
||||||
|
if (type === 'user') {
|
||||||
|
// return createIcon({ icon: watchIconR, iconSize, imageSize });
|
||||||
|
return watchIconR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export function typeToIcon1(type: string) {
|
||||||
|
const iconSize = [21, 52];
|
||||||
|
const imageSize = [21, 52];
|
||||||
|
if (type == '1') {
|
||||||
|
const iconS = [28, 65];
|
||||||
|
const imageS = [28, 65];
|
||||||
|
return createIcon({ icon: mapIcon11, iconSize: iconS, imageSize: imageS });
|
||||||
|
}
|
||||||
|
if (type == '3') {
|
||||||
|
return createIcon({ icon: mapIcon51, iconSize, imageSize });
|
||||||
|
}
|
||||||
|
if (type == '4') {
|
||||||
|
return createIcon({ icon: mapIcon41, iconSize, imageSize });
|
||||||
|
}
|
||||||
|
if (type == '5') {
|
||||||
return createIcon({ icon: mapIcon3, iconSize, imageSize });
|
return createIcon({ icon: mapIcon3, iconSize, imageSize });
|
||||||
}
|
}
|
||||||
if (type == '6') {
|
if (type === 'user') {
|
||||||
return createIcon({ icon: watchIcon, iconSize: [23, 51], imageSize: [23, 51] });
|
const iconS = [36, 88];
|
||||||
|
const imageS = [36, 88];
|
||||||
|
return createIcon({ icon: watchIconR, iconSize: iconS, imageSize: imageS });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,14 +92,17 @@ export function createCircle(option) {
|
|||||||
export function getLonLat(result = []) {
|
export function getLonLat(result = []) {
|
||||||
let arr = [];
|
let arr = [];
|
||||||
for (let i = 0; i < result.length; i++) {
|
for (let i = 0; i < result.length; i++) {
|
||||||
let { lon, lat, lng } = result[i];
|
let { lon, lat, lng, longitude, latitude } = result[i];
|
||||||
if (lng) {
|
if (longitude) {
|
||||||
lon = lng;
|
lon = longitude;
|
||||||
}
|
}
|
||||||
if (!lon || !lat) {
|
if (latitude) {
|
||||||
|
lat = latitude;
|
||||||
|
}
|
||||||
|
if (!longitude || !latitude) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let point = turf.point([lon, lat]);
|
let point = turf.point([lon * 1, lat * 1]);
|
||||||
arr.push(point);
|
arr.push(point);
|
||||||
}
|
}
|
||||||
return arr;
|
return arr;
|
||||||
@@ -96,3 +122,330 @@ export function getCenter(list = []) {
|
|||||||
let centerObj = turf.center(features);
|
let centerObj = turf.center(features);
|
||||||
return centerObj?.geometry?.coordinates;
|
return centerObj?.geometry?.coordinates;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc 球面距离(haversine),返回米
|
||||||
|
* @param lng1 经度1
|
||||||
|
* @param lat1 纬度1
|
||||||
|
* @param lng2 经度2
|
||||||
|
* @param lat2 纬度2
|
||||||
|
*/
|
||||||
|
export function haversine(lng1: number, lat1: number, lng2: number, lat2: number): number {
|
||||||
|
const toRad = (d: number) => (d * Math.PI) / 180;
|
||||||
|
const R = 6371008.8; // 地球平均半径,单位米
|
||||||
|
const dLat = toRad(lat2 - lat1);
|
||||||
|
const dLng = toRad(lng2 - lng1);
|
||||||
|
const a = Math.sin(dLat / 2) ** 2 + Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) * Math.sin(dLng / 2) ** 2;
|
||||||
|
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
||||||
|
return R * c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc 单链聚合:距离阈值(米)内的点归为同一簇;簇内不足2点返回 null
|
||||||
|
* @param items 待聚合的点位(必须含 lon/lat 经纬度)
|
||||||
|
* @param radiusMeters 聚合半径,单位米,默认 30
|
||||||
|
*/
|
||||||
|
export function clusterByRadius(items: any[] = [], radiusMeters = 30): any[][] {
|
||||||
|
const clusters: any[][] = [];
|
||||||
|
const visited = new Array(items.length).fill(false);
|
||||||
|
for (let i = 0; i < items.length; i++) {
|
||||||
|
if (visited[i]) continue;
|
||||||
|
const seed = items[i];
|
||||||
|
const seedLng = Number(seed.lon ?? seed.longitude ?? seed.lng);
|
||||||
|
const seedLat = Number(seed.lat ?? seed.latitude);
|
||||||
|
if (!seedLng || !seedLat) continue;
|
||||||
|
const cluster: any[] = [seed];
|
||||||
|
visited[i] = true;
|
||||||
|
for (let j = i + 1; j < items.length; j++) {
|
||||||
|
if (visited[j]) continue;
|
||||||
|
const cur = items[j];
|
||||||
|
const curLng = Number(cur.lon ?? cur.longitude ?? cur.lng);
|
||||||
|
const curLat = Number(cur.lat ?? cur.latitude);
|
||||||
|
if (!curLng || !curLat) continue;
|
||||||
|
const d = haversine(seedLng, seedLat, curLng, curLat);
|
||||||
|
if (d <= radiusMeters) {
|
||||||
|
cluster.push(cur);
|
||||||
|
visited[j] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 不足 2 个点不形成聚合(保留为独立 marker)
|
||||||
|
if (cluster.length >= 2) {
|
||||||
|
clusters.push(cluster);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return clusters;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc 计算簇的几何中心(取所有点的经纬度平均)
|
||||||
|
*/
|
||||||
|
export function getClusterCenter(cluster: any[] = []): [number, number] {
|
||||||
|
if (!cluster.length) return [0, 0];
|
||||||
|
let sumLng = 0;
|
||||||
|
let sumLat = 0;
|
||||||
|
let count = 0;
|
||||||
|
for (const item of cluster) {
|
||||||
|
const lng = Number(item.lon ?? item.longitude ?? item.lng);
|
||||||
|
const lat = Number(item.lat ?? item.latitude);
|
||||||
|
if (!lng || !lat) continue;
|
||||||
|
sumLng += lng;
|
||||||
|
sumLat += lat;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
if (!count) return [0, 0];
|
||||||
|
return [sumLng / count, sumLat / count];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc 按数量返回聚合徽标的颜色
|
||||||
|
* @param count 簇内点数
|
||||||
|
*/
|
||||||
|
export function getClusterColor(count: number): { bg: string; border: string } {
|
||||||
|
if (count >= 10) return { bg: '#E74C3C', border: '#B83A2D' };
|
||||||
|
if (count >= 5) return { bg: '#F5A623', border: '#C77F12' };
|
||||||
|
return { bg: '#3FA9F5', border: '#2380C7' };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc 生成聚合 marker 的 HTML 内容(纯色圆形 + 数量 N + 左上角位置编号角标)
|
||||||
|
* @param count 簇内点数(主圈中心数字)
|
||||||
|
* @param items 簇内所有点位,用于挑最近资源并生成 title 提示
|
||||||
|
* @param userLocation [lng, lat] 用户位置,用于挑最近资源
|
||||||
|
* @param clusterNumber 聚合点本身的位置编号(按距离用户升序),左上角小角标显示;不传则不显示
|
||||||
|
*/
|
||||||
|
export function buildClusterIcon(
|
||||||
|
count: number,
|
||||||
|
items: any[] = [],
|
||||||
|
userLocation?: [number, number],
|
||||||
|
clusterNumber?: number
|
||||||
|
): string {
|
||||||
|
// 挑选最近的资源(仅用于 title 提示)
|
||||||
|
let nearest: any = items[0] || {};
|
||||||
|
if (userLocation && items.length > 1) {
|
||||||
|
let minDist = Infinity;
|
||||||
|
for (const it of items) {
|
||||||
|
const lng = Number(it.lon ?? it.longitude ?? it.lng);
|
||||||
|
const lat = Number(it.lat ?? it.latitude);
|
||||||
|
if (!lng || !lat) continue;
|
||||||
|
const d = haversine(userLocation[0], userLocation[1], lng, lat);
|
||||||
|
if (d < minDist) {
|
||||||
|
minDist = d;
|
||||||
|
nearest = it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const { bg, border } = getClusterColor(count);
|
||||||
|
const nearestName = nearest.name || typeToLabel(nearest.type);
|
||||||
|
const tip = `附近 ${count} 个资源,最近:${nearestName}`;
|
||||||
|
|
||||||
|
// 左上角位置编号角标(仅当传了 clusterNumber 才显示)
|
||||||
|
const badgeHtml =
|
||||||
|
clusterNumber !== undefined && clusterNumber !== null
|
||||||
|
? `<div style="
|
||||||
|
position: absolute;
|
||||||
|
top: -6px;
|
||||||
|
left: -6px;
|
||||||
|
min-width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
padding: 0 6px;
|
||||||
|
line-height: 22px;
|
||||||
|
border-radius: 11px;
|
||||||
|
background: #E74C3C;
|
||||||
|
border: 2px solid #fff;
|
||||||
|
color: #fff;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 12px;
|
||||||
|
box-shadow: 0 1px 3px rgba(0,0,0,0.4);
|
||||||
|
">${clusterNumber}</div>`
|
||||||
|
: '';
|
||||||
|
|
||||||
|
// 主圈:纯色圆 + 数量 N 在中心
|
||||||
|
return `
|
||||||
|
<div class="cluster-marker" title="${tip}" style="
|
||||||
|
position: relative;
|
||||||
|
width: 44px;
|
||||||
|
height: 44px;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
">
|
||||||
|
<div style="
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 44px;
|
||||||
|
height: 44px;
|
||||||
|
line-height: 44px;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: ${bg};
|
||||||
|
border: 3px solid ${border};
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 18px;
|
||||||
|
box-shadow: 0 2px 6px rgba(0,0,0,0.35);
|
||||||
|
">${count}</div>
|
||||||
|
${badgeHtml}
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc 异步创建带数字角标的图标:图片加载完成后绘制到 canvas,再叠加红色数字角标
|
||||||
|
* @param originalIconUrl 原始图标 url(来自 typeToIcon)
|
||||||
|
* @param text 角标文字(数字 1/2/3/...)
|
||||||
|
* @param type 类型:1 医院 / 3 健康小屋 / 4 AED / 5 救护车,影响 canvas 尺寸
|
||||||
|
*/
|
||||||
|
export function createNumberedMarkerIcon(originalIconUrl: string, text: string | number, type: string | number): Promise<string> {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
// 异常兜底:url 为空直接返回空串,让调用方决定
|
||||||
|
if (!originalIconUrl) return resolve('');
|
||||||
|
// 兜底:不在浏览器环境(SSR / 测试),返回原 url
|
||||||
|
if (typeof window === 'undefined' || typeof document === 'undefined') {
|
||||||
|
return resolve(originalIconUrl);
|
||||||
|
}
|
||||||
|
const width = type == 1 ? 40 : 28;
|
||||||
|
const height = type == 1 ? 69 : 49;
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
canvas.width = width;
|
||||||
|
canvas.height = height + 15;
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
if (!ctx) return resolve(originalIconUrl);
|
||||||
|
const img = new Image();
|
||||||
|
// 不设置 crossOrigin:Vite 的本地资源是 blob URL,无需 CORS;
|
||||||
|
// 设置 crossOrigin=anonymous 在某些环境下反而触发 onerror。
|
||||||
|
img.onload = () => {
|
||||||
|
try {
|
||||||
|
ctx.drawImage(img, 0, 10, width, height);
|
||||||
|
const textX = type == 1 ? 32 : 22;
|
||||||
|
const textY = 11;
|
||||||
|
const textStr = String(text);
|
||||||
|
const textWidth = ctx.measureText(textStr).width;
|
||||||
|
const circleRadius = Math.max(textWidth / 2 + 1, 6);
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(textX, textY, circleRadius, 0, Math.PI * 2);
|
||||||
|
ctx.fillStyle = 'red';
|
||||||
|
ctx.fill();
|
||||||
|
ctx.font = 'bold 10px Arial';
|
||||||
|
ctx.textAlign = 'right';
|
||||||
|
ctx.textBaseline = 'middle';
|
||||||
|
ctx.fillStyle = '#ffffff';
|
||||||
|
ctx.fillText(textStr, type == 1 ? 35 : 24.5, 10.8);
|
||||||
|
resolve(canvas.toDataURL());
|
||||||
|
} catch (e) {
|
||||||
|
// canvas 污染等异常:降级到原 url
|
||||||
|
resolve(originalIconUrl);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
img.onerror = () => resolve(originalIconUrl);
|
||||||
|
img.src = originalIconUrl;
|
||||||
|
// 兜底:5 秒未触发 onload/onerror 也降级
|
||||||
|
setTimeout(() => resolve(originalIconUrl), 5000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc 类型编号 → 中文标签(用于簇列表)
|
||||||
|
* @param type 1 医院 / 3 健康小屋 / 4 AED / 5 救护车
|
||||||
|
*/
|
||||||
|
export function typeToLabel(type: string | number): string {
|
||||||
|
const map: Record<string, string> = {
|
||||||
|
'1': '医院',
|
||||||
|
'3': '健康小屋',
|
||||||
|
'4': 'AED',
|
||||||
|
'5': '救护车',
|
||||||
|
};
|
||||||
|
return map[String(type)] || '资源';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc 生成簇列表 InfoWindow 的 HTML 内容
|
||||||
|
* @param items 簇内点位
|
||||||
|
* @param userLocation [lng, lat] 用户位置,用于计算直线距离并排序
|
||||||
|
*/
|
||||||
|
export function buildClusterInfoContent(items: any[] = [], userLocation?: [number, number]): string {
|
||||||
|
// 计算直线距离并按距离升序
|
||||||
|
const list = items.map((it) => {
|
||||||
|
const lng = Number(it.lon ?? it.longitude ?? it.lng);
|
||||||
|
const lat = Number(it.lat ?? it.latitude);
|
||||||
|
let distance = Number(it.distance);
|
||||||
|
if (!distance && userLocation) {
|
||||||
|
distance = haversine(userLocation[0], userLocation[1], lng, lat);
|
||||||
|
}
|
||||||
|
return { ...it, _distance: distance || 0, _lng: lng, _lat: lat };
|
||||||
|
});
|
||||||
|
list.sort((a, b) => a._distance - b._distance);
|
||||||
|
|
||||||
|
const formatDist = (m: number) => (m >= 1000 ? `${(m / 1000).toFixed(2)} 公里` : `${Math.round(m)} 米`);
|
||||||
|
|
||||||
|
const rows = list
|
||||||
|
.map(
|
||||||
|
(it, idx) => `
|
||||||
|
<div class="cluster-row" data-idx="${idx}" style="
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 8px 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||||
|
transition: background 0.2s;
|
||||||
|
">
|
||||||
|
<div style="display: flex; align-items: center; gap: 8px;">
|
||||||
|
<span style="
|
||||||
|
display: inline-block;
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #3FA9F5;
|
||||||
|
flex-shrink: 0;
|
||||||
|
"></span>
|
||||||
|
<span style="color: #fff; font-size: 14px;">${it.name || typeToLabel(it.type)}</span>
|
||||||
|
<span style="color: rgba(255,255,255,0.6); font-size: 12px;">${typeToLabel(it.type)}</span>
|
||||||
|
</div>
|
||||||
|
<span style="color: rgba(255,255,255,0.85); font-size: 13px;">${formatDist(it._distance)}</span>
|
||||||
|
</div>`
|
||||||
|
)
|
||||||
|
.join('');
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div class="cluster-info-window" style="
|
||||||
|
position: relative;
|
||||||
|
width: 260px;
|
||||||
|
background: rgba(15, 32, 60, 0.95);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 12px 0;
|
||||||
|
color: #fff;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||||
|
box-shadow: 0 4px 16px rgba(0,0,0,0.35);
|
||||||
|
box-sizing: border-box;
|
||||||
|
">
|
||||||
|
<button class="cluster-info-close" type="button" style="
|
||||||
|
position: absolute;
|
||||||
|
top: 6px;
|
||||||
|
right: 6px;
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
line-height: 18px;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: rgba(255,255,255,0.15);
|
||||||
|
border: 0;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0;
|
||||||
|
font-family: inherit;
|
||||||
|
">×</button>
|
||||||
|
<div style="
|
||||||
|
padding: 0 36px 8px 14px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 700;
|
||||||
|
border-bottom: 1px solid rgba(255,255,255,0.15);
|
||||||
|
">附近 ${items.length} 个资源</div>
|
||||||
|
<div class="cluster-rows">${rows}</div>
|
||||||
|
<div style="
|
||||||
|
padding: 6px 14px 0;
|
||||||
|
font-size: 12px;
|
||||||
|
color: rgba(255,255,255,0.5);
|
||||||
|
">点击列表项查看详情并规划路线</div>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,14 +1,28 @@
|
|||||||
import AMapLoader from '@amap/amap-jsapi-loader';
|
import AMapLoader from '@amap/amap-jsapi-loader';
|
||||||
import china from '/@/assets/lngLat/china.ts';
|
import china from '/@/assets/lngLat/china.ts';
|
||||||
import chinaInner from '/@/assets/lngLat/chinaInner.ts';
|
import chinaInner from '/@/assets/lngLat/chinaInner.ts';
|
||||||
import { createCircle, createIcon, getCenter, getLonLat, typeToIcon } from '/@/assets/mapUtils/commonFun.ts';
|
import {
|
||||||
import { aircraftContent, alarmIcon, female, male } from '/@/components/chinaMap/chinaHooks.ts';
|
buildClusterIcon,
|
||||||
|
buildClusterInfoContent,
|
||||||
|
clusterByRadius,
|
||||||
|
createCircle,
|
||||||
|
createIcon,
|
||||||
|
createNumberedMarkerIcon,
|
||||||
|
getCenter,
|
||||||
|
getClusterCenter,
|
||||||
|
getLonLat,
|
||||||
|
haversine,
|
||||||
|
typeToIcon,
|
||||||
|
typeToIcon1,
|
||||||
|
} from '/@/assets/mapUtils/commonFun.ts';
|
||||||
|
import { aircraftContent, alarmIcon, female, male, watchIconR } from '/@/components/chinaMap/chinaHooks.ts';
|
||||||
import { isFunction } from '/@/utils/is.ts';
|
import { isFunction } from '/@/utils/is.ts';
|
||||||
import { Callback, ExtDataType, InitMap, MarkerOption, ResultType } from '/@/assets/mapUtils/mapFunTypes.ts';
|
import { Callback, ExtDataType, InitMap, MarkerOption, ResultType } from '/@/assets/mapUtils/mapFunTypes.ts';
|
||||||
import { defaultOption, getZoom, mapKey, mapPlugin } from '/@/assets/mapUtils/mapConstant.ts';
|
import { defaultOption, getZoom, mapKey, mapPlugin } from '/@/assets/mapUtils/mapConstant.ts';
|
||||||
|
|
||||||
let AMap = null;
|
let AMap = null;
|
||||||
export const Map = {
|
export const Map = {
|
||||||
|
aMap: null,
|
||||||
map: null,
|
map: null,
|
||||||
overlayGroup: null, // 地图中marker
|
overlayGroup: null, // 地图中marker
|
||||||
circleGroup: null, //直升机背景圆
|
circleGroup: null, //直升机背景圆
|
||||||
@@ -19,6 +33,16 @@ export const Map = {
|
|||||||
infoWindow: null, //地图弹窗
|
infoWindow: null, //地图弹窗
|
||||||
driveLine: null, // 导航轨迹线
|
driveLine: null, // 导航轨迹线
|
||||||
emergencyMarker: null, // 应急人员marker
|
emergencyMarker: null, // 应急人员marker
|
||||||
|
startMarker: null,
|
||||||
|
endMarker: null,
|
||||||
|
driving: null,
|
||||||
|
walking: null,
|
||||||
|
mapDriverOrWalkLoading: false,
|
||||||
|
layerGroup: null,
|
||||||
|
layerData: null,
|
||||||
|
clusterInfoWindow: null, // 簇列表弹窗(聚合 marker 点击后展示)
|
||||||
|
clusterRadius: 30, // 聚合半径,单位米
|
||||||
|
clusterUserLocation: null, // 用户位置 [lng, lat],用于列表显示距离
|
||||||
/**
|
/**
|
||||||
* @desc 地图舒适化
|
* @desc 地图舒适化
|
||||||
* @param el dom元素 id选择器
|
* @param el dom元素 id选择器
|
||||||
@@ -34,6 +58,7 @@ export const Map = {
|
|||||||
plugins: mapPlugin, // 需要使用的的插件列表,如比例尺'AMap.Scale'等
|
plugins: mapPlugin, // 需要使用的的插件列表,如比例尺'AMap.Scale'等
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
AMap = res;
|
AMap = res;
|
||||||
|
this.aMap = res;
|
||||||
const { zoom, center, mapStyle } = { ...defaultOption, ...option };
|
const { zoom, center, mapStyle } = { ...defaultOption, ...option };
|
||||||
//基本地图加载
|
//基本地图加载
|
||||||
this.map = new AMap.Map(el, {
|
this.map = new AMap.Map(el, {
|
||||||
@@ -53,6 +78,18 @@ export const Map = {
|
|||||||
this.drawOutLine();
|
this.drawOutLine();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
const satellite = new AMap.TileLayer.Satellite({
|
||||||
|
zIndex: 11,
|
||||||
|
});
|
||||||
|
|
||||||
|
const roadNet = new AMap.TileLayer.RoadNet({
|
||||||
|
zIndex: 12,
|
||||||
|
});
|
||||||
|
this.layerData = { satellite, roadNet };
|
||||||
|
this.layerGroup = new AMap.LayerGroup([satellite, roadNet]);
|
||||||
|
this.layerGroup.setMap(this.map);
|
||||||
|
satellite.hide();
|
||||||
|
roadNet.hide();
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -125,6 +162,11 @@ export const Map = {
|
|||||||
}
|
}
|
||||||
this.clearActiveHospital();
|
this.clearActiveHospital();
|
||||||
this.clearInfoModal();
|
this.clearInfoModal();
|
||||||
|
// 关闭簇列表弹窗(聚合 marker 列表弹窗)
|
||||||
|
if (this.clusterInfoWindow) {
|
||||||
|
this.clusterInfoWindow.close();
|
||||||
|
this.clusterInfoWindow = null;
|
||||||
|
}
|
||||||
// this.map.remove(this.airMarkerList);
|
// this.map.remove(this.airMarkerList);
|
||||||
}, //
|
}, //
|
||||||
/**
|
/**
|
||||||
@@ -134,17 +176,22 @@ export const Map = {
|
|||||||
* @param callback
|
* @param callback
|
||||||
* @param computedCenter 是否计算中心点
|
* @param computedCenter 是否计算中心点
|
||||||
*/
|
*/
|
||||||
createOverlay(result: ResultType[], extData?: ExtDataType, callback?: Callback, computedCenter?: boolean) {
|
createOverlay(result: ResultType[], extData?: ExtDataType, callback?: Callback, computedCenter?: boolean, iconType?: string) {
|
||||||
this.removeOverlayGroup();
|
this.removeOverlayGroup();
|
||||||
this.overlayGroup = new AMap.OverlayGroup();
|
this.overlayGroup = new AMap.OverlayGroup();
|
||||||
let list = getLonLat(result);
|
// const list: any[] = getLonLat(result);
|
||||||
let center = getCenter(list);
|
// const center = getCenter(list);
|
||||||
let zoom = getZoom(list);
|
// const zoom = getZoom(list);
|
||||||
if (computedCenter) {
|
let arr: any[] = [];
|
||||||
this.setZoomCenter(center, zoom);
|
|
||||||
}
|
|
||||||
for (let i = 0; i < result.length; i++) {
|
for (let i = 0; i < result.length; i++) {
|
||||||
let { type, lon, lat, lng, centerResource } = result[i];
|
let { type, lon, lat, longitude, latitude, lng, centerResource } = result[i];
|
||||||
|
|
||||||
|
if (longitude) {
|
||||||
|
lon = longitude;
|
||||||
|
}
|
||||||
|
if (latitude) {
|
||||||
|
lat = latitude;
|
||||||
|
}
|
||||||
if (lng) {
|
if (lng) {
|
||||||
lon = lng;
|
lon = lng;
|
||||||
}
|
}
|
||||||
@@ -152,7 +199,7 @@ export const Map = {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// 创建一个 Icon
|
// 创建一个 Icon
|
||||||
let startIcon = typeToIcon(type, centerResource);
|
let startIcon = typeToIcon1(type);
|
||||||
// 将 icon 传入 marker
|
// 将 icon 传入 marker
|
||||||
let startMarker = new AMap.Marker({
|
let startMarker = new AMap.Marker({
|
||||||
// 点的坐标
|
// 点的坐标
|
||||||
@@ -167,6 +214,121 @@ export const Map = {
|
|||||||
},
|
},
|
||||||
...extData?.extOption,
|
...extData?.extOption,
|
||||||
});
|
});
|
||||||
|
arr.push(startMarker);
|
||||||
|
// 可以调用setExtData()设置自定义属性
|
||||||
|
//将组 挂载到地图上
|
||||||
|
this.overlayGroup && this.overlayGroup.setMap(this.map);
|
||||||
|
//将需要批量控制的点放到一个组中,通过控制组,就可以操作组内所有所有覆盖物
|
||||||
|
this.overlayGroup && this.overlayGroup.addOverlay(startMarker);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.overlayGroup &&
|
||||||
|
this.overlayGroup.on('click', function (e: any) {
|
||||||
|
//拿到点中携带的数据
|
||||||
|
if (!isFunction(callback)) return;
|
||||||
|
callback(e.target.getExtData());
|
||||||
|
});
|
||||||
|
if (computedCenter) {
|
||||||
|
this.map && this.map.setFitView(arr, false, [50, 50, 0, 50]);
|
||||||
|
}
|
||||||
|
return arr;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @desc 创建覆盖物组,用于批量处理点 (控制显隐,点击事件等)
|
||||||
|
* @param result
|
||||||
|
* @param extData
|
||||||
|
* @param callback
|
||||||
|
* @param computedCenter 是否计算中心点
|
||||||
|
*/
|
||||||
|
async createOverlay1(result: ResultType[], extData?: ExtDataType, callback?: Callback, computedCenter?: boolean, iconType?: string) {
|
||||||
|
this.removeOverlayGroup();
|
||||||
|
this.overlayGroup = new AMap.OverlayGroup();
|
||||||
|
const list: any[] = getLonLat(result);
|
||||||
|
const center = getCenter(list);
|
||||||
|
const zoom = getZoom(list);
|
||||||
|
if (computedCenter) {
|
||||||
|
this.setZoomCenter(center, zoom);
|
||||||
|
}
|
||||||
|
function createTextOverlayIcon(originalIconUrl, text, type) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
const img = new Image();
|
||||||
|
|
||||||
|
img.crossOrigin = 'anonymous';
|
||||||
|
img.onload = function () {
|
||||||
|
const width = type == 1 ? 40 : 28;
|
||||||
|
const height = type == 1 ? 69 : 49;
|
||||||
|
// 设置画布大小(比原图稍大以容纳文字)
|
||||||
|
canvas.width = width;
|
||||||
|
canvas.height = height + 15; // 为文字留出空间
|
||||||
|
|
||||||
|
// 绘制原始图标
|
||||||
|
ctx.drawImage(img, 0, 10, width, height);
|
||||||
|
// 绘制圆形文字背景
|
||||||
|
const textX = type == 1 ? 32 : 22; // 文字X坐标(居右对齐)
|
||||||
|
|
||||||
|
const textY = 11; // 文字Y坐标
|
||||||
|
const textWidth = ctx.measureText(text).width;
|
||||||
|
const circleRadius = Math.max(textWidth / 2 + 1, 6); // 根据文字宽度计算半径
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(textX, textY, circleRadius, 0, Math.PI * 2);
|
||||||
|
ctx.fillStyle = 'red';
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
// 绘制文字背景(可选)
|
||||||
|
// ctx.fillStyle = 'red';
|
||||||
|
// ctx.fillRect(0, 0, 28, 15);
|
||||||
|
|
||||||
|
// 绘制文字
|
||||||
|
ctx.font = `bold 10px Arial`;
|
||||||
|
ctx.textAlign = 'right';
|
||||||
|
ctx.textBaseline = 'middle';
|
||||||
|
ctx.fillStyle = '#ffffff';
|
||||||
|
ctx.fillText(text, type == 1 ? 35 : 24.5, 10.8);
|
||||||
|
|
||||||
|
resolve(canvas.toDataURL());
|
||||||
|
};
|
||||||
|
|
||||||
|
img.src = originalIconUrl;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let arr = [];
|
||||||
|
for (let i = 0; i < result.length; i++) {
|
||||||
|
let { type, lon, lat, longitude, latitude, lng, centerResource } = result[i];
|
||||||
|
|
||||||
|
if (longitude) {
|
||||||
|
lon = longitude;
|
||||||
|
}
|
||||||
|
if (latitude) {
|
||||||
|
lat = latitude;
|
||||||
|
}
|
||||||
|
if (lng) {
|
||||||
|
lon = lng;
|
||||||
|
}
|
||||||
|
if (!lon || !lat || !type) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 创建一个 Icon
|
||||||
|
const startIcon: any = typeToIcon(type);
|
||||||
|
const newIconUrl = await createTextOverlayIcon(startIcon, i + 1 + '', result[i]?.type);
|
||||||
|
// 将 icon 传入 marker
|
||||||
|
const startMarker = new AMap.Marker({
|
||||||
|
// 点的坐标
|
||||||
|
position: [lon, lat], // 指定点的icon,也可以是个url ,但这样就不能对图片进行设置了
|
||||||
|
icon: newIconUrl, //启用点击事件 如果需要点击事件,必须开启
|
||||||
|
clickable: true, //点额外携带的数据,用户自定义属性,支持JavaScript API任意数据类型
|
||||||
|
offset: [-18, -32],
|
||||||
|
maxZoom: 14,
|
||||||
|
extData: result[i],
|
||||||
|
label: {
|
||||||
|
content: isFunction(extData?.content) && extData?.content(result[i]),
|
||||||
|
},
|
||||||
|
zIndex: 99,
|
||||||
|
...extData?.extOption,
|
||||||
|
});
|
||||||
|
arr.push(startMarker);
|
||||||
// 可以调用setExtData()设置自定义属性
|
// 可以调用setExtData()设置自定义属性
|
||||||
//将组 挂载到地图上
|
//将组 挂载到地图上
|
||||||
this.overlayGroup.setMap(this.map);
|
this.overlayGroup.setMap(this.map);
|
||||||
@@ -179,6 +341,228 @@ export const Map = {
|
|||||||
if (!isFunction(callback)) return;
|
if (!isFunction(callback)) return;
|
||||||
callback(e.target.getExtData());
|
callback(e.target.getExtData());
|
||||||
});
|
});
|
||||||
|
return arr;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @desc 创建带聚合的覆盖物组:距离阈值内的点位合并为聚合 marker(最近资源图标+数字角标),
|
||||||
|
* 点击聚合 marker 弹出 InfoWindow 展示簇内点位列表;簇外点位独立渲染。
|
||||||
|
* @param result 待渲染的点位(医院/医疗点/AED 等)
|
||||||
|
* @param extData 与 createOverlay1 一致,content/extOption 用于 label 等
|
||||||
|
* @param callback 单个点位被选中时的回调(被聚合列表选中也会调用,签名相同)
|
||||||
|
* @param computedCenter 是否计算视野中心
|
||||||
|
* @param iconType 同 createOverlay1(保留参数占位)
|
||||||
|
* @param radius 聚合半径,单位米,默认 30
|
||||||
|
* @param userLocation 用户位置 [lng, lat],用于挑选最近资源 + 簇列表显示距离
|
||||||
|
*/
|
||||||
|
async createClusterOverlay(
|
||||||
|
result: ResultType[],
|
||||||
|
extData?: ExtDataType,
|
||||||
|
callback?: Callback,
|
||||||
|
computedCenter?: boolean,
|
||||||
|
iconType?: string,
|
||||||
|
radius?: number,
|
||||||
|
userLocation?: [number, number]
|
||||||
|
) {
|
||||||
|
this.removeOverlayGroup();
|
||||||
|
this.overlayGroup = new AMap.OverlayGroup();
|
||||||
|
// 关闭上一次的簇列表弹窗
|
||||||
|
if (this.clusterInfoWindow) {
|
||||||
|
this.clusterInfoWindow.close();
|
||||||
|
this.clusterInfoWindow = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const r = radius || this.clusterRadius || 30;
|
||||||
|
if (userLocation) {
|
||||||
|
this.clusterUserLocation = userLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 过滤掉缺少经纬度或类型的点
|
||||||
|
const validItems: any[] = [];
|
||||||
|
for (const item of result || []) {
|
||||||
|
const lon = Number(item.lon ?? item.longitude ?? item.lng);
|
||||||
|
const lat = Number(item.lat ?? item.latitude);
|
||||||
|
const type = item.type;
|
||||||
|
if (!lon || !lat || !type) continue;
|
||||||
|
validItems.push({ ...item, lon, lat });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 单链聚类(30m 内合并)
|
||||||
|
const clusters = clusterByRadius(validItems, r);
|
||||||
|
const clusteredSet = new Set();
|
||||||
|
for (const c of clusters) {
|
||||||
|
for (const item of c) clusteredSet.add(item);
|
||||||
|
}
|
||||||
|
const singles = validItems.filter((it) => !clusteredSet.has(it));
|
||||||
|
|
||||||
|
const allMarkers: any[] = [];
|
||||||
|
|
||||||
|
// 收集所有"位置点":每个聚合点(按 cluster center)和每个独立点都是一个位置点
|
||||||
|
// 统一按距离用户位置升序排序后连续编号 1, 2, 3, ...
|
||||||
|
// 这样聚合点和独立 marker 不会各自从 #1 开始,地图上编号全局唯一
|
||||||
|
const positionPoints: any[] = [];
|
||||||
|
for (const cluster of clusters) {
|
||||||
|
const [cLng, cLat] = getClusterCenter(cluster);
|
||||||
|
positionPoints.push({ kind: 'cluster', cluster, cLng, cLat });
|
||||||
|
}
|
||||||
|
for (const item of singles) {
|
||||||
|
positionPoints.push({ kind: 'single', item });
|
||||||
|
}
|
||||||
|
if (userLocation) {
|
||||||
|
positionPoints.sort((a, b) => {
|
||||||
|
const da =
|
||||||
|
a.kind === 'cluster'
|
||||||
|
? haversine(userLocation[0], userLocation[1], a.cLng, a.cLat)
|
||||||
|
: haversine(userLocation[0], userLocation[1], a.item.lon, a.item.lat);
|
||||||
|
const db =
|
||||||
|
b.kind === 'cluster'
|
||||||
|
? haversine(userLocation[0], userLocation[1], b.cLng, b.cLat)
|
||||||
|
: haversine(userLocation[0], userLocation[1], b.item.lon, b.item.lat);
|
||||||
|
return da - db;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 同步渲染聚合 marker(HTML,无异步依赖)
|
||||||
|
const singleMarkerRefs: Array<{ marker: any; item: any; number: number }> = [];
|
||||||
|
positionPoints.forEach((p, idx) => {
|
||||||
|
const number = idx + 1;
|
||||||
|
if (p.kind === 'cluster') {
|
||||||
|
// 聚合 marker:纯色圆 + 数量 N + 左上角位置编号角标
|
||||||
|
const clusterMarker: any = new AMap.Marker({
|
||||||
|
position: [p.cLng, p.cLat],
|
||||||
|
content: buildClusterIcon(p.cluster.length, p.cluster, userLocation, number),
|
||||||
|
clickable: true,
|
||||||
|
offset: new AMap.Pixel(-22, -22), // 主圈 44x44,中心对准坐标
|
||||||
|
extData: { __isCluster: true, items: p.cluster, clusterNumber: number },
|
||||||
|
zIndex: 110,
|
||||||
|
});
|
||||||
|
// 点击聚合 → 打开 InfoWindow
|
||||||
|
clusterMarker.on('click', () => {
|
||||||
|
this.openClusterInfoWindow(p.cluster, [p.cLng, p.cLat], callback);
|
||||||
|
});
|
||||||
|
this.overlayGroup.setMap(this.map);
|
||||||
|
this.overlayGroup.addOverlay(clusterMarker);
|
||||||
|
allMarkers.push(clusterMarker);
|
||||||
|
} else {
|
||||||
|
// 同步渲染独立 marker:先用 typeToIcon 的纯 url(保证渲染成功,无角标也行)
|
||||||
|
// 后续异步优化:用 canvas 画带角标的图标,setIcon 替换
|
||||||
|
const iconUrl: string = typeToIcon(p.item.type);
|
||||||
|
const startMarker: any = new AMap.Marker({
|
||||||
|
position: [p.item.lon, p.item.lat],
|
||||||
|
icon: iconUrl,
|
||||||
|
clickable: true,
|
||||||
|
offset: [-18, -32],
|
||||||
|
maxZoom: 14,
|
||||||
|
extData: p.item,
|
||||||
|
label: {
|
||||||
|
content: isFunction(extData?.content) && extData?.content(p.item),
|
||||||
|
},
|
||||||
|
zIndex: 99,
|
||||||
|
...extData?.extOption,
|
||||||
|
});
|
||||||
|
this.overlayGroup.setMap(this.map);
|
||||||
|
this.overlayGroup.addOverlay(startMarker);
|
||||||
|
allMarkers.push(startMarker);
|
||||||
|
// 记录独立 marker 引用,后续异步替换为带角标图标
|
||||||
|
singleMarkerRefs.push({ marker: startMarker, item: p.item, number });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 异步优化:为独立 marker 叠加数字角标(canvas 失败也不影响主渲染流程)
|
||||||
|
// 主渲染已经完成(marker 已上地图),这里只是 setIcon 替换图标
|
||||||
|
singleMarkerRefs.forEach(({ marker, item, number }) => {
|
||||||
|
createNumberedMarkerIcon(typeToIcon(item.type), number, item.type)
|
||||||
|
.then((numberedUrl) => {
|
||||||
|
if (numberedUrl && marker && typeof marker.setIcon === 'function') {
|
||||||
|
try {
|
||||||
|
marker.setIcon(numberedUrl);
|
||||||
|
} catch (e) {
|
||||||
|
// 静默:setIcon 失败不影响
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
// 静默:canvas 失败保留原 url 图标
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 独立 marker 点击 → 直接走原回调
|
||||||
|
this.overlayGroup.on('click', (e: any) => {
|
||||||
|
const ext = e?.target?.getExtData?.();
|
||||||
|
if (!ext || ext.__isCluster) return; // 聚合已自行处理
|
||||||
|
if (!isFunction(callback)) return;
|
||||||
|
callback(ext);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (computedCenter && allMarkers.length) {
|
||||||
|
try {
|
||||||
|
this.map && this.map.setFitView(allMarkers, false, [50, 50, 0, 50]);
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('[createClusterOverlay] setFitView failed:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return allMarkers;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @desc 打开簇列表 InfoWindow,点击列表项触发与单点点击相同的回调
|
||||||
|
*/
|
||||||
|
openClusterInfoWindow(cluster: any[], position: [number, number], callback?: Callback) {
|
||||||
|
if (!this.map || !AMap) return;
|
||||||
|
// 关闭上一次的
|
||||||
|
if (this.clusterInfoWindow) {
|
||||||
|
this.clusterInfoWindow.close();
|
||||||
|
this.clusterInfoWindow = null;
|
||||||
|
}
|
||||||
|
// 把 HTML 转成 DOM 元素直接传给 InfoWindow,避免 setTimeout + querySelector 的时序问题
|
||||||
|
// 事件直接绑在元素上,AMap 插入到地图 DOM 后 listener 依然生效(事件 listener 跟元素绑定,不跟 DOM 位置绑定)
|
||||||
|
const wrapper = document.createElement('div');
|
||||||
|
wrapper.innerHTML = buildClusterInfoContent(cluster, this.clusterUserLocation);
|
||||||
|
const root = wrapper.querySelector('.cluster-info-window') as HTMLElement | null;
|
||||||
|
|
||||||
|
const closeWindow = () => {
|
||||||
|
if (this.clusterInfoWindow) {
|
||||||
|
this.clusterInfoWindow.close();
|
||||||
|
this.clusterInfoWindow = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (root) {
|
||||||
|
// 列表行点击
|
||||||
|
const rows = root.querySelectorAll('.cluster-row');
|
||||||
|
rows.forEach((row) => {
|
||||||
|
row.addEventListener('click', (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
const idx = Number((row as HTMLElement).getAttribute('data-idx') || 0);
|
||||||
|
const target = cluster[idx];
|
||||||
|
if (!target) return;
|
||||||
|
closeWindow();
|
||||||
|
if (isFunction(callback)) callback(target);
|
||||||
|
});
|
||||||
|
row.addEventListener('mouseenter', () => {
|
||||||
|
(row as HTMLElement).style.background = 'rgba(255,255,255,0.08)';
|
||||||
|
});
|
||||||
|
row.addEventListener('mouseleave', () => {
|
||||||
|
(row as HTMLElement).style.background = 'transparent';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// 关闭按钮
|
||||||
|
const closeBtn = root.querySelector('.cluster-info-close');
|
||||||
|
if (closeBtn) {
|
||||||
|
closeBtn.addEventListener('click', (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
closeWindow();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.clusterInfoWindow = new AMap.InfoWindow({
|
||||||
|
position,
|
||||||
|
offset: new AMap.Pixel(0, -10),
|
||||||
|
content: wrapper,
|
||||||
|
isCustom: true,
|
||||||
|
autoMove: true,
|
||||||
|
closeWhenClickMap: true,
|
||||||
|
});
|
||||||
|
this.clusterInfoWindow.open(this.map, position);
|
||||||
},
|
},
|
||||||
clearOverlay() {
|
clearOverlay() {
|
||||||
if (this.overlayGroup) {
|
if (this.overlayGroup) {
|
||||||
@@ -309,13 +693,23 @@ export const Map = {
|
|||||||
* @desc 创建地图详情弹窗
|
* @desc 创建地图详情弹窗
|
||||||
*
|
*
|
||||||
* */
|
* */
|
||||||
createInfoModal({ option, content }) {
|
createInfoModal({ option, content }, flag = true) {
|
||||||
let { lat, lng } = option;
|
let { latitude, longitude } = option;
|
||||||
this.infoWindow = new AMap.InfoWindow({
|
this.infoWindow = new AMap.InfoWindow({
|
||||||
position: [lng, lat],
|
position: [longitude, latitude],
|
||||||
offset: new AMap.Pixel(-5, -25),
|
offset: new AMap.Pixel(-8, -25),
|
||||||
content: content,
|
content: content,
|
||||||
closeWhenClickMap: true,
|
closeWhenClickMap: flag,
|
||||||
|
});
|
||||||
|
this.infoWindow.open(this.map);
|
||||||
|
},
|
||||||
|
createEmerInfoModal({ option, content }, flag = true) {
|
||||||
|
let { latitude, longitude } = option;
|
||||||
|
this.infoWindow = new AMap.InfoWindow({
|
||||||
|
position: [longitude, latitude],
|
||||||
|
offset: new AMap.Pixel(0, -8),
|
||||||
|
content: content,
|
||||||
|
closeWhenClickMap: flag,
|
||||||
});
|
});
|
||||||
this.infoWindow.open(this.map);
|
this.infoWindow.open(this.map);
|
||||||
},
|
},
|
||||||
@@ -362,6 +756,32 @@ export const Map = {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* @desc 计算两个坐标点的步行时间
|
||||||
|
* @param {Array} listA 坐标点数组
|
||||||
|
* @param {Array} listB 坐标点数组
|
||||||
|
* @param {Function} callback 回调
|
||||||
|
* @return {Number} 时间
|
||||||
|
*/
|
||||||
|
getWalkingTime(listA: number[], listB: number[], callback?: Callback) {
|
||||||
|
let lnglatA = new AMap.LngLat(listA[0], listA[1]);
|
||||||
|
let lnglatB = new AMap.LngLat(listB[0], listB[1]);
|
||||||
|
const drivingOptions = {
|
||||||
|
policy: AMap.DrivingPolicy.LEAST_TIME,
|
||||||
|
};
|
||||||
|
let walking = new AMap.Walking(drivingOptions);
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
walking.search(lnglatA, lnglatB, (status, result) => {
|
||||||
|
if (status === 'complete') {
|
||||||
|
// console.log('result', result);
|
||||||
|
if (callback && isFunction(callback)) {
|
||||||
|
callback(result);
|
||||||
|
}
|
||||||
|
return resolve(result.routes[0]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc 创建导航轨迹线路
|
* @desc 创建导航轨迹线路
|
||||||
@@ -387,7 +807,6 @@ export const Map = {
|
|||||||
});
|
});
|
||||||
this.driveLine.on('error', function (result) {
|
this.driveLine.on('error', function (result) {
|
||||||
let str = '定位失败:';
|
let str = '定位失败:';
|
||||||
console.log('error', result);
|
|
||||||
switch (result.info) {
|
switch (result.info) {
|
||||||
case 'PERMISSION_DENIED':
|
case 'PERMISSION_DENIED':
|
||||||
str += '浏览器阻止了定位操作';
|
str += '浏览器阻止了定位操作';
|
||||||
@@ -402,7 +821,6 @@ export const Map = {
|
|||||||
str += '未知错误';
|
str += '未知错误';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
console.log(str);
|
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -417,27 +835,37 @@ export const Map = {
|
|||||||
/**
|
/**
|
||||||
* @desc 创建应急人员Marker
|
* @desc 创建应急人员Marker
|
||||||
*/
|
*/
|
||||||
createEmergencyMarker(option) {
|
createEmergencyMarker(option: any, isLabel = true, callback?: Callback) {
|
||||||
this.clearEmergencyMarker();
|
this.clearEmergencyMarker();
|
||||||
const { sex, address, name, position } = option;
|
const { sex, address, name, position } = option;
|
||||||
const icon = sex == '1' ? female : male;
|
const icon = sex == '1' ? female : male;
|
||||||
this.emergencyMarker = new AMap.Marker({
|
this.emergencyMarker = new AMap.Marker({
|
||||||
position: position,
|
position: position,
|
||||||
icon: createIcon({ icon: icon, iconSize: [36, 36], imageSize: [36, 36] }),
|
icon: createIcon({ icon: watchIconR, iconSize: [80, 80], imageSize: [80, 80] }),
|
||||||
offset: new AMap.Pixel(0, 0), //设置偏移量
|
offset: new AMap.Pixel(-40, -40), //设置偏移量
|
||||||
label: {
|
...(isLabel
|
||||||
content: `<div class="alarm-content" style="width: 180px" >
|
? {
|
||||||
|
label: {
|
||||||
|
content: `<div class="alarm-content" style="width: 180px;z-index: 99" >
|
||||||
<div style="text-align: center;padding-bottom: 6px">${name}</div>
|
<div style="text-align: center;padding-bottom: 6px">${name}</div>
|
||||||
<div style="text-wrap: wrap">${address}</div>
|
<div style="text-wrap: wrap">${address}</div>
|
||||||
</div>`,
|
</div>`,
|
||||||
offset: new AMap.Pixel(-100, 55),
|
offset: new AMap.Pixel(-120, -70),
|
||||||
},
|
},
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
});
|
});
|
||||||
this.map.add(this.emergencyMarker);
|
this.map.add(this.emergencyMarker);
|
||||||
this.setZoomCenter(position);
|
this.emergencyMarker.on('click', () => {
|
||||||
|
if (callback && isFunction(callback)) {
|
||||||
|
callback(option);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// this.setZoomCenter(position);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @desc 清除应急人员Marker
|
* @desc 清除应急人员Marker..
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
clearEmergencyMarker() {
|
clearEmergencyMarker() {
|
||||||
if (this.emergencyMarker) {
|
if (this.emergencyMarker) {
|
||||||
@@ -464,6 +892,30 @@ export const Map = {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* @desc 根据坐标获取地址信息
|
||||||
|
* @param {Array} list 坐标点数组
|
||||||
|
*/
|
||||||
|
getInfoByPoint(list: number[]) {
|
||||||
|
const geocoder = new AMap.Geocoder({
|
||||||
|
city: '全国',
|
||||||
|
radius: 1000,
|
||||||
|
});
|
||||||
|
let lnglat = new AMap.LngLat(list[0], list[1]);
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
geocoder.getAddress(lnglat, (status, result) => {
|
||||||
|
if (status === 'complete' && result.regeocode) {
|
||||||
|
let code = result?.regeocode?.addressComponent?.adcode;
|
||||||
|
code = code.substring(0, 4) + '00';
|
||||||
|
if (code.substring(0, 3) !== '650') {
|
||||||
|
code = '650100';
|
||||||
|
}
|
||||||
|
return resolve(code);
|
||||||
|
}
|
||||||
|
return reject(result);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
* @desc 清除导航元素
|
* @desc 清除导航元素
|
||||||
*/
|
*/
|
||||||
@@ -471,4 +923,40 @@ export const Map = {
|
|||||||
this.clearNaviLine();
|
this.clearNaviLine();
|
||||||
this.clearEmergencyMarker();
|
this.clearEmergencyMarker();
|
||||||
},
|
},
|
||||||
|
createSMarker(position: any) {
|
||||||
|
if (this.startMarker) {
|
||||||
|
this.startMarker?.setMap(null);
|
||||||
|
this.startMarker = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.startMarker = new AMap.Marker({
|
||||||
|
position: position,
|
||||||
|
offset: new AMap.Pixel(0, 0),
|
||||||
|
});
|
||||||
|
this.map.add(this.startMarker);
|
||||||
|
this.setZoomCenter(position);
|
||||||
|
},
|
||||||
|
createEMarker(position: any) {
|
||||||
|
if (this.endMarker) {
|
||||||
|
this.endMarker?.setMap(null);
|
||||||
|
this.endMarker = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.endMarker = new AMap.Marker({
|
||||||
|
position: position,
|
||||||
|
offset: new AMap.Pixel(0, 0),
|
||||||
|
});
|
||||||
|
this.map.add(this.endMarker);
|
||||||
|
this.setZoomCenter(position);
|
||||||
|
},
|
||||||
|
clearSOrE() {
|
||||||
|
if (this.startMarker) {
|
||||||
|
this.startMarker?.setMap(null);
|
||||||
|
this.startMarker = null;
|
||||||
|
}
|
||||||
|
if (this.endMarker) {
|
||||||
|
this.endMarker?.setMap(null);
|
||||||
|
this.endMarker = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// 地图默认配置
|
// 地图默认配置
|
||||||
export const defaultOption = {
|
export const defaultOption = {
|
||||||
center: [108.660704, 36.386058], //地图中心点
|
center: [87.627313, 43.826809], //地图中心点
|
||||||
zoom: 6.5, //地图显示的缩放级别
|
zoom: 11, //地图显示的缩放级别
|
||||||
mapStyle: 'darkblue', //地图样式
|
mapStyle: 'darkblue', //地图样式
|
||||||
};
|
};
|
||||||
export const YinChuanOption = {
|
export const YinChuanOption = {
|
||||||
@@ -21,9 +21,21 @@ export const innerLineLight = {
|
|||||||
strokeColor: '#93aaf8',
|
strokeColor: '#93aaf8',
|
||||||
};
|
};
|
||||||
// 地图key
|
// 地图key
|
||||||
export const mapKey = '4bbcb216b889f2d612cbed05ae6979c8';
|
// export const mapKey = '4bbcb216b889f2d612cbed05ae6979c8';
|
||||||
|
// 2025年11月12日11:25:52 更新新的key
|
||||||
|
export const mapKey = '0075068dd9782107fc0e6a7209a6b494';
|
||||||
// 地图插件
|
// 地图插件
|
||||||
export const mapPlugin = ['AMap.ToolBar', 'AMap.Driving', 'AMap.GeoJSON', 'AMap.MarkerCluster', 'AMap.PlaceSearch', 'AMap.Driving', 'AMap.Geocoder'];
|
export const mapPlugin = [
|
||||||
|
'AMap.ToolBar',
|
||||||
|
'AMap.Driving',
|
||||||
|
'AMap.GeoJSON',
|
||||||
|
'AMap.MarkerCluster',
|
||||||
|
'AMap.PlaceSearch',
|
||||||
|
'AMap.Driving',
|
||||||
|
'AMap.Walking',
|
||||||
|
'AMap.Geocoder',
|
||||||
|
'AMap.AutoComplete',
|
||||||
|
];
|
||||||
|
|
||||||
//根据数据量设置地图层级
|
//根据数据量设置地图层级
|
||||||
export function getZoom(list = []) {
|
export function getZoom(list = []) {
|
||||||
|
|||||||
@@ -4,12 +4,12 @@
|
|||||||
--antSelectDropdownBorderColor: #1b7ef2;
|
--antSelectDropdownBorderColor: #1b7ef2;
|
||||||
--antSelectItemActive: #002e64;
|
--antSelectItemActive: #002e64;
|
||||||
--antSelectFontColor: #fff;
|
--antSelectFontColor: #fff;
|
||||||
--antSelectBg: #000;
|
--antSelectBg: #082147;
|
||||||
/*服务详情数据*/
|
/*服务详情数据*/
|
||||||
--typeTimeSelectColor: #45a2ff;
|
--typeTimeSelectColor: #45a2ff;
|
||||||
--typeTimeUnSelectColor: #a3a4a4;
|
--typeTimeUnSelectColor: #a3a4a4;
|
||||||
/*表格 */
|
/*表格 */
|
||||||
--tableHeadBg: rgba(35, 132, 221, 0.5);
|
--tableHeadBg: #073063;
|
||||||
--tableFontColor: #fff;
|
--tableFontColor: #fff;
|
||||||
--tableRowBg: #00152b;
|
--tableRowBg: #00152b;
|
||||||
--tableRowBorder: #2384dd;
|
--tableRowBorder: #2384dd;
|
||||||
|
|||||||
@@ -13,14 +13,16 @@ export enum ServerDetailType {
|
|||||||
export function useStatistics() {
|
export function useStatistics() {
|
||||||
const statistics = ref([
|
const statistics = ref([
|
||||||
{
|
{
|
||||||
name: '最新呼入电话',
|
name: '本周呼入电话',
|
||||||
value: '',
|
value: '',
|
||||||
key: 'gross',
|
key: 'gross',
|
||||||
|
dateType: '0',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '最新受理电话',
|
name: '本周受理电话',
|
||||||
value: '',
|
value: '',
|
||||||
key: 'alerdyreceive',
|
key: 'gross', // 20251213临时处理方案
|
||||||
|
dateType: '0',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '突发重大伤病应急',
|
name: '突发重大伤病应急',
|
||||||
@@ -85,6 +87,21 @@ export const sexType = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const timeTabNew = [
|
||||||
|
{
|
||||||
|
name: '本周',
|
||||||
|
value: '2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '本月',
|
||||||
|
value: '3',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '本年',
|
||||||
|
value: '4',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
export function useSpin() {
|
export function useSpin() {
|
||||||
const spinning = ref(true);
|
const spinning = ref(true);
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ export enum Api {
|
|||||||
complaint = '/health-emergency/emergency/tblHelpCenter/sicknessTypeStatistic',
|
complaint = '/health-emergency/emergency/tblHelpCenter/sicknessTypeStatistic',
|
||||||
orderDetail = '/health-emergency/emergency/order/list',
|
orderDetail = '/health-emergency/emergency/order/list',
|
||||||
listCustom = '/health-watch/watch/watchMonitorData/listCustom',
|
listCustom = '/health-watch/watch/watchMonitorData/listCustom',
|
||||||
|
|
||||||
|
listCustomNew = 'health-watch/watch/watchMonitorData/largeScreen/listCustom',
|
||||||
}
|
}
|
||||||
|
|
||||||
//获取全部部门
|
//获取全部部门
|
||||||
@@ -21,3 +23,4 @@ export const getDetail = (params: any) => get(Api.getDetail, params);
|
|||||||
export const getOrderDetail = (params: any) => get(Api.orderDetail, params);
|
export const getOrderDetail = (params: any) => get(Api.orderDetail, params);
|
||||||
//副屏预警查看详情
|
//副屏预警查看详情
|
||||||
export const getEarlyWarningDetail = (params: any) => get(Api.listCustom, params);
|
export const getEarlyWarningDetail = (params: any) => get(Api.listCustom, params);
|
||||||
|
export const getEarlyWarningDetailNew = (params: any) => get(Api.listCustomNew, params);
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="common-card" :class="[themeType]">
|
<div class="common-card" :class="[themeType]">
|
||||||
<public-title title="服务详情数据" :themeType="themeType" />
|
<public-title title="服务详情数据" themeType="dark" />
|
||||||
<div class="inner">
|
<div class="inner">
|
||||||
<div class="type-time">
|
<div class="type-time">
|
||||||
<span
|
<span
|
||||||
v-for="(item, index) in typeTime"
|
v-for="(item, index) in typeTime"
|
||||||
:key="index"
|
:key="index"
|
||||||
:class="[selectIndex === index ? 'select' : 'unSelect', themeType]"
|
:class="[selectIndex === index ? 'select' : 'unSelect', 'dark']"
|
||||||
@click="handleSelect(index, item.value)"
|
@click="handleSelect(index, item.value)"
|
||||||
>{{ item.name }}</span
|
>{{ item.name }}</span
|
||||||
>
|
>
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
v-for="(item, index) in statistics"
|
v-for="(item, index) in statistics"
|
||||||
:key="index"
|
:key="index"
|
||||||
class="server-item"
|
class="server-item"
|
||||||
:class="[['gross', 'alerdyreceive'].includes(item.key) ? 'hasCursor' : '', themeType]"
|
:class="[['gross', 'alerdyreceive'].includes(item.key) ? 'hasCursor' : '', 'dark']"
|
||||||
@click="handleClick(item)"
|
@click="handleClick(item)"
|
||||||
>
|
>
|
||||||
<span>{{ item?.name }}</span>
|
<span>{{ item?.name }}</span>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="common-card" :class="[themeType]">
|
<div class="common-card" :class="[themeType]">
|
||||||
<public-title title="主诉分类" :themeType="themeType">
|
<public-title title="应急求助信息" :themeType="themeType">
|
||||||
<template #right><span class="right-text" :class="[themeType]" @click="viewDetail">查看详情</span> </template>
|
<template #right><span class="right-text" :class="[themeType]" @click="viewDetail">查看详情</span> </template>
|
||||||
</public-title>
|
</public-title>
|
||||||
<div class="zc-inner">
|
<div class="zc-inner">
|
||||||
|
|||||||
@@ -153,7 +153,6 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
async function getScrollList() {
|
async function getScrollList() {
|
||||||
console.log(dayjs());
|
|
||||||
try {
|
try {
|
||||||
const { code, result } = await getMonitorList({
|
const { code, result } = await getMonitorList({
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
|
|||||||
@@ -7,5 +7,5 @@ export enum Api {
|
|||||||
|
|
||||||
//健康终端报警
|
//健康终端报警
|
||||||
export const getMonitorList = (params: any) => get(Api.getMonitorList, params);
|
export const getMonitorList = (params: any) => get(Api.getMonitorList, params);
|
||||||
//长庆油田大病人数
|
//大病人数
|
||||||
export const getHealthDataList = (params: any) => get(Api.getHealthDataList, params);
|
export const getHealthDataList = (params: any) => get(Api.getHealthDataList, params);
|
||||||
|
|||||||
@@ -2,10 +2,7 @@
|
|||||||
<div class="common-card" :class="themeType">
|
<div class="common-card" :class="themeType">
|
||||||
<public-title title="基层医疗点远程会诊" :theme-type="themeType" />
|
<public-title title="基层医疗点远程会诊" :theme-type="themeType" />
|
||||||
<div class="inner">
|
<div class="inner">
|
||||||
<div class="inner-item" data-text="应急就医-服务中心"></div>
|
<div class="inner-item" data-text="服务中心"></div>
|
||||||
<div class="inner-item" data-text="采油九厂-薛岔作业区"></div>
|
|
||||||
<div class="inner-item" data-text="第九采油厂-五谷城作业区"></div>
|
|
||||||
<div class="inner-item" data-text="采油十一厂-彭阳作业区"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="common-card" :class="themeType">
|
<div class="common-card" :class="themeType">
|
||||||
<public-title title="长庆油田大病人数" :themeType="themeType" />
|
<public-title title="大病人数" :themeType="themeType" />
|
||||||
<div class="inner" ref="healthChart"></div>
|
<div class="inner" ref="healthChart"></div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { getAllList, getAmbulanceList } from '/@/components/chinaMap/chinaMapApi
|
|||||||
|
|
||||||
export enum TabType {
|
export enum TabType {
|
||||||
all = '', //所有资源
|
all = '', //所有资源
|
||||||
youTianYiYuan = '1', //油田医院
|
youTianYiYuan = '1', //医院
|
||||||
heZuoYiYuan = '2', //合作医院
|
heZuoYiYuan = '2', //合作医院
|
||||||
yiLiaoDian = '3', //医疗点
|
yiLiaoDian = '3', //医疗点
|
||||||
jiuHuChe = '5', //救护车
|
jiuHuChe = '5', //救护车
|
||||||
@@ -105,7 +105,7 @@ export function mapApiSwitch(type, params: any) {
|
|||||||
lat: 34, //纬度34
|
lat: 34, //纬度34
|
||||||
lon: 108, //经度
|
lon: 108, //经度
|
||||||
};
|
};
|
||||||
// 全部 油田 医疗点 合作医院
|
// 全部 医疗点 合作医院
|
||||||
if ([TabType.all, TabType.youTianYiYuan, TabType.yiLiaoDian, TabType.heZuoYiYuan, TabType.jiuHuChe, TabType.jiuHuChe].includes(type)) {
|
if ([TabType.all, TabType.youTianYiYuan, TabType.yiLiaoDian, TabType.heZuoYiYuan, TabType.jiuHuChe, TabType.jiuHuChe].includes(type)) {
|
||||||
return getAllList({ ...basicParams, ...params });
|
return getAllList({ ...basicParams, ...params });
|
||||||
}
|
}
|
||||||
@@ -115,12 +115,14 @@ export function mapApiSwitch(type, params: any) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const mapIcon1 = new URL('/@/assets/img/mapIcon1.png', import.meta.url).href;
|
export const mapIcon1 = new URL('/@/assets/img/mapIcon1.png', import.meta.url).href;
|
||||||
|
export const mapIcon11 = new URL('/@/assets/img/mapIcon1-1.png', import.meta.url).href;
|
||||||
export const mapIcon2 = new URL('/@/assets/img/mapIcon2.png', import.meta.url).href;
|
export const mapIcon2 = new URL('/@/assets/img/mapIcon2.png', import.meta.url).href;
|
||||||
export const mapIcon3 = new URL('/@/assets/img/mapIcon3.png', import.meta.url).href;
|
export const mapIcon3 = new URL('/@/assets/img/mapIcon3.png', import.meta.url).href;
|
||||||
export const mapIcon4 = new URL('/@/assets/img/mapIcon4.png', import.meta.url).href;
|
export const mapIcon4 = new URL('/@/assets/img/mapIcon4.png', import.meta.url).href;
|
||||||
|
export const mapIcon41 = new URL('/@/assets/img/mapIcon4-1.png', import.meta.url).href;
|
||||||
export const mapIcon5 = new URL('/@/assets/img/mapIcon5.png', import.meta.url).href;
|
export const mapIcon5 = new URL('/@/assets/img/mapIcon5.png', import.meta.url).href;
|
||||||
export const mapIcon6 = new URL('/@/assets/img/mapIcon6.png', import.meta.url).href;
|
export const mapIcon51 = new URL('/@/assets/img/mapIcon5-1.png', import.meta.url).href;
|
||||||
export const mapIcon7 = new URL('/@/assets/img/mapIcon7.png', import.meta.url).href;
|
export const watchIconR = new URL('/@/assets/img/watchIconR.svg', import.meta.url).href;
|
||||||
|
|
||||||
export const legend1 = new URL('/@/assets/img/legend1.png', import.meta.url).href;
|
export const legend1 = new URL('/@/assets/img/legend1.png', import.meta.url).href;
|
||||||
export const legend2 = new URL('/@/assets/img/legend2.png', import.meta.url).href;
|
export const legend2 = new URL('/@/assets/img/legend2.png', import.meta.url).href;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
:width="props.width"
|
:width="props.width"
|
||||||
:maskClosable="true"
|
:maskClosable="true"
|
||||||
@cancel="handleCancel"
|
@cancel="handleCancel"
|
||||||
|
v-bind="$attrs"
|
||||||
destroy-on-close
|
destroy-on-close
|
||||||
>
|
>
|
||||||
<template #title>
|
<template #title>
|
||||||
@@ -23,6 +24,7 @@
|
|||||||
:width="props.width"
|
:width="props.width"
|
||||||
:maskClosable="true"
|
:maskClosable="true"
|
||||||
@cancel="handleCancel"
|
@cancel="handleCancel"
|
||||||
|
v-bind="$attrs"
|
||||||
destroy-on-close
|
destroy-on-close
|
||||||
>
|
>
|
||||||
<template #title>
|
<template #title>
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
// 步数
|
// 步数
|
||||||
import {visibleChartLabel} from "/@/utils/utils.ts";
|
import { visibleChartLabel } from '/@/utils/utils.ts';
|
||||||
|
|
||||||
export function screenPie(chartData) {
|
export function screenPie(chartData) {
|
||||||
const data = visibleChartLabel(chartData)
|
const data = visibleChartLabel(chartData).map((item) => {
|
||||||
|
item = { ...item, label: { show: item.value > 0 }, labelLine: { show: item.value > 0 } };
|
||||||
|
return item;
|
||||||
|
});
|
||||||
return {
|
return {
|
||||||
color: ['#B750BE', '#6C63F0', '#3AACFF', '#ED589D', '#FB466C'],
|
color: ['#B750BE', '#6C63F0', '#3AACFF', '#ED589D', '#FB466C'],
|
||||||
tooltip: {
|
tooltip: {
|
||||||
@@ -24,7 +27,7 @@ export function screenPie(chartData) {
|
|||||||
color: '#ffffff',
|
color: '#ffffff',
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
},
|
},
|
||||||
selectedMode: false
|
selectedMode: false,
|
||||||
},
|
},
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
@@ -35,13 +38,14 @@ export function screenPie(chartData) {
|
|||||||
label: {
|
label: {
|
||||||
show: true,
|
show: true,
|
||||||
position: 'outside',
|
position: 'outside',
|
||||||
|
formatter: '{c}人次',
|
||||||
color: '#ffffff',
|
color: '#ffffff',
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
},
|
},
|
||||||
labelLine: {
|
labelLine: {
|
||||||
show: true,
|
show: true,
|
||||||
length: 6,
|
length: 6,
|
||||||
length2: 10,
|
length2: 30,
|
||||||
minTurnAngle: 120,
|
minTurnAngle: 120,
|
||||||
},
|
},
|
||||||
data: data,
|
data: data,
|
||||||
@@ -52,7 +56,7 @@ export function screenPie(chartData) {
|
|||||||
|
|
||||||
//左侧圆
|
//左侧圆
|
||||||
export function circleRing(chartData, total, color) {
|
export function circleRing(chartData, total, color) {
|
||||||
const data = visibleChartLabel(chartData)
|
const data = visibleChartLabel(chartData);
|
||||||
return {
|
return {
|
||||||
color: color,
|
color: color,
|
||||||
tooltip: {
|
tooltip: {
|
||||||
@@ -74,7 +78,7 @@ export function circleRing(chartData, total, color) {
|
|||||||
color: '#ffffff',
|
color: '#ffffff',
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
},
|
},
|
||||||
selectedMode: false
|
selectedMode: false,
|
||||||
},
|
},
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
@@ -95,14 +99,14 @@ export function circleRing(chartData, total, color) {
|
|||||||
label: {
|
label: {
|
||||||
show: true,
|
show: true,
|
||||||
position: 'outside',
|
position: 'outside',
|
||||||
formatter: '{d}%',
|
formatter: '{d}%\n{c}人次',
|
||||||
color: '#ffffff',
|
color: '#ffffff',
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
},
|
},
|
||||||
labelLine: {
|
labelLine: {
|
||||||
show: true,
|
show: true,
|
||||||
length: 4,
|
length: 2,
|
||||||
length2: 12,
|
length2: 30,
|
||||||
minTurnAngle: 120,
|
minTurnAngle: 120,
|
||||||
},
|
},
|
||||||
data: data,
|
data: data,
|
||||||
|
|||||||
@@ -3,17 +3,23 @@ import { get, post } from '/@/api/request.ts';
|
|||||||
export enum Api {
|
export enum Api {
|
||||||
depart = '/health-watch/watch/watchDevice/getUnitList',
|
depart = '/health-watch/watch/watchDevice/getUnitList',
|
||||||
getWatchDataByOrgCode = '/health-watch/watch/watchData/getWatchDataByOrgCode',
|
getWatchDataByOrgCode = '/health-watch/watch/watchData/getWatchDataByOrgCode',
|
||||||
|
|
||||||
|
getWatchDataByOrgCodeNew = '/health-watch/watch/watchData/getWatchDataByOrgCode/v2',
|
||||||
getMonitorList = '/health-watch/watch/watchMonitorData/getMonitorList',
|
getMonitorList = '/health-watch/watch/watchMonitorData/getMonitorList',
|
||||||
sleepApi = '/health-watch/watch/watchData/getSleep',
|
sleepApi = '/health-watch/watch/watchData/getSleep/v2',
|
||||||
stepApi = '/health-watch/watch/watchData/getSdc',
|
stepApi = '/health-watch/watch/watchData/getSdc',
|
||||||
|
stepNewApi = '/health-watch/watch/watchData/getSdc/v2',
|
||||||
listCustom = 'health-watch/watch/watchMonitorData/listCustom',
|
listCustom = 'health-watch/watch/watchMonitorData/listCustom',
|
||||||
|
listCustomNew = 'health-watch/watch/watchMonitorData/largeScreen/listCustom',
|
||||||
}
|
}
|
||||||
|
|
||||||
// 单位-
|
// 单位-
|
||||||
export const allSecondaryDeparts = () => get(Api.depart);
|
export const allSecondaryDeparts = () => get(Api.depart);
|
||||||
|
|
||||||
//左側
|
//左側
|
||||||
export const getWatchDataByOrgCode = (params) => post(Api.getWatchDataByOrgCode, params);
|
export const getWatchDataByOrgCode = (params: any) => post(Api.getWatchDataByOrgCode, params);
|
||||||
|
|
||||||
|
export const getWatchDataByOrgCodeNew = (params: any) => post(Api.getWatchDataByOrgCodeNew, params);
|
||||||
|
|
||||||
//右-健康终端报警
|
//右-健康终端报警
|
||||||
export const getMonitorList = (params: any) => get(Api.getMonitorList, params);
|
export const getMonitorList = (params: any) => get(Api.getMonitorList, params);
|
||||||
@@ -21,6 +27,8 @@ export const getMonitorList = (params: any) => get(Api.getMonitorList, params);
|
|||||||
export const getSleep = (params: any) => post(Api.sleepApi, params);
|
export const getSleep = (params: any) => post(Api.sleepApi, params);
|
||||||
// 右-步数
|
// 右-步数
|
||||||
export const getStep = (params: any) => post(Api.stepApi, params);
|
export const getStep = (params: any) => post(Api.stepApi, params);
|
||||||
|
export const getStepNew = (params: any) => post(Api.stepNewApi, params);
|
||||||
|
|
||||||
//副屏-预警
|
//副屏-预警
|
||||||
export const getCustom = (params: any) => get(Api.listCustom, params);
|
export const getCustom = (params: any) => get(Api.listCustom, params);
|
||||||
|
export const getCustomNew = (params: any) => get(Api.listCustomNew, params);
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
import { onMounted, ref, watch } from 'vue';
|
import { onMounted, ref, watch } from 'vue';
|
||||||
import { cloneDeep } from 'lodash-es';
|
import { cloneDeep } from 'lodash-es';
|
||||||
import ItemD from '/@/components/item-d/item-d.vue';
|
import ItemD from '/@/components/item-d/item-d.vue';
|
||||||
import { getWatchDataByOrgCode } from '/@/components/secondaryScreen/commonApi.ts';
|
import { getWatchDataByOrgCode, getWatchDataByOrgCodeNew } from '/@/components/secondaryScreen/commonApi.ts';
|
||||||
import { useSpin } from '/@/components/body-d-left/bodyLeftHooks.ts';
|
import { useSpin } from '/@/components/body-d-left/bodyLeftHooks.ts';
|
||||||
import { useSession, caching } from '/@/hooks/autoRefresh/caching.ts';
|
import { useSession, caching } from '/@/hooks/autoRefresh/caching.ts';
|
||||||
|
|
||||||
@@ -79,8 +79,14 @@
|
|||||||
orgCode: String,
|
orgCode: String,
|
||||||
type: String,
|
type: String,
|
||||||
themeType: String,
|
themeType: String,
|
||||||
|
refreshState: {
|
||||||
|
type: Boolean,
|
||||||
|
default: () => false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
onMounted(() => {
|
||||||
|
init();
|
||||||
});
|
});
|
||||||
onMounted(() => {});
|
|
||||||
watch(props, () => {
|
watch(props, () => {
|
||||||
init();
|
init();
|
||||||
});
|
});
|
||||||
@@ -88,14 +94,15 @@
|
|||||||
async function init() {
|
async function init() {
|
||||||
setSpin(true);
|
setSpin(true);
|
||||||
try {
|
try {
|
||||||
const { code, result } = await getWatchDataByOrgCode(props);
|
// const { code, result } = await getWatchDataByOrgCode(props);
|
||||||
|
const { code, result } = await getWatchDataByOrgCodeNew(props);
|
||||||
if (code == 200) {
|
if (code == 200) {
|
||||||
setValue(result);
|
setValue(result);
|
||||||
setSession(PAGE2_LEFT, JSON.stringify(result));
|
// setSession(PAGE2_LEFT, JSON.stringify(result));
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
const result = JSON.parse(getSession(PAGE2_LEFT));
|
// const result = JSON.parse(getSession(PAGE2_LEFT));
|
||||||
setValue(result);
|
// setValue(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,8 +112,10 @@
|
|||||||
data.forEach((item) => {
|
data.forEach((item) => {
|
||||||
let resData = result.find((v) => v.type == item.type);
|
let resData = result.find((v) => v.type == item.type);
|
||||||
item.userCount = resData.userCount;
|
item.userCount = resData.userCount;
|
||||||
item.data.forEach((val) => {
|
item.data = item.data.map((val) => {
|
||||||
val.value = resData[val.key];
|
val.value = resData[val.key];
|
||||||
|
val = { ...val, label: { show: val.value > 0 }, labelLine: { show: val.value > 0 } };
|
||||||
|
return val;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
dataList.value = data;
|
dataList.value = data;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<public-title title="睡眠" :theme-type="themeType" />
|
<public-title title="睡眠" :theme-type="themeType" />
|
||||||
<div class="inner">
|
<div class="inner">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<span class="lightSleep">平均睡眠 {{ nums.avg }} 深睡 {{ nums.avgLong }} 浅睡 {{ nums.avgShort }}</span>
|
<span class="lightSleep">平均睡眠 {{ nums.avg || 0 }} 深睡 {{ nums.avgLong || 0 }} 浅睡 {{ nums.avgShort || 0 }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="sleepChart" ref="sleepChart"></div>
|
<div class="sleepChart" ref="sleepChart"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ export function sleepCharts({ time, short, long, awake, type }) {
|
|||||||
grid: {
|
grid: {
|
||||||
top: '15%',
|
top: '15%',
|
||||||
left: '5%',
|
left: '5%',
|
||||||
right: '5%',
|
right: '8%',
|
||||||
bottom: 30,
|
bottom: 30,
|
||||||
},
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
|
|||||||
@@ -26,14 +26,14 @@
|
|||||||
</a-table>
|
</a-table>
|
||||||
</div>
|
</div>
|
||||||
<!--预警查看详情-->
|
<!--预警查看详情-->
|
||||||
<EarlyWarningDialog ref="earlyWarningRef"></EarlyWarningDialog>
|
<EarlyWarningDialog ref="earlyWarningRef" :org-codes="orgCode" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onUnmounted, ref, watch } from 'vue';
|
import { onUnmounted, ref, watch } from 'vue';
|
||||||
import PublicTitle from '/@/components/publicTitle.vue';
|
import PublicTitle from '/@/components/publicTitle.vue';
|
||||||
import EarlyWarningDialog from '/@/views/components/earlyWarningDialog.vue';
|
import EarlyWarningDialog from '/@/views/components/earlyWarningDialog.vue';
|
||||||
import { getCustom, getMonitorList } from '/@/components/secondaryScreen/commonApi.ts';
|
import { getCustom, getCustomNew, getMonitorList } from '/@/components/secondaryScreen/commonApi.ts';
|
||||||
import { monitorColumns } from '/@/components/secondaryScreen/screen-right/screenRightHooks.ts';
|
import { monitorColumns } from '/@/components/secondaryScreen/screen-right/screenRightHooks.ts';
|
||||||
import $bus from '/@/utils/bus.ts';
|
import $bus from '/@/utils/bus.ts';
|
||||||
import { earlyWarning } from '/@/utils/busConstant.ts';
|
import { earlyWarning } from '/@/utils/busConstant.ts';
|
||||||
@@ -64,7 +64,7 @@
|
|||||||
function handlePoliceDetail(record) {
|
function handlePoliceDetail(record) {
|
||||||
$bus.emit(earlyWarning, record);
|
$bus.emit(earlyWarning, record);
|
||||||
}
|
}
|
||||||
|
const threeCodes = ref();
|
||||||
watch(
|
watch(
|
||||||
props,
|
props,
|
||||||
() => {
|
() => {
|
||||||
@@ -75,7 +75,12 @@
|
|||||||
},
|
},
|
||||||
{ immediate: true, deep: true }
|
{ immediate: true, deep: true }
|
||||||
);
|
);
|
||||||
|
watch(
|
||||||
|
() => props.orgCode,
|
||||||
|
() => {
|
||||||
|
threeCodes.value = props.orgCode;
|
||||||
|
}
|
||||||
|
);
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
window.removeEventListener('resize', setHeight);
|
window.removeEventListener('resize', setHeight);
|
||||||
});
|
});
|
||||||
@@ -89,7 +94,8 @@
|
|||||||
async function init(page) {
|
async function init(page) {
|
||||||
try {
|
try {
|
||||||
const { pageSize } = pagination.value;
|
const { pageSize } = pagination.value;
|
||||||
const { code, result } = await getCustom({ pageSize, pageNo: page, orgCode: props.orgCode });
|
// const { code, result } = await getCustom({ pageSize, pageNo: page, orgCode: props.orgCode });
|
||||||
|
const { code, result } = await getCustomNew({ pageSize, pageNo: page, orgCodes: props.orgCode });
|
||||||
if (code == 200) {
|
if (code == 200) {
|
||||||
setSession(PAGE2_RIGHT3, JSON.stringify({ page, result }));
|
setSession(PAGE2_RIGHT3, JSON.stringify({ page, result }));
|
||||||
setValue(result, page);
|
setValue(result, page);
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
import { useSpin } from '/@/components/body-d-left/bodyLeftHooks.ts';
|
import { useSpin } from '/@/components/body-d-left/bodyLeftHooks.ts';
|
||||||
import { useStep } from '/@/components/secondaryScreen/screen-right/screenRightHooks.ts';
|
import { useStep } from '/@/components/secondaryScreen/screen-right/screenRightHooks.ts';
|
||||||
import { screenPie } from '/@/components/item-d/pieCharts.ts';
|
import { screenPie } from '/@/components/item-d/pieCharts.ts';
|
||||||
import { getStep } from '/@/components/secondaryScreen/commonApi.ts';
|
import { getStep, getStepNew } from '/@/components/secondaryScreen/commonApi.ts';
|
||||||
import { caching, useSession } from '/@/hooks/autoRefresh/caching.ts';
|
import { caching, useSession } from '/@/hooks/autoRefresh/caching.ts';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -41,7 +41,8 @@
|
|||||||
orgCode: props.orgCode,
|
orgCode: props.orgCode,
|
||||||
type: props.type,
|
type: props.type,
|
||||||
};
|
};
|
||||||
const { code, result } = await getStep(params);
|
// const { code, result } = await getStep(params);
|
||||||
|
const { code, result } = await getStepNew(params);
|
||||||
if (code == 200) {
|
if (code == 200) {
|
||||||
setValue(result);
|
setValue(result);
|
||||||
setSession(PAGE2_RIGHT2, JSON.stringify(result));
|
setSession(PAGE2_RIGHT2, JSON.stringify(result));
|
||||||
|
|||||||
@@ -9,13 +9,12 @@
|
|||||||
:rowClassName="(record, index) => (index % 2 === 1 ? 'table-default' : 'table-striped')"
|
:rowClassName="(record, index) => (index % 2 === 1 ? 'table-default' : 'table-striped')"
|
||||||
>
|
>
|
||||||
<template #bodyCell="{ column, record, index, text }">
|
<template #bodyCell="{ column, record, index, text }">
|
||||||
<template v-if="column.customRender && isFunction(column.customRender)" v-html="column.customRender({ column, record, index, text })">
|
<template v-if="column?.customRender && isFunction(column.customRender)" v-html="column.customRender({ column, record, index, text })">
|
||||||
</template>
|
</template>
|
||||||
<slot v-else-if="column.slot" :name="column.slot" v-bind="{ column, record, index, text } || {}"></slot>
|
<slot v-else-if="column?.slot" :name="column.slot" v-bind="{ column, record, index, text } || {}"></slot>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
{{ text }}
|
{{ text }}
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<slot name="bodyCell" v-bind="{ column, record, index, text } || {}"></slot>
|
<slot name="bodyCell" v-bind="{ column, record, index, text } || {}"></slot>
|
||||||
</template>
|
</template>
|
||||||
</a-table>
|
</a-table>
|
||||||
@@ -32,6 +31,7 @@
|
|||||||
type: Function,
|
type: Function,
|
||||||
},
|
},
|
||||||
apiParams: {
|
apiParams: {
|
||||||
|
type: Object,
|
||||||
default: () => {},
|
default: () => {},
|
||||||
},
|
},
|
||||||
afterFetch: {
|
afterFetch: {
|
||||||
@@ -53,6 +53,7 @@
|
|||||||
showTotal: (total) => `总 ${total} 条`,
|
showTotal: (total) => `总 ${total} 条`,
|
||||||
onChange: pageChange,
|
onChange: pageChange,
|
||||||
});
|
});
|
||||||
|
const emit = defineEmits(['tableLoad']);
|
||||||
|
|
||||||
function pageChange(page, pageS) {
|
function pageChange(page, pageS) {
|
||||||
current.value = page;
|
current.value = page;
|
||||||
@@ -75,21 +76,24 @@
|
|||||||
async function getRecords() {
|
async function getRecords() {
|
||||||
try {
|
try {
|
||||||
tableLoading.value = true;
|
tableLoading.value = true;
|
||||||
|
emit('tableLoad', true);
|
||||||
const { code, result } = await props.api({ ...props.apiParams, pageSize: pageSize.value, pageNo: current.value });
|
const { code, result } = await props.api({ ...props.apiParams, pageSize: pageSize.value, pageNo: current.value });
|
||||||
if (code == 200) {
|
if (code == 200) {
|
||||||
if (props.afterFetch && isFunction(props.afterFetch)) {
|
if (props.afterFetch && isFunction(props.afterFetch)) {
|
||||||
dataSource.value = props.afterFetch({ result, setPage: setPage, setDataSource: setDataSource });
|
dataSource.value = props.afterFetch({ result, setPage: setPage, setDataSource: setDataSource });
|
||||||
} else {
|
} else {
|
||||||
dataSource.value = result?.records;
|
dataSource.value = result?.records ? result?.records : result;
|
||||||
|
}
|
||||||
|
if (result.hasOwnProperty('records')) {
|
||||||
|
paginationProp.value = { ...paginationProp.value, ...result };
|
||||||
|
paginationProp.value.pageSize = result.size;
|
||||||
}
|
}
|
||||||
paginationProp.value = { ...paginationProp.value, ...result };
|
|
||||||
paginationProp.value.pageSize = result.size;
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
dataSource.value = [];
|
dataSource.value = [];
|
||||||
tableLoading.value = false;
|
|
||||||
} finally {
|
} finally {
|
||||||
tableLoading.value = false;
|
tableLoading.value = false;
|
||||||
|
emit('tableLoad', false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,8 +117,8 @@
|
|||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
:deep(.ant-table-thead > tr > th) {
|
:deep(.ant-table-thead > tr > th) {
|
||||||
color: var(--tableFontColor) !important;
|
color: var(--tableFontColor) !important;
|
||||||
background: var(--tableHeadBg) !important;
|
background: #073063 !important;
|
||||||
border-top: 1px solid var(--tableHeadBg) !important;
|
border-top: 1px solid #073063 !important;
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
padding: 9px 9px;
|
padding: 9px 9px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|||||||
@@ -12,12 +12,22 @@
|
|||||||
<div v-show="isShow" :class="className" class="footer-data" style="--moveUp: 20px; --moveDown: -80px">
|
<div v-show="isShow" :class="className" class="footer-data" style="--moveUp: 20px; --moveDown: -80px">
|
||||||
<div
|
<div
|
||||||
v-for="item in footerList"
|
v-for="item in footerList"
|
||||||
:style="{ cursor: item.key == 'allotDeviceNum' ? 'pointer' : 'default' }"
|
:style="{
|
||||||
|
cursor: item.key == 'allotDeviceNum' ? 'pointer' : 'default',
|
||||||
|
}"
|
||||||
|
:key="`footer-list-${item.key}`"
|
||||||
class="col-item nowrap"
|
class="col-item nowrap"
|
||||||
@click="openWatchList(item)"
|
@click="openWatchList(item)"
|
||||||
>
|
>
|
||||||
<div class="col-text">{{ item?.name }}</div>
|
<div class="col-text">{{ item?.name }}</div>
|
||||||
<div :title="item.value" class="col-value nowrap">{{ item?.value }}</div>
|
<div
|
||||||
|
:title="item.value"
|
||||||
|
class="col-value nowrap"
|
||||||
|
:style="{
|
||||||
|
color: item.key == 'allotDeviceNum' ? '#0098fa' : '#fff',
|
||||||
|
}"
|
||||||
|
>{{ item?.value }}</div
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
@@ -26,10 +36,10 @@
|
|||||||
class="footer-dialog-con"
|
class="footer-dialog-con"
|
||||||
style="--moveUp: 20px; --moveDown: -180px"
|
style="--moveUp: 20px; --moveDown: -180px"
|
||||||
>
|
>
|
||||||
<FooterDialog :detailLeft="detailLeft" :detailRight="detailRight" @closeDialog="closeDialog"></FooterDialog>
|
<FooterDialog :detailLeft="detailLeft" :detailRight="detailRight" @closeDialog="closeDialog" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<WatchListDialog ref="watchDialogRef" @close="closeWatch"></WatchListDialog>
|
<WatchListDialog ref="watchDialogRef" @close="closeWatch" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
@@ -103,7 +113,7 @@
|
|||||||
async (val) => {
|
async (val) => {
|
||||||
let params = {
|
let params = {
|
||||||
orgCodeLeaf: val.orgCode,
|
orgCodeLeaf: val.orgCode,
|
||||||
orgCode: null,
|
orgCode: props.orgCode,
|
||||||
};
|
};
|
||||||
const { code, result } = await footerApi(params);
|
const { code, result } = await footerApi(params);
|
||||||
if (code == 200) {
|
if (code == 200) {
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export function useDepart() {
|
|||||||
async function getOptionData() {
|
async function getOptionData() {
|
||||||
const { result = [], code } = await allSecondaryDeparts();
|
const { result = [], code } = await allSecondaryDeparts();
|
||||||
if (code == 200) {
|
if (code == 200) {
|
||||||
let res = [{ departName: '长庆油田', orgCode: 'A01' }, ...result];
|
let res = [...result];
|
||||||
setOption(res);
|
setOption(res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -264,14 +264,12 @@
|
|||||||
// console.log('绘制驾车路线完成');
|
// console.log('绘制驾车路线完成');
|
||||||
searchFlag.value = false;
|
searchFlag.value = false;
|
||||||
} else {
|
} else {
|
||||||
console.log('获取驾车数据失败:', result);
|
|
||||||
searchFlag.value = false;
|
searchFlag.value = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
AMap.Event.addListener(driving, 'complete', (res) => {
|
AMap.Event.addListener(driving, 'complete', (res) => {
|
||||||
const data = JSON.parse(JSON.stringify(res));
|
const data = JSON.parse(JSON.stringify(res));
|
||||||
searchFlag.value = false;
|
searchFlag.value = false;
|
||||||
console.log('查询结果', data);
|
|
||||||
if (data.info == 'OK') {
|
if (data.info == 'OK') {
|
||||||
const { routes } = data;
|
const { routes } = data;
|
||||||
setTitle(routes[0]);
|
setTitle(routes[0]);
|
||||||
|
|||||||
@@ -2,10 +2,12 @@ import { get } from '/@/api/request.ts';
|
|||||||
|
|
||||||
export enum Api {
|
export enum Api {
|
||||||
allList = '/health-emergency/emergency/tblAmbulanceGps/getAllList',
|
allList = '/health-emergency/emergency/tblAmbulanceGps/getAllList',
|
||||||
|
allListNew = '/health-emergency/emergency/LargeScreenNew/map',
|
||||||
getAmbulanceList = '/health-emergency/emergency/tblAmbulanceGps/getAmbulanceList',
|
getAmbulanceList = '/health-emergency/emergency/tblAmbulanceGps/getAmbulanceList',
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取所有资源
|
// 获取所有资源
|
||||||
export const getAllList = (params) => get(Api.allList, params);
|
export const getAllList = (params) => get(Api.allList, params);
|
||||||
|
export const getAllListNew = (params) => get(Api.allListNew, params);
|
||||||
//获取救护车GPS信息
|
//获取救护车GPS信息
|
||||||
export const getAmbulanceList = (params) => get(Api.getAmbulanceList, params);
|
export const getAmbulanceList = (params) => get(Api.getAmbulanceList, params);
|
||||||
@@ -1,44 +1,29 @@
|
|||||||
import { getAllList, getAmbulanceList } from '/@/components/xianMap/xianMapApi.ts';
|
import { getAllListNew } from '/@/components/xianMap/xianMapApi.ts';
|
||||||
|
|
||||||
export enum TabType {
|
export enum TabType {
|
||||||
all = '', //所有资源
|
all = '', //所有资源
|
||||||
youTianYiYuan = '1', //油田医院
|
youTianYiYuan = '1', //医院
|
||||||
heZuoYiYuan = '2', //合作医院
|
jianKangXiaoWu = '3', //健康小屋
|
||||||
yiLiaoDian = '3', //医疗点
|
aed = '4', //AED
|
||||||
jiuHuChe = '5', //救护车
|
jiuHuChe = '5', //救护车
|
||||||
zhiShengJi = '4', //直升机
|
|
||||||
diFangYiYuan = '6', //地方医院
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const tabList = [
|
export const tabList = [
|
||||||
{
|
{
|
||||||
name: '所有资源',
|
name: '所有资源',
|
||||||
type: TabType.all,
|
type: TabType.all,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '油田医院',
|
name: '健康小屋',
|
||||||
type: TabType.youTianYiYuan,
|
type: TabType.jianKangXiaoWu,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '医疗点',
|
name: '油田医院',
|
||||||
type: TabType.yiLiaoDian,
|
type: TabType.youTianYiYuan,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '救护车',
|
name: '救护车',
|
||||||
type: TabType.jiuHuChe,
|
type: TabType.jiuHuChe,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: '合作医院',
|
|
||||||
type: TabType.heZuoYiYuan,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '地方医院',
|
|
||||||
type: TabType.diFangYiYuan,
|
|
||||||
},
|
|
||||||
// {
|
|
||||||
// name: '直升机',
|
|
||||||
// type: TabType.zhiShengJi,
|
|
||||||
// },
|
|
||||||
{
|
{
|
||||||
name: '急救路线',
|
name: '急救路线',
|
||||||
},
|
},
|
||||||
@@ -100,14 +85,11 @@ export function aircraftContent() {
|
|||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mapApiSwitch(type, params: any) {
|
export function mapApiSwitch(type: any, params: any) {
|
||||||
const basicParams = {
|
// 全部 医疗点 合作医院
|
||||||
lat: 34, //纬度34
|
if ([TabType.all, TabType.youTianYiYuan, TabType.jianKangXiaoWu, TabType.jiuHuChe, TabType.aed].includes(type)) {
|
||||||
lon: 108, //经度
|
// return getAllList({ ...params });
|
||||||
};
|
return getAllListNew({ ...params });
|
||||||
// 全部 油田 医疗点 合作医院
|
|
||||||
if ([TabType.all, TabType.youTianYiYuan, TabType.yiLiaoDian, TabType.heZuoYiYuan, TabType.jiuHuChe, TabType.jiuHuChe].includes(type)) {
|
|
||||||
return getAllList({ ...basicParams, ...params });
|
|
||||||
}
|
}
|
||||||
// if ([TabType.jiuHuChe].includes(type)) {
|
// if ([TabType.jiuHuChe].includes(type)) {
|
||||||
// return getAmbulanceList({});
|
// return getAmbulanceList({});
|
||||||
|
|||||||
@@ -13,11 +13,13 @@ export enum PageEnum {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function setBaseLogin(baseLogin) {
|
export function setBaseLogin(baseLogin) {
|
||||||
localStorage.setItem('baseLogin', baseLogin);
|
// localStorage.setItem('baseLogin', baseLogin);
|
||||||
|
localStorage.setItem('baseLogin', '/login');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getBaseLogin() {
|
export function getBaseLogin() {
|
||||||
return localStorage.getItem('baseLogin');
|
// return localStorage.getItem('baseLogin');
|
||||||
|
return '/login';
|
||||||
}
|
}
|
||||||
|
|
||||||
//根据当前路径跳转登录页
|
//根据当前路径跳转登录页
|
||||||
@@ -54,7 +56,7 @@ export function toHomeByLogoutPath(nowPath: string) {
|
|||||||
|
|
||||||
//缓存退出时的页面路径
|
//缓存退出时的页面路径
|
||||||
export function setCurrentPath(nowPath) {
|
export function setCurrentPath(nowPath) {
|
||||||
localStorage.setItem(LOGOUT_CURRENT_PATH, nowPath);
|
localStorage.setItem(LOGOUT_CURRENT_PATH, '/login');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getCurrentPath() {
|
export function getCurrentPath() {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export const caching = {
|
|||||||
LEFT_KEY3: 'LEFT_KEY3',
|
LEFT_KEY3: 'LEFT_KEY3',
|
||||||
//健康终端报警
|
//健康终端报警
|
||||||
RIGHT_KEY1: 'RIGHT_KEY1',
|
RIGHT_KEY1: 'RIGHT_KEY1',
|
||||||
//长庆油田大病人数
|
//大病人数
|
||||||
RIGHT_KEY2: 'RIGHT_KEY2',
|
RIGHT_KEY2: 'RIGHT_KEY2',
|
||||||
//心血管监测-心率-压力-血氧-体温
|
//心血管监测-心率-压力-血氧-体温
|
||||||
PAGE2_LEFT: 'PAGE2_LEFT',
|
PAGE2_LEFT: 'PAGE2_LEFT',
|
||||||
|
|||||||
@@ -2,6 +2,6 @@
|
|||||||
const BASIC_MIN = 60 * 1000; //1min
|
const BASIC_MIN = 60 * 1000; //1min
|
||||||
const BASIC_SECOND = 1000; //1s
|
const BASIC_SECOND = 1000; //1s
|
||||||
//副屏 心率、血氧、压力、睡眠、步数
|
//副屏 心率、血氧、压力、睡眠、步数
|
||||||
export const DEFAULT_TIME = BASIC_MIN * 10;
|
export const DEFAULT_TIME = BASIC_MIN * 0.1;
|
||||||
//预警分页切换
|
//预警分页切换
|
||||||
export const PAGE_SWITCH = BASIC_SECOND * 3;
|
export const PAGE_SWITCH = BASIC_SECOND * 3;
|
||||||
|
|||||||