diff --git a/src/components/AttachmentUpload/index.js b/src/components/AttachmentUpload/index.js index db65e5d..16f6684 100644 --- a/src/components/AttachmentUpload/index.js +++ b/src/components/AttachmentUpload/index.js @@ -1,10 +1,10 @@ -import { Form, Image, Upload } from "antd"; +import { Form, Image, Upload, message } from "antd"; import { useState } from "react"; import { PlusOutlined } from "@ant-design/icons"; const isImage = (url) => /\.(png|jpe?g|gif|bmp|webp|svg)(\?.*)?$/i.test(url || ""); -export default function AttachmentUpload({ name, label, disabled = false, maxCount, accept }) { +export default function AttachmentUpload({ name, label, disabled = false, maxCount, accept, extra }) { const [previewImage, setPreviewImage] = useState(""); return ( @@ -12,25 +12,31 @@ export default function AttachmentUpload({ name, label, disabled = false, maxCou { - if (Array.isArray(value)) { return { fileList: value }; } - return { - fileList: value - ? value.split(",").map((item) => ({ - url: item, - uid: item, - status: "done", - name: "附件", - })) - : [], - }; + if (typeof value === "string" && value) { + try { + const parsed = JSON.parse(value); + if (Array.isArray(parsed)) { + return { fileList: parsed }; + } + } catch {} + return { + fileList: value.split(",").map((item) => ({ + url: item, + uid: item, + status: "done", + name: "附件", + })), + }; + } + return { fileList: [] }; }} getValueFromEvent={({ fileList }) => { - return ( fileList?.map((file) => ({ url: file.response?.data?.url || file.url, @@ -43,11 +49,32 @@ export default function AttachmentUpload({ name, label, disabled = false, maxCou }} > { + if (!accept) return true; + const rules = accept.split(",").map((e) => e.trim().toLowerCase()); + const ext = "." + file.name.split(".").pop().toLowerCase(); + const matchExt = rules.includes(ext); + const matchMime = rules.some((r) => { + if (r.endsWith("/*")) { + const prefix = r.slice(0, -1); + return file.type && file.type.toLowerCase().startsWith(prefix); + } + return false; + }); + if (!matchExt && !matchMime) { + message.error(`仅支持 ${accept} 格式文件`); + return Upload.LIST_IGNORE; + } + return true; + }} onPreview={(file) => { if (isImage(file.url || file.thumbUrl)) { setPreviewImage(file.url || file.thumbUrl); diff --git a/src/components/BaiduMapPicker/index.js b/src/components/BaiduMapPicker/index.js new file mode 100644 index 0000000..cc01e97 --- /dev/null +++ b/src/components/BaiduMapPicker/index.js @@ -0,0 +1,136 @@ +import React, { useEffect, useRef, useState } from 'react'; +import { Modal, Input, message } from 'antd'; +import './index.less'; + +const BaiduMapPicker = ({ visible, onCancel, onConfirm }) => { + const mapRef = useRef(null); + const mapInstanceRef = useRef(null); + const markerRef = useRef(null); + const [loaded, setLoaded] = useState(false); + const [selectedPoint, setSelectedPoint] = useState(null); + + // 动态加载百度地图脚本 + useEffect(() => { + if (!visible) return; + if (window.BMap) { + // 如果已有 BMap,等 DOM 渲染后再初始化 + setTimeout(() => setLoaded(true), 100); + return; + } + + const scriptId = 'baiduMapScript'; + if (document.getElementById(scriptId)) return; + + window.__onBMapLoaded = () => { + setLoaded(true); + }; + + const script = document.createElement('script'); + script.id = scriptId; + script.src = `https://api.map.baidu.com/api?v=3.0&ak=6lkWDSnHTCcvUenZhovQPER9nkhFfT9A&callback=__onBMapLoaded`; + script.async = true; + document.body.appendChild(script); + }, [visible]); + + // 初始化地图 + useEffect(() => { + if (!loaded || !visible || !mapRef.current) return; + // 避免重复初始化 + if (mapInstanceRef.current) return; + + const map = new window.BMap.Map(mapRef.current); + // 默认以重庆市中心为中心 + map.centerAndZoom(new window.BMap.Point(106.55, 29.57), 12); + map.enableScrollWheelZoom(true); + mapInstanceRef.current = map; + + // 点击地图获取坐标 + map.addEventListener('click', (e) => { + const point = e.point; + setSelectedPoint(point); + if (markerRef.current) { + map.removeOverlay(markerRef.current); + } + const marker = new window.BMap.Marker(point); + map.addOverlay(marker); + markerRef.current = marker; + }); + + // 搜索自动完成 + const ac = new window.BMap.Autocomplete({ + input: 'baiduSearchInput', + location: map, + }); + ac.addEventListener('onconfirm', (e) => { + const value = e.item.value; + const local = new window.BMap.LocalSearch(map, { + onSearchComplete: (results) => { + if (results.getCurrentNumPois() > 0) { + const poi = results.getPoi(0); + const point = poi.point; + setSelectedPoint(point); + map.centerAndZoom(point, 15); + if (markerRef.current) { + map.removeOverlay(markerRef.current); + } + const marker = new window.BMap.Marker(point); + map.addOverlay(marker); + markerRef.current = marker; + } + }, + }); + local.search( + [value.province, value.city, value.district, value.street, value.business] + .filter(Boolean) + .join(''), + ); + }); + + return () => { + mapInstanceRef.current = null; + }; + }, [loaded, visible]); + + // 弹窗关闭时重置 + useEffect(() => { + if (!visible) { + setLoaded(false); + setSelectedPoint(null); + markerRef.current = null; + mapInstanceRef.current = null; + } + }, [visible]); + + const handleConfirm = () => { + if (!selectedPoint) { + message.warning('请在地图上选择位置'); + return; + } + onConfirm({ + lng: selectedPoint.lng, + lat: selectedPoint.lat, + }); + }; + + return ( + + +
+ + ); +}; + +export default BaiduMapPicker; diff --git a/src/components/BaiduMapPicker/index.less b/src/components/BaiduMapPicker/index.less new file mode 100644 index 0000000..cc8763f --- /dev/null +++ b/src/components/BaiduMapPicker/index.less @@ -0,0 +1,33 @@ +.baidu-map-search { + width: 100%; + height: 36px; + padding: 4px 11px; + border: 1px solid #d9d9d9; + border-radius: 6px; + outline: none; + box-sizing: border-box; + margin-bottom: 12px; + font-size: 14px; + transition: border-color 0.2s; + + &:focus { + border-color: #4096ff; + box-shadow: 0 0 0 2px rgba(5, 145, 255, 0.1); + } + + &::placeholder { + color: #bfbfbf; + } +} + +.baidu-map-container { + width: 100%; + height: 480px; + border-radius: 6px; + overflow: hidden; +} + +/* 百度地图 Autocomplete 联想面板默认 z-index 过低,被 Modal 遮挡 */ +.tangram-suggestion-main { + z-index: 10000 !important; +} diff --git a/src/pages/Container/RegisterMore/index.js b/src/pages/Container/RegisterMore/index.js index afdbde9..588cd00 100644 --- a/src/pages/Container/RegisterMore/index.js +++ b/src/pages/Container/RegisterMore/index.js @@ -15,7 +15,6 @@ import { InputNumber, DatePicker, Space, - Modal, } from "antd"; import { NS_REGISTER } from "~/enumerate/namespace"; import AttachmentUpload from "~/components/AttachmentUpload"; @@ -40,6 +39,7 @@ import { REGISTERED_ORG_FILING_TYPE_SEARCH_OPTIONS, REGISTERED_ORG_FILING_RECORD_STATUS_OPTIONS, } from "~/enumerate/enterpriseOptions"; +import BaiduMapPicker from "~/components/BaiduMapPicker"; import "./index.less"; const { router } = tools; @@ -47,6 +47,7 @@ const { router } = tools; const RegisterMore = (props) => { const { orgInfoSave, register, syncUserToGBS } = props; const [current, setCurrent] = useState(0); + const [mapPickerVisible, setMapPickerVisible] = useState(false); const [form] = Form.useForm(); const { registerLoading, syncUserToGBSLoading } = register; const hasRegisterContext = Boolean( @@ -109,6 +110,11 @@ const RegisterMore = (props) => { } }; + const handleMapConfirm = ({ lng, lat }) => { + form.setFieldsValue({ longitude: lng, latitude: lat }); + setMapPickerVisible(false); + }; + const handleLogin = () => { message.success("即将跳转到登录页面,请稍候..."); setTimeout(() => { @@ -173,11 +179,11 @@ const RegisterMore = (props) => {
- + { { required: true, message: "请输入生产经营单位名称" }, ]} > - + @@ -194,7 +204,11 @@ const RegisterMore = (props) => { label="统一社会信用代码" rules={[creditCodeRule(true)]} > - + @@ -235,47 +249,64 @@ const RegisterMore = (props) => { label="所属镇、街道" rules={[{ required: true, message: "请输入所属镇街道" }]} > - + - + - - + + + + + + + + @@ -319,7 +350,11 @@ const RegisterMore = (props) => { label="注册地址" rules={[{ required: true, message: "请输入注册地址" }]} > - + @@ -328,12 +363,20 @@ const RegisterMore = (props) => { label="经营地址" rules={[{ required: true, message: "请输入经营地址" }]} > - + - + @@ -341,7 +384,11 @@ const RegisterMore = (props) => { name="economyIndustryCode" label="国民经济行业分类(GB/T4754-2017)" > - + @@ -350,7 +397,11 @@ const RegisterMore = (props) => { label="法定代表人" rules={[{ required: true, message: "请输入法定代表人" }]} > - + @@ -359,7 +410,11 @@ const RegisterMore = (props) => { label="法定代表人联系电话" rules={[phoneRule("法定代表人联系电话", false)]} > - + @@ -368,7 +423,11 @@ const RegisterMore = (props) => { label="主要负责人" rules={[{ required: true, message: "请输入主要负责人" }]} > - + @@ -377,7 +436,11 @@ const RegisterMore = (props) => { label="主要负责人联系电话" rules={[phoneRule("主要负责人联系电话", true)]} > - + @@ -385,7 +448,11 @@ const RegisterMore = (props) => { name="safetyDeptManager" label="安全管理部门负责人" > - + @@ -401,7 +468,7 @@ const RegisterMore = (props) => { /> - + { - + @@ -434,7 +505,11 @@ const RegisterMore = (props) => { label="信息公开网址" rules={[urlRule("信息公开网址", false)]} > - + @@ -446,6 +521,7 @@ const RegisterMore = (props) => { @@ -460,6 +536,7 @@ const RegisterMore = (props) => { @@ -476,6 +553,7 @@ const RegisterMore = (props) => { @@ -492,6 +570,7 @@ const RegisterMore = (props) => { @@ -504,7 +583,10 @@ const RegisterMore = (props) => { placeholder="请选择企业状态" allowClear onChange={(value, option) => { - form.setFieldValue("enterpriseStatusName", option?.label || ""); + form.setFieldValue( + "enterpriseStatusName", + option?.label || "", + ); }} /> @@ -517,7 +599,10 @@ const RegisterMore = (props) => { placeholder="请选择企业规模" allowClear onChange={(value, option) => { - form.setFieldValue("enterpriseScaleName", option?.label || ""); + form.setFieldValue( + "enterpriseScaleName", + option?.label || "", + ); }} /> @@ -553,10 +638,21 @@ const RegisterMore = (props) => { - +
+ setMapPickerVisible(false)} + onConfirm={handleMapConfirm} + /> {/*