Browse Source

feat: 公共路径修改,以便适配生产环境使用

feature-v1.0.0
邱伟洋 1 month ago
parent
commit
74034294b7
  1. 2
      env/.env.development
  2. 2
      env/.env.production
  3. 67
      src/api/safety/index.ts
  4. 6
      src/components.d.ts
  5. BIN
      src/components/.DS_Store
  6. 209
      src/components/Upload/UploadFrag.vue
  7. 420
      src/components/Upload/UploadImage.vue
  8. 343
      src/components/Upload/useFileUpload/index.ts
  9. 57
      src/components/Upload/useMd5/index.ts
  10. 289
      src/components/Video/index.vue
  11. 16
      src/components/Video/lib/global.d.ts
  12. 5194
      src/components/Video/lib/mp4box.all.min.js
  13. 125
      src/components/Video/lib/syPlayer.js
  14. 2
      src/hooks/web/useMap.tsx
  15. 244
      src/hooks/web/useProject.tsx
  16. 60
      src/utils/mapUtils.ts
  17. 13
      src/views/BottomPanel/index.vue
  18. 31
      src/views/Main/Map/components/LayerTree.vue
  19. 139
      vite.config.ts

2
env/.env.development

@ -2,7 +2,7 @@
VITE_APP_TITLE = 水利工程
# 打包路径
VITE_BASE_URL= /sy-water-data-board-ui
VITE_BASE_URL= /sgcyy-slgcyxgl/sy-water-data-board-ui
# 接口请求基础地址
VITE_GLOB_API_URL= /api

2
env/.env.production

@ -1,7 +1,7 @@
# 标题
VITE_APP_TITLE = 水利工程
SERVICE_NAME = sy-water-data-board-ui
SERVICE_NAME = sgcyy-slgcyxgl/sy-water-data-board-ui
# 打包路径
VITE_BASE_URL= /${SERVICE_NAME}

67
src/api/safety/index.ts

@ -1,107 +1,106 @@
// import axios from 'axios';
import { he } from 'element-plus/es/locale';
import { request } from '../axios';
import { request } from "../axios";
// 预警统计
export const getWarningStatistic = (params: any) => {
return request({
url: '/run/api/warning/statistic',
method: 'post',
params
url: "/run/api/warning/statistic",
method: "post",
params,
});
};
// 预警列表
export const getWarningList = (params: any) => {
return request({
url: '/run/api/warning/list',
method: 'post',
params
url: "/run/api/warning/list",
method: "post",
params,
});
};
// 查询行政区划数据
export const getAreasData = () => {
return request({
url: '/run/api/common/xzgh/getGuangDong',
method: 'get'
url: "/run/api/common/xzgh/getGuangDong",
method: "get",
});
};
// 根据字典类型查询字典数据信息
export function getDicts(dictType: any) {
return request({
url: '/run/api/common/dict/type/' + dictType,
method: 'get'
url: "/run/api/common/dict/type/" + dictType,
method: "get",
});
}
// 通过类型和编码查询对象详细信息
export const getObjectInfo = (params: any) => {
return request({
url: '/run/api/object/info/' + params.type + '/' + params.code,
method: 'get'
url: "/run/api/object/info/" + params.type + "/" + params.code,
method: "get",
});
};
// 视频图像
export const getVideoWarningList = (params: any) => {
return request({
url: '/run/api/video/warning/list', // /run/api/video/warning/list
method: 'post',
params
url: "/run/api/video/warning/list", // /run/api/video/warning/list
method: "post",
params,
});
};
// 运行指标列表
export const getIndicatorList = (params: any) => {
return request({
url: '/run/api/indicator/list',
method: 'post',
params
url: "/run/api/indicator/list",
method: "post",
params,
});
};
// 按小时统计运行坐标
export const getIndicatorHour = (params: any) => {
return request({
url: '/run/api/indicator/hour',
method: 'post',
params
url: "/run/api/indicator/hour",
method: "post",
params,
});
};
// 最大最小平均值
export const getIndicatorStatistic = (params: any) => {
return request({
url: '/run/api/indicator/statistic',
method: 'post',
params
url: "/run/api/indicator/statistic",
method: "post",
params,
});
};
// 水利对象统计
export const getObjectStatistic = (params: any) => {
return request({
url: '/run/api/object/statistic',
method: 'post',
params
url: "/run/api/object/statistic",
method: "post",
params,
});
};
function getCookie(name: string): string | null {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop()?.split(';').shift() || null;
if (parts.length === 2) return parts.pop()?.split(";").shift() || null;
return null;
}
export function getlStatisticChart(data: any) {
// 获取cookie
const shuili = getCookie('Admin-Token');
const shuili = getCookie("Admin-Token");
return request({
url: `/run/statistic/chart`,
method: 'post',
method: "post",
data,
headers: {
// 'Admin-Token': shuili,
shuili: 'water ' + shuili
}
shuili: "water " + shuili,
},
});
}

6
src/components.d.ts

@ -23,11 +23,5 @@ declare module 'vue' {
RouterView: typeof import('vue-router')['RouterView']
TableColumn: typeof import('./components/TableColumn/index.vue')['default']
Tree: typeof import('./components/Tree/index.vue')['default']
UploadFrag: typeof import('./components/Upload/UploadFrag.vue')['default']
UploadImage: typeof import('./components/Upload/UploadImage.vue')['default']
Video: typeof import('./components/Video/index.vue')['default']
}
export interface ComponentCustomProperties {
vInfiniteScroll: typeof import('element-plus/es')['ElInfiniteScroll']
}
}

BIN
src/components/.DS_Store

Binary file not shown.

209
src/components/Upload/UploadFrag.vue

@ -1,209 +0,0 @@
<script lang="ts">
import { defineComponent, ref } from 'vue';
import { ElMessage } from 'element-plus';
import { Plus } from '@element-plus/icons-vue';
import { imagesAgent } from '@/utils';
import { useFileUpload } from './useFileUpload';
import type { PropType } from 'vue';
import type { UploadUserFile } from 'element-plus/es/components/upload/src/upload';
const { fileUpload } = useFileUpload();
export default defineComponent({
name: 'upload-frag',
components: { Plus },
props: {
action: {
type: String,
default: '#'
},
limit: {
type: Number,
default: 0
},
accept: {
type: String,
default: ''
},
showPer: {
type: Boolean,
default: true
},
multiple: {
type: Boolean,
default: false
},
bucketName: {
type: String,
default: 'marker'
},
// fileType: () => [],
showFileList: {
type: Boolean,
default: false
},
fileList: {
type: Array as PropType<UploadUserFile[]>,
default: () => []
},
autoUpload: {
type: Boolean,
default: true
},
fileSize: {
type: Number,
default: 20
},
isCanRefreshUpload: {
//
type: Boolean,
default: false
}
},
setup(props: any, { emit }) {
const upload = ref();
const percentage = ref(0);
// 使
const clearFiles = () => {
upload.value?.clearFiles();
};
//
const beforeUpload = (file: any) => {
if (props.accept !== '*') {
const fileSuffix = file.name.substring(file.name.lastIndexOf('.') + 1);
const whiteList = props.accept;
if (whiteList.indexOf(fileSuffix) === -1) {
ElMessage({
type: 'error',
message: `上传文件只能是${props.accept}格式`
});
return false;
}
}
const isLt2M = file.size / 1024 / 1024 <= props.fileSize;
if (!isLt2M) {
ElMessage({
type: 'error',
message: `上传文件不能大于${props.fileSize}M`
});
return false;
}
if (props.fileList.length > props.limit) {
ElMessage({
type: 'error',
message: `上传文件数不能超过${props.limit}`
});
return false;
}
return true;
};
const handleFileUpload = (fileObject: any): any => {
const params = {
bucketName: props.bucketName,
file: fileObject.file
};
fileUpload(params, (res: any) => {
percentage.value = Math.floor(res) || 0;
if (res.result) {
emit('uploadSuccess', {
...res,
bucketName: props.bucketName,
file: fileObject.file
});
}
});
};
const onRemove = (file: any) => {
emit('fileRemove', file);
};
const onPreview = (file: any) => {
emit('onPreview', file);
};
const onSuccess = (file: any) => {
// emit('uploadSuccess', file);
};
const handleChange = (file: any) => {
// emit('uploadChange', file);
};
return {
upload,
imagesAgent,
clearFiles,
percentage,
beforeUpload,
handleFileUpload,
onRemove,
onPreview,
onSuccess,
handleChange
};
}
});
</script>
<template>
<div class="image-wrapper">
<el-upload
ref="upload"
:action="action"
v-bind="$attrs"
:show-file-list="showFileList"
:limit="limit"
:accept="accept"
:file-list="fileList"
:auto-upload="autoUpload"
:before-upload="beforeUpload"
:http-request="handleFileUpload"
:on-remove="onRemove"
:on-preview="onPreview"
:on-success="onSuccess"
:on-change="handleChange"
>
<div class="progress" v-if="percentage && showPer">
<el-icon><Plus /></el-icon>
<ElProgress :stroke-width="8" :percentage="percentage" />
</div>
<slot v-else></slot>
</el-upload>
</div>
</template>
<style lang="less" scoped>
.image-wrapper {
width: 100%;
& > div {
display: flex;
flex-direction: column-reverse;
}
.sy-upload--picture-card {
width: 64px !important;
height: 64px !important;
margin-right: 8px;
line-height: 64px;
color: rgba(255, 255, 255, 0.8);
background: rgba(255, 255, 255, 0.12);
border: none;
i {
line-height: unset;
}
}
.sy-upload-list {
display: none;
margin-top: 8px;
// display: none;
.sy-upload-list__item {
width: 196px !important;
height: 96px !important;
margin: 0 8px 8px 0;
& > div {
width: 100%;
height: 100%;
}
.sy-icon-check {
position: relative;
top: -16px;
left: 0px;
}
}
}
}
</style>

