Implement mobile UI enhancements including a sidebar, mobile search functionality, and a floating results button. Add port checking and killing logic in main.go. Update HTML and CSS for mobile responsiveness and new UI elements.
This commit is contained in:
parent
d082c97823
commit
a85fabc2d8
64
main.go
64
main.go
@ -9,10 +9,14 @@ import (
|
|||||||
"io/fs"
|
"io/fs"
|
||||||
"log"
|
"log"
|
||||||
"math"
|
"math"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
@ -120,10 +124,65 @@ type AmapTipsResponse struct {
|
|||||||
|
|
||||||
var config Config
|
var config Config
|
||||||
|
|
||||||
|
// checkAndKillPort 检查端口是否被占用,如果被占用则尝试杀死占用进程
|
||||||
|
func checkAndKillPort(port string) {
|
||||||
|
addr := ":" + port
|
||||||
|
ln, err := net.Listen("tcp", addr)
|
||||||
|
if err == nil {
|
||||||
|
// 端口未被占用,关闭监听
|
||||||
|
ln.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("端口 %s 已被占用,正在尝试释放...", port)
|
||||||
|
|
||||||
|
// 根据操作系统执行不同的命令
|
||||||
|
var cmd *exec.Cmd
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
// Windows: 查找并杀死占用端口的进程
|
||||||
|
// 先查找 PID
|
||||||
|
findCmd := exec.Command("cmd", "/c", fmt.Sprintf("netstat -ano | findstr :%s | findstr LISTENING", port))
|
||||||
|
output, err := findCmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("查找端口占用进程失败: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析 PID
|
||||||
|
lines := strings.Split(string(output), "\n")
|
||||||
|
for _, line := range lines {
|
||||||
|
fields := strings.Fields(line)
|
||||||
|
if len(fields) >= 5 {
|
||||||
|
pid := fields[len(fields)-1]
|
||||||
|
if pid != "" && pid != "0" {
|
||||||
|
log.Printf("发现占用进程 PID: %s,正在终止...", pid)
|
||||||
|
killCmd := exec.Command("taskkill", "/F", "/PID", pid)
|
||||||
|
killCmd.Run()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Linux/Mac: 使用 lsof 和 kill
|
||||||
|
cmd = exec.Command("sh", "-c", fmt.Sprintf("lsof -ti:%s | xargs -r kill -9", port))
|
||||||
|
cmd.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 等待端口释放
|
||||||
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
log.Printf("端口 %s 已释放", port)
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// 加载配置
|
// 加载配置
|
||||||
loadConfig()
|
loadConfig()
|
||||||
|
|
||||||
|
// 检查并释放端口
|
||||||
|
port := config.Port
|
||||||
|
if port == "" {
|
||||||
|
port = "8080"
|
||||||
|
}
|
||||||
|
checkAndKillPort(port)
|
||||||
|
|
||||||
// 设置Gin模式
|
// 设置Gin模式
|
||||||
gin.SetMode(gin.ReleaseMode)
|
gin.SetMode(gin.ReleaseMode)
|
||||||
|
|
||||||
@ -145,11 +204,6 @@ func main() {
|
|||||||
r.POST("/api/search", searchPOIHandler)
|
r.POST("/api/search", searchPOIHandler)
|
||||||
r.GET("/api/tips", getTipsHandler)
|
r.GET("/api/tips", getTipsHandler)
|
||||||
|
|
||||||
port := config.Port
|
|
||||||
if port == "" {
|
|
||||||
port = "8080"
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("========================================")
|
log.Printf("========================================")
|
||||||
log.Printf(" 会面点 Meeting Point")
|
log.Printf(" 会面点 Meeting Point")
|
||||||
log.Printf(" 服务启动在 http://localhost:%s", port)
|
log.Printf(" 服务启动在 http://localhost:%s", port)
|
||||||
|
|||||||
BIN
meeting-point.exe
Normal file
BIN
meeting-point.exe
Normal file
Binary file not shown.
@ -738,6 +738,276 @@ ul {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 移动端搜索框 */
|
||||||
|
.mobile-search-bar {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
top: var(--spacing-md);
|
||||||
|
left: var(--spacing-md);
|
||||||
|
right: var(--spacing-md);
|
||||||
|
z-index: 50;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-search-input-wrapper {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-search-bar input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px 40px 12px 16px;
|
||||||
|
background: rgba(26, 26, 46, 0.95);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: var(--radius-lg);
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
box-shadow: var(--shadow-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-search-bar input:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--primary);
|
||||||
|
box-shadow: 0 0 0 3px var(--primary-glow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-search-bar input::placeholder {
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-search-clear {
|
||||||
|
position: absolute;
|
||||||
|
right: 8px;
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
background: var(--bg-card-hover);
|
||||||
|
border: none;
|
||||||
|
border-radius: 50%;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 1.1rem;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-search-tips {
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
margin-top: 4px;
|
||||||
|
background: rgba(26, 26, 46, 0.98);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
max-height: 250px;
|
||||||
|
overflow-y: auto;
|
||||||
|
display: none;
|
||||||
|
box-shadow: var(--shadow-lg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-search-tips.active {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-search-tip-item {
|
||||||
|
padding: 12px 16px;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-search-tip-item:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-search-tip-item:active {
|
||||||
|
background: var(--bg-card-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-search-tip-item .tip-name {
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-search-tip-item .tip-address {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 地图点击确认弹框 */
|
||||||
|
.map-click-confirm {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
z-index: 200;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: var(--spacing-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.map-click-confirm .confirm-content {
|
||||||
|
background: var(--bg-card);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: var(--radius-lg);
|
||||||
|
padding: var(--spacing-lg);
|
||||||
|
max-width: 320px;
|
||||||
|
width: 100%;
|
||||||
|
box-shadow: var(--shadow-lg);
|
||||||
|
animation: popIn 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes popIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(0.9);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.map-click-confirm .confirm-title {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-primary);
|
||||||
|
margin-bottom: var(--spacing-md);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.map-click-confirm .confirm-address {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
text-align: center;
|
||||||
|
padding: var(--spacing-md);
|
||||||
|
background: var(--bg-darker);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
margin-bottom: var(--spacing-lg);
|
||||||
|
line-height: 1.5;
|
||||||
|
max-height: 80px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.map-click-confirm .confirm-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--spacing-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.map-click-confirm .confirm-btn {
|
||||||
|
flex: 1;
|
||||||
|
padding: 12px;
|
||||||
|
border: none;
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
font-size: 0.95rem;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: var(--transition-fast);
|
||||||
|
}
|
||||||
|
|
||||||
|
.map-click-confirm .confirm-btn.cancel {
|
||||||
|
background: var(--bg-elevated);
|
||||||
|
color: var(--text-secondary);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.map-click-confirm .confirm-btn.cancel:active {
|
||||||
|
background: var(--bg-card-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.map-click-confirm .confirm-btn.ok {
|
||||||
|
background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
|
||||||
|
color: var(--bg-dark);
|
||||||
|
}
|
||||||
|
|
||||||
|
.map-click-confirm .confirm-btn.ok:active {
|
||||||
|
transform: scale(0.98);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 浮动结果按钮 */
|
||||||
|
.floating-result-btn {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 24px;
|
||||||
|
right: 24px;
|
||||||
|
padding: 10px 16px;
|
||||||
|
background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
|
||||||
|
border: none;
|
||||||
|
border-radius: 25px;
|
||||||
|
color: #0f0f1a;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
font-weight: 700;
|
||||||
|
cursor: pointer;
|
||||||
|
z-index: 9999;
|
||||||
|
display: none;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
box-shadow: 0 4px 16px rgba(245, 158, 11, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.floating-result-btn:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 6px 20px rgba(245, 158, 11, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.floating-result-btn.visible {
|
||||||
|
display: flex !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.floating-result-btn .result-icon {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.floating-result-btn .result-text {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.floating-result-btn .result-badge {
|
||||||
|
background: #0f0f1a;
|
||||||
|
color: #f59e0b;
|
||||||
|
font-weight: 700;
|
||||||
|
padding: 4px 8px;
|
||||||
|
border-radius: 10px;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
min-width: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* PC端显示为紧凑按钮:图标+数字 */
|
||||||
|
.floating-result-btn::before {
|
||||||
|
content: '📋';
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.mobile-search-bar {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.floating-result-btn {
|
||||||
|
bottom: 90px;
|
||||||
|
left: 50%;
|
||||||
|
right: auto;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
padding: 12px 20px;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
box-shadow: 0 6px 24px rgba(245, 158, 11, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.floating-result-btn::before {
|
||||||
|
content: '📋 查看结果';
|
||||||
|
font-size: 0.95rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.floating-result-btn .result-badge {
|
||||||
|
padding: 4px 10px;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* 中心点信息 */
|
/* 中心点信息 */
|
||||||
.center-info {
|
.center-info {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -901,11 +1171,12 @@ ul {
|
|||||||
padding: var(--spacing-md) var(--spacing-lg);
|
padding: var(--spacing-md) var(--spacing-lg);
|
||||||
min-width: 220px;
|
min-width: 220px;
|
||||||
max-width: 300px;
|
max-width: 300px;
|
||||||
background: rgba(22, 33, 62, 0.92);
|
background: rgba(22, 33, 62, 0.95);
|
||||||
backdrop-filter: blur(12px);
|
backdrop-filter: blur(12px);
|
||||||
border: 1px solid rgba(245, 158, 11, 0.3);
|
border: 1px solid rgba(245, 158, 11, 0.3);
|
||||||
border-radius: var(--radius-md);
|
border-radius: var(--radius-md);
|
||||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4), 0 0 20px rgba(245, 158, 11, 0.1);
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4), 0 0 20px rgba(245, 158, 11, 0.1);
|
||||||
|
overflow: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-window h3 {
|
.info-window h3 {
|
||||||
@ -982,32 +1253,571 @@ ul {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ========================================
|
||||||
|
移动端快捷操作栏
|
||||||
|
======================================== */
|
||||||
|
|
||||||
|
.mobile-action-bar {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
padding: var(--spacing-sm) var(--spacing-md);
|
||||||
|
padding-bottom: calc(var(--spacing-sm) + env(safe-area-inset-bottom, 0));
|
||||||
|
background: rgba(26, 26, 46, 0.95);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
border-top: 1px solid var(--border);
|
||||||
|
z-index: 60;
|
||||||
|
gap: var(--spacing-sm);
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-action-bar .action-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--spacing-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-action-bar .action-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--spacing-sm);
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 0.85rem;
|
||||||
|
padding: 0 var(--spacing-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-action-bar .location-badge {
|
||||||
|
background: var(--primary);
|
||||||
|
color: var(--bg-dark);
|
||||||
|
font-weight: 700;
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 12px;
|
||||||
|
min-width: 20px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-action-bar .action-search-btn {
|
||||||
|
flex: 1;
|
||||||
|
padding: 10px var(--spacing-md);
|
||||||
|
background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
|
||||||
|
border: none;
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
color: var(--bg-dark);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-action-bar .action-search-btn:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-action-bar .menu-btn {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
background: var(--bg-elevated);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-size: 1.2rem;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========================================
|
||||||
|
移动端切换按钮(隐藏,改用底部菜单)
|
||||||
|
======================================== */
|
||||||
|
|
||||||
|
.mobile-toggle {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 侧边栏遮罩 */
|
||||||
|
.sidebar-overlay {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
z-index: 99;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity var(--transition-normal);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-overlay.active {
|
||||||
|
opacity: 1;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========================================
|
||||||
|
平板端适配 (768px - 1024px)
|
||||||
|
======================================== */
|
||||||
|
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
:root {
|
||||||
|
--sidebar-width: 360px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.floating-results {
|
||||||
|
width: 340px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========================================
|
||||||
|
移动端适配 (< 768px)
|
||||||
|
======================================== */
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
|
:root {
|
||||||
|
--spacing-md: 12px;
|
||||||
|
--spacing-lg: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
.app-container {
|
.app-container {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 移动端显示操作栏 */
|
||||||
|
.mobile-action-bar {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 侧边栏改为底部弹出面板 */
|
||||||
.sidebar {
|
.sidebar {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
top: auto;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-width: 100%;
|
min-width: 100%;
|
||||||
max-height: 50vh;
|
max-height: 70vh;
|
||||||
|
z-index: 100;
|
||||||
|
transform: translateY(100%);
|
||||||
|
transition: transform var(--transition-normal);
|
||||||
|
border-radius: var(--radius-xl) var(--radius-xl) 0 0;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sidebar.active {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-overlay {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 地图全屏 */
|
||||||
.map-area {
|
.map-area {
|
||||||
height: 50vh;
|
width: 100%;
|
||||||
|
height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
.location-list {
|
/* Logo 简化 */
|
||||||
max-height: 120px;
|
.logo {
|
||||||
|
padding: var(--spacing-md);
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 添加拖动指示条 */
|
||||||
|
.logo::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 8px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
width: 40px;
|
||||||
|
height: 4px;
|
||||||
|
background: var(--border-light);
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-icon {
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-icon svg {
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo h1 {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 侧边栏内容可滚动 */
|
||||||
|
.sidebar-content {
|
||||||
|
padding: var(--spacing-md);
|
||||||
|
gap: var(--spacing-md);
|
||||||
|
max-height: calc(70vh - 80px);
|
||||||
|
overflow-y: auto;
|
||||||
|
padding-bottom: calc(var(--spacing-lg) + env(safe-area-inset-bottom, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
.section {
|
||||||
|
padding: var(--spacing-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-header h2 {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 位置列表 */
|
||||||
|
.locations-section {
|
||||||
|
display: none; /* 移动端隐藏位置列表,只在地图上显示 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-section .section-header {
|
||||||
|
display: none; /* 简化标题 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 快捷标签 */
|
||||||
|
.quick-tags {
|
||||||
|
gap: 6px;
|
||||||
|
margin-top: var(--spacing-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag {
|
||||||
|
padding: 8px 12px;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
margin-bottom: var(--spacing-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group label {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 隐藏侧边栏中的搜索按钮(使用底部操作栏的按钮) */
|
||||||
|
.btn-search {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 中心点信息 */
|
||||||
|
.center-info {
|
||||||
|
top: var(--spacing-md);
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.center-badge {
|
||||||
|
padding: 6px var(--spacing-md);
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 浮动结果面板 - 底部弹出,更紧凑 */
|
||||||
|
.floating-results {
|
||||||
|
position: fixed;
|
||||||
|
top: auto;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 100%;
|
||||||
|
max-height: 75vh;
|
||||||
|
border-radius: var(--radius-xl) var(--radius-xl) 0 0;
|
||||||
|
animation: slideUp 0.3s ease;
|
||||||
|
background: var(--bg-card) !important;
|
||||||
|
backdrop-filter: blur(16px);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideUp {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(100%);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.floating-results-header {
|
||||||
|
padding: var(--spacing-sm) var(--spacing-md);
|
||||||
|
padding-top: var(--spacing-lg);
|
||||||
|
border-radius: var(--radius-xl) var(--radius-xl) 0 0;
|
||||||
|
position: relative;
|
||||||
|
background: var(--bg-elevated);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 拖动指示条 */
|
||||||
|
.floating-results-header::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 8px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
width: 36px;
|
||||||
|
height: 4px;
|
||||||
|
background: var(--border-light);
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.floating-results-header h3 {
|
||||||
|
font-size: 1rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.floating-results-filter {
|
||||||
|
padding: 8px var(--spacing-md);
|
||||||
|
background: var(--bg-card);
|
||||||
|
}
|
||||||
|
|
||||||
|
.floating-results-filter input {
|
||||||
|
padding: 10px var(--spacing-md);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.floating-result-list {
|
||||||
|
padding: 10px 16px;
|
||||||
|
padding-bottom: calc(20px + env(safe-area-inset-bottom, 0));
|
||||||
|
gap: 10px;
|
||||||
|
background: var(--bg-card);
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 移动端列表项布局修复 - 使用 !important 覆盖基础样式 */
|
||||||
|
.floating-result-list > .floating-result-item {
|
||||||
|
display: grid !important;
|
||||||
|
grid-template-columns: 36px 1fr 65px !important;
|
||||||
|
gap: 10px !important;
|
||||||
|
align-items: center !important;
|
||||||
|
padding: 12px !important;
|
||||||
|
background: var(--bg-darker) !important;
|
||||||
|
border: 1px solid var(--border) !important;
|
||||||
|
border-radius: var(--radius-md) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.floating-result-list > .floating-result-item > .rank {
|
||||||
|
grid-column: 1 !important;
|
||||||
|
width: 36px !important;
|
||||||
|
height: 36px !important;
|
||||||
|
display: flex !important;
|
||||||
|
align-items: center !important;
|
||||||
|
justify-content: center !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.floating-result-list > .floating-result-item > .info {
|
||||||
|
grid-column: 2 !important;
|
||||||
|
display: flex !important;
|
||||||
|
flex-direction: column !important;
|
||||||
|
gap: 4px !important;
|
||||||
|
min-width: 0 !important;
|
||||||
|
overflow: hidden !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.floating-result-list > .floating-result-item > .info > .name {
|
||||||
|
font-size: 0.95rem !important;
|
||||||
|
font-weight: 600 !important;
|
||||||
|
color: var(--text-primary) !important;
|
||||||
|
white-space: nowrap !important;
|
||||||
|
overflow: hidden !important;
|
||||||
|
text-overflow: ellipsis !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.floating-result-list > .floating-result-item > .info > .address {
|
||||||
|
font-size: 0.8rem !important;
|
||||||
|
color: var(--text-muted) !important;
|
||||||
|
white-space: nowrap !important;
|
||||||
|
overflow: hidden !important;
|
||||||
|
text-overflow: ellipsis !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.floating-result-list > .floating-result-item > .info > .tel {
|
||||||
|
font-size: 0.8rem !important;
|
||||||
|
color: var(--info) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.floating-result-list > .floating-result-item > .distance-badge {
|
||||||
|
grid-column: 3 !important;
|
||||||
|
width: 65px !important;
|
||||||
|
min-width: 65px !important;
|
||||||
|
max-width: 65px !important;
|
||||||
|
padding: 8px 4px !important;
|
||||||
|
text-align: center !important;
|
||||||
|
display: flex !important;
|
||||||
|
flex-direction: column !important;
|
||||||
|
align-items: center !important;
|
||||||
|
justify-content: center !important;
|
||||||
|
background: rgba(16, 185, 129, 0.12) !important;
|
||||||
|
border-radius: var(--radius-sm) !important;
|
||||||
|
border: 1px solid rgba(16, 185, 129, 0.25) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.floating-result-list > .floating-result-item > .distance-badge > .num {
|
||||||
|
font-size: 1.1rem !important;
|
||||||
|
font-weight: 700 !important;
|
||||||
|
color: var(--success) !important;
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.floating-result-list > .floating-result-item > .distance-badge > .unit {
|
||||||
|
font-size: 0.7rem !important;
|
||||||
|
color: var(--success) !important;
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 信息窗口更紧凑 */
|
||||||
|
.info-window {
|
||||||
|
min-width: 200px;
|
||||||
|
max-width: 280px;
|
||||||
|
padding: var(--spacing-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-window h3 {
|
||||||
|
font-size: 0.95rem;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-window p {
|
||||||
|
font-size: 0.82rem;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-window .nav-btn {
|
||||||
|
padding: 10px 14px;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-window .nav-btn svg {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 地图标记更小 */
|
||||||
|
.custom-marker {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
font-size: 11px;
|
||||||
|
border-width: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center-marker {
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
font-size: 18px;
|
||||||
|
border-width: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.poi-marker {
|
||||||
|
width: 26px;
|
||||||
|
height: 26px;
|
||||||
|
font-size: 11px;
|
||||||
|
border-width: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========================================
|
||||||
|
小屏手机适配 (< 375px)
|
||||||
|
======================================== */
|
||||||
|
|
||||||
|
@media (max-width: 375px) {
|
||||||
|
.floating-results {
|
||||||
|
max-height: 60vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag {
|
||||||
|
padding: 6px 10px;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-action-bar .action-search-btn {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
padding: 8px var(--spacing-md);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========================================
|
||||||
|
横屏模式
|
||||||
|
======================================== */
|
||||||
|
|
||||||
|
@media (max-width: 768px) and (orientation: landscape) {
|
||||||
|
.sidebar {
|
||||||
|
max-height: 85vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-content {
|
||||||
|
max-height: calc(85vh - 70px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.floating-results {
|
.floating-results {
|
||||||
width: calc(100% - 32px);
|
max-height: 80vh;
|
||||||
max-height: 40vh;
|
width: 55%;
|
||||||
top: auto;
|
right: 0;
|
||||||
bottom: var(--spacing-md);
|
left: auto;
|
||||||
right: var(--spacing-md);
|
border-radius: var(--radius-lg) 0 0 var(--radius-lg);
|
||||||
left: var(--spacing-md);
|
}
|
||||||
|
|
||||||
|
.mobile-action-bar {
|
||||||
|
width: 45%;
|
||||||
|
left: 0;
|
||||||
|
right: auto;
|
||||||
|
border-radius: 0 var(--radius-lg) 0 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========================================
|
||||||
|
触摸优化
|
||||||
|
======================================== */
|
||||||
|
|
||||||
|
@media (hover: none) and (pointer: coarse) {
|
||||||
|
/* 增大触摸目标 */
|
||||||
|
.tag {
|
||||||
|
min-height: 44px;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.location-remove {
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.floating-result-item {
|
||||||
|
min-height: 64px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 移除hover效果,改用active */
|
||||||
|
.floating-result-item:hover {
|
||||||
|
background: var(--bg-darker);
|
||||||
|
border-color: transparent;
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.floating-result-item:active {
|
||||||
|
background: var(--bg-card-hover);
|
||||||
|
border-color: var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag:hover {
|
||||||
|
background: var(--bg-card);
|
||||||
|
border-color: var(--border);
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag:active {
|
||||||
|
background: var(--bg-card-hover);
|
||||||
|
border-color: var(--primary);
|
||||||
|
color: var(--primary);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1029,8 +1839,7 @@ ul {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
animation: slideInRight 0.3s ease;
|
animation: slideInRight 0.3s ease;
|
||||||
backdrop-filter: blur(10px);
|
overflow: hidden;
|
||||||
background: rgba(26, 26, 46, 0.95);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes slideInRight {
|
@keyframes slideInRight {
|
||||||
@ -1128,6 +1937,7 @@ ul {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: var(--spacing-sm);
|
gap: var(--spacing-sm);
|
||||||
|
background: var(--bg-card);
|
||||||
}
|
}
|
||||||
|
|
||||||
.floating-result-list::-webkit-scrollbar {
|
.floating-result-list::-webkit-scrollbar {
|
||||||
@ -1145,7 +1955,7 @@ ul {
|
|||||||
|
|
||||||
.floating-result-item {
|
.floating-result-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: stretch;
|
align-items: flex-start;
|
||||||
gap: var(--spacing-md);
|
gap: var(--spacing-md);
|
||||||
padding: var(--spacing-md);
|
padding: var(--spacing-md);
|
||||||
background: var(--bg-darker);
|
background: var(--bg-darker);
|
||||||
@ -1206,6 +2016,7 @@ ul {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
|
overflow: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
.floating-result-item .name {
|
.floating-result-item .name {
|
||||||
@ -1220,10 +2031,7 @@ ul {
|
|||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
display: -webkit-box;
|
word-break: break-word;
|
||||||
-webkit-line-clamp: 2;
|
|
||||||
-webkit-box-orient: vertical;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.floating-result-item .tel {
|
.floating-result-item .tel {
|
||||||
@ -1249,11 +2057,13 @@ ul {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
min-width: 52px;
|
min-width: 50px;
|
||||||
|
max-width: 55px;
|
||||||
padding: var(--spacing-sm);
|
padding: var(--spacing-sm);
|
||||||
background: rgba(16, 185, 129, 0.12);
|
background: rgba(16, 185, 129, 0.12);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
border: 1px solid rgba(16, 185, 129, 0.25);
|
border: 1px solid rgba(16, 185, 129, 0.25);
|
||||||
|
align-self: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.floating-result-item .distance-badge .num {
|
.floating-result-item .distance-badge .num {
|
||||||
|
|||||||
446
static/js/app.js
446
static/js/app.js
@ -34,8 +34,224 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||||||
await loadConfig();
|
await loadConfig();
|
||||||
initMap();
|
initMap();
|
||||||
bindEvents();
|
bindEvents();
|
||||||
|
initMobileUI();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// 移动端UI初始化
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
function initMobileUI() {
|
||||||
|
const sidebar = document.getElementById('sidebar');
|
||||||
|
const overlay = document.getElementById('sidebarOverlay');
|
||||||
|
const menuBtn = document.getElementById('menuBtn');
|
||||||
|
const mobileSearchBtn = document.getElementById('mobileSearchBtn');
|
||||||
|
const mobileActionBar = document.getElementById('mobileActionBar');
|
||||||
|
const mobileSearchInput = document.getElementById('mobileSearchInput');
|
||||||
|
const mobileSearchClear = document.getElementById('mobileSearchClear');
|
||||||
|
const mobileSearchTips = document.getElementById('mobileSearchTips');
|
||||||
|
const floatingResultBtn = document.getElementById('floatingResultBtn');
|
||||||
|
const floatingResultBadge = document.getElementById('floatingResultBadge');
|
||||||
|
|
||||||
|
if (!sidebar || !overlay) return;
|
||||||
|
|
||||||
|
// 切换侧边栏
|
||||||
|
function toggleSidebar() {
|
||||||
|
sidebar.classList.toggle('active');
|
||||||
|
overlay.classList.toggle('active');
|
||||||
|
// 打开侧边栏时隐藏浮动按钮
|
||||||
|
if (sidebar.classList.contains('active') && floatingResultBtn) {
|
||||||
|
floatingResultBtn.style.display = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭侧边栏
|
||||||
|
function closeSidebar() {
|
||||||
|
sidebar.classList.remove('active');
|
||||||
|
overlay.classList.remove('active');
|
||||||
|
// 关闭侧边栏时,如果有搜索结果且结果面板隐藏,显示浮动按钮
|
||||||
|
const panel = document.getElementById('floatingResults');
|
||||||
|
if (floatingResultBtn && state.currentPOIs && state.currentPOIs.length > 0 &&
|
||||||
|
(!panel || panel.style.display === 'none')) {
|
||||||
|
window.showFloatingResultBtn(state.currentPOIs.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 菜单按钮点击
|
||||||
|
if (menuBtn) {
|
||||||
|
menuBtn.addEventListener('click', toggleSidebar);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 遮罩点击关闭
|
||||||
|
overlay.addEventListener('click', closeSidebar);
|
||||||
|
|
||||||
|
// 移动端搜索按钮
|
||||||
|
if (mobileSearchBtn) {
|
||||||
|
mobileSearchBtn.addEventListener('click', handleSearch);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移动端地址搜索
|
||||||
|
if (mobileSearchInput) {
|
||||||
|
let searchTimeout;
|
||||||
|
|
||||||
|
mobileSearchInput.addEventListener('input', (e) => {
|
||||||
|
const value = e.target.value.trim();
|
||||||
|
mobileSearchClear.style.display = value ? 'block' : 'none';
|
||||||
|
|
||||||
|
clearTimeout(searchTimeout);
|
||||||
|
if (value.length >= 2) {
|
||||||
|
searchTimeout = setTimeout(() => fetchMobileSearchTips(value), 300);
|
||||||
|
} else {
|
||||||
|
mobileSearchTips.classList.remove('active');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mobileSearchInput.addEventListener('focus', () => {
|
||||||
|
if (mobileSearchInput.value.trim().length >= 2) {
|
||||||
|
mobileSearchTips.classList.add('active');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mobileSearchClear.addEventListener('click', () => {
|
||||||
|
mobileSearchInput.value = '';
|
||||||
|
mobileSearchClear.style.display = 'none';
|
||||||
|
mobileSearchTips.classList.remove('active');
|
||||||
|
});
|
||||||
|
|
||||||
|
// 点击外部关闭搜索提示
|
||||||
|
document.addEventListener('click', (e) => {
|
||||||
|
if (!e.target.closest('.mobile-search-bar')) {
|
||||||
|
mobileSearchTips.classList.remove('active');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 浮动结果按钮点击 - 重新打开结果面板
|
||||||
|
if (floatingResultBtn) {
|
||||||
|
floatingResultBtn.addEventListener('click', () => {
|
||||||
|
const panel = document.getElementById('floatingResults');
|
||||||
|
if (panel && state.currentPOIs.length > 0) {
|
||||||
|
panel.style.display = 'flex';
|
||||||
|
floatingResultBtn.style.display = 'none';
|
||||||
|
if (mobileActionBar) {
|
||||||
|
mobileActionBar.style.display = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 点击搜索结果后自动关闭侧边栏(移动端)
|
||||||
|
window.closeSidebarOnMobile = function() {
|
||||||
|
if (window.innerWidth <= 768) {
|
||||||
|
closeSidebar();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 隐藏/显示移动端操作栏
|
||||||
|
window.toggleMobileActionBar = function(show) {
|
||||||
|
if (mobileActionBar && window.innerWidth <= 768) {
|
||||||
|
mobileActionBar.style.display = show ? 'flex' : 'none';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 显示浮动结果按钮
|
||||||
|
window.showFloatingResultBtn = function(count) {
|
||||||
|
if (floatingResultBtn) {
|
||||||
|
// 清除所有内联样式,让CSS控制
|
||||||
|
floatingResultBtn.style.cssText = 'display: flex;';
|
||||||
|
if (floatingResultBadge) {
|
||||||
|
floatingResultBadge.textContent = count;
|
||||||
|
}
|
||||||
|
console.log('显示浮动结果按钮,数量:', count);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 隐藏浮动结果按钮
|
||||||
|
window.hideFloatingResultBtn = function() {
|
||||||
|
if (floatingResultBtn) {
|
||||||
|
floatingResultBtn.style.display = 'none';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 监听窗口大小变化
|
||||||
|
window.addEventListener('resize', () => {
|
||||||
|
if (window.innerWidth > 768) {
|
||||||
|
closeSidebar();
|
||||||
|
if (mobileActionBar) {
|
||||||
|
mobileActionBar.style.display = 'none';
|
||||||
|
}
|
||||||
|
if (floatingResultBtn) {
|
||||||
|
floatingResultBtn.style.display = 'none';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const floatingResults = document.getElementById('floatingResults');
|
||||||
|
const resultsHidden = !floatingResults || floatingResults.style.display === 'none';
|
||||||
|
|
||||||
|
if (mobileActionBar && resultsHidden) {
|
||||||
|
mobileActionBar.style.display = 'flex';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果有搜索结果且结果面板隐藏,显示浮动按钮
|
||||||
|
if (resultsHidden && state.currentPOIs && state.currentPOIs.length > 0) {
|
||||||
|
window.showFloatingResultBtn(state.currentPOIs.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移动端地址搜索提示
|
||||||
|
async function fetchMobileSearchTips(keywords) {
|
||||||
|
const mobileSearchTips = document.getElementById('mobileSearchTips');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/api/tips?keywords=${encodeURIComponent(keywords)}&city=`);
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
if (data.tips && data.tips.length > 0) {
|
||||||
|
renderMobileSearchTips(data.tips);
|
||||||
|
} else {
|
||||||
|
mobileSearchTips.classList.remove('active');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取搜索提示失败:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderMobileSearchTips(tips) {
|
||||||
|
const mobileSearchTips = document.getElementById('mobileSearchTips');
|
||||||
|
const mobileSearchInput = document.getElementById('mobileSearchInput');
|
||||||
|
const mobileSearchClear = document.getElementById('mobileSearchClear');
|
||||||
|
|
||||||
|
mobileSearchTips.innerHTML = tips.map(tip => `
|
||||||
|
<div class="mobile-search-tip-item" data-lng="${tip.location.lng}" data-lat="${tip.location.lat}" data-name="${tip.name}" data-address="${tip.district}${tip.address}">
|
||||||
|
<div class="tip-name">${tip.name}</div>
|
||||||
|
<div class="tip-address">${tip.district}${tip.address}</div>
|
||||||
|
</div>
|
||||||
|
`).join('');
|
||||||
|
|
||||||
|
// 绑定点击事件
|
||||||
|
mobileSearchTips.querySelectorAll('.mobile-search-tip-item').forEach(item => {
|
||||||
|
item.addEventListener('click', () => {
|
||||||
|
const location = {
|
||||||
|
name: item.dataset.name,
|
||||||
|
address: item.dataset.address,
|
||||||
|
coordinate: {
|
||||||
|
lng: parseFloat(item.dataset.lng),
|
||||||
|
lat: parseFloat(item.dataset.lat)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
addLocation(location);
|
||||||
|
|
||||||
|
// 清理搜索框
|
||||||
|
mobileSearchInput.value = '';
|
||||||
|
mobileSearchClear.style.display = 'none';
|
||||||
|
mobileSearchTips.classList.remove('active');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
mobileSearchTips.classList.add('active');
|
||||||
|
}
|
||||||
|
|
||||||
async function loadConfig() {
|
async function loadConfig() {
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api/config');
|
const response = await fetch('/api/config');
|
||||||
@ -84,16 +300,17 @@ function initMap() {
|
|||||||
// 地图点击事件
|
// 地图点击事件
|
||||||
state.map.on('click', handleMapClick);
|
state.map.on('click', handleMapClick);
|
||||||
|
|
||||||
// 尝试定位到用户位置
|
// 使用高德IP定位,只定位地图中心,不自动添加位置
|
||||||
AMap.plugin('AMap.Geolocation', () => {
|
AMap.plugin('AMap.CitySearch', () => {
|
||||||
const geolocation = new AMap.Geolocation({
|
const citySearch = new AMap.CitySearch();
|
||||||
enableHighAccuracy: true,
|
citySearch.getLocalCity((status, result) => {
|
||||||
timeout: 10000
|
if (status === 'complete' && result.info === 'OK') {
|
||||||
});
|
// IP定位成功,将地图移动到当前城市
|
||||||
|
const bounds = result.bounds;
|
||||||
geolocation.getCurrentPosition((status, result) => {
|
if (bounds) {
|
||||||
if (status === 'complete') {
|
state.map.setBounds(bounds);
|
||||||
state.map.setCenter([result.position.lng, result.position.lat]);
|
}
|
||||||
|
console.log('已定位到城市:', result.city);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -220,10 +437,13 @@ function renderSearchTips(tips) {
|
|||||||
// 地图点击添加位置
|
// 地图点击添加位置
|
||||||
// ========================================
|
// ========================================
|
||||||
|
|
||||||
|
// 待确认的位置
|
||||||
|
let pendingLocation = null;
|
||||||
|
|
||||||
function handleMapClick(e) {
|
function handleMapClick(e) {
|
||||||
const lnglat = e.lnglat;
|
const lnglat = e.lnglat;
|
||||||
|
|
||||||
// 逆地理编码获取地址
|
// 逆地理编码获取地址,然后直接添加
|
||||||
AMap.plugin('AMap.Geocoder', () => {
|
AMap.plugin('AMap.Geocoder', () => {
|
||||||
const geocoder = new AMap.Geocoder();
|
const geocoder = new AMap.Geocoder();
|
||||||
|
|
||||||
@ -238,12 +458,50 @@ function handleMapClick(e) {
|
|||||||
lat: lnglat.lat
|
lat: lnglat.lat
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 直接添加位置
|
||||||
addLocation(location);
|
addLocation(location);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 显示地图点击确认弹框
|
||||||
|
function showMapClickConfirm(address) {
|
||||||
|
const confirmDialog = document.getElementById('mapClickConfirm');
|
||||||
|
const confirmAddress = document.getElementById('confirmAddress');
|
||||||
|
const confirmOk = document.getElementById('confirmOk');
|
||||||
|
const confirmCancel = document.getElementById('confirmCancel');
|
||||||
|
|
||||||
|
if (!confirmDialog) return;
|
||||||
|
|
||||||
|
confirmAddress.textContent = address;
|
||||||
|
confirmDialog.style.display = 'flex';
|
||||||
|
|
||||||
|
// 确认添加
|
||||||
|
confirmOk.onclick = () => {
|
||||||
|
if (pendingLocation) {
|
||||||
|
addLocation(pendingLocation);
|
||||||
|
pendingLocation = null;
|
||||||
|
}
|
||||||
|
confirmDialog.style.display = 'none';
|
||||||
|
};
|
||||||
|
|
||||||
|
// 取消
|
||||||
|
confirmCancel.onclick = () => {
|
||||||
|
pendingLocation = null;
|
||||||
|
confirmDialog.style.display = 'none';
|
||||||
|
};
|
||||||
|
|
||||||
|
// 点击背景关闭
|
||||||
|
confirmDialog.onclick = (e) => {
|
||||||
|
if (e.target === confirmDialog) {
|
||||||
|
pendingLocation = null;
|
||||||
|
confirmDialog.style.display = 'none';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
// 位置管理
|
// 位置管理
|
||||||
// ========================================
|
// ========================================
|
||||||
@ -343,7 +601,27 @@ function updateLocationList() {
|
|||||||
|
|
||||||
function updateSearchButton() {
|
function updateSearchButton() {
|
||||||
const btn = document.getElementById('searchBtn');
|
const btn = document.getElementById('searchBtn');
|
||||||
btn.disabled = state.locations.length < 2;
|
const mobileBtn = document.getElementById('mobileSearchBtn');
|
||||||
|
const mobileCount = document.getElementById('mobileLocationCount');
|
||||||
|
|
||||||
|
const count = state.locations.length;
|
||||||
|
const disabled = count < 2;
|
||||||
|
btn.disabled = disabled;
|
||||||
|
|
||||||
|
if (mobileBtn) {
|
||||||
|
mobileBtn.disabled = disabled;
|
||||||
|
// 更新按钮文字显示当前状态
|
||||||
|
if (count === 0) {
|
||||||
|
mobileBtn.textContent = '🔍 搜索会面点';
|
||||||
|
} else if (count === 1) {
|
||||||
|
mobileBtn.textContent = '🔍 再添加1个位置';
|
||||||
|
} else {
|
||||||
|
mobileBtn.textContent = '🔍 搜索会面点';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mobileCount) {
|
||||||
|
mobileCount.textContent = count;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
@ -355,7 +633,23 @@ async function handleSearch() {
|
|||||||
const radius = parseInt(document.getElementById('searchRadius').value);
|
const radius = parseInt(document.getElementById('searchRadius').value);
|
||||||
|
|
||||||
if (!keywords) {
|
if (!keywords) {
|
||||||
alert('请输入搜索关键词');
|
// 自动打开侧边栏设置面板
|
||||||
|
const sidebar = document.getElementById('sidebar');
|
||||||
|
const overlay = document.getElementById('sidebarOverlay');
|
||||||
|
if (sidebar && window.innerWidth <= 768) {
|
||||||
|
sidebar.classList.add('active');
|
||||||
|
if (overlay) overlay.classList.add('active');
|
||||||
|
}
|
||||||
|
// 聚焦到关键词输入框
|
||||||
|
const poiInput = document.getElementById('poiKeywords');
|
||||||
|
if (poiInput) {
|
||||||
|
poiInput.focus();
|
||||||
|
poiInput.setAttribute('placeholder', '⚠️ 请选择或输入搜索类型...');
|
||||||
|
// 3秒后恢复原来的placeholder
|
||||||
|
setTimeout(() => {
|
||||||
|
poiInput.setAttribute('placeholder', '例如:咖啡馆、餐厅、KTV...');
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -396,6 +690,11 @@ async function handleSearch() {
|
|||||||
// 显示搜索结果
|
// 显示搜索结果
|
||||||
showSearchResults(searchData.pois, radius);
|
showSearchResults(searchData.pois, radius);
|
||||||
|
|
||||||
|
// 移动端自动关闭侧边栏
|
||||||
|
if (window.closeSidebarOnMobile) {
|
||||||
|
window.closeSidebarOnMobile();
|
||||||
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('搜索失败:', error);
|
console.error('搜索失败:', error);
|
||||||
alert('搜索失败,请稍后重试');
|
alert('搜索失败,请稍后重试');
|
||||||
@ -420,8 +719,8 @@ function showCenterMarker() {
|
|||||||
|
|
||||||
state.map.add(state.centerMarker);
|
state.map.add(state.centerMarker);
|
||||||
|
|
||||||
// 显示中心点信息
|
// 不显示中心点浮动提示
|
||||||
document.getElementById('centerInfo').style.display = 'block';
|
// document.getElementById('centerInfo').style.display = 'block';
|
||||||
}
|
}
|
||||||
|
|
||||||
function showSearchResults(pois, radius) {
|
function showSearchResults(pois, radius) {
|
||||||
@ -484,8 +783,8 @@ function renderResultList(pois) {
|
|||||||
|
|
||||||
if (pois.length === 0) {
|
if (pois.length === 0) {
|
||||||
list.innerHTML = `
|
list.innerHTML = `
|
||||||
<li class="floating-result-empty">
|
<li style="display:flex;flex-direction:column;align-items:center;padding:32px;color:#64748b;text-align:center;">
|
||||||
<span class="icon">😕</span>
|
<span style="font-size:2.5rem;opacity:0.5;">😕</span>
|
||||||
<span>未找到相关地点</span>
|
<span>未找到相关地点</span>
|
||||||
<span style="font-size:0.8rem;opacity:0.7;">尝试增大搜索半径或更换关键词</span>
|
<span style="font-size:0.8rem;opacity:0.7;">尝试增大搜索半径或更换关键词</span>
|
||||||
</li>
|
</li>
|
||||||
@ -493,38 +792,83 @@ function renderResultList(pois) {
|
|||||||
} else {
|
} else {
|
||||||
list.innerHTML = pois.map((poi, index) => {
|
list.innerHTML = pois.map((poi, index) => {
|
||||||
const dist = parseDistance(poi.distance);
|
const dist = parseDistance(poi.distance);
|
||||||
return `
|
const rankColors = [
|
||||||
<li class="floating-result-item" data-index="${index}" data-name="${poi.name}">
|
'background:linear-gradient(135deg,#ffd700,#ffb800);color:#0f0f1a',
|
||||||
<div class="rank">${index + 1}</div>
|
'background:linear-gradient(135deg,#c0c0c0,#a0a0a0);color:#0f0f1a',
|
||||||
<div class="info">
|
'background:linear-gradient(135deg,#cd7f32,#b87333);color:#0f0f1a'
|
||||||
<div class="name">${poi.name}</div>
|
];
|
||||||
<div class="address">${poi.address || poi.type}</div>
|
const rankBg = rankColors[index] || 'background:#16213e;color:#64748b';
|
||||||
${poi.tel ? `<div class="tel">📞 <a href="tel:${poi.tel}" onclick="event.stopPropagation()">${poi.tel}</a></div>` : ''}
|
|
||||||
</div>
|
|
||||||
<div class="distance-badge">
|
|
||||||
<span class="num">${dist.value}</span>
|
|
||||||
<span class="unit">${dist.unit}</span>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
`}).join('');
|
|
||||||
|
|
||||||
// 绑定点击事件
|
return `<li data-index="${index}" data-name="${poi.name}" onclick="focusPOI(${index})" style="
|
||||||
list.querySelectorAll('.floating-result-item').forEach(item => {
|
display: flex;
|
||||||
item.addEventListener('click', (e) => {
|
flex-direction: row;
|
||||||
e.preventDefault();
|
align-items: center;
|
||||||
e.stopPropagation();
|
padding: 12px;
|
||||||
const index = parseInt(item.dataset.index);
|
margin-bottom: 8px;
|
||||||
focusPOI(index);
|
background: #0a0a12;
|
||||||
});
|
border: 1px solid #2d2d44;
|
||||||
});
|
border-radius: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
">
|
||||||
|
<span style="
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
margin-right: 12px;
|
||||||
|
${rankBg};
|
||||||
|
">${index + 1}</span>
|
||||||
|
<span style="
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
margin-right: 10px;
|
||||||
|
">
|
||||||
|
<span style="display:block;font-weight:600;font-size:0.95rem;color:#f8fafc;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">${poi.name}</span>
|
||||||
|
<span style="display:block;font-size:0.8rem;color:#64748b;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">${poi.address || poi.type}</span>
|
||||||
|
${poi.tel ? `<span style="display:block;font-size:0.8rem;color:#3b82f6;">📞 ${poi.tel}</span>` : ''}
|
||||||
|
</span>
|
||||||
|
<span style="
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: 55px;
|
||||||
|
padding: 6px 4px;
|
||||||
|
text-align: center;
|
||||||
|
background: rgba(16,185,129,0.12);
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid rgba(16,185,129,0.25);
|
||||||
|
">
|
||||||
|
<span style="display:block;font-size:1rem;font-weight:700;color:#10b981;">${dist.value}</span>
|
||||||
|
<span style="display:block;font-size:0.7rem;color:#10b981;">${dist.unit}</span>
|
||||||
|
</span>
|
||||||
|
</li>`;
|
||||||
|
}).join('');
|
||||||
|
|
||||||
|
// 点击事件已通过 onclick 内联绑定
|
||||||
}
|
}
|
||||||
|
|
||||||
panel.style.display = 'flex';
|
panel.style.display = 'flex';
|
||||||
filterInput.value = '';
|
filterInput.value = '';
|
||||||
|
|
||||||
|
// 移动端隐藏操作栏
|
||||||
|
if (window.toggleMobileActionBar) {
|
||||||
|
window.toggleMobileActionBar(false);
|
||||||
|
}
|
||||||
|
|
||||||
// 绑定关闭按钮事件
|
// 绑定关闭按钮事件
|
||||||
document.getElementById('closeFloatingResults').onclick = () => {
|
document.getElementById('closeFloatingResults').onclick = () => {
|
||||||
panel.style.display = 'none';
|
panel.style.display = 'none';
|
||||||
|
// 移动端显示操作栏和浮动结果按钮
|
||||||
|
if (window.toggleMobileActionBar) {
|
||||||
|
window.toggleMobileActionBar(true);
|
||||||
|
}
|
||||||
|
if (window.showFloatingResultBtn && state.currentPOIs.length > 0) {
|
||||||
|
window.showFloatingResultBtn(state.currentPOIs.length);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 绑定筛选事件
|
// 绑定筛选事件
|
||||||
@ -592,6 +936,20 @@ function focusPOI(index) {
|
|||||||
state.map.setZoom(16);
|
state.map.setZoom(16);
|
||||||
showPOIInfoWindow(marker, poi);
|
showPOIInfoWindow(marker, poi);
|
||||||
}, 300);
|
}, 300);
|
||||||
|
|
||||||
|
// 移动端:关闭结果面板,显示浮动按钮
|
||||||
|
if (window.innerWidth <= 768) {
|
||||||
|
const panel = document.getElementById('floatingResults');
|
||||||
|
if (panel) {
|
||||||
|
panel.style.display = 'none';
|
||||||
|
}
|
||||||
|
if (window.showFloatingResultBtn) {
|
||||||
|
window.showFloatingResultBtn(state.currentPOIs.length);
|
||||||
|
}
|
||||||
|
if (window.toggleMobileActionBar) {
|
||||||
|
window.toggleMobileActionBar(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -656,6 +1014,14 @@ function clearSearchResults() {
|
|||||||
document.getElementById('centerInfo').style.display = 'none';
|
document.getElementById('centerInfo').style.display = 'none';
|
||||||
document.getElementById('resultFilter').value = '';
|
document.getElementById('resultFilter').value = '';
|
||||||
|
|
||||||
|
// 移动端显示操作栏,隐藏浮动按钮
|
||||||
|
if (window.toggleMobileActionBar) {
|
||||||
|
window.toggleMobileActionBar(true);
|
||||||
|
}
|
||||||
|
if (window.hideFloatingResultBtn) {
|
||||||
|
window.hideFloatingResultBtn();
|
||||||
|
}
|
||||||
|
|
||||||
// 清除标记
|
// 清除标记
|
||||||
clearPOIMarkers();
|
clearPOIMarkers();
|
||||||
|
|
||||||
|
|||||||
@ -2,17 +2,23 @@
|
|||||||
<html lang="zh-CN">
|
<html lang="zh-CN">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||||
|
<meta name="theme-color" content="#0f0f1a">
|
||||||
<title>会面点 - 寻找最佳聚会地点</title>
|
<title>会面点 - 寻找最佳聚会地点</title>
|
||||||
<link rel="stylesheet" href="/static/css/style.css">
|
<link rel="stylesheet" href="/static/css/style.css?v=20260112d">
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="app-container">
|
<div class="app-container">
|
||||||
|
<!-- 侧边栏遮罩 -->
|
||||||
|
<div class="sidebar-overlay" id="sidebarOverlay"></div>
|
||||||
|
|
||||||
<!-- 侧边栏 -->
|
<!-- 侧边栏 -->
|
||||||
<aside class="sidebar">
|
<aside class="sidebar" id="sidebar">
|
||||||
<div class="logo">
|
<div class="logo">
|
||||||
<div class="logo-icon">
|
<div class="logo-icon">
|
||||||
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
@ -81,8 +87,8 @@
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="searchRadius">搜索半径</label>
|
<label for="searchRadius">搜索半径</label>
|
||||||
<div class="radius-slider">
|
<div class="radius-slider">
|
||||||
<input type="range" id="searchRadius" min="500" max="10000" step="500" value="3000">
|
<input type="range" id="searchRadius" min="500" max="10000" step="500" value="1000">
|
||||||
<span class="radius-value" id="radiusValue">3 公里</span>
|
<span class="radius-value" id="radiusValue">1 公里</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -102,6 +108,34 @@
|
|||||||
<main class="map-area">
|
<main class="map-area">
|
||||||
<div id="mapContainer"></div>
|
<div id="mapContainer"></div>
|
||||||
|
|
||||||
|
<!-- 移动端搜索框 -->
|
||||||
|
<div class="mobile-search-bar" id="mobileSearchBar">
|
||||||
|
<div class="mobile-search-input-wrapper">
|
||||||
|
<input type="text" id="mobileSearchInput" placeholder="🔍 搜索地址添加位置...">
|
||||||
|
<button class="mobile-search-clear" id="mobileSearchClear" style="display:none;">×</button>
|
||||||
|
</div>
|
||||||
|
<div class="mobile-search-tips" id="mobileSearchTips"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 地图点击确认弹框 -->
|
||||||
|
<div class="map-click-confirm" id="mapClickConfirm" style="display:none;">
|
||||||
|
<div class="confirm-content">
|
||||||
|
<div class="confirm-title">添加此位置?</div>
|
||||||
|
<div class="confirm-address" id="confirmAddress">加载中...</div>
|
||||||
|
<div class="confirm-actions">
|
||||||
|
<button class="confirm-btn cancel" id="confirmCancel">取消</button>
|
||||||
|
<button class="confirm-btn ok" id="confirmOk">添加</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 浮动结果按钮 -->
|
||||||
|
<button class="floating-result-btn" id="floatingResultBtn">
|
||||||
|
<span class="result-icon">📋</span>
|
||||||
|
<span class="result-text">查看结果</span>
|
||||||
|
<span class="result-badge" id="floatingResultBadge">0</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
<!-- 中心点信息 -->
|
<!-- 中心点信息 -->
|
||||||
<div class="center-info" id="centerInfo" style="display: none;">
|
<div class="center-info" id="centerInfo" style="display: none;">
|
||||||
<div class="center-badge">
|
<div class="center-badge">
|
||||||
@ -123,6 +157,19 @@
|
|||||||
<ul class="floating-result-list" id="floatingResultList"></ul>
|
<ul class="floating-result-list" id="floatingResultList"></ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 移动端快捷操作栏 -->
|
||||||
|
<div class="mobile-action-bar" id="mobileActionBar">
|
||||||
|
<div class="action-row">
|
||||||
|
<button class="menu-btn" id="menuBtn" aria-label="设置">⚙️</button>
|
||||||
|
<button class="action-search-btn" id="mobileSearchBtn" disabled>
|
||||||
|
🔍 搜索会面点
|
||||||
|
</button>
|
||||||
|
<div class="action-info">
|
||||||
|
<span class="location-badge" id="mobileLocationCount">0</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 加载提示 -->
|
<!-- 加载提示 -->
|
||||||
<div class="loading-overlay" id="loadingOverlay" style="display: none;">
|
<div class="loading-overlay" id="loadingOverlay" style="display: none;">
|
||||||
<div class="loading-spinner"></div>
|
<div class="loading-spinner"></div>
|
||||||
@ -132,6 +179,6 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 应用脚本 -->
|
<!-- 应用脚本 -->
|
||||||
<script src="/static/js/app.js"></script>
|
<script src="/static/js/app.js?v=20260112h"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user