
阿里妹導讀
本文記錄了一次從靈光一現到快速落地的 AI + 地圖服務實踐,透過結合 Cursor 與高德 MCP 地圖服務平臺,作者僅用幾個小時就實現了一個可互動、可篩選、可推薦的杭州美食地圖應用。
最近刷到一篇熱帖,一位在阿里五年的同學總結了一份她這五年(吃的每一口記錄)收集的杭州美食排行榜,為“美食荒漠”正名。也讓我回憶起自己初到杭州進入團隊時也曾想整理一份“美食地圖”,卻因種種原因而作罷。恰逢最近團隊準備選團建的餐廳,於是立馬把這篇帖子分享給投食官。投食官仔細調研後感慨道,“這篇文章真是太好了,如果能在地圖上看到它們的位置就更好了。”
這讓我萌生了為這個美食排行榜做一個杭州美食地圖的想法,以便能快速地看到餐廳的位置並進行選擇。實現方法上 AI coding 加高德 MCP 有沒有搞頭?
文末附原始碼和專案地址供大家參考和取用。
安裝高德MCP server
過程參考 :Cursor快速接入高德MCP server
https://lbs.amap.com/api/mcp-server/gettingstarted
只需準備好你的高德 key,在 cursor 上設定裡如下配置即可。
{
"mcpServers":{
"amap-amap-sse":{
"url":"https://mcp.amap.com/sse?key=您在高德官網上申請的key"
}
}
}
配置好後檢查 mcpserver 的狀態,如下圖則是正常可用的。

給 AI 提需求-構建工程提示詞
1. 在當前資料夾下進行編寫專案,儘量少的建立檔案,只建立必要檔案。
2. 根據給定的杭州推薦的美食店,製作一份可以預覽的美食地圖。最終的產物使用便捷的方式呈現,如使用html進行展示。
3. 地圖上標註下面每個店鋪精確的位置,為每個型別的餐廳用不同的合適的圖標表示。
4. 地圖採用真實的杭州市地圖。
5. 點選圖示可顯示餐廳更多資訊,餐廳優勢,預約電話,推薦指數等。
6. 過程中可以藉助任何工具和能力實現這一目標,如 高德MCP工具。
7. 展示的店鋪已給定的為準,最終給出的資料要真實可靠。
8.將下列資料,透過amp-amp-sse 的 mcpserver,製作成適配高德地圖的 json 格式資料。若沒有找到餐廳,不要編造資料,座標欄位設為空值即可。
餐廳資料如下:
如*:主廚團隊對傳統杭幫菜的解構能力堪稱藝術,招牌魚圓彈潤如凝脂,黃酒鵝肝配牛小排的組合既有東方韻味又有西式巧思。景觀位雖為日式下沉設計,但私密性極佳,適合重要紀念日。唯一提醒:訂位時建議直接對接主理人確認細節,米其林加身後服務響應速度有提升空間。
金*廳:四季酒店的鎮店之寶,時令選單每月更新,清明前的香椿嫩芽配河蝦仁、霜降後的桂花糖藕都是必嘗限定。脆皮雞的表皮酥脆度堪比藝術品,和牛炒飯的鑊氣十足,適合帶海外友人感受中式烹飪的精細度。
八*杯:裡園的孿生品牌,早期的黃魚鍋貼仍是教科書級別,但近年創新菜式如松茸釀飯口感層次稍顯單薄。建議避開週末午市,工作日晚間用餐能更好體會主廚團隊的匠心。
集*樓:庭院裡的江南美學展,青梅鵝肝配茉莉花茶凍的創意值得點贊。近期推出的「盲盒套餐」價效比突出,適合想體驗高階杭菜又怕踩雷的新客。
木*溪:宋宴主題套餐需提前三天預訂,茶點與菜餚的搭配暗合二十四節氣,但每道菜量控制在2-3口剛好嚐鮮。特別推薦茶藝師現場點茶,宋代七湯點茶法視覺效果震撼。
桂*房:滿覺隴店的露臺夜景是最大賣點,桂花釀醉蝦的甜度把控精準。若多人聚餐可嘗試「山房九宮格」拼盤,冷熱葷素搭配考究,但需注意部分創意菜式如黑松露貓耳朵口味較重。
【浙菜】
錦*湖:原乾朧匯班底升級之作,龍翔橋店的脆皮雞改良自港式燒味,外皮的焦糖層厚度比EFC店更均勻。推薦搭配店家自釀的青梅酒,解膩效果出眾。
算*擋:濱江園區的寶藏食堂,人均150可吃到現拆的蟹粉豆腐和炭烤鰻魚。建議4人以上拼單,把招牌菜湊齊分著吃更划算。
【創意菜】
雅*:西溪溼地內的隱秘據點,荷葉包蒸東山羊是夏季限定,肉質軟爛入味。划船至碼頭需提前預約接駁船,整體體驗像私人會所。
【融合菜】
喬*:阿里園區周邊的老牌宴請場所,最近推出的「數字化點餐系統」可即時檢視後廚烹飪進度。推薦黑椒牛排配龍井茶湯,西式主菜與杭幫調味的結合恰到好處。
略...
輸入“需求文件”後,cursor 會進入 Plan 模式進行規劃整體設計方案。
已開始為你的美食地圖收集真實的高德地圖資料。以下是實現方案和進度說明:
實現方案
資料收集:為每個餐廳透過高德地圖API檢索真實的地理位置(經緯度)、地址、照片等資訊。若未找到則座標為空。
資料格式:整理為適配高德地圖MCP的JSON格式,包含必要的欄位(如名稱、型別、座標、推薦指數、電話、優勢等)。
地圖展示:最終生成一個HTML檔案,使用高德地圖JSAPI進行視覺化。不同型別餐廳用不同圖示,點選可彈窗顯示詳細資訊。
檔案結構:只建立必要的檔案(如index.html和資料JSON),保證專案簡潔。
進度說明
已完成“如*”“金*廳”真實資料檢索,獲取了精確座標和相關資訊。
正在依次為所有餐廳檢索真實資料,確保資料可靠。
檢索不到的餐廳將座標設為空,不會編造資訊。
請確認:
是否需要為每家餐廳都補充“推薦指數、預約電話、優勢”等欄位?如無公開資料,是否允許留空或用描述性文字?
是否需要將所有資料單獨匯出為JSON檔案,還是直接嵌入HTML中?
如無異議,將繼續批次檢索並整理資料,隨後生成地圖預覽HTML。你可隨時檢視進度或指定展示細節。
十幾分鍾寫完“需求文件”,晚飯時間到,提給 AI 讓它先寫著,我們去吃飯~
AI coding-開始尋找杭州美食之旅
呼叫高德 MCP Server-獲取餐廳準確位置和資訊
第一輪-模糊查詢找到候選餐廳
AI 會先呼叫高德 MCP Server 的 maps_text_search 方法透過模糊查詢,查到候選餐廳資訊。

