Merge branch 'developer' of ssh://code.cyweb.top:30033/scj/zhxy/v3/cloud-ui into developer

This commit is contained in:
zhoutianchi
2026-01-19 18:50:35 +08:00
11 changed files with 427 additions and 142 deletions

7
.gitattributes vendored Normal file
View File

@@ -0,0 +1,7 @@
# 确保所有文本文件使用 UTF-8 编码
* text=auto eol=lf
*.vue text eol=lf charset=utf-8
*.ts text eol=lf charset=utf-8
*.js text eol=lf charset=utf-8
*.json text eol=lf charset=utf-8

View File

@@ -458,6 +458,7 @@ export const batchPushAll = (obj: any) => {
*/ */
export const BMPGL = (ak: string) => { export const BMPGL = (ak: string) => {
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
// @ts-ignore
window.init = function () { window.init = function () {
// eslint-disable-next-line // eslint-disable-next-line
// resolve(BMapGL); // resolve(BMapGL);
@@ -470,6 +471,45 @@ export const BMPGL = (ak: string) => {
}); });
}; };
/**
* 天地图
* @param tk 天地图token
*/
export const loadTiandituMap = (tk: string) => {
return new Promise(function (resolve, reject) {
// @ts-ignore
// 如果天地图API已经加载直接返回
if (window.T) {
// @ts-ignore
resolve(window.T);
return;
}
// 检查是否已经有加载中的脚本
const existingScript = document.querySelector('script[src*="api.tianditu.gov.cn"]');
if (existingScript) {
// 如果脚本正在加载中,等待加载完成
existingScript.addEventListener('load', () => {
// @ts-ignore
resolve(window.T);
});
existingScript.addEventListener('error', reject);
return;
}
// 加载天地图主库
const script = document.createElement('script');
script.type = 'text/javascript';
script.src = `https://api.tianditu.gov.cn/api?v=4.0&tk=${tk}`;
script.onload = () => {
// @ts-ignore
resolve(window.T);
};
script.onerror = reject;
document.head.appendChild(script);
});
};
/** /**
* 宿舍申请分析 * 宿舍申请分析
* @param obj * @param obj

40
src/config/map.ts Normal file
View File

@@ -0,0 +1,40 @@
/**
* 地图配置文件
* 统一管理地图相关的配置信息
*/
/**
* 天地图Token
* 请到天地图开放平台申请https://console.tianditu.gov.cn/
* 申请后请将下方的token替换为您自己的token
*/
export const TIANDITU_TOKEN = 'd584b11f3c0d801105df2f415a2d3530'
/**
* 地理编码服务配置
* 使用OpenStreetMap Nominatim服务完全免费
*/
export const GEOCODING_SERVICE = 'nominatim' // 使用Nominatim服务
/**
* 天地图API版本
*/
export const TIANDITU_API_VERSION = '4.0'
/**
* 天地图地理编码服务地址
*/
export const TIANDITU_GEOCODE_URL = 'https://api.tianditu.gov.cn/geocoder'
/**
* 默认地图中心点(可根据实际情况修改)
*/
export const DEFAULT_MAP_CENTER = {
lng: 116.397428,
lat: 39.90923
}
/**
* 默认地图缩放级别
*/
export const DEFAULT_MAP_ZOOM = 13

View File

