diff --git a/components/common/MiniMap.vue b/components/common/MiniMap.vue
new file mode 100644
index 0000000..cf65fe5
--- /dev/null
+++ b/components/common/MiniMap.vue
@@ -0,0 +1,454 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ node.name }}
+
+
+ 🔒
+
+
+ {{ getDistance(node) }}
+
+
+
+
+
+
+
+ 当前位置
+
+
+
+ 安全
+
+
+
+ 危险
+
+
+
+ 副本
+
+
+
+
+
+ 前往路径: {{ selectedPath.map(id => getLocationName(id)).join(' → ') }}
+
+
+
+
+
+
+
diff --git a/components/panels/MapPanel.vue b/components/panels/MapPanel.vue
index 7416ebe..fd134ce 100644
--- a/components/panels/MapPanel.vue
+++ b/components/panels/MapPanel.vue
@@ -14,6 +14,11 @@
{{ currentLocation.description }}
+
+
+
+
+
⚔️ 战斗中
@@ -123,6 +128,7 @@ import { triggerExploreEvent, tryFlee } from '@/utils/eventSystem.js'
import TextButton from '@/components/common/TextButton.vue'
import ProgressBar from '@/components/common/ProgressBar.vue'
import FilterTabs from '@/components/common/FilterTabs.vue'
+import MiniMap from '@/components/common/MiniMap.vue'
const game = useGameStore()
const player = usePlayerStore()
@@ -424,6 +430,10 @@ function toggleAutoCombat() {
margin-bottom: 16rpx;
}
+ &__minimap {
+ margin-bottom: 16rpx;
+ }
+
&__locations-header {
padding: 12rpx 16rpx 8rpx;
}
diff --git a/utils/mapLayout.js b/utils/mapLayout.js
new file mode 100644
index 0000000..df4ab23
--- /dev/null
+++ b/utils/mapLayout.js
@@ -0,0 +1,254 @@
+/**
+ * 地图布局系统 - 根据区域连接生成可视化地图
+ */
+
+import { LOCATION_CONFIG } from '@/config/locations.js'
+
+/**
+ * 构建地图图形结构
+ * @returns {Object} 地图数据
+ */
+export function buildMapGraph() {
+ const nodes = []
+ const edges = []
+
+ // 首先创建所有节点
+ for (const [id, config] of Object.entries(LOCATION_CONFIG)) {
+ nodes.push({
+ id,
+ name: config.name,
+ type: config.type,
+ x: 0,
+ y: 0
+ })
+ }
+
+ // 创建连接边
+ for (const [id, config] of Object.entries(LOCATION_CONFIG)) {
+ if (config.connections) {
+ for (const connId of config.connections) {
+ // 避免重复边
+ const edgeExists = edges.some(e =>
+ (e.from === id && e.to === connId) ||
+ (e.from === connId && e.to === id)
+ )
+ if (!edgeExists) {
+ edges.push({ from: id, to: connId })
+ }
+ }
+ }
+ }
+
+ return { nodes, edges }
+}
+
+/**
+ * 简单的力导向布局算法
+ * 将节点分布在合理的坐标上
+ * @param {Object} graph - 地图图形结构
+ * @param {Object} options - 布局选项
+ * @returns {Object} 带有坐标的节点
+ */
+export function layoutMap(graph, options = {}) {
+ const { nodes, edges } = graph
+ const { width = 300, height = 400 } = options
+
+ // 创建邻接表
+ const adj = {}
+ for (const node of nodes) {
+ adj[node.id] = []
+ }
+ for (const edge of edges) {
+ adj[edge.from].push(edge.to)
+ adj[edge.to].push(edge.from)
+ }
+
+ // 找到中心节点(连接最多的节点作为起点)
+ let centerNode = nodes[0]
+ for (const node of nodes) {
+ if (adj[node.id].length > adj[centerNode.id].length) {
+ centerNode = node
+ }
+ }
+
+ // BFS 分层布局
+ const levels = {}
+ const visited = new Set()
+ const queue = [{ id: centerNode.id, level: 0 }]
+
+ while (queue.length > 0) {
+ const { id, level } = queue.shift()
+ if (visited.has(id)) continue
+ visited.add(id)
+
+ if (!levels[level]) levels[level] = []
+ levels[level].push(id)
+
+ for (const neighbor of adj[id]) {
+ if (!visited.has(neighbor)) {
+ queue.push({ id: neighbor, level: level + 1 })
+ }
+ }
+ }
+
+ // 计算节点位置
+ const nodePositions = {}
+ const levelHeight = height / (Object.keys(levels).length + 1)
+
+ for (const [level, nodeIds] of Object.entries(levels)) {
+ const y = (parseInt(level) + 1) * levelHeight
+ const levelWidth = width / (nodeIds.length + 1)
+
+ nodeIds.forEach((nodeId, index) => {
+ nodePositions[nodeId] = {
+ x: (index + 1) * levelWidth,
+ y
+ }
+ })
+ }
+
+ // 更新节点坐标
+ return nodes.map(node => ({
+ ...node,
+ x: nodePositions[node.id]?.x || width / 2,
+ y: nodePositions[node.id]?.y || height / 2
+ }))
+}
+
+/**
+ * 获取从当前位置可达的区域(包括多步可达)
+ * @param {string} currentLocation - 当前位置ID
+ * @param {number} maxDistance - 最大距离(步数)
+ * @returns {Object} 可达区域信息,key为locationId,value为距离
+ */
+export function getReachableLocations(currentLocation, maxDistance = 10) {
+ const distances = {}
+ const queue = [{ id: currentLocation, dist: 0 }]
+
+ distances[currentLocation] = 0
+
+ while (queue.length > 0) {
+ const { id, dist } = queue.shift()
+
+ if (dist >= maxDistance) continue
+
+ // 找到所有连接的节点
+ const connections = LOCATION_CONFIG[id]?.connections || []
+ for (const connId of connections) {
+ if (distances[connId] === undefined) {
+ distances[connId] = dist + 1
+ queue.push({ id: connId, dist: dist + 1 })
+ }
+ }
+ }
+
+ return distances
+}
+
+/**
+ * 获取地图的路径信息
+ * @param {string} from - 起点
+ * @param {string} to - 终点
+ * @returns {Array} 路径数组
+ */
+export function getPath(from, to) {
+ if (from === to) return [from]
+
+ const { edges } = buildMapGraph()
+ const adj = {}
+ for (const node of Object.keys(LOCATION_CONFIG)) {
+ adj[node] = LOCATION_CONFIG[node]?.connections || []
+ }
+
+ // BFS 寻找最短路径
+ const queue = [[from]]
+ const visited = new Set([from])
+
+ while (queue.length > 0) {
+ const path = queue.shift()
+ const current = path[path.length - 1]
+
+ if (current === to) {
+ return path
+ }
+
+ for (const neighbor of adj[current]) {
+ if (!visited.has(neighbor)) {
+ visited.add(neighbor)
+ queue.push([...path, neighbor])
+ }
+ }
+ }
+
+ return null // 无路径
+}
+
+/**
+ * 预定义的地图布局配置
+ * 为每个区域指定固定的显示位置
+ */
+export const PREDEFINED_LAYOUT = {
+ // 营地为中心
+ camp: { x: 150, y: 200 },
+ // 营地周围
+ market: { x: 50, y: 120 },
+ blackmarket: { x: 250, y: 120 },
+ wild1: { x: 150, y: 320 },
+ // 更远的地方
+ boss_lair: { x: 150, y: 440 },
+ basement: { x: 150, y: 540 }
+}
+
+/**
+ * 使用预定义布局获取节点位置
+ * @param {Object} graph - 地图图形结构
+ * @returns {Array} 带有坐标的节点
+ */
+export function getPredefinedLayout(graph) {
+ const { nodes } = graph
+
+ return nodes.map(node => ({
+ ...node,
+ x: PREDEFINED_LAYOUT[node.id]?.x || 150,
+ y: PREDEFINED_LAYOUT[node.id]?.y || 200
+ }))
+}
+
+/**
+ * 获取可视化的地图数据
+ * @param {string} currentLocation - 当前位置
+ * @returns {Object} 地图数据
+ */
+export function getMapData(currentLocation) {
+ const graph = buildMapGraph()
+ const layoutNodes = getPredefinedLayout(graph)
+
+ // 计算画布大小
+ let maxX = 0, maxY = 0
+ for (const node of layoutNodes) {
+ maxX = Math.max(maxX, node.x)
+ maxY = Math.max(maxY, node.y)
+ }
+
+ // 获取可达距离
+ const reachable = getReachableLocations(currentLocation, 10)
+
+ // 计算到每个区域的路径
+ const paths = {}
+ for (const node of layoutNodes) {
+ const path = getPath(currentLocation, node.id)
+ if (path) {
+ paths[node.id] = path
+ }
+ }
+
+ return {
+ nodes: layoutNodes,
+ edges: graph.edges,
+ width: maxX + 50,
+ height: maxY + 50,
+ currentLocation,
+ reachable,
+ paths
+ }
+}