420
src/components/Upload/UploadImage.vue

@ -1,420 +0,0 @@
<script lang="ts">
import { type PropType, defineComponent, ref, watch } from 'vue';
import { ElMessage, type UploadUserFile } from 'element-plus';
import { Plus, ZoomIn, Refresh, Delete } from '@element-plus/icons-vue';
import { imagesAgent } from '@/utils';
import { useFileUpload } from './useFileUpload';
const { fileUpload } = useFileUpload();
export default defineComponent({
name: 'upload-image',
components: { Plus, ZoomIn, Refresh, Delete },
props: {
action: {
type: String,
default: '#'
},
limit: {
type: Number,
default: 0
},
accept: {
type: String,
default: ''
},
showPer: {
type: Boolean,
default: true
},
multiple: {
type: Boolean,
default: false
},
bucketName: {
type: String,
default: 'marker'
},
// fileType: () => [],
showFileList: {
type: Boolean,
default: false
},
autoUpload: {
type: Boolean,
default: true
},
fileSize: {
type: Number,
default: 20
},
isRefreshUpload: {
//
type: Boolean,
default: false
},
fileList: {
type: Array as PropType<UploadUserFile[]>,
default: () => []
}
},
setup(props: any, { emit }) {
const upload = ref();
const percentage = ref(0);
const uploadImg = ref();
const previewList = ref<any[]>([]);
const loadFiles = ref<any[]>([]);
const isResetImg = ref(false);
const resetFile = ref<any>({});
watch(
() => props.fileList,
(val) => {
if (val) {
loadFiles.value = [];
previewList.value = [];
val.forEach((element: any) => {
loadFiles.value.push({
...element,
url: imagesAgent(element.url)
});
previewList.value.push(imagesAgent(element.url));
});
}
},
{
deep: true
}
);
// 使
const clearFiles = () => {
upload.value?.clearFiles();
};
//
const beforeUpload = (file: any) => {
if (props.accept !== '*') {
const fileSuffix = file.name.substring(file.name.lastIndexOf('.') + 1);
const whiteList = props.accept;
if (whiteList.indexOf(fileSuffix) === -1) {
ElMessage({
type: 'error',
message: `上传文件只能是${props.accept}格式`
});
return false;
}
}
const isLt2M = file.size / 1024 / 1024 <= props.fileSize;
if (!isLt2M) {
ElMessage({
type: 'error',
message: `上传文件不能大于${props.fileSize}M`
});
return false;
}
if (props.fileList.length > props.limit) {
ElMessage({
type: 'error',
message: `上传文件数不能超过${props.limit}`
});
return false;
}
return true;
};
const handleFileUpload = (fileObject: any): any => {
const params = {
bucketName: props.bucketName,
file: fileObject.file
};
fileUpload(params, (res: any) => {
percentage.value = Math.floor(res) || 0;
if (res.result) {
//
res.result.name = res.result.fileName;
loadFiles.value.push(res.result);
previewList.value.push(imagesAgent(res.result.url));
if (isResetImg.value && loadFiles.value && Array.isArray(loadFiles.value)) {
let indexImg = 0;
const lastImg = loadFiles.value[loadFiles.value.length - 1];
loadFiles.value.forEach((item: any, index: any) => {
if (item.ossId === resetFile.value.ossId) {
indexImg = index;
item.fileName = lastImg.fileName;
}
});
loadFiles.value = _deleteData(indexImg, loadFiles.value);
previewList.value = _deleteData(indexImg, previewList.value);
}
emit('uploadSuccess', loadFiles.value);
}
});
};
const onRemove = (file: any) => {
emit('fileRemove', file);
};
const onPreview = (file: any) => {
emit('onPreview', file);
};
const onSuccess = (file: any) => {
// emit('uploadSuccess', file);
};
const handleChange = (file: any, fileData: any) => {
// emit('uploadChange', file);
};
const _deleteData = (num: any, data: any) => {
const sliceData = data.slice(num + 1, data.length - 1) || [];
const lastData = data[data.length - 1];
// num0 num<0
return num < 0 ? data : data.slice(0, num).concat(lastData).concat(sliceData);
};
//
const handleResetUpload = (file: any) => {
resetFile.value = file;
isResetImg.value = true;
uploadImg.value?.click();
};
//
const handleRemove = (file: any) => {
loadFiles.value = loadFiles.value.filter((el) => el.ossId !== file.ossId) || [];
loadFiles.value.forEach((element) => {
previewList.value.push(imagesAgent(element.url));
});
emit('uploadSuccess', loadFiles.value);
};
//
const handlePreview = (file: any) => {
loadFiles.value.forEach((item, index) => {
if (item.ossId === file.ossId) {
const dom = document.querySelectorAll('.media-list-img');
dom?.[index].querySelector('img')?.click();
}
});
};
return {
upload,
uploadImg,
previewList,
loadFiles,
isResetImg,
resetFile,
imagesAgent,
clearFiles,
percentage,
beforeUpload,
handleFileUpload,
onRemove,
onPreview,
onSuccess,
handleChange,
handlePreview,
handleRemove,
handleResetUpload
};
}
});
</script>
<template>
<div class="img-video-content">
<el-upload
ref="upload"
:action="action"
v-bind="$attrs"
:show-file-list="showFileList"
:accept="accept"
:limit="limit"
:file-list="fileList"
:auto-upload="autoUpload"
:before-upload="beforeUpload"
:http-request="handleFileUpload"
:on-remove="onRemove"
:on-preview="onPreview"
:on-success="onSuccess"
:on-change="handleChange"
v-show="fileList.length < limit"
>
<div ref="uploadImg">
<div class="progress" v-if="percentage && showPer">
<el-icon><Plus /></el-icon>
<ElProgress :stroke-width="8" :percentage="percentage" />
</div>
<slot v-else></slot>
</div>
</el-upload>
<div class="sy-image-container" v-if="isRefreshUpload">
<div class="img-video-item" v-for="(file, index) in loadFiles" :key="file.ossId + index">
<el-image
preview-teleported
class="media-list-img"
:alt="file.name"
:src="imagesAgent(file.url)"
fit="fill"
crossOrigin="anonymous"
:preview-src-list="[imagesAgent(file.url)]"
/>
<div class="img-icon-bt">
<el-icon style="margin-right: 8px" @click.stop="handlePreview(file)">
<ZoomIn />
</el-icon>
<el-icon style="margin-right: 8px" @click.stop="handleResetUpload(file)">
<Refresh />
</el-icon>
<el-icon @click.stop="handleRemove(file)"><Delete /></el-icon>
</div>
</div>
</div>
</div>
</template>
<style lang="less" scoped>
.image-wrapper {
width: 100%;
.sy-upload--picture-card {
width: 64px !important;
height: 64px !important;
margin-right: 8px;
line-height: 64px;
color: rgba(255, 255, 255, 0.8);
background: rgba(255, 255, 255, 0.12);
border: none;
i {
line-height: unset;
}
}
.sy-upload-list {
display: none;
margin-top: 8px;
// display: none;
.sy-upload-list__item {
width: 196px !important;
height: 96px !important;
margin: 0 8px 8px 0;
& > div {
width: 100%;
height: 100%;
}
.sy-icon-check {
position: relative;
top: -16px;
left: 0px;
}
}
}
}
.upload-btn {
width: 100%;
// margin-top: 8px;
.download-btn {
width: 293px;
margin: 0px;
border: none;
background: rgba(255, 255, 255, 0.12);
font-weight: 350;
line-height: 20px;
display: flex;
align-items: center;
letter-spacing: 0em;
color: rgba(255, 255, 255, 0.55);
img {
width: 20px;
height: 20px;
margin: 0px 8px 0px 0px;
}
.text {
display: flex;
align-items: center;
justify-content: center;
}
}
.img-card-btn {
height: 72px;
width: 72px;
background: rgba(255, 255, 255, 0.12);
box-sizing: border-box;
border: 1px solid rgba(255, 255, 255, 0.12);
margin: auto;
text-align: center;
display: flex;
justify-content: center;
align-items: center;
.sy-icon {
margin: 16px auto;
text-align: center;
}
.img-card-text {
height: 72px;
width: 72px;
background: rgba(255, 255, 255, 0.12);
box-sizing: border-box;
border: 1px solid rgba(255, 255, 255, 0.12);
margin: auto;
text-align: center;
border-radius: 4px;
}
}
.text-card-btn {
border: none;
background: transparent;
right: 0;
position: absolute;
color: #409eff;
}
}
.img-video-content {
display: flex;
flex-wrap: nowrap;
float: left;
height: 72px;
.upload-btn {
width: 72px;
// margin: 0px 4px 0px 0px;
border-radius: 4px;
}
}
.sy-image-container {
display: flex;
align-items: center;
width: 212px;
height: 72px;
.img-video-item {
width: 72px;
height: 72px;
margin: auto 6px;
border-radius: 4px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
//padding: 4px 8px;
background: rgba(0, 0, 0, 0.4);
.media-list-img {
// margin: auto 12px;
width: 72px;
height: 72px;
}
&:hover {
.img-icon-bt {
display: block;
}
}
}
.img-icon-bt {
display: none;
position: absolute;
width: 72px;
height: 72px;
background: rgba(43, 44, 47, 0.5);
margin: auto;
text-align: center;
.sy-icon {
margin-top: 24px;
cursor: pointer;
}
}
}
.display-add {
.upload-btn {
display: none;
}
}
</style>