@@ -590,7 +590,7 @@
<template #footer v-if="type==1"> <template #footer v-if="type==1">
<div class="dialog-footer"> <div class="dialog-footer">
<el-button @click="visible = false">取消</el-button> <el-button @click="visible = false">取消</el-button>
<el-button type="success" @click="dataFormSubmit(20)" v-if="canSubmit">确认录取</el-button> <el-button type="primary" @click="dataFormSubmit(20)" v-if="canSubmit">确认录取</el-button>
<el-button type="danger" plain @click="dataFormSubmit(-20)" v-if="canSubmit">驳回录取</el-button> <el-button type="danger" plain @click="dataFormSubmit(-20)" v-if="canSubmit">驳回录取</el-button>
</div> </div>
</template> </template>
@@ -612,7 +612,7 @@ import { list as listByGroupId } from '/@/api/recruit/recruitstudentschool'
import { getDeptList } from "/@/api/basic/basicclass" import { getDeptList } from "/@/api/basic/basicclass"
import { getList } from "/@/api/recruit/recruitstudentplangroup" import { getList } from "/@/api/recruit/recruitstudentplangroup"
import { listByEdu } from "/@/api/recruit/recruitstudentplan" import { listByEdu } from "/@/api/recruit/recruitstudentplan"
import { getDictsByTypes } from "/@/api/admin/dict" import { getDicts, getDictsByTypes } from "/@/api/admin/dict"
import { useDict } from '/@/hooks/dict' import { useDict } from '/@/hooks/dict'
import { areaList, areaSonList } from "/@/api/recruit/recruitstudentschool" import { areaList, areaSonList } from "/@/api/recruit/recruitstudentschool"
import { list as scoreList } from "/@/api/recruit/recruitstudentplancorrectscoreconfig" import { list as scoreList } from "/@/api/recruit/recruitstudentplancorrectscoreconfig"
@@ -832,6 +832,9 @@ const init = (id: string | null, typeParam: number) => {
isShow.value = true isShow.value = true
nextTick(() => { nextTick(() => {
dataFormRef.value?.resetFields() dataFormRef.value?.resetFields()
getDicts('finance_student_source').then((res: any) => {
eduList.value = res.data || []
})
if (dataForm.id) { if (dataForm.id) {
areaPList.value = [] areaPList.value = []
areaCList.value = [] areaCList.value = []

View File

@@ -4,22 +4,19 @@
append-to-body append-to-body
:close-on-click-modal="false" :close-on-click-modal="false"
v-model="visible" v-model="visible"
width="800"> width="90%">
<div> <div>
<el-form :model="form" :rules="rules" ref="formRef" label-width="120px" <el-form :model="form" :rules="rules" ref="formRef" class="demo-ruleForm">
class="demo-ruleForm">
<el-form-item label="住宿半径(米)" prop="raidus"> <el-form-item label="住宿半径(米)" prop="raidus">
<el-input-number v-model="form.raidus" :min="0" style="width: 100%"></el-input-number> <el-input-number v-model="form.raidus" :min="0" style="width: 100%"></el-input-number>
</el-form-item> </el-form-item>
</el-form> </el-form>
<div id="container"></div> <div id="container"></div>
</div> </div>
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">
<el-button type="primary" @click="dataFormSubmit">确定</el-button>
<el-button @click="visible = false">取消</el-button> <el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="dataFormSubmit">确定</el-button>
</div> </div>
</template> </template>
</el-dialog> </el-dialog>
@@ -28,9 +25,10 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, watch, nextTick } from 'vue' import { ref, reactive, watch, nextTick } from 'vue'
import { useMessage, useMessageBox } from '/@/hooks/message' import { useMessage, useMessageBox } from '/@/hooks/message'
import { BMPGL } from "@/api/recruit/recruitstudentsignup" import { loadTiandituMap } from "/@/api/recruit/recruitstudentsignup"
import { putItemObj } from "@/api/admin/dict" import { putItemObj } from "/@/api/admin/dict"
import { getTypeValue } from "@/api/admin/dict" import { getDicts } from "/@/api/admin/dict"
import { TIANDITU_TOKEN } from '/@/config/map'
// 消息提示 hooks // 消息提示 hooks
const message = useMessage() const message = useMessage()
@@ -40,17 +38,16 @@ const messageBox = useMessageBox()
const formRef = ref() const formRef = ref()
// 响应式数据 // 响应式数据
const ak = "V0ooaf2RZyEGOkD8UzZB3gvw7pCb0Kx7" // 百度的地图密钥 const tk = TIANDITU_TOKEN // 天地图的token在 src/config/map.ts 中配置)
const visible = ref(false) const visible = ref(false)
const canSubmit = ref(false) const canSubmit = ref(false)
const circleShow = ref(false) const circleShow = ref(false)
const circle = ref<any>(null) const circle = ref<any>(null)
const map = ref<any>(null)
// 地址信息 // 地址信息
const address = ref(null)
const center = reactive({ lng: 0, lat: 0 }) const center = reactive({ lng: 0, lat: 0 })
const dictId = ref("") const dictId = ref("")
const circleArr = ref<any[]>([])
const form = reactive({ const form = reactive({
raidus: 0, raidus: 0,
@@ -64,8 +61,39 @@ const rules = {
// 监听半径变化 // 监听半径变化
watch(() => form.raidus, (newVal) => { watch(() => form.raidus, (newVal) => {
if (newVal != '' && newVal != undefined && circle.value) { if (newVal !== 0 && newVal !== undefined && circle.value && map.value) {
circle.value.setRadius(newVal) // 设置圆形覆盖物的半径 // 移除旧圆形
map.value.removeOverLay(circle.value)
// 创建新圆形(确保数据类型正确)
// @ts-ignore
const newCircle = new window.T.Circle(
// @ts-ignore
new window.T.LngLat(
// @ts-ignore
parseFloat(center.lng),
// @ts-ignore
parseFloat(center.lat)
),
// @ts-ignore
parseFloat(newVal), // 确保半径是数字类型
{
color: '#FF0000', // 边框颜色:鲜红色
weight: 4, // 边框粗细4像素
opacity: 0.9, // 边框不透明度90%
fillColor: '#FF4444', // 填充颜色:亮红色
fillOpacity: 0.12 // 填充透明度12%
}
)
map.value.addOverLay(newCircle)
circle.value = newCircle
// 让新圆形也不拦截鼠标事件
setTimeout(() => {
const circleElements = document.querySelectorAll('.tdt-circle, [class*="circle"]')
circleElements.forEach((el: any) => {
el.style.pointerEvents = 'none'
})
}, 100)
} }
}) })
@@ -75,7 +103,7 @@ const init = () => {
canSubmit.value = true canSubmit.value = true
circleShow.value = true circleShow.value = true
nextTick(() => { nextTick(() => {
getTypeValue("dorm_jw").then((data: any) => { getDicts("dorm_jw").then((data: any) => {
const arr = data.data const arr = data.data
arr.forEach((e: any) => { arr.forEach((e: any) => {
if (e.label == 'bj') { if (e.label == 'bj') {
@@ -87,25 +115,101 @@ const init = () => {
center.lat = e.value center.lat = e.value
} }
}) })
BMPGL(ak).then((BMapGL: any) => {
// 创建地图实例
const map = new BMapGL.Map("container")
// 创建点坐标
const point = new BMapGL.Point(center.lng, center.lat)
// 初始化地图,设置中心点坐标和地图级别
map.centerAndZoom(point, 13)
// 开启鼠标滚轮缩放
map.enableScrollWheelZoom(true)
// 绘制圆 // 等待对话框渲染完成后再加载地图
circle.value = new BMapGL.Circle(new BMapGL.Point(center.lng, center.lat), form.raidus, { setTimeout(() => {
strokeColor: 'blue', // 调试信息:打印配置数据
strokeWeight: 2, // eslint-disable-next-line no-console
strokeOpacity: 0.5, console.log('地图配置:', { lng: center.lng, lat: center.lat, radius: form.raidus, token: tk })
enableEditing: false
loadTiandituMap(tk).then((T: any) => {
// eslint-disable-next-line no-console
console.log('天地图API加载成功', T)
// 清除之前的地图实例(如果存在)
if (map.value) {
map.value.clearOverLays()
}
// 创建地图实例
map.value = new T.Map("container")
// 创建点坐标(确保经纬度是数字类型)
// @ts-ignore
const point = new T.LngLat(
// @ts-ignore
parseFloat(center.lng),
// @ts-ignore
parseFloat(center.lat)
)
// 根据半径自动计算合适的缩放级别
// @ts-ignore
const radius = parseFloat(form.raidus)
let zoomLevel = 12 // 默认缩放级别
// 根据半径动态调整缩放级别
if (radius <= 1000) {
zoomLevel = 15 // 1公里以内 - 非常近
} else if (radius <= 3000) {
zoomLevel = 14 // 3公里以内 - 近距离
} else if (radius <= 5000) {
zoomLevel = 13 // 5公里以内 - 中等距离
} else if (radius <= 10000) {
zoomLevel = 12 // 10公里以内 - 较远
} else if (radius <= 20000) {
zoomLevel = 11 // 20公里以内 - 远距离
} else {
zoomLevel = 10 // 20公里以上 - 超远距离
}
// 初始化地图,设置中心点坐标和地图级别
map.value.centerAndZoom(point, zoomLevel)
// eslint-disable-next-line no-console
console.log('地图缩放级别:', zoomLevel, '(半径:', radius, '米)')
// 添加地图类型切换控件(让用户可以切换卫星图/普通图)
const ctrl = new T.Control.MapType()
map.value.addControl(ctrl)
// 默认使用矢量地图(普通地图),用户可通过右上角控件切换到卫星图
// 绘制圆形覆盖物(使用前面已计算好的 radius 变量)
// eslint-disable-next-line no-console
console.log('绘制圆形:', { center: point, radius: radius, radiusType: typeof radius })
circle.value = new T.Circle(point, radius, {
color: '#FF0000', // 边框颜色:鲜红色,更醒目
weight: 4, // 边框粗细4像素更明显
opacity: 0.9, // 边框不透明度90%,清晰可见
fillColor: '#FF4444', // 填充颜色:亮红色
fillOpacity: 0.12 // 填充透明度12%,轻盈不遮挡地图
})
map.value.addOverLay(circle.value)
// 让圆形不拦截鼠标事件,使地图可以在圆圈内拖拽
// 通过 CSS 设置 pointer-events 为 none
setTimeout(() => {
const circleElements = document.querySelectorAll('.tdt-circle, [class*="circle"]')
circleElements.forEach((el: any) => {
el.style.pointerEvents = 'none'
})
}, 100)
// 添加中心点标记(更明显地标识中心位置)
const marker = new T.Marker(point)
map.value.addOverLay(marker)
// eslint-disable-next-line no-console
console.log('地图初始化完成 - 圆形已添加')
}).catch((error: any) => {
// eslint-disable-next-line no-console
console.error('天地图加载失败:', error)
message.error('地图加载失败请检查网络连接或Token配置')
}) })
map.addOverlay(circle.value) }, 200)
}) }).catch(() => {
message.error('获取地图配置失败')
}) })
}) })
} }
@@ -141,9 +245,10 @@ defineExpose({
#container { #container {
overflow: hidden; overflow: hidden;
width: 100%; width: 100%;
height: 500px; height: 700px;
margin: 0; margin: 0;
font-family: "微软雅黑"; font-family: "微软雅黑";
margin-top: 15px;
} }
ul li { ul li {
@@ -154,3 +259,19 @@ ul li {
text-align: right; text-align: right;
} }
</style> </style>
<style>
/* 让地图上的圆形覆盖物不拦截鼠标事件,使地图可以拖拽 */
#container svg path[fill*="#FF"],
#container svg path[stroke*="#FF"],
#container svg circle,
#container canvas {
pointer-events: none !important;
}
/* 但保持标记点可点击 */
#container .tdt-marker,
#container img {
pointer-events: auto !important;
}
</style>

View File

@@ -290,14 +290,14 @@
icon="Download" icon="Download"
@click="handleExport()">名单导出 @click="handleExport()">名单导出
</el-button> </el-button>
<el-button <!-- <el-button
class="ml10" class="ml10"
type="primary" type="primary"
plain plain
icon="UploadFilled" icon="UploadFilled"
v-auth="'recruit_send_img'" v-auth="'recruit_send_img'"
@click="handleSendImg()">图片同步 @click="handleSendImg()">图片同步
</el-button> </el-button> -->
</div> </div>
</el-row> </el-row>
@@ -347,7 +347,7 @@
prop="name" prop="name"
header-align="center" header-align="center"
align="left" align="left"
min-width="250" width="290"
label="资料检测"> label="资料检测">
<template #default="scope"> <template #default="scope">
<div v-if="scope.row.isOut=='0'" class="material-check-cell"> <div v-if="scope.row.isOut=='0'" class="material-check-cell">

View File

@@ -126,7 +126,7 @@
<el-table-column prop="name" label="姓名" width="100" align="center" show-overflow-tooltip /> <el-table-column prop="name" label="姓名" width="100" align="center" show-overflow-tooltip />
<el-table-column prop="gender" label="性别" width="80" align="center" show-overflow-tooltip> <el-table-column prop="gender" label="性别" width="80" align="center" show-overflow-tooltip>
<template #default="scope"> <template #default="scope">
{{ getLabelValue(sexy, scope.row.gender) }} <GenderTag :sex="scope.row.gender" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="idNumber" label="身份证号" width="180" align="center" show-overflow-tooltip /> <el-table-column prop="idNumber" label="身份证号" width="180" align="center" show-overflow-tooltip />
@@ -159,7 +159,7 @@
<template #default="scope"> <template #default="scope">
<el-button <el-button
v-if="permissions.recruitStuDormSd && scope.row.isOutFw != '1'" v-if="permissions.recruitStuDormSd && scope.row.isOutFw != '1'"
type="success" type="primary"
link link
icon="CircleCheck" icon="CircleCheck"
@click="setFw(scope.row, 1)" @click="setFw(scope.row, 1)"
@@ -168,7 +168,7 @@
</el-button> </el-button>
<el-button <el-button
v-if="permissions.recruitStuDormSd && scope.row.isOutFw != '2'" v-if="permissions.recruitStuDormSd && scope.row.isOutFw != '2'"
type="warning" type="primary"
link link
icon="Close" icon="Close"
@click="setFw(scope.row, 2)" @click="setFw(scope.row, 2)"
@@ -220,6 +220,8 @@ import { getList } from '/@/api/recruit/recruitstudentplangroup'
import { fetchListStuDorm, yjOut as yjOutApi, setFw as setFwApi, delFw, yjSend as yjSendApi } from '/@/api/recruit/recruitstudentsignup' import { fetchListStuDorm, yjOut as yjOutApi, setFw as setFwApi, delFw, yjSend as yjSendApi } from '/@/api/recruit/recruitstudentsignup'
import { getDeptList } from '/@/api/basic/basicclass' import { getDeptList } from '/@/api/basic/basicclass'
const GenderTag = defineAsyncComponent(() => import('/@/components/GenderTag/index.vue'))
const DormFW = defineAsyncComponent(() => import('./dormFW.vue')) const DormFW = defineAsyncComponent(() => import('./dormFW.vue'))
const ShowMap = defineAsyncComponent(() => import('./showMap.vue')) const ShowMap = defineAsyncComponent(() => import('./showMap.vue'))

View File

@@ -4,14 +4,14 @@
:close-on-click-modal="false" :close-on-click-modal="false"
v-model="visible" v-model="visible"
append-to-body append-to-body
width="90%"> width="600">
<el-form :model="dataForm" :rules="dataRule" ref="dataFormRef" @keyup.enter="dataFormSubmit" <el-form :model="dataForm" :rules="dataRule" ref="dataFormRef" @keyup.enter="dataFormSubmit"
label-width="170px" size="small"> label-width="120px">
<el-row> <el-row>
<el-col :span="24"> <el-col :span="24">
<el-form-item label="招生计划" prop="groupId"> <el-form-item label="招生计划" prop="groupId">
<el-select v-model="dataForm.groupId" filterable :disabled="!!dataForm.id" <el-select v-model="dataForm.groupId" filterable :disabled="!!dataForm.id"
placeholder="请选择招生计划" size="small" style="width: 100%"> placeholder="请选择招生计划">
<el-option <el-option
v-for="item in planList" v-for="item in planList"
:key="item.id" :key="item.id"
@@ -39,14 +39,14 @@
<el-row> <el-row>
<el-col :span="24"> <el-col :span="24">
<el-form-item label="成绩折算分" prop="correctedScore"> <el-form-item label="成绩折算分" prop="correctedScore">
<el-input-number v-model="dataForm.correctedScore" controls-position="right" :min="0" :max="999" :step-strictly="true" style="width: 100%;"></el-input-number> <el-input-number v-model="dataForm.correctedScore" controls-position="right" :min="0" :max="999" :step-strictly="true"></el-input-number>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
<el-row> <el-row>
<el-col :span="24"> <el-col :span="24">
<el-form-item label="原录取专业" prop="confirmedMajor"> <el-form-item label="原录取专业" prop="confirmedMajor">
<el-select v-model="dataForm.confirmedMajor" filterable clearable placeholder="" size="small" style="width: 100%" :disabled="type != 1" @change="changeM(dataForm.confirmedMajor)"> <el-select v-model="dataForm.confirmedMajor" filterable clearable placeholder="" :disabled="type != 1" @change="changeM(dataForm.confirmedMajor)">
<el-option <el-option
v-for="item in planMajorList" v-for="item in planMajorList"
:key="item.majorCode" :key="item.majorCode"
@@ -60,7 +60,7 @@
<el-row> <el-row>
<el-col :span="24"> <el-col :span="24">
<el-form-item label="新录取专业" prop="newConfirmedMajor"> <el-form-item label="新录取专业" prop="newConfirmedMajor">
<el-select v-model="dataForm.newConfirmedMajor" filterable placeholder="" size="small" style="width: 100%" @change="changeCM(dataForm.newConfirmedMajor)"> <el-select v-model="dataForm.newConfirmedMajor" filterable placeholder="" @change="changeCM(dataForm.newConfirmedMajor)">
<el-option <el-option
v-for="item in planMajorList" v-for="item in planMajorList"
:key="item.majorCode" :key="item.majorCode"
@@ -73,23 +73,24 @@
</el-row> </el-row>
<el-row> <el-row>
<el-col :span="6"> <el-col :span="12">
<el-form-item label="学费" prop="feeTuition"> <el-form-item label="学费" prop="feeTuition">
<el-input-number v-model="dataForm.feeTuition" controls-position="right" :min="0" :max="999999" :step-strictly="true" style="width:100%;" :disabled="type == 2"></el-input-number> <el-input-number v-model="dataForm.feeTuition" controls-position="right" :min="0" :max="999999" :step-strictly="true" :disabled="type == 2"></el-input-number>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="6"> <el-col :span="12">
<el-form-item label="代办费" prop="feeAgency"> <el-form-item label="代办费" prop="feeAgency">
<el-input-number v-model="dataForm.feeAgency" controls-position="right" :min="0" :max="999999" :step-strictly="true" style="width:100%;" :disabled="type == 2"></el-input-number> <el-input-number v-model="dataForm.feeAgency" controls-position="right" :min="0" :max="999999" :step-strictly="true" :disabled="type == 2"></el-input-number>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="6"> </el-row>
<el-row>
<el-col :span="24">
<el-form-item label="总费用" prop="allMoney"> <el-form-item label="总费用" prop="allMoney">
<span style="color: red">{{ dataForm.feeTuition + dataForm.feeAgency }}</span> <span style="color: red">{{ dataForm.feeTuition + dataForm.feeAgency }}</span>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
<el-row> <el-row>
<el-col :span="24"> <el-col :span="24">
<el-form-item label="审核备注" prop="auditRemarks"> <el-form-item label="审核备注" prop="auditRemarks">
@@ -101,7 +102,7 @@
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">
<el-button @click="visible = false">取消</el-button> <el-button @click="visible = false">取消</el-button>
<el-button type="success" @click="dataFormSubmit" v-if="canSubmit">确认修改</el-button> <el-button type="primary" @click="dataFormSubmit" v-if="canSubmit">确认修改</el-button>
</div> </div>
</template> </template>
</el-dialog> </el-dialog>
@@ -380,7 +381,12 @@ defineExpose({
}) })
</script> </script>
<style scoped> <style lang="scss" scoped>
.el-form {
.el-row:not(:last-of-type) {
margin-bottom: 18px !important;
}
}
.dialog-footer { .dialog-footer {
text-align: right; text-align: right;
} }

View File

@@ -1,19 +1,14 @@
<template> <template>
<el-dialog <el-dialog
title="家庭地址地图选点" title="家庭地址地图查看"
append-to-body append-to-body
:close-on-click-modal="false" :close-on-click-modal="false"
v-model="visible" v-model="visible"
width="90%"> width="90%">
<div style="height: 100%;width:100%"> <div style="height: 100%;width:100%">
<el-form :model="form" :rules="rules" ref="formRef" label-width="120px" <el-descriptions :column="1" border>
class="demo-ruleForm"> <el-descriptions-item label="家庭地址">{{ form.homeAddressDetail }}</el-descriptions-item>
</el-descriptions>
<el-form-item label="家庭地址" prop="homeAddressDetail">
<el-input v-model="form.homeAddressDetail" style="width: 100%"></el-input>
</el-form-item>
</el-form>
<div id="container2"></div> <div id="container2"></div>
</div> </div>
</el-dialog> </el-dialog>
@@ -21,36 +16,34 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, nextTick } from 'vue' import { ref, reactive, nextTick } from 'vue'
import { BMPGL } from "@/api/recruit/recruitstudentsignup" import { loadTiandituMap } from "/@/api/recruit/recruitstudentsignup"
import { getTypeValue } from "@/api/admin/dict" import { getDicts } from "/@/api/admin/dict"
import { ElMessage } from 'element-plus'
import { TIANDITU_TOKEN } from '/@/config/map'
// 表单引用 // 表单引用
const formRef = ref() const formRef = ref()
// 响应式数据 // 响应式数据
const ak = "V0ooaf2RZyEGOkD8UzZB3gvw7pCb0Kx7" // 百度的地图密钥 const tk = TIANDITU_TOKEN // 天地图的token在 src/config/map.ts 中配置)
const visible = ref(false) const visible = ref(false)
const canSubmit = ref(false) const canSubmit = ref(false)
const circleShow = ref(false) const circleShow = ref(false)
const circle = ref<any>(null) const circle = ref<any>(null)
const map = ref<any>(null)
// 地址信息 // 地址信息
const address = ref(null)
const center = reactive({ lng: 0, lat: 0 }) const center = reactive({ lng: 0, lat: 0 })
const dictId = ref("") const dictId = ref("")
const circleArr = ref<any[]>([])
const form = reactive({ const form = reactive({
id: "", id: "",
homeAddressDetail: "", homeAddressDetail: "",
homeLng: 0, // 家庭地址经度
homeLat: 0, // 家庭地址纬度
raidus: 0 raidus: 0
}) })
const rules = {
homeAddressDetail: [
{ required: true, message: '家庭地址不能为空', trigger: ["blur", "change"] }
],
}
// 初始化 // 初始化
const init = (row: any) => { const init = (row: any) => {
@@ -59,8 +52,11 @@ const init = (row: any) => {
circleShow.value = true circleShow.value = true
form.id = row.id form.id = row.id
form.homeAddressDetail = row.homeAddressDetail form.homeAddressDetail = row.homeAddressDetail
// 获取家庭地址的经纬度(如果有的话)
form.homeLng = row.homeLng || row.homeLongitude || 0
form.homeLat = row.homeLat || row.homeLatitude || 0
nextTick(() => { nextTick(() => {
getTypeValue("dorm_jw").then((data: any) => { getDicts("dorm_jw").then((data: any) => {
const arr = data.data const arr = data.data
arr.forEach((e: any) => { arr.forEach((e: any) => {
if (e.label == 'bj') { if (e.label == 'bj') {
@@ -72,26 +68,94 @@ const init = (row: any) => {
center.lat = e.value center.lat = e.value
} }
}) })
BMPGL(ak).then((BMapGL: any) => {
// 创建地图实例 // 等待对话框渲染完成后再加载地图
const map = new BMapGL.Map("container2") setTimeout(() => {
// 创建点坐标 loadTiandituMap(tk).then((T: any) => {
const point = new BMapGL.Point(center.lng, center.lat) // 清除之前的地图实例(如果存在)
// 初始化地图,设置中心点坐标和地图级别 if (map.value) {
map.centerAndZoom(point, 13) map.value.clearOverLays()
// 开启鼠标滚轮缩放
map.enableScrollWheelZoom(true)
// 创建地址解析器实例
const myGeo = new BMapGL.Geocoder()
myGeo.getPoint(form.homeAddressDetail, function (point: any) {
if (point) {
map.centerAndZoom(point, 16)
map.addOverlay(new BMapGL.Marker(point, { title: form.homeAddressDetail }))
} else {
alert('您选择的地址没有解析到结果!')
} }
}, '北京市')
}) // 创建地图实例
map.value = new T.Map("container2")
// 创建点坐标
const point = new T.LngLat(center.lng, center.lat)
// 初始化地图,设置中心点坐标和地图级别(使用默认矢量地图)
map.value.centerAndZoom(point, 13)
// 启用地图交互功能
map.value.enableDrag() // 启用拖拽
map.value.enableScrollWheelZoom() // 启用滚轮缩放
map.value.enableDoubleClickZoom() // 启用双击放大
map.value.enableKeyboard() // 启用键盘操作
// 使用天地图JavaScript API的Geocoder进行地址解析
if (form.homeAddressDetail) {
// eslint-disable-next-line no-console
console.log('开始地理编码,地址:', form.homeAddressDetail)
// 创建地理编码对象
const geocoder = new T.Geocoder()
// 进行地址解析
geocoder.getPoint(form.homeAddressDetail, (result: any) => {
if (result && result.getStatus() === 0) {
// 解析成功
const location = result.getLocationPoint()
// eslint-disable-next-line no-console
console.log('地理编码成功:', { lng: location.lng, lat: location.lat })
// 将地图中心移动到该位置
map.value.centerAndZoom(location, 15)
// 添加标记
const marker = new T.Marker(location)
map.value.addOverLay(marker)
// 添加信息窗口
const infoWin = new T.InfoWindow()
infoWin.setContent(`<div style="padding:12px;max-width:300px;">
<div style="font-size:14px;font-weight:bold;margin-bottom:8px;">📍 家庭地址</div>
<div style="color:#666;margin-bottom:6px;">${form.homeAddressDetail}</div>
<div style="font-size:12px;color:#999;padding-top:6px;border-top:1px solid #eee;">
坐标: ${location.lng.toFixed(6)}, ${location.lat.toFixed(6)}
</div>
</div>`)
marker.addEventListener('click', function () {
marker.openInfoWindow(infoWin)
})
// 自动打开信息窗口
marker.openInfoWindow(infoWin)
} else {
// 解析失败,显示学校中心位置
// eslint-disable-next-line no-console
console.log('地理编码失败,显示学校中心位置')
ElMessage.warning('地址解析失败')
}
})
} else {
// 没有地址信息
const marker = new T.Marker(point)
map.value.addOverLay(marker)
const infoWin = new T.InfoWindow()
infoWin.setContent(`<div style="padding:12px;max-width:300px;">
<div style="font-size:14px;font-weight:bold;margin-bottom:8px;">📍 学校位置</div>
<div style="font-size:12px;color:#999;">暂无家庭地址信息</div>
</div>`)
marker.addEventListener('click', function () {
marker.openInfoWindow(infoWin)
})
}
}).catch(() => {
ElMessage.error('地图加载失败请检查网络连接或Token配置')
})
}, 200)
}).catch(() => {
ElMessage.error('获取地图配置失败')
}) })
}) })
} }
@@ -109,6 +173,7 @@ defineExpose({
height: 700px; height: 700px;
margin: 0; margin: 0;
font-family: "微软雅黑"; font-family: "微软雅黑";
margin-top: 15px;
} }
ul li { ul li {

View File

@@ -1,6 +1,6 @@
<template> <template>
<el-dialog <el-dialog
:title="form.id ? '缂傛牞绶? : '閺傛澘顤?" :title="form.id ? '编辑' : '新增'"
v-model="visible" v-model="visible"
:close-on-click-modal="false" :close-on-click-modal="false"
draggable draggable
@@ -13,13 +13,13 @@
:validate-on-rule-change="false" :validate-on-rule-change="false"
v-loading="loading"> v-loading="loading">
<el-row :gutter="24"> <el-row :gutter="24">
<!-- 缂傛牞绶弮鑸垫鍝勵劅闂?--> <!-- 编辑时显示学院 -->
<el-col :span="12" class="mb20"> <el-col :span="12" class="mb20">
<el-form-item label="閻濐厼褰? prop="classCode"> <el-form-item label="班号" prop="classCode">
<el-select <el-select
v-model="form.classCode" v-model="form.classCode"
placeholder="鐠囩兘鈧瀚ㄩ悵顓炲娇" placeholder="请选择班号"
clearable clearable
filterable filterable
style="width: 100%" > style="width: 100%" >
@@ -34,11 +34,11 @@
</el-col> </el-col>
<el-col :span="12" class="mb20"> <el-col :span="12" class="mb20">
<el-form-item label="濡偓閺屻儲妫╅張? prop="recordDate"> <el-form-item label="检查日期" prop="recordDate">
<el-date-picker <el-date-picker
v-model="form.recordDate" v-model="form.recordDate"
type="date" type="date"
placeholder="闁瀚ㄥΛ鈧弻銉︽)閺? placeholder="选择检查日期"
format="YYYY-MM-DD" format="YYYY-MM-DD"
value-format="YYYY-MM-DD" value-format="YYYY-MM-DD"
style="width: 100%" /> style="width: 100%" />
@@ -46,24 +46,24 @@
</el-col> </el-col>
<el-col :span="12" class="mb20"> <el-col :span="12" class="mb20">
<el-form-item label="閹碉絽鍨? prop="score"> <el-form-item label="扣分" prop="score">
<el-input-number <el-input-number
v-model="form.score" v-model="form.score"
:precision="0" :precision="0"
:step="1" :step="1"
:min="0" :min="0"
placeholder="鐠囩柉绶崗銉﹀⒏閸? placeholder="请输入扣分"
style="width: 100%" /> style="width: 100%" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24" class="mb20"> <el-col :span="24" class="mb20">
<el-form-item label="濡偓閺屻儴顔囪ぐ? prop="note"> <el-form-item label="检查记录" prop="note">
<el-input <el-input
v-model="form.note" v-model="form.note"
type="textarea" type="textarea"
:rows="4" :rows="4"
placeholder="鐠囩柉绶崗銉︻梾閺屻儴顔囪ぐ? placeholder="请输入检查记录"
maxlength="500" maxlength="500"
show-word-limit /> show-word-limit />
</el-form-item> </el-form-item>
@@ -72,14 +72,14 @@
</el-form> </el-form>
<template #footer> <template #footer>
<span class="dialog-footer"> <span class="dialog-footer">
<el-button @click="visible = false">??/el-button> <el-button @click="visible = false"> </el-button>
<el-button type="primary" @click="onSubmit" :disabled="loading">??/el-button> <el-button type="primary" @click="onSubmit" :disabled="loading"> </el-button>
</span> </span>
</template> </template>
</el-dialog> </el-dialog>
</template> </template>
<script setup lang="ts" name="classroomhygienedailyDialog"> <script setup lang="ts" name="ClassRoomHygieneDailyDialog">
import { ref, reactive, nextTick, computed, onMounted } from 'vue' import { ref, reactive, nextTick, computed, onMounted } from 'vue'
import { useMessage } from "/@/hooks/message"; import { useMessage } from "/@/hooks/message";
import { getObj, addObj, putObj } from '/@/api/stuwork/classroomhygienedaily' import { getObj, addObj, putObj } from '/@/api/stuwork/classroomhygienedaily'
@@ -88,14 +88,14 @@ import { getClassListByRole, queryClassByDeptCode } from '/@/api/basic/basicclas
const emit = defineEmits(['refresh']); const emit = defineEmits(['refresh']);
// 鐎规矮绠熼崣姗€鍣洪崘鍛啇 // 定义变量内容
const dataFormRef = ref(); const dataFormRef = ref();
const visible = ref(false) const visible = ref(false)
const loading = ref(false) const loading = ref(false)
const deptList = ref<any[]>([]) const deptList = ref<any[]>([])
const classList = ref<any[]>([]) const classList = ref<any[]>([])
// 閹绘劒姘︾悰銊ュ礋閺佺増宓? // 提交表单数据
const form = reactive({ const form = reactive({
id: '', id: '',
deptCode: '', deptCode: '',
@@ -105,7 +105,7 @@ const form = reactive({
note: '' note: ''
}); });
// 閺嶈宓佺€涳箓娅岀粵娑⑩偓澶屽疆缁狙冨灙鐞? // 根据学院筛选班级列表
const filteredClassList = computed(() => { const filteredClassList = computed(() => {
if (!form.deptCode) { if (!form.deptCode) {
return [] return []
@@ -113,36 +113,36 @@ const filteredClassList = computed(() => {
return classList.value.filter((item: any) => item.deptCode === form.deptCode) return classList.value.filter((item: any) => item.deptCode === form.deptCode)
}) })
// 鐎规矮绠熼弽锟犵崣鐟欏嫬鍨? // 定义校验规则
const dataRules = computed(() => ({ const dataRules = computed(() => ({
deptCode: form.id ? [ deptCode: form.id ? [
{ required: true, message: '鐎涳箓娅屾稉宥堝厴娑撹櫣鈹?, trigger: 'change' } { required: true, message: '学院不能为空', trigger: 'change' }
] : [], ] : [],
classCode: [ classCode: [
{ required: true, message: '閻濐厼褰挎稉宥堝厴娑撹櫣鈹?, trigger: 'change' } { required: true, message: '班号不能为空', trigger: 'change' }
], ],
recordDate: [ recordDate: [
{ required: true, message: '濡偓閺屻儲妫╅張鐔剁瑝閼虫垝璐熺粚?, trigger: 'blur' } { required: true, message: '检查日期不能为空', trigger: 'blur' }
], ],
score: [ score: [
{ required: true, message: '閹碉絽鍨庢稉宥堝厴娑撹櫣鈹?, trigger: 'blur' } { required: true, message: '扣分不能为空', trigger: 'blur' }
], ],
note: [ note: [
{ required: true, message: '濡偓閺屻儴顔囪ぐ鏇氱瑝閼虫垝璐熺粚?, trigger: 'blur' } { required: true, message: '检查记录不能为空', trigger: 'blur' }
] ]
})) }))
// 鐎涳箓娅岄柅澶嬪閸欐ê瀵? // 学院选择变化
const handleDeptChange = () => { const handleDeptChange = () => {
// 濞撳懐鈹栭悵顓炲娇闁瀚? // 清空班号选择
form.classCode = '' form.classCode = ''
} }
// 閹垫挸绱戝鍦崶 // 打开弹窗
const openDialog = (id?: string) => { const openDialog = (id?: string) => {
visible.value = true visible.value = true
// 闁插秶鐤嗙悰銊ュ礋閺佺増宓? // 重置表单数据
Object.assign(form, { Object.assign(form, {
id: '', id: '',
deptCode: '', deptCode: '',
@@ -152,25 +152,25 @@ const openDialog = (id?: string) => {
note: '' note: ''
}) })
// 濞撳懘娅庣悰銊ュ礋妤犲矁鐦夐悩鑸碘偓渚婄礉娑撳秷袝閸欐垿鐛欑拠? // 清除表单验证状态,不触发验证
nextTick(() => { nextTick(() => {
dataFormRef.value?.clearValidate(); dataFormRef.value?.clearValidate();
dataFormRef.value?.resetFields(); dataFormRef.value?.resetFields();
}); });
// 閼惧嘲褰囩拠锔藉剰 // 获取详情
if (id) { if (id) {
form.id = id form.id = id
getclassroomhygienedailyData(id) getClassRoomHygieneDailyData(id)
} }
// 閺傛澘顤冮弮璁圭礉绾喕绻氭宀冪槈閻樿埖鈧礁鍑″〒鍛存珟 // 新增时,确保验证状态已清除
nextTick(() => { nextTick(() => {
dataFormRef.value?.clearValidate(); dataFormRef.value?.clearValidate();
}); });
}; };
// 閹绘劒姘? // 提交
const onSubmit = async () => { const onSubmit = async () => {
const valid = await dataFormRef.value.validate().catch(() => {}); const valid = await dataFormRef.value.validate().catch(() => {});
if (!valid) return false; if (!valid) return false;
@@ -178,22 +178,22 @@ const onSubmit = async () => {
try { try {
loading.value = true; loading.value = true;
form.id ? await putObj(form) : await addObj(form); form.id ? await putObj(form) : await addObj(form);
useMessage().success(form.id ? '娣囶喗鏁奸幋鎰' : '濞h濮為幋鎰'); useMessage().success(form.id ? '修改成功' : '添加成功');
visible.value = false; visible.value = false;
emit('refresh'); emit('refresh');
} catch (err: any) { } catch (err: any) {
useMessage().error(err.msg || '閹垮秳缍旀径杈Е'); useMessage().error(err.msg || '操作失败');
} finally { } finally {
loading.value = false; loading.value = false;
} }
}; };
// 閸掓繂顫愰崠鏍€冮崡鏇熸殶閹? // 初始化表单数据
const getclassroomhygienedailyData = (id: string) => { const getClassRoomHygieneDailyData = (id: string) => {
loading.value = true loading.value = true
getObj({ id: id }).then((res: any) => { getObj({ id: id }).then((res: any) => {
if (res.data) { if (res.data) {
// 婢跺嫮鎮婃潻鏂挎礀閺佺増宓侀敍灞藉讲閼宠姤妲哥€电钖勯幋鏍ㄦ殶缂? // 处理返回数据,可能是对象或数组
const data = Array.isArray(res.data) ? res.data[0] : res.data const data = Array.isArray(res.data) ? res.data[0] : res.data
if (data) { if (data) {
Object.assign(form, { Object.assign(form, {
@@ -205,7 +205,7 @@ const getclassroomhygienedailyData = (id: string) => {
note: data.note || '' note: data.note || ''
}) })
// 婵″倹鐏夌紓鏍帆閺?deptCode 娑撹櫣鈹栭敍灞肩稻 classCode 閺堝鈧》绱濈亸婵婄槸娴?classList 娑擃厽澹橀崚鏉款嚠鎼存梻娈?deptCode // 如果编辑时 deptCode 为空,但 classCode 有值,尝试从 classList 中找到对应的 deptCode
if (!form.deptCode && form.classCode && classList.value.length > 0) { if (!form.deptCode && form.classCode && classList.value.length > 0) {
const classItem = classList.value.find((item: any) => item.classCode === form.classCode) const classItem = classList.value.find((item: any) => item.classCode === form.classCode)
if (classItem && classItem.deptCode) { if (classItem && classItem.deptCode) {
@@ -213,21 +213,21 @@ const getclassroomhygienedailyData = (id: string) => {
} }
} }
// 閺佺増宓侀崝鐘烘祰鐎瑰本鍨氶崥搴礉濞撳懘娅庢宀冪槈閻樿埖鈧緤绱濋柆鍨帳鐟欙箑褰傛宀冪槈 // 数据加载完成后,清除验证状态,避免触发验证
nextTick(() => { nextTick(() => {
dataFormRef.value?.clearValidate(); dataFormRef.value?.clearValidate();
}); });
} }
} }
}).catch((err: any) => { }).catch((err: any) => {
console.error('閼惧嘲褰囩拠锔藉剰婢惰精瑙?, err) console.error('获取详情失败', err)
useMessage().error('閼惧嘲褰囩拠锔藉剰婢惰精瑙?) useMessage().error('获取详情失败')
}).finally(() => { }).finally(() => {
loading.value = false loading.value = false
}) })
} }
// 閼惧嘲褰囩€涳箓娅岄崚妤勩€? // 获取学院列表
const getDeptListData = async () => { const getDeptListData = async () => {
try { try {
const res = await getDeptList() const res = await getDeptList()
@@ -235,12 +235,12 @@ const getDeptListData = async () => {
deptList.value = Array.isArray(res.data) ? res.data : [] deptList.value = Array.isArray(res.data) ? res.data : []
} }
} catch (err) { } catch (err) {
console.error('閼惧嘲褰囩涳箓娅岄崚妤勩冩径杈Е', err) console.error('获取学院列表失败', err)
deptList.value = [] deptList.value = []
} }
} }
// 閼惧嘲褰囬悵顓炲娇閸掓銆? // 获取班号列表
const getClassListData = async () => { const getClassListData = async () => {
try { try {
const res = await getClassListByRole() const res = await getClassListByRole()
@@ -248,18 +248,18 @@ const getClassListData = async () => {
classList.value = Array.isArray(res.data) ? res.data : [] classList.value = Array.isArray(res.data) ? res.data : []
} }
} catch (err) { } catch (err) {
console.error('閼惧嘲褰囬悵顓炲娇閸掓銆冩径杈Е', err) console.error('获取班号列表失败', err)
classList.value = [] classList.value = []
} }
} }
// 閸掓繂顫愰崠? // 初始化
onMounted(() => { onMounted(() => {
getDeptListData() getDeptListData()
getClassListData() getClassListData()
}) })
// 閺嗘挳婀堕崣姗€鍣? // 暴露变量
defineExpose({ defineExpose({
openDialog openDialog
}); });

View File

@@ -1,3 +1,4 @@
<!-- RISE -->
<template> <template>
<el-dialog <el-dialog
:title="form.id ? '编辑' : '新增'" :title="form.id ? '编辑' : '新增'"