index.html · 约 11686 字符
1<!DOCTYPE html>
2<html lang="zh-CN">
3<head>
4<meta charset="UTF-8">
5<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no">
6<title>日历选择</title>
7<script src="../_vendor/jquery.min.js"></script>
8<style>
9 * { margin:0; padding:0; box-sizing:border-box; -webkit-tap-highlight-color:transparent; }
10 :root{
11 --blue:#2563eb; --blue2:#1d4ed8; --sky:#60a5fa; --light:#eff6ff;
12 --bg:#f4f7fb; --card:#ffffff; --text:#1e293b; --mute:#94a3b8; --line:#e2e8f0;
13 }
14 body{ font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC","Microsoft YaHei",sans-serif;
15 background:#d6e3f5; color:var(--text); }
16 .app{ max-width:430px; margin:0 auto; min-height:100vh; background:var(--bg);
17 position:relative; box-shadow:0 0 28px rgba(0,0,0,.08); display:flex; flex-direction:column; }
18 .topbar{ background:linear-gradient(120deg,var(--blue),var(--blue2)); color:#fff;
19 padding:16px 18px 14px; display:flex; align-items:center; gap:12px; flex-shrink:0; }
20 .topbar .back{ font-size:28px; cursor:pointer; }
21 .topbar .tt{ font-size:18px; font-weight:800; letter-spacing:1px; flex:1; text-align:center; margin-right:28px; }
22 .head-area{ background:linear-gradient(120deg,var(--blue),var(--blue2)); color:#fff; padding:0 18px 26px; flex-shrink:0; }
23 .date-cards{ display:flex; gap:11px; }
24 .date-card{ flex:1; background:rgba(255,255,255,.95); border-radius:16px; padding:14px; color:var(--text); }
25 .date-card .lab{ font-size:12px; color:var(--mute); font-weight:700; margin-bottom:8px; display:flex; align-items:center; gap:6px; }
26 .date-card .val{ font-size:16px; font-weight:900; color:var(--text); }
27 .date-card .placeholder{ font-size:14px; font-weight:700; color:var(--blue); display:flex; align-items:center; gap:5px; }
28 .date-card.on{ border:2px solid #fff; background:#fff; box-shadow:0 6px 18px rgba(0,0,0,.12); }
29 .date-card.on .val{ color:var(--blue); }
30 .sheet{ background:var(--card); border-radius:24px 24px 0 0; margin-top:-18px; padding:18px 18px 100px; flex:1; position:relative; }
31 .sheet .close-bar{ display:flex; align-items:center; justify-content:space-between; margin-bottom:10px; }
32 .sheet .close-bar .x{ font-size:22px; color:var(--mute); cursor:pointer; }
33 .sheet .close-bar .tit{ font-size:16px; font-weight:900; flex:1; text-align:center; margin-right:22px; }
34 .month-row{ display:flex; align-items:center; justify-content:space-between; margin:18px 0 14px; }
35 .month-row .arrow{ width:34px; height:34px; border-radius:50%; background:var(--light); color:var(--blue); display:flex; align-items:center; justify-content:center; font-size:18px; cursor:pointer; font-weight:800; }
36 .month-row .month{ font-size:18px; font-weight:900; color:var(--text); }
37 .weekdays{ display:grid; grid-template-columns:repeat(7,1fr); text-align:center; margin-bottom:8px; }
38 .weekdays span{ font-size:12px; font-weight:700; color:var(--mute); padding:8px 0; }
39 .days{ display:grid; grid-template-columns:repeat(7,1fr); gap:8px; text-align:center; }
40 .days .day{ aspect-ratio:1; display:flex; align-items:center; justify-content:center; border-radius:50%; font-size:15px; font-weight:700; cursor:pointer; color:var(--text); position:relative; }
41 .days .day.mute{ color:var(--mute); }
42 .days .day.today::after{ content:""; position:absolute; bottom:6px; left:50%; transform:translateX(-50%); width:4px; height:4px; border-radius:50%; background:var(--blue); }
43 .days .day.sel{ background:var(--blue); color:#fff; }
44 .days .day.range{ background:var(--light); color:var(--blue); }
45 .days .day.start::after,.days .day.end::after{ content:"出发"; position:absolute; bottom:-16px; left:50%; transform:translateX(-50%); font-size:9px; color:var(--blue); font-weight:700; }
46 .days .day.end::after{ content:"返程"; }
47 .bottom{ position:fixed; bottom:0; left:50%; transform:translateX(-50%); width:100%; max-width:430px; padding:14px 18px 22px; background:#fff; border-top:1px solid var(--line); }
48 .btn{ width:100%; padding:14px; border:none; border-radius:14px;
49 background:linear-gradient(120deg,var(--blue),var(--blue2)); color:#fff; font-size:16px;
50 font-weight:800; cursor:pointer; letter-spacing:1px; box-shadow:0 6px 16px rgba(37,99,235,.3); }
51 .btn:active{ transform:scale(.99); }
52 .toast{ position:fixed; left:50%; top:42%; transform:translate(-50%,-50%) scale(.9); z-index:50;
53 background:rgba(15,30,60,.92); color:#fff; padding:13px 22px; border-radius:12px; font-size:14px;
54 font-weight:700; opacity:0; pointer-events:none; transition:.2s; }
55 .toast.show{ opacity:1; transform:translate(-50%,-50%) scale(1); }
56 .summary{ display:none; background:var(--card); border-radius:18px; padding:18px; margin:18px; box-shadow:0 4px 18px rgba(30,60,120,.06); }
57 .summary .row{ display:flex; align-items:center; justify-content:space-between; margin-bottom:12px; }
58 .summary .row .l{ font-size:12px; color:var(--mute); font-weight:700; }
59 .summary .row .v{ font-size:15px; font-weight:900; }
60 .summary .days-count{ text-align:center; background:var(--light); border-radius:12px; padding:12px; color:var(--blue); font-weight:900; }
61</style>
62</head>
63<body>
64<div class="app" id="app">
65 <header class="topbar">
66 <span class="back" id="backBtn">‹</span>
67 <span class="tt">出发日期</span>
68 </header>
69
70 <div class="head-area">
71 <div class="date-cards">
72 <div class="date-card on" id="depCard">
73 <div class="lab">📅 出发日期</div>
74 <div class="val" id="depVal">2021/12/23</div>
75 </div>
76 <div class="date-card" id="retCard">
77 <div class="lab">📅 返程日期</div>
78 <div class="placeholder" id="retVal"><span>+</span> 添加返程</div>
79 </div>
80 </div>
81 </div>
82
83 <div class="sheet" id="sheet">
84 <div class="close-bar">
85 <span class="x" id="closeBtn">✕</span>
86 <span class="tit">选择您的行程</span>
87 </div>
88 <div class="month-row">
89 <span class="arrow" id="prevMonth">‹</span>
90 <span class="month" id="monthTit">2021年12月</span>
91 <span class="arrow" id="nextMonth">›</span>
92 </div>
93 <div class="weekdays">
94 <span>日</span><span>一</span><span>二</span><span>三</span><span>四</span><span>五</span><span>六</span>
95 </div>
96 <div class="days" id="daysBox"></div>
97 </div>
98
99 <div class="summary" id="summary">
100 <div class="row"><span class="l">出发</span><span class="v" id="sumDep">2021/12/23</span></div>
101 <div class="row"><span class="l">返程</span><span class="v" id="sumRet">—</span></div>
102 <div class="days-count" id="sumDays">共 1 天</div>
103 </div>
104
105 <div class="bottom" id="bottom">
106 <button class="btn" id="setBtn">设置日期</button>
107 </div>
108</div>
109<div class="toast" id="toast"></div>
110
111<script>
112var curY=2021, curM=11; // 2021-12 (0-indexed)
113var depD={y:2021,m:11,d:23}, retD=null;
114var selMode="dep"; // dep | ret
115var monthNames=["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"];
116function toast(m){ var t=$("#toast"); t.text(m).addClass("show"); setTimeout(function(){t.removeClass("show");},1500); }
117function fmt(d){ return d.y+"/"+String(d.m+1).padStart(2,"0")+"/"+String(d.d).padStart(2,"0"); }
118function eq(a,b){ return a&&b&&a.y===b.y&&a.m===b.m&&a.d===b.d; }
119function inRange(target, a, b){
120 if(!a||!b) return false;
121 var t=target.y*10000+(target.m+1)*100+target.d;
122 var s=a.y*10000+(a.m+1)*100+a.d;
123 var e=b.y*10000+(b.m+1)*100+b.d;
124 return t>s && t<e;
125}
126function daysInMonth(y,m){ return new Date(y,m+1,0).getDate(); }
127function firstDay(y,m){ return new Date(y,m,1).getDay(); }
128function render(){
129 $("#monthTit").text(curY+"年"+monthNames[curM]);
130 var total=daysInMonth(curY,curM), first=firstDay(curY,curM);
131 var prevTotal=daysInMonth(curY,curM-1);
132 var html="";
133 for(var i=first-1;i>=0;i--){
134 html+='<span class="day mute">'+(prevTotal-i)+'</span>';
135 }
136 for(var d=1;d<=total;d++){
137 var dayObj={y:curY,m:curM,d:d};
138 var cls="day";
139 var isToday=(curY===2021&&curM===11&&d===23);
140 if(isToday) cls+=" today";
141 if(eq(dayObj,depD)) cls+=" sel start";
142 else if(eq(dayObj,retD)) cls+=" sel end";
143 else if(inRange(dayObj,depD,retD)) cls+=" range";
144 html+='<span class="'+cls+'" data-d="'+d+'">'+d+'</span>';
145 }
146 var nextDays=42-(first+total);
147 for(var d=1;d<=nextDays;d++) html+='<span class="day mute">'+d+'</span>';
148 $("#daysBox").html(html);
149}
150function pickDate(d){
151 var picked={y:curY,m:curM,d:d};
152 if(selMode==="dep"){
153 depD=picked;
154 if(retD && (picked.y*10000+(picked.m+1)*100+picked.d) > (retD.y*10000+(retD.m+1)*100+retD.d)) retD=null;
155 $("#depVal").text(fmt(depD));
156 $("#retVal").html('<span>+</span> 添加返程');
157 selMode="ret";
158 $(".date-card").removeClass("on"); $("#retCard").addClass("on");
159 toast("已选出发日期,请选择返程");
160 }else{
161 if(!depD){ selMode="dep"; toast("请先选出发日期"); return; }
162 if((picked.y*10000+(picked.m+1)*100+picked.d) < (depD.y*10000+(depD.m+1)*100+depD.d)){
163 toast("返程不能早于出发"); return;
164 }
165 retD=picked;
166 $("#retVal").text(fmt(retD));
167 $(".date-card").removeClass("on"); $("#depCard").addClass("on");
168 selMode="dep";
169 toast("已选返程日期");
170 }
171 updateSummary();
172 render();
173}
174function updateSummary(){
175 if(!depD) return;
176 $("#sumDep").text(fmt(depD));
177 $("#sumRet").text(retD?fmt(retD):"—");
178 var days="1";
179 if(depD && retD){
180 var s=new Date(depD.y,depD.m,depD.d);
181 var e=new Date(retD.y,retD.m,retD.d);
182 days=Math.max(1,Math.round((e-s)/86400000)+1);
183 }
184 $("#sumDays").text("共 "+days+" 天");
185}
186function reset(){
187 depD={y:2021,m:11,d:23}; retD=null; selMode="dep";
188 curY=2021; curM=11;
189 $("#depVal").text(fmt(depD));
190 $("#retVal").html('<span>+</span> 添加返程');
191 $(".date-card").removeClass("on"); $("#depCard").addClass("on");
192 $("#summary").hide(); $("#sheet").show(); $("#bottom").show();
193 render();
194}
195$(function(){
196 render();
197 $(document).on("click","#daysBox .day:not(.mute)",function(){ pickDate(parseInt($(this).data("d"))); });
198 $("#prevMonth").on("click",function(){
199 curM--; if(curM<0){ curM=11; curY--; } render();
200 });
201 $("#nextMonth").on("click",function(){
202 curM++; if(curM>11){ curM=0; curY++; } render();
203 });
204 $("#depCard").on("click",function(){ selMode="dep"; $(".date-card").removeClass("on"); $(this).addClass("on"); });
205 $("#retCard").on("click",function(){
206 if(!depD){ toast("请先选出发日期"); return; }
207 selMode="ret"; $(".date-card").removeClass("on"); $(this).addClass("on");
208 });
209 $("#setBtn").on("click",function(){
210 $("#sheet").hide(); $("#bottom").hide();
211 $("#summary").show();
212 toast("日期设置成功");
213 });
214 $("#backBtn").on("click",function(){
215 if($("#summary").is(":visible")){ reset(); return; }
216 toast("返回上一级");
217 });
218 $("#closeBtn").on("click",function(){ toast("日历已关闭"); });
219});
220// 全局钩子供截图脚本调用
221window.setCalendarMonth = function(y,m){ curY=y; curM=m; render(); };
222window.selectCalendarDate = function(y,m,d){
223 curY=y; curM=m; depD={y:y,m:m,d:d}; retD=null; selMode="ret";
224 $("#depVal").text(fmt(depD)); $("#retVal").html('<span>+</span> 添加返程');
225 $(".date-card").removeClass("on"); $("#retCard").addClass("on");
226 updateSummary(); render();
227};
228window.showCalendarSummary = function(){
229 $("#setBtn").trigger("click");
230};
231</script>
232</body>
233</html>