Stroke.html 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687
  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.0">
  6. <title>智途 - 行程管家</title>
  7. <script type="module" src="https://cdn.jsdelivr.net/npm/@ionic/core/dist/ionic/ionic.esm.js"></script>
  8. <script nomodule src="https://cdn.jsdelivr.net/npm/@ionic/core/dist/ionic/ionic.js"></script>
  9. <link href="https://cdn.jsdelivr.net/npm/@ionic/core/css/ionic.bundle.css" rel="stylesheet">
  10. <style>
  11. :root {
  12. --ion-color-primary: #4CAF50;
  13. --ion-color-primary-rgb: 76,175,80;
  14. --ion-color-primary-contrast: #ffffff;
  15. --ion-color-primary-contrast-rgb: 255,255,255;
  16. --ion-color-primary-shade: #439a46;
  17. --ion-color-primary-tint: #5eb762;
  18. --ion-color-secondary: #FF9800;
  19. --ion-color-tertiary: #2196F3;
  20. }
  21. * {
  22. margin: 0;
  23. padding: 0;
  24. box-sizing: border-box;
  25. font-family: "PingFang SC", "Helvetica Neue", Arial, sans-serif;
  26. }
  27. body {
  28. background-color: #f5f5f5;
  29. color: #333;
  30. max-width: 100vw;
  31. overflow-x: hidden;
  32. padding-bottom: 60px;
  33. }
  34. /* 顶部标题栏 */
  35. .header {
  36. position: fixed;
  37. top: 0;
  38. left: 0;
  39. right: 0;
  40. height: 50px;
  41. background-color: var(--ion-color-primary);
  42. color: white;
  43. display: flex;
  44. justify-content: center;
  45. align-items: center;
  46. z-index: 100;
  47. box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
  48. }
  49. .header-title {
  50. font-size: 18px;
  51. font-weight: bold;
  52. }
  53. .header-back {
  54. position: absolute;
  55. left: 15px;
  56. font-size: 20px;
  57. cursor: pointer;
  58. }
  59. /* 内容区域 */
  60. .content {
  61. margin-top: 50px;
  62. padding: 15px;
  63. padding-bottom: 70px;
  64. }
  65. /* 模式切换 */
  66. .mode-switcher {
  67. display: flex;
  68. background: white;
  69. border-radius: 12px;
  70. overflow: hidden;
  71. margin-bottom: 20px;
  72. box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
  73. }
  74. .mode-tab {
  75. flex: 1;
  76. text-align: center;
  77. padding: 12px;
  78. font-size: 14px;
  79. color: #666;
  80. transition: all 0.3s;
  81. }
  82. .mode-tab.active {
  83. background-color: var(--ion-color-primary);
  84. color: white;
  85. font-weight: bold;
  86. }
  87. /* AI助手按钮 */
  88. .ai-button-container {
  89. margin-bottom: 20px;
  90. }
  91. .ai-toggle-button {
  92. width: 100%;
  93. background: white;
  94. border: none;
  95. border-radius: 12px;
  96. padding: 15px;
  97. display: flex;
  98. align-items: center;
  99. justify-content: space-between;
  100. box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
  101. cursor: pointer;
  102. }
  103. .ai-button-left {
  104. display: flex;
  105. align-items: center;
  106. }
  107. .ai-avatar {
  108. width: 40px;
  109. height: 40px;
  110. background: linear-gradient(135deg, #2196F3, #64B5F6);
  111. border-radius: 50%;
  112. display: flex;
  113. justify-content: center;
  114. align-items: center;
  115. color: white;
  116. margin-right: 10px;
  117. }
  118. .ai-button-text {
  119. text-align: left;
  120. }
  121. .ai-button-title {
  122. font-weight: bold;
  123. font-size: 16px;
  124. margin-bottom: 2px;
  125. }
  126. .ai-button-subtitle {
  127. font-size: 12px;
  128. color: #666;
  129. }
  130. /* AI对话区 (默认隐藏) */
  131. .ai-assistant {
  132. background: white;
  133. border-radius: 12px;
  134. padding: 15px;
  135. margin-bottom: 20px;
  136. box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
  137. display: none;
  138. }
  139. .ai-assistant.active {
  140. display: block;
  141. }
  142. .ai-header {
  143. display: flex;
  144. align-items: center;
  145. margin-bottom: 15px;
  146. }
  147. .ai-name {
  148. font-weight: bold;
  149. font-size: 16px;
  150. }
  151. .ai-status {
  152. font-size: 12px;
  153. color: #4CAF50;
  154. margin-left: auto;
  155. }
  156. .chat-container {
  157. height: 250px;
  158. overflow-y: auto;
  159. margin-bottom: 15px;
  160. padding-right: 5px;
  161. }
  162. .message {
  163. margin-bottom: 10px;
  164. display: flex;
  165. flex-direction: column;
  166. }
  167. .message-ai {
  168. align-items: flex-start;
  169. }
  170. .message-user {
  171. align-items: flex-end;
  172. }
  173. .message-bubble {
  174. max-width: 80%;
  175. padding: 10px 15px;
  176. border-radius: 18px;
  177. font-size: 14px;
  178. line-height: 1.4;
  179. }
  180. .ai-bubble {
  181. background: #f1f1f1;
  182. border-top-left-radius: 4px;
  183. }
  184. .user-bubble {
  185. background: var(--ion-color-primary);
  186. color: white;
  187. border-top-right-radius: 4px;
  188. }
  189. .message-time {
  190. font-size: 10px;
  191. color: #999;
  192. margin-top: 4px;
  193. }
  194. .input-area {
  195. display: flex;
  196. gap: 10px;
  197. }
  198. .message-input {
  199. flex: 1;
  200. border: 1px solid #eee;
  201. border-radius: 20px;
  202. padding: 10px 15px;
  203. font-size: 14px;
  204. outline: none;
  205. }
  206. .send-button {
  207. width: 40px;
  208. height: 40px;
  209. border-radius: 50%;
  210. background: var(--ion-color-primary);
  211. color: white;
  212. border: none;
  213. display: flex;
  214. justify-content: center;
  215. align-items: center;
  216. cursor: pointer;
  217. }
  218. /* 快速建议按钮 */
  219. .quick-suggestions {
  220. display: flex;
  221. flex-wrap: wrap;
  222. gap: 8px;
  223. margin-bottom: 15px;
  224. }
  225. .quick-suggestion {
  226. background: #f1f1f1;
  227. border-radius: 15px;
  228. padding: 8px 12px;
  229. font-size: 12px;
  230. cursor: pointer;
  231. transition: all 0.2s;
  232. }
  233. .quick-suggestion:hover {
  234. background: #e0e0e0;
  235. }
  236. /* 推荐行程 */
  237. .recommended-trips {
  238. margin-top: 20px;
  239. }
  240. .section-title {
  241. font-size: 16px;
  242. font-weight: bold;
  243. margin-bottom: 15px;
  244. display: flex;
  245. align-items: center;
  246. }
  247. .section-title ion-icon {
  248. margin-right: 8px;
  249. color: var(--ion-color-primary);
  250. }
  251. .trip-card {
  252. background: white;
  253. border-radius: 12px;
  254. overflow: hidden;
  255. margin-bottom: 15px;
  256. box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
  257. }
  258. .trip-image {
  259. width: 100%;
  260. height: 120px;
  261. object-fit: cover;
  262. }
  263. .trip-info {
  264. padding: 12px;
  265. }
  266. .trip-title {
  267. font-size: 16px;
  268. font-weight: bold;
  269. margin-bottom: 5px;
  270. }
  271. .trip-duration {
  272. font-size: 12px;
  273. color: #666;
  274. margin-bottom: 8px;
  275. display: flex;
  276. align-items: center;
  277. }
  278. .trip-duration ion-icon {
  279. margin-right: 5px;
  280. font-size: 14px;
  281. }
  282. .trip-tags {
  283. display: flex;
  284. flex-wrap: wrap;
  285. gap: 5px;
  286. margin-top: 10px;
  287. }
  288. .trip-tag {
  289. font-size: 10px;
  290. padding: 2px 6px;
  291. background-color: #e8f5e9;
  292. color: #2e7d32;
  293. border-radius: 10px;
  294. }
  295. /* 底部Tab栏 */
  296. .tab-bar {
  297. position: fixed;
  298. bottom: 0;
  299. left: 0;
  300. right: 0;
  301. height: 60px;
  302. background-color: white;
  303. display: flex;
  304. justify-content: space-around;
  305. align-items: center;
  306. box-shadow: 0 -2px 5px rgba(0, 0, 0, 0.05);
  307. z-index: 100;
  308. }
  309. .tab-item {
  310. display: flex;
  311. flex-direction: column;
  312. align-items: center;
  313. justify-content: center;
  314. color: #666;
  315. font-size: 12px;
  316. flex: 1;
  317. height: 100%;
  318. }
  319. .tab-item.active {
  320. color: var(--ion-color-primary);
  321. }
  322. .tab-item ion-icon {
  323. font-size: 20px;
  324. margin-bottom: 3px;
  325. }
  326. /* 加载动画 */
  327. .typing-indicator {
  328. display: flex;
  329. padding: 10px 15px;
  330. }
  331. .typing-dot {
  332. width: 8px;
  333. height: 8px;
  334. background: #ccc;
  335. border-radius: 50%;
  336. margin: 0 2px;
  337. animation: typing 1.4s infinite ease-in-out;
  338. }
  339. .typing-dot:nth-child(1) {
  340. animation-delay: 0s;
  341. }
  342. .typing-dot:nth-child(2) {
  343. animation-delay: 0.2s;
  344. }
  345. .typing-dot:nth-child(3) {
  346. animation-delay: 0.4s;
  347. }
  348. @keyframes typing {
  349. 0%, 60%, 100% {
  350. transform: translateY(0);
  351. }
  352. 30% {
  353. transform: translateY(-5px);
  354. }
  355. }
  356. /* 旋转箭头动画 */
  357. .toggle-arrow {
  358. transition: transform 0.3s ease;
  359. }
  360. .toggle-arrow.active {
  361. transform: rotate(180deg);
  362. }
  363. </style>
  364. </head>
  365. <body>
  366. <ion-app>
  367. <!-- 顶部标题栏 -->
  368. <div class="header">
  369. <ion-icon name="arrow-back" class="header-back"></ion-icon>
  370. <div class="header-title">行程管家</div>
  371. </div>
  372. <!-- 内容区域 -->
  373. <div class="content">
  374. <!-- 模式切换 -->
  375. <div class="mode-switcher">
  376. <div class="mode-tab active">智能规划</div>
  377. <div class="mode-tab">我的行程</div>
  378. <div class="mode-tab">收藏</div>
  379. </div>
  380. <!-- AI助手按钮 -->
  381. <div class="ai-button-container">
  382. <button class="ai-toggle-button" id="aiToggleButton">
  383. <div class="ai-button-left">
  384. <div class="ai-avatar">
  385. <ion-icon name="sparkles"></ion-icon>
  386. </div>
  387. <div class="ai-button-text">
  388. <div class="ai-button-title">咨询AI助手</div>
  389. <div class="ai-button-subtitle">获取个性化行程建议</div>
  390. </div>
  391. </div>
  392. <ion-icon name="chevron-down" class="toggle-arrow"></ion-icon>
  393. </button>
  394. </div>
  395. <!-- AI助手对话区 (默认隐藏) -->
  396. <div class="ai-assistant" id="aiAssistant">
  397. <div class="ai-header">
  398. <div class="ai-avatar">
  399. <ion-icon name="sparkles"></ion-icon>
  400. </div>
  401. <div class="ai-name">智途AI助手</div>
  402. <div class="ai-status">在线</div>
  403. </div>
  404. <!-- 快速建议 -->
  405. <div class="quick-suggestions">
  406. <div class="quick-suggestion">三日游推荐</div>
  407. <div class="quick-suggestion">当地美食</div>
  408. <div class="quick-suggestion">交通方式</div>
  409. <div class="quick-suggestion">景点门票</div>
  410. </div>
  411. <div class="chat-container" id="chatContainer">
  412. <div class="message message-ai">
  413. <div class="message-bubble ai-bubble">
  414. 您好!我是您的行程管家助手,可以为您推荐旅游路线、预订门票、解答旅行问题等。您今天想去哪里玩呢?
  415. </div>
  416. <div class="message-time">10:30</div>
  417. </div>
  418. </div>
  419. <div class="input-area">
  420. <input type="text" class="message-input" placeholder="输入您的问题..." id="messageInput">
  421. <button class="send-button" id="sendButton">
  422. <ion-icon name="send"></ion-icon>
  423. </button>
  424. </div>
  425. </div>
  426. <!-- 推荐行程 -->
  427. <div class="recommended-trips">
  428. <div class="section-title">
  429. <ion-icon name="map-outline"></ion-icon>
  430. 热门行程推荐
  431. </div>
  432. <div class="trip-card">
  433. <img src="https://picsum.photos/400/300?random=hangzhou" class="trip-image" alt="杭州行程">
  434. <div class="trip-info">
  435. <div class="trip-title">杭州经典三日游</div>
  436. <div class="trip-duration">
  437. <ion-icon name="time-outline"></ion-icon>
  438. 3天2晚 · 精华路线
  439. </div>
  440. <div class="trip-tags">
  441. <span class="trip-tag">西湖</span>
  442. <span class="trip-tag">灵隐寺</span>
  443. <span class="trip-tag">宋城</span>
  444. <span class="trip-tag">美食</span>
  445. </div>
  446. </div>
  447. </div>
  448. <div class="trip-card">
  449. <img src="https://picsum.photos/400/300?random=shanghai" class="trip-image" alt="上海行程">
  450. <div class="trip-info">
  451. <div class="trip-title">上海都市两日游</div>
  452. <div class="trip-duration">
  453. <ion-icon name="time-outline"></ion-icon>
  454. 2天1晚 · 城市探索
  455. </div>
  456. <div class="trip-tags">
  457. <span class="trip-tag">外滩</span>
  458. <span class="trip-tag">迪士尼</span>
  459. <span class="trip-tag">豫园</span>
  460. <span class="trip-tag">购物</span>
  461. </div>
  462. </div>
  463. </div>
  464. </div>
  465. </div>
  466. <!-- 底部Tab栏 -->
  467. <div class="tab-bar">
  468. <div class="tab-item">
  469. <ion-icon name="home-outline"></ion-icon>
  470. <span>首页</span>
  471. </div>
  472. <div class="tab-item active">
  473. <ion-icon name="calendar-outline"></ion-icon>
  474. <span>行程</span>
  475. </div>
  476. <div class="tab-item">
  477. <ion-icon name="compass-outline"></ion-icon>
  478. <span>发现</span>
  479. </div>
  480. <div class="tab-item">
  481. <ion-icon name="person-outline"></ion-icon>
  482. <span>我的</span>
  483. </div>
  484. </div>
  485. </ion-app>
  486. <script>
  487. // 获取DOM元素
  488. const aiToggleButton = document.getElementById('aiToggleButton');
  489. const aiAssistant = document.getElementById('aiAssistant');
  490. const toggleArrow = document.querySelector('.toggle-arrow');
  491. const sendButton = document.getElementById('sendButton');
  492. const messageInput = document.getElementById('messageInput');
  493. const chatContainer = document.getElementById('chatContainer');
  494. // 切换AI助手显示/隐藏
  495. aiToggleButton.addEventListener('click', () => {
  496. aiAssistant.classList.toggle('active');
  497. toggleArrow.classList.toggle('active');
  498. });
  499. // 获取当前时间
  500. function getCurrentTime() {
  501. const now = new Date();
  502. return `${now.getHours()}:${now.getMinutes().toString().padStart(2, '0')}`;
  503. }
  504. // 模拟AI回复
  505. function simulateAIResponse(message) {
  506. // 显示AI正在输入
  507. const typingIndicator = document.createElement('div');
  508. typingIndicator.className = 'message message-ai';
  509. typingIndicator.innerHTML = `
  510. <div class="typing-indicator">
  511. <div class="typing-dot"></div>
  512. <div class="typing-dot"></div>
  513. <div class="typing-dot"></div>
  514. </div>
  515. `;
  516. chatContainer.appendChild(typingIndicator);
  517. chatContainer.scrollTop = chatContainer.scrollHeight;
  518. // 模拟AI思考时间
  519. setTimeout(() => {
  520. typingIndicator.remove();
  521. let aiResponseText = "";
  522. // 根据用户消息生成不同回复
  523. if (message.includes("杭州") || message.includes("hangzhou")) {
  524. aiResponseText = `
  525. <strong>杭州三日游推荐行程:</strong><br><br>
  526. <strong>第一天:西湖景区</strong><br>
  527. - 断桥残雪<br>
  528. - 雷峰塔<br>
  529. - 三潭印月<br><br>
  530. <strong>第二天:文化体验</strong><br>
  531. - 灵隐寺<br>
  532. - 西溪湿地<br><br>
  533. <strong>第三天:娱乐休闲</strong><br>
  534. - 宋城主题公园<br>
  535. - 河坊街美食<br><br>
  536. 需要我帮您预订门票或酒店吗?
  537. `;
  538. } else if (message.includes("上海") || message.includes("shanghai")) {
  539. aiResponseText = `
  540. <strong>上海两日游推荐行程:</strong><br><br>
  541. <strong>第一天:城市地标</strong><br>
  542. - 外滩<br>
  543. - 东方明珠<br>
  544. - 南京路步行街<br><br>
  545. <strong>第二天:娱乐体验</strong><br>
  546. - 上海迪士尼乐园<br>
  547. - 豫园<br><br>
  548. 需要我帮您规划详细路线吗?
  549. `;
  550. } else {
  551. aiResponseText = `
  552. 我已收到您的请求:"${message}"<br><br>
  553. 我可以为您提供以下帮助:<br>
  554. 1. 旅游路线规划<br>
  555. 2. 景点门票预订<br>
  556. 3. 酒店推荐<br>
  557. 4. 当地美食推荐<br><br>
  558. 您需要哪方面的帮助呢?
  559. `;
  560. }
  561. const aiResponse = document.createElement('div');
  562. aiResponse.className = 'message message-ai';
  563. aiResponse.innerHTML = `
  564. <div class="message-bubble ai-bubble">${aiResponseText}</div>
  565. <div class="message-time">${getCurrentTime()}</div>
  566. `;
  567. chatContainer.appendChild(aiResponse);
  568. chatContainer.scrollTop = chatContainer.scrollHeight;
  569. }, 1500);
  570. }
  571. // 发送消息功能
  572. sendButton.addEventListener('click', sendMessage);
  573. messageInput.addEventListener('keypress', (e) => {
  574. if (e.key === 'Enter') sendMessage();
  575. });
  576. function sendMessage() {
  577. const message = messageInput.value.trim();
  578. if (!message) return;
  579. // 添加用户消息
  580. const userMessage = document.createElement('div');
  581. userMessage.className = 'message message-user';
  582. userMessage.innerHTML = `
  583. <div class="message-bubble user-bubble">${message}</div>
  584. <div class="message-time">${getCurrentTime()}</div>
  585. `;
  586. chatContainer.appendChild(userMessage);
  587. messageInput.value = '';
  588. chatContainer.scrollTop = chatContainer.scrollHeight;
  589. // 模拟AI回复
  590. simulateAIResponse(message);
  591. }
  592. // 快速建议点击事件
  593. const quickSuggestions = document.querySelectorAll('.quick-suggestion');
  594. quickSuggestions.forEach(suggestion => {
  595. suggestion.addEventListener('click', () => {
  596. const text = suggestion.textContent;
  597. messageInput.value = text;
  598. messageInput.focus();
  599. });
  600. });
  601. </script>
  602. </body>
  603. </html>