輸入
{
"keywords":"如院餐廳 杭州",
"city":"杭州",
"citylimit":true
}
輸出
可以看到,查出了一系列餐廳,其中有如*餐廳的基本資訊,取出 id,作為下一輪的輸入。
{
"pois": [
...
{
"id": "B0JA5UF6R*",
"name": "如*餐廳",
"address": "玉泉路1*號",
"typecode": "0501**",
"photo": "http://store.is.autonavi.com/showpic/b08795ab6adeba25a0115d74130862**"
},
{
"id": "B023B019A*",
"name": "群*飯店(信*路店)",
"address": "濱安路119*號(近信*路)",
"typecode": "0501**",
"photo": "http://store.is.autonavi.com/showpic/2ac9e95efffe6e27a8eda43ef82711**"
},
...
],
"suggestion": {
"keywords": "",
"ciytes": {
"suggestion": []
}
}
}
第二輪-透過 id 獲取餐廳詳情
可以看到緊接著 AI 會呼叫高德 MCP Server 的
maps_search_detail
能力,透過 id 來獲取餐廳詳情。
輸入
{
"id":"B0JA5UF6R*"
}
輸出
可以看到這裡獲取到了餐廳的詳情資訊,包含名稱、經緯度、地址、照片、營業時間、評分等。
{"id":"B0JA5UF6R*","name":"如*餐廳","location":"120.122175,30.253356","address":"玉泉路1*號","business_area":"西湖旅遊區中心片區","city":"杭州市","type":"餐飲服務;中餐廳;中餐廳","alias":"","photo":"http://store.is.autonavi.com/showpic/b08795ab6adeba25a0115d74130862a*","cost":"","opentime2":"週一至週日 11:30-14:00,17:30-21:00","rating":"4.7","open_time":"11:30-14:00 17:30-21:00","meal_ordering":"0"}
AI 會將高德 MCP Server 中獲取的資料格式化,整理到 json 檔案中,作為資料來源。
編寫頁面
資料收集完後,就開始根據需求,編寫程式碼了,透過引入高德 API 展示杭州地圖,然後將上一步整理的資料引入進來,標出餐廳的位置。並且支援點選餐廳的名稱,彈框展示餐廳的詳情,餐廳的推薦理由。

雖然沒有關注程式碼具體怎麼實現的,但是它就這樣水靈靈的工作起來了~
但我認為我們還是需要理解 AI 寫的每一行程式碼,對程式碼質量有一個評估,一方面自己能有所進步,一方面不能讓 AI 自由發揮的太過,到後面一行也看不懂,改也改不動的時候,專案就崩了。自己不理解的程式碼,可以讓 AI 為程式碼都加上註釋,方便理解。
除錯頁面
AI 寫完了主體頁面和功能點後,預覽一下,還是有挺多細節需要修改的,如名稱大小、背景框樣式、彈框點選關閉邏輯等,這些和 AI 溝通,說明需要修改的位置和要求即可,如果說不清的時候,可以直接截圖給 AI,告訴它要改什麼,理解意圖上還是十分強大的。
找素材
但是 AI 有一點不足的是不太方便從外部直接獲取資源,比如試圖讓 AI 自己去網上找合適的素材圖示,但是失敗了,它會讓我去給它下載,不然它會自己畫一個醜陋抽象的 SVG 圖示。
實在忍不了,只能去網上找素材,可以說整個過程中,花的最多的時間就是這裡了。找合適的素材,嘗試下載,要收費?換一個地方找,終於是免費的了,下載下來,傳到圖床上,變成公網可訪問的連結。
整個找素材的過程十分痛苦,如果 AI 可以自己找就好了,也許是使用的姿勢不太對,還需要繼續研究。
好在最終找到了一個非常棒的平臺,那就是阿里巴巴圖示向量庫,還是自家兄弟好,素材非常的全且好用,大力推薦。
地址:https://www.iconfont.cn/?spm=a313x.search_index.i3.d4d0a486a.2dff3a81XH4Q3q
餐廳分類與篩選
對於之前的收集的餐廳的資料,沒有一個很好的分類進行表達,於是讓AI 重新分析一下整個餐廳資料,結合餐廳的資訊,進行分類,然後將餐廳型別與圖示素材再進行一個匹配。得到一個分類好的餐廳列表,與圖示的對映關係。

然後就可以愉快的讓 AI 結合這個分類資料,改寫一下餐廳的圖示啦,並且基於分類,再加上了圖例列表,和篩選項的功能,方便展示,不然 100個餐廳全部展示在地圖上,也不太方便小夥伴們看。
然後頁面的效果如下:

