From 16a8656b490dae559104dcd8f878a455d4ca89b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B6=85=E7=BA=A7=E8=80=81=E7=99=BD=E5=85=94?= Date: Thu, 20 Nov 2025 15:52:05 +0800 Subject: [PATCH 01/15] =?UTF-8?q?=E4=BB=8E=E6=B6=88=E6=81=AF=E6=8E=A8?= =?UTF-8?q?=E9=80=81=E5=8A=A9=E6=89=8B=E4=BB=BB=E5=8A=A1=E5=88=9B=E5=BB=BA?= =?UTF-8?q?=E4=B8=AD=E5=88=A0=E9=99=A4=E5=BC=83=E7=94=A8=E7=9A=84=E6=8F=90?= =?UTF-8?q?=E7=A4=BA=E7=A4=BA=E4=BE=8B=EF=BC=8C=E6=9B=B4=E6=96=B0=E6=8E=A8?= =?UTF-8?q?=E9=80=81=E5=8E=86=E5=8F=B2API=E4=BB=A5=E4=BD=BF=E7=94=A8pushTy?= =?UTF-8?q?peCode=E5=92=8C=E7=8A=B6=E6=80=81=EF=BC=8C=E5=B9=B6=E9=80=9A?= =?UTF-8?q?=E8=BF=87=E6=94=B9=E8=BF=9B=E7=9A=84=E7=B1=BB=E5=9E=8B=E5=A4=84?= =?UTF-8?q?=E7=90=86=E5=92=8CUI=E8=B0=83=E6=95=B4=E6=9D=A5=E5=A2=9E?= =?UTF-8?q?=E5=BC=BAPushHistory=E7=BB=84=E4=BB=B6=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../StepSendMessage/index.module.scss | 1 + .../components/StepSendMessage/index.tsx | 4 - .../create-push-task/提示词.txt | 79 --------- .../pc/ckbox/powerCenter/push-history/api.ts | 33 +++- .../ckbox/powerCenter/push-history/index.tsx | 159 +++++++++++++----- 5 files changed, 149 insertions(+), 127 deletions(-) delete mode 100644 Touchkebao/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/提示词.txt diff --git a/Touchkebao/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/components/StepSendMessage/index.module.scss b/Touchkebao/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/components/StepSendMessage/index.module.scss index f7c7d1a4..0896f840 100644 --- a/Touchkebao/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/components/StepSendMessage/index.module.scss +++ b/Touchkebao/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/components/StepSendMessage/index.module.scss @@ -32,6 +32,7 @@ .rightColumn { flex: 1; + max-width: 500px; display: flex; flex-direction: column; gap: 20px; diff --git a/Touchkebao/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/components/StepSendMessage/index.tsx b/Touchkebao/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/components/StepSendMessage/index.tsx index ab0eefe1..82f109e7 100644 --- a/Touchkebao/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/components/StepSendMessage/index.tsx +++ b/Touchkebao/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/components/StepSendMessage/index.tsx @@ -746,10 +746,6 @@ const StepSendMessage: React.FC = ({ /> -
- {group.messages[0]} - {group.messages.length > 1 && " ..."} -
)) )} diff --git a/Touchkebao/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/提示词.txt b/Touchkebao/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/提示词.txt deleted file mode 100644 index df1db7a1..00000000 --- a/Touchkebao/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/提示词.txt +++ /dev/null @@ -1,79 +0,0 @@ -帮我对接数据,以下是传参实例,三种模式都是同一界面的。 - -群发助手传参实例 -{ - "name": "群群发-新品宣传", // 任务名称 - "type": 3, // 工作台类型:3=群消息推送 - "autoStart": 1, // 保存后自动启动 - "status": 1, // 是否启用 - "pushType": 0, // 推送方式:0=定时,1=立即 - "targetType": 1, // 目标类型:1=群推送 - "groupPushSubType": 1, // 群推送子类型:1=群群发,2=群公告 - "startTime": "09:00", // 推送起始时间 - "endTime": "20:00", // 推送结束时间 - "maxPerDay": 200, // 每日最大推送群数 - "pushOrder": 1, // 推送顺序:1=最早优先,2=最新优先 - "wechatGroups": [102, 205, 318], // 选择的微信群 ID 列表 - "contentGroups": [11, 12], // 关联内容库 ID 列表 - "friendIntervalMin": 10, // 群间最小间隔(秒) - "friendIntervalMax": 25, // 群间最大间隔(秒) - "messageIntervalMin": 2, // 同一群消息间最小间隔(秒) - "messageIntervalMax": 6, // 同一群消息间最大间隔(秒) - "isRandomTemplate": 1, // 是否随机选择话术模板 - "postPushTags": [301, 302], // 推送完成后打的标签 - ownerWechatIds:[123123,1231231] //客服id -} - -//群公告传参实例 -{ - "name": "群公告-双11活动", // 任务名称 - "type": 3, // 群消息推送 - "autoStart": 0, // 不自动启动 - "status": 1, // 启用 - "pushType": 1, // 立即推送 - "targetType": 1, // 群推送 - "groupPushSubType": 2, // 群公告 - "startTime": "08:30", // 开始时间 - "endTime": "18:30", // 结束时间 - "maxPerDay": 80, // 每日最大公告数 - "pushOrder": 2, // 最新优先 - "wechatGroups": [5021, 5026], // 公告目标群 - "announcementContent": "…", // 公告正文 - "enableAiRewrite": 1, // 启用 AI 改写 - "aiRewritePrompt": "保持活泼口吻…", // AI 改写提示词 - "contentGroups": [21], // 关联内容库 - "friendIntervalMin": 15, // 群间最小间隔 - "friendIntervalMax": 30, // 群间最大间隔 - "messageIntervalMin": 3, // 消息间最小间隔 - "messageIntervalMax": 9, // 消息间最大间隔 - "isRandomTemplate": 0, // 不随机模板 - "postPushTags": [], // 推送后标签 - ownerWechatIds:[123123,1231231] //客服id -} - -//好友传参实例 -{ - "name": "好友私聊-新客转化", // 任务名称 - "type": 3, // 群消息推送 - "autoStart": 1, // 自动启动 - "status": 1, // 启用 - "pushType": 0, // 定时推送 - "targetType": 2, // 目标类型:2=好友推送 - "groupPushSubType": 1, // 固定为群群发(好友推送不支持公告) - "startTime": "10:00", // 开始时间 - "endTime": "22:00", // 结束时间 - "maxPerDay": 150, // 每日最大推送好友数 - "pushOrder": 1, // 最早优先 - "wechatFriends": ["12312"], // 指定好友列表(可为空数组) - "deviceGroups": [9001, 9002], // 必选:推送设备分组 ID - "contentGroups": [41, 42], // 话术内容库 - "friendIntervalMin": 12, // 好友间最小间隔 - "friendIntervalMax": 28, // 好友间最大间隔 - "messageIntervalMin": 4, // 消息间最小间隔 - "messageIntervalMax": 10, // 消息间最大间隔 - "isRandomTemplate": 1, // 随机话术 - "postPushTags": [501], // 推送后标签 - ownerWechatIds:[123123,1231231] //客服id -} - -请求接口是 queryWorkbenchCreate diff --git a/Touchkebao/src/pages/pc/ckbox/powerCenter/push-history/api.ts b/Touchkebao/src/pages/pc/ckbox/powerCenter/push-history/api.ts index a3627033..9b4a4abe 100644 --- a/Touchkebao/src/pages/pc/ckbox/powerCenter/push-history/api.ts +++ b/Touchkebao/src/pages/pc/ckbox/powerCenter/push-history/api.ts @@ -6,8 +6,10 @@ export interface GetPushHistoryParams { page?: number; pageSize?: number; keyword?: string; - pushType?: string; - status?: string; + pushTypeCode?: string; // 推送类型代码:friend, group, announcement + status?: string; // 状态:pending, completed, failed + workbenchId?: string; + [property: string]: any; } // 获取推送历史接口响应 @@ -27,11 +29,30 @@ export interface GetPushHistoryResponse { */ export interface GetGroupPushHistoryParams { keyword?: string; - limit: string; - page: string; + limit?: string | number; + page?: string | number; + pageSize?: string | number; + pushTypeCode?: string; + status?: string; workbenchId?: string; [property: string]: any; } -export const getPushHistory = async (params: GetGroupPushHistoryParams) => { - return request("/v1/workbench/group-push-history", params, "GET"); + +export const getPushHistory = async ( + params: GetGroupPushHistoryParams, +): Promise => { + // 转换参数格式,确保 limit 和 page 是字符串 + const requestParams: Record = { + ...params, + }; + + if (params.page !== undefined) { + requestParams.page = String(params.page); + } + + if (params.pageSize !== undefined) { + requestParams.limit = String(params.pageSize); + } + + return request("/v1/workbench/group-push-history", requestParams, "GET"); }; diff --git a/Touchkebao/src/pages/pc/ckbox/powerCenter/push-history/index.tsx b/Touchkebao/src/pages/pc/ckbox/powerCenter/push-history/index.tsx index 1236b5a8..8b71fa3c 100644 --- a/Touchkebao/src/pages/pc/ckbox/powerCenter/push-history/index.tsx +++ b/Touchkebao/src/pages/pc/ckbox/powerCenter/push-history/index.tsx @@ -15,30 +15,33 @@ import styles from "./index.module.scss"; const { Option } = Select; -// 推送类型枚举 -export enum PushType { - FRIEND_MESSAGE = "friend-message", // 好友消息 - GROUP_MESSAGE = "group-message", // 群消息 - GROUP_ANNOUNCEMENT = "group-announcement", // 群公告 +// 推送类型代码枚举 +export enum PushTypeCode { + FRIEND = "friend", // 好友消息 + GROUP = "group", // 群消息 + ANNOUNCEMENT = "announcement", // 群公告 } // 推送状态枚举 export enum PushStatus { + PENDING = "pending", // 进行中 COMPLETED = "completed", // 已完成 - IN_PROGRESS = "in-progress", // 进行中 FAILED = "failed", // 失败 } // 推送历史记录接口 export interface PushHistoryRecord { - id: string; - pushType: PushType; - pushContent: string; + workbenchId: number; + taskName: string; + pushType: string; // 推送类型中文名称,如 "好友消息" + pushTypeCode: string; // 推送类型代码,如 "friend" targetCount: number; successCount: number; - failureCount: number; - status: PushStatus; + failCount: number; + status: string; // 状态代码,如 "pending" + statusText: string; // 状态中文名称,如 "进行中" createTime: string; + contentLibraryName: string; // 内容库名称 } const PushHistory: React.FC = () => { @@ -59,8 +62,8 @@ const PushHistory: React.FC = () => { try { setLoading(true); const params: any = { - page, - pageSize: pagination.pageSize, + page: String(page), + limit: String(pagination.pageSize), }; if (searchValue.trim()) { @@ -68,7 +71,7 @@ const PushHistory: React.FC = () => { } if (typeFilter !== "all") { - params.pushType = typeFilter; + params.pushTypeCode = typeFilter; } if (statusFilter !== "all") { @@ -157,13 +160,33 @@ const PushHistory: React.FC = () => { }; // 获取推送类型标签 - const getPushTypeTag = (type: PushType) => { - const typeMap = { - [PushType.FRIEND_MESSAGE]: { text: "好友消息", color: "#666" }, - [PushType.GROUP_MESSAGE]: { text: "群消息", color: "#666" }, - [PushType.GROUP_ANNOUNCEMENT]: { text: "群公告", color: "#666" }, + const getPushTypeTag = (pushType: string, pushTypeCode?: string) => { + // 优先使用中文名称,如果没有则根据代码映射 + if (pushType) { + const colorMap: Record = { + 好友消息: "#1890ff", + 群消息: "#52c41a", + 群公告: "#722ed1", + }; + return ( + + {pushType} + + ); + } + // 如果没有中文名称,根据代码映射 + const codeMap: Record = { + [PushTypeCode.FRIEND]: { text: "好友消息", color: "#1890ff" }, + [PushTypeCode.GROUP]: { text: "群消息", color: "#52c41a" }, + [PushTypeCode.ANNOUNCEMENT]: { text: "群公告", color: "#722ed1" }, }; - const config = typeMap[type] || { text: "未知", color: "#666" }; + const config = + pushTypeCode && codeMap[pushTypeCode] + ? codeMap[pushTypeCode] + : { text: pushType || "未知", color: "#666" }; return ( {config.text} @@ -172,14 +195,31 @@ const PushHistory: React.FC = () => { }; // 获取状态标签 - const getStatusTag = (status: PushStatus) => { - const statusMap = { + const getStatusTag = (status: string, statusText?: string) => { + // 优先使用中文状态文本 + const displayText = statusText || status; + + // 根据状态代码或文本匹配 + const statusMap: Record< + string, + { text: string; color: string; icon: React.ReactNode } + > = { [PushStatus.COMPLETED]: { text: "已完成", color: "#52c41a", icon: , }, - [PushStatus.IN_PROGRESS]: { + completed: { + text: "已完成", + color: "#52c41a", + icon: , + }, + [PushStatus.PENDING]: { + text: "进行中", + color: "#1890ff", + icon: , + }, + pending: { text: "进行中", color: "#1890ff", icon: , @@ -189,12 +229,43 @@ const PushHistory: React.FC = () => { color: "#ff4d4f", icon: , }, + failed: { + text: "失败", + color: "#ff4d4f", + icon: , + }, }; - const config = statusMap[status] || { - text: "未知", - color: "#666", - icon: null, + + // 根据状态文本匹配 + const textMap: Record< + string, + { text: string; color: string; icon: React.ReactNode } + > = { + 已完成: { + text: "已完成", + color: "#52c41a", + icon: , + }, + 进行中: { + text: "进行中", + color: "#1890ff", + icon: , + }, + 失败: { + text: "失败", + color: "#ff4d4f", + icon: , + }, }; + + const config = textMap[displayText] || + statusMap[status] || + statusMap[status.toLowerCase()] || { + text: displayText, + color: "#666", + icon: null, + }; + return ( { dataIndex: "pushType", key: "pushType", width: 120, - render: (type: PushType) => getPushTypeTag(type), + render: (pushType: string, record: PushHistoryRecord) => + getPushTypeTag(pushType, record.pushTypeCode), }, { title: "任务名称", - dataIndex: "pushContent", - key: "pushContent", + dataIndex: "taskName", + key: "taskName", ellipsis: true, render: (text: string) => {text}, }, + { + title: "内容库", + dataIndex: "contentLibraryName", + key: "contentLibraryName", + width: 150, + ellipsis: true, + render: (text: string) => ( + {text || "-"} + ), + }, { title: "目标数量", dataIndex: "targetCount", @@ -246,8 +328,8 @@ const PushHistory: React.FC = () => { }, { title: "失败数", - dataIndex: "failureCount", - key: "failureCount", + dataIndex: "failCount", + key: "failCount", width: 100, align: "center" as const, render: (count: number) => ( @@ -260,7 +342,8 @@ const PushHistory: React.FC = () => { key: "status", width: 120, align: "center" as const, - render: (status: PushStatus) => getStatusTag(status), + render: (status: string, record: PushHistoryRecord) => + getStatusTag(status, record.statusText), }, { title: "创建时间", @@ -329,9 +412,9 @@ const PushHistory: React.FC = () => { suffixIcon={} > - - - + + + @@ -353,7 +436,7 @@ const PushHistory: React.FC = () => { columns={columns} dataSource={dataSource} loading={loading} - rowKey="id" + rowKey="workbenchId" pagination={false} className={styles.dataTable} /> From 84ced0c32a3bfe0a48b00abe202ba9c06b11f13d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B6=85=E7=BA=A7=E8=80=81=E7=99=BD=E5=85=94?= Date: Thu, 20 Nov 2025 16:13:57 +0800 Subject: [PATCH 02/15] =?UTF-8?q?=E4=BC=98=E5=8C=96ProfileModules=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E4=B8=AD=E7=9A=84=E7=8A=B6=E6=80=81=E7=AE=A1=E7=90=86?= =?UTF-8?q?=EF=BC=8C=E4=BD=BF=E7=94=A8=E9=80=89=E6=8B=A9=E5=99=A8=E5=87=BD?= =?UTF-8?q?=E6=95=B0=E8=AE=A2=E9=98=85=E5=8C=B9=E9=85=8D=E7=9A=84=E5=AE=A2?= =?UTF-8?q?=E6=9C=8D=E5=AF=B9=E8=B1=A1=EF=BC=8C=E9=81=BF=E5=85=8D=E4=B8=8D?= =?UTF-8?q?=E5=BF=85=E8=A6=81=E7=9A=84=E9=87=8D=E6=96=B0=E6=B8=B2=E6=9F=93?= =?UTF-8?q?=E3=80=82=E5=90=8C=E6=97=B6=E5=9C=A8DetailValue=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E4=B8=AD=E5=BC=95=E5=85=A5useRef=E8=BF=9B=E8=A1=8C?= =?UTF-8?q?=E6=B7=B1=E5=BA=A6=E6=AF=94=E8=BE=83=EF=BC=8C=E7=A1=AE=E4=BF=9D?= =?UTF-8?q?=E5=8F=AA=E6=9C=89=E5=9C=A8=E5=80=BC=E7=9C=9F=E6=AD=A3=E5=8F=98?= =?UTF-8?q?=E5=8C=96=E6=97=B6=E6=89=8D=E6=9B=B4=E6=96=B0=E7=BC=96=E8=BE=91?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ProfileModules/components/detailValue.tsx | 31 ++++++++++++++-- .../components/ProfileModules/index.tsx | 36 ++++++++++++++----- 2 files changed, 57 insertions(+), 10 deletions(-) diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/ProfileModules/components/detailValue.tsx b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/ProfileModules/components/detailValue.tsx index 4966a863..a986f5cb 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/ProfileModules/components/detailValue.tsx +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/ProfileModules/components/detailValue.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useState, useEffect } from "react"; +import React, { useCallback, useState, useEffect, useRef } from "react"; import { Input, message } from "antd"; import { Button } from "antd-mobile"; import { EditOutlined } from "@ant-design/icons"; @@ -56,8 +56,32 @@ const DetailValue: React.FC = ({ useState>(value); const [changedKeys, setChangedKeys] = useState([]); + // 使用 useRef 存储上一次的 value,用于深度比较 + const prevValueRef = useRef>(value); + + // 深度比较函数:比较两个对象的值是否真的变化了 + const isValueChanged = useCallback( + (prev: Record, next: Record) => { + const allKeys = new Set([...Object.keys(prev), ...Object.keys(next)]); + for (const key of allKeys) { + if (prev[key] !== next[key]) { + return true; + } + } + return false; + }, + [], + ); + // 当外部value变化时,更新内部状态 + // 优化:只有当值真正变化时才重置编辑状态,避免因对象引用变化导致编辑状态丢失 useEffect(() => { + // 深度比较,只有当值真正变化时才更新 + if (!isValueChanged(prevValueRef.current, value)) { + return; + } + + // 只有在值真正变化时才更新状态 setFieldValues(value); setOriginalValues(value); setChangedKeys([]); @@ -67,7 +91,10 @@ const DetailValue: React.FC = ({ newEditingFields[field.key] = false; }); setEditingFields(newEditingFields); - }, [value, fields]); + + // 更新 ref + prevValueRef.current = value; + }, [value, fields, isValueChanged]); const handleFieldChange = useCallback( (fieldKey: string, nextVal: string) => { diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/ProfileModules/index.tsx b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/ProfileModules/index.tsx index 6f40e175..951b5bcc 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/ProfileModules/index.tsx +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/ProfileModules/index.tsx @@ -210,14 +210,34 @@ const Person: React.FC = ({ contract }) => { // 构建联系人或群聊详细信息 - const customerList = useCustomerStore(state => state.customerList); - const kfSelectedUser = useMemo(() => { - if (!contract.wechatAccountId) return null; - const matchedCustomer = customerList.find( - customer => customer.id === contract.wechatAccountId, - ); - return matchedCustomer || null; - }, [customerList, contract.wechatAccountId]); + // 优化:使用选择器函数直接订阅匹配的客服对象,避免订阅整个 customerList + // 添加相等性比较,只有当匹配的客服对象或其 labels 真正变化时才触发重新渲染 + const kfSelectedUser = useCustomerStore( + state => { + if (!contract.wechatAccountId) return null; + return ( + state.customerList.find( + customer => customer.id === contract.wechatAccountId, + ) || null + ); + }, + (prev, next) => { + // 如果都是 null,认为相等 + if (!prev && !next) return true; + // 如果一个是 null 另一个不是,认为不相等 + if (!prev || !next) return false; + // 比较关键字段:id 和 labels(因为 useEffect 中使用了 labels) + if (prev.id !== next.id) return false; + // 比较 labels 数组是否真的变化了 + const prevLabels = prev.labels || []; + const nextLabels = next.labels || []; + if (prevLabels.length !== nextLabels.length) return false; + // 深度比较 labels 数组内容(先复制再排序,避免修改原数组) + const prevLabelsStr = JSON.stringify([...prevLabels].sort()); + const nextLabelsStr = JSON.stringify([...nextLabels].sort()); + return prevLabelsStr === nextLabelsStr; + }, + ); // 不再需要从useContactStore获取getContactsByCustomer From a2b0041490a5eb5875de44a730784d8e00df9d21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B6=85=E7=BA=A7=E8=80=81=E7=99=BD=E5=85=94?= Date: Thu, 20 Nov 2025 16:50:20 +0800 Subject: [PATCH 03/15] =?UTF-8?q?=E6=9B=B4=E6=96=B0NavCommon=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E4=B8=AD=E7=9A=84=E6=8C=89=E9=92=AE=E5=9B=BE=E6=A0=87?= =?UTF-8?q?=E5=92=8C=E6=96=87=E6=9C=AC=EF=BC=8C=E5=B0=86=E5=86=85=E5=AE=B9?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E6=8C=89=E9=92=AE=E7=9A=84=E5=9B=BE=E6=A0=87?= =?UTF-8?q?=E6=9B=B4=E6=94=B9=E4=B8=BASendOutlined=EF=BC=8C=E5=B9=B6?= =?UTF-8?q?=E5=B0=86=E5=85=B6=E6=96=87=E6=9C=AC=E6=9B=B4=E6=94=B9=E4=B8=BA?= =?UTF-8?q?=E2=80=9C=E5=8F=91=E6=9C=8B=E5=8F=8B=E5=9C=88=E2=80=9D=E3=80=82?= =?UTF-8?q?=E5=90=8C=E6=97=B6=EF=BC=8C=E8=B0=83=E6=95=B4powerCenter?= =?UTF-8?q?=E6=A8=A1=E5=9D=97=E4=B8=AD=E7=9A=84=E6=95=B0=E6=8D=AE=E7=BB=93?= =?UTF-8?q?=E6=9E=84=EF=BC=8C=E6=B3=A8=E9=87=8A=E6=8E=89=E5=86=85=E5=AE=B9?= =?UTF-8?q?=E5=BA=93=E9=85=8D=E7=BD=AE=E9=83=A8=E5=88=86=EF=BC=8C=E5=B9=B6?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=A0=B7=E5=BC=8F=E4=BB=A3=E7=A0=81=E3=80=82?= =?UTF-8?q?=E6=9B=B4=E6=96=B0ContentManagement=E7=BB=84=E4=BB=B6=E7=9A=84?= =?UTF-8?q?=E6=A0=87=E9=A2=98=E4=B8=BA=E2=80=9C=E5=8F=91=E6=9C=8B=E5=8F=8B?= =?UTF-8?q?=E5=9C=88=E2=80=9D=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pc/ckbox/components/NavCommon/index.tsx | 6 +-- .../powerCenter/content-management/index.tsx | 2 +- .../pages/pc/ckbox/powerCenter/index.data.tsx | 37 +++++++++++-------- .../src/pages/pc/ckbox/powerCenter/index.tsx | 30 ++++++--------- 4 files changed, 36 insertions(+), 39 deletions(-) diff --git a/Touchkebao/src/pages/pc/ckbox/components/NavCommon/index.tsx b/Touchkebao/src/pages/pc/ckbox/components/NavCommon/index.tsx index 52d9e5df..fcd3f7a4 100644 --- a/Touchkebao/src/pages/pc/ckbox/components/NavCommon/index.tsx +++ b/Touchkebao/src/pages/pc/ckbox/components/NavCommon/index.tsx @@ -17,7 +17,7 @@ import { LogoutOutlined, ThunderboltOutlined, SettingOutlined, - CalendarOutlined, + SendOutlined, ClearOutlined, } from "@ant-design/icons"; import { noticeList, readMessage, readAll } from "./api"; @@ -317,10 +317,10 @@ const NavCommon: React.FC = ({ title = "触客宝" }) => { > {title} diff --git a/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/index.tsx b/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/index.tsx index fed9b453..97ea2452 100644 --- a/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/index.tsx +++ b/Touchkebao/src/pages/pc/ckbox/powerCenter/content-management/index.tsx @@ -20,7 +20,7 @@ const ContentManagement: React.FC = () => { return (
, - color: "#52c41a", - tag: "内容管理", - features: [ - "多库管理与分类", - "AI调用权限配置", - "内容检索规则设置", - "手动内容上传", - ], - path: "/pc/powerCenter/content-library", - }, + // { + // id: "content-library", + // title: "AI内容库配置", + // description: "管理AI内容库,配置调用权限,优化AI推送效果和内容质量", + // icon: , + // color: "#52c41a", + // tag: "内容管理", + // features: [ + // "多库管理与分类", + // "AI调用权限配置", + // "内容检索规则设置", + // "手动内容上传", + // ], + // path: "/pc/powerCenter/content-library", + // }, { id: "message-push-assistant", title: "消息推送助手", diff --git a/Touchkebao/src/pages/pc/ckbox/powerCenter/index.tsx b/Touchkebao/src/pages/pc/ckbox/powerCenter/index.tsx index 51652b1e..87090063 100644 --- a/Touchkebao/src/pages/pc/ckbox/powerCenter/index.tsx +++ b/Touchkebao/src/pages/pc/ckbox/powerCenter/index.tsx @@ -39,18 +39,6 @@ const PowerCenter: React.FC = () => { return (
{/* 页面标题区域 */} -
-
-
-
-

功能中心

-
-

- AI智能营销·一站式客户管理·高效业务增长 -

