fix: 修复地图坐标校验与解析

- 小程序兼容字符串坐标并自动纠正历史经纬度反向数据

- 后台机构资料页增加经纬度与地址联动校验

- org 模块保存机构资料时拦截非法坐标输入
This commit is contained in:
2026-03-21 11:17:02 +08:00
parent 74543cb9bf
commit 728847a8e3
3 changed files with 218 additions and 19 deletions

View File

@@ -1,5 +1,6 @@
<script setup lang="ts">
import { computed, onMounted, reactive, ref } from 'vue'
import type { FormInstance, FormRules } from 'element-plus'
import { ElMessage } from 'element-plus'
import type { UploadRequestOptions } from 'element-plus'
import { Plus } from '@element-plus/icons-vue'
@@ -19,6 +20,7 @@ function generateDeptCode() {
const loading = ref(false)
const saving = ref(false)
const formRef = ref<FormInstance>()
const imageUploading = reactive({
logo: false,
hero: false,
@@ -72,6 +74,95 @@ const deptOptions = computed(() => departments.value.map(item => ({
value: item.id,
})))
function normalizeText(value: string | null | undefined) {
return typeof value === 'string' ? value.trim() : ''
}
function parseCoordinate(value: string | null | undefined) {
const normalized = normalizeText(value)
if (!normalized) {
return null
}
const parsed = Number(normalized)
return Number.isFinite(parsed) ? parsed : null
}
function hasCoordinateInput() {
return Boolean(normalizeText(profile.hqLatitude) || normalizeText(profile.hqLongitude))
}
function getCoordinateValidationMessage() {
const latitudeText = normalizeText(profile.hqLatitude)
const longitudeText = normalizeText(profile.hqLongitude)
const hasLatitude = Boolean(latitudeText)
const hasLongitude = Boolean(longitudeText)
if (!hasLatitude && !hasLongitude) {
return ''
}
if (!hasLatitude || !hasLongitude) {
return '纬度和经度需同时填写'
}
const latitude = parseCoordinate(profile.hqLatitude)
if (latitude === null) {
return '纬度必须是数字'
}
const longitude = parseCoordinate(profile.hqLongitude)
if (longitude === null) {
return '经度必须是数字'
}
if (latitude < -90 || latitude > 90) {
if (latitude >= -180 && latitude <= 180 && longitude >= -90 && longitude <= 90) {
return '纬度超出范围,请检查是否与经度填反'
}
return '纬度范围应在 -90 到 90 之间'
}
if (longitude < -180 || longitude > 180) {
return '经度范围应在 -180 到 180 之间'
}
if (!normalizeText(profile.hqAddress)) {
return '填写地图坐标时请同时填写详细地址'
}
return ''
}
const profileRules: FormRules = {
firmName: [{ required: true, message: '请输入机构名称', trigger: 'blur' }],
hqAddress: [{
trigger: ['blur', 'change'],
validator: async () => {
if (hasCoordinateInput() && !normalizeText(profile.hqAddress)) {
throw new Error('填写地图坐标时请同时填写详细地址')
}
},
}],
hqLatitude: [{
trigger: ['blur', 'change'],
validator: async () => {
const message = getCoordinateValidationMessage()
if (message) {
throw new Error(message)
}
},
}],
hqLongitude: [{
trigger: ['blur', 'change'],
validator: async () => {
const message = getCoordinateValidationMessage()
if (message) {
throw new Error(message)
}
},
}],
}
async function loadData() {
loading.value = true
try {
@@ -135,6 +226,11 @@ function handleHeroUpload(options: UploadRequestOptions) {
}
async function saveProfile() {
const valid = await formRef.value?.validate().then(() => true).catch(() => false)
if (!valid) {
return
}
saving.value = true
try {
const saved = await tenantApi.saveFirmProfile(profile)
@@ -267,9 +363,15 @@ onMounted(() => {
</div>
</div>
<el-form label-position="top" class="card-form material-form">
<el-form
ref="formRef"
:model="profile"
:rules="profileRules"
label-position="top"
class="card-form material-form"
>
<div class="form-grid">
<el-form-item label="名称">
<el-form-item label="名称" prop="firmName">
<el-input v-model="profile.firmName" class="material-input" placeholder="完整的机构注册名称" />
</el-form-item>
<el-form-item label="简称">
@@ -284,13 +386,13 @@ onMounted(() => {
<el-form-item label="官网地址">
<el-input v-model="profile.websiteUrl" class="material-input" />
</el-form-item>
<el-form-item label="详细地址" class="form-grid__full">
<el-form-item label="详细地址" prop="hqAddress" class="form-grid__full">
<el-input v-model="profile.hqAddress" class="material-input" placeholder="完整的真实办公地址" />
</el-form-item>
<el-form-item label="纬度 (LAT)">
<el-form-item label="纬度 (LAT)" prop="hqLatitude">
<el-input v-model="profile.hqLatitude" class="material-input" placeholder="例如31.230416" />
</el-form-item>
<el-form-item label="经度 (LNG)">
<el-form-item label="经度 (LNG)" prop="hqLongitude">
<el-input v-model="profile.hqLongitude" class="material-input" placeholder="例如121.473701" />
</el-form-item>
<el-form-item label="机构简介" class="form-grid__full">