AI 推薦餐廳
看了地圖上標註的餐廳後,才發現,離自己園區的距離都比較遠,於是含淚再寫一份自己團隊的聚餐選點指南,讓 AI 幫助推薦餐廳。
1. 在當前專案中進行改進,刪去圖例、篩選項功能。
2. 使用給出的餐廳資料,在地圖上進行標註。
3. 我們的出發位置是杭州市阿里巴巴濱江園區,標註出發點位置,用星星圖標表示。
4. 圖示素材在 resource/icon.txt中.
5. 計算出發點到各個餐廳的位置,在地圖上用線連起來進行展示。
6. 分析一下各個餐廳的優劣,離出發點的距離,交通方式,和所需時間,周邊配套,列在餐廳詳情裡。
7. 最後進行一個智慧總結,推薦一個餐廳
8.
9. 過程中可以藉助任何工具和能力實現這一目標,如 高德MCP工具。
10. 展示的店鋪已給定的為準,最終給出的資料要真實可靠。
將下列資料,透過amp-amp-sse 的 mcpserver,製作成適配高德地圖的 json 格式資料。若沒有找到餐廳,不要編造資料,座標欄位設為空值即可。
1. 海*撈中南樂遊城店
2. 黃*海鮮大排檔(長河地鐵站旁)
3. 群*飯店
4. 龍*·濱江烤全羊(杭州總店)dong'da
5. 東*方保利時光裡店
有了前面的基礎,這次 20 分鐘就搞定了,主要在寫需求文件和微調頁面樣式上,可以分析一下各個餐廳的優劣,離出發點的距離,交通方式,和所需時間,周邊配套等。 最後進行一個智慧總結,推薦一個餐廳,效果如下:

小夥伴們就可以愉快的進行決策,到底選哪個餐廳了~
新的功能正在和我們投食官持續最佳化中,比如先選擇團建地方,再選擇聚餐地方,多個地點讓 AI 分析推薦等~
釋出與部署
公網部署方法挺多的,比如 GitHub、Gitee上提供了靜態頁面部署的能力,或者可以租一個阿里雲伺服器申請域名,搭建自己的網站。
在 GitHub 部署靜態頁面的簡要指南
以下為 GitHub Pages 靜態部署的核心步驟,基於官方文件和工具鏈實現:
1.建立倉庫
-
新建公共倉庫,命名格式為 username.github.io
(username
為你的 GitHub 使用者名稱)。 -
克隆倉庫到本地,新增靜態檔案(如 index.html
),並推送至main
分支。
2. 配置釋出源
-
進入倉庫Settings > Pages,選擇釋出分支(如 main
或gh-pages
)或docs
資料夾作為源。 -
若使用自定義構建工具(如 Hugo、Vue 等),需停用預設 Jekyll 構建:在源目錄根路徑建立空檔案 .nojekyll
。
3. 使用 GitHub Actions 自動部署
-
在工作流配置檔案(如 .github/workflows/deploy.yml
)中新增以下步驟:
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./public # 替換為你的構建輸出目錄
此 Action 會將指定目錄內容部署到
gh-pages
分支。4.訪問頁面
- 頁面地址為
https://username.github.io
,更新後通常需等待 1-10 分鐘生效。
5. 注意事項
-
許可權問題:若使用 GitHub Actions 推送,需確保管理員已驗證郵箱並推送過程式碼。 -
符號連結:倉庫含符號連結時,需透過 GitHub Actions 部署。
最後
這次的實踐我認為還是非常的有趣的,整個開發過程大概 3-4 小時,而且主要花在了找素材和調整樣式上了。放在以前,有了一個好的點子,但是可能會因為麻煩,跨技術棧,生產週期長、拖延症等各種因素導致“創業未半而中道崩殂”。
不得不感嘆,AI 真的能幫助人們提高效率,並且使之前不可能或者很難完成的事情變得可行。在 AI 時代,可能需要從之前傳統的“關注技術棧和專業,關注如何實現”的思維轉向聚焦到“擴充套件思維的寬度和認知,尋找事物的可達路徑”上。
另外,高德的小夥伴是否考慮一下,增加一個餐廳、團建的地點投票功能~
可以在高德上發起投票,錄入幾個候選位置,邀請小夥伴們一起投票。可以很方便的檢視當前位置(或者出發地點)距離候選地點的距離、交通方式和用時、候選地點周邊配套等。 也可以讓 AI 結合需求背景,總結一下各候選地點的優勢。然後小夥伴們就可以愉快地投票了!
附錄
原始碼如下,需要的小夥伴可自取,在這個基礎上改改就能用了。
杭州美食地圖
<htmllang="zh-CN">
<head>
<metacharset="UTF-8">
<title>杭州美食地圖</title>
<metaname="viewport"content="width=device-width, initial-scale=1.0">
<style>
html, body, #map { height: 100%; margin: 0; padding: 0; }
.amap-info-content { font-size: 14px; }
.legend {
position: absolute;
top: 10px;
left: 10px;
background: rgba(255,255,255,0.9);
border-radius: 6px;
padding: 10px;
box-shadow: 02px8pxrgba(0,0,0,0.15);
z-index: 1000;
}
.legend-item { display: flex; align-items: center; margin-bottom: 6px; }
.legend-icon { width: 20px; height: 20px; margin-right: 6px; }
.marker-label {
font-size: 16px;
color: #fff;
background: rgba(34,34,34,0.75);
border-radius: 6px;
padding: 2px10px;
box-shadow: 02px8pxrgba(0,0,0,0.15);
white-space: nowrap;
transition: background 0.2s;
border: none;
}
.marker-label-hover {
background: rgba(34,34,34,0.95);
color: #ffe58f;
border: none;
}
/* 強制覆蓋高德地圖label的藍色邊框和背景 */
.amap-marker-label {
border: none ;
box-shadow: none ;
background: none ;
padding: 0 ;
}
.filter-panel {
position: absolute;
top: 40px;
right: 20px;
width: 220px;
background: rgba(255,255,255,0.97);
border-radius: 10px;
box-shadow: 02px16pxrgba(0,0,0,0.13);
padding: 18px18px12px18px;
z-index: 1200;
font-family: 'PingFang SC', 'Microsoft YaHei', Arial, sans-serif;
font-size: 15px;
color: #222;
transition: box-shadow 0.2s;
max-height: 600px;
overflow-y: auto;
}
.filter-title {
font-size: 18px;
font-weight: bold;
margin-bottom: 12px;
letter-spacing: 2px;
}
.filter-section { margin-bottom: 14px; }
.filter-label { font-size: 14px; margin-bottom: 6px; color: #666; }
.filter-recommendlabel, .filter-categorylabel {
display: inline-block;
margin-right: 10px;
margin-bottom: 6px;
cursor: pointer;
font-size: 14px;
}
.filter-category {
max-height: 280px;
overflow-y: auto;
border-radius: 6px;
background: #f7f7f7;
padding: 6px4px4px8px;
border: 1px solid #eee;
}
.filter-categorylabel { width: 120px; }
.filter-panelinput[type="checkbox"] {
accent-color: #1890ff;
margin-right: 3px;
vertical-align: middle;
}
</style>
<!-- 高德地圖 JS API -->
<scriptsrc="https://webapi.amap.com/maps?v=2.0&key=「你的高德 apikey」"></script>
</head>
<body>
<divid="map"></div>
<divclass="legend"id="legend"></div>
<divid="filter-panel"class="filter-panel">
<divclass="filter-title">杭州美食指南</div>
<divclass="filter-section">
<divclass="filter-label">推薦指數</div>
<divclass="filter-recommend">
<label><inputtype="checkbox"name="recommend"value="5"> 5星</label>
<label><inputtype="checkbox"name="recommend"value="4"> 4星</label>
<label><inputtype="checkbox"name="recommend"value="3"> 3星</label>
<label><inputtype="checkbox"name="recommend"value="2"> 2星</label>
<label><inputtype="checkbox"name="recommend"value="1"> 1星</label>
</div>
</div>
<divclass="filter-section">
<divclass="filter-label">餐廳類別</div>
<divclass="filter-category"id="filter-category-list"></div>
</div>
</div>
<script>
// 餐廳資料直接嵌入或讀取同級目錄下檔案都可
const data = [
{
"name": "如*餐廳",
"type": "中餐廳",
"location": "120.122175,30.253356",
"address": "玉泉路1*號",
"photo": "http://store.is.autonavi.com/showpic/b08795ab6adeba25a0115d74130862aa",
"cost": "",
"open_time": "11:30-14:00 17:30-21:00",
"rating": "4.7",
"phone": "",
"advantage": "推薦理由 xxxx",
"recommend_index": "5",
"category": "中餐"
},
{
"name": "杭州**湖四季酒店金沙廳",
"type": "浙江菜",
"location": "120.129637,30.251887",
"address": "靈隱路5號杭州**湖四季酒店1層(近麴院風荷)",
"photo": "http://store.is.autonavi.com/showpic/03f435393b04359460529d3cc484e826",
"cost": "533.00",
"open_time": "11:30-14:00 17:30-21:00",
"rating": "4.8",
"phone": "",
"advantage": "推薦理由 xxxx",
"recommend_index": "5",
"category": "杭幫菜/浙江菜"
},
{
"name": "門沒鎖咖啡館",
"type": "咖啡館",
"location": "120.099355,30.297518",
"address": "政紫弄23幢**號沿街(**地鐵站F口步行210米)",
"photo": "https://aos-comment.amap.com/B0KUPD8084/comment/A241830B_F0B4_4496_A253_95A1F5385C51_L0_001_1500_2000_1735672012627_76019873.jpg",
"cost": "",
"open_time": "10:00-17:00",
"rating": "4.3",
"phone": "",
"advantage": "推薦理由 xxxx",
"recommend_index": "4",
"category": "咖啡館"
}
{
"name": "helloworld",
"type": "咖啡館",
"location": "120.005480,30.280547",
"address": "歐美金融城西溪麗晶居*幢底商1-13號",
"photo": "http://store.is.autonavi.com/showpic/72efe174477c732d4264f7373dfaf768",
"cost": "30.00",
"open_time": "08:00-18:00",
"rating": "4.3",
"phone": "",
"advantage": "推薦理由 xxxx",
"recommend_index": "4",
"category": "咖啡館"
}
];
// 圖示對映表,按最新icon.txt
const typeIcons = {
'辣椒': '「需要自己準備」',
'甜點': '「需要自己準備」',
'咖啡': '「需要自己準備」',
'中餐': '「需要自己準備」',
'日料': '「需要自己準備」',
'快餐': '「需要自己準備」',
'烤肉': '「需要自己準備」',
'燒烤': '「需要自己準備」',
'星星': '「需要自己準備」',
'火鍋': '「需要自己準備」',
'牛排': '「需要自己準備」',
'麵食': '「需要自己準備」',
'海鮮': '「需要自己準備」',
'韓餐': '「需要自己準備」',
'小吃': '「需要自己準備」',
'粵菜': '「需要自己準備」',
'default': '「需要自己準備」'
};
// 圖例分類,按icon.txt順序
const legendTypes = [
{type: '中餐', label: '中餐/杭幫菜/其他'},
{type: '粵菜', label: '粵菜'},
{type: '辣椒', label: '川菜'},
{type: '火鍋', label: '火鍋'},
{type: '燒烤', label: '燒烤'},
{type: '烤肉', label: '烤肉'},
{type: '日料', label: '日料'},
{type: '韓餐', label: '韓餐'},
{type: '牛排', label: '西餐'},
{type: '麵食', label: '麵館/麵食'},
{type: '小吃', label: '小吃'},
{type: '甜點', label: '甜品/飲品'},
{type: '咖啡', label: '咖啡館'},
{type: '快餐', label: '快餐'},
{type: '海鮮', label: '海鮮'}
];
// category到icon的對映規則
functiongetCategoryIcon(category){
if (!category) return typeIcons['default'];
if (category === '中餐' || category === '杭幫菜/浙江菜' || category === '湘菜' || category === '東北菜' || category === '素食' || category === '私房菜') return typeIcons['中餐'];
if (category === '粵菜') return typeIcons['粵菜'];
if (category === '川菜') return typeIcons['辣椒'];
if (category === '火鍋') return typeIcons['火鍋'];
if (category === '燒烤/烤肉') return typeIcons['烤肉'];
if (category === '燒烤') return typeIcons['燒烤'];
if (category === '日料') return typeIcons['日料'];
if (category === '韓餐') return typeIcons['韓餐'];
if (category === '西餐') return typeIcons['牛排'];
if (category === '麵館') return typeIcons['麵食'];
if (category === '小吃') return typeIcons['小吃'];
if (category === '甜品/飲品') return typeIcons['甜點'];
if (category === '咖啡館') return typeIcons['咖啡'];
if (category === '快餐') return typeIcons['快餐'];
if (category === '海鮮') return typeIcons['海鮮'];
return typeIcons['default'];
}
// 初始化地圖
constmap = newAMap.Map('map', {
center: [120.15507, 30.274085], // 杭州中心
zoom: 12
});
// 移除所有 data.forEach(...) 相關的marker渲染,只保留 renderMarkers() 控制
// ... existing code ...
// 圖例渲染
const legend = document.getElementById('legend');
legend.innerHTML = legendTypes.map(t =>
`<div class="legend-item"><img class='legend-icon' src='${typeIcons[t.type]}' alt='${t.label}' />${t.label}</div>`
).join('');
// InfoWindow全域性變數
let infoWindow = null;
// label樣式
const style = document.createElement('style');
style.innerHTML = `
.marker-label {
font-size: 16px;
color: #fff;
background: rgba(34,34,34,0.75);
border-radius: 6px;
padding: 2px 10px;
box-shadow: 02px 8px rgba(0,0,0,0.15);
white-space: nowrap;
transition: background 0.2s;
border: none;
}
.marker-label-hover {
background: rgba(34,34,34,0.95);
color: #ffe58f;
border: none;
}
/* 強制覆蓋高德地圖label的藍色邊框和背景 */
.amap-marker-label {
border: none !important;
box-shadow: none !important;
background: none !important;
padding: 0 !important;
}
`;
document.head.appendChild(style);
// 動態生成類別篩選項
const allCategories = Array.from(newSet(data.map(d => d.category).filter(Boolean)));
const filterCategoryList = document.getElementById('filter-category-list');
filterCategoryList.innerHTML = allCategories.map(cat =>
`<label><input type='checkbox' name='category' value='${cat}'> ${cat}</label>`
).join('');
// 預設勾選5星
document.querySelector('input[name="recommend"][value="5"]').checked = true;
// 篩選邏輯
functiongetSelectedRecommends() {
returnArray.from(document.querySelectorAll('input[name="recommend"]:checked')).map(i => i.value);
}
functiongetSelectedCategories(){
returnArray.from(document.querySelectorAll('input[name="category"]:checked')).map(i => i.value);
}
let allMarkers = [];
functionrenderMarkers(){
// 清除舊marker
allMarkers.forEach(m => m.setMap(null));
allMarkers = [];
const selectedRecommends = getSelectedRecommends();
const selectedCategories = getSelectedCategories();
let filtered = data;
// 取交集策略:都選了才展示
if (selectedRecommends.length) {
filtered = filtered.filter(d => selectedRecommends.includes(d.recommend_index));
}
if (selectedCategories.length) {
filtered = filtered.filter(d => selectedCategories.includes(d.category));
}
// 如果都沒選,展示全部
if (!selectedRecommends.length && !selectedCategories.length) {
filtered = data;
}
filtered.forEach(item => {
if (!item.location) return;
const [lng, lat] = item.location.split(',').map(Number);
const iconUrl = getCategoryIcon(item.category);
const marker = newAMap.Marker({
position: [lng, lat],
icon: newAMap.Icon({
image: iconUrl,
size: newAMap.Size(32, 32),
imageSize: newAMap.Size(32, 32)
}),
title: item.name,
offset: newAMap.Pixel(-16, -36),
label: {
content: `<span class='marker-label'>${item.name}</span>`,
direction: 'top',
offset: newAMap.Pixel(0, -16),
clickable: true
}
});
marker.on('mouseover', () => marker.setLabel({ ...marker.getLabel(), content: `<span class='marker-label marker-label-hover'>${item.name}</span>` }));
marker.on('mouseout', () => marker.setLabel({ ...marker.getLabel(), content: `<span class='marker-label'>${item.name}</span>` }));
marker.on('click', () =>showInfo(item));
marker.on('labelClick', () =>showInfo(item));
marker.setMap(map);
allMarkers.push(marker);
});
}
// 監聽篩選項變化
document.querySelectorAll('.filter-panel input[type="checkbox"]').forEach(input => {
input.addEventListener('change', renderMarkers);
});
// 初始化時渲染一次
renderMarkers();
functionshowInfo(item){
// 關閉已有 infoWindow
if (infoWindow) {
infoWindow.close();
}
// 構建詳情內容
const content = `
<div style="min-width:220px;max-width:320px;">
<div style="font-size:18px;font-weight:bold;margin-bottom:6px;">${item.name}</div>
<div style="margin-bottom:4px;">${item.address ? '📍 ' + item.address : ''}</div>
${item.photo ? `<img src="${item.photo}" style="width:100%;max-height:120px;object-fit:cover;border-radius:6px;margin-bottom:6px;">` : ''}
<div>型別:${item.category || item.type || ''}</div>
<div>推薦指數:${item.recommend_index || ''}</div>
<div>評分:${item.rating || ''}</div>
<div>人均:${item.cost ? '¥' + item.cost : ''}</div>
<div>營業時間:${item.open_time || ''}</div>
<div>電話:${item.phone || ''}</div>
<div style="margin-top:4px;">${item.advantage || ''}</div>
</div>
`;
// 取座標
const [lng, lat] = item.location.split(',').map(Number);
infoWindow = newAMap.InfoWindow({
content,
offset: newAMap.Pixel(0, -36)
});
infoWindow.open(map, [lng, lat]);
}
// 點選地圖空白處關閉詳情彈窗
map.on('click', function() {
if (infoWindow) infoWindow.close();
});
</script>
</body>
</html>
“去哪吃”原始碼
<htmllang="zh-CN">
<head>
<metacharset="UTF-8">
<title>去哪吃-AI推薦餐廳</title>
<metaname="viewport"content="width=device-width, initial-scale=1.0">
<style>
html, body, #map { height: 100%; margin: 0; padding: 0; }
.amap-info-content { font-size: 14px; }
.legend {
position: absolute;
top: 10px;
left: 10px;
background: rgba(255,255,255,0.9);
border-radius: 6px;
padding: 10px;
box-shadow: 02px8pxrgba(0,0,0,0.15);
z-index: 1000;
}
.legend-item { display: flex; align-items: center; margin-bottom: 6px; }
.legend-icon { width: 20px; height: 20px; margin-right: 6px; }
.marker-label {
font-size: 16px;
color: #fff;
background: rgba(34,34,34,0.75);
border-radius: 6px;
padding: 2px10px;
box-shadow: 02px8pxrgba(0,0,0,0.15);
white-space: nowrap;
transition: background 0.2s;
border: none;
}
.marker-label-hover {
background: rgba(34,34,34,0.95);
color: #ffe58f;
border: none;
}
/* 強制覆蓋高德地圖label的藍色邊框和背景 */
.amap-marker-label {
border: none ;
box-shadow: none ;
background: none ;
padding: 0 ;
}
.filter-panel {
position: absolute;
top: 40px;
right: 20px;
width: 220px;
background: rgba(255,255,255,0.97);
border-radius: 10px;
box-shadow: 02px16pxrgba(0,0,0,0.13);
padding: 18px18px12px18px;
z-index: 1200;
font-family: 'PingFang SC', 'Microsoft YaHei', Arial, sans-serif;
font-size: 15px;
color: #222;
transition: box-shadow 0.2s;
max-height: 600px;
overflow-y: auto;
}
.filter-title {
font-size: 18px;
font-weight: bold;
margin-bottom: 12px;
letter-spacing: 2px;
}
.filter-section { margin-bottom: 14px; }
.filter-label { font-size: 14px; margin-bottom: 6px; color: #666; }
.filter-recommendlabel, .filter-categorylabel {
display: inline-block;
margin-right: 10px;
margin-bottom: 6px;
cursor: pointer;
font-size: 14px;
}
.filter-category {
max-height: 280px;
overflow-y: auto;
border-radius: 6px;
background: #f7f7f7;
padding: 6px4px4px8px;
border: 1px solid #eee;
}
.filter-categorylabel { width: 120px; }
.filter-panelinput[type="checkbox"] {
accent-color: #1890ff;
margin-right: 3px;
vertical-align: middle;
}
.detail-panel {
position: absolute;
top: 40px;
right: 20px;
width: 400px;
background: rgba(255,255,255,0.98);
border-radius: 14px;
box-shadow: 04px32pxrgba(0,0,0,0.13);
padding: 28px28px18px28px;
z-index: 1200;
font-family: 'PingFang SC', 'Microsoft YaHei', Arial, sans-serif;
font-size: 16px;
color: #222;
max-height: 80vh;
overflow-y: auto;
transition: box-shadow 0.2s;
display: none;
}
.detail-title {
font-size: 22px;
font-weight: bold;
margin-bottom: 10px;
color: #1890ff;
letter-spacing: 1px;
}
.detail-recommend {
background: #fffbe6;
color: #faad14;
border-radius: 8px;
padding: 8px14px;
font-size: 18px;
font-weight: bold;
margin-bottom: 16px;
box-shadow: 02px8pxrgba(250,173,20,0.08);
display: flex;
align-items: center;
gap: 8px;
}
.detail-photo {
width: 100%;
max-height: 180px;
object-fit: cover;
border-radius: 8px;
margin-bottom: 10px;
box-shadow: 02px8pxrgba(0,0,0,0.08);
}
.detail-field { margin-bottom: 8px; }
.detail-label { color: #888; margin-right: 6px; }
.detail-analysis {
background: #f6ffed;
color: #389e0d;
border-radius: 8px;
padding: 10px14px;
margin-top: 12px;
font-size: 15px;
box-shadow: 02px8pxrgba(56,158,13,0.06);
}
.detail-panel-title {
font-size: 20px;
font-weight: bold;
text-align: center;
margin-bottom: 18px;
color: #222;
letter-spacing: 1px;
}
</style>
<!-- 高德地圖 JS API -->
<scriptsrc="https://webapi.amap.com/maps?v=2.0&key=「你的高德 apikey」"></script>
</head>
<body>
<divid="map"></div>
<divid="detail-panel"class="detail-panel"></div>
<script>
// 直接嵌入餐廳資料
const data = [
{
"name": "杭州市阿里巴巴濱江園區(出發點)",
"type": "出發點",
"location": "120.190371,30.189600",
"address": "杭州市濱江區網商路6**號",
"photo": "https://gw.alicdn.com/imgextra/i2/O1CN014DVQlb1oVqsnsrbiT_!!6000000005231-2-tps-64-64.png",
"cost": "-",
"open_time": "-",
"rating": "-",
"phone": "-",
"advantage": "公司園區,交通便利,地鐵直達,周邊配套齊全。",
"recommend_index": "-",
"category": "出發點",
"is_origin": true
},
{
"name": "海*撈火鍋(中南樂遊城店)",
"type": "火鍋",
"location": "120.190309,30.193994",
"address": "江南大道10**號",
"photo": "https://aos-comment.amap.com/B0GU9MT4N7/comment/6845185a8eedb6301996b74240ba088b_2048_2048_80.jpg",
"cost": "80.00",
"open_time": "09:00-07:00",
"rating": "4.7",
"phone": "-",
"advantage": "服務優質,環境舒適,適合聚餐。",
"recommend_index": "5",
"category": "火鍋",
"distance_m": 2030,
"duration_s": 459,
"traffic": "駕車約8分鐘,公交便利,地鐵近。",
"surrounding": "中南樂遊城、知嘛家等商場,生活便利。",
"analysis": "距離適中,交通便利,配套豐富,適合團隊聚餐。"
},
{
"name": "黃*海鮮大排檔(濱江店)",
"type": "海鮮",
"location": "120.195922,30.197039",
"address": "浙商發展大廈*座1-2樓(**地鐵站G口)",
"photo": "https://aos-comment.amap.com/B0J1B5R0EY/comment/0e8ed310fa3bb3135ddbefe27244562f_2048_2048_80.jpg",
"cost": "139.00",
"open_time": "11:00-15:30 17:30-02:30",
"rating": "4.7",
"phone": "-",
"advantage": "菜品新鮮,適合多人聚餐,夜宵豐富。",
"recommend_index": "5",
"category": "海鮮",
"distance_m": 2395,
"duration_s": 478,
"traffic": "駕車約8分鐘,地鐵直達,公交便利。",
"surrounding": "知嘛家等商場,夜生活豐富。",
"analysis": "交通便利,夜宵豐富,適合下班聚會。"
},
{
"name": "群*飯店(信誠路店)",
"type": "中餐",
"location": "120.174707,30.183901",
"address": "濱安路11**號(近信誠路)",
"photo": "http://store.is.autonavi.com/showpic/2ac9e95efffe6e27a8eda43ef8271134",
"cost": "66.00",
"open_time": "11:00-13:30 16:00-20:30",
"rating": "4.7",
"phone": "-",
"advantage": "家常菜,價效比高,適合日常用餐。",
"recommend_index": "4",
"category": "中餐",
"distance_m": 2797,
"duration_s": 757,
"traffic": "駕車約13分鐘,公交可達。",
"surrounding": "周邊無大型商場,環境安靜。",
"analysis": "價效比高,適合小團隊日常聚餐。"
},
{
"name": "龍*·濱江烤全羊(杭州總店)",
"type": "烤肉",
"location": "120.195890,30.184231",
"address": "長河路47*號和瑞科技廣場S*幢樓下",
"photo": "http://store.is.autonavi.com/showpic/45d72ae00c69cf56d4fe514b1346c55c",
"cost": "132.00",
"open_time": "11:00-21:00",
"rating": "4.8",
"phone": "-",
"advantage": "烤全羊特色,適合聚會,環境寬敞。",
"recommend_index": "5",
"category": "烤肉",
"distance_m": 1226,
"duration_s": 254,
"traffic": "駕車約4分鐘,公交可達。",
"surrounding": "保利·時光裡等商場,配套齊全。",
"analysis": "距離最近,評分最高,配套好,強烈推薦。"
},
{
"name": "東*方(杭州保利時光裡店)",
"type": "中餐",
"location": "120.197088,30.182094",
"address": "長河街道長河路與濱康路交叉口保利時光裡*樓13號樓2017商鋪",
"photo": "http://store.is.autonavi.com/query_pic?id=st1ef630c8-39ef-4d63-93ce-ba3a9732851e&user=search&operate=original",
"cost": "-",
"open_time": "-",
"rating": "4.8",
"phone": "-",
"advantage": "環境優雅,適合家庭聚餐。",
"recommend_index": "5",
"category": "中餐",
"distance_m": 2088,
"duration_s": 491,
"traffic": "駕車約8分鐘,公交便利。",
"surrounding": "保利·時光裡等商場,生活便利。",
"analysis": "評分高,環境好,適合家庭和小型聚會。"
}
];
// 圖示對映表
const typeIcons = {
'辣椒': '「需要自己準備」',
'甜點': '「需要自己準備」',
'咖啡': '「需要自己準備」',
'中餐': '「需要自己準備」',
'日料': '「需要自己準備」',
'快餐': '「需要自己準備」',
'烤肉': '「需要自己準備」',
'燒烤': '「需要自己準備」',
'星星': '「需要自己準備」',
'火鍋': '「需要自己準備」',
'牛排': '「需要自己準備」',
'麵食': '「需要自己準備」',
'海鮮': '「需要自己準備」',
'韓餐': '「需要自己準備」',
'小吃': '「需要自己準備」',
'粵菜': '「需要自己準備」',
'default': '「需要自己準備」'
};
functiongetCategoryIcon(category){
if (!category) return typeIcons['default'];
if (category === '出發點') return typeIcons['星星'];
if (category === '中餐' || category === '杭幫菜/浙江菜' || category === '湘菜' || category === '東北菜' || category === '素食' || category === '私房菜') return typeIcons['中餐'];
if (category === '粵菜') return typeIcons['粵菜'];
if (category === '川菜') return typeIcons['辣椒'];
if (category === '火鍋') return typeIcons['火鍋'];
if (category === '燒烤/烤肉' || category === '烤肉') return typeIcons['烤肉'];
if (category === '燒烤') return typeIcons['燒烤'];
if (category === '日料') return typeIcons['日料'];
if (category === '韓餐') return typeIcons['韓餐'];
if (category === '西餐') return typeIcons['牛排'];
if (category === '麵館') return typeIcons['麵食'];
if (category === '小吃') return typeIcons['小吃'];
if (category === '甜品/飲品') return typeIcons['甜點'];
if (category === '咖啡館') return typeIcons['咖啡'];
if (category === '快餐') return typeIcons['快餐'];
if (category === '海鮮') return typeIcons['海鮮'];
return typeIcons['default'];
}
// 初始化地圖
constmap = newAMap.Map('map', {
center: [120.190371, 30.189600],
zoom: 14
});
let allMarkers = [];
let allLines = [];
functionrenderMarkers(){
// 清除舊marker和線
allMarkers.forEach(m => m.setMap(null));
allMarkers = [];
allLines.forEach(l => l.setMap(null));
allLines = [];
if (!data.length) return;
// 出發點
const origin = data.find(d => d.is_origin);
// 餐廳
const restaurants = data.filter(d => !d.is_origin);
// 出發點marker
if (origin) {
const [lng, lat] = origin.location.split(',').map(Number);
const marker = newAMap.Marker({
position: [lng, lat],
icon: newAMap.Icon({
image: getCategoryIcon('出發點'),
size: newAMap.Size(40, 40),
imageSize: newAMap.Size(40, 40)
}),
title: origin.name,
offset: newAMap.Pixel(-20, -40),
label: {
content: `<span class='marker-label'>${origin.name}</span>`,
direction: 'top',
offset: newAMap.Pixel(0, -20),
clickable: true
}
});
marker.setMap(map);
allMarkers.push(marker);
}
// 餐廳marker和連線
restaurants.forEach(item => {
if (!item.location) return;
const [lng, lat] = item.location.split(',').map(Number);
const iconUrl = getCategoryIcon(item.category);
const marker = newAMap.Marker({
position: [lng, lat],
icon: newAMap.Icon({
image: iconUrl,
size: newAMap.Size(32, 32),
imageSize: newAMap.Size(32, 32)
}),
title: item.name,
offset: newAMap.Pixel(-16, -36),
label: {
content: `<span class='marker-label'>${item.name}</span>`,
direction: 'top',
offset: newAMap.Pixel(0, -16),
clickable: true
}
});
marker.on('mouseover', () => marker.setLabel({ ...marker.getLabel(), content: `<span class='marker-label marker-label-hover'>${item.name}</span>` }));
marker.on('mouseout', () => marker.setLabel({ ...marker.getLabel(), content: `<span class='marker-label'>${item.name}</span>` }));
marker.on('click', () =>showInfo(item));
marker.on('labelClick', () =>showInfo(item));
marker.setMap(map);
allMarkers.push(marker);
// 連線
if (origin && origin.location) {
const [olng, olat] = origin.location.split(',').map(Number);
const line = newAMap.Polyline({
path: [ [olng, olat], [lng, lat] ],
strokeColor: '#1890ff',
strokeWeight: 4,
strokeOpacity: 0.7,
isOutline: true,
outlineColor: '#fff'
});
line.setMap(map);
allLines.push(line);
}
});
}
// 智慧推薦(評分最高且距離最近)
functionshowRecommend(){
if (!data.length) return;
const restaurants = data.filter(d => !d.is_origin);
// 評分優先,距離次之
let best = restaurants[0];
restaurants.forEach(r => {
if (
Number(r.rating) > Number(best.rating) ||
(r.rating === best.rating && r.distance_m < best.distance_m)
) {
best = r;
}
});
showInfo(best, true);
}
// 右側詳情面板
functionshowInfo(item, isRecommend = false){
const panel = document.getElementById('detail-panel');
if (!item) { panel.style.display = 'none'; return; }
let html = '';
html += `<div class='detail-panel-title'>AI推薦餐廳</div>`;
if (isRecommend) {
html += `<div class='detail-recommend'>🌟 智慧推薦:<span>${item.name}</span></div>`;
}
html += `<div class='detail-title'>${item.name}</div>`;
if (item.photo) html += `<img class='detail-photo' src='${item.photo}' alt='${item.name}'>`;
html += `<div class='detail-field'><span class='detail-label'>型別:</span>${item.type || ''}</div>`;
html += `<div class='detail-field'><span class='detail-label'>地址:</span>${item.address || ''}</div>`;
if (item.distance_m) html += `<div class='detail-field'><span class='detail-label'>距離:</span>${(item.distance_m/1000).toFixed(2)} km</div>`;
if (item.duration_s) html += `<div class='detail-field'><span class='detail-label'>車程:</span>${Math.round(item.duration_s/60)} 分鐘</div>`;
if (item.cost && item.cost !== '-') html += `<div class='detail-field'><span class='detail-label'>人均:</span>¥${item.cost}</div>`;
if (item.open_time && item.open_time !== '-') html += `<div class='detail-field'><span class='detail-label'>營業時間:</span>${item.open_time}</div>`;
if (item.rating && item.rating !== '-') html += `<div class='detail-field'><span class='detail-label'>評分:</span>${item.rating}</div>`;
if (item.phone && item.phone !== '-') html += `<div class='detail-field'><span class='detail-label'>電話:</span>${item.phone}</div>`;
if (item.traffic) html += `<div class='detail-field'><span class='detail-label'>交通:</span>${item.traffic}</div>`;
if (item.surrounding) html += `<div class='detail-field'><span class='detail-label'>周邊配套:</span>${item.surrounding}</div>`;
if (item.advantage) html += `<div class='detail-field'><span class='detail-label'>優點:</span>${item.advantage}</div>`;
if (item.analysis) html += `<div class='detail-analysis'>${item.analysis}</div>`;
panel.innerHTML = html;
panel.style.display = 'block';
}
// 地圖點選空白關閉詳情
map.on('click', function() {
document.getElementById('detail-panel').style.display = 'none';
});
renderMarkers();
showRecommend();
</script>
</body>
</html>
10分鐘微調:讓0.6B模型媲美235B模型
大引數模型效果好,但成本高、響應慢。針對該問題,可以用大引數模型的輸出去微調小引數模型,使其在特定任務中的表現接近大引數模型,這一過程也被稱為模型蒸餾。本方案介紹如何透過模型蒸餾提升 Qwen3-0.6B 模型在特定任務的表現(本案例中準確率從14%提升至90%以上)。
點選閱讀原文檢視詳情。