-
-
- {/* KPI统计区域(置顶,按图展示) */}
@@ -157,9 +145,11 @@ const PowerCenter: React.FC = () => { {card.features.map((feature, index) => (
  • {feature}
  • @@ -186,7 +176,7 @@ const PowerCenter: React.FC = () => { className={styles.cardIcon} style={{ backgroundColor: getIconBgColor( - featureCategories[3].color + featureCategories[3].color, ), }} > @@ -212,9 +202,11 @@ const PowerCenter: React.FC = () => { {featureCategories[3].features.map((feature, index) => (
  • {feature}
  • From fe12f78c1d5765110a51e400b844cdee9c2540b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B6=85=E7=BA=A7=E8=80=81=E7=99=BD=E5=85=94?= Date: Fri, 21 Nov 2025 10:34:55 +0800 Subject: [PATCH 04/15] =?UTF-8?q?=E4=BC=98=E5=8C=96MessageRecord=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E7=9A=84=E8=87=AA=E5=8A=A8=E6=BB=9A=E5=8A=A8=E9=80=BB?= =?UTF-8?q?=E8=BE=91=EF=BC=8C=E7=A1=AE=E4=BF=9D=E5=9C=A8=E6=96=B0=E6=B6=88?= =?UTF-8?q?=E6=81=AF=E5=88=B0=E8=BE=BE=E4=B8=94=E6=B2=A1=E6=9C=89=E8=A7=86?= =?UTF-8?q?=E9=A2=91=E7=8A=B6=E6=80=81=E5=8F=98=E5=8C=96=E6=97=B6=E6=89=8D?= =?UTF-8?q?=E6=BB=9A=E5=8A=A8=E5=88=B0=E5=BA=95=E9=83=A8=EF=BC=8C=E5=90=8C?= =?UTF-8?q?=E6=97=B6=E4=BF=AE=E5=A4=8D=E4=BA=86=E5=AF=B9=E6=B6=88=E6=81=AF?= =?UTF-8?q?=E9=95=BF=E5=BA=A6=E7=9A=84=E5=BC=95=E7=94=A8=E4=BB=A5=E6=8F=90?= =?UTF-8?q?=E9=AB=98=E6=80=A7=E8=83=BD=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ChatWindow/components/MessageRecord/index.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageRecord/index.tsx b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageRecord/index.tsx index 4bfe7c51..a3de4b84 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageRecord/index.tsx +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageRecord/index.tsx @@ -347,6 +347,7 @@ const MessageRecord: React.FC = ({ contract }) => { useEffect(() => { const prevMessages = prevMessagesRef.current; + const prevLength = prevMessages.length; const hasVideoStateChange = currentMessages.some((msg, index) => { // 首先检查消息对象本身是否为null或undefined @@ -384,8 +385,9 @@ const MessageRecord: React.FC = ({ contract }) => { } }); - // 只有在没有视频状态变化时才自动滚动到底部 - if (!hasVideoStateChange && isLoadingData) { + if (currentMessages.length > prevLength && !hasVideoStateChange) { + scrollToBottom(); + } else if (isLoadingData && !hasVideoStateChange) { scrollToBottom(); } From d296d019e54190d2c74623236dd48bc3918858a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B6=85=E7=BA=A7=E8=80=81=E7=99=BD=E5=85=94?= Date: Fri, 21 Nov 2025 14:55:56 +0800 Subject: [PATCH 05/15] Enhance MessageEnter component by adding a map selection feature. Introduced a new button for map visibility and integrated SelectMap component for improved user interaction. --- .../InputMessage/InputMessage.tsx | 0 .../InputMessage/index.module.scss | 147 --- .../StepSendMessage/index.module.scss | 265 ------ .../components/StepSendMessage/index.tsx | 6 - Moncter/src/pages/pc/ckbox/weChat/api.ts | 0 .../components/ProfileModules/index.tsx | 0 .../MessageEnter/components/selectMap.tsx | 900 ++++++++++++++++++ .../components/MessageEnter/index.tsx | 31 +- 8 files changed, 928 insertions(+), 421 deletions(-) delete mode 100644 Moncter/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/components/StepSendMessage/InputMessage/InputMessage.tsx delete mode 100644 Moncter/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/components/StepSendMessage/InputMessage/index.module.scss delete mode 100644 Moncter/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/components/StepSendMessage/index.module.scss delete mode 100644 Moncter/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/components/StepSendMessage/index.tsx delete mode 100644 Moncter/src/pages/pc/ckbox/weChat/api.ts delete mode 100644 Moncter/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/ProfileModules/index.tsx create mode 100644 Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/selectMap.tsx diff --git a/Moncter/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/components/StepSendMessage/InputMessage/InputMessage.tsx b/Moncter/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/components/StepSendMessage/InputMessage/InputMessage.tsx deleted file mode 100644 index e69de29b..00000000 diff --git a/Moncter/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/components/StepSendMessage/InputMessage/index.module.scss b/Moncter/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/components/StepSendMessage/InputMessage/index.module.scss deleted file mode 100644 index b8085ce9..00000000 --- a/Moncter/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/components/StepSendMessage/InputMessage/index.module.scss +++ /dev/null @@ -1,147 +0,0 @@ -.chatFooter { - background: #f7f7f7; - border-top: 1px solid #e1e1e1; - padding: 0; - height: auto; - border-radius: 8px; -} - -.inputContainer { - padding: 8px 12px; - display: flex; - flex-direction: column; - gap: 6px; -} - -.inputToolbar { - display: flex; - align-items: center; - padding: 4px 0; -} - -.leftTool { - display: flex; - gap: 4px; - align-items: center; -} - -.toolbarButton { - width: 28px; - height: 28px; - display: flex; - align-items: center; - justify-content: center; - border-radius: 4px; - color: #666; - font-size: 16px; - transition: all 0.15s; - border: none; - background: transparent; - - &:hover { - background: #e6e6e6; - color: #333; - } - - &:active { - background: #d9d9d9; - } -} - -.inputArea { - display: flex; - flex-direction: column; - padding: 4px 0; -} - -.inputWrapper { - border: 1px solid #d1d1d1; - border-radius: 4px; - background: #fff; - overflow: hidden; - - &:focus-within { - border-color: #07c160; - } -} - -.messageInput { - width: 100%; - border: none; - resize: none; - font-size: 13px; - line-height: 1.4; - padding: 8px 10px; - background: transparent; - - &:focus { - box-shadow: none; - outline: none; - } - - &::placeholder { - color: #b3b3b3; - } -} - -.sendButtonArea { - padding: 8px 10px; - display: flex; - justify-content: flex-end; - gap: 8px; -} - -.sendButton { - height: 32px; - border-radius: 4px; - font-weight: normal; - min-width: 60px; - font-size: 13px; - background: #07c160; - border-color: #07c160; - - &:hover { - background: #06ad56; - border-color: #06ad56; - } - - &:active { - background: #059748; - border-color: #059748; - } - - &:disabled { - background: #b3b3b3; - border-color: #b3b3b3; - opacity: 1; - } -} - -.hintButton { - border: none; - background: transparent; - color: #666; - font-size: 12px; - - &:hover { - color: #333; - } -} - -.inputHint { - font-size: 11px; - color: #999; - text-align: right; - margin-top: 2px; -} - -@media (max-width: 768px) { - .inputToolbar { - flex-wrap: wrap; - gap: 8px; - } - - .sendButtonArea { - justify-content: space-between; - } -} diff --git a/Moncter/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/components/StepSendMessage/index.module.scss b/Moncter/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/components/StepSendMessage/index.module.scss deleted file mode 100644 index fee50c0a..00000000 --- a/Moncter/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/components/StepSendMessage/index.module.scss +++ /dev/null @@ -1,265 +0,0 @@ -.stepContent { - .stepHeader { - margin-bottom: 20px; - - h3 { - font-size: 18px; - font-weight: 600; - color: #1a1a1a; - margin: 0 0 8px 0; - } - - p { - font-size: 14px; - color: #666; - margin: 0; - } - } -} - -.step3Content { - display: flex; - gap: 24px; - align-items: flex-start; - - .leftColumn { - flex: 1; - display: flex; - flex-direction: column; - gap: 20px; - } - - .rightColumn { - width: 400px; - flex: 1; - display: flex; - flex-direction: column; - gap: 20px; - } - - .messagePreview { - border: 2px dashed #52c41a; - border-radius: 8px; - padding: 20px; - background: #f6ffed; - - .previewTitle { - font-size: 14px; - color: #52c41a; - font-weight: 500; - margin-bottom: 12px; - } - - .messageBubble { - min-height: 60px; - padding: 12px; - background: #fff; - border-radius: 6px; - color: #666; - font-size: 14px; - line-height: 1.6; - - .currentEditingLabel { - font-size: 12px; - color: #999; - margin-bottom: 8px; - } - - .messageText { - color: #333; - white-space: pre-wrap; - word-break: break-word; - } - } - } - - .savedScriptGroups { - .scriptGroupTitle { - font-size: 14px; - font-weight: 500; - color: #333; - margin-bottom: 12px; - } - - .scriptGroupItem { - border: 1px solid #e8e8e8; - border-radius: 8px; - padding: 12px; - margin-bottom: 12px; - background: #fff; - - .scriptGroupHeader { - display: flex; - justify-content: space-between; - align-items: center; - - .scriptGroupLeft { - display: flex; - align-items: center; - gap: 8px; - flex: 1; - - :global(.ant-radio) { - margin-right: 4px; - } - - .scriptGroupName { - font-size: 14px; - font-weight: 500; - color: #333; - } - - .messageCount { - font-size: 12px; - color: #999; - margin-left: 8px; - } - } - - .scriptGroupActions { - display: flex; - gap: 4px; - - .actionButton { - padding: 4px; - color: #666; - - &:hover { - color: #1890ff; - } - } - } - } - - .scriptGroupContent { - margin-top: 8px; - padding-top: 8px; - border-top: 1px solid #f0f0f0; - font-size: 13px; - color: #666; - } - } - } - - .messageInputArea { - .messageInput { - margin-bottom: 12px; - } - - .attachmentButtons { - display: flex; - gap: 8px; - margin-bottom: 12px; - } - - .aiRewriteSection { - display: flex; - align-items: center; - margin-bottom: 8px; - } - - .messageHint { - font-size: 12px; - color: #999; - } - } - - .settingsPanel { - border: 1px solid #e8e8e8; - border-radius: 8px; - padding: 20px; - background: #fafafa; - - .settingsTitle { - font-size: 14px; - font-weight: 500; - color: #1a1a1a; - margin-bottom: 16px; - } - - .settingItem { - margin-bottom: 20px; - - &:last-child { - margin-bottom: 0; - } - - .settingLabel { - font-size: 14px; - font-weight: 500; - color: #1a1a1a; - margin-bottom: 12px; - } - - .settingControl { - display: flex; - align-items: center; - gap: 8px; - - span { - font-size: 14px; - color: #666; - min-width: 80px; - } - } - } - } - - .tagSection { - .settingLabel { - font-size: 14px; - font-weight: 500; - color: #1a1a1a; - margin-bottom: 12px; - } - } - - .pushPreview { - border: 1px solid #e8e8e8; - border-radius: 8px; - padding: 20px; - background: #f0f7ff; - - .previewTitle { - font-size: 14px; - font-weight: 500; - color: #1a1a1a; - margin-bottom: 12px; - } - - ul { - list-style: none; - padding: 0; - margin: 0; - - li { - font-size: 14px; - color: #666; - line-height: 1.8; - } - } - } -} - -@media (max-width: 1200px) { - .step3Content { - .rightColumn { - width: 350px; - } - } -} - -@media (max-width: 768px) { - .step3Content { - flex-direction: column; - - .leftColumn { - width: 100%; - } - - .rightColumn { - width: 100%; - } - } -} - diff --git a/Moncter/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/components/StepSendMessage/index.tsx b/Moncter/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/components/StepSendMessage/index.tsx deleted file mode 100644 index 082831d5..00000000 --- a/Moncter/src/pages/pc/ckbox/powerCenter/message-push-assistant/create-push-task/components/StepSendMessage/index.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import ContentSelection from "@/components/ContentSelection"; -import { ContentItem } from "@/components/ContentSelection/data"; -import InputMessage from "./InputMessage/InputMessage"; -import styles from "./index.module.scss"; - -interface StepSendMessageProps { diff --git a/Moncter/src/pages/pc/ckbox/weChat/api.ts b/Moncter/src/pages/pc/ckbox/weChat/api.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/Moncter/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/ProfileModules/index.tsx b/Moncter/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/ProfileModules/index.tsx deleted file mode 100644 index e69de29b..00000000 diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/selectMap.tsx b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/selectMap.tsx new file mode 100644 index 00000000..7393c08c --- /dev/null +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/selectMap.tsx @@ -0,0 +1,900 @@ +import React, { useCallback, useEffect, useRef, useState } from "react"; +import { + AutoComplete, + Input as AntInput, + message, + Modal, + Spin, + Button, +} from "antd"; +import { ContractData, weChatGroup, ChatRecord } from "@/pages/pc/ckbox/data"; +import { useWebSocketStore } from "@/store/module/websocket/websocket"; + +declare const AMap: any; + +interface SelectMapProps { + visible: boolean; + onClose: () => void; + contract: ContractData | weChatGroup; + addMessage: (message: ChatRecord) => void; +} + +const SelectMap: React.FC = ({ + visible, + onClose, + contract, + addMessage, +}) => { + const [selectedLocation, setSelectedLocation] = useState(null); + const mapRef = useRef(null); + const markersRef = useRef([]); // 使用数组保存所有标记点 + const geocoderRef = useRef(null); // 保存 Geocoder 实例 + const placeSearchRef = useRef(null); // 保存 PlaceSearch 实例 + const geolocationRef = useRef(null); // 保存 Geolocation 实例 + const pendingClickRef = useRef<{ + lat: number; + lng: number; + lnglat: any; + } | null>(null); // 保存待处理的点击坐标 + const [options, setOptions] = useState([]); + const [mapLoading, setMapLoading] = useState(true); + + const sendCommand = useWebSocketStore(state => state.sendCommand); + + // 清除所有标记点 + const clearAllMarkers = useCallback(() => { + if (markersRef.current && markersRef.current.length > 0) { + console.log(`清除 ${markersRef.current.length} 个标记点`); + markersRef.current.forEach(marker => { + if (marker) { + marker.setMap(null); + marker = null; + } + }); + markersRef.current = []; + } + }, []); + + const addMarker = useCallback( + (lnglat: any) => { + console.log("addMarker 调用,坐标:", lnglat); + + // 先清除所有现有的标记点 + clearAllMarkers(); + + // 创建红色图标,保持默认倒水滴形状 + const redIcon = new AMap.Icon({ + size: new AMap.Size(25, 34), // 默认标记点尺寸 + image: "https://webapi.amap.com/theme/v1.3/markers/n/mark_r.png", // 红色标记点图片 + imageOffset: new AMap.Pixel(0, 0), + imageSize: new AMap.Size(25, 34), + }); + + // 创建新的标记点 + const newMarker = new AMap.Marker({ + position: lnglat, + map: mapRef.current, + icon: redIcon, + }); + + // 将新标记点添加到数组中 + markersRef.current.push(newMarker); + + mapRef.current.setCenter(lnglat); + mapRef.current.setZoom(16); // 确保缩放到合适级别 + console.log("新 marker 已添加并居中"); + }, + [clearAllMarkers], + ); + + // 通用的地址获取函数 + const getAddressForLocation = useCallback( + (lat: number, lng: number, lnglat: any) => { + console.log("=== getAddressForLocation 调用 ==="); + console.log("坐标:", { lat, lng }); + console.log( + "Geocoder ref 状态:", + geocoderRef.current ? "存在" : "不存在", + ); + + // 检查 Geocoder 是否已初始化 + if (!geocoderRef.current) { + console.warn("Geocoder 未初始化,无法获取地址"); + return false; + } + + // 使用更精确的坐标进行查询 + // 高德地图 getAddress 支持 LngLat 对象或 [lng, lat] 数组 + let queryLnglat: any; + if (lnglat && typeof lnglat.getLat === "function") { + // 如果传入的是 LngLat 对象,直接使用 + queryLnglat = lnglat; + } else { + // 否则创建新的 LngLat 对象或使用数组格式 + try { + queryLnglat = new AMap.LngLat(lng, lat); + } catch (error) { + console.error("创建 LngLat 对象失败,使用数组格式:", error); + queryLnglat = [lng, lat]; + } + } + console.log("调用 geocoder.getAddress,坐标:", queryLnglat); + + try { + geocoderRef.current.getAddress( + queryLnglat, + (status: string, result: any) => { + console.log("=== Geocoder 回调触发(通用函数) ==="); + console.log("Status:", status); + console.log( + "Result:", + result ? JSON.stringify(result, null, 2) : "null", + ); + + if ( + status === "complete" && + result && + result.info === "OK" && + result.regeocode + ) { + const regeocode = result.regeocode; + const formattedAddress = regeocode.formattedAddress || ""; + const addressComponent = regeocode.addressComponent || {}; + + // 构建详细地址信息 + let addressLabel = formattedAddress; + let poiName = "点击位置"; + + // 优先级1: 如果有POI信息,优先使用POI名称 + if (regeocode.pois && regeocode.pois.length > 0) { + const poi = regeocode.pois[0]; + poiName = poi.name || poiName; + const poiAddress = poi.address || ""; + addressLabel = poiAddress + ? `${poiName} ${poiAddress}` + : `${poiName} ${formattedAddress}`; + } + // 优先级2: 如果有建筑物信息 + else if (regeocode.buildings && regeocode.buildings.length > 0) { + const building = regeocode.buildings[0]; + poiName = building.name || addressComponent.building || poiName; + addressLabel = `${poiName} ${formattedAddress}`; + } + // 优先级3: 如果有AOI(兴趣区域)信息 + else if (regeocode.aois && regeocode.aois.length > 0) { + const aoi = regeocode.aois[0]; + poiName = aoi.name || poiName; + addressLabel = `${poiName} ${formattedAddress}`; + } + // 优先级4: 使用地址组件构建详细地址 + else if (addressComponent.building) { + poiName = addressComponent.building; + addressLabel = `${poiName} ${formattedAddress}`; + } + // 优先级5: 组合地址组件 + else { + const parts = []; + if (addressComponent.province) + parts.push(addressComponent.province); + if (addressComponent.city) parts.push(addressComponent.city); + if (addressComponent.district) + parts.push(addressComponent.district); + if (addressComponent.township) + parts.push(addressComponent.township); + if (addressComponent.street) + parts.push(addressComponent.street); + if (addressComponent.streetNumber) + parts.push(addressComponent.streetNumber); + + if (parts.length > 0) { + const fullAddress = parts.join(""); + poiName = + addressComponent.street || + addressComponent.district || + "点击位置"; + addressLabel = fullAddress || formattedAddress; + } else { + addressLabel = + formattedAddress || `纬度: ${lat}, 经度: ${lng}`; + } + } + + setSelectedLocation({ + lat: lat, + lng: lng, + label: addressLabel, + poiname: poiName, + }); + + message.success("地址信息获取成功"); + } else { + console.warn("=== Geocoder 返回异常,尝试 PlaceSearch ==="); + console.warn("Status:", status); + console.warn("Result:", result); + // Geocoder 失败,使用 PlaceSearch 作为备用 + if (placeSearchRef.current) { + try { + const searchLnglat = lnglat || [lng, lat]; + placeSearchRef.current.searchNearBy( + "", + searchLnglat, + 1000, + (searchStatus: string, searchResult: any) => { + if ( + searchStatus === "complete" && + searchResult && + searchResult.info === "OK" && + searchResult.poiList?.pois?.length > 0 + ) { + const poi = searchResult.poiList.pois[0]; + const poiLabel = poi.address + ? `${poi.name} ${poi.address}` + : poi.name; + setSelectedLocation({ + lat: lat, + lng: lng, + label: poiLabel, + poiname: poi.name, + }); + message.success("通过附近搜索获取到地址信息"); + } else { + console.warn("PlaceSearch 返回异常:", { + status: searchStatus, + result: searchResult, + }); + const coordLabel = `纬度: ${lat}, 经度: ${lng}`; + setSelectedLocation({ + lat: lat, + lng: lng, + label: coordLabel, + poiname: "点击位置", + }); + message.warning("无法获取详细地址信息,但坐标已记录"); + } + }, + ); + } catch (placeSearchError) { + console.error( + "PlaceSearch.searchNearBy 调用异常:", + placeSearchError, + ); + const coordLabel = `纬度: ${lat}, 经度: ${lng}`; + setSelectedLocation({ + lat: lat, + lng: lng, + label: coordLabel, + poiname: "点击位置", + }); + message.warning("无法获取详细地址信息,但坐标已记录"); + } + } else { + const coordLabel = `纬度: ${lat}, 经度: ${lng}`; + setSelectedLocation({ + lat: lat, + lng: lng, + label: coordLabel, + poiname: "点击位置", + }); + message.warning("无法获取详细地址信息,但坐标已记录"); + } + } + }, + ); + } catch (error) { + console.error("=== Geocoder.getAddress 调用异常 ===", error); + // 如果 geocoder 调用失败,尝试使用 PlaceSearch + if (placeSearchRef.current) { + console.log("尝试使用 PlaceSearch 作为备用方案"); + try { + placeSearchRef.current.searchNearBy( + "", + lnglat || [lng, lat], + 1000, + (status: string, result: any) => { + if ( + status === "complete" && + result && + result.info === "OK" && + result.poiList?.pois?.length > 0 + ) { + const poi = result.poiList.pois[0]; + const poiLabel = poi.address + ? `${poi.name} ${poi.address}` + : poi.name; + setSelectedLocation({ + lat: lat, + lng: lng, + label: poiLabel, + poiname: poi.name, + }); + message.success("通过附近搜索获取到地址信息"); + } else { + const coordLabel = `纬度: ${lat}, 经度: ${lng}`; + setSelectedLocation({ + lat: lat, + lng: lng, + label: coordLabel, + poiname: "点击位置", + }); + message.warning("无法获取详细地址信息,但坐标已记录"); + } + }, + ); + } catch (placeSearchError) { + console.error("PlaceSearch 调用也失败:", placeSearchError); + const coordLabel = `纬度: ${lat}, 经度: ${lng}`; + setSelectedLocation({ + lat: lat, + lng: lng, + label: coordLabel, + poiname: "点击位置", + }); + message.warning("无法获取详细地址信息,但坐标已记录"); + } + } else { + const coordLabel = `纬度: ${lat}, 经度: ${lng}`; + setSelectedLocation({ + lat: lat, + lng: lng, + label: coordLabel, + poiname: "点击位置", + }); + message.warning("无法获取详细地址信息,但坐标已记录"); + } + return false; + } + + return true; + }, + [], + ); + + const initMap = useCallback(() => { + console.log("initMap 执行中"); + setMapLoading(true); + + // 确保容器存在 + const container = document.getElementById("amap-container"); + if (!container) { + console.error("地图容器不存在"); + setMapLoading(false); + return; + } + + // 确保容器样式正确 + container.style.pointerEvents = "auto"; + container.style.cursor = "crosshair"; + container.style.position = "relative"; + container.style.zIndex = "1"; + + const map = new AMap.Map("amap-container", { + zoom: 16, + center: [118.113653, 24.470164], // 默认中心 + viewMode: "2D", // 明确指定视图模式 + }); + mapRef.current = map; + + // 添加超时机制,防止 loading 一直显示 + const loadingTimeout = setTimeout(() => { + console.warn("地图加载超时,强制关闭 loading"); + setMapLoading(false); + }, 10000); // 10秒超时 + + // 立即加载插件,不等待地图 complete 事件 + console.log("=== 立即开始加载 AMap 插件(地图创建后) ==="); + AMap.plugin( + [ + "AMap.AutoComplete", + "AMap.PlaceSearch", + "AMap.Geocoder", + "AMap.Geolocation", + ], + error => { + if (error) { + console.error("=== AMap 插件加载失败 ===", error); + message.error("地图插件加载失败,部分功能可能不可用"); + clearTimeout(loadingTimeout); + setMapLoading(false); + return; + } + console.log("=== AMap 插件加载成功 ==="); + + // 立即创建 PlaceSearch 和 Geocoder 实例 + const placeSearch = new AMap.PlaceSearch({ + city: "全国", + map: map, + }); + placeSearchRef.current = placeSearch; + console.log("PlaceSearch 实例已创建"); + + const geocoder = new AMap.Geocoder({ + city: "全国", + radius: 1000, // 搜索半径,单位米 + extensions: "all", // 返回详细信息,包括POI、建筑物等 + }); + geocoderRef.current = geocoder; + console.log("Geocoder 实例已创建并保存到 ref,现在可以立即使用"); + + // 创建 Geolocation 实例用于获取当前位置 + const geolocation = new AMap.Geolocation({ + enableHighAccuracy: true, // 是否使用高精度定位,默认:true + timeout: 10000, // 超过10秒后停止定位,默认:无穷大 + maximumAge: 0, // 定位结果缓存0毫秒,默认:0 + convert: true, // 自动偏移坐标,偏移后的坐标为高德坐标,默认:true + showButton: false, // 显示定位按钮,默认:true + buttonPosition: "RB", // 定位按钮停靠位置,默认:'LB',左下角 + showMarker: false, // 定位成功后在定位到的位置显示点标记,默认:true + showCircle: false, // 定位成功后用圆圈表示定位精度范围,默认:true + panToLocation: false, // 定位成功后将定位到的位置作为地图中心点,默认:true + zoomToAccuracy: false, // 定位成功后调整地图视野范围使定位位置及精度范围视野内可见,默认:false + }); + geolocationRef.current = geolocation; + + // 获取当前位置 + console.log("=== 开始获取当前位置 ==="); + geolocation.getCurrentPosition((status: string, result: any) => { + console.log("=== 定位回调触发 ==="); + console.log("定位状态:", status); + console.log("定位结果:", result); + + if (status === "complete") { + const { position, formattedAddress, addressComponent } = result; + const lat = position.lat; + const lng = position.lng; + const lnglat = new AMap.LngLat(lng, lat); + + console.log("定位成功,当前位置:", { lat, lng }); + console.log("定位地址:", formattedAddress); + + // 将地图中心设置为当前位置 + map.setCenter(lnglat); + map.setZoom(16); + + // 添加当前位置标记 + addMarker(lnglat); + + // 设置选中位置信息 + let addressLabel = formattedAddress || `纬度: ${lat}, 经度: ${lng}`; + let poiName = "当前位置"; + + // 尝试从地址组件中获取更详细的信息 + if (addressComponent) { + const parts = []; + if (addressComponent.province) + parts.push(addressComponent.province); + if (addressComponent.city) parts.push(addressComponent.city); + if (addressComponent.district) + parts.push(addressComponent.district); + if (addressComponent.street) parts.push(addressComponent.street); + if (addressComponent.streetNumber) + parts.push(addressComponent.streetNumber); + + if (parts.length > 0) { + addressLabel = parts.join(""); + } + + poiName = + addressComponent.street || + addressComponent.district || + "当前位置"; + } + + setSelectedLocation({ + lat: lat, + lng: lng, + label: addressLabel, + poiname: poiName, + }); + + message.success("已获取当前位置"); + } else { + console.warn("定位失败:", result); + message.warning( + "无法获取当前位置,请手动点击地图选择位置。原因: " + + (result.message || "定位服务不可用"), + ); + // 定位失败时,使用默认中心点 + console.log("使用默认中心点"); + } + }); + + // 如果有待处理的点击坐标,立即处理它 + if (pendingClickRef.current) { + console.log("=== 检测到待处理的点击坐标,立即处理 ==="); + const { lat, lng, lnglat } = pendingClickRef.current; + console.log("待处理坐标:", { lat, lng }); + setTimeout(() => { + const success = getAddressForLocation(lat, lng, lnglat); + if (success) { + console.log("✓ 待处理的坐标已成功获取地址"); + pendingClickRef.current = null; // 清除待处理坐标 + } else { + console.warn("待处理的坐标获取地址失败"); + } + }, 100); + } + }, + ); + + // 立即绑定点击事件,插件可能已加载或正在加载 + console.log("立即绑定点击事件(插件可能已初始化)"); + map.on("click", (e: any) => { + console.log("=== 地图点击事件触发 ==="); + console.log("点击事件对象:", e); + console.log("点击位置对象:", e.lnglat); + console.log( + "Geocoder ref 状态:", + geocoderRef.current ? "已初始化" : "未初始化", + ); + + if (!e || !e.lnglat) { + console.error("点击事件无效,缺少 lnglat"); + return; + } + + const lnglat = e.lnglat; + const lat = lnglat.getLat(); + const lng = lnglat.getLng(); + console.log(`点击坐标 - 纬度: ${lat}, 经度: ${lng}`); + + // 立即添加标记和居中 + addMarker(lnglat); + + // 设置基本 selectedLocation(至少有坐标) + setSelectedLocation({ + lat: lat, + lng: lng, + label: "正在获取地址信息...", + poiname: "点击位置", + }); + + // 如果 Geocoder 已初始化,立即使用它 + if (geocoderRef.current) { + console.log("Geocoder 已初始化,立即获取地址"); + getAddressForLocation(lat, lng, lnglat); + } else { + console.log("Geocoder 未初始化,保存坐标待插件加载完成后处理"); + // 保存待处理的坐标 + pendingClickRef.current = { lat, lng, lnglat }; + } + }); + + // 等待地图完全加载后关闭 loading + map.on("complete", () => { + console.log("地图加载完成"); + // 清除超时定时器 + clearTimeout(loadingTimeout); + // 关闭 loading + setMapLoading(false); + console.log("地图加载完成,loading 已关闭"); + + // 确保地图容器可点击 + const mapContainer = map.getContainer(); + if (mapContainer) { + mapContainer.style.pointerEvents = "auto"; + mapContainer.style.cursor = "crosshair"; + console.log("地图容器指针事件已启用"); + } + }); + + // 如果地图加载失败,也设置加载完成 + map.on("error", (error: any) => { + console.error("地图加载错误:", error); + clearTimeout(loadingTimeout); + setMapLoading(false); + message.error("地图加载失败"); + }); + }, [addMarker, getAddressForLocation]); + + const handleSearch = value => { + if (value) { + AMap.plugin("AMap.AutoComplete", () => { + const auto = new AMap.AutoComplete({ city: "全国" }); + auto.search(value, (status, result) => { + if (status === "complete") { + setOptions( + result.tips.map(tip => ({ + value: tip.name, + data: tip, + })), + ); + } + }); + }); + } else { + setOptions([]); + } + }; + + const onSelect = (value, option) => { + const { district, address, name, location } = option.data; + const lnglat = location; + setSelectedLocation({ + lat: lnglat.lat, + lng: lnglat.lng, + label: `${name} ${address || district}`, + poiname: name, + }); + addMarker(lnglat); + mapRef.current.setCenter(lnglat); + }; + + const handleModalChange = useCallback( + (visible: boolean) => { + if (visible) { + console.log("模态打开:开始加载地图脚本"); + setMapLoading(true); + setSelectedLocation(null); + const script = document.createElement("script"); + script.src = + "https://webapi.amap.com/maps?v=1.4.15&key=79370028f5763e46742125ed2e900c76&plugin=AMap.PlaceSearch,AMap.AutoComplete,AMap.Geocoder,AMap.Geolocation"; + script.async = true; + script.onload = () => { + console.log("脚本加载成功:开始初始化地图"); + setTimeout(() => initMap(), 100); // 添加延迟确保 DOM 就绪 + }; + script.onerror = () => { + console.error("脚本加载失败"); + message.error("地图加载失败,请检查网络或API密钥"); + setMapLoading(false); + }; + document.body.appendChild(script); + } else { + console.log("模态关闭:清理地图和脚本"); + // 清除所有标记点 + clearAllMarkers(); + // 重置 ref + geocoderRef.current = null; + placeSearchRef.current = null; + geolocationRef.current = null; + // Cleanup on close + const scripts = document.querySelectorAll( + 'script[src*="webapi.amap.com"]', + ); + scripts.forEach(s => { + if (document.body.contains(s)) { + document.body.removeChild(s); + } + }); + if (mapRef.current) { + mapRef.current.destroy(); + mapRef.current = null; + } + setMapLoading(false); + setOptions([]); // 重置搜索选项 + } + }, + [initMap, clearAllMarkers], + ); + + // 手动获取当前位置 + const handleGetCurrentLocation = useCallback(() => { + if (!geolocationRef.current) { + message.warning("定位服务未初始化,请稍候再试"); + return; + } + + console.log("=== 手动触发获取当前位置 ==="); + message.loading({ content: "正在获取当前位置...", key: "location" }); + + geolocationRef.current.getCurrentPosition((status: string, result: any) => { + message.destroy("location"); + console.log("=== 手动定位回调触发 ==="); + console.log("定位状态:", status); + console.log("定位结果:", result); + + if (status === "complete" && result && result.position) { + const { position, formattedAddress, addressComponent } = result; + const lat = position.lat; + const lng = position.lng; + const lnglat = new AMap.LngLat(lng, lat); + + console.log("定位成功,当前位置:", { lat, lng }); + + // 将地图中心设置为当前位置 + if (mapRef.current) { + mapRef.current.setCenter(lnglat); + mapRef.current.setZoom(16); + } + + // 添加当前位置标记 + addMarker(lnglat); + + // 设置选中位置信息 + let addressLabel = formattedAddress || `纬度: ${lat}, 经度: ${lng}`; + let poiName = "当前位置"; + + // 尝试从地址组件中获取更详细的信息 + if (addressComponent) { + const parts = []; + if (addressComponent.province) parts.push(addressComponent.province); + if (addressComponent.city) parts.push(addressComponent.city); + if (addressComponent.district) parts.push(addressComponent.district); + if (addressComponent.street) parts.push(addressComponent.street); + if (addressComponent.streetNumber) + parts.push(addressComponent.streetNumber); + + if (parts.length > 0) { + addressLabel = parts.join(""); + } + + poiName = + addressComponent.street || addressComponent.district || "当前位置"; + } + + setSelectedLocation({ + lat: lat, + lng: lng, + label: addressLabel, + poiname: poiName, + }); + + message.success("已获取当前位置"); + } else { + console.warn("定位失败:", result); + message.error( + "获取当前位置失败: " + (result?.message || "定位服务不可用"), + ); + } + }); + }, [addMarker]); + + const handleSendLocation = () => { + if (!selectedLocation || !selectedLocation.lat || !selectedLocation.lng) { + message.warning("请选择有效位置"); + return; + } + + const { lat, lng, label, poiname } = selectedLocation; + const content = ``; + + const messageId = +Date.now(); + const params = { + wechatAccountId: contract.wechatAccountId, + wechatChatroomId: contract?.chatroomId ? contract.id : 0, + wechatFriendId: contract?.chatroomId ? 0 : contract.id, + msgSubType: 0, + msgType: 48, + content: content, + seq: messageId, + }; + + // 构造本地消息 + const localMessage: ChatRecord = { + id: messageId, + wechatAccountId: contract.wechatAccountId, + wechatFriendId: contract?.chatroomId ? 0 : contract.id, + wechatChatroomId: contract?.chatroomId ? contract.id : 0, + tenantId: 0, + accountId: 0, + synergyAccountId: 0, + content: content, + msgType: 48, + msgSubType: 0, + msgSvrId: "", + isSend: true, + createTime: new Date().toISOString(), + isDeleted: false, + deleteTime: "", + sendStatus: 1, + wechatTime: Date.now(), + origin: 0, + msgId: 0, + recalled: false, + seq: messageId, + }; + addMessage(localMessage); + + sendCommand("CmdSendMessage", params); + + onClose(); + setSelectedLocation(null); + }; + + useEffect(() => { + if (visible) { + handleModalChange(true); + // 确保容器在模态框打开后可以接收事件 + setTimeout(() => { + const container = document.getElementById("amap-container"); + if (container) { + container.style.pointerEvents = "auto"; + container.style.cursor = "crosshair"; + console.log( + "容器样式已设置,pointerEvents:", + container.style.pointerEvents, + ); + } + }, 200); + } else { + handleModalChange(false); + } + }, [visible, handleModalChange]); + + return ( + +
    + + + + +
    + + {/* 显示点击位置的信息 */} + {selectedLocation && ( +
    +
    + 位置信息: +
    +
    + 地址: {selectedLocation.label || "加载中..."} +
    +
    + 坐标: {selectedLocation.lat},{" "} + {selectedLocation.lng} +
    +
    + )} + +
    + {mapLoading && ( +
    + +
    + )} +
    { + // 添加原生点击事件监听作为备用 + console.log("容器原生点击事件触发", e); + }} + >
    +
    +
    + ); +}; + +export default SelectMap; diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/index.tsx b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/index.tsx index 864ccb6e..9135624f 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/index.tsx +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/index.tsx @@ -1,5 +1,15 @@ -import React, { useEffect, useState } from "react"; -import { Layout, Input, Button, Modal, message, Tooltip } from "antd"; +import React, { useEffect, useState, useRef } from "react"; +import { + Layout, + Input, + Button, + Modal, + message, + Tooltip, + AutoComplete, + Input as AntInput, + Spin, +} from "antd"; import { SendOutlined, FolderOutlined, @@ -8,6 +18,7 @@ import { CloseOutlined, MessageOutlined, ReloadOutlined, + EnvironmentOutlined, } from "@ant-design/icons"; import { ContractData, weChatGroup, ChatRecord } from "@/pages/pc/ckbox/data"; import { useWebSocketStore } from "@/store/module/websocket/websocket"; @@ -23,6 +34,7 @@ import { manualTriggerAi, } from "@/store/module/weChat/weChat"; import { useContactStore } from "@/store/module/weChat/contacts"; +import SelectMap from "./components/selectMap"; const { Footer } = Layout; const { TextArea } = Input; @@ -326,6 +338,8 @@ const MessageEnter: React.FC = ({ contract }) => { updateShowChatRecordModel(!showChatRecordModel); }; + const [mapVisible, setMapVisible] = useState(false); + return ( <> {/* 聊天输入 */} @@ -423,6 +437,12 @@ const MessageEnter: React.FC = ({ contract }) => { } className={styles.toolbarButton} /> + @@ -877,7 +652,7 @@ const SelectMap: React.FC = ({
    )}
    { try { - console.log("getContactCount 调用参数:", { - userId, - type, - customerId, - groupIds, - exclude, - }); + // console.log("getContactCount 调用参数:", { + // userId, + // type, + // customerId, + // groupIds, + // exclude, + // }); const conditions: any[] = [ { field: "userId", operator: "equals", value: userId }, @@ -394,14 +394,14 @@ export class ContactManager { } } - console.log("查询条件:", conditions); + // console.log("查询条件:", conditions); const contacts = await contactUnifiedService.findWhereMultiple(conditions); - console.log( - `查询结果数量: ${contacts.length}, type: ${type}, groupIds: ${groupIds}`, - ); + // console.log( + // `查询结果数量: ${contacts.length}, type: ${type}, groupIds: ${groupIds}`, + // ); return contacts.length; } catch (error) { From 7dcbcfacc1f0b5899719e72a15449053f86d1f27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B6=85=E7=BA=A7=E8=80=81=E7=99=BD=E5=85=94?= Date: Fri, 21 Nov 2025 16:33:02 +0800 Subject: [PATCH 07/15] =?UTF-8?q?=E9=87=8D=E7=BD=AE=E4=B8=80=E4=B8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MessageEnter/components/selectMap.tsx | 773 +++++++++++------- 1 file changed, 499 insertions(+), 274 deletions(-) diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/selectMap.tsx b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/selectMap.tsx index 7492be75..7393c08c 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/selectMap.tsx +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/selectMap.tsx @@ -10,7 +10,7 @@ import { import { ContractData, weChatGroup, ChatRecord } from "@/pages/pc/ckbox/data"; import { useWebSocketStore } from "@/store/module/websocket/websocket"; -declare const qq: any; +declare const AMap: any; interface SelectMapProps { visible: boolean; @@ -34,7 +34,7 @@ const SelectMap: React.FC = ({ const pendingClickRef = useRef<{ lat: number; lng: number; - latlng: any; + lnglat: any; } | null>(null); // 保存待处理的点击坐标 const [options, setOptions] = useState([]); const [mapLoading, setMapLoading] = useState(true); @@ -56,103 +56,282 @@ const SelectMap: React.FC = ({ }, []); const addMarker = useCallback( - (latlng: any) => { - console.log("addMarker 调用,坐标:", latlng); - - const qq = (window as any).qq; - if (!qq || !qq.maps || !mapRef.current) { - console.error("地图API未加载或地图未初始化"); - return; - } + (lnglat: any) => { + console.log("addMarker 调用,坐标:", lnglat); // 先清除所有现有的标记点 clearAllMarkers(); - // 创建新的标记点(腾讯地图默认红色标记) - const newMarker = new qq.maps.Marker({ - position: latlng, + // 创建红色图标,保持默认倒水滴形状 + const redIcon = new AMap.Icon({ + size: new AMap.Size(25, 34), // 默认标记点尺寸 + image: "https://webapi.amap.com/theme/v1.3/markers/n/mark_r.png", // 红色标记点图片 + imageOffset: new AMap.Pixel(0, 0), + imageSize: new AMap.Size(25, 34), + }); + + // 创建新的标记点 + const newMarker = new AMap.Marker({ + position: lnglat, map: mapRef.current, + icon: redIcon, }); // 将新标记点添加到数组中 markersRef.current.push(newMarker); - mapRef.current.setCenter(latlng); + mapRef.current.setCenter(lnglat); mapRef.current.setZoom(16); // 确保缩放到合适级别 console.log("新 marker 已添加并居中"); }, [clearAllMarkers], ); - // 通用的地址获取函数(使用腾讯地图逆地理编码API) - const getAddressForLocation = useCallback((lat: number, lng: number) => { - console.log("=== getAddressForLocation 调用 ==="); - console.log("坐标:", { lat, lng }); + // 通用的地址获取函数 + const getAddressForLocation = useCallback( + (lat: number, lng: number, lnglat: any) => { + console.log("=== getAddressForLocation 调用 ==="); + console.log("坐标:", { lat, lng }); + console.log( + "Geocoder ref 状态:", + geocoderRef.current ? "存在" : "不存在", + ); - // 使用腾讯地图逆地理编码API - const key = "5ZSBZ-23ICU-XDKVU-4ZQ7Z-O35AJ-XUF6S"; - const url = `https://apis.map.qq.com/ws/geocoder/v1/?location=${lat},${lng}&key=${key}&get_poi=1`; + // 检查 Geocoder 是否已初始化 + if (!geocoderRef.current) { + console.warn("Geocoder 未初始化,无法获取地址"); + return false; + } - fetch(url) - .then(response => response.json()) - .then(result => { - console.log("=== 腾讯地图逆地理编码回调 ==="); - console.log("Result:", result); + // 使用更精确的坐标进行查询 + // 高德地图 getAddress 支持 LngLat 对象或 [lng, lat] 数组 + let queryLnglat: any; + if (lnglat && typeof lnglat.getLat === "function") { + // 如果传入的是 LngLat 对象,直接使用 + queryLnglat = lnglat; + } else { + // 否则创建新的 LngLat 对象或使用数组格式 + try { + queryLnglat = new AMap.LngLat(lng, lat); + } catch (error) { + console.error("创建 LngLat 对象失败,使用数组格式:", error); + queryLnglat = [lng, lat]; + } + } + console.log("调用 geocoder.getAddress,坐标:", queryLnglat); - if (result.status === 0 && result.result) { - const data = result.result; - const formattedAddress = - data.formatted_addresses?.recommend || data.address || ""; - const addressComponent = data.address_component || {}; - const pois = data.pois || []; + try { + geocoderRef.current.getAddress( + queryLnglat, + (status: string, result: any) => { + console.log("=== Geocoder 回调触发(通用函数) ==="); + console.log("Status:", status); + console.log( + "Result:", + result ? JSON.stringify(result, null, 2) : "null", + ); - // 构建详细地址信息 - let addressLabel = formattedAddress; - let poiName = "点击位置"; + if ( + status === "complete" && + result && + result.info === "OK" && + result.regeocode + ) { + const regeocode = result.regeocode; + const formattedAddress = regeocode.formattedAddress || ""; + const addressComponent = regeocode.addressComponent || {}; - // 优先级1: 如果有POI信息,优先使用POI名称 - if (pois.length > 0) { - const poi = pois[0]; - poiName = poi.title || poiName; - const poiAddress = poi.address || ""; - addressLabel = poiAddress - ? `${poiName} ${poiAddress}` - : `${poiName} ${formattedAddress}`; - } - // 优先级2: 组合地址组件 - else { - const parts = []; - if (addressComponent.province) - parts.push(addressComponent.province); - if (addressComponent.city) parts.push(addressComponent.city); - if (addressComponent.district) - parts.push(addressComponent.district); - if (addressComponent.street) parts.push(addressComponent.street); - if (addressComponent.street_number) - parts.push(addressComponent.street_number); + // 构建详细地址信息 + let addressLabel = formattedAddress; + let poiName = "点击位置"; - if (parts.length > 0) { - const fullAddress = parts.join(""); - poiName = - addressComponent.street || - addressComponent.district || - "点击位置"; - addressLabel = fullAddress || formattedAddress; + // 优先级1: 如果有POI信息,优先使用POI名称 + if (regeocode.pois && regeocode.pois.length > 0) { + const poi = regeocode.pois[0]; + poiName = poi.name || poiName; + const poiAddress = poi.address || ""; + addressLabel = poiAddress + ? `${poiName} ${poiAddress}` + : `${poiName} ${formattedAddress}`; + } + // 优先级2: 如果有建筑物信息 + else if (regeocode.buildings && regeocode.buildings.length > 0) { + const building = regeocode.buildings[0]; + poiName = building.name || addressComponent.building || poiName; + addressLabel = `${poiName} ${formattedAddress}`; + } + // 优先级3: 如果有AOI(兴趣区域)信息 + else if (regeocode.aois && regeocode.aois.length > 0) { + const aoi = regeocode.aois[0]; + poiName = aoi.name || poiName; + addressLabel = `${poiName} ${formattedAddress}`; + } + // 优先级4: 使用地址组件构建详细地址 + else if (addressComponent.building) { + poiName = addressComponent.building; + addressLabel = `${poiName} ${formattedAddress}`; + } + // 优先级5: 组合地址组件 + else { + const parts = []; + if (addressComponent.province) + parts.push(addressComponent.province); + if (addressComponent.city) parts.push(addressComponent.city); + if (addressComponent.district) + parts.push(addressComponent.district); + if (addressComponent.township) + parts.push(addressComponent.township); + if (addressComponent.street) + parts.push(addressComponent.street); + if (addressComponent.streetNumber) + parts.push(addressComponent.streetNumber); + + if (parts.length > 0) { + const fullAddress = parts.join(""); + poiName = + addressComponent.street || + addressComponent.district || + "点击位置"; + addressLabel = fullAddress || formattedAddress; + } else { + addressLabel = + formattedAddress || `纬度: ${lat}, 经度: ${lng}`; + } + } + + setSelectedLocation({ + lat: lat, + lng: lng, + label: addressLabel, + poiname: poiName, + }); + + message.success("地址信息获取成功"); } else { - addressLabel = formattedAddress || `纬度: ${lat}, 经度: ${lng}`; + console.warn("=== Geocoder 返回异常,尝试 PlaceSearch ==="); + console.warn("Status:", status); + console.warn("Result:", result); + // Geocoder 失败,使用 PlaceSearch 作为备用 + if (placeSearchRef.current) { + try { + const searchLnglat = lnglat || [lng, lat]; + placeSearchRef.current.searchNearBy( + "", + searchLnglat, + 1000, + (searchStatus: string, searchResult: any) => { + if ( + searchStatus === "complete" && + searchResult && + searchResult.info === "OK" && + searchResult.poiList?.pois?.length > 0 + ) { + const poi = searchResult.poiList.pois[0]; + const poiLabel = poi.address + ? `${poi.name} ${poi.address}` + : poi.name; + setSelectedLocation({ + lat: lat, + lng: lng, + label: poiLabel, + poiname: poi.name, + }); + message.success("通过附近搜索获取到地址信息"); + } else { + console.warn("PlaceSearch 返回异常:", { + status: searchStatus, + result: searchResult, + }); + const coordLabel = `纬度: ${lat}, 经度: ${lng}`; + setSelectedLocation({ + lat: lat, + lng: lng, + label: coordLabel, + poiname: "点击位置", + }); + message.warning("无法获取详细地址信息,但坐标已记录"); + } + }, + ); + } catch (placeSearchError) { + console.error( + "PlaceSearch.searchNearBy 调用异常:", + placeSearchError, + ); + const coordLabel = `纬度: ${lat}, 经度: ${lng}`; + setSelectedLocation({ + lat: lat, + lng: lng, + label: coordLabel, + poiname: "点击位置", + }); + message.warning("无法获取详细地址信息,但坐标已记录"); + } + } else { + const coordLabel = `纬度: ${lat}, 经度: ${lng}`; + setSelectedLocation({ + lat: lat, + lng: lng, + label: coordLabel, + poiname: "点击位置", + }); + message.warning("无法获取详细地址信息,但坐标已记录"); + } } + }, + ); + } catch (error) { + console.error("=== Geocoder.getAddress 调用异常 ===", error); + // 如果 geocoder 调用失败,尝试使用 PlaceSearch + if (placeSearchRef.current) { + console.log("尝试使用 PlaceSearch 作为备用方案"); + try { + placeSearchRef.current.searchNearBy( + "", + lnglat || [lng, lat], + 1000, + (status: string, result: any) => { + if ( + status === "complete" && + result && + result.info === "OK" && + result.poiList?.pois?.length > 0 + ) { + const poi = result.poiList.pois[0]; + const poiLabel = poi.address + ? `${poi.name} ${poi.address}` + : poi.name; + setSelectedLocation({ + lat: lat, + lng: lng, + label: poiLabel, + poiname: poi.name, + }); + message.success("通过附近搜索获取到地址信息"); + } else { + const coordLabel = `纬度: ${lat}, 经度: ${lng}`; + setSelectedLocation({ + lat: lat, + lng: lng, + label: coordLabel, + poiname: "点击位置", + }); + message.warning("无法获取详细地址信息,但坐标已记录"); + } + }, + ); + } catch (placeSearchError) { + console.error("PlaceSearch 调用也失败:", placeSearchError); + const coordLabel = `纬度: ${lat}, 经度: ${lng}`; + setSelectedLocation({ + lat: lat, + lng: lng, + label: coordLabel, + poiname: "点击位置", + }); + message.warning("无法获取详细地址信息,但坐标已记录"); } - - setSelectedLocation({ - lat: lat, - lng: lng, - label: addressLabel, - poiname: poiName, - }); - - message.success("地址信息获取成功"); } else { - console.warn("逆地理编码返回异常:", result); const coordLabel = `纬度: ${lat}, 经度: ${lng}`; setSelectedLocation({ lat: lat, @@ -162,48 +341,20 @@ const SelectMap: React.FC = ({ }); message.warning("无法获取详细地址信息,但坐标已记录"); } - }) - .catch(error => { - console.error("=== 逆地理编码API调用异常 ===", error); - const coordLabel = `纬度: ${lat}, 经度: ${lng}`; - setSelectedLocation({ - lat: lat, - lng: lng, - label: coordLabel, - poiname: "点击位置", - }); - message.warning("无法获取详细地址信息,但坐标已记录"); - }); - }, []); + return false; + } + + return true; + }, + [], + ); const initMap = useCallback(() => { console.log("initMap 执行中"); setMapLoading(true); - // 检查 qq 对象是否已加载 - if (!(window as any).qq || !(window as any).qq.maps) { - console.warn("腾讯地图API尚未加载,等待中..."); - // 等待最多5秒,每100ms检查一次 - let attempts = 0; - const maxAttempts = 50; - const checkInterval = setInterval(() => { - attempts++; - if ((window as any).qq && (window as any).qq.maps) { - clearInterval(checkInterval); - console.log("腾讯地图API已加载,继续初始化"); - initMap(); - } else if (attempts >= maxAttempts) { - clearInterval(checkInterval); - console.error("腾讯地图API加载超时"); - message.error("地图加载失败,请刷新页面重试"); - setMapLoading(false); - } - }, 100); - return; - } - // 确保容器存在 - const container = document.getElementById("tencent-map-container"); + const container = document.getElementById("amap-container"); if (!container) { console.error("地图容器不存在"); setMapLoading(false); @@ -216,13 +367,10 @@ const SelectMap: React.FC = ({ container.style.position = "relative"; container.style.zIndex = "1"; - const qq = (window as any).qq; - - // 腾讯地图初始化(注意:坐标顺序是 lat, lng) - const center = new qq.maps.LatLng(24.470164, 118.113653); // 默认中心 - const map = new qq.maps.Map("tencent-map-container", { + const map = new AMap.Map("amap-container", { zoom: 16, - center: center, + center: [118.113653, 24.470164], // 默认中心 + viewMode: "2D", // 明确指定视图模式 }); mapRef.current = map; @@ -232,91 +380,165 @@ const SelectMap: React.FC = ({ setMapLoading(false); }, 10000); // 10秒超时 - // 标记服务已初始化 - geocoderRef.current = true; // 使用HTTP API,不需要实例 - placeSearchRef.current = true; // 使用HTTP API,不需要实例 - - // 获取当前位置(使用浏览器原生定位) - console.log("=== 开始获取当前位置 ==="); - if (navigator.geolocation) { - navigator.geolocation.getCurrentPosition( - position => { - const lat = position.coords.latitude; - const lng = position.coords.longitude; - const qq = (window as any).qq; - if (!qq || !qq.maps) { - console.error("腾讯地图API未加载"); - return; - } - const latlng = new qq.maps.LatLng(lat, lng); - - console.log("定位成功,当前位置:", { lat, lng }); - - // 将地图中心设置为当前位置 - map.setCenter(latlng); - map.setZoom(16); - - // 添加当前位置标记 - addMarker(latlng); - - // 获取地址信息 - getAddressForLocation(lat, lng); - }, - error => { - console.warn("定位失败:", error); - message.warning( - "无法获取当前位置,请手动点击地图选择位置。原因: " + - (error.message || "定位服务不可用"), - ); - // 定位失败时,使用默认中心点 - console.log("使用默认中心点"); + // 立即加载插件,不等待地图 complete 事件 + console.log("=== 立即开始加载 AMap 插件(地图创建后) ==="); + AMap.plugin( + [ + "AMap.AutoComplete", + "AMap.PlaceSearch", + "AMap.Geocoder", + "AMap.Geolocation", + ], + error => { + if (error) { + console.error("=== AMap 插件加载失败 ===", error); + message.error("地图插件加载失败,部分功能可能不可用"); clearTimeout(loadingTimeout); setMapLoading(false); - }, - { - enableHighAccuracy: true, - timeout: 10000, - maximumAge: 0, - }, - ); - } else { - console.warn("浏览器不支持定位"); - message.warning("浏览器不支持定位功能"); - clearTimeout(loadingTimeout); - setMapLoading(false); - } + return; + } + console.log("=== AMap 插件加载成功 ==="); - // 如果有待处理的点击坐标,立即处理它 - if (pendingClickRef.current) { - console.log("=== 检测到待处理的点击坐标,立即处理 ==="); - const { lat, lng } = pendingClickRef.current; - console.log("待处理坐标:", { lat, lng }); - setTimeout(() => { - getAddressForLocation(lat, lng); - console.log("✓ 待处理的坐标已成功获取地址"); - pendingClickRef.current = null; // 清除待处理坐标 - }, 100); - } + // 立即创建 PlaceSearch 和 Geocoder 实例 + const placeSearch = new AMap.PlaceSearch({ + city: "全国", + map: map, + }); + placeSearchRef.current = placeSearch; + console.log("PlaceSearch 实例已创建"); - // 绑定点击事件 - console.log("立即绑定点击事件"); - qq.maps.event.addListener(map, "click", (e: any) => { + const geocoder = new AMap.Geocoder({ + city: "全国", + radius: 1000, // 搜索半径,单位米 + extensions: "all", // 返回详细信息,包括POI、建筑物等 + }); + geocoderRef.current = geocoder; + console.log("Geocoder 实例已创建并保存到 ref,现在可以立即使用"); + + // 创建 Geolocation 实例用于获取当前位置 + const geolocation = new AMap.Geolocation({ + enableHighAccuracy: true, // 是否使用高精度定位,默认:true + timeout: 10000, // 超过10秒后停止定位,默认:无穷大 + maximumAge: 0, // 定位结果缓存0毫秒,默认:0 + convert: true, // 自动偏移坐标,偏移后的坐标为高德坐标,默认:true + showButton: false, // 显示定位按钮,默认:true + buttonPosition: "RB", // 定位按钮停靠位置,默认:'LB',左下角 + showMarker: false, // 定位成功后在定位到的位置显示点标记,默认:true + showCircle: false, // 定位成功后用圆圈表示定位精度范围,默认:true + panToLocation: false, // 定位成功后将定位到的位置作为地图中心点,默认:true + zoomToAccuracy: false, // 定位成功后调整地图视野范围使定位位置及精度范围视野内可见,默认:false + }); + geolocationRef.current = geolocation; + + // 获取当前位置 + console.log("=== 开始获取当前位置 ==="); + geolocation.getCurrentPosition((status: string, result: any) => { + console.log("=== 定位回调触发 ==="); + console.log("定位状态:", status); + console.log("定位结果:", result); + + if (status === "complete") { + const { position, formattedAddress, addressComponent } = result; + const lat = position.lat; + const lng = position.lng; + const lnglat = new AMap.LngLat(lng, lat); + + console.log("定位成功,当前位置:", { lat, lng }); + console.log("定位地址:", formattedAddress); + + // 将地图中心设置为当前位置 + map.setCenter(lnglat); + map.setZoom(16); + + // 添加当前位置标记 + addMarker(lnglat); + + // 设置选中位置信息 + let addressLabel = formattedAddress || `纬度: ${lat}, 经度: ${lng}`; + let poiName = "当前位置"; + + // 尝试从地址组件中获取更详细的信息 + if (addressComponent) { + const parts = []; + if (addressComponent.province) + parts.push(addressComponent.province); + if (addressComponent.city) parts.push(addressComponent.city); + if (addressComponent.district) + parts.push(addressComponent.district); + if (addressComponent.street) parts.push(addressComponent.street); + if (addressComponent.streetNumber) + parts.push(addressComponent.streetNumber); + + if (parts.length > 0) { + addressLabel = parts.join(""); + } + + poiName = + addressComponent.street || + addressComponent.district || + "当前位置"; + } + + setSelectedLocation({ + lat: lat, + lng: lng, + label: addressLabel, + poiname: poiName, + }); + + message.success("已获取当前位置"); + } else { + console.warn("定位失败:", result); + message.warning( + "无法获取当前位置,请手动点击地图选择位置。原因: " + + (result.message || "定位服务不可用"), + ); + // 定位失败时,使用默认中心点 + console.log("使用默认中心点"); + } + }); + + // 如果有待处理的点击坐标,立即处理它 + if (pendingClickRef.current) { + console.log("=== 检测到待处理的点击坐标,立即处理 ==="); + const { lat, lng, lnglat } = pendingClickRef.current; + console.log("待处理坐标:", { lat, lng }); + setTimeout(() => { + const success = getAddressForLocation(lat, lng, lnglat); + if (success) { + console.log("✓ 待处理的坐标已成功获取地址"); + pendingClickRef.current = null; // 清除待处理坐标 + } else { + console.warn("待处理的坐标获取地址失败"); + } + }, 100); + } + }, + ); + + // 立即绑定点击事件,插件可能已加载或正在加载 + console.log("立即绑定点击事件(插件可能已初始化)"); + map.on("click", (e: any) => { console.log("=== 地图点击事件触发 ==="); console.log("点击事件对象:", e); - console.log("点击位置对象:", e.latLng); + console.log("点击位置对象:", e.lnglat); + console.log( + "Geocoder ref 状态:", + geocoderRef.current ? "已初始化" : "未初始化", + ); - if (!e || !e.latLng) { - console.error("点击事件无效,缺少 latLng"); + if (!e || !e.lnglat) { + console.error("点击事件无效,缺少 lnglat"); return; } - const latlng = e.latLng; - const lat = latlng.getLat(); - const lng = latlng.getLng(); + const lnglat = e.lnglat; + const lat = lnglat.getLat(); + const lng = lnglat.getLng(); console.log(`点击坐标 - 纬度: ${lat}, 经度: ${lng}`); // 立即添加标记和居中 - addMarker(latlng); + addMarker(lnglat); // 设置基本 selectedLocation(至少有坐标) setSelectedLocation({ @@ -326,12 +548,19 @@ const SelectMap: React.FC = ({ poiname: "点击位置", }); - // 立即获取地址 - getAddressForLocation(lat, lng); + // 如果 Geocoder 已初始化,立即使用它 + if (geocoderRef.current) { + console.log("Geocoder 已初始化,立即获取地址"); + getAddressForLocation(lat, lng, lnglat); + } else { + console.log("Geocoder 未初始化,保存坐标待插件加载完成后处理"); + // 保存待处理的坐标 + pendingClickRef.current = { lat, lng, lnglat }; + } }); // 等待地图完全加载后关闭 loading - qq.maps.event.addListener(map, "tilesloaded", () => { + map.on("complete", () => { console.log("地图加载完成"); // 清除超时定时器 clearTimeout(loadingTimeout); @@ -347,63 +576,47 @@ const SelectMap: React.FC = ({ console.log("地图容器指针事件已启用"); } }); + + // 如果地图加载失败,也设置加载完成 + map.on("error", (error: any) => { + console.error("地图加载错误:", error); + clearTimeout(loadingTimeout); + setMapLoading(false); + message.error("地图加载失败"); + }); }, [addMarker, getAddressForLocation]); const handleSearch = value => { if (value) { - // 使用腾讯地图搜索建议API - const key = "5ZSBZ-23ICU-XDKVU-4ZQ7Z-O35AJ-XUF6S"; - const url = `https://apis.map.qq.com/ws/place/v1/suggestion?keyword=${encodeURIComponent(value)}&key=${key}®ion=全国`; - - fetch(url) - .then(response => response.json()) - .then(result => { - if (result.status === 0 && result.data) { + AMap.plugin("AMap.AutoComplete", () => { + const auto = new AMap.AutoComplete({ city: "全国" }); + auto.search(value, (status, result) => { + if (status === "complete") { setOptions( - result.data.map((item: any) => ({ - value: item.title, - data: { - name: item.title, - address: item.address, - district: item.adname, - location: { - lat: item.location.lat, - lng: item.location.lng, - }, - }, + result.tips.map(tip => ({ + value: tip.name, + data: tip, })), ); - } else { - setOptions([]); } - }) - .catch(error => { - console.error("搜索建议API调用失败:", error); - setOptions([]); }); + }); } else { setOptions([]); } }; const onSelect = (value, option) => { - const qq = (window as any).qq; - if (!qq || !qq.maps) { - message.error("地图API未加载,请稍后再试"); - return; - } const { district, address, name, location } = option.data; - const latlng = new qq.maps.LatLng(location.lat, location.lng); + const lnglat = location; setSelectedLocation({ - lat: location.lat, - lng: location.lng, - label: `${name} ${address || district || ""}`, + lat: lnglat.lat, + lng: lnglat.lng, + label: `${name} ${address || district}`, poiname: name, }); - addMarker(latlng); - if (mapRef.current) { - mapRef.current.setCenter(latlng); - } + addMarker(lnglat); + mapRef.current.setCenter(lnglat); }; const handleModalChange = useCallback( @@ -412,17 +625,9 @@ const SelectMap: React.FC = ({ console.log("模态打开:开始加载地图脚本"); setMapLoading(true); setSelectedLocation(null); - - // 检查是否已加载腾讯地图脚本 - if ((window as any).qq && (window as any).qq.maps) { - console.log("腾讯地图脚本已加载,直接初始化"); - setTimeout(() => initMap(), 100); - return; - } - const script = document.createElement("script"); script.src = - "https://map.qq.com/api/gljs?v=1.exp&key=5ZSBZ-23ICU-XDKVU-4ZQ7Z-O35AJ-XUF6S"; + "https://webapi.amap.com/maps?v=1.4.15&key=79370028f5763e46742125ed2e900c76&plugin=AMap.PlaceSearch,AMap.AutoComplete,AMap.Geocoder,AMap.Geolocation"; script.async = true; script.onload = () => { console.log("脚本加载成功:开始初始化地图"); @@ -443,14 +648,16 @@ const SelectMap: React.FC = ({ placeSearchRef.current = null; geolocationRef.current = null; // Cleanup on close - const scripts = document.querySelectorAll('script[src*="map.qq.com"]'); + const scripts = document.querySelectorAll( + 'script[src*="webapi.amap.com"]', + ); scripts.forEach(s => { if (document.body.contains(s)) { document.body.removeChild(s); } }); if (mapRef.current) { - // 腾讯地图没有destroy方法,设置为null即可 + mapRef.current.destroy(); mapRef.current = null; } setMapLoading(false); @@ -462,57 +669,75 @@ const SelectMap: React.FC = ({ // 手动获取当前位置 const handleGetCurrentLocation = useCallback(() => { - if (!navigator.geolocation) { - message.warning("浏览器不支持定位服务"); - return; - } - - const qq = (window as any).qq; - if (!qq || !qq.maps) { - message.error("地图API未加载,请稍后再试"); + if (!geolocationRef.current) { + message.warning("定位服务未初始化,请稍候再试"); return; } console.log("=== 手动触发获取当前位置 ==="); message.loading({ content: "正在获取当前位置...", key: "location" }); - navigator.geolocation.getCurrentPosition( - position => { - message.destroy("location"); - console.log("=== 手动定位回调触发 ==="); - const lat = position.coords.latitude; - const lng = position.coords.longitude; - const latlng = new qq.maps.LatLng(lat, lng); + geolocationRef.current.getCurrentPosition((status: string, result: any) => { + message.destroy("location"); + console.log("=== 手动定位回调触发 ==="); + console.log("定位状态:", status); + console.log("定位结果:", result); + + if (status === "complete" && result && result.position) { + const { position, formattedAddress, addressComponent } = result; + const lat = position.lat; + const lng = position.lng; + const lnglat = new AMap.LngLat(lng, lat); console.log("定位成功,当前位置:", { lat, lng }); // 将地图中心设置为当前位置 if (mapRef.current) { - mapRef.current.setCenter(latlng); + mapRef.current.setCenter(lnglat); mapRef.current.setZoom(16); } // 添加当前位置标记 - addMarker(latlng); + addMarker(lnglat); + + // 设置选中位置信息 + let addressLabel = formattedAddress || `纬度: ${lat}, 经度: ${lng}`; + let poiName = "当前位置"; + + // 尝试从地址组件中获取更详细的信息 + if (addressComponent) { + const parts = []; + if (addressComponent.province) parts.push(addressComponent.province); + if (addressComponent.city) parts.push(addressComponent.city); + if (addressComponent.district) parts.push(addressComponent.district); + if (addressComponent.street) parts.push(addressComponent.street); + if (addressComponent.streetNumber) + parts.push(addressComponent.streetNumber); + + if (parts.length > 0) { + addressLabel = parts.join(""); + } + + poiName = + addressComponent.street || addressComponent.district || "当前位置"; + } + + setSelectedLocation({ + lat: lat, + lng: lng, + label: addressLabel, + poiname: poiName, + }); - // 获取地址信息 - getAddressForLocation(lat, lng); message.success("已获取当前位置"); - }, - error => { - message.destroy("location"); - console.warn("定位失败:", error); + } else { + console.warn("定位失败:", result); message.error( - "获取当前位置失败: " + (error.message || "定位服务不可用"), + "获取当前位置失败: " + (result?.message || "定位服务不可用"), ); - }, - { - enableHighAccuracy: true, - timeout: 10000, - maximumAge: 0, - }, - ); - }, [addMarker, getAddressForLocation]); + } + }); + }, [addMarker]); const handleSendLocation = () => { if (!selectedLocation || !selectedLocation.lat || !selectedLocation.lng) { @@ -571,7 +796,7 @@ const SelectMap: React.FC = ({ handleModalChange(true); // 确保容器在模态框打开后可以接收事件 setTimeout(() => { - const container = document.getElementById("tencent-map-container"); + const container = document.getElementById("amap-container"); if (container) { container.style.pointerEvents = "auto"; container.style.cursor = "crosshair"; @@ -607,7 +832,7 @@ const SelectMap: React.FC = ({ @@ -652,7 +877,7 @@ const SelectMap: React.FC = ({
    )}
    Date: Fri, 21 Nov 2025 18:30:12 +0800 Subject: [PATCH 08/15] =?UTF-8?q?=E8=85=BE=E8=AE=AF=E5=9C=B0=E5=9B=BE?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E9=94=81=E5=AE=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Touchkebao/index.html | 4 + .../components/selectMap.module.scss | 111 ++ .../MessageEnter/components/selectMap.tsx | 1235 +++++------------ 3 files changed, 487 insertions(+), 863 deletions(-) create mode 100644 Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/selectMap.module.scss diff --git a/Touchkebao/index.html b/Touchkebao/index.html index 92656ef9..d0dcd4e9 100644 --- a/Touchkebao/index.html +++ b/Touchkebao/index.html @@ -11,6 +11,10 @@ +
    diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/selectMap.module.scss b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/selectMap.module.scss new file mode 100644 index 00000000..f0a77bb3 --- /dev/null +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/selectMap.module.scss @@ -0,0 +1,111 @@ +.selectMapContainer { + display: flex; + flex-direction: column; + height: 600px; + gap: 16px; +} + +.searchArea { + flex-shrink: 0; + position: relative; +} + +.searchInput { + width: 100%; +} + +.searchResults { + position: absolute; + top: 100%; + left: 0; + right: 0; + z-index: 1000; + background: #fff; + border: 1px solid #e8e8e8; + border-radius: 4px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); + margin-top: 4px; + max-height: 300px; + overflow-y: auto; + + :global(.ant-list-item) { + cursor: pointer; + padding: 12px 16px; + transition: background-color 0.2s; + + &:hover { + background-color: #f5f5f5; + } + } +} + +.mapArea { + flex: 1; + position: relative; + border: 1px solid #e8e8e8; + border-radius: 4px; + overflow: hidden; +} + +.mapContainer { + width: 100%; + height: 100%; + min-height: 400px; +} + +.loadingOverlay { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(255, 255, 255, 0.8); + display: flex; + align-items: center; + justify-content: center; + z-index: 100; +} + +.locationInfo { + flex-shrink: 0; + padding: 12px 16px; + background: #f5f5f5; + border-radius: 4px; + border: 1px solid #e8e8e8; +} + +.locationLabel { + display: flex; + align-items: center; + gap: 8px; + font-size: 14px; + font-weight: 500; + color: #1890ff; + margin-bottom: 8px; +} + +.locationText { + font-size: 14px; + color: #333; + margin-bottom: 4px; + word-break: break-all; +} + +.locationCoords { + font-size: 12px; + color: #999; + font-family: "Monaco", "Menlo", "Ubuntu Mono", monospace; +} + +.resultItem { + :global(.ant-list-item-meta-title) { + font-size: 14px; + color: #333; + margin-bottom: 4px; + } + + :global(.ant-list-item-meta-description) { + font-size: 12px; + color: #999; + } +} diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/selectMap.tsx b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/selectMap.tsx index 7393c08c..d2658da6 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/selectMap.tsx +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/selectMap.tsx @@ -1,22 +1,45 @@ -import React, { useCallback, useEffect, useRef, useState } from "react"; -import { - AutoComplete, - Input as AntInput, - message, - Modal, - Spin, - Button, -} from "antd"; -import { ContractData, weChatGroup, ChatRecord } from "@/pages/pc/ckbox/data"; +import React, { useState, useEffect, useRef } from "react"; +import { Modal, Input, Button, List, message, Spin } from "antd"; +import { SearchOutlined, EnvironmentOutlined } from "@ant-design/icons"; import { useWebSocketStore } from "@/store/module/websocket/websocket"; +import styles from "./selectMap.module.scss"; -declare const AMap: any; +// 声明腾讯地图类型 +declare global { + interface Window { + qq: any; + } +} interface SelectMapProps { visible: boolean; onClose: () => void; - contract: ContractData | weChatGroup; - addMessage: (message: ChatRecord) => void; + contract?: any; + addMessage?: (message: any) => void; + onConfirm?: (locationXml: string) => void; +} + +interface SearchResult { + id: string; + title: string; + address: string; + location: { + lat: number; + lng: number; + }; + adcode?: string; + city?: string; + district?: string; +} + +interface LocationData { + x: string; // 经度 + y: string; // 纬度 + scale: string; // 缩放级别 + label: string; // 地址标签 + poiname: string; // POI名称 + maptype: string; // 地图类型 + poiid: string; // POI ID } const SelectMap: React.FC = ({ @@ -24,874 +47,360 @@ const SelectMap: React.FC = ({ onClose, contract, addMessage, + onConfirm, }) => { - const [selectedLocation, setSelectedLocation] = useState(null); - const mapRef = useRef(null); - const markersRef = useRef([]); // 使用数组保存所有标记点 - const geocoderRef = useRef(null); // 保存 Geocoder 实例 - const placeSearchRef = useRef(null); // 保存 PlaceSearch 实例 - const geolocationRef = useRef(null); // 保存 Geolocation 实例 - const pendingClickRef = useRef<{ - lat: number; - lng: number; - lnglat: any; - } | null>(null); // 保存待处理的点击坐标 - const [options, setOptions] = useState([]); - const [mapLoading, setMapLoading] = useState(true); - - const sendCommand = useWebSocketStore(state => state.sendCommand); - - // 清除所有标记点 - const clearAllMarkers = useCallback(() => { - if (markersRef.current && markersRef.current.length > 0) { - console.log(`清除 ${markersRef.current.length} 个标记点`); - markersRef.current.forEach(marker => { - if (marker) { - marker.setMap(null); - marker = null; - } - }); - markersRef.current = []; - } - }, []); - - const addMarker = useCallback( - (lnglat: any) => { - console.log("addMarker 调用,坐标:", lnglat); - - // 先清除所有现有的标记点 - clearAllMarkers(); - - // 创建红色图标,保持默认倒水滴形状 - const redIcon = new AMap.Icon({ - size: new AMap.Size(25, 34), // 默认标记点尺寸 - image: "https://webapi.amap.com/theme/v1.3/markers/n/mark_r.png", // 红色标记点图片 - imageOffset: new AMap.Pixel(0, 0), - imageSize: new AMap.Size(25, 34), - }); - - // 创建新的标记点 - const newMarker = new AMap.Marker({ - position: lnglat, - map: mapRef.current, - icon: redIcon, - }); - - // 将新标记点添加到数组中 - markersRef.current.push(newMarker); - - mapRef.current.setCenter(lnglat); - mapRef.current.setZoom(16); // 确保缩放到合适级别 - console.log("新 marker 已添加并居中"); - }, - [clearAllMarkers], + const [searchValue, setSearchValue] = useState(""); + const [searchResults, setSearchResults] = useState([]); + const [isSearching, setIsSearching] = useState(false); + const [selectedLocation, setSelectedLocation] = useState( + null, ); + const [map, setMap] = useState(null); + const [isReverseGeocoding, setIsReverseGeocoding] = useState(false); + const mapContainerRef = useRef(null); + const geocoderRef = useRef(null); + const searchServiceRef = useRef(null); + const markerRef = useRef(null); + const { sendCommand } = useWebSocketStore.getState(); - // 通用的地址获取函数 - const getAddressForLocation = useCallback( - (lat: number, lng: number, lnglat: any) => { - console.log("=== getAddressForLocation 调用 ==="); - console.log("坐标:", { lat, lng }); - console.log( - "Geocoder ref 状态:", - geocoderRef.current ? "存在" : "不存在", - ); - - // 检查 Geocoder 是否已初始化 - if (!geocoderRef.current) { - console.warn("Geocoder 未初始化,无法获取地址"); - return false; - } - - // 使用更精确的坐标进行查询 - // 高德地图 getAddress 支持 LngLat 对象或 [lng, lat] 数组 - let queryLnglat: any; - if (lnglat && typeof lnglat.getLat === "function") { - // 如果传入的是 LngLat 对象,直接使用 - queryLnglat = lnglat; - } else { - // 否则创建新的 LngLat 对象或使用数组格式 - try { - queryLnglat = new AMap.LngLat(lng, lat); - } catch (error) { - console.error("创建 LngLat 对象失败,使用数组格式:", error); - queryLnglat = [lng, lat]; - } - } - console.log("调用 geocoder.getAddress,坐标:", queryLnglat); - - try { - geocoderRef.current.getAddress( - queryLnglat, - (status: string, result: any) => { - console.log("=== Geocoder 回调触发(通用函数) ==="); - console.log("Status:", status); - console.log( - "Result:", - result ? JSON.stringify(result, null, 2) : "null", - ); - - if ( - status === "complete" && - result && - result.info === "OK" && - result.regeocode - ) { - const regeocode = result.regeocode; - const formattedAddress = regeocode.formattedAddress || ""; - const addressComponent = regeocode.addressComponent || {}; - - // 构建详细地址信息 - let addressLabel = formattedAddress; - let poiName = "点击位置"; - - // 优先级1: 如果有POI信息,优先使用POI名称 - if (regeocode.pois && regeocode.pois.length > 0) { - const poi = regeocode.pois[0]; - poiName = poi.name || poiName; - const poiAddress = poi.address || ""; - addressLabel = poiAddress - ? `${poiName} ${poiAddress}` - : `${poiName} ${formattedAddress}`; - } - // 优先级2: 如果有建筑物信息 - else if (regeocode.buildings && regeocode.buildings.length > 0) { - const building = regeocode.buildings[0]; - poiName = building.name || addressComponent.building || poiName; - addressLabel = `${poiName} ${formattedAddress}`; - } - // 优先级3: 如果有AOI(兴趣区域)信息 - else if (regeocode.aois && regeocode.aois.length > 0) { - const aoi = regeocode.aois[0]; - poiName = aoi.name || poiName; - addressLabel = `${poiName} ${formattedAddress}`; - } - // 优先级4: 使用地址组件构建详细地址 - else if (addressComponent.building) { - poiName = addressComponent.building; - addressLabel = `${poiName} ${formattedAddress}`; - } - // 优先级5: 组合地址组件 - else { - const parts = []; - if (addressComponent.province) - parts.push(addressComponent.province); - if (addressComponent.city) parts.push(addressComponent.city); - if (addressComponent.district) - parts.push(addressComponent.district); - if (addressComponent.township) - parts.push(addressComponent.township); - if (addressComponent.street) - parts.push(addressComponent.street); - if (addressComponent.streetNumber) - parts.push(addressComponent.streetNumber); - - if (parts.length > 0) { - const fullAddress = parts.join(""); - poiName = - addressComponent.street || - addressComponent.district || - "点击位置"; - addressLabel = fullAddress || formattedAddress; - } else { - addressLabel = - formattedAddress || `纬度: ${lat}, 经度: ${lng}`; - } - } - - setSelectedLocation({ - lat: lat, - lng: lng, - label: addressLabel, - poiname: poiName, - }); - - message.success("地址信息获取成功"); - } else { - console.warn("=== Geocoder 返回异常,尝试 PlaceSearch ==="); - console.warn("Status:", status); - console.warn("Result:", result); - // Geocoder 失败,使用 PlaceSearch 作为备用 - if (placeSearchRef.current) { - try { - const searchLnglat = lnglat || [lng, lat]; - placeSearchRef.current.searchNearBy( - "", - searchLnglat, - 1000, - (searchStatus: string, searchResult: any) => { - if ( - searchStatus === "complete" && - searchResult && - searchResult.info === "OK" && - searchResult.poiList?.pois?.length > 0 - ) { - const poi = searchResult.poiList.pois[0]; - const poiLabel = poi.address - ? `${poi.name} ${poi.address}` - : poi.name; - setSelectedLocation({ - lat: lat, - lng: lng, - label: poiLabel, - poiname: poi.name, - }); - message.success("通过附近搜索获取到地址信息"); - } else { - console.warn("PlaceSearch 返回异常:", { - status: searchStatus, - result: searchResult, - }); - const coordLabel = `纬度: ${lat}, 经度: ${lng}`; - setSelectedLocation({ - lat: lat, - lng: lng, - label: coordLabel, - poiname: "点击位置", - }); - message.warning("无法获取详细地址信息,但坐标已记录"); - } - }, - ); - } catch (placeSearchError) { - console.error( - "PlaceSearch.searchNearBy 调用异常:", - placeSearchError, - ); - const coordLabel = `纬度: ${lat}, 经度: ${lng}`; - setSelectedLocation({ - lat: lat, - lng: lng, - label: coordLabel, - poiname: "点击位置", - }); - message.warning("无法获取详细地址信息,但坐标已记录"); - } - } else { - const coordLabel = `纬度: ${lat}, 经度: ${lng}`; - setSelectedLocation({ - lat: lat, - lng: lng, - label: coordLabel, - poiname: "点击位置", - }); - message.warning("无法获取详细地址信息,但坐标已记录"); - } - } - }, - ); - } catch (error) { - console.error("=== Geocoder.getAddress 调用异常 ===", error); - // 如果 geocoder 调用失败,尝试使用 PlaceSearch - if (placeSearchRef.current) { - console.log("尝试使用 PlaceSearch 作为备用方案"); - try { - placeSearchRef.current.searchNearBy( - "", - lnglat || [lng, lat], - 1000, - (status: string, result: any) => { - if ( - status === "complete" && - result && - result.info === "OK" && - result.poiList?.pois?.length > 0 - ) { - const poi = result.poiList.pois[0]; - const poiLabel = poi.address - ? `${poi.name} ${poi.address}` - : poi.name; - setSelectedLocation({ - lat: lat, - lng: lng, - label: poiLabel, - poiname: poi.name, - }); - message.success("通过附近搜索获取到地址信息"); - } else { - const coordLabel = `纬度: ${lat}, 经度: ${lng}`; - setSelectedLocation({ - lat: lat, - lng: lng, - label: coordLabel, - poiname: "点击位置", - }); - message.warning("无法获取详细地址信息,但坐标已记录"); - } - }, - ); - } catch (placeSearchError) { - console.error("PlaceSearch 调用也失败:", placeSearchError); - const coordLabel = `纬度: ${lat}, 经度: ${lng}`; - setSelectedLocation({ - lat: lat, - lng: lng, - label: coordLabel, - poiname: "点击位置", - }); - message.warning("无法获取详细地址信息,但坐标已记录"); - } - } else { - const coordLabel = `纬度: ${lat}, 经度: ${lng}`; - setSelectedLocation({ - lat: lat, - lng: lng, - label: coordLabel, - poiname: "点击位置", - }); - message.warning("无法获取详细地址信息,但坐标已记录"); - } - return false; - } - - return true; - }, - [], - ); - - const initMap = useCallback(() => { - console.log("initMap 执行中"); - setMapLoading(true); - - // 确保容器存在 - const container = document.getElementById("amap-container"); - if (!container) { - console.error("地图容器不存在"); - setMapLoading(false); - return; - } - - // 确保容器样式正确 - container.style.pointerEvents = "auto"; - container.style.cursor = "crosshair"; - container.style.position = "relative"; - container.style.zIndex = "1"; - - const map = new AMap.Map("amap-container", { - zoom: 16, - center: [118.113653, 24.470164], // 默认中心 - viewMode: "2D", // 明确指定视图模式 - }); - mapRef.current = map; - - // 添加超时机制,防止 loading 一直显示 - const loadingTimeout = setTimeout(() => { - console.warn("地图加载超时,强制关闭 loading"); - setMapLoading(false); - }, 10000); // 10秒超时 - - // 立即加载插件,不等待地图 complete 事件 - console.log("=== 立即开始加载 AMap 插件(地图创建后) ==="); - AMap.plugin( - [ - "AMap.AutoComplete", - "AMap.PlaceSearch", - "AMap.Geocoder", - "AMap.Geolocation", - ], - error => { - if (error) { - console.error("=== AMap 插件加载失败 ===", error); - message.error("地图插件加载失败,部分功能可能不可用"); - clearTimeout(loadingTimeout); - setMapLoading(false); - return; - } - console.log("=== AMap 插件加载成功 ==="); - - // 立即创建 PlaceSearch 和 Geocoder 实例 - const placeSearch = new AMap.PlaceSearch({ - city: "全国", - map: map, - }); - placeSearchRef.current = placeSearch; - console.log("PlaceSearch 实例已创建"); - - const geocoder = new AMap.Geocoder({ - city: "全国", - radius: 1000, // 搜索半径,单位米 - extensions: "all", // 返回详细信息,包括POI、建筑物等 - }); - geocoderRef.current = geocoder; - console.log("Geocoder 实例已创建并保存到 ref,现在可以立即使用"); - - // 创建 Geolocation 实例用于获取当前位置 - const geolocation = new AMap.Geolocation({ - enableHighAccuracy: true, // 是否使用高精度定位,默认:true - timeout: 10000, // 超过10秒后停止定位,默认:无穷大 - maximumAge: 0, // 定位结果缓存0毫秒,默认:0 - convert: true, // 自动偏移坐标,偏移后的坐标为高德坐标,默认:true - showButton: false, // 显示定位按钮,默认:true - buttonPosition: "RB", // 定位按钮停靠位置,默认:'LB',左下角 - showMarker: false, // 定位成功后在定位到的位置显示点标记,默认:true - showCircle: false, // 定位成功后用圆圈表示定位精度范围,默认:true - panToLocation: false, // 定位成功后将定位到的位置作为地图中心点,默认:true - zoomToAccuracy: false, // 定位成功后调整地图视野范围使定位位置及精度范围视野内可见,默认:false - }); - geolocationRef.current = geolocation; - - // 获取当前位置 - console.log("=== 开始获取当前位置 ==="); - geolocation.getCurrentPosition((status: string, result: any) => { - console.log("=== 定位回调触发 ==="); - console.log("定位状态:", status); - console.log("定位结果:", result); - - if (status === "complete") { - const { position, formattedAddress, addressComponent } = result; - const lat = position.lat; - const lng = position.lng; - const lnglat = new AMap.LngLat(lng, lat); - - console.log("定位成功,当前位置:", { lat, lng }); - console.log("定位地址:", formattedAddress); - - // 将地图中心设置为当前位置 - map.setCenter(lnglat); - map.setZoom(16); - - // 添加当前位置标记 - addMarker(lnglat); - - // 设置选中位置信息 - let addressLabel = formattedAddress || `纬度: ${lat}, 经度: ${lng}`; - let poiName = "当前位置"; - - // 尝试从地址组件中获取更详细的信息 - if (addressComponent) { - const parts = []; - if (addressComponent.province) - parts.push(addressComponent.province); - if (addressComponent.city) parts.push(addressComponent.city); - if (addressComponent.district) - parts.push(addressComponent.district); - if (addressComponent.street) parts.push(addressComponent.street); - if (addressComponent.streetNumber) - parts.push(addressComponent.streetNumber); - - if (parts.length > 0) { - addressLabel = parts.join(""); - } - - poiName = - addressComponent.street || - addressComponent.district || - "当前位置"; - } - - setSelectedLocation({ - lat: lat, - lng: lng, - label: addressLabel, - poiname: poiName, - }); - - message.success("已获取当前位置"); - } else { - console.warn("定位失败:", result); - message.warning( - "无法获取当前位置,请手动点击地图选择位置。原因: " + - (result.message || "定位服务不可用"), - ); - // 定位失败时,使用默认中心点 - console.log("使用默认中心点"); - } - }); - - // 如果有待处理的点击坐标,立即处理它 - if (pendingClickRef.current) { - console.log("=== 检测到待处理的点击坐标,立即处理 ==="); - const { lat, lng, lnglat } = pendingClickRef.current; - console.log("待处理坐标:", { lat, lng }); - setTimeout(() => { - const success = getAddressForLocation(lat, lng, lnglat); - if (success) { - console.log("✓ 待处理的坐标已成功获取地址"); - pendingClickRef.current = null; // 清除待处理坐标 - } else { - console.warn("待处理的坐标获取地址失败"); - } - }, 100); - } - }, - ); - - // 立即绑定点击事件,插件可能已加载或正在加载 - console.log("立即绑定点击事件(插件可能已初始化)"); - map.on("click", (e: any) => { - console.log("=== 地图点击事件触发 ==="); - console.log("点击事件对象:", e); - console.log("点击位置对象:", e.lnglat); - console.log( - "Geocoder ref 状态:", - geocoderRef.current ? "已初始化" : "未初始化", - ); - - if (!e || !e.lnglat) { - console.error("点击事件无效,缺少 lnglat"); - return; - } - - const lnglat = e.lnglat; - const lat = lnglat.getLat(); - const lng = lnglat.getLng(); - console.log(`点击坐标 - 纬度: ${lat}, 经度: ${lng}`); - - // 立即添加标记和居中 - addMarker(lnglat); - - // 设置基本 selectedLocation(至少有坐标) - setSelectedLocation({ - lat: lat, - lng: lng, - label: "正在获取地址信息...", - poiname: "点击位置", - }); - - // 如果 Geocoder 已初始化,立即使用它 - if (geocoderRef.current) { - console.log("Geocoder 已初始化,立即获取地址"); - getAddressForLocation(lat, lng, lnglat); - } else { - console.log("Geocoder 未初始化,保存坐标待插件加载完成后处理"); - // 保存待处理的坐标 - pendingClickRef.current = { lat, lng, lnglat }; - } - }); - - // 等待地图完全加载后关闭 loading - map.on("complete", () => { - console.log("地图加载完成"); - // 清除超时定时器 - clearTimeout(loadingTimeout); - // 关闭 loading - setMapLoading(false); - console.log("地图加载完成,loading 已关闭"); - - // 确保地图容器可点击 - const mapContainer = map.getContainer(); - if (mapContainer) { - mapContainer.style.pointerEvents = "auto"; - mapContainer.style.cursor = "crosshair"; - console.log("地图容器指针事件已启用"); - } - }); - - // 如果地图加载失败,也设置加载完成 - map.on("error", (error: any) => { - console.error("地图加载错误:", error); - clearTimeout(loadingTimeout); - setMapLoading(false); - message.error("地图加载失败"); - }); - }, [addMarker, getAddressForLocation]); - - const handleSearch = value => { - if (value) { - AMap.plugin("AMap.AutoComplete", () => { - const auto = new AMap.AutoComplete({ city: "全国" }); - auto.search(value, (status, result) => { - if (status === "complete") { - setOptions( - result.tips.map(tip => ({ - value: tip.name, - data: tip, - })), - ); - } - }); - }); - } else { - setOptions([]); - } - }; - - const onSelect = (value, option) => { - const { district, address, name, location } = option.data; - const lnglat = location; - setSelectedLocation({ - lat: lnglat.lat, - lng: lnglat.lng, - label: `${name} ${address || district}`, - poiname: name, - }); - addMarker(lnglat); - mapRef.current.setCenter(lnglat); - }; - - const handleModalChange = useCallback( - (visible: boolean) => { - if (visible) { - console.log("模态打开:开始加载地图脚本"); - setMapLoading(true); - setSelectedLocation(null); - const script = document.createElement("script"); - script.src = - "https://webapi.amap.com/maps?v=1.4.15&key=79370028f5763e46742125ed2e900c76&plugin=AMap.PlaceSearch,AMap.AutoComplete,AMap.Geocoder,AMap.Geolocation"; - script.async = true; - script.onload = () => { - console.log("脚本加载成功:开始初始化地图"); - setTimeout(() => initMap(), 100); // 添加延迟确保 DOM 就绪 - }; - script.onerror = () => { - console.error("脚本加载失败"); - message.error("地图加载失败,请检查网络或API密钥"); - setMapLoading(false); - }; - document.body.appendChild(script); - } else { - console.log("模态关闭:清理地图和脚本"); - // 清除所有标记点 - clearAllMarkers(); - // 重置 ref - geocoderRef.current = null; - placeSearchRef.current = null; - geolocationRef.current = null; - // Cleanup on close - const scripts = document.querySelectorAll( - 'script[src*="webapi.amap.com"]', - ); - scripts.forEach(s => { - if (document.body.contains(s)) { - document.body.removeChild(s); - } - }); - if (mapRef.current) { - mapRef.current.destroy(); - mapRef.current = null; - } - setMapLoading(false); - setOptions([]); // 重置搜索选项 - } - }, - [initMap, clearAllMarkers], - ); - - // 手动获取当前位置 - const handleGetCurrentLocation = useCallback(() => { - if (!geolocationRef.current) { - message.warning("定位服务未初始化,请稍候再试"); - return; - } - - console.log("=== 手动触发获取当前位置 ==="); - message.loading({ content: "正在获取当前位置...", key: "location" }); - - geolocationRef.current.getCurrentPosition((status: string, result: any) => { - message.destroy("location"); - console.log("=== 手动定位回调触发 ==="); - console.log("定位状态:", status); - console.log("定位结果:", result); - - if (status === "complete" && result && result.position) { - const { position, formattedAddress, addressComponent } = result; - const lat = position.lat; - const lng = position.lng; - const lnglat = new AMap.LngLat(lng, lat); - - console.log("定位成功,当前位置:", { lat, lng }); - - // 将地图中心设置为当前位置 - if (mapRef.current) { - mapRef.current.setCenter(lnglat); - mapRef.current.setZoom(16); - } - - // 添加当前位置标记 - addMarker(lnglat); - - // 设置选中位置信息 - let addressLabel = formattedAddress || `纬度: ${lat}, 经度: ${lng}`; - let poiName = "当前位置"; - - // 尝试从地址组件中获取更详细的信息 - if (addressComponent) { - const parts = []; - if (addressComponent.province) parts.push(addressComponent.province); - if (addressComponent.city) parts.push(addressComponent.city); - if (addressComponent.district) parts.push(addressComponent.district); - if (addressComponent.street) parts.push(addressComponent.street); - if (addressComponent.streetNumber) - parts.push(addressComponent.streetNumber); - - if (parts.length > 0) { - addressLabel = parts.join(""); - } - - poiName = - addressComponent.street || addressComponent.district || "当前位置"; - } - - setSelectedLocation({ - lat: lat, - lng: lng, - label: addressLabel, - poiname: poiName, - }); - - message.success("已获取当前位置"); - } else { - console.warn("定位失败:", result); - message.error( - "获取当前位置失败: " + (result?.message || "定位服务不可用"), - ); - } - }); - }, [addMarker]); - - const handleSendLocation = () => { - if (!selectedLocation || !selectedLocation.lat || !selectedLocation.lng) { - message.warning("请选择有效位置"); - return; - } - - const { lat, lng, label, poiname } = selectedLocation; - const content = ``; - - const messageId = +Date.now(); - const params = { - wechatAccountId: contract.wechatAccountId, - wechatChatroomId: contract?.chatroomId ? contract.id : 0, - wechatFriendId: contract?.chatroomId ? 0 : contract.id, - msgSubType: 0, - msgType: 48, - content: content, - seq: messageId, - }; - - // 构造本地消息 - const localMessage: ChatRecord = { - id: messageId, - wechatAccountId: contract.wechatAccountId, - wechatFriendId: contract?.chatroomId ? 0 : contract.id, - wechatChatroomId: contract?.chatroomId ? contract.id : 0, - tenantId: 0, - accountId: 0, - synergyAccountId: 0, - content: content, - msgType: 48, - msgSubType: 0, - msgSvrId: "", - isSend: true, - createTime: new Date().toISOString(), - isDeleted: false, - deleteTime: "", - sendStatus: 1, - wechatTime: Date.now(), - origin: 0, - msgId: 0, - recalled: false, - seq: messageId, - }; - addMessage(localMessage); - - sendCommand("CmdSendMessage", params); - - onClose(); - setSelectedLocation(null); - }; - + // 初始化地图 useEffect(() => { - if (visible) { - handleModalChange(true); - // 确保容器在模态框打开后可以接收事件 - setTimeout(() => { - const container = document.getElementById("amap-container"); - if (container) { - container.style.pointerEvents = "auto"; - container.style.cursor = "crosshair"; - console.log( - "容器样式已设置,pointerEvents:", - container.style.pointerEvents, - ); + if (visible && mapContainerRef.current && window.qq && window.qq.maps) { + // 创建地图实例 + const center = new window.qq.maps.LatLng(39.908823, 116.39747); // 默认北京 + const mapInstance = new window.qq.maps.Map(mapContainerRef.current, { + center: center, + zoom: 13, + }); + + setMap(mapInstance); + + // 创建地理编码服务 + geocoderRef.current = new window.qq.maps.Geocoder({ + complete: (result: any) => { + setIsReverseGeocoding(false); + if (result && result.detail) { + const detail = result.detail; + const location = detail.location || detail.latLng; + if (location) { + setSelectedLocation({ + x: location.lng?.toString() || location.getLng().toString(), + y: location.lat?.toString() || location.getLat().toString(), + scale: "16", + label: + detail.address || detail.formatted_addresses?.recommend || "", + poiname: + detail.addressComponents?.street || detail.address || "", + maptype: "0", + poiid: detail.poiid || "", + }); + } + } + }, + error: () => { + setIsReverseGeocoding(false); + message.error("获取地址信息失败"); + }, + }); + + // 创建搜索服务 + searchServiceRef.current = new window.qq.maps.SearchService({ + complete: (result: any) => { + setIsSearching(false); + if (result && result.detail) { + const pois = result.detail.pois || []; + if (pois.length > 0) { + const searchResults = pois.map((poi: any) => { + const location = poi.location || poi.latLng; + return { + id: + poi.id || + `${location.lat || location.getLat()},${ + location.lng || location.getLng() + }`, + title: poi.title || poi.name || "", + address: poi.address || poi.ad_info?.adcode || "", + location: { + lat: location.lat || location.getLat(), + lng: location.lng || location.getLng(), + }, + adcode: poi.adcode || poi.ad_info?.adcode || "", + city: poi.city || poi.ad_info?.city || "", + district: poi.district || poi.ad_info?.district || "", + }; + }); + setSearchResults(searchResults); + } else { + setSearchResults([]); + message.info("未找到相关地址"); + } + } else { + setSearchResults([]); + message.info("未找到相关地址"); + } + }, + error: () => { + setIsSearching(false); + message.error("搜索失败,请重试"); + }, + }); + + // 地图点击事件 + window.qq.maps.event.addListener(mapInstance, "click", (event: any) => { + const lat = event.latLng.lat; + const lng = event.latLng.lng; + + // 更新标记点 + if (markerRef.current) { + markerRef.current.setMap(null); } - }, 200); - } else { - handleModalChange(false); + + const newMarker = new window.qq.maps.Marker({ + position: new window.qq.maps.LatLng(lat, lng), + map: mapInstance, + }); + + markerRef.current = newMarker; + + // 反向地理编码 + setIsReverseGeocoding(true); + geocoderRef.current.getAddress(new window.qq.maps.LatLng(lat, lng)); + }); + + return () => { + if (mapInstance) { + window.qq.maps.event.clearListeners(mapInstance, "click"); + } + }; } - }, [visible, handleModalChange]); + }, [visible]); + + // 搜索地址 + const handleSearch = () => { + if (!searchValue.trim()) { + message.warning("请输入搜索关键词"); + return; + } + + if (!searchServiceRef.current) { + message.error("搜索服务未初始化"); + return; + } + + setIsSearching(true); + searchServiceRef.current.search(searchValue); + }; + + // 选择搜索结果 + const handleSelectResult = (result: SearchResult) => { + if (!map) return; + + const lat = result.location.lat; + const lng = result.location.lng; + + // 移动地图中心 + map.setCenter(new window.qq.maps.LatLng(lat, lng)); + map.setZoom(16); + + // 更新标记点 + if (markerRef.current) { + markerRef.current.setMap(null); + } + + const newMarker = new window.qq.maps.Marker({ + position: new window.qq.maps.LatLng(lat, lng), + map: map, + }); + + markerRef.current = newMarker; + + // 设置选中的位置信息 + setSelectedLocation({ + x: lng.toString(), + y: lat.toString(), + scale: "16", + label: result.address || result.title, + poiname: result.title || "", + maptype: "0", + poiid: result.id || "", + }); + + // 清空搜索结果 + setSearchResults([]); + setSearchValue(""); + }; + + // 确认选择 + const handleConfirm = () => { + if (!selectedLocation) { + message.warning("请先选择位置"); + return; + } + + // 生成XML格式的位置信息 + const locationXml = ``; + + // 如果有onConfirm回调,调用它 + if (onConfirm) { + onConfirm(locationXml); + } + + // 如果有addMessage和contract,发送位置消息 + if (addMessage && contract) { + const messageId = +Date.now(); + const localMessage = { + id: messageId, + wechatAccountId: contract.wechatAccountId, + wechatFriendId: contract?.chatroomId ? 0 : contract.id, + wechatChatroomId: contract?.chatroomId ? contract.id : 0, + tenantId: 0, + accountId: 0, + synergyAccountId: 0, + content: locationXml, + msgType: 48, // 位置消息类型 + msgSubType: 0, + msgSvrId: "", + isSend: true, + createTime: new Date().toISOString(), + isDeleted: false, + deleteTime: "", + sendStatus: 1, + wechatTime: Date.now(), + origin: 0, + msgId: 0, + recalled: false, + seq: messageId, + }; + + addMessage(localMessage); + + // 发送消息到服务器 + sendCommand("CmdSendMessage", { + wechatAccountId: contract.wechatAccountId, + wechatChatroomId: contract?.chatroomId ? contract.id : 0, + wechatFriendId: contract?.chatroomId ? 0 : contract.id, + msgSubType: 0, + msgType: 48, + content: locationXml, + seq: messageId, + }); + } + + // 关闭弹窗并重置状态 + handleClose(); + }; + + // 关闭弹窗 + const handleClose = () => { + setSearchValue(""); + setSearchResults([]); + setSelectedLocation(null); + if (markerRef.current) { + markerRef.current.setMap(null); + markerRef.current = null; + } + setIsSearching(false); + setIsReverseGeocoding(false); + onClose(); + }; return ( -
    - - - + open={visible} + onCancel={handleClose} + width={900} + centered + footer={[ + , -
    + {isReverseGeocoding ? "正在获取地址信息..." : "确认"} + , + ]} + > +
    + {/* 搜索区域 */} +
    + setSearchValue(e.target.value)} + onPressEnter={handleSearch} + prefix={} + suffix={ + + } + className={styles.searchInput} + /> - {/* 显示点击位置的信息 */} - {selectedLocation && ( -
    -
    - 位置信息: -
    -
    - 地址: {selectedLocation.label || "加载中..."} -
    -
    - 坐标: {selectedLocation.lat},{" "} - {selectedLocation.lng} -
    + {/* 搜索结果列表 */} + {searchResults.length > 0 && ( +
    + ( + handleSelectResult(item)} + > + } + title={item.title} + description={item.address} + /> + + )} + /> +
    + )}
    - )} -
    - {mapLoading && ( -
    - + {/* 地图区域 */} +
    +
    + {isReverseGeocoding && ( +
    + +
    + )} +
    + + {/* 选中位置信息 */} + {selectedLocation && ( +
    +
    + 已选择位置 +
    +
    + {selectedLocation.label || selectedLocation.poiname} +
    +
    + 经度: {selectedLocation.x}, 纬度: {selectedLocation.y} +
    )} -
    { - // 添加原生点击事件监听作为备用 - console.log("容器原生点击事件触发", e); - }} - >
    ); From 782031cbc6c451a76fc705a0f9e5eb3b351853b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B6=85=E7=BA=A7=E8=80=81=E7=99=BD=E5=85=94?= Date: Fri, 21 Nov 2025 19:02:41 +0800 Subject: [PATCH 09/15] =?UTF-8?q?=E5=A2=9E=E5=BC=BASelectMap=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=EF=BC=8C=E6=B7=BB=E5=8A=A0=E7=94=A8=E6=88=B7=E5=BD=93?= =?UTF-8?q?=E5=89=8D=E4=BD=8D=E7=BD=AE=E8=8E=B7=E5=8F=96=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=EF=BC=8C=E4=BC=98=E5=8C=96=E5=9C=B0=E5=9D=80=E8=A7=A3=E6=9E=90?= =?UTF-8?q?=E9=80=BB=E8=BE=91=E5=92=8C=E9=94=99=E8=AF=AF=E5=A4=84=E7=90=86?= =?UTF-8?q?=EF=BC=8C=E6=94=B9=E8=BF=9B=E5=8A=A0=E8=BD=BD=E7=8A=B6=E6=80=81?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=EF=BC=8C=E6=8F=90=E5=8D=87=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E4=BD=93=E9=AA=8C=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MessageEnter/components/selectMap.tsx | 145 +++++++++++++++--- 1 file changed, 120 insertions(+), 25 deletions(-) diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/selectMap.tsx b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/selectMap.tsx index d2658da6..bc75ddc5 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/selectMap.tsx +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/selectMap.tsx @@ -57,6 +57,7 @@ const SelectMap: React.FC = ({ ); const [map, setMap] = useState(null); const [isReverseGeocoding, setIsReverseGeocoding] = useState(false); + const [isLocating, setIsLocating] = useState(false); const mapContainerRef = useRef(null); const geocoderRef = useRef(null); const searchServiceRef = useRef(null); @@ -79,27 +80,70 @@ const SelectMap: React.FC = ({ geocoderRef.current = new window.qq.maps.Geocoder({ complete: (result: any) => { setIsReverseGeocoding(false); - if (result && result.detail) { - const detail = result.detail; - const location = detail.location || detail.latLng; - if (location) { - setSelectedLocation({ - x: location.lng?.toString() || location.getLng().toString(), - y: location.lat?.toString() || location.getLat().toString(), - scale: "16", - label: - detail.address || detail.formatted_addresses?.recommend || "", - poiname: - detail.addressComponents?.street || detail.address || "", - maptype: "0", - poiid: detail.poiid || "", - }); + try { + if (result && result.detail) { + const detail = result.detail; + const location = detail.location || detail.latLng; + if (location) { + const lat = + location.lat || (location.getLat ? location.getLat() : null); + const lng = + location.lng || (location.getLng ? location.getLng() : null); + + if (lat && lng) { + // 构建地址标签 + let addressLabel = ""; + if (detail.formatted_addresses) { + addressLabel = + detail.formatted_addresses.recommend || + detail.formatted_addresses.rough || + ""; + } + if (!addressLabel && detail.address) { + addressLabel = detail.address; + } + if (!addressLabel && detail.addressComponents) { + const addr = detail.addressComponents; + const parts = []; + if (addr.province) parts.push(addr.province); + if (addr.city) parts.push(addr.city); + if (addr.district) parts.push(addr.district); + if (addr.street) parts.push(addr.street); + if (addr.street_number) parts.push(addr.street_number); + addressLabel = parts.join(""); + } + if (!addressLabel) { + addressLabel = `${lat.toFixed(6)}, ${lng.toFixed(6)}`; + } + + setSelectedLocation({ + x: lng.toString(), + y: lat.toString(), + scale: "16", + label: addressLabel, + poiname: + detail.addressComponents?.street || detail.poiid || "", + maptype: "0", + poiid: detail.poiid || "", + }); + } else { + message.warning("无法解析位置信息"); + } + } else { + message.warning("未找到位置信息"); + } + } else { + message.warning("获取地址信息失败:返回数据为空"); } + } catch (error) { + console.error("解析地址信息错误:", error); + message.error("解析地址信息失败"); } }, - error: () => { + error: (error: any) => { setIsReverseGeocoding(false); - message.error("获取地址信息失败"); + console.error("反向地理编码错误:", error); + message.error("获取地址信息失败,请稍后重试"); }, }); @@ -163,10 +207,60 @@ const SelectMap: React.FC = ({ markerRef.current = newMarker; // 反向地理编码 - setIsReverseGeocoding(true); - geocoderRef.current.getAddress(new window.qq.maps.LatLng(lat, lng)); + if (geocoderRef.current) { + setIsReverseGeocoding(true); + geocoderRef.current.getAddress(new window.qq.maps.LatLng(lat, lng)); + } }); + // 获取用户当前位置 + if (navigator.geolocation) { + setIsLocating(true); + navigator.geolocation.getCurrentPosition( + position => { + setIsLocating(false); + const userLat = position.coords.latitude; + const userLng = position.coords.longitude; + + // 移动地图中心到用户位置 + const userLocation = new window.qq.maps.LatLng(userLat, userLng); + mapInstance.setCenter(userLocation); + mapInstance.setZoom(16); + + // 添加标记点 + if (markerRef.current) { + markerRef.current.setMap(null); + } + + const newMarker = new window.qq.maps.Marker({ + position: userLocation, + map: mapInstance, + }); + + markerRef.current = newMarker; + + // 获取用户位置的地址信息 + if (geocoderRef.current) { + setIsReverseGeocoding(true); + geocoderRef.current.getAddress(userLocation); + } + }, + error => { + setIsLocating(false); + console.error("获取位置失败:", error); + // 如果获取位置失败,使用默认位置(北京) + message.info("无法获取您的位置,已定位到默认位置"); + }, + { + enableHighAccuracy: true, + timeout: 10000, + maximumAge: 0, + }, + ); + } else { + message.info("您的浏览器不支持地理定位功能"); + } + return () => { if (mapInstance) { window.qq.maps.event.clearListeners(mapInstance, "click"); @@ -309,6 +403,7 @@ const SelectMap: React.FC = ({ } setIsSearching(false); setIsReverseGeocoding(false); + setIsLocating(false); onClose(); }; @@ -379,12 +474,12 @@ const SelectMap: React.FC = ({ {/* 地图区域 */}
    -
    - {isReverseGeocoding && ( -
    - -
    - )} + +
    +
    {/* 选中位置信息 */} From 21de87ca32efd51f3125d2eaf259f2376e3839bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B6=85=E7=BA=A7=E8=80=81=E7=99=BD=E5=85=94?= Date: Sat, 22 Nov 2025 11:45:53 +0800 Subject: [PATCH 10/15] =?UTF-8?q?=E6=9B=B4=E6=96=B0SelectMap=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E4=BB=A5=E4=BD=BF=E7=94=A8=E6=96=B0=E7=89=88=E8=85=BE?= =?UTF-8?q?=E8=AE=AF=E5=9C=B0=E5=9B=BEAPI=EF=BC=8C=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E5=9C=B0=E5=9B=BE=E5=8A=A0=E8=BD=BD=E5=92=8C=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E4=BD=8D=E7=BD=AE=E8=8E=B7=E5=8F=96=E9=80=BB=E8=BE=91=EF=BC=8C?= =?UTF-8?q?=E5=A2=9E=E5=BC=BA=E9=94=99=E8=AF=AF=E5=A4=84=E7=90=86=E5=92=8C?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E4=BD=93=E9=AA=8C=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Touchkebao/index.html | 2 +- .../MessageEnter/components/selectMap.tsx | 742 +++++++++++------- Touchkebao/腾讯地图定位服务修复说明.md | 73 ++ 3 files changed, 534 insertions(+), 283 deletions(-) create mode 100644 Touchkebao/腾讯地图定位服务修复说明.md diff --git a/Touchkebao/index.html b/Touchkebao/index.html index d0dcd4e9..f1c04fe6 100644 --- a/Touchkebao/index.html +++ b/Touchkebao/index.html @@ -13,7 +13,7 @@ diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/selectMap.tsx b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/selectMap.tsx index bc75ddc5..d09316ce 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/selectMap.tsx +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/selectMap.tsx @@ -4,10 +4,11 @@ import { SearchOutlined, EnvironmentOutlined } from "@ant-design/icons"; import { useWebSocketStore } from "@/store/module/websocket/websocket"; import styles from "./selectMap.module.scss"; -// 声明腾讯地图类型 +// 声明腾讯地图类型(新版TMap API) declare global { interface Window { - qq: any; + TMap: any; + geolocationRef: any; // 全局IP定位服务引用 } } @@ -58,281 +59,454 @@ const SelectMap: React.FC = ({ const [map, setMap] = useState(null); const [isReverseGeocoding, setIsReverseGeocoding] = useState(false); const [isLocating, setIsLocating] = useState(false); + const [tmapLoaded, setTmapLoaded] = useState(false); const mapContainerRef = useRef(null); const geocoderRef = useRef(null); - const searchServiceRef = useRef(null); + const suggestServiceRef = useRef(null); const markerRef = useRef(null); const { sendCommand } = useWebSocketStore.getState(); + // 加载腾讯地图SDK + useEffect(() => { + // 检查TMap是否已经加载 + if (window.TMap) { + setTmapLoaded(true); + return; + } + + // 动态加载腾讯地图SDK(使用与index.html相同的密钥) + const script = document.createElement("script"); + script.src = + "https://map.qq.com/api/gljs?v=1.exp&libraries=service&key=7DZBZ-ZSRK3-QJN3W-O5VTV-4E2P6-7GFYX"; + script.async = true; + script.onload = () => { + console.log("腾讯地图SDK加载成功"); + setTmapLoaded(true); + }; + script.onerror = () => { + console.error("腾讯地图SDK加载失败"); + message.error("地图加载失败,请刷新页面重试"); + }; + document.head.appendChild(script); + + return () => { + // 清理script标签 + if (document.head.contains(script)) { + document.head.removeChild(script); + } + }; + }, []); + // 初始化地图 useEffect(() => { - if (visible && mapContainerRef.current && window.qq && window.qq.maps) { - // 创建地图实例 - const center = new window.qq.maps.LatLng(39.908823, 116.39747); // 默认北京 - const mapInstance = new window.qq.maps.Map(mapContainerRef.current, { - center: center, - zoom: 13, - }); + if (visible && mapContainerRef.current && tmapLoaded && window.TMap) { + console.log("开始初始化地图"); - setMap(mapInstance); - - // 创建地理编码服务 - geocoderRef.current = new window.qq.maps.Geocoder({ - complete: (result: any) => { - setIsReverseGeocoding(false); - try { - if (result && result.detail) { - const detail = result.detail; - const location = detail.location || detail.latLng; - if (location) { - const lat = - location.lat || (location.getLat ? location.getLat() : null); - const lng = - location.lng || (location.getLng ? location.getLng() : null); - - if (lat && lng) { - // 构建地址标签 - let addressLabel = ""; - if (detail.formatted_addresses) { - addressLabel = - detail.formatted_addresses.recommend || - detail.formatted_addresses.rough || - ""; - } - if (!addressLabel && detail.address) { - addressLabel = detail.address; - } - if (!addressLabel && detail.addressComponents) { - const addr = detail.addressComponents; - const parts = []; - if (addr.province) parts.push(addr.province); - if (addr.city) parts.push(addr.city); - if (addr.district) parts.push(addr.district); - if (addr.street) parts.push(addr.street); - if (addr.street_number) parts.push(addr.street_number); - addressLabel = parts.join(""); - } - if (!addressLabel) { - addressLabel = `${lat.toFixed(6)}, ${lng.toFixed(6)}`; - } - - setSelectedLocation({ - x: lng.toString(), - y: lat.toString(), - scale: "16", - label: addressLabel, - poiname: - detail.addressComponents?.street || detail.poiid || "", - maptype: "0", - poiid: detail.poiid || "", - }); - } else { - message.warning("无法解析位置信息"); - } - } else { - message.warning("未找到位置信息"); - } - } else { - message.warning("获取地址信息失败:返回数据为空"); - } - } catch (error) { - console.error("解析地址信息错误:", error); - message.error("解析地址信息失败"); - } - }, - error: (error: any) => { - setIsReverseGeocoding(false); - console.error("反向地理编码错误:", error); - message.error("获取地址信息失败,请稍后重试"); - }, - }); - - // 创建搜索服务 - searchServiceRef.current = new window.qq.maps.SearchService({ - complete: (result: any) => { - setIsSearching(false); - if (result && result.detail) { - const pois = result.detail.pois || []; - if (pois.length > 0) { - const searchResults = pois.map((poi: any) => { - const location = poi.location || poi.latLng; - return { - id: - poi.id || - `${location.lat || location.getLat()},${ - location.lng || location.getLng() - }`, - title: poi.title || poi.name || "", - address: poi.address || poi.ad_info?.adcode || "", - location: { - lat: location.lat || location.getLat(), - lng: location.lng || location.getLng(), - }, - adcode: poi.adcode || poi.ad_info?.adcode || "", - city: poi.city || poi.ad_info?.city || "", - district: poi.district || poi.ad_info?.district || "", - }; - }); - setSearchResults(searchResults); - } else { - setSearchResults([]); - message.info("未找到相关地址"); - } - } else { - setSearchResults([]); - message.info("未找到相关地址"); - } - }, - error: () => { - setIsSearching(false); - message.error("搜索失败,请重试"); - }, - }); - - // 地图点击事件 - window.qq.maps.event.addListener(mapInstance, "click", (event: any) => { - const lat = event.latLng.lat; - const lng = event.latLng.lng; - - // 更新标记点 - if (markerRef.current) { - markerRef.current.setMap(null); - } - - const newMarker = new window.qq.maps.Marker({ - position: new window.qq.maps.LatLng(lat, lng), - map: mapInstance, + try { + // 创建地图实例 + const center = new window.TMap.LatLng(39.908823, 116.39747); // 默认北京 + const mapInstance = new window.TMap.Map(mapContainerRef.current, { + center: center, + zoom: 13, + rotation: 0, + pitch: 0, }); - markerRef.current = newMarker; + setMap(mapInstance); - // 反向地理编码 - if (geocoderRef.current) { - setIsReverseGeocoding(true); - geocoderRef.current.getAddress(new window.qq.maps.LatLng(lat, lng)); - } - }); + // 创建地理编码服务(用于反向地理编码) + geocoderRef.current = new window.TMap.service.Geocoder(); - // 获取用户当前位置 - if (navigator.geolocation) { - setIsLocating(true); - navigator.geolocation.getCurrentPosition( - position => { - setIsLocating(false); - const userLat = position.coords.latitude; - const userLng = position.coords.longitude; + // 使用腾讯地图内置的定位服务 + window.geolocationRef = window.TMap.service.Geolocation; - // 移动地图中心到用户位置 - const userLocation = new window.qq.maps.LatLng(userLat, userLng); - mapInstance.setCenter(userLocation); - mapInstance.setZoom(16); + // 创建搜索建议服务 + suggestServiceRef.current = new window.TMap.service.Suggestion({ + pageSize: 10, + autoExtend: true, + }); - // 添加标记点 + // 地图点击事件处理函数 + const handleMapClick = (evt: any) => { + try { + const lat = evt.latLng.getLat(); + const lng = evt.latLng.getLng(); + + console.log("地图点击:", lat, lng); + + // 更新标记点 if (markerRef.current) { markerRef.current.setMap(null); + markerRef.current = null; } - const newMarker = new window.qq.maps.Marker({ - position: userLocation, + // 创建新标记 + const newMarker = new window.TMap.MultiMarker({ + id: "marker-layer", map: mapInstance, + geometries: [ + { + id: "selected-marker", + styleId: "marker", + position: new window.TMap.LatLng(lat, lng), + properties: { + title: "选中位置", + }, + }, + ], }); markerRef.current = newMarker; - // 获取用户位置的地址信息 - if (geocoderRef.current) { - setIsReverseGeocoding(true); - geocoderRef.current.getAddress(userLocation); - } - }, - error => { + // 设置基本位置信息(防止白屏) + setSelectedLocation({ + x: lng.toString(), + y: lat.toString(), + scale: "16", + label: `${lat.toFixed(6)}, ${lng.toFixed(6)}`, + poiname: "选中位置", + maptype: "0", + poiid: "", + }); + + // 反向地理编码获取地址 + setIsReverseGeocoding(true); + geocoderRef.current + .getAddress({ location: new window.TMap.LatLng(lat, lng) }) + .then((result: any) => { + setIsReverseGeocoding(false); + console.log("反向地理编码结果:", result); + + try { + if (result && result.result) { + const resultData = result.result; + const address = resultData.address || ""; + const addressComponent = resultData.address_component || {}; + const formattedAddresses = + resultData.formatted_addresses || {}; + + // 构建地址标签 + let addressLabel = + formattedAddresses.recommend || + formattedAddresses.rough || + address; + + if (!addressLabel) { + const parts = []; + if (addressComponent.province) + parts.push(addressComponent.province); + if (addressComponent.city) + parts.push(addressComponent.city); + if (addressComponent.district) + parts.push(addressComponent.district); + if (addressComponent.street) + parts.push(addressComponent.street); + if (addressComponent.street_number) + parts.push(addressComponent.street_number); + addressLabel = parts.join(""); + } + + if (!addressLabel) { + addressLabel = `${lat.toFixed(6)}, ${lng.toFixed(6)}`; + } + + setSelectedLocation({ + x: lng.toString(), + y: lat.toString(), + scale: "16", + label: addressLabel, + poiname: addressComponent.street || "未知位置", + maptype: "0", + poiid: resultData.poi_id || "", + }); + } else { + message.warning("获取详细地址信息失败,将使用坐标显示"); + } + } catch (error) { + console.error("解析地址信息错误:", error); + message.warning("解析地址信息失败,将使用坐标显示"); + } + }) + .catch((error: any) => { + setIsReverseGeocoding(false); + console.error("反向地理编码错误:", error); + message.warning("获取详细地址信息失败,将使用坐标显示"); + }); + } catch (error) { + console.error("地图点击处理错误:", error); + message.error("处理地图点击时出错,请重试"); + } + }; + + // 绑定地图点击事件 + mapInstance.on("click", handleMapClick); + + // 使用腾讯地图API初始化用户位置 + const initializeUserLocation = ( + lat: number, + lng: number, + isDefault: boolean = false, + ) => { + console.log(isDefault ? "使用默认位置:" : "用户位置:", lat, lng); + + // 移动地图中心到位置 + const userLocation = new window.TMap.LatLng(lat, lng); + mapInstance.setCenter(userLocation); + mapInstance.setZoom(16); + + // 添加标记点 + if (markerRef.current) { + markerRef.current.setMap(null); + markerRef.current = null; + } + + const newMarker = new window.TMap.MultiMarker({ + id: "marker-layer", + map: mapInstance, + geometries: [ + { + id: "user-location", + styleId: "marker", + position: userLocation, + properties: { + title: isDefault ? "默认位置" : "当前位置", + }, + }, + ], + }); + + markerRef.current = newMarker; + + // 使用腾讯地图服务获取该位置的地址信息 + setIsReverseGeocoding(true); + geocoderRef.current + .getAddress({ location: userLocation }) + .then((result: any) => { + setIsReverseGeocoding(false); + if (result && result.result) { + const resultData = result.result; + const formattedAddresses = resultData.formatted_addresses || {}; + const addressComponent = resultData.address_component || {}; + + const addressLabel = + formattedAddresses.recommend || + formattedAddresses.rough || + resultData.address || + `${lat.toFixed(6)}, ${lng.toFixed(6)}`; + + setSelectedLocation({ + x: lng.toString(), + y: lat.toString(), + scale: "16", + label: addressLabel, + poiname: + addressComponent.street || + (isDefault ? "默认位置" : "当前位置"), + maptype: "0", + poiid: resultData.poi_id || "", + }); + } + }) + .catch((error: any) => { + setIsReverseGeocoding(false); + console.error("获取地址信息失败:", error); + // 即使获取地址失败,也设置基本的位置信息 + setSelectedLocation({ + x: lng.toString(), + y: lat.toString(), + scale: "16", + label: `${lat.toFixed(6)}, ${lng.toFixed(6)}`, + poiname: isDefault ? "默认位置" : "当前位置", + maptype: "0", + poiid: "", + }); + }); + }; + + // 使用腾讯地图IP定位获取用户位置 + setIsLocating(true); + try { + if (window.geolocationRef) { + window.geolocationRef.getLocation({ + timeout: 10000, + convert: true, + success: function (result: any) { + setIsLocating(false); + if (result && result.location) { + const { lat, lng } = result.location; + message.info("已定位到您的大致位置"); + initializeUserLocation(lat, lng, false); + } else { + // IP定位失败:使用默认位置 + message.info("无法获取您的位置,已定位到北京"); + // 使用默认位置(北京市) + initializeUserLocation(39.908823, 116.39747, true); + } + }, + error: function () { + setIsLocating(false); + message.info("无法获取您的位置,已定位到北京"); + // 使用默认位置(北京市) + initializeUserLocation(39.908823, 116.39747, true); + }, + }); + } else { + // 地理编码服务未初始化:使用默认位置 setIsLocating(false); - console.error("获取位置失败:", error); - // 如果获取位置失败,使用默认位置(北京) - message.info("无法获取您的位置,已定位到默认位置"); - }, - { - enableHighAccuracy: true, - timeout: 10000, - maximumAge: 0, - }, - ); - } else { - message.info("您的浏览器不支持地理定位功能"); + message.info("无法获取您的位置,已定位到北京"); + // 使用默认位置(北京市) + initializeUserLocation(39.908823, 116.39747, true); + } + } catch (error) { + // 捕获任何可能的错误,防止白屏 + console.error("定位过程中发生错误:", error); + setIsLocating(false); + message.error("定位服务出现异常,已定位到北京"); + // 使用默认位置(北京市) + initializeUserLocation(39.908823, 116.39747, true); + } + + return () => { + // 清理地图事件监听 + if (mapInstance) { + mapInstance.off("click", handleMapClick); + } + }; + } catch (error) { + console.error("初始化地图时出错:", error); + message.error("地图加载失败,请刷新页面重试"); + setIsLocating(false); + } + } + }, [visible, tmapLoaded]); + + // 搜索地址(获取搜索建议) + const handleSearch = () => { + try { + if (!searchValue.trim()) { + message.warning("请输入搜索关键词"); + return; } - return () => { - if (mapInstance) { - window.qq.maps.event.clearListeners(mapInstance, "click"); - } - }; - } - }, [visible]); + if (!suggestServiceRef.current) { + message.error("搜索服务未初始化,请刷新页面重试"); + return; + } - // 搜索地址 - const handleSearch = () => { - if (!searchValue.trim()) { - message.warning("请输入搜索关键词"); - return; - } + setIsSearching(true); + suggestServiceRef.current + .getSuggestions({ + keyword: searchValue, + location: map ? map.getCenter() : undefined, + }) + .then((result: any) => { + setIsSearching(false); + console.log("搜索建议结果:", result); - if (!searchServiceRef.current) { - message.error("搜索服务未初始化"); - return; + if (result && result.data && result.data.length > 0) { + const searchResults = result.data.map((item: any) => ({ + id: item.id, + title: item.title || item.name || "", + address: item.address || "", + location: { + lat: item.location.lat, + lng: item.location.lng, + }, + adcode: item.adcode || "", + city: item.city || "", + district: item.district || "", + })); + setSearchResults(searchResults); + } else { + setSearchResults([]); + message.info("未找到相关地址"); + } + }) + .catch((error: any) => { + setIsSearching(false); + console.error("搜索失败:", error); + message.error("搜索失败,请重试"); + // 确保搜索状态被重置 + setSearchResults([]); + }); + } catch (error) { + setIsSearching(false); + console.error("搜索处理错误:", error); + message.error("搜索过程中出错,请重试"); + setSearchResults([]); } - - setIsSearching(true); - searchServiceRef.current.search(searchValue); }; // 选择搜索结果 const handleSelectResult = (result: SearchResult) => { - if (!map) return; + try { + if (!map) { + message.error("地图未初始化,请刷新页面重试"); + return; + } - const lat = result.location.lat; - const lng = result.location.lng; + const lat = result.location.lat; + const lng = result.location.lng; - // 移动地图中心 - map.setCenter(new window.qq.maps.LatLng(lat, lng)); - map.setZoom(16); + console.log("选择搜索结果:", result); - // 更新标记点 - if (markerRef.current) { - markerRef.current.setMap(null); + // 移动地图中心 + map.setCenter(new window.TMap.LatLng(lat, lng)); + map.setZoom(16); + + // 更新标记点 + if (markerRef.current) { + markerRef.current.setMap(null); + markerRef.current = null; + } + + const newMarker = new window.TMap.MultiMarker({ + id: "marker-layer", + map: map, + geometries: [ + { + id: "selected-poi", + styleId: "marker", + position: new window.TMap.LatLng(lat, lng), + properties: { + title: result.title, + }, + }, + ], + }); + + markerRef.current = newMarker; + + // 设置选中的位置信息 + setSelectedLocation({ + x: lng.toString(), + y: lat.toString(), + scale: "16", + label: result.address || result.title, + poiname: result.title || "", + maptype: "0", + poiid: result.id || "", + }); + + // 清空搜索结果 + setSearchResults([]); + setSearchValue(""); + } catch (error) { + console.error("选择搜索结果错误:", error); + message.error("选择位置时出错,请重试"); } - - const newMarker = new window.qq.maps.Marker({ - position: new window.qq.maps.LatLng(lat, lng), - map: map, - }); - - markerRef.current = newMarker; - - // 设置选中的位置信息 - setSelectedLocation({ - x: lng.toString(), - y: lat.toString(), - scale: "16", - label: result.address || result.title, - poiname: result.title || "", - maptype: "0", - poiid: result.id || "", - }); - - // 清空搜索结果 - setSearchResults([]); - setSearchValue(""); }; // 确认选择 const handleConfirm = () => { - if (!selectedLocation) { - message.warning("请先选择位置"); - return; - } + try { + if (!selectedLocation) { + message.warning("请先选择位置"); + return; + } - // 生成XML格式的位置信息 - const locationXml = ` = ({ maptype="${selectedLocation.maptype}" poiid="${selectedLocation.poiid}" />`; - // 如果有onConfirm回调,调用它 - if (onConfirm) { - onConfirm(locationXml); + // 如果有onConfirm回调,调用它 + if (onConfirm) { + onConfirm(locationXml); + } + + // 如果有addMessage和contract,发送位置消息 + if (addMessage && contract) { + const messageId = +Date.now(); + const localMessage = { + id: messageId, + wechatAccountId: contract.wechatAccountId, + wechatFriendId: contract?.chatroomId ? 0 : contract.id, + wechatChatroomId: contract?.chatroomId ? contract.id : 0, + tenantId: 0, + accountId: 0, + synergyAccountId: 0, + content: locationXml, + msgType: 48, // 位置消息类型 + msgSubType: 0, + msgSvrId: "", + isSend: true, + createTime: new Date().toISOString(), + isDeleted: false, + deleteTime: "", + sendStatus: 1, + wechatTime: Date.now(), + origin: 0, + msgId: 0, + recalled: false, + seq: messageId, + }; + + addMessage(localMessage); + + // 发送消息到服务器 + sendCommand("CmdSendMessage", { + wechatAccountId: contract.wechatAccountId, + wechatChatroomId: contract?.chatroomId ? contract.id : 0, + wechatFriendId: contract?.chatroomId ? 0 : contract.id, + msgSubType: 0, + msgType: 48, + content: locationXml, + seq: messageId, + }); + } + + // 关闭弹窗并重置状态 + handleClose(); + } catch (error) { + console.error("确认位置时出错:", error); + message.error("发送位置信息时出错,请重试"); } - - // 如果有addMessage和contract,发送位置消息 - if (addMessage && contract) { - const messageId = +Date.now(); - const localMessage = { - id: messageId, - wechatAccountId: contract.wechatAccountId, - wechatFriendId: contract?.chatroomId ? 0 : contract.id, - wechatChatroomId: contract?.chatroomId ? contract.id : 0, - tenantId: 0, - accountId: 0, - synergyAccountId: 0, - content: locationXml, - msgType: 48, // 位置消息类型 - msgSubType: 0, - msgSvrId: "", - isSend: true, - createTime: new Date().toISOString(), - isDeleted: false, - deleteTime: "", - sendStatus: 1, - wechatTime: Date.now(), - origin: 0, - msgId: 0, - recalled: false, - seq: messageId, - }; - - addMessage(localMessage); - - // 发送消息到服务器 - sendCommand("CmdSendMessage", { - wechatAccountId: contract.wechatAccountId, - wechatChatroomId: contract?.chatroomId ? contract.id : 0, - wechatFriendId: contract?.chatroomId ? 0 : contract.id, - msgSubType: 0, - msgType: 48, - content: locationXml, - seq: messageId, - }); - } - - // 关闭弹窗并重置状态 - handleClose(); }; // 关闭弹窗 diff --git a/Touchkebao/腾讯地图定位服务修复说明.md b/Touchkebao/腾讯地图定位服务修复说明.md new file mode 100644 index 00000000..601db72f --- /dev/null +++ b/Touchkebao/腾讯地图定位服务修复说明.md @@ -0,0 +1,73 @@ +# 腾讯地图定位服务修复说明 + +## 问题描述 + +在 `selectMap.tsx` 文件中使用腾讯地图定位服务时出现以下错误: + +``` +TypeError: window.TMap.service.Location is not a constructor + at selectMap.tsx:121:33 +``` + +## 原因分析 + +错误原因是尝试将 `TMap.service.Location` 作为构造函数使用,但在腾讯地图 GL API 中,定位服务不是通过构造函数方式创建的。 + +## 修复方法 + +### 1. 修改定位服务的初始化方式 + +将原来的代码: + +```typescript +// 创建IP定位服务 +window.geolocationRef = new window.TMap.service.Location({ + timeout: 10000, + convert: true, +}); +``` + +修改为: + +```typescript +// 使用腾讯地图内置的定位服务 +window.geolocationRef = window.TMap.service.Geolocation; +``` + +### 2. 修改定位服务的调用方式 + +在调用定位服务时,将配置参数直接传入 `getLocation` 方法: + +```typescript +window.geolocationRef.getLocation({ + timeout: 10000, + convert: true, + success: function (result: any) { + // 处理成功回调 + }, + error: function () { + // 处理错误回调 + }, +}); +``` + +## 技术说明 + +1. **腾讯地图 GL API 中的定位服务**: + - 正确的服务名称是 `TMap.service.Geolocation`,而非 `TMap.service.Location` + - 它是一个对象,不需要使用 `new` 关键字实例化 + - 配置参数应该直接传递给 `getLocation` 方法 + +2. **定位服务参数**: + - `timeout`:定位超时时间,单位毫秒 + - `convert`:是否将坐标转换为腾讯地图坐标系 + +3. **回调处理**: + - `success`:定位成功回调函数,返回位置信息 + - `error`:定位失败回调函数 + +## 注意事项 + +1. 确保腾讯地图 SDK 已正确加载 +2. 确保 API 密钥有定位服务的权限 +3. 定位精度可能受网络环境影响 From ab2ee050def8d64474a52d351f7b601970c0e570 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B6=85=E7=BA=A7=E8=80=81=E7=99=BD=E5=85=94?= Date: Sat, 22 Nov 2025 11:53:54 +0800 Subject: [PATCH 11/15] =?UTF-8?q?=E4=BC=98=E5=8C=96SelectMap=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=EF=BC=8C=E6=9B=B4=E6=96=B0IP=E5=AE=9A=E4=BD=8D?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1=E7=9A=84=E5=AE=9E=E7=8E=B0=EF=BC=8C=E5=A2=9E?= =?UTF-8?q?=E5=BC=BA=E9=94=99=E8=AF=AF=E5=A4=84=E7=90=86=E9=80=BB=E8=BE=91?= =?UTF-8?q?=EF=BC=8C=E7=A1=AE=E4=BF=9D=E7=94=A8=E6=88=B7=E4=BD=8D=E7=BD=AE?= =?UTF-8?q?=E8=8E=B7=E5=8F=96=E6=9B=B4=E5=8A=A0=E5=87=86=E7=A1=AE=E5=92=8C?= =?UTF-8?q?=E7=A8=B3=E5=AE=9A=EF=BC=8C=E5=90=8C=E6=97=B6=E8=B0=83=E6=95=B4?= =?UTF-8?q?=E6=A0=B7=E5=BC=8F=E4=BB=A5=E6=8F=90=E5=8D=87=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E4=BD=93=E9=AA=8C=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/selectMap.module.scss | 6 +++- .../MessageEnter/components/selectMap.tsx | 28 +++++++++---------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/selectMap.module.scss b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/selectMap.module.scss index f0a77bb3..b8f65724 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/selectMap.module.scss +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/selectMap.module.scss @@ -8,10 +8,13 @@ .searchArea { flex-shrink: 0; position: relative; + z-index: 10000; } .searchInput { width: 100%; + position: relative; + z-index: 10000; } .searchResults { @@ -19,7 +22,7 @@ top: 100%; left: 0; right: 0; - z-index: 1000; + z-index: 10001; background: #fff; border: 1px solid #e8e8e8; border-radius: 4px; @@ -27,6 +30,7 @@ margin-top: 4px; max-height: 300px; overflow-y: auto; + pointer-events: auto; :global(.ant-list-item) { cursor: pointer; diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/selectMap.tsx b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/selectMap.tsx index d09316ce..7df7fdfc 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/selectMap.tsx +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/selectMap.tsx @@ -8,7 +8,7 @@ import styles from "./selectMap.module.scss"; declare global { interface Window { TMap: any; - geolocationRef: any; // 全局IP定位服务引用 + geolocationRef: any; // 全局IP定位服务引用(TMap.service.IPLocation实例) } } @@ -117,8 +117,8 @@ const SelectMap: React.FC = ({ // 创建地理编码服务(用于反向地理编码) geocoderRef.current = new window.TMap.service.Geocoder(); - // 使用腾讯地图内置的定位服务 - window.geolocationRef = window.TMap.service.Geolocation; + // 创建IP定位服务 + window.geolocationRef = new window.TMap.service.IPLocation(); // 创建搜索建议服务 suggestServiceRef.current = new window.TMap.service.Suggestion({ @@ -327,13 +327,13 @@ const SelectMap: React.FC = ({ setIsLocating(true); try { if (window.geolocationRef) { - window.geolocationRef.getLocation({ - timeout: 10000, - convert: true, - success: function (result: any) { + window.geolocationRef + .locate() + .then((result: any) => { setIsLocating(false); - if (result && result.location) { - const { lat, lng } = result.location; + console.log("IP定位结果:", result); + if (result && result.result && result.result.location) { + const { lat, lng } = result.result.location; message.info("已定位到您的大致位置"); initializeUserLocation(lat, lng, false); } else { @@ -342,16 +342,16 @@ const SelectMap: React.FC = ({ // 使用默认位置(北京市) initializeUserLocation(39.908823, 116.39747, true); } - }, - error: function () { + }) + .catch((error: any) => { setIsLocating(false); + console.error("IP定位失败:", error); message.info("无法获取您的位置,已定位到北京"); // 使用默认位置(北京市) initializeUserLocation(39.908823, 116.39747, true); - }, - }); + }); } else { - // 地理编码服务未初始化:使用默认位置 + // IP定位服务未初始化:使用默认位置 setIsLocating(false); message.info("无法获取您的位置,已定位到北京"); // 使用默认位置(北京市) From 43b60a0049696bb9b3ed3ddd25d74d561be273f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B6=85=E7=BA=A7=E8=80=81=E7=99=BD=E5=85=94?= Date: Sat, 22 Nov 2025 14:16:40 +0800 Subject: [PATCH 12/15] =?UTF-8?q?=E4=BC=98=E5=8C=96SelectMap=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=EF=BC=8C=E5=A2=9E=E5=BC=BATMap=20API=E5=8A=A0?= =?UTF-8?q?=E8=BD=BD=E5=92=8C=E5=88=9D=E5=A7=8B=E5=8C=96=E9=80=BB=E8=BE=91?= =?UTF-8?q?=EF=BC=8C=E6=B7=BB=E5=8A=A0API=E5=8F=AF=E7=94=A8=E6=80=A7?= =?UTF-8?q?=E6=A3=80=E6=9F=A5=EF=BC=8C=E6=94=B9=E8=BF=9B=E6=A0=87=E8=AE=B0?= =?UTF-8?q?=E6=A0=B7=E5=BC=8F=E5=88=9B=E5=BB=BA=E5=92=8C=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E5=A4=84=E7=90=86=EF=BC=8C=E6=8F=90=E5=8D=87=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E4=BD=93=E9=AA=8C=E5=92=8C=E7=A8=B3=E5=AE=9A=E6=80=A7=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MessageEnter/components/selectMap.tsx | 793 ++++++++++++------ 1 file changed, 547 insertions(+), 246 deletions(-) diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/selectMap.tsx b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/selectMap.tsx index 7df7fdfc..71de2273 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/selectMap.tsx +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/selectMap.tsx @@ -70,7 +70,17 @@ const SelectMap: React.FC = ({ useEffect(() => { // 检查TMap是否已经加载 if (window.TMap) { - setTmapLoaded(true); + // 等待 API 完全初始化 + const checkAPIReady = () => { + if (window.TMap && window.TMap.Map) { + console.log("腾讯地图SDK已加载,API 可用"); + setTmapLoaded(true); + } else { + // 如果 API 还未完全初始化,等待一段时间后重试 + setTimeout(checkAPIReady, 100); + } + }; + checkAPIReady(); return; } @@ -80,8 +90,19 @@ const SelectMap: React.FC = ({ "https://map.qq.com/api/gljs?v=1.exp&libraries=service&key=7DZBZ-ZSRK3-QJN3W-O5VTV-4E2P6-7GFYX"; script.async = true; script.onload = () => { - console.log("腾讯地图SDK加载成功"); - setTmapLoaded(true); + console.log("腾讯地图SDK脚本加载成功,等待 API 初始化..."); + // 等待 API 完全初始化 + const checkAPIReady = () => { + if (window.TMap && window.TMap.Map) { + console.log("腾讯地图SDK API 初始化完成"); + setTmapLoaded(true); + } else { + // 如果 API 还未完全初始化,等待一段时间后重试(最多等待 5 秒) + setTimeout(checkAPIReady, 100); + } + }; + // 延迟检查,给 API 一些初始化时间 + setTimeout(checkAPIReady, 200); }; script.onerror = () => { console.error("腾讯地图SDK加载失败"); @@ -97,220 +118,359 @@ const SelectMap: React.FC = ({ }; }, []); + // 检查 TMap API 是否可用(辅助函数) + const checkTMapAPI = () => { + if (!window.TMap) { + console.error("TMap 未加载"); + return false; + } + + // 检查 MultiMarker 是否可用 + if (!window.TMap.MultiMarker) { + console.error("TMap.MultiMarker 不可用", { + TMap: window.TMap, + keys: Object.keys(window.TMap || {}), + }); + return false; + } + + // 检查 Style 是否存在(可能是构造函数、对象或命名空间) + // 注意:Style 可能不是构造函数,而是配置对象或命名空间 + const hasStyle = + window.TMap.MultiMarker.Style !== undefined || + window.TMap.MarkerStyle !== undefined; + + if (!hasStyle) { + console.warn("TMap Style API 不可用,将使用配置对象方式", { + MultiMarker: window.TMap.MultiMarker, + MultiMarkerKeys: Object.keys(window.TMap.MultiMarker || {}), + MarkerStyle: window.TMap.MarkerStyle, + }); + // 不返回 false,因为 MultiMarker 可能接受配置对象 + } + + return true; + }; + + // 创建标记样式(兼容不同的 API 版本) + const createMarkerStyle = (options: any) => { + // 检查 MultiMarker.Style 是否存在 + if (window.TMap.MultiMarker?.Style) { + // 如果 Style 是函数(构造函数),使用 new + if (typeof window.TMap.MultiMarker.Style === "function") { + try { + return new window.TMap.MultiMarker.Style(options); + } catch (error) { + console.warn( + "使用 new MultiMarker.Style 失败,尝试直接返回配置对象:", + error, + ); + // 如果构造函数调用失败,直接返回配置对象 + return options; + } + } else { + // 如果 Style 不是函数,可能是对象或命名空间,直接返回配置对象 + // MultiMarker 可能接受配置对象而不是 Style 实例 + console.log("MultiMarker.Style 不是构造函数,直接使用配置对象"); + return options; + } + } + // 尝试 MarkerStyle + if (window.TMap.MarkerStyle) { + if (typeof window.TMap.MarkerStyle === "function") { + try { + return new window.TMap.MarkerStyle(options); + } catch (error) { + console.warn( + "使用 new MarkerStyle 失败,尝试直接返回配置对象:", + error, + ); + return options; + } + } else { + return options; + } + } + // 如果都不存在,直接返回配置对象(让 MultiMarker 自己处理) + console.warn("未找到 Style API,直接使用配置对象"); + return options; + }; + // 初始化地图 useEffect(() => { if (visible && mapContainerRef.current && tmapLoaded && window.TMap) { console.log("开始初始化地图"); + console.log("TMap API 检查:", { + TMap: !!window.TMap, + MultiMarker: !!window.TMap.MultiMarker, + MultiMarkerStyle: !!window.TMap.MultiMarker?.Style, + MarkerStyle: !!window.TMap.MarkerStyle, + }); - try { - // 创建地图实例 - const center = new window.TMap.LatLng(39.908823, 116.39747); // 默认北京 - const mapInstance = new window.TMap.Map(mapContainerRef.current, { - center: center, - zoom: 13, - rotation: 0, - pitch: 0, - }); + // 检查容器尺寸,确保容器有有效的宽高 + const checkContainerSize = () => { + if (!mapContainerRef.current) return false; + const rect = mapContainerRef.current.getBoundingClientRect(); + return rect.width > 0 && rect.height > 0; + }; - setMap(mapInstance); + let mapInstance: any = null; + let handleMapClickFn: ((evt: any) => void) | null = null; + let delayTimer: NodeJS.Timeout | null = null; + let isMounted = true; // 标记弹窗是否仍然打开 - // 创建地理编码服务(用于反向地理编码) - geocoderRef.current = new window.TMap.service.Geocoder(); + // 初始化地图函数(使用箭头函数避免函数声明位置问题) + const initializeMap = () => { + if (!mapContainerRef.current) return; - // 创建IP定位服务 - window.geolocationRef = new window.TMap.service.IPLocation(); - - // 创建搜索建议服务 - suggestServiceRef.current = new window.TMap.service.Suggestion({ - pageSize: 10, - autoExtend: true, - }); - - // 地图点击事件处理函数 - const handleMapClick = (evt: any) => { - try { - const lat = evt.latLng.getLat(); - const lng = evt.latLng.getLng(); - - console.log("地图点击:", lat, lng); - - // 更新标记点 - if (markerRef.current) { - markerRef.current.setMap(null); - markerRef.current = null; - } - - // 创建新标记 - const newMarker = new window.TMap.MultiMarker({ - id: "marker-layer", - map: mapInstance, - geometries: [ - { - id: "selected-marker", - styleId: "marker", - position: new window.TMap.LatLng(lat, lng), - properties: { - title: "选中位置", - }, - }, - ], - }); - - markerRef.current = newMarker; - - // 设置基本位置信息(防止白屏) - setSelectedLocation({ - x: lng.toString(), - y: lat.toString(), - scale: "16", - label: `${lat.toFixed(6)}, ${lng.toFixed(6)}`, - poiname: "选中位置", - maptype: "0", - poiid: "", - }); - - // 反向地理编码获取地址 - setIsReverseGeocoding(true); - geocoderRef.current - .getAddress({ location: new window.TMap.LatLng(lat, lng) }) - .then((result: any) => { - setIsReverseGeocoding(false); - console.log("反向地理编码结果:", result); - - try { - if (result && result.result) { - const resultData = result.result; - const address = resultData.address || ""; - const addressComponent = resultData.address_component || {}; - const formattedAddresses = - resultData.formatted_addresses || {}; - - // 构建地址标签 - let addressLabel = - formattedAddresses.recommend || - formattedAddresses.rough || - address; - - if (!addressLabel) { - const parts = []; - if (addressComponent.province) - parts.push(addressComponent.province); - if (addressComponent.city) - parts.push(addressComponent.city); - if (addressComponent.district) - parts.push(addressComponent.district); - if (addressComponent.street) - parts.push(addressComponent.street); - if (addressComponent.street_number) - parts.push(addressComponent.street_number); - addressLabel = parts.join(""); - } - - if (!addressLabel) { - addressLabel = `${lat.toFixed(6)}, ${lng.toFixed(6)}`; - } - - setSelectedLocation({ - x: lng.toString(), - y: lat.toString(), - scale: "16", - label: addressLabel, - poiname: addressComponent.street || "未知位置", - maptype: "0", - poiid: resultData.poi_id || "", - }); - } else { - message.warning("获取详细地址信息失败,将使用坐标显示"); - } - } catch (error) { - console.error("解析地址信息错误:", error); - message.warning("解析地址信息失败,将使用坐标显示"); - } - }) - .catch((error: any) => { - setIsReverseGeocoding(false); - console.error("反向地理编码错误:", error); - message.warning("获取详细地址信息失败,将使用坐标显示"); - }); - } catch (error) { - console.error("地图点击处理错误:", error); - message.error("处理地图点击时出错,请重试"); - } - }; - - // 绑定地图点击事件 - mapInstance.on("click", handleMapClick); - - // 使用腾讯地图API初始化用户位置 - const initializeUserLocation = ( - lat: number, - lng: number, - isDefault: boolean = false, - ) => { - console.log(isDefault ? "使用默认位置:" : "用户位置:", lat, lng); - - // 移动地图中心到位置 - const userLocation = new window.TMap.LatLng(lat, lng); - mapInstance.setCenter(userLocation); - mapInstance.setZoom(16); - - // 添加标记点 - if (markerRef.current) { - markerRef.current.setMap(null); - markerRef.current = null; + try { + // 再次检查容器尺寸 + const rect = mapContainerRef.current.getBoundingClientRect(); + if (rect.width <= 0 || rect.height <= 0) { + console.error("地图容器尺寸无效:", rect); + message.error("地图容器尺寸无效,请刷新页面重试"); + return; } - const newMarker = new window.TMap.MultiMarker({ - id: "marker-layer", - map: mapInstance, - geometries: [ - { - id: "user-location", - styleId: "marker", - position: userLocation, - properties: { - title: isDefault ? "默认位置" : "当前位置", - }, - }, - ], + // 创建地图实例 + const center = new window.TMap.LatLng(39.908823, 116.39747); // 默认北京 + mapInstance = new window.TMap.Map(mapContainerRef.current, { + center: center, + zoom: 13, + rotation: 0, + pitch: 0, }); - markerRef.current = newMarker; + setMap(mapInstance); - // 使用腾讯地图服务获取该位置的地址信息 - setIsReverseGeocoding(true); - geocoderRef.current - .getAddress({ location: userLocation }) - .then((result: any) => { - setIsReverseGeocoding(false); - if (result && result.result) { - const resultData = result.result; - const formattedAddresses = resultData.formatted_addresses || {}; - const addressComponent = resultData.address_component || {}; + // 创建地理编码服务(用于反向地理编码) + geocoderRef.current = new window.TMap.service.Geocoder(); - const addressLabel = - formattedAddresses.recommend || - formattedAddresses.rough || - resultData.address || - `${lat.toFixed(6)}, ${lng.toFixed(6)}`; + // 创建IP定位服务 + window.geolocationRef = new window.TMap.service.IPLocation(); - setSelectedLocation({ - x: lng.toString(), - y: lat.toString(), - scale: "16", - label: addressLabel, - poiname: - addressComponent.street || - (isDefault ? "默认位置" : "当前位置"), - maptype: "0", - poiid: resultData.poi_id || "", - }); + // 创建搜索建议服务 + suggestServiceRef.current = new window.TMap.service.Suggestion({ + pageSize: 10, + autoExtend: true, + }); + + // 地图点击事件处理函数 + handleMapClickFn = (evt: any) => { + try { + // 检查弹窗是否仍然打开,以及必要的API是否可用 + if (!isMounted || !mapInstance || !mapContainerRef.current) { + return; } - }) - .catch((error: any) => { - setIsReverseGeocoding(false); - console.error("获取地址信息失败:", error); - // 即使获取地址失败,也设置基本的位置信息 + + // 检查 TMap API 是否可用 + if (!checkTMapAPI()) { + console.error("TMap API 不可用,无法创建标记点"); + message.warning("地图标记功能不可用,请刷新页面重试"); + return; + } + + const lat = evt.latLng.getLat(); + const lng = evt.latLng.getLng(); + + console.log("地图点击:", lat, lng); + + // 更新标记点 + if (markerRef.current) { + markerRef.current.setMap(null); + markerRef.current = null; + } + + // 创建标记样式 + const markerStyle = createMarkerStyle({ + width: 25, + height: 35, + anchor: { x: 12, y: 35 }, + src: "https://mapapi.qq.com/web/lbs/javascriptGL/demo/img/markerDefault.png", + }); + + // 创建新标记 + const newMarker = new window.TMap.MultiMarker({ + id: "marker-layer", + map: mapInstance, + styles: { + marker: markerStyle, + }, + geometries: [ + { + id: "selected-marker", + styleId: "marker", + position: new window.TMap.LatLng(lat, lng), + properties: { + title: "选中位置", + }, + }, + ], + }); + + markerRef.current = newMarker; + + // 设置基本位置信息(防止白屏) + setSelectedLocation({ + x: lng.toString(), + y: lat.toString(), + scale: "16", + label: `${lat.toFixed(6)}, ${lng.toFixed(6)}`, + poiname: "选中位置", + maptype: "0", + poiid: "", + }); + + // 反向地理编码获取地址 + if (!isMounted || !geocoderRef.current) { + return; + } + + setIsReverseGeocoding(true); + geocoderRef.current + .getAddress({ location: new window.TMap.LatLng(lat, lng) }) + .then((result: any) => { + // 检查弹窗是否仍然打开 + if (!isMounted) { + return; + } + setIsReverseGeocoding(false); + console.log("反向地理编码结果:", result); + + try { + if (result && result.result) { + const resultData = result.result; + const address = resultData.address || ""; + const addressComponent = + resultData.address_component || {}; + const formattedAddresses = + resultData.formatted_addresses || {}; + + // 构建地址标签 + let addressLabel = + formattedAddresses.recommend || + formattedAddresses.rough || + address; + + if (!addressLabel) { + const parts = []; + if (addressComponent.province) + parts.push(addressComponent.province); + if (addressComponent.city) + parts.push(addressComponent.city); + if (addressComponent.district) + parts.push(addressComponent.district); + if (addressComponent.street) + parts.push(addressComponent.street); + if (addressComponent.street_number) + parts.push(addressComponent.street_number); + addressLabel = parts.join(""); + } + + if (!addressLabel) { + addressLabel = `${lat.toFixed(6)}, ${lng.toFixed(6)}`; + } + + setSelectedLocation({ + x: lng.toString(), + y: lat.toString(), + scale: "16", + label: addressLabel, + poiname: addressComponent.street || "未知位置", + maptype: "0", + poiid: resultData.poi_id || "", + }); + } else { + message.warning("获取详细地址信息失败,将使用坐标显示"); + } + } catch (error) { + console.error("解析地址信息错误:", error); + message.warning("解析地址信息失败,将使用坐标显示"); + } + }) + .catch((error: any) => { + // 检查弹窗是否仍然打开 + if (!isMounted) { + return; + } + setIsReverseGeocoding(false); + console.error("反向地理编码错误:", error); + message.warning("获取详细地址信息失败,将使用坐标显示"); + }); + } catch (error) { + console.error("地图点击处理错误:", error); + message.error("处理地图点击时出错,请重试"); + } + }; + + // 绑定地图点击事件 + mapInstance.on("click", handleMapClickFn); + + // 使用腾讯地图API初始化用户位置 + const initializeUserLocation = ( + lat: number, + lng: number, + isDefault: boolean = false, + ) => { + // 检查弹窗是否仍然打开,以及必要的API是否可用 + if (!isMounted || !mapInstance || !mapContainerRef.current) { + console.log("弹窗已关闭或地图实例无效,跳过初始化位置"); + return; + } + + // 检查 TMap API 是否可用 + if (!checkTMapAPI()) { + console.error("TMap API 不可用,无法创建标记点"); + message.warning("地图标记功能不可用,请刷新页面重试"); + return; + } + + // 创建位置对象 + let userLocation: any = null; + try { + console.log(isDefault ? "使用默认位置:" : "用户位置:", lat, lng); + + // 移动地图中心到位置 + userLocation = new window.TMap.LatLng(lat, lng); + mapInstance.setCenter(userLocation); + mapInstance.setZoom(16); + + // 添加标记点 + if (markerRef.current) { + markerRef.current.setMap(null); + markerRef.current = null; + } + + // 创建标记样式 + const markerStyle = createMarkerStyle({ + width: 25, + height: 35, + anchor: { x: 12, y: 35 }, + src: "https://mapapi.qq.com/web/lbs/javascriptGL/demo/img/markerDefault.png", + }); + + const newMarker = new window.TMap.MultiMarker({ + id: "marker-layer", + map: mapInstance, + styles: { + marker: markerStyle, + }, + geometries: [ + { + id: "user-location", + styleId: "marker", + position: userLocation, + properties: { + title: isDefault ? "默认位置" : "当前位置", + }, + }, + ], + }); + + markerRef.current = newMarker; + } catch (error) { + console.error("创建标记点失败:", error); + // 即使创建标记失败,也设置基本的位置信息 setSelectedLocation({ x: lng.toString(), y: lat.toString(), @@ -320,63 +480,186 @@ const SelectMap: React.FC = ({ maptype: "0", poiid: "", }); - }); - }; + return; + } - // 使用腾讯地图IP定位获取用户位置 - setIsLocating(true); - try { - if (window.geolocationRef) { - window.geolocationRef - .locate() + // 使用腾讯地图服务获取该位置的地址信息 + if (!isMounted || !geocoderRef.current || !userLocation) { + return; + } + + setIsReverseGeocoding(true); + geocoderRef.current + .getAddress({ location: userLocation }) .then((result: any) => { - setIsLocating(false); - console.log("IP定位结果:", result); - if (result && result.result && result.result.location) { - const { lat, lng } = result.result.location; - message.info("已定位到您的大致位置"); - initializeUserLocation(lat, lng, false); - } else { - // IP定位失败:使用默认位置 - message.info("无法获取您的位置,已定位到北京"); - // 使用默认位置(北京市) - initializeUserLocation(39.908823, 116.39747, true); + // 检查弹窗是否仍然打开 + if (!isMounted) { + return; + } + setIsReverseGeocoding(false); + if (result && result.result) { + const resultData = result.result; + const formattedAddresses = + resultData.formatted_addresses || {}; + const addressComponent = resultData.address_component || {}; + + const addressLabel = + formattedAddresses.recommend || + formattedAddresses.rough || + resultData.address || + `${lat.toFixed(6)}, ${lng.toFixed(6)}`; + + setSelectedLocation({ + x: lng.toString(), + y: lat.toString(), + scale: "16", + label: addressLabel, + poiname: + addressComponent.street || + (isDefault ? "默认位置" : "当前位置"), + maptype: "0", + poiid: resultData.poi_id || "", + }); } }) .catch((error: any) => { - setIsLocating(false); - console.error("IP定位失败:", error); - message.info("无法获取您的位置,已定位到北京"); - // 使用默认位置(北京市) - initializeUserLocation(39.908823, 116.39747, true); + // 检查弹窗是否仍然打开 + if (!isMounted) { + return; + } + setIsReverseGeocoding(false); + console.error("获取地址信息失败:", error); + // 即使获取地址失败,也设置基本的位置信息 + setSelectedLocation({ + x: lng.toString(), + y: lat.toString(), + scale: "16", + label: `${lat.toFixed(6)}, ${lng.toFixed(6)}`, + poiname: isDefault ? "默认位置" : "当前位置", + maptype: "0", + poiid: "", + }); }); - } else { - // IP定位服务未初始化:使用默认位置 - setIsLocating(false); - message.info("无法获取您的位置,已定位到北京"); - // 使用默认位置(北京市) - initializeUserLocation(39.908823, 116.39747, true); + }; + + // 使用腾讯地图IP定位获取用户位置 + setIsLocating(true); + try { + if (window.geolocationRef) { + window.geolocationRef + .locate() + .then((result: any) => { + // 检查弹窗是否仍然打开 + if (!isMounted) { + return; + } + setIsLocating(false); + console.log("IP定位结果:", result); + if (result && result.result && result.result.location) { + const { lat, lng } = result.result.location; + // message.info("已定位到您的大致位置"); + initializeUserLocation(lat, lng, false); + } else { + // IP定位失败:使用默认位置 + message.info("无法获取您的位置,已定位到北京"); + // 使用默认位置(北京市) + initializeUserLocation(39.908823, 116.39747, true); + } + }) + .catch((error: any) => { + // 检查弹窗是否仍然打开 + if (!isMounted) { + return; + } + setIsLocating(false); + console.error("IP定位失败:", error); + message.info("无法获取您的位置,已定位到北京"); + // 使用默认位置(北京市) + initializeUserLocation(39.908823, 116.39747, true); + }); + } else { + // IP定位服务未初始化:使用默认位置 + setIsLocating(false); + message.info("无法获取您的位置,已定位到北京"); + // 使用默认位置(北京市) + initializeUserLocation(39.908823, 116.39747, true); + } + } catch (error) { + // 捕获任何可能的错误,防止白屏 + console.error("定位过程中发生错误:", error); + if (isMounted) { + setIsLocating(false); + message.error("定位服务出现异常,已定位到北京"); + // 使用默认位置(北京市) + initializeUserLocation(39.908823, 116.39747, true); + } } } catch (error) { - // 捕获任何可能的错误,防止白屏 - console.error("定位过程中发生错误:", error); + console.error("初始化地图时出错:", error); + message.error("地图加载失败,请刷新页面重试"); setIsLocating(false); - message.error("定位服务出现异常,已定位到北京"); - // 使用默认位置(北京市) - initializeUserLocation(39.908823, 116.39747, true); + } + }; + + // 使用 requestAnimationFrame 确保容器尺寸正确后再初始化 + const initTimer = requestAnimationFrame(() => { + // 再次检查容器尺寸 + if (!checkContainerSize()) { + console.log("容器尺寸无效,延迟初始化地图"); + delayTimer = setTimeout(() => { + if (checkContainerSize() && mapContainerRef.current) { + initializeMap(); + } else { + console.error("地图容器尺寸仍然无效"); + message.error("地图容器初始化失败,请刷新页面重试"); + } + }, 100); + return; } - return () => { - // 清理地图事件监听 - if (mapInstance) { - mapInstance.off("click", handleMapClick); + // 容器尺寸有效,立即初始化 + initializeMap(); + }); + + // 清理函数 + return () => { + // 标记弹窗已关闭 + isMounted = false; + // 取消 requestAnimationFrame + cancelAnimationFrame(initTimer); + // 清理延迟定时器 + if (delayTimer) { + clearTimeout(delayTimer); + } + // 清理地图事件监听 + if (mapInstance && handleMapClickFn) { + try { + mapInstance.off("click", handleMapClickFn); + } catch (error) { + console.error("清理地图事件监听失败:", error); } - }; - } catch (error) { - console.error("初始化地图时出错:", error); - message.error("地图加载失败,请刷新页面重试"); - setIsLocating(false); - } + } + // 清理地图实例 + if (mapInstance) { + try { + mapInstance.destroy(); + } catch (error) { + console.error("销毁地图实例失败:", error); + } + mapInstance = null; + } + // 清理标记点 + if (markerRef.current) { + try { + markerRef.current.setMap(null); + } catch (error) { + console.error("清理标记点失败:", error); + } + markerRef.current = null; + } + // 重置地图状态 + setMap(null); + }; } }, [visible, tmapLoaded]); @@ -445,6 +728,13 @@ const SelectMap: React.FC = ({ return; } + // 检查 TMap API 是否可用 + if (!checkTMapAPI()) { + console.error("TMap API 不可用,无法创建标记点"); + message.error("地图API不可用,请刷新页面重试"); + return; + } + const lat = result.location.lat; const lng = result.location.lng; @@ -460,9 +750,20 @@ const SelectMap: React.FC = ({ markerRef.current = null; } + // 创建标记样式 + const markerStyle = createMarkerStyle({ + width: 25, + height: 35, + anchor: { x: 12, y: 35 }, + src: "https://mapapi.qq.com/web/lbs/javascriptGL/demo/img/markerDefault.png", + }); + const newMarker = new window.TMap.MultiMarker({ id: "marker-layer", map: map, + styles: { + marker: markerStyle, + }, geometries: [ { id: "selected-poi", From deac306f699eda2bd5e008cfa1c2e1eac81f446b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B6=85=E7=BA=A7=E8=80=81=E7=99=BD=E5=85=94?= Date: Sat, 22 Nov 2025 15:57:16 +0800 Subject: [PATCH 13/15] =?UTF-8?q?=E4=BF=AE=E6=AD=A3SelectMap=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E4=B8=AD=E7=9A=84=E7=BB=8F=E7=BA=AC=E5=BA=A6=E6=A0=87?= =?UTF-8?q?=E8=AE=B0=E9=A1=BA=E5=BA=8F=EF=BC=8C=E6=9B=B4=E6=96=B0XML?= =?UTF-8?q?=E7=94=9F=E6=88=90=E9=80=BB=E8=BE=91=E4=BB=A5=E7=A1=AE=E4=BF=9D?= =?UTF-8?q?=E7=89=B9=E6=AE=8A=E5=AD=97=E7=AC=A6=E8=BD=AC=E4=B9=89=EF=BC=8C?= =?UTF-8?q?=E6=8F=90=E5=8D=87=E5=9C=B0=E5=9B=BE=E4=BD=8D=E7=BD=AE=E9=80=89?= =?UTF-8?q?=E6=8B=A9=E7=9A=84=E5=87=86=E7=A1=AE=E6=80=A7=E5=92=8C=E7=A8=B3?= =?UTF-8?q?=E5=AE=9A=E6=80=A7=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MessageEnter/components/selectMap.tsx | 93 +++++++++++++------ 1 file changed, 63 insertions(+), 30 deletions(-) diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/selectMap.tsx b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/selectMap.tsx index 71de2273..093b3067 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/selectMap.tsx +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/selectMap.tsx @@ -34,8 +34,8 @@ interface SearchResult { } interface LocationData { - x: string; // 经度 - y: string; // 纬度 + x: string; // 纬度 + y: string; // 经度 scale: string; // 缩放级别 label: string; // 地址标签 poiname: string; // POI名称 @@ -66,6 +66,17 @@ const SelectMap: React.FC = ({ const markerRef = useRef(null); const { sendCommand } = useWebSocketStore.getState(); + // XML转义函数,防止特殊字符破坏XML格式 + const escapeXml = (str: string | undefined | null): string => { + if (!str) return ""; + return String(str) + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); + }; + // 加载腾讯地图SDK useEffect(() => { // 检查TMap是否已经加载 @@ -311,11 +322,12 @@ const SelectMap: React.FC = ({ markerRef.current = newMarker; // 设置基本位置信息(防止白屏) + // 经纬度格式化为6位小数(微信位置消息标准格式) setSelectedLocation({ - x: lng.toString(), - y: lat.toString(), + x: lat.toString(), + y: lng.toString(), scale: "16", - label: `${lat.toFixed(6)}, ${lng.toFixed(6)}`, + label: `${lat}, ${lng}`, poiname: "选中位置", maptype: "0", poiid: "", @@ -368,12 +380,13 @@ const SelectMap: React.FC = ({ } if (!addressLabel) { - addressLabel = `${lat.toFixed(6)}, ${lng.toFixed(6)}`; + addressLabel = `${lat}, ${lng}`; } + // 经纬度格式化为6位小数(微信位置消息标准格式) setSelectedLocation({ - x: lng.toString(), - y: lat.toString(), + x: lat.toString(), + y: lng.toString(), scale: "16", label: addressLabel, poiname: addressComponent.street || "未知位置", @@ -471,11 +484,12 @@ const SelectMap: React.FC = ({ } catch (error) { console.error("创建标记点失败:", error); // 即使创建标记失败,也设置基本的位置信息 + // 经纬度格式化为6位小数(微信位置消息标准格式) setSelectedLocation({ - x: lng.toString(), - y: lat.toString(), + x: lat.toString(), + y: lng.toString(), scale: "16", - label: `${lat.toFixed(6)}, ${lng.toFixed(6)}`, + label: `${lat}, ${lng}`, poiname: isDefault ? "默认位置" : "当前位置", maptype: "0", poiid: "", @@ -507,11 +521,12 @@ const SelectMap: React.FC = ({ formattedAddresses.recommend || formattedAddresses.rough || resultData.address || - `${lat.toFixed(6)}, ${lng.toFixed(6)}`; + `${lat}, ${lng}`; + // 经纬度格式化为6位小数(微信位置消息标准格式) setSelectedLocation({ - x: lng.toString(), - y: lat.toString(), + x: lat.toString(), + y: lng.toString(), scale: "16", label: addressLabel, poiname: @@ -530,11 +545,12 @@ const SelectMap: React.FC = ({ setIsReverseGeocoding(false); console.error("获取地址信息失败:", error); // 即使获取地址失败,也设置基本的位置信息 + // 经纬度格式化为6位小数(微信位置消息标准格式) setSelectedLocation({ - x: lng.toString(), - y: lat.toString(), + x: lat.toString(), + y: lng.toString(), scale: "16", - label: `${lat.toFixed(6)}, ${lng.toFixed(6)}`, + label: `${lat}, ${lng}`, poiname: isDefault ? "默认位置" : "当前位置", maptype: "0", poiid: "", @@ -779,9 +795,10 @@ const SelectMap: React.FC = ({ markerRef.current = newMarker; // 设置选中的位置信息 + // 经纬度格式化为6位小数(微信位置消息标准格式) setSelectedLocation({ - x: lng.toString(), - y: lat.toString(), + x: lat.toString(), + y: lng.toString(), scale: "16", label: result.address || result.title, poiname: result.title || "", @@ -806,16 +823,31 @@ const SelectMap: React.FC = ({ return; } - // 生成XML格式的位置信息 - const locationXml = ``; + // 转义XML特殊字符,确保格式正确 + // 注意:经纬度在存储时已经格式化为6位小数,直接使用即可 + const escapedLabel = escapeXml(selectedLocation.label); + const escapedPoiname = escapeXml(selectedLocation.poiname); + const scale = selectedLocation.scale || "16"; + const maptype = selectedLocation.maptype || "0"; + const poiid = escapeXml(selectedLocation.poiid || ""); + + // 生成XML格式的位置信息(格式与正确示例保持一致) + const locationXml = + ''; // 如果有onConfirm回调,调用它 if (onConfirm) { @@ -850,6 +882,7 @@ const SelectMap: React.FC = ({ }; addMessage(localMessage); + console.log(locationXml); // 发送消息到服务器 sendCommand("CmdSendMessage", { @@ -971,7 +1004,7 @@ const SelectMap: React.FC = ({ {selectedLocation.label || selectedLocation.poiname}
    - 经度: {selectedLocation.x}, 纬度: {selectedLocation.y} + 经度: {selectedLocation.y}, 纬度: {selectedLocation.x}
    )} From bda57a84e862ff4572f84222e8ba1d533798d3fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B6=85=E7=BA=A7=E8=80=81=E7=99=BD=E5=85=94?= Date: Sat, 22 Nov 2025 16:15:33 +0800 Subject: [PATCH 14/15] =?UTF-8?q?=E4=BC=98=E5=8C=96LocationMessage?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=EF=BC=8C=E6=B7=BB=E5=8A=A0=E7=BB=8F=E7=BA=AC?= =?UTF-8?q?=E5=BA=A6=E6=A0=BC=E5=BC=8F=E5=8C=96=E5=8A=9F=E8=83=BD=EF=BC=8C?= =?UTF-8?q?=E7=94=9F=E6=88=90=E9=9D=99=E6=80=81=E5=9C=B0=E5=9B=BE=E9=A2=84?= =?UTF-8?q?=E8=A7=88=E5=9B=BEURL=EF=BC=8C=E6=94=B9=E8=BF=9B=E4=BD=8D?= =?UTF-8?q?=E7=BD=AE=E4=BF=A1=E6=81=AF=E5=B1=95=E7=A4=BA=EF=BC=8C=E5=A2=9E?= =?UTF-8?q?=E5=BC=BA=E7=94=A8=E6=88=B7=E4=BD=93=E9=AA=8C=E5=92=8C=E8=A7=86?= =?UTF-8?q?=E8=A7=89=E6=95=88=E6=9E=9C=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LocationMessage.module.scss | 76 ++++++++++++++- .../components/LocationMessage/index.tsx | 92 +++++++++++++------ 2 files changed, 137 insertions(+), 31 deletions(-) diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageRecord/components/LocationMessage/LocationMessage.module.scss b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageRecord/components/LocationMessage/LocationMessage.module.scss index 61ef4774..99bdc3e4 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageRecord/components/LocationMessage/LocationMessage.module.scss +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageRecord/components/LocationMessage/LocationMessage.module.scss @@ -9,7 +9,7 @@ // 位置消息基础样式 .locationMessage { - max-width: 420px; + width: 420px; margin: 4px 0; } @@ -21,6 +21,8 @@ cursor: pointer; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); overflow: hidden; + display: flex; + flex-direction: column; &:hover { box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12); @@ -33,6 +35,45 @@ } } +// 地图预览区域 +.mapPreview { + position: relative; + width: 100%; + height: 200px; + overflow: hidden; + background: #f5f5f5; + display: flex; + align-items: center; + justify-content: center; +} + +.mapImage { + width: 100%; + height: 100%; + object-fit: cover; + display: block; +} + +.mapPlaceholder { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + display: none; + flex-direction: column; + align-items: center; + justify-content: center; + background: #f5f5f5; + color: #999; + font-size: 14px; + gap: 8px; + + span:first-child { + font-size: 32px; + } +} + // 位置消息头部 .locationHeader { display: flex; @@ -70,6 +111,21 @@ // 位置消息内容 .locationContent { padding: 12px 16px; + display: flex; + flex-direction: column; + gap: 4px; +} + +.roadName { + font-size: 16px; + font-weight: 600; + color: #1a1a1a; + line-height: 1.4; + display: -webkit-box; + -webkit-line-clamp: 1; + -webkit-box-orient: vertical; + overflow: hidden; + text-overflow: ellipsis; } .poiName { @@ -89,9 +145,8 @@ font-size: 13px; color: #666; line-height: 1.5; - margin-bottom: 12px; display: -webkit-box; - -webkit-line-clamp: 3; + -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; text-overflow: ellipsis; @@ -175,13 +230,17 @@ // 响应式设计 @media (max-width: 768px) { .locationMessage { - max-width: 280px; + width: 280px; } .locationCard { border-radius: 10px; } + .mapPreview { + height: 150px; + } + .locationHeader { padding: 10px 14px 6px; } @@ -253,6 +312,15 @@ } } + .mapPreview { + background: #2a2a2a; + } + + .mapPlaceholder { + background: #2a2a2a; + color: #999; + } + .locationHeader { background: linear-gradient(135deg, #2a2a2a 0%, #1a1a1a 100%); border-bottom-color: #333; diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageRecord/components/LocationMessage/index.tsx b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageRecord/components/LocationMessage/index.tsx index c2d6d8a4..4456f3e8 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageRecord/components/LocationMessage/index.tsx +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageRecord/components/LocationMessage/index.tsx @@ -85,10 +85,36 @@ const LocationMessage: React.FC = ({ content }) => { return renderErrorMessage("[位置消息 - 解析失败]"); } - // 生成地图链接 + // 格式化经纬度为6位小数 + const formatCoordinate = (coord: string): string => { + const num = parseFloat(coord); + if (isNaN(num)) { + return coord; // 如果无法解析,返回原值 + } + return num.toFixed(6); + }; + + // 生成地图链接(用于点击跳转) const generateMapUrl = (lat: string, lng: string, label: string) => { + const formattedLat = formatCoordinate(lat); + const formattedLng = formatCoordinate(lng); // 使用腾讯地图链接 - return `https://apis.map.qq.com/uri/v1/marker?marker=coord:${lat},${lng};title:${encodeURIComponent(label)}&referer=wechat`; + return `https://apis.map.qq.com/uri/v1/marker?marker=coord:${formattedLng},${formattedLat};title:${encodeURIComponent(label)}&referer=wechat`; + }; + + // 生成静态地图预览图URL + const generateStaticMapUrl = ( + lat: string, + lng: string, + width: number = 420, + height: number = 200, + ) => { + const formattedLat = formatCoordinate(lat); + const formattedLng = formatCoordinate(lng); + const key = "7DZBZ-ZSRK3-QJN3W-O5VTV-4E2P6-7GFYX"; + const zoom = locationData.scale || "15"; + // 腾讯地图静态地图API + return `https://apis.map.qq.com/ws/staticmap/v2/?center=${formattedLng},${formattedLat}&zoom=${zoom}&size=${width}x${height}&markers=${formattedLng},${formattedLat}&key=${key}`; }; const mapUrl = generateMapUrl( @@ -97,12 +123,18 @@ const LocationMessage: React.FC = ({ content }) => { locationData.label, ); + const staticMapUrl = generateStaticMapUrl( + locationData.y, + locationData.x, + 420, + 200, + ); + // 处理POI信息 - const poiName = locationData.poiname || locationData.label; - const poiCategory = locationData.poiCategoryTips - ? locationData.poiCategoryTips.split(":")[0] - : ""; - const poiPhone = locationData.poiPhone || ""; + // 提取道路名称(如果有的话,从label中提取) + const roadName = + locationData.poiname.split(/[((]/)[0] || locationData.label; + const detailAddress = locationData.label; return (
    @@ -110,29 +142,35 @@ const LocationMessage: React.FC = ({ content }) => { className={styles.locationCard} onClick={() => window.open(mapUrl, "_blank")} > - {/* 位置详情 */} + {/* 地图预览图 */} +
    + {locationData.label} { + // 如果图片加载失败,显示占位符 + const target = e.target as HTMLImageElement; + target.style.display = "none"; + const placeholder = target.nextElementSibling as HTMLElement; + if (placeholder) { + placeholder.style.display = "flex"; + } + }} + /> +
    + 📍 + 地图加载中... +
    +
    + + {/* 位置信息 */}
    - {/* POI名称 */} - {poiName &&
    {poiName}
    } + {/* 道路名称 */} + {roadName &&
    {roadName}
    } {/* 详细地址 */} -
    {locationData.label}
    - - {/* POI分类和电话 */} -
    - {poiCategory && ( -
    - 🏷️ - {poiCategory} -
    - )} - {poiPhone && ( -
    - 📞 - {poiPhone} -
    - )} -
    +
    {detailAddress}
    From c801490c2f5bf2350b1b510e3c3db09542b00fa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B6=85=E7=BA=A7=E8=80=81=E7=99=BD=E5=85=94?= Date: Mon, 24 Nov 2025 15:37:57 +0800 Subject: [PATCH 15/15] =?UTF-8?q?=E5=88=A0=E9=99=A4=E8=85=BE=E8=AE=AF?= =?UTF-8?q?=E5=9C=B0=E5=9B=BE=E5=AE=9A=E4=BD=8D=E6=9C=8D=E5=8A=A1=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E8=AF=B4=E6=98=8E=E6=96=87=E6=A1=A3=EF=BC=8C=E6=9B=B4?= =?UTF-8?q?=E6=96=B0CustomerList=E7=BB=84=E4=BB=B6=E6=A0=B7=E5=BC=8F?= =?UTF-8?q?=E4=BB=A5=E6=94=AF=E6=8C=81=E7=A6=BB=E7=BA=BF=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E7=8A=B6=E6=80=81=EF=BC=8C=E8=B0=83=E6=95=B4MessageList?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=E7=9A=84=E5=8A=A0=E8=BD=BD=E7=8A=B6=E6=80=81?= =?UTF-8?q?=E7=AE=A1=E7=90=86=EF=BC=8C=E5=A2=9E=E5=BC=BAfilter=E5=B7=A5?= =?UTF-8?q?=E5=85=B7=E4=BB=A5=E6=94=AF=E6=8C=81XML=E6=A0=BC=E5=BC=8F?= =?UTF-8?q?=E4=BD=8D=E7=BD=AE=E6=B6=88=E6=81=AF=E7=9A=84=E8=AF=86=E5=88=AB?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/CustomerList/com.module.scss | 5 ++ .../weChat/components/CustomerList/index.tsx | 6 +- .../SidebarMenu/MessageList/index.tsx | 4 +- Touchkebao/src/utils/filter.ts | 5 ++ Touchkebao/消息功能规划.md | 0 Touchkebao/腾讯地图定位服务修复说明.md | 73 ------------------- 6 files changed, 13 insertions(+), 80 deletions(-) create mode 100644 Touchkebao/消息功能规划.md delete mode 100644 Touchkebao/腾讯地图定位服务修复说明.md diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/CustomerList/com.module.scss b/Touchkebao/src/pages/pc/ckbox/weChat/components/CustomerList/com.module.scss index 9cc2941e..29e92bf8 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/components/CustomerList/com.module.scss +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/CustomerList/com.module.scss @@ -47,6 +47,11 @@ .active & { border-color: #1890ff; } + + &.offline { + filter: grayscale(100%); + opacity: 0.6; + } } } .allUser { diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/CustomerList/index.tsx b/Touchkebao/src/pages/pc/ckbox/weChat/components/CustomerList/index.tsx index 9ce88a1f..55ba297f 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/components/CustomerList/index.tsx +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/CustomerList/index.tsx @@ -89,7 +89,6 @@ const CustomerList: React.FC = () => { >
    全部
    -
    {customerList.map(customer => (
    { { {!customer.avatar && customer.name.charAt(0)} -
    ))} diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/MessageList/index.tsx b/Touchkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/MessageList/index.tsx index 2625b581..0e39a94b 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/MessageList/index.tsx +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/MessageList/index.tsx @@ -383,7 +383,7 @@ const MessageList: React.FC = () => { const requestId = ++loadRequestRef.current; const initializeSessions = async () => { - setLoading(true); + // setLoading(true); try { const cachedSessions = @@ -416,7 +416,7 @@ const MessageList: React.FC = () => { } } finally { if (!isCancelled && loadRequestRef.current === requestId) { - setLoading(false); + // setLoading(false); } } }; diff --git a/Touchkebao/src/utils/filter.ts b/Touchkebao/src/utils/filter.ts index 5e2d3754..22887a8b 100644 --- a/Touchkebao/src/utils/filter.ts +++ b/Touchkebao/src/utils/filter.ts @@ -58,6 +58,11 @@ export const messageFilter = (message: string) => { return "[图片]"; } + // XML 格式的位置消息:包含 ]/i.test(message)) { + return "[位置]"; + } + // 其他情况直接返回原始消息 return message; } diff --git a/Touchkebao/消息功能规划.md b/Touchkebao/消息功能规划.md new file mode 100644 index 00000000..e69de29b diff --git a/Touchkebao/腾讯地图定位服务修复说明.md b/Touchkebao/腾讯地图定位服务修复说明.md deleted file mode 100644 index 601db72f..00000000 --- a/Touchkebao/腾讯地图定位服务修复说明.md +++ /dev/null @@ -1,73 +0,0 @@ -# 腾讯地图定位服务修复说明 - -## 问题描述 - -在 `selectMap.tsx` 文件中使用腾讯地图定位服务时出现以下错误: - -``` -TypeError: window.TMap.service.Location is not a constructor - at selectMap.tsx:121:33 -``` - -## 原因分析 - -错误原因是尝试将 `TMap.service.Location` 作为构造函数使用,但在腾讯地图 GL API 中,定位服务不是通过构造函数方式创建的。 - -## 修复方法 - -### 1. 修改定位服务的初始化方式 - -将原来的代码: - -```typescript -// 创建IP定位服务 -window.geolocationRef = new window.TMap.service.Location({ - timeout: 10000, - convert: true, -}); -``` - -修改为: - -```typescript -// 使用腾讯地图内置的定位服务 -window.geolocationRef = window.TMap.service.Geolocation; -``` - -### 2. 修改定位服务的调用方式 - -在调用定位服务时,将配置参数直接传入 `getLocation` 方法: - -```typescript -window.geolocationRef.getLocation({ - timeout: 10000, - convert: true, - success: function (result: any) { - // 处理成功回调 - }, - error: function () { - // 处理错误回调 - }, -}); -``` - -## 技术说明 - -1. **腾讯地图 GL API 中的定位服务**: - - 正确的服务名称是 `TMap.service.Geolocation`,而非 `TMap.service.Location` - - 它是一个对象,不需要使用 `new` 关键字实例化 - - 配置参数应该直接传递给 `getLocation` 方法 - -2. **定位服务参数**: - - `timeout`:定位超时时间,单位毫秒 - - `convert`:是否将坐标转换为腾讯地图坐标系 - -3. **回调处理**: - - `success`:定位成功回调函数,返回位置信息 - - `error`:定位失败回调函数 - -## 注意事项 - -1. 确保腾讯地图 SDK 已正确加载 -2. 确保 API 密钥有定位服务的权限 -3. 定位精度可能受网络环境影响