BUG修改配置

This commit is contained in:
wangxk 2025-04-21 11:18:59 +08:00
parent 0b98f72e17
commit 1622d4cfac
5 changed files with 212 additions and 148 deletions

View File

@ -6,53 +6,96 @@
</div>
</template>
<script setup>
import { ref, onMounted, watch, defineEmits, onBeforeUnmount } from 'vue'
import * as d3 from 'd3'
import chinaData from './newJson.json'
// ==================== ====================
const DEFAULT_MAP_SIZE = { width: 800, height: 600 }
const PROJECTION_CENTER = [104, 37] //
const SCALE_FACTORS = { normal: 600, fullscreen: 1000 }
// ==================== Props/Emits ====================
const props = defineProps({
coordinates: {
type: Array,
default: () => [
[116.405285, 39.904989], //
[121.472644, 31.231706] //
]
],
validator: value => value.every(coord =>
Array.isArray(coord) && coord.length === 2 &&
typeof coord[0] === 'number' && typeof coord[1] === 'number'
)
},
qvehuan: {
type: Boolean,
default: () => false
default: false
}
})
const emit = defineEmits(['qvehuan1']);
const emit = defineEmits(['qvehuan1'])
// ==================== ====================
const mapContainer = ref(null)
const isFullscreen = ref(false)
let svg, mapGroup, markersGroup, zoom, width, height
const isRouteInitialized = ref(false) // initqie
const initMap = () => {
//
width = mapContainer.value.clientWidth?mapContainer.value.clientWidth:800
height = mapContainer.value.clientHeight?mapContainer.value.clientHeight:600
const projection = d3.geoMercator()
.center([104, 37])
.scale(isFullscreen.value ? 1000 : 600)
// ==================== D3 ====================
let svgInstance = null
let mapGroup = null
let markersGroup = null
let zoomBehavior = null
// ==================== ====================
/**
* 创建地理投影配置
* @returns {d3.GeoProjection} D3地理投影实例
*/
const createProjection = () => {
const { width, height } = getContainerSize()
return d3.geoMercator()
.center(PROJECTION_CENTER)
.scale(isFullscreen.value ? SCALE_FACTORS.fullscreen : SCALE_FACTORS.normal)
.translate([width / 2, height / 2])
}
const path = d3.geoPath().projection(projection)
/**
* 初始化地图基础结构
*/
const initializeMap = () => {
//
if (svgInstance) svgInstance.remove()
if (svg) svg.remove()
const { width, height } = getContainerSize()
const projection = createProjection()
const pathGenerator = d3.geoPath().projection(projection)
svg = d3.select(mapContainer.value)
// SVG
svgInstance = d3.select(mapContainer.value)
.append('svg')
.attr('width', width)
.attr('height', height)
//
const mainGroup = svg.append('g')
mapGroup = mainGroup.append('g')
markersGroup = mainGroup.append('g') //
//
const mainGroup = svgInstance.append('g')
mapGroup = mainGroup.append('g').attr('class', 'map-layer')
markersGroup = mainGroup.append('g').attr('class', 'markers-layer')
//
renderChinaMap(pathGenerator)
//
initializeZoomBehavior(mainGroup)
}
/**
* 渲染中国地图路径和文字
* @param {d3.GeoPath} path - 路径生成器
*/
const renderChinaMap = (path) => {
//
mapGroup.append('g')
.selectAll('path')
.data(chinaData.features)
@ -61,64 +104,63 @@ const initMap = () => {
.attr('d', path)
.attr('fill', '#e7e7e7')
.attr('stroke', '#fff')
//
mapGroup.append('g')
.selectAll('text')
.data(chinaData.features)
.enter()
.append('text')
.attr('x', d => {
const centroid = path.centroid(d)
return centroid[0]
})
.attr('y', d => {
const centroid = path.centroid(d)
return centroid[1]
})
.text(d => d.properties.name || '') // JSONproperties.name
.attr('transform', d => `translate(${path.centroid(d)})`)
.text(d => d.properties.name || '')
.attr('text-anchor', 'middle')
.attr('dominant-baseline', 'central')
.style('font', '12px Arial')
.attr('fill', '#333')
.style('font-size', '12px')
.style('font-family', 'Arial')
.style('pointer-events', 'none') //
zoom = d3.zoom()
.scaleExtent([0.2, 8])
.on('zoom', (event) => {
mainGroup.attr('transform', event.transform)
.style('pointer-events', 'none')
}
/**
* 初始化缩放行为
* @param {d3.Selection} group - 需要应用缩放的分组
*/
const initializeZoomBehavior = (group) => {
zoomBehavior = d3.zoom()
.scaleExtent([0.2, 68])
.on('zoom', ({ transform }) => {
group.attr('transform', transform)
})
svg.call(zoom)
}
const handleResize = () => {
if (!isFullscreen.value) return
initMap()
updateRoute()
svgInstance.call(zoomBehavior)
}
const toggleFullscreen = () => {
isFullscreen.value = !isFullscreen.value
setTimeout(() => {
initMap()
updateRoute()
}, 100)
}
// ==================== 线 ====================
/**
* 更新路线和标记点
*/
const updateRoute = () => {
width = mapContainer.value.clientWidth?mapContainer.value.clientWidth:800
height = mapContainer.value.clientHeight?mapContainer.value.clientHeight:600
const projection = d3.geoMercator()
.center([104, 37])
.scale(isFullscreen.value ? 1000 : 600)
.translate([width / 2, height / 2])
if (initqie.value) {
// debugger
if (!props.coordinates?.length) return
if (!mapGroup) initializeMap()
const projection = createProjection()
const pathGenerator = d3.geoPath().projection(projection)
//
if (isRouteInitialized.value) {
mapGroup.selectAll('.route-path').remove()
markersGroup.selectAll('*').remove()
initqie.value = false
emit('qvehuan1', initqie.value);
isRouteInitialized.value = false
emit('qvehuan1', isRouteInitialized.value)
}
// 线
renderRoutePath(pathGenerator)
renderMarkers(projection)
}
/**
* 绘制路径动画
* @param {d3.GeoPath} path - 路径生成器
*/
const renderRoutePath = (path) => {
const routeData = {
type: "LineString",
coordinates: props.coordinates
@ -127,57 +169,87 @@ const updateRoute = () => {
mapGroup.append('path')
.datum(routeData)
.attr('class', 'route-path')
.attr('d', d3.geoPath().projection(projection))
.attr('d', path)
.attr('fill', 'none')
.attr('stroke', '#f00')
.attr('stroke-width', 0.1)
.attr('stroke-dasharray', function () {
.attr('stroke-dasharray', function() {
return this.getTotalLength()
})
.attr('stroke-dashoffset', function () {
.attr('stroke-dashoffset', function() {
return this.getTotalLength()
})
.transition()
.duration(2000)
.attr('stroke-dashoffset', 0)
}
//
props.coordinates.forEach((coord, i) => {
if (!Array.isArray(coord) || coord.length !== 2) return
const lon = Number(coord[0])
const lat = Number(coord[1])
/**
* 绘制位置标记点
* @param {d3.GeoProjection} projection - 地理投影实例
*/
const renderMarkers = (projection) => {
props.coordinates.forEach(coord => {
if (!isValidCoordinate(coord)) return
if (isNaN(lon) || isNaN(lat)) return
const [x, y] = projection([lon, lat])
//
const [x, y] = projection(coord)
markersGroup.append('circle')
.attr('cx', x)
.attr('cy', y)
.attr('r', 0.3) //
.attr('r', 0.3)
.attr('fill', '#ff4757')
.attr('stroke', 'white')
.attr('stroke-width', 0) //
.attr('stroke-width', 0)
})
}
const initqie = ref(false)
onMounted(() => {
window.addEventListener('resize', handleResize)
if (props.coordinates) {
initMap()
updateRoute()
}
// ==================== ====================
const getContainerSize = () => ({
width: mapContainer.value?.clientWidth || DEFAULT_MAP_SIZE.width,
height: mapContainer.value?.clientHeight || DEFAULT_MAP_SIZE.height
})
onBeforeUnmount(() => {
window.removeEventListener('resize', handleResize)
})
watch(() => props.qvehuan, (newVal) => {
initqie.value = newVal
const isValidCoordinate = (coord) =>
Array.isArray(coord) && coord.length === 2 &&
!isNaN(coord[0]) && !isNaN(coord[1])
// ==================== ====================
const handleWindowResize = () => {
if (!isFullscreen.value) return
initializeMap()
updateRoute()
}, { deep: true })
watch(() => props.coordinates, (newVal) => {
}
const toggleFullscreen = () => {
isFullscreen.value = !isFullscreen.value
setTimeout(() => {
initializeMap()
updateRoute()
}, 100) // DOM
}
// ==================== ====================
onMounted(() => {
window.addEventListener('resize', handleWindowResize)
//
initializeMap() // [!code ++]
// coordinates线
if (props.coordinates?.length) { // [!code ++]
updateRoute() // [!code ++]
} // [!code ++]
})
onBeforeUnmount(() => {
window.removeEventListener('resize', handleWindowResize)
})
// ==================== ====================
watch(() => props.qvehuan, (newVal) => {
isRouteInitialized.value = newVal
updateRoute()
})
watch(() => props.coordinates, () => {
updateRoute()
}, { deep: true })
</script>
@ -201,19 +273,24 @@ watch(() => props.coordinates, (newVal) => {
right: 0;
bottom: 0;
margin: 0 !important;
z-index: 9999;
z-index: 1000;
}
.fullscreen-btn {
position: absolute;
top: 10px;
right: 25px;
z-index: 10000;
z-index: 1001;
padding: 8px 16px;
background: rgba(0, 0, 0, 0.7);
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
transition: opacity 0.2s;
}
.fullscreen-btn:hover {
opacity: 0.9;
}
</style>

View File

@ -54,7 +54,7 @@ const info = ref({
description: "",
custom1: "",
});
//
//
const causeInfo = ref({
id: "",
parentid: "",
@ -68,9 +68,9 @@ const causeInfo = ref({
const title = ref("");
const dialogVisible = ref(false);
const dialogCause = ref(false);
//
//
const choosetreeid = ref("");
//
//
function getTree() {
const params = {
parentid: "0",
@ -103,10 +103,10 @@ function getData() {
});
}
const multipleSelection = ref([]);
//
//
const rules = reactive<FormRules>({
orgname: [{ required: true, message: "请填写企业名称", trigger: "blur" }],
manager: [{ required: true, message: "请填写企业负责人名称", trigger: "blur" }],
orgname: [{ required: true, message: "请填写组织名称", trigger: "blur" }],
manager: [{ required: true, message: "请填写组织负责人名称", trigger: "blur" }],
});
//
const moderules = reactive<FormRules>({
@ -148,9 +148,9 @@ function switchChange(row: any) {
getData();
});
}
//
//
function causeAdd() {
title.value = "新增企业";
title.value = "新增组织";
dialogCause.value = true;
const causeInfogase = ref({
id: "",
@ -164,7 +164,7 @@ function causeAdd() {
});
causeInfo.value = causeInfogase.value;
}
//-
//-
function editisrepetition(formEl: any) {
formEl.validate((valid: any) => {
if (valid) {
@ -214,16 +214,16 @@ function handleClose() {
dialogCause.value = false;
}
//
//
function edittree(data: any) {
title.value = "修改企业";
title.value = "修改组织";
causeInfo.value = JSON.parse(JSON.stringify(data));
dialogCause.value = true;
}
function remove(data: any) {
choosetreeid.value = "";
choosetreeid.value = data.id;
ElMessageBox.confirm("确定删除该企业及该企业下的所有部门吗?", "删除提示", {
ElMessageBox.confirm("确定删除该组织及该组织下的所有部门吗?", "删除提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
@ -375,7 +375,7 @@ const vMove = {
<div class="faulttemplate-box">
<aside id="silderLeft">
<div v-hasPerm="['add:org']" class="p-[15px]"><el-button class="w-full mb-[15px]" style="color: #0099ff; border: 1px solid #0099ff;"
@click="causeAdd"> <img src="@/assets/MenuIcon/u241.png" style="margin-right: 10px;"> 新增企业</el-button></div>
@click="causeAdd"> <img src="@/assets/MenuIcon/u241.png" style="margin-right: 10px;"> 新增组织</el-button></div>
<el-tree :class="useAppStore().size === 'default' ? 'silderLeft-large' : 'silderLeft-default'" v-loading="treeloading"
ref="treeRef" node-key="id" :data="treedata" :highlight-current="true" :props="defaultProps"
@node-click="handleNodeClick" style="height: calc(100vh - 215px); overflow: auto">
@ -475,25 +475,25 @@ const vMove = {
<el-button type="primary" style="padding: 10px 15px" @click="confirmClick(infoForm)">确定</el-button>
</span>
</el-dialog>
<!-- 企业弹框-->
<!-- 组织弹框-->
<el-dialog v-model="dialogCause" :before-close="handleClose" :title="title" draggable width="620px">
<el-form ref="causeForm" :model="causeInfo" label-width="90px" :rules="rules">
<el-form-item label="企业编号" prop="orgcode">
<el-form-item label="组织编号" prop="orgcode">
<el-input v-model="causeInfo.orgcode" style="width: 100%" :disabled="true"></el-input>
</el-form-item>
<el-form-item label="企业名称" prop="orgname">
<el-input v-model="causeInfo.orgname" style="width: 100%" placeholder="请输入企业名称"></el-input>
<el-form-item label="组织名称" prop="orgname">
<el-input v-model="causeInfo.orgname" style="width: 100%" placeholder="请输入组织名称"></el-input>
</el-form-item>
<el-form-item label="负责人" prop="manager">
<el-input v-model="causeInfo.manager" style="width: 100%" placeholder="请输入负责人姓名"> </el-input>
</el-form-item>
<el-form-item label="企业描述" prop="description">
<el-form-item label="组织描述" prop="description">
<el-input v-model="causeInfo.description" type="textarea" :autosize="{ minRows: 2 }" style="width: 100%"
placeholder="请输入企业描述"></el-input>
placeholder="请输入组织描述"></el-input>
</el-form-item>
<el-form-item label="联系信息" prop="custom1">
<el-input v-model="causeInfo.custom1" type="textarea" :autosize="{ minRows: 2 }" style="width: 100%"
placeholder="请输入企业联系信息"></el-input>
placeholder="请输入组织联系信息"></el-input>
</el-form-item>
</el-form>
<span class="dialog-footer">

View File

@ -137,10 +137,10 @@ function handleNodeClick(row: any) {
init();
}
// / -
function activeNameChange(name: any) {
activeName.value = name.props.name
getTree()
}
// function activeNameChange(name: any) {
// activeName.value = name.props.name
// getTree()
// }
//
function dictAdd() {
@ -389,18 +389,18 @@ const total = ref()
<template>
<div class="faulttemplate-box">
<aside id="silderLeft">
<el-tabs :class="useAppStore().size === 'default' ? 'silderLeft-large' : 'silderLeft-default'" v-model="activeName"
<!-- <el-tabs :class="useAppStore().size === 'default' ? 'silderLeft-large' : 'silderLeft-default'" v-model="activeName"
class="demo-tabs" @tab-click="activeNameChange">
<el-tab-pane label="系统配置" name="00"></el-tab-pane>
<el-tab-pane label="用户配置" name="01"></el-tab-pane>
</el-tabs>
</el-tabs> -->
<div class="menu-box">
<el-button style="border:1px solid #0099FF;width:100%;color:#0099FF;margin-bottom:10px;" @click="dictAdd"> <img
src="@/assets/MenuIcon/czan_xz.png" alt="" style="width: 14px; margin-right: 5px;"> 新增字典</el-button>
<el-tree v-loading="treeloading" ref="treeRef"
:class="useAppStore().size === 'default' ? 'silderLeft-large' : 'silderLeft-default'" node-key="id"
:allow-drop="allowDrop" :data="treedata" draggable :highlight-current="true" :props="defaultProps"
@node-click="handleNodeClick" @node-drop="treenodeDrop" style="height:calc(100vh - 254px);overflow: auto;">
@node-click="handleNodeClick" @node-drop="treenodeDrop" style="height:calc(79vh);overflow: auto;">
<template #default="{ node, data }">
<span class="custom-tree-node">
<span class="custom-tree-node-img">

View File

@ -52,7 +52,8 @@ const infoForm = ref();
const treeloading = ref(true)
function getTree() {
const params = {
parentid: "0",
parentid:'0',
params:queryName.value
};
getTreelist(params).then((res: any) => {
treedata.value = res
@ -68,14 +69,13 @@ function getTree() {
})
}
const currentNodeKey = ref("")
const institutionId = ref('')
function handleNodeClick(data: Tree, node: any) {
institutionId.value = data.id
// institutionId.value = data.id
orgId.value = data.id;
getdata();
// if (data.childList.length == 0) {
// orgId.value = data.id;
//
// dialog.value = true
// getdata();
// } else {
@ -119,25 +119,26 @@ function addClick() {
const multipleSelection = ref([]);
const tableData = ref([]);
const orgId = ref("");
const dataloading = ref(false)
//
function getdata() {
const params = {
current: queryParams.value.current,
size: queryParams.value.size,
orgid: orgId.value,
institutionId: institutionId.value,
username: queryParams.value.querystr,
nickname: queryParams.value.querystr,
}
loading.value = true;
dataloading.value = true
gettableData(params).then((res => {
dataloading.value = false
total.value = res.data.total
tableData.value = res.data.records
queryParams.value.size = res.data.size
queryParams.value.current = res.data.current
dialog.value = true
loading.value = false;
})).catch(() => {
loading.value = false;
dataloading.value = false;
})
}
//
@ -221,7 +222,6 @@ function confirmClick(formEl: any) {
email: info.value.email,
phone: info.value.phone,
orgid: orgId.value,
institutionId: info.value.institution_id,
institutionType: info.value.institution_type
};
const roleids = String(info.value.roleinfo)
@ -241,7 +241,6 @@ function confirmClick(formEl: any) {
email: info.value.email,
phone: info.value.phone,
orgid: orgId.value,
institutionId: info.value.institution_id,
institutionType: info.value.institution_type
};
const roleids = info.value.roleinfo;
@ -476,7 +475,7 @@ const queryName = ref('')
<template>
<div class="faulttemplate-box">
<aside id="silderLeft">
<el-input v-model="queryName" clearable @keydown.enter="getTree()" placeholder="组织名称">
<el-input v-model="queryName" clearable @change="getTree()" placeholder="部门名称">
<template #append>
<el-button type="primary" :icon="Search" @click="getTree()" />
</template>
@ -513,7 +512,7 @@ const queryName = ref('')
</el-row>
<el-table :data="tableData" style="width: 100%;margin-bottom: 20px;height: calc(100vh - 250px);" row-key="id"
border default-expand-all :v-loading="dialog" @selection-change="handleSelectionChange"
border default-expand-all v-loading="dataloading" @selection-change="handleSelectionChange"
:header-cell-style="{ background: 'rgb(250 250 250)', color: ' #383838', height: '50px' }">
<el-table-column type="selection" width="50" align="center" />
<el-table-column type="index" label="序号" width="70" align="center" />
@ -521,16 +520,13 @@ const queryName = ref('')
<!-- <el-table-column prop="avatar" label="头像"></el-table-column> -->
<el-table-column prop="email" label="邮箱"></el-table-column>
<el-table-column prop="phone" label="手机号" min-width="90"></el-table-column>
<el-table-column prop="username" label="登录账号" width="120"></el-table-column>
<el-table-column prop="username" label="登录账号" ></el-table-column>
<!-- <el-table-column prop="custom1" label="登录账号"></el-table-column> -->
<el-table-column prop="rolename" label="所属角色" width="210">
<template #default="scope">
<span v-for="(item, index) in scope.row.roles" :key="index">{{ item.rolename }} <span></span> </span>
</template>
</el-table-column>
<el-table-column prop="institution_name" label="所属机构" min-width="90"></el-table-column>
<el-table-column prop="status" label="账号状态" align="center" width="120">
<template #default="scope">
<el-switch v-model="scope.row.status" active-value="1" inactive-value="0" @change="switchChange(scope.row)"
@ -581,21 +577,12 @@ const queryName = ref('')
<el-input v-model="info.username" style="width: 100%" placeholder="请输入登录账号"></el-input>
</el-form-item>
<el-form-item label="所属角色">
<el-select v-model="info.roleinfo" placeholder=" " style="width: 100%" multiple :disabled="isRoles == true"
<el-select v-model="info.roleinfo" placeholder="请选择所属角色" style="width: 100%" multiple :disabled="isRoles == true"
filterable>
<el-option v-for="item in rolesdata" :key="item.id" :label="item.rolename" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="组织机构" v-if="isRoles == false">
<el-input v-if="userInfo.level == '1'" v-model="info.institution_name" autocomplete="off"
placeholder="请选择所属机构" disabled style="width: 390px;" />
<el-button v-if="userInfo.level == '1'" type="primary" class="ml-[5px]"
@click="viewInstitution">选择</el-button>
<el-select v-if="userInfo.level != '1'" v-model="info.institution_id" placeholder="请选择所属机构"
style="width: 100%" @change="institutionClick">
<el-option v-for="item in userInfo.list" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
</el-form>
<span class="dialog-footer"
style="display: flex;display: -webkit-flex; justify-content: flex-end;-webkit-justify-content: flex-end;">

View File

@ -28,7 +28,7 @@ export default ({ mode }: ConfigEnv): UserConfig => {
//线上API地址
//target: 'https://edu.mmhyvision.com:8443',
// 本地API地址
target: 'http://192.168.1.40:8087',
target: 'http://192.168.1.208:8087',
changeOrigin: true,
rewrite: path =>
path.replace(new RegExp('^' + env.VITE_APP_BASE_API), '')