|
|
@@ -3,8 +3,10 @@
|
|
|
<head>
|
|
|
<meta charset="UTF-8">
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
- <title>SVG Path 平滑曲线实现</title>
|
|
|
- <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
|
|
|
+ <title>Vue2 高德地图地区查询与绘制</title>
|
|
|
+ <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.14/vue.min.js"></script>
|
|
|
+ <script src="https://webapi.amap.com/maps?v=2.0&key=b96a1c32b0bb828f1d153b219fa59ecc&plugin=AMap.ElasticMarker,AMap.PolygonEditor,AMap.DistrictSearch,AMap.ToolBar, AMap.Scale, AMap.MouseTool, AMap.Geocoder,AMap.Zoom"></script>
|
|
|
+
|
|
|
<style>
|
|
|
* {
|
|
|
margin: 0;
|
|
|
@@ -14,424 +16,524 @@
|
|
|
}
|
|
|
|
|
|
body {
|
|
|
- background: linear-gradient(135deg, #1a2a6c, #0d1b36);
|
|
|
- min-height: 100vh;
|
|
|
- display: flex;
|
|
|
- justify-content: center;
|
|
|
- align-items: center;
|
|
|
- padding: 20px;
|
|
|
- color: #fff;
|
|
|
+ background-color: #f5f7fa;
|
|
|
+ color: #333;
|
|
|
+ line-height: 1.6;
|
|
|
}
|
|
|
|
|
|
.container {
|
|
|
- width: 100%;
|
|
|
- max-width: 900px;
|
|
|
- background: rgba(13, 27, 54, 0.8);
|
|
|
- border-radius: 20px;
|
|
|
- padding: 30px;
|
|
|
- box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
|
|
|
- border: 1px solid rgba(76, 201, 240, 0.3);
|
|
|
+ max-width: 1200px;
|
|
|
+ margin: 0 auto;
|
|
|
+ padding: 20px;
|
|
|
}
|
|
|
|
|
|
- h1 {
|
|
|
+ header {
|
|
|
text-align: center;
|
|
|
- color: #4cc9f0;
|
|
|
margin-bottom: 30px;
|
|
|
- text-shadow: 0 0 10px rgba(76, 201, 240, 0.5);
|
|
|
+ padding: 20px;
|
|
|
+ background: linear-gradient(135deg, #1e5799 0%, #207cca 100%);
|
|
|
+ color: white;
|
|
|
+ border-radius: 10px;
|
|
|
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
|
|
}
|
|
|
|
|
|
- .chart-container {
|
|
|
- width: 100%;
|
|
|
- height: 400px;
|
|
|
- background: rgba(30, 45, 80, 0.5);
|
|
|
- border-radius: 10px;
|
|
|
- padding: 20px;
|
|
|
- margin-bottom: 30px;
|
|
|
- position: relative;
|
|
|
+ h1 {
|
|
|
+ font-size: 2.2rem;
|
|
|
+ margin-bottom: 10px;
|
|
|
}
|
|
|
|
|
|
- .chart {
|
|
|
- width: 100%;
|
|
|
- height: 100%;
|
|
|
+ .subtitle {
|
|
|
+ font-size: 1.1rem;
|
|
|
+ opacity: 0.9;
|
|
|
}
|
|
|
|
|
|
- .controls {
|
|
|
+ .app-container {
|
|
|
display: flex;
|
|
|
- justify-content: center;
|
|
|
- gap: 15px;
|
|
|
- margin-bottom: 20px;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 20px;
|
|
|
}
|
|
|
|
|
|
- .control-btn {
|
|
|
- background: rgba(76, 201, 240, 0.3);
|
|
|
- border: none;
|
|
|
- padding: 10px 20px;
|
|
|
- border-radius: 5px;
|
|
|
- color: white;
|
|
|
- font-weight: bold;
|
|
|
- cursor: pointer;
|
|
|
- transition: all 0.3s;
|
|
|
+ .search-panel {
|
|
|
+ background: white;
|
|
|
+ padding: 20px;
|
|
|
+ border-radius: 10px;
|
|
|
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
|
|
|
}
|
|
|
|
|
|
- .control-btn:hover {
|
|
|
- background: rgba(76, 201, 240, 0.5);
|
|
|
+ .search-box {
|
|
|
+ display: flex;
|
|
|
+ gap: 10px;
|
|
|
+ margin-bottom: 15px;
|
|
|
}
|
|
|
|
|
|
- .control-btn.active {
|
|
|
- background: rgba(76, 201, 240, 0.7);
|
|
|
- box-shadow: 0 0 10px rgba(76, 201, 240, 0.5);
|
|
|
+ .search-input {
|
|
|
+ flex: 1;
|
|
|
+ padding: 12px 15px;
|
|
|
+ border: 1px solid #ddd;
|
|
|
+ border-radius: 6px;
|
|
|
+ font-size: 16px;
|
|
|
+ transition: border-color 0.3s;
|
|
|
}
|
|
|
|
|
|
- .code-section {
|
|
|
- background: rgba(0, 0, 0, 0.3);
|
|
|
- border-radius: 10px;
|
|
|
- padding: 20px;
|
|
|
- margin-top: 30px;
|
|
|
+ .search-input:focus {
|
|
|
+ outline: none;
|
|
|
+ border-color: #1e5799;
|
|
|
+ box-shadow: 0 0 0 2px rgba(30, 87, 153, 0.2);
|
|
|
}
|
|
|
|
|
|
- .code-title {
|
|
|
- color: #a5b4fc;
|
|
|
- margin-bottom: 15px;
|
|
|
+ .search-btn, .draw-btn, .clear-btn, .edit-btn {
|
|
|
+ padding: 12px 20px;
|
|
|
+ background: #1e5799;
|
|
|
+ color: white;
|
|
|
+ border: none;
|
|
|
+ border-radius: 6px;
|
|
|
+ cursor: pointer;
|
|
|
+ font-size: 16px;
|
|
|
+ transition: background 0.3s;
|
|
|
}
|
|
|
|
|
|
- .code-block {
|
|
|
- background: #1e1e1e;
|
|
|
- border-radius: 8px;
|
|
|
- padding: 15px;
|
|
|
- font-family: 'Courier New', monospace;
|
|
|
- overflow-x: auto;
|
|
|
- line-height: 1.5;
|
|
|
+ .search-btn:hover, .draw-btn:hover, .edit-btn:hover {
|
|
|
+ background: #16457a;
|
|
|
}
|
|
|
|
|
|
- .comment {
|
|
|
- color: #6a9955;
|
|
|
+ .clear-btn {
|
|
|
+ background: #e74c3c;
|
|
|
}
|
|
|
|
|
|
- .property {
|
|
|
- color: #9cdcfe;
|
|
|
+ .clear-btn:hover {
|
|
|
+ background: #c0392b;
|
|
|
}
|
|
|
|
|
|
- .value {
|
|
|
- color: #ce9178;
|
|
|
+ .edit-btn.active {
|
|
|
+ background: #27ae60;
|
|
|
}
|
|
|
|
|
|
- .explanation {
|
|
|
- background: rgba(30, 30, 60, 0.5);
|
|
|
- padding: 15px;
|
|
|
- border-radius: 8px;
|
|
|
- margin-top: 20px;
|
|
|
- border-left: 4px solid #4cc9f0;
|
|
|
+ .map-container {
|
|
|
+ height: 500px;
|
|
|
+ border-radius: 10px;
|
|
|
+ overflow: hidden;
|
|
|
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
|
|
}
|
|
|
|
|
|
- .explanation h3 {
|
|
|
- color: #a5b4fc;
|
|
|
- margin-bottom: 10px;
|
|
|
+ .controls {
|
|
|
+ display: flex;
|
|
|
+ gap: 10px;
|
|
|
+ margin-top: 15px;
|
|
|
+ flex-wrap: wrap;
|
|
|
}
|
|
|
|
|
|
- .point {
|
|
|
- fill: #4cc9f0;
|
|
|
- stroke: #fff;
|
|
|
- stroke-width: 2;
|
|
|
+ .results-panel {
|
|
|
+ background: white;
|
|
|
+ padding: 20px;
|
|
|
+ border-radius: 10px;
|
|
|
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
|
|
|
+ max-height: 200px;
|
|
|
+ overflow-y: auto;
|
|
|
+ }
|
|
|
+
|
|
|
+ .result-item {
|
|
|
+ padding: 10px 15px;
|
|
|
+ border-bottom: 1px solid #eee;
|
|
|
cursor: pointer;
|
|
|
- transition: all 0.3s;
|
|
|
+ transition: background 0.2s;
|
|
|
}
|
|
|
|
|
|
- .point:hover {
|
|
|
- r: 8;
|
|
|
- fill: #ff6b6b;
|
|
|
+ .result-item:hover {
|
|
|
+ background: #f0f7ff;
|
|
|
}
|
|
|
|
|
|
- .point-label {
|
|
|
- font-size: 0.8rem;
|
|
|
- fill: #fff;
|
|
|
- text-anchor: middle;
|
|
|
- font-weight: bold;
|
|
|
+ .result-item:last-child {
|
|
|
+ border-bottom: none;
|
|
|
}
|
|
|
|
|
|
- .grid-line {
|
|
|
- stroke: rgba(255, 255, 255, 0.1);
|
|
|
- stroke-width: 1;
|
|
|
+ .instructions {
|
|
|
+ background: white;
|
|
|
+ padding: 20px;
|
|
|
+ border-radius: 10px;
|
|
|
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
|
|
|
+ margin-top: 20px;
|
|
|
}
|
|
|
|
|
|
- .axis-label {
|
|
|
- font-size: 0.8rem;
|
|
|
- fill: #a0c8ff;
|
|
|
+ .instructions h3 {
|
|
|
+ color: #1e5799;
|
|
|
+ margin-bottom: 10px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .instructions ul {
|
|
|
+ padding-left: 20px;
|
|
|
}
|
|
|
|
|
|
- .curve-high {
|
|
|
- stroke: #ff6b6b;
|
|
|
- stroke-width: 3;
|
|
|
- fill: none;
|
|
|
+ .instructions li {
|
|
|
+ margin-bottom: 8px;
|
|
|
}
|
|
|
|
|
|
- .curve-low {
|
|
|
- stroke: #4facfe;
|
|
|
- stroke-width: 3;
|
|
|
- fill: none;
|
|
|
+ .status {
|
|
|
+ margin-top: 10px;
|
|
|
+ padding: 10px;
|
|
|
+ border-radius: 6px;
|
|
|
+ background: #e8f4fd;
|
|
|
+ color: #1e5799;
|
|
|
}
|
|
|
|
|
|
- .curve-area {
|
|
|
- fill: url(#gradient);
|
|
|
- opacity: 0.3;
|
|
|
+ .drawing-status {
|
|
|
+ background: #fff8e1;
|
|
|
+ color: #ff9800;
|
|
|
}
|
|
|
|
|
|
- .chart-title {
|
|
|
- font-size: 1.2rem;
|
|
|
- fill: #a5b4fc;
|
|
|
- text-anchor: middle;
|
|
|
+ .editing-status {
|
|
|
+ background: #e8f6f3;
|
|
|
+ color: #27ae60;
|
|
|
+ }
|
|
|
+
|
|
|
+ @media (min-width: 768px) {
|
|
|
+ .app-container {
|
|
|
+ flex-direction: row;
|
|
|
+ }
|
|
|
+
|
|
|
+ .search-panel {
|
|
|
+ width: 300px;
|
|
|
+ flex-shrink: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .map-container {
|
|
|
+ flex: 1;
|
|
|
+ }
|
|
|
}
|
|
|
</style>
|
|
|
</head>
|
|
|
<body>
|
|
|
- <div id="app">
|
|
|
- <div class="container">
|
|
|
- <h1>SVG Path 平滑曲线实现</h1>
|
|
|
-
|
|
|
- <div class="controls">
|
|
|
- <button class="control-btn" :class="{active: curveType === 'linear'}" @click="curveType = 'linear'">线性曲线</button>
|
|
|
- <button class="control-btn" :class="{active: curveType === 'quadratic'}" @click="curveType = 'quadratic'">二次贝塞尔曲线</button>
|
|
|
- <button class="control-btn" :class="{active: curveType === 'cubic'}" @click="curveType = 'cubic'">三次贝塞尔曲线</button>
|
|
|
- <button class="control-btn" :class="{active: curveType === 'catmull'}" @click="curveType = 'catmull'">Catmull-Rom曲线</button>
|
|
|
- </div>
|
|
|
+ <div id="app" class="container">
|
|
|
+ <header>
|
|
|
+ <h1>Vue2 高德地图地区查询与绘制</h1>
|
|
|
+ <p class="subtitle">搜索地区并在高德地图上绘制区域边界</p>
|
|
|
+ </header>
|
|
|
+
|
|
|
+ <div class="app-container">
|
|
|
+ <div class="search-panel">
|
|
|
+ <h3>地区搜索</h3>
|
|
|
+ <div class="search-box">
|
|
|
+ <input
|
|
|
+ type="text"
|
|
|
+ class="search-input"
|
|
|
+ placeholder="输入地区名称,如:北京市朝阳区"
|
|
|
+ v-model="searchKeyword"
|
|
|
+ @keyup.enter="searchPlace"
|
|
|
+ >
|
|
|
+ <button class="search-btn" @click="searchPlace">搜索</button>
|
|
|
+ </div>
|
|
|
|
|
|
- <div class="chart-container">
|
|
|
- <svg class="chart" viewBox="0 0 800 400" preserveAspectRatio="xMidYMid meet">
|
|
|
- <!-- 定义渐变 -->
|
|
|
- <defs>
|
|
|
- <linearGradient id="gradient" x1="0%" y1="0%" x2="0%" y2="100%">
|
|
|
- <stop offset="0%" stop-color="#ff6b6b" />
|
|
|
- <stop offset="100%" stop-color="#4facfe" />
|
|
|
- </linearGradient>
|
|
|
- </defs>
|
|
|
-
|
|
|
- <!-- 网格线 -->
|
|
|
- <g v-for="i in 5" :key="'grid-'+i">
|
|
|
- <line class="grid-line"
|
|
|
- :x1="0" :y1="i * 80"
|
|
|
- :x2="800" :y2="i * 80" />
|
|
|
- </g>
|
|
|
-
|
|
|
- <!-- X轴标签 -->
|
|
|
- <g v-for="(day, index) in forecast" :key="'label-'+index">
|
|
|
- <text class="axis-label"
|
|
|
- :x="getXPosition(index)"
|
|
|
- :y="390"
|
|
|
- text-anchor="middle">
|
|
|
- {{ day.day }}
|
|
|
- </text>
|
|
|
- </g>
|
|
|
-
|
|
|
- <!-- Y轴标签 -->
|
|
|
- <g v-for="i in 5" :key="'y-label-'+i">
|
|
|
- <text class="axis-label" x="20" :y="i * 80" text-anchor="end">
|
|
|
- {{ 40 - i * 5 }}℃
|
|
|
- </text>
|
|
|
- </g>
|
|
|
-
|
|
|
- <!-- 曲线区域填充 -->
|
|
|
- <path class="curve-area" :d="areaPath" />
|
|
|
-
|
|
|
- <!-- 高温曲线 -->
|
|
|
- <path class="curve-high" :d="highCurvePath" />
|
|
|
-
|
|
|
- <!-- 低温曲线 -->
|
|
|
- <path class="curve-low" :d="lowCurvePath" />
|
|
|
-
|
|
|
- <!-- 数据点 -->
|
|
|
- <g v-for="(day, index) in forecast" :key="'point-'+index">
|
|
|
- <circle class="point"
|
|
|
- :cx="getXPosition(index)"
|
|
|
- :cy="getTemperatureY(parseInt(day.highTemp))"
|
|
|
- r="5" />
|
|
|
- <circle class="point"
|
|
|
- :cx="getXPosition(index)"
|
|
|
- :cy="getTemperatureY(parseInt(day.lowTemp))"
|
|
|
- r="5" />
|
|
|
-
|
|
|
- <!-- 数值标签 -->
|
|
|
- <text class="point-label"
|
|
|
- :x="getXPosition(index)"
|
|
|
- :y="getTemperatureY(parseInt(day.highTemp)) - 15">
|
|
|
- {{ day.highTemp }}
|
|
|
- </text>
|
|
|
- <text class="point-label"
|
|
|
- :x="getXPosition(index)"
|
|
|
- :y="getTemperatureY(parseInt(day.lowTemp)) + 20">
|
|
|
- {{ day.lowTemp }}
|
|
|
- </text>
|
|
|
- </g>
|
|
|
-
|
|
|
- <!-- 图表标题 -->
|
|
|
- <text class="chart-title" x="400" y="30">7天温度变化曲线</text>
|
|
|
- </svg>
|
|
|
- </div>
|
|
|
+ <div class="controls">
|
|
|
+ <button class="draw-btn" @click="toggleDrawingMode">
|
|
|
+ {{ drawingMode ? '退出绘制模式' : '进入绘制模式' }}
|
|
|
+ </button>
|
|
|
+ <button class="edit-btn" :class="{active: editingMode}" @click="toggleEditingMode">
|
|
|
+ {{ editingMode ? '退出编辑模式' : '进入编辑模式' }}
|
|
|
+ </button>
|
|
|
+ <button class="clear-btn" @click="clearMap">清除地图</button>
|
|
|
+ </div>
|
|
|
|
|
|
- <div class="code-section">
|
|
|
- <h3 class="code-title">当前曲线代码</h3>
|
|
|
- <div class="code-block">
|
|
|
- <span class="comment">// {{ curveType }} 曲线路径</span><br>
|
|
|
- <span class="property">高温曲线</span>: <span class="value">{{ highCurvePath }}</span><br><br>
|
|
|
- <span class="property">低温曲线</span>: <span class="value">{{ lowCurvePath }}</span>
|
|
|
+ <div class="status" :class="statusClass">
|
|
|
+ {{ statusMessage }}
|
|
|
</div>
|
|
|
|
|
|
- <div class="explanation">
|
|
|
- <h3>曲线类型说明</h3>
|
|
|
- <p><strong>线性曲线</strong>:使用直线连接各点,简单但不够平滑。</p>
|
|
|
- <p><strong>二次贝塞尔曲线</strong>:使用单个控制点创建平滑曲线。</p>
|
|
|
- <p><strong>三次贝塞尔曲线</strong>:使用两个控制点创建更平滑的曲线。</p>
|
|
|
- <p><strong>Catmull-Rom曲线</strong>:通过所有点的曲线,特别适合数据可视化。</p>
|
|
|
+ <div class="results-panel" v-if="searchResults.length > 0">
|
|
|
+ <h4>搜索结果 ({{ searchResults.length }})</h4>
|
|
|
+ <div
|
|
|
+ class="result-item"
|
|
|
+ v-for="(result, index) in searchResults"
|
|
|
+ :key="index"
|
|
|
+ @click="selectResult(result)"
|
|
|
+ >
|
|
|
+ <strong>{{ result.name }}</strong><br>
|
|
|
+ <small>{{ result.district }}{{ result.address }}</small>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
+
|
|
|
+ <div class="map-container" id="map-container"></div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
|
|
|
+ <div class="instructions">
|
|
|
+ <h3>使用说明</h3>
|
|
|
+ <ul>
|
|
|
+ <li>在搜索框中输入地区名称(如"北京市朝阳区")并点击搜索或按Enter键</li>
|
|
|
+ <li>点击搜索结果项可在地图上定位到该地区</li>
|
|
|
+ <li>点击"进入绘制模式"可手动在地图上绘制多边形区域</li>
|
|
|
+ <li>绘制完成后,点击第一个点或双击可完成绘制</li>
|
|
|
+ <li>点击"进入编辑模式"可选择并编辑已绘制的区域</li>
|
|
|
+ <li>点击"清除地图"可移除所有标记和绘制区域</li>
|
|
|
+ </ul>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <script type="text/javascript">
|
|
|
+ window._AMapSecurityConfig = {
|
|
|
+ serviceHost: 'http://47.110.79.22:9000/_AMapService',
|
|
|
+ }
|
|
|
+ </script>
|
|
|
<script>
|
|
|
+ // 请替换为您的高德地图API密钥
|
|
|
+ const AMAP_KEY = 'b96a1c32b0bb828f1d153b219fa59ecc';
|
|
|
+
|
|
|
new Vue({
|
|
|
el: '#app',
|
|
|
data: {
|
|
|
- curveType: 'catmull',
|
|
|
- forecast: [
|
|
|
- { day: '周三', highTemp: '32', lowTemp: '21' },
|
|
|
- { day: '周四', highTemp: '28', lowTemp: '24' },
|
|
|
- { day: '周五', highTemp: '36', lowTemp: '22' },
|
|
|
- { day: '周六', highTemp: '30', lowTemp: '23' },
|
|
|
- { day: '周日', highTemp: '31', lowTemp: '25' },
|
|
|
- { day: '周一', highTemp: '29', lowTemp: '20' },
|
|
|
- { day: '周二', highTemp: '27', lowTemp: '19' }
|
|
|
- ]
|
|
|
+ map: null,
|
|
|
+ searchKeyword: '',
|
|
|
+ searchResults: [],
|
|
|
+ drawingMode: false,
|
|
|
+ editingMode: false,
|
|
|
+ mouseTool: null,
|
|
|
+ polygonEditor: null,
|
|
|
+ polygons: [],
|
|
|
+ markers: [],
|
|
|
+ selectedPolygon: null,
|
|
|
+ statusMessage: '请输入地区名称进行搜索',
|
|
|
+ statusClass: ''
|
|
|
},
|
|
|
computed: {
|
|
|
- highCurvePath() {
|
|
|
- return this.generateCurvePath(this.forecast.map(day => parseInt(day.highTemp)));
|
|
|
+ statusClass() {
|
|
|
+ if (this.drawingMode) return 'drawing-status';
|
|
|
+ if (this.editingMode) return 'editing-status';
|
|
|
+ return '';
|
|
|
+ }
|
|
|
+ },
|
|
|
+ mounted() {
|
|
|
+ this.initMap();
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ initMap() {
|
|
|
+ // 初始化地图
|
|
|
+ this.map = new AMap.Map('map-container', {
|
|
|
+ zoom: 10,
|
|
|
+ center: [116.397428, 39.90923], // 默认中心点(北京)
|
|
|
+ viewMode: '3D'
|
|
|
+ });
|
|
|
+
|
|
|
+ // 添加缩放控件
|
|
|
+ // this.map.addControl(new AMap.Zoom());
|
|
|
+
|
|
|
+ // 添加比例尺控件
|
|
|
+ this.map.addControl(new AMap.Scale());
|
|
|
+
|
|
|
+ // 初始化鼠标工具
|
|
|
+ this.mouseTool = new AMap.MouseTool(this.map);
|
|
|
+
|
|
|
+ // 监听绘制完成事件
|
|
|
+ this.mouseTool.on('draw', (event) => {
|
|
|
+ if (event.obj instanceof AMap.Polygon) {
|
|
|
+ const polygon = event.obj;
|
|
|
+ this.polygons.push(polygon);
|
|
|
+
|
|
|
+ // 添加右键菜单删除功能
|
|
|
+ this.addPolygonContextMenu(polygon);
|
|
|
+
|
|
|
+ this.statusMessage = `已绘制区域,顶点数: ${polygon.getPath().length}`;
|
|
|
+
|
|
|
+ // 自动退出绘制模式
|
|
|
+ this.drawingMode = false;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ this.statusMessage = '地图加载完成,请输入地区名称进行搜索';
|
|
|
},
|
|
|
- lowCurvePath() {
|
|
|
- return this.generateCurvePath(this.forecast.map(day => parseInt(day.lowTemp)));
|
|
|
+
|
|
|
+ searchPlace() {
|
|
|
+ if (!this.searchKeyword.trim()) {
|
|
|
+ this.statusMessage = '请输入搜索关键词';
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ this.statusMessage = '搜索中...';
|
|
|
+
|
|
|
+ // 使用高德地图PlaceSearch插件进行搜索
|
|
|
+ AMap.plugin('AMap.PlaceSearch', () => {
|
|
|
+ const placeSearch = new AMap.PlaceSearch({
|
|
|
+ pageSize: 10,
|
|
|
+ pageIndex: 1,
|
|
|
+ citylimit: false
|
|
|
+ });
|
|
|
+
|
|
|
+ placeSearch.search(this.searchKeyword, (status, result) => {
|
|
|
+ if (status === 'complete' && result.poiList && result.poiList.pois) {
|
|
|
+ this.searchResults = result.poiList.pois;
|
|
|
+ this.statusMessage = `找到 ${this.searchResults.length} 个结果`;
|
|
|
+ } else {
|
|
|
+ this.searchResults = [];
|
|
|
+ this.statusMessage = '未找到相关结果,请尝试其他关键词';
|
|
|
+ }
|
|
|
+ });
|
|
|
+ });
|
|
|
},
|
|
|
- areaPath() {
|
|
|
- const highPoints = this.forecast.map((day, i) => ({
|
|
|
- x: this.getXPosition(i),
|
|
|
- y: this.getTemperatureY(parseInt(day.highTemp))
|
|
|
- }));
|
|
|
|
|
|
- const lowPoints = this.forecast.map((day, i) => ({
|
|
|
- x: this.getXPosition(i),
|
|
|
- y: this.getTemperatureY(parseInt(day.lowTemp))
|
|
|
- })).reverse();
|
|
|
+ selectResult(result) {
|
|
|
+ // 清除之前的标记
|
|
|
+ this.clearMarkers();
|
|
|
|
|
|
- let path = `M ${highPoints[0].x} ${highPoints[0].y}`;
|
|
|
+ // 添加标记
|
|
|
+ const marker = new AMap.Marker({
|
|
|
+ position: [result.location.lng, result.location.lat],
|
|
|
+ title: result.name,
|
|
|
+ map: this.map
|
|
|
+ });
|
|
|
|
|
|
- // 生成高温曲线
|
|
|
- path += this.generateCurveSegment(highPoints);
|
|
|
+ this.markers.push(marker);
|
|
|
|
|
|
- // 连接到低温曲线终点
|
|
|
- path += ` L ${lowPoints[0].x} ${lowPoints[0].y}`;
|
|
|
+ // 移动到标记位置
|
|
|
+ this.map.setZoom(15);
|
|
|
+ this.map.setCenter([result.location.lng, result.location.lat]);
|
|
|
|
|
|
- // 生成低温曲线(反向)
|
|
|
- path += this.generateCurveSegment(lowPoints);
|
|
|
+ this.statusMessage = `已定位到: ${result.name}`;
|
|
|
+ },
|
|
|
|
|
|
- // 闭合路径
|
|
|
- path += ' Z';
|
|
|
+ toggleDrawingMode() {
|
|
|
+ if (this.editingMode) {
|
|
|
+ this.toggleEditingMode(); // 先退出编辑模式
|
|
|
+ }
|
|
|
|
|
|
- return path;
|
|
|
- }
|
|
|
- },
|
|
|
- methods: {
|
|
|
- getXPosition(index) {
|
|
|
- return 100 + index * (600 / (this.forecast.length - 1));
|
|
|
+ this.drawingMode = !this.drawingMode;
|
|
|
+
|
|
|
+ if (this.drawingMode) {
|
|
|
+ // 进入绘制模式
|
|
|
+ this.mouseTool.polygon({
|
|
|
+ strokeColor: '#1e5799',
|
|
|
+ strokeOpacity: 1,
|
|
|
+ strokeWeight: 3,
|
|
|
+ fillColor: '#1e5799',
|
|
|
+ fillOpacity: 0.3,
|
|
|
+ strokeStyle: 'solid'
|
|
|
+ });
|
|
|
+
|
|
|
+ this.statusMessage = '绘制模式:请在地图上点击绘制多边形区域,双击或点击第一个点完成绘制';
|
|
|
+ } else {
|
|
|
+ // 退出绘制模式
|
|
|
+ this.mouseTool.close(true);
|
|
|
+ this.statusMessage = '已退出绘制模式';
|
|
|
+ }
|
|
|
},
|
|
|
|
|
|
- getTemperatureY(temperature) {
|
|
|
- // 温度范围:15-40℃,映射到图表高度
|
|
|
- const minTemp = 15;
|
|
|
- const maxTemp = 40;
|
|
|
- const normalized = (temperature - minTemp) / (maxTemp - minTemp);
|
|
|
- return 350 - (normalized * 300);
|
|
|
+ toggleEditingMode() {
|
|
|
+ if (this.drawingMode) {
|
|
|
+ this.toggleDrawingMode(); // 先退出绘制模式
|
|
|
+ }
|
|
|
+
|
|
|
+ this.editingMode = !this.editingMode;
|
|
|
+
|
|
|
+ if (this.editingMode) {
|
|
|
+ this.statusMessage = '编辑模式:请点击要编辑的多边形区域';
|
|
|
+
|
|
|
+ // 为所有多边形添加点击事件
|
|
|
+ this.polygons.forEach(polygon => {
|
|
|
+ polygon.on('click', () => {
|
|
|
+ this.startEditPolygon(polygon);
|
|
|
+ });
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ this.stopEditPolygon();
|
|
|
+ this.statusMessage = '已退出编辑模式';
|
|
|
+ }
|
|
|
},
|
|
|
|
|
|
- generateCurvePath(values) {
|
|
|
- const points = values.map((value, i) => ({
|
|
|
- x: this.getXPosition(i),
|
|
|
- y: this.getTemperatureY(value)
|
|
|
- }));
|
|
|
+ startEditPolygon(polygon) {
|
|
|
+ // 停止之前的编辑
|
|
|
+ this.stopEditPolygon();
|
|
|
|
|
|
- let path = `M ${points[0].x} ${points[0].y}`;
|
|
|
+ // 开始编辑选中的多边形
|
|
|
+ this.selectedPolygon = polygon;
|
|
|
|
|
|
- path += this.generateCurveSegment(points);
|
|
|
+ // 加载PolyEditor插件
|
|
|
+ AMap.plugin('AMap.PolyEditor', () => {
|
|
|
+ this.polygonEditor = new AMap.PolyEditor(this.map, polygon);
|
|
|
+ this.polygonEditor.open();
|
|
|
|
|
|
- return path;
|
|
|
+ this.statusMessage = '正在编辑多边形,拖动顶点或边进行调整';
|
|
|
+
|
|
|
+ // 监听编辑事件
|
|
|
+ this.polygonEditor.on('adjust', (event) => {
|
|
|
+ this.statusMessage = '正在调整多边形顶点';
|
|
|
+ });
|
|
|
+
|
|
|
+ this.polygonEditor.on('end', (event) => {
|
|
|
+ this.statusMessage = `编辑完成,当前顶点数: ${polygon.getPath().length}`;
|
|
|
+ });
|
|
|
+ });
|
|
|
},
|
|
|
|
|
|
- generateCurveSegment(points) {
|
|
|
- if (points.length < 2) return '';
|
|
|
+ stopEditPolygon() {
|
|
|
+ if (this.polygonEditor) {
|
|
|
+ this.polygonEditor.close();
|
|
|
+ this.polygonEditor = null;
|
|
|
+ }
|
|
|
+ this.selectedPolygon = null;
|
|
|
|
|
|
- let path = '';
|
|
|
+ // 移除多边形的点击事件
|
|
|
+ this.polygons.forEach(polygon => {
|
|
|
+ polygon.off('click');
|
|
|
+ });
|
|
|
+ },
|
|
|
|
|
|
- switch (this.curveType) {
|
|
|
- case 'linear':
|
|
|
- // 线性曲线 - 直接连接各点
|
|
|
- for (let i = 1; i < points.length; i++) {
|
|
|
- path += ` L ${points[i].x} ${points[i].y}`;
|
|
|
- }
|
|
|
- break;
|
|
|
-
|
|
|
- case 'quadratic':
|
|
|
- // 二次贝塞尔曲线
|
|
|
- for (let i = 1; i < points.length; i++) {
|
|
|
- const prev = points[i-1];
|
|
|
- const curr = points[i];
|
|
|
-
|
|
|
- // 控制点为两点中点
|
|
|
- const controlX = (prev.x + curr.x) / 2;
|
|
|
- const controlY = (prev.y + curr.y) / 2;
|
|
|
-
|
|
|
- if (i === 1) {
|
|
|
- path += ` Q ${controlX} ${controlY} ${curr.x} ${curr.y}`;
|
|
|
- } else {
|
|
|
- path += ` T ${curr.x} ${curr.y}`;
|
|
|
- }
|
|
|
- }
|
|
|
- break;
|
|
|
+ addPolygonContextMenu(polygon) {
|
|
|
+ // 添加右键菜单删除功能
|
|
|
+ polygon.on('rightclick', (e) => {
|
|
|
+ // 创建右键菜单
|
|
|
+ const menu = new AMap.ContextMenu();
|
|
|
|
|
|
- case 'cubic':
|
|
|
- // 三次贝塞尔曲线
|
|
|
- for (let i = 1; i < points.length; i++) {
|
|
|
- const prev = points[i-1];
|
|
|
- const curr = points[i];
|
|
|
+ menu.addItem("删除此区域", () => {
|
|
|
+ // 从地图移除
|
|
|
+ polygon.setMap(null);
|
|
|
|
|
|
- // 控制点1为前一点向右偏移
|
|
|
- const control1X = prev.x + (curr.x - prev.x) * 0.3;
|
|
|
- const control1Y = prev.y;
|
|
|
+ // 从数组中移除
|
|
|
+ const index = this.polygons.indexOf(polygon);
|
|
|
+ if (index > -1) {
|
|
|
+ this.polygons.splice(index, 1);
|
|
|
+ }
|
|
|
|
|
|
- // 控制点2为当前点向左偏移
|
|
|
- const control2X = curr.x - (curr.x - prev.x) * 0.3;
|
|
|
- const control2Y = curr.y;
|
|
|
+ this.statusMessage = '已删除选中区域';
|
|
|
|
|
|
- path += ` C ${control1X} ${control1Y}, ${control2X} ${control2Y}, ${curr.x} ${curr.y}`;
|
|
|
+ // 如果正在编辑此多边形,停止编辑
|
|
|
+ if (this.selectedPolygon === polygon) {
|
|
|
+ this.stopEditPolygon();
|
|
|
+ this.editingMode = false;
|
|
|
}
|
|
|
- break;
|
|
|
+ }, 0);
|
|
|
|
|
|
- case 'catmull':
|
|
|
- default:
|
|
|
- // Catmull-Rom样条曲线
|
|
|
- for (let i = 1; i < points.length; i++) {
|
|
|
- const p0 = i > 1 ? points[i-2] : points[i-1];
|
|
|
- const p1 = points[i-1];
|
|
|
- const p2 = points[i];
|
|
|
- const p3 = i < points.length - 1 ? points[i+1] : points[i];
|
|
|
+ // 在点击位置打开菜单
|
|
|
+ menu.open(this.map, e.lnglat);
|
|
|
+ });
|
|
|
+ },
|
|
|
|
|
|
- // 计算控制点
|
|
|
- const control1X = p1.x + (p2.x - p0.x) / 6;
|
|
|
- const control1Y = p1.y + (p2.y - p0.y) / 6;
|
|
|
+ clearMap() {
|
|
|
+ // 清除所有标记
|
|
|
+ this.clearMarkers();
|
|
|
|
|
|
- const control2X = p2.x - (p3.x - p1.x) / 6;
|
|
|
- const control2Y = p2.y - (p3.y - p1.y) / 6;
|
|
|
+ // 清除所有多边形
|
|
|
+ this.clearPolygons();
|
|
|
|
|
|
- path += ` C ${control1X} ${control1Y}, ${control2X} ${control2Y}, ${p2.x} ${p2.y}`;
|
|
|
- }
|
|
|
- break;
|
|
|
+ // 退出绘制和编辑模式
|
|
|
+ if (this.drawingMode) {
|
|
|
+ this.drawingMode = false;
|
|
|
+ this.mouseTool.close(true);
|
|
|
}
|
|
|
|
|
|
- return path;
|
|
|
+ if (this.editingMode) {
|
|
|
+ this.editingMode = false;
|
|
|
+ this.stopEditPolygon();
|
|
|
+ }
|
|
|
+
|
|
|
+ this.statusMessage = '地图已清除';
|
|
|
+ },
|
|
|
+
|
|
|
+ clearMarkers() {
|
|
|
+ this.markers.forEach(marker => {
|
|
|
+ marker.setMap(null);
|
|
|
+ });
|
|
|
+ this.markers = [];
|
|
|
+ },
|
|
|
+
|
|
|
+ clearPolygons() {
|
|
|
+ this.polygons.forEach(polygon => {
|
|
|
+ polygon.setMap(null);
|
|
|
+ });
|
|
|
+ this.polygons = [];
|
|
|
}
|
|
|
}
|
|
|
});
|