343
src/components/Upload/useFileUpload/index.ts

@ -1,343 +0,0 @@
import {
uploadNormalFile,
preChunkUpload,
chunkUpload,
chunkMerge
} from '@/api/upload';
import { useMd5 } from '../useMd5';
interface UploadParams {
bucketName: string;
file: File;
}
interface UploadResponse {
fileName: string;
ossId: string;
suffix: string;
url: string;
}
export const useFileUpload = () => {
// 已上传的切片大小
let chunkLoaded = {};
// 文件大小
let totalSize = 0;
// 已上传的文件大小
let loadedSize = 0;
// 文件上传过程的回调
// eslint-disable-next-line @typescript-eslint/ban-types
let uploadCallback: Function | undefined | null;
// 计算文件 md5 的 hook
const { computedFileMd5 } = useMd5();
// 文件上传,根据文件大小选择上传方式,大于50M的文件采用切片上传的方式,小于50M直接上传
// eslint-disable-next-line @typescript-eslint/ban-types
const fileUpload = (params: UploadParams, callback?: Function) => {
const { file } = params;
totalSize = file.size;
uploadCallback = callback;
triggerUploadCallback({
status: 'startUpload'
});
if (file.size < 50 * 1024 * 1024) {
normalUpload(params);
} else {
largeUpload(params);
}
};
// 普通文件上传
const normalUpload = (params: UploadParams) => {
uploadNormalFile(params, (e: any) => {
handleUploadProgress(e, 1);
})
.then((res: any) => {
if (res.code === 200 && res.data.url) {
triggerUploadCallback({
status: 'uploadSuccess',
result: {
fileName: res.data.fileName,
ossId: res.data.ossId,
suffix: res.data.suffix,
url: res.data.url
} satisfies UploadResponse
});
} else {
triggerUploadCallback({
status: 'uploadFail',
message: '文件上传失败',
error: res
});
}
})
.catch((e: any) => {
triggerUploadCallback({
status: 'uploadFail',
message: '文件上传失败',
error: e
});
})
.finally(() => {
resetStatus();
});
};
// 大文件上传
const largeUpload = (params: UploadParams) => {
const { file, bucketName } = params;
const fileChunks = createFileChunk(file);
const groupChunks = chunksGroup(fileChunks);
let uploadId = '';
let filepath = '';
computedFileMd5(fileChunks)
.then((md5: any) => {
return fetchUploadId({
md5,
bucket: bucketName,
filename: file.name
});
})
.then((res: any) => {
uploadId = res.uploadId;
filepath = res.filepath;
if (res.sysOss) {
return Promise.resolve({ ossObj: res.sysOss });
} else if (!res.parts) {
return fileChunkUpload({
groupChunks,
chunkSize: fileChunks.length,
bucket: bucketName,
uploadId,
filepath
});
} else if (res.parts && res.parts.length !== fileChunks.length) {
const unUploadChunks = computedUnUploadChunks(res.parts, fileChunks);
return fileChunkUpload({
groupChunks: chunksGroup(unUploadChunks),
chunkSize: unUploadChunks.filter((chunk: any) => chunk).length,
bucket: bucketName,
uploadId,
filepath
});
} else {
return Promise.resolve(res);
}
})
.then((res: any) => {
if (res.ossObj) {
return Promise.resolve({ data: res.ossObj });
} else {
return mergeChunks({
uploadId,
filepath,
bucket: bucketName,
filename: file.name
});
}
})
.then((res: any) => {
triggerUploadCallback({
status: 'uploadSuccess',
result: {
fileName: res.data.originalName,
ossId: res.data.ossId,
suffix: res.data.fileSuffix,
url: res.data.url
} satisfies UploadResponse
});
})
.catch((e: any) => {
triggerUploadCallback({
status: 'uploadFail',
message: '文件上传失败',
error: e
});
})
.finally(() => {
resetStatus();
});
};
// 获取上传id
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
const fetchUploadId = ({ md5, bucket, filename }) => {
return new Promise((resolve, reject) => {
preChunkUpload({
md5,
bucket,
filename
})
.then((res: any) => {
if (res.code === 200 && (res.data.uploadId || res.data.sysOss)) {
resolve({
uploadId: res.data.uploadId,
filepath: res.data.filepath,
parts: res.data.parts,
sysOss: res.data.sysOss
});
} else {
// eslint-disable-next-line prefer-promise-reject-errors
reject({
message: '获取文件上传id失败',
error: res
});
}
})
.catch((e: any) => {
// eslint-disable-next-line prefer-promise-reject-errors
reject({
message: '获取文件上传id失败',
error: e
});
});
});
};
// 切片上传
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
const fileChunkUpload = ({
groupChunks,
chunkSize,
bucket,
uploadId,
filepath
}: any) => {
// eslint-disable-next-line @typescript-eslint/no-misused-promises,no-async-promise-executor
return new Promise(async (resolve, reject) => {
const responseBuffer: any[] = [];
try {
for (let i = 0; i < groupChunks.length; i++) {
const groupRequest: any[] = [];
for (let j = 0; j < groupChunks[i].length; j++) {
if (groupChunks[i][j]) {
const params = {
file: groupChunks[i][j],
bucket,
uploadId,
filepath,
partNum: i * 3 + (j + 1) + ''
};
groupRequest.push(
chunkUpload(params, (e: any) => {
handleUploadProgress(e, i * 3 + (j + 1));
})
);
}
}
const res = await Promise.all(groupRequest);
responseBuffer.push(...res);
}
} catch (e) {
// eslint-disable-next-line prefer-promise-reject-errors
reject({
message: '切片上传失败',
error: e
});
} finally {
if (responseBuffer.length === chunkSize) {
resolve(responseBuffer);
}
}
});
};
// 合并切换
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
const mergeChunks = async ({ uploadId, filepath, bucket, filename }) => {
return await new Promise((resolve, reject) => {
chunkMerge({
uploadId,
filepath,
bucket,
filename
})
.then((res: any) => {
resolve(res);
})
.catch((e: any) => {
// eslint-disable-next-line prefer-promise-reject-errors
reject({
message: '文件合并失败',
error: e
});
});
});
};
// 创建文件切片
const createFileChunk = (file: File, size = 5 * 1024 * 1024) => {
const chunkList: Blob[] = [];
let currentSize = 0;
while (currentSize < file.size) {
chunkList.push(file.slice(currentSize, currentSize + size));
currentSize += size;
}
return chunkList;
};
// 切片分组
const chunksGroup = (chunks: any) => {
const groups: any[][] = [];
for (let i = 0; i < chunks.length; i += 3) {
groups.push(chunks.slice(i, i + 3));
}
return groups;
};
const computedUnUploadChunks = (parts: any, chunks: any) => {
parts.forEach((item: any) => {
if (Number(item.num)) {
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete chunks[Number(item.num) - 1];
}
});
return chunks;
};
// 切片文件上传进度处理
const handleUploadProgress = (e: ProgressEvent, chunkIndex: any) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
chunkLoaded[chunkIndex] = e.loaded;
computedUploadProgress();
};
// 计算文件上传总进度
const computedUploadProgress = () => {
let tmpSize = 0;
for (const key in chunkLoaded) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
tmpSize += chunkLoaded[key];
}
loadedSize = tmpSize;
const uploadResult = (loadedSize / totalSize) * 100;
const percentage = (uploadResult > 100 ? 100 : uploadResult).toFixed(2);
triggerUploadCallback({
status: 'uploading',
percentage: Number(percentage)
});
};
// 重置上传文件涉及到的变量状态
const resetStatus = () => {
chunkLoaded = {};
totalSize = 0;
loadedSize = 0;
uploadCallback = null;
};
const triggerUploadCallback = (options: any) => {
if (uploadCallback instanceof Function) {
uploadCallback(options);
}
};
return {
fileUpload
};
};

57
src/components/Upload/useMd5/index.ts

@ -1,57 +0,0 @@
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
import SparkMD5 from 'spark-md5';
export const useMd5 = () => {
const computedFileMd5 = (file: File | Blob[]) => {
// eslint-disable-next-line @typescript-eslint/no-misused-promises,no-async-promise-executor
return new Promise(async (resolve, reject) => {
try {
let md5 = '';
const spark = new SparkMD5.ArrayBuffer();
const fileReader = new FileReader();
if (Array.isArray(file)) {
for (let i = 0; i < file.length; i++) {
const res: any = await readFileAsArrayBuffer(fileReader, file[i]);
spark.append(res?.target?.result);
}
md5 = spark.end();
} else {
const res: any = await readFileAsBinaryString(fileReader, file);
md5 = SparkMD5.hashBinary(res?.target?.result);
}
resolve(md5);
} catch (e) {
// eslint-disable-next-line prefer-promise-reject-errors
reject({
message: '计算文件md5失败',
error: e
});
}
});
};
// 读取文件并转为ArrayBuffer
const readFileAsArrayBuffer = (fileReader: any, blob: any) => {
return new Promise((resolve) => {
fileReader.readAsArrayBuffer(blob);
fileReader.onload = function (e: any) {
resolve(e);
};
});
};
// 读取文件并转为BinaryString
const readFileAsBinaryString = (fileReader: any, file: any) => {
return new Promise((resolve) => {
fileReader.readAsBinaryString(file);
fileReader.onload = function (e: any) {
resolve(e);
};
});
};
return {
computedFileMd5
};
};

289
src/components/Video/index.vue

@ -1,289 +0,0 @@
<script lang="ts">
import { defineComponent, ref, reactive, nextTick, watch } from 'vue';
import { getProxyPath } from '@/utils/index';
import { getVideoStreamInfo } from '@/api/video';
import { ElMessage } from 'element-plus';
import SyPlayer from './lib/syPlayer.js';
import createGuid from '@/utils/uuid';
import { isEqual } from 'lodash-es';
export default defineComponent({
name: 'sy-video',
components: {},
props: {
playInfo: {
type: Object,
default: () => {}
},
autoPlay: {
type: Boolean,
default: false
}
},
// // 线
// {
// "videoType": "netvideo",
// "videoName": "#",
// "isStream": false,
// "videoUrl": "http://172.16.32.102:9000/camera/20230322/8209ba0965174072b3fc97d862548d15.mp4",
// "isFull": false,
// "isVisible": false,
// "deviceIndex": "641a6e91e4b06c8e49163229",
// "cameraPosition": {
// "lng": 0,
// "lat": 0,
// "alt": 0
// }
// }
// //
// {
// "videoType": "surveillanceVideo",
// "videoName": "IP32",
// "isStream": true,
// "videoUrl": "",
// "isFull": false,
// "isVisible": false,
// "deviceIndex": "64dcb7dfe4b0956ea8c53590",
// "cameraPosition": {
// "lng": 0,
// "lat": 0,
// "alt": 0
// },
// "platform": "datametatech"
// }
// //
// {
// "videoType": "localvideo",
// "videoName": "",
// "isStream": false,
// "videoUrl": "http://minio:9000/images/20240110/ab983754109f4df09831bc1538c6870e.mp4",
// "isFull": false,
// "isVisible": false,
// "deviceIndex": "1744914088635305985",
// "platform": ""
// }
setup(props: any) {
const isPlay = ref(false);
const videoRef = ref();
let videoPlayer = ref();
//
const playItem = () => {
if (!isPlay.value) {
isPlay.value = !isPlay.value;
play();
}
};
//
const createVideo = (url: any, isStream: any) => {
if (!videoRef.value) return;
videoRef.value.style.width = '100%';
if (isStream) {
const deviceIndex = props.playInfo.deviceIndex;
if (!deviceIndex) return;
if (!url) {
ElMessage({
type: 'error',
message: `获取视频流失败`
});
isPlay.value = false;
return;
}
if (videoPlayer.value) {
videoPlayer.value?.close();
videoPlayer.value = null;
}
videoPlayer.value = new SyPlayer(videoRef.value, url);
nextTick(() => {
videoPlayer.value?.open();
});
return;
}
};
const videoPlayInfo = reactive({
id: '',
videoType: '', // netvideo-线surveillanceVideo-, localvideo-
videoName: '',
isStream: false,
videoUrl: '',
isFull: false,
isVisible: false,
deviceIndex: '',
cameraPosition: {
lng: 0,
lat: 0,
alt: 0
},
deviceIPCAddress: null,
platform: ''
});
const play = async () => {
const {
videoType,
videoName,
isStream,
videoUrl,
isFull,
isVisible,
deviceIndex,
cameraPosition,
deviceIPCAddress,
platform,
id
} = props.playInfo;
videoPlayInfo.videoType = videoType;
videoPlayInfo.videoName = videoName;
videoPlayInfo.isFull = isFull;
videoPlayInfo.isVisible = isVisible;
videoPlayInfo.deviceIndex = deviceIndex;
videoPlayInfo.cameraPosition = cameraPosition;
videoPlayInfo.deviceIPCAddress = deviceIPCAddress;
videoPlayInfo.platform = platform;
videoPlayInfo.id = id ?? createGuid();
if (['netvideo', 'localvideo'].includes(videoType)) {
videoPlayInfo.isStream = false;
videoPlayInfo.videoUrl =
videoUrl && videoUrl.includes('//minio:') && !videoUrl.includes('/objs/')
? getProxyPath(videoUrl, 'objs')
: videoUrl;
createVideo(videoPlayInfo.videoUrl, isStream);
isPlay.value = true;
} else {
const videoStream = await getStreamVideo(deviceIndex);
if (videoStream) {
videoPlayInfo.videoUrl = videoStream.videoUrl;
videoPlayInfo.isStream = videoStream.isStream;
videoPlayInfo.platform = videoStream.platform;
isPlay.value = true;
}
}
};
//
const getStreamVideo = async (id: any, _isPlay = true) => {
try {
const res = await getVideoStreamInfo({
deviceIndex: id
});
if (res?.url) {
const { url, platform } = res;
let videoPlayInfo = {
isStream: true,
videoUrl: url,
platform: platform
};
_isPlay && createVideo(url, true);
return videoPlayInfo;
} else {
ElMessage({
type: 'error',
message: res.msg
});
isPlay.value = false;
return false;
}
} catch (error) {
isPlay.value = false;
return false;
}
};
const handleCanPlay = () => {
if (props.autoPlay && videoRef.value) {
videoRef.value.play?.();
nextTick(() => {
isPlay.value = !videoRef.value.paused;
});
}
};
nextTick(() => {
if (
!props.autoPlay &&
((!props.playInfo.isStream && props.playInfo.videoUrl) ||
(props.playInfo.isStream && props.playInfo.deviceIndex))
) {
play();
}
});
watch(
() => props.playInfo,
(val, old) => {
if (!isEqual(val, old)) {
nextTick(() => {
// if (props.autoPlay) {
play();
// }
});
}
},
{ deep: true }
);
return {
isPlay,
videoPlayInfo,
videoRef,
createVideo,
playItem,
play,
handleCanPlay
};
}
});
</script>
<template>
<div class="video-wrapper">
<video
:id="videoPlayInfo.id"
v-if="!playInfo.isStream"
ref="videoRef"
:src="videoPlayInfo.videoUrl"
width="100%"
controls
:autoplay="autoPlay"
loop
muted
@canplay="handleCanPlay"
:poster="playInfo.imgUrl"
>
<source type="video/mp4" />
</video>
<video
:id="videoPlayInfo.id"
v-else
ref="videoRef"
width="100%"
controls
autoplay
muted
@canplay="handleCanPlay"
></video>
<div @click="playItem" v-if="!isPlay" class="play-icon">
<iconpark-icon name="play" fill="#fff" size="22"></iconpark-icon>
</div>
</div>
</template>
<style scoped lang="less">
.video-wrapper {
width: 100%;
height: 100%;
position: relative;
video {
width: 100%;
height: 100%;
// width: 528px;
// height: 297px;
border-radius: 8px;
}
.play-icon {
position: absolute;
bottom: 37px;
left: 13px;
cursor: pointer;
opacity: 0.7;
&:hover {
opacity: 1;
}
}
}
</style>

16
src/components/Video/lib/global.d.ts

@ -1,16 +0,0 @@
declare module '*/mp4box.all.min.js' {
class MP4Box {
onReady: Function
appendBuffer: Function
}
export default MP4Box
}
declare module '*/syPlayer.js' {
class SyPlayer {
open: Function
close: Function
dispose: Function
}
export default SyPlayer(element, url)
}

5194
src/components/Video/lib/mp4box.all.min.js

File diff suppressed because it is too large

125
src/components/Video/lib/syPlayer.js

@ -1,125 +0,0 @@
'use strict';
import MP4Box from './mp4box.all.min';
function syPlayer(videoEl, wsUrl) {
this.videoEl = videoEl;
this.wsUrl = wsUrl;
this.ws = null;
this.frameQueue = [];
}
syPlayer.prototype.open = function () {
let sourcebuffer = null;
this.ws = new WebSocket(this.wsUrl);
this.ws.binaryType = 'arraybuffer';
let firstMessage = true;
let demux_moov = function (info) {
let codecs = [];
for (let i = 0; i < info.tracks.length; i++) {
codecs.push(info.tracks[i].codec);
}
let video = this.videoEl;
let mediasource = new MediaSource();
video.src = URL.createObjectURL(mediasource);
// 设置播放速度,如果是实时流,可以设置为1.1,降低延迟,如果是视频文件,则设置为1.0
video.playbackRate = 1.1;
let pre_pos = 0;
mediasource.onsourceopen = function () {
sourcebuffer = mediasource.addSourceBuffer(
'video/mp4; codecs="' + codecs.join(', ') + '"'
);
sourcebuffer.onupdate = function () {
let pos = video.currentTime;
if (video.buffered.length > 0) {
let start = video.buffered.start(video.buffered.length - 1);
let end = video.buffered.end(video.buffered.length - 1);
if (pos < start) {
video.currentTime = start;
}
if (pos > end) {
// console.warn('chase frame pos=' + pos + ',start=' + start + ',end=' + end + ',pre_pose=' + pre_pos);
video.currentTime = end;
}
if (pos - pre_pos != 0 && end - pos > 3) {
video.currentTime = end;
}
for (let i = 0; i < video.buffered.length - 1; i++) {
let prestart = video.buffered.start(i);
let preend = video.buffered.end(i);
if (!sourcebuffer.updating) {
sourcebuffer.remove(prestart, preend);
}
}
if (pos - start > 10 && !sourcebuffer.updating) {
// console.warn('remove start pos=' + pos + ',start=' + start + ',end=' + end);
sourcebuffer.remove(0, pos - 3);
}
if (end - pos > 10 && !sourcebuffer.updating) {
// console.warn('remove end pos=' + pos + ',start=' + start + ',end=' + end);
sourcebuffer.remove(0, end - 3);
}
// 如果是实时流,则根据缓冲区的大小,动态调整播放速度
if (this.is_stream) {
if (end - pos > 0.5) {
video.playbackRate = 1.1;
} else {
video.playbackRate = 1.0;
}
}
}
pre_pos = pos;
};
sourcebuffer.onupdateend = pushFrame;
};
}.bind(this);
let pushFrame = function () {
if (this.frameQueue.length == 0) {
return;
}
if (!sourcebuffer || sourcebuffer.updating) {
return;
}
let frame = this.frameQueue.shift();
try {
// 视频流有可能出现错误,这里可以避免追入,然后引发cesium奔溃
sourcebuffer.appendBuffer(frame);
} catch (error) {}
}.bind(this);
this.ws.onmessage = function (e) {
if (firstMessage) {
firstMessage = false;
let moov = e.data;
let mp4Box = new MP4Box();
mp4Box.onReady = demux_moov;
moov.fileStart = 0;
mp4Box.appendBuffer(moov);
}
this.frameQueue.push(e.data);
pushFrame();
}.bind(this);
this.ws.onclose = function (ev) {
console.log('🚀 ws: close~', ev);
};
};
syPlayer.prototype.close = function () {
this.ws && this.ws.close();
};
syPlayer.prototype.dispose = function () {
this.ws && this.ws.close();
};
export default syPlayer;

2
src/hooks/web/useMap.tsx

@ -6,7 +6,7 @@ import { useViewer } from "@/hooks/web/useViewer";
import createGuid from "@/utils/uuid";
import router from "@/router";
import { useUtils } from "@/hooks/web/useUtils";
SyCim.config.baseUrl = "./resources/";
// 弹框图层
let divLayer: SyCim.HtmlLayer;

244
src/hooks/web/useProject.tsx

@ -1,8 +1,8 @@
import { reactive } from 'vue';
import * as SyCim from 'sy-cesium-sdk';
import { LayerTypeEnum } from '@/enums/projectEnum';
import { queryLayersByPos } from "@/utils/mapUtils";
import axios from 'axios';
import { reactive } from "vue";
import * as SyCim from "sy-cesium-sdk";
import { LayerTypeEnum } from "@/enums/projectEnum";
// import { queryLayersByPos } from "@/utils/mapUtils";
import axios from "axios";
import { throttle } from "lodash-es";
const clustering = reactive({
enabled: true,
@ -13,37 +13,37 @@ const clustering = reactive({
const createXyzLayer = (data: any) =>
new SyCim.XyzLayer(data.id, {
...data,
maximumLevel: data.maximumLevel || 22
maximumLevel: data.maximumLevel || 22,
});
const layerActions: Record<string, any> = {
'011100': (data: any) =>
"011100": (data: any) =>
new SyCim.BaiduLayer(data.id, {
...data,
style: data.layerTable,
crs: 'WGS84'
crs: "WGS84",
}),
'012000': (data: any) => new SyCim.PrimitiveLayer(data.id),
'013000': (data: any) => new SyCim.TilesetLayer(data.id),
'014000': createXyzLayer,
'021102': (data: any) =>
"012000": (data: any) => new SyCim.PrimitiveLayer(data.id),
"013000": (data: any) => new SyCim.TilesetLayer(data.id),
"014000": createXyzLayer,
"021102": (data: any) =>
new SyCim.ArcgisDynamicLayer(data.id, {
url: data.url
url: data.url,
}),
'030200': (data: any) =>
"030200": (data: any) =>
new SyCim.WmsLayer(data.id, {
...data,
url: data.url,
layer: data.layerTable,
parameters: {
version: '1.3.0'
}
version: "1.3.0",
},
}),
'030300': async (data: any) => {
"030300": async (data: any) => {
const options: any = {
version: '2.0.0',
version: "2.0.0",
TYPENAMES: data.layerTable,
outputFormat: 'json'
outputFormat: "json",
};
// TODO: 后续获取真实数据
// if (data.relationStyleId) {
@ -52,15 +52,15 @@ const layerActions: Record<string, any> = {
// }
return new SyCim.WfsLayer(data.id, data.url, data.layerTable, options);
},
'030100': (data: any) =>
"030100": (data: any) =>
new SyCim.WmtsLayer(data.id, {
...data,
url: data.url,
layer: data.layerTable,
tileMatrixSetID: data.tileMatrixSet,
style: data.style || ''
style: data.style || "",
}),
'030400': async (data: any) => {
"030400": async (data: any) => {
const options: any = {};
// TODO: 后续获取真实数据
// if (data.relationStyleId) {
@ -69,14 +69,14 @@ const layerActions: Record<string, any> = {
// }
return new SyCim.GeoJsonLayer(data.id, data.url, options);
},
'040003': (data: any) =>
"040003": (data: any) =>
new SyCim.WfsLayer(data.id, data.url, data.layerTable, {
cql_filter: data.filter,
propertyName: '*'
propertyName: "*",
}),
'050100': (data: any) => {
"050100": (data: any) => {
let url = `${data.proxyUrl}${data.url}`;
if (!url.includes('?tk=') && data.serviceToken) {
if (!url.includes("?tk=") && data.serviceToken) {
url = `${url}?tk=${data.serviceToken}`;
}
return new SyCim.TdtWmtsLayer(data.id, {
@ -85,16 +85,16 @@ const layerActions: Record<string, any> = {
layer: data.layerTable,
tileMatrixSetID: data.tileMatrixSet,
spatialReference: {
wkid: data.epsg
}
wkid: data.epsg,
},
});
},
'060100': createXyzLayer,
'070100': (data: any) =>
"060100": createXyzLayer,
"070100": (data: any) =>
new SyCim.BingMapLayer(data.id, {
...data
...data,
}),
'081100': (data: any) =>
"081100": (data: any) =>
// if (data.serviceToken) {
// new SyCim.SuperMapRestLayer(data.id, { url: data.url, k: data.serviceToken });
// } else {
@ -102,10 +102,13 @@ const layerActions: Record<string, any> = {
// ...data
// });
// }
new SyCim.SuperMapRestLayer(data.id, { url: data.url, k: data.serviceToken })
new SyCim.SuperMapRestLayer(data.id, {
url: data.url,
k: data.serviceToken,
}),
};
export const getFeatures: any = (data: any, filter: '1=1') => {
export const getFeatures: any = (data: any, filter: "1=1") => {
return new Promise(async (resolve, reject) => {
const { url, serviceToken } = data;
let newUrl = decodeURI(url).split("maps/")[0].replace("map-", "data-");
@ -116,31 +119,27 @@ export const getFeatures: any = (data: any, filter: '1=1') => {
const datasourceName = `${l?.[1]}:${l?.[0]}`;
let queryUrl = `${newUrl}data/featureResults.geojson?returnContent=true`;
let params: any = {
getFeatureMode: 'SQL',
getFeatureMode: "SQL",
datasetNames: [datasourceName],
maxFeatures: 100000000,
queryParameter: {
attributeFilter: filter
}
attributeFilter: filter,
},
};
if (serviceToken) {
params.k = serviceToken;
}
const result = await axios.post(
queryUrl,
JSON.stringify(params),
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
}
}
);
const result = await axios.post(queryUrl, JSON.stringify(params), {
headers: {
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
},
});
if (result?.data?.features?.length > 0) {
resolve(result.data);
} else {
resolve({
type: "FeatureCollection",
features: []
features: [],
});
}
});
@ -148,7 +147,7 @@ export const getFeatures: any = (data: any, filter: '1=1') => {
const defaultZoomToLayer = (data: any) => {
const { mapParam } = data;
if (!mapParam?.tileCenter) return;
const p = mapParam.tileCenter.split(',');
const p = mapParam.tileCenter.split(",");
window.viewer.flyToPosition(new SyCim.Position(+p[0], +p[1], 500, 0, -90, 0));
};
const zoomToLayerByType = (data: any) => {
@ -192,75 +191,86 @@ function zoomToS3MLayer(data: any) {
orientation: {
heading: cameraPosition.heading,
pitch: tilt,
roll: 0
}
roll: 0,
},
});
});
});
} else {
alert('请先加载s3m图层');
alert("请先加载s3m图层");
}
}
const zoomToLayerActions: Record<string, any> = {
'011100': zoomToLayerByType,
'012000': (data: any) => {
"011100": zoomToLayerByType,
"012000": (data: any) => {
// modal
const { modelInitPosition } = data;
if (!modelInitPosition) return;
const p = JSON.parse(modelInitPosition);
window.viewer.flyToPosition(new SyCim.Position(p.lng, p.lat, p.alt + 500, 0, -90, 0));
window.viewer.flyToPosition(
new SyCim.Position(p.lng, p.lat, p.alt + 500, 0, -90, 0)
);
},
'013000': zoomToLayerByType,
'014000': zoomToLayerByType,
'021102': zoomToLayerByType,
'030200': zoomToLayerByType,
'030300': zoomToLayerByType,
'030100': zoomToLayerByType,
'030400': zoomToLayerByType,
'040003': zoomToLayerByType,
'050100': zoomToLayerByType,
'060100': zoomToLayerByType,
'070100': zoomToLayerByType,
'080100': zoomToS3MLayer,
'081100': zoomToLayerByType
"013000": zoomToLayerByType,
"014000": zoomToLayerByType,
"021102": zoomToLayerByType,
"030200": zoomToLayerByType,
"030300": zoomToLayerByType,
"030100": zoomToLayerByType,
"030400": zoomToLayerByType,
"040003": zoomToLayerByType,
"050100": zoomToLayerByType,
"060100": zoomToLayerByType,
"070100": zoomToLayerByType,
"080100": zoomToS3MLayer,
"081100": zoomToLayerByType,
};
const baseLayerActions: Record<string, any> = {
// 百度
'011100': async (data: any) =>
await SyCim.ImageryLayerFactory.createImageryLayer(SyCim.ImageryType.BAIDU, {
...data,
style: data.layerTable,
crs: 'WGS84'
}),
"011100": async (data: any) =>
await SyCim.ImageryLayerFactory.createImageryLayer(
SyCim.ImageryType.BAIDU,
{
...data,
style: data.layerTable,
crs: "WGS84",
}
),
// 天地图
'050100': async (data: any) => {
"050100": async (data: any) => {
let url = `${data.proxyUrl}${data.url}`;
if (!url.includes('?tk=') && data.serviceToken) {
if (!url.includes("?tk=") && data.serviceToken) {
url = `${url}?tk=${data.serviceToken}`;
}
return await SyCim.ImageryLayerFactory.createImageryLayer(SyCim.ImageryType.TDTWMTS, {
...data,
url,
layer: data.layerTable,
tileMatrixSetID: data.tileMatrixSet,
spatialReference: {
wkid: data.epsg
return await SyCim.ImageryLayerFactory.createImageryLayer(
SyCim.ImageryType.TDTWMTS,
{
...data,
url,
layer: data.layerTable,
tileMatrixSetID: data.tileMatrixSet,
spatialReference: {
wkid: data.epsg,
},
}
});
);
},
// 必应影像
'070100': async (data: any) =>
await SyCim.ImageryLayerFactory.createImageryLayer(SyCim.ImageryType.BINGMAP, {
...data
}),
"070100": async (data: any) =>
await SyCim.ImageryLayerFactory.createImageryLayer(
SyCim.ImageryType.BINGMAP,
{
...data,
}
),
// 必应地图离线瓦片
'060100': async (data: any) =>
"060100": async (data: any) =>
await SyCim.ImageryLayerFactory.createImageryLayer(SyCim.ImageryType.XYZ, {
...data,
maximumLevel: data.maximumLevel || 22
})
maximumLevel: data.maximumLevel || 22,
}),
};
export function useLayer() {
@ -268,7 +278,7 @@ export function useLayer() {
const { layerType, url, id, isNeedCluster } = layerData;
let action = null;
// 超图 S3M图层
if (layerType === '080100') {
if (layerType === "080100") {
if (supermapLayer[id]) {
supermapLayer[id].show = true;
} else {
@ -278,7 +288,7 @@ export function useLayer() {
requestUrl = url + "?k=" + layerData.serviceToken;
}
const layerPromise = scene.open(requestUrl, {
autoSetView: false //不自动定位
autoSetView: false, //不自动定位
});
layerPromise.then((layer: any) => {
supermapLayer[id] = layer?.[0];
@ -288,10 +298,10 @@ export function useLayer() {
}
if (!layerType || !(action = layerActions[layerType as string])) return;
let layer: any = await action(layerData);
if (isNeedCluster === '1' && layerData.nameCn === "水库点") {
if (isNeedCluster === "1" && layerData.nameCn === "水库点") {
let baseUrl = import.meta.env.VITE_BASE_URL;
const data = await getFeatures(layerData);
if (data?.features?.[0]?.geometry?.type === 'Point') {
if (data?.features?.[0]?.geometry?.type === "Point") {
let layerConfig = {};
switch (layerData.nameCn) {
case "水库点":
@ -332,16 +342,19 @@ export function useLayer() {
}
}
layer && window.viewer.addLayer(layer);
if (['013000'].includes(layerType)) {
if (["013000"].includes(layerType)) {
// 3d tiles
const tileset = new SyCim.Tileset(url);
tileset.id = layerData.id;
layer.addGraphic(tileset);
} else if (['012000'].includes(layerType)) {
} else if (["012000"].includes(layerType)) {
// modal
if (!layerData.modelInitPosition) return;
const p = JSON.parse(layerData.modelInitPosition);
const model = new SyCim.ModelPrimitive(new SyCim.Position(p.lng, p.lat, p.alt, p.heading, p.pitch, p.roll), url);
const model = new SyCim.ModelPrimitive(
new SyCim.Position(p.lng, p.lat, p.alt, p.heading, p.pitch, p.roll),
url
);
model.id = layerData.id;
layer.addGraphic(model);
}
@ -351,14 +364,14 @@ export function useLayer() {
if (!layerType) return;
const type: any = LayerTypeEnum[layerType as never];
if (layerType === '080100') {
if (layerType === "080100") {
if (supermapLayer[id]) {
supermapLayer[id].show = false;
}
} else {
window.viewer.removeLayer({
id,
type: SyCim.LayerType[type]
type: SyCim.LayerType[type],
} as any);
}
};
@ -370,48 +383,55 @@ export function useLayer() {
console.log(e);
}, 500);
const addViewerMouseClick = () => {
window.viewer?.on(SyCim.MouseEventType.MOUSE_MOVE, viewerClick);
window.viewer?.on(SyCim.MouseEventType.MOUSE_MOVE, viewerClick, viewer);
};
const removeViewerMouseClick = () => {
window.viewer?.off(SyCim.MouseEventType.MOUSE_MOVE, viewerClick);
window.viewer?.off(SyCim.MouseEventType.MOUSE_MOVE, viewerClick, viewer);
};
return {
addLayer,
removeLayer,
zoomToLayer,
addViewerMouseClick,
removeViewerMouseClick
removeViewerMouseClick,
};
}
export function useBaseLayer() {
const changeBaseLayer = (layerData: any) => {
if (!layerData.layerType || !baseLayerActions[layerData.layerType as string]) {
if (
!layerData.layerType ||
!baseLayerActions[layerData.layerType as string]
) {
return;
}
const baseLayer = baseLayerActions[layerData.layerType as string](layerData);
const baseLayer =
baseLayerActions[layerData.layerType as string](layerData);
window.viewer.addBaseLayer(baseLayer, {
id: layerData.id,
name: layerData.nameCn,
iconUrl: layerData.picture || '/WGS84.png'
iconUrl: layerData.picture || "/WGS84.png",
});
window.viewer.changeBaseLayerById(layerData.id);
};
return {
changeBaseLayer
changeBaseLayer,
};
}
export function useTerrain() {
const changeTerrain = (terrainData: any) => {
if (terrainData.layerType === '014000') {
if (terrainData.layerType === "014000") {
// 地形
const url = `${terrainData.proxyUrl}${terrainData.url}`;
const terrain = SyCim.TerrainFactory.createTerrain(SyCim.TerrainType.XYZ, {
...terrainData,
url
});
const terrain = SyCim.TerrainFactory.createTerrain(
SyCim.TerrainType.XYZ,
{
...terrainData,
url,
}
);
window.viewer.setTerrain(terrain);
}
};
@ -420,6 +440,6 @@ export function useTerrain() {
};
return {
changeTerrain,
removeTerrain
removeTerrain,
};
}

60
src/utils/mapUtils.ts

@ -1,17 +1,23 @@
import { getLayerLegend } from "@/api/map";
import axios from "axios";
const identifyLayerActions: any = {
"021102": async (params: any) => {
return new Promise(async (resolve, reject) => {
const { url, input_geometry, geometry_type, layers = "all", returnGeometry = false, tolerance = 1 } = params;
const {
url,
input_geometry,
geometry_type,
layers = "all",
returnGeometry = false,
tolerance = 1,
} = params;
const result = await axios.get(`${url}.json`);
if (result.status === 200 && result.data) {
const { xmin, ymin, xmax, ymax } = result.data.fullExtent;
const mapExtent = `${xmin},${ymin},${xmax},${ymax}`;
const data = await axios.get(
`${url}/identify?geometry=${input_geometry}&geometryType=${geometry_type}&layers=${layers}&returnGeometry=${returnGeometry}&mapExtent=${mapExtent}&imageDisplay=600,550,96&tolerance=${tolerance}&out_fields=*&f=json`,
`${url}/identify?geometry=${input_geometry}&geometryType=${geometry_type}&layers=${layers}&returnGeometry=${returnGeometry}&mapExtent=${mapExtent}&imageDisplay=600,550,96&tolerance=${tolerance}&out_fields=*&f=json`
);
if (data.status === 200 && data.data) {
resolve(data.data);
@ -26,7 +32,15 @@ const identifyLayerActions: any = {
},
"081100": async (params: any) => {
return new Promise(async (resolve, reject) => {
let { queryMode, bufferDistance, geometry, url, layerTable, layerName, k } = params;
let {
queryMode,
bufferDistance,
geometry,
url,
layerTable,
layerName,
k,
} = params;
// const queryUrl = url.split('maps/')[0].replace('map-', 'data-');
let queryUrl = "";
if (layerName.split(";").length == 1 && layerName.includes(":")) {
@ -52,7 +66,7 @@ const identifyLayerActions: any = {
headers: {
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
},
},
}
);
resolve(result);
});
@ -99,11 +113,11 @@ export const queryLayersByPos = (position: any, layerList = [], cb: any) => {
],
type: "POINT",
};
let type = "";
let isSite = false;
// let type = "";
// let isSite = false;
let layerTable = "";
if (layer.extendData) {
const extendData = JSON.parse(layer.extendData);
// const extendData = JSON.parse(layer.extendData);
}
const result = await action({
queryMode,
@ -116,14 +130,21 @@ export const queryLayersByPos = (position: any, layerList = [], cb: any) => {
k: layer.serviceToken,
});
let resData;
if ([200, 201].includes(result.status) && result?.data?.datasetInfos?.length > 0) {
if (
[200, 201].includes(result.status) &&
result?.data?.datasetInfos?.length > 0
) {
console.log("处理业务数据 >>>>>> ", result?.data);
const fieldInfos = result.data.datasetInfos?.[0]?.fieldInfos;
console.log("fieldInfos >>>>> ", fieldInfos);
const currentFeature = result.data.features?.[0];
console.log("currentFeature >>>>> ", currentFeature);
const nameIndex = fieldInfos.findIndex((f: any) => ["res_name", "dike_name", "waga_name"].includes(f.name));
const codeIndex = fieldInfos.findIndex((f: any) => ["res_code", "dike_code", "waga_code"].includes(f.name));
const nameIndex = fieldInfos.findIndex((f: any) =>
["res_name", "dike_name", "waga_name"].includes(f.name)
);
const codeIndex = fieldInfos.findIndex((f: any) =>
["res_code", "dike_code", "waga_code"].includes(f.name)
);
// if (layer?.fields?.length > 0) {
// const fieldIndex = fieldInfos.findIndex((f) => f.name === layer?.fields[0]?.field);
// code = currentFeature?.fieldValues[fieldIndex];
@ -164,7 +185,9 @@ export const getSelectedLayersLegend = (selectedLayersList: any) => {
// 超图的arcgisrest服务 - 021102
let currentLayer = [];
if (mapName) {
currentLayer = result.data.layers.filter((item: any) => item.layerName === mapName);
currentLayer = result.data.layers.filter(
(item: any) => item.layerName === mapName
);
}
const legends: any = [];
currentLayer.forEach((item: any) => {
@ -198,9 +221,13 @@ export const getSelectedLayersLegend = (selectedLayersList: any) => {
res?.data?.viewBounds?.top;
let result = null;
if (layer.serviceToken) {
result = await getLayerLegend(`${layer.url}/legend.json?bbox=${bbox}&k=${layer.serviceToken}`);
result = await getLayerLegend(
`${layer.url}/legend.json?bbox=${bbox}&k=${layer.serviceToken}`
);
} else {
result = await getLayerLegend(`${layer.url}/legend.json?bbox=${bbox}`);
result = await getLayerLegend(
`${layer.url}/legend.json?bbox=${bbox}`
);
}
console.log(result);
if (result.status === 200 && result.data) {
@ -217,7 +244,10 @@ export const getSelectedLayersLegend = (selectedLayersList: any) => {
// }
if (result.data.layerLegends && result.data.layerLegends.length > 0) {
let layerLegends = result.data.layerLegends[0];
if (layerLegends.subLayerLegends && layerLegends.subLayerLegends.length > 0) {
if (
layerLegends.subLayerLegends &&
layerLegends.subLayerLegends.length > 0
) {
let subLayerLegends = layerLegends.subLayerLegends;
let legends = [];
for (let item of subLayerLegends) {

13
src/views/BottomPanel/index.vue

@ -1,10 +1,7 @@
<template>
<div class="bottom-panel">
<!-- <div class="tool-item active">水库</div>
<div class="tool-item">水闸</div>
<div class="tool-item">堤防</div> -->
<div
v-for="(item, index) in layerData"
v-for="item in layerData"
:class="['tool-item', { active: selectedKeys.includes(item.id) }]"
@click="handleChangeLayer(item)"
>
@ -21,10 +18,10 @@ const projectStore = useProjectStore();
defineOptions({
name: "bottom-panel",
});
const activeLayers = ref([]);
const activeLayers = ref<any[]>([]);
const listName = ref(["水库点", "堤防", "水闸"]);
const layerData: any = computed(() => {
const list = [];
const list: any[] = [];
listName.value.forEach((item, index) => {
const layer = projectStore.getLayerByName(item);
if (layer) {
@ -37,10 +34,10 @@ const selectedKeys = computed(() => projectStore.selectedLayerKeys);
function handleChangeLayer(item: any) {
if (activeLayers.value.includes(item.id)) {
activeLayers.value = activeLayers.value.filter((id: any) => id !== item.id);
projectStore.setLayerVisibility(item.id, false);
// projectStore.setLayerVisibility(item.id, false);
} else {
activeLayers.value.push(item.id);
projectStore.setLayerVisibility(item.id, true);
// projectStore.setLayerVisibility(item.id, true);
}
}
</script>

31
src/views/Main/Map/components/LayerTree.vue

@ -1,5 +1,9 @@
<template>
<SyDialog dialog-class="layer-tree-dialog" :dialog-show="showDialog" :style="style">
<SyDialog
dialog-class="layer-tree-dialog"
:dialog-show="showDialog"
:style="style"
>
<template #body>
<Tree
ref="layerTreeRef"
@ -18,7 +22,11 @@
<template v-slot:default="{ data, onMouseoverTip }">
<div class="item-name">
<div class="icon">
<img v-if="data && !data.iconCls" src="@/assets/map/icon-layer.png" alt="" />
<img
v-if="data && !data.iconCls"
src="@/assets/map/icon-layer.png"
alt=""
/>
<img v-else src="@/assets/map/icon-folder.png" alt="" />
</div>
<span class="name" @mouseover="onMouseoverTip($event)">
@ -26,8 +34,12 @@
</span>
</div>
</template>
<template v-slot:right="{ node, data }">
<span class="btn-item" v-if="data && !data.iconCls" @click.stop="flyTo(data)">
<template v-slot:right="{ data }">
<span
class="btn-item"
v-if="data && !data.iconCls"
@click.stop="flyTo(data)"
>
<iconpark-icon size="16" name="location"></iconpark-icon>
</span>
</template>
@ -37,7 +49,14 @@
</template>
<script setup lang="ts">
import { ref, watch, computed, nextTick, onMounted, onBeforeUnmount } from "vue";
import {
ref,
watch,
computed,
nextTick,
onMounted,
onBeforeUnmount,
} from "vue";
import SyDialog from "@/components/Dialog/index.vue";
import { useProjectStore } from "@/store/modules/project";
import { useLayer } from "@/hooks/web/useProject";
@ -114,7 +133,7 @@ watch(
}, 500);
});
}
},
}
);
onMounted(() => {

139
vite.config.ts

@ -1,132 +1,131 @@
import { defineConfig, loadEnv } from 'vite';
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';
import path from 'path';
import { viteStaticCopy } from 'vite-plugin-static-copy';
import AutoImport from 'unplugin-auto-import/vite';
import Components from 'unplugin-vue-components/vite';
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers';
import gzipPlugin from 'rollup-plugin-gzip';
import { defineConfig, loadEnv } from "vite";
import vue from "@vitejs/plugin-vue";
import vueJsx from "@vitejs/plugin-vue-jsx";
import path from "path";
import { viteStaticCopy } from "vite-plugin-static-copy";
import AutoImport from "unplugin-auto-import/vite";
import Components from "unplugin-vue-components/vite";
import { ElementPlusResolver } from "unplugin-vue-components/resolvers";
import gzipPlugin from "rollup-plugin-gzip";
// import eslintPlugin from 'vite-plugin-eslint';
const pathSrc = path.resolve(__dirname, 'src');
const pathSrc = path.resolve(__dirname, "src");
const root = process.cwd();
// 开发
const apiTarget = 'http://gateway.product.dev.com:30115/';
const apiSluiceTarget = 'http://shuili-admin.product.dev.com:30115';
const apiTarget = "http://gateway.product.dev.com:30115/";
const apiSluiceTarget = "http://shuili-admin.product.dev.com:30115";
// const apiTarget = 'http://sy.datametatech.com:60006/';
// https://vitejs.dev/config/
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, path.resolve(root, './env'));
const env = loadEnv(mode, path.resolve(root, "./env"));
return {
envDir: './env',
envDir: "./env",
base: env.VITE_BASE_URL,
resolve: {
alias: {
'@': pathSrc,
'~/': `${pathSrc}/`
}
"@": pathSrc,
"~/": `${pathSrc}/`,
},
},
css: {
preprocessorOptions: {
scss: {
additionalData: `@use "~/theme/element/index.scss" as *;`
}
}
additionalData: `@use "~/theme/element/index.scss" as *;`,
},
},
},
plugins: [
vue({
template: {
compilerOptions: {
isCustomElement: (tag) => tag.startsWith('iconpark-')
}
}
isCustomElement: (tag) => tag.startsWith("iconpark-"),
},
},
}),
vueJsx(),
AutoImport({
resolvers: [ElementPlusResolver()]
resolvers: [ElementPlusResolver()],
}),
Components({
// allow auto load markdown components under `./src/components/`
extensions: ['vue', 'md'],
extensions: ["vue", "md"],
// allow auto import and register components used in markdown
include: [/\.vue$/, /\.vue\?vue/, /\.md$/],
resolvers: [
ElementPlusResolver({
importStyle: 'sass'
})
importStyle: "sass",
}),
],
dts: 'src/components.d.ts'
dts: "src/components.d.ts",
}),
viteStaticCopy({
targets: [
{
src: './node_modules/sy-cesium-sdk/dist/resources',
dest: ''
}
]
})
src: "./node_modules/sy-cesium-sdk/dist/resources",
dest: mode === "development" ? "/sgcyy-slgcyxgl/" : "",
},
],
}),
],
server: {
host: '0.0.0.0',
host: "0.0.0.0",
port: 6050,
proxy: {
'/proxy': {
"/proxy": {
target: apiTarget,
changeOrigin: true
changeOrigin: true,
},
"/iserver": {
target: "http://172.16.34.41:8090",
changeOrigin: true,
},
'/iserver': {
target: 'http://172.16.34.41:8090',
changeOrigin: true
"/api/iserver": {
target: "http://172.16.34.41:8090",
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api\/iserver/, "/iserver"),
},
'/api/iserver': {
target: 'http://172.16.34.41:8090',
"/api/run": {
// target: "http://shuili.product.dev.com:30115/",
target: "http://172.16.34.80:18082",
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api\/iserver/, '/iserver')
rewrite: (path) => path.replace(/^\/api/, "/tianhui-admin-web"),
},
// '/api/run': {
// // target: "http://shuili.product.dev.com:30115/",
// target: 'http://172.16.34.80:18082',
// changeOrigin: true,
// rewrite: (path) => path.replace(/^\/api/, '/tianhui-admin-web')
// },
'/api': {
target: 'http://shuili.product.dev.com:30115', // 'http://172.16.34.59:18083'
"/api": {
target: "http://shuili.product.dev.com:30115", // 'http://172.16.34.59:18083'
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '/tianhui-admin-web')
rewrite: (path) => path.replace(/^\/api/, "/tianhui-admin-web"),
},
'/geoserver': {
target: 'http://172.16.32.77:8700',
changeOrigin: true
"/geoserver": {
target: "http://172.16.32.77:8700",
changeOrigin: true,
},
'/mapserver': {
target: 'http://172.16.34.71:8083/mapdata',
"/mapserver": {
target: "http://172.16.34.71:8083/mapdata",
changeOrigin: true,
rewrite: (path) => path.replace(/^\/mapserver/, '')
rewrite: (path) => path.replace(/^\/mapserver/, ""),
},
'/images': {
"/images": {
target: apiTarget,
changeOrigin: true
changeOrigin: true,
},
'/objs': {
"/objs": {
target: apiTarget,
changeOrigin: true
changeOrigin: true,
// rewrite: (path) => path.replace(/^\/sy-mixengine-ui/, '')
}
}
},
},
},
build: {
rollupOptions: {
output: {
manualChunks: {
sycim: ['sy-cesium-sdk'],
vue: ['vue']
}
sycim: ["sy-cesium-sdk"],
vue: ["vue"],
},
},
plugins: [gzipPlugin()]
plugins: [gzipPlugin()],
},
chunkSizeWarningLimit: 6000
}
chunkSizeWarningLimit: 6000,
},
} as any;
});

Loading…
Cancel
Save