123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044 |
- "use strict";
- const common_vendor = require("../../common/vendor.js");
- const pages_api_ai = require("../api/ai.js");
- const pages_api_config = require("../api/config.js");
- const common_assets = require("../../common/assets.js");
- const _sfc_main = {
- data() {
- return {
- inputMessage: "",
- messages: [],
- isLoading: false,
- scrollTop: 0,
- maxLength: 300,
- userId: "user_123",
- // 模拟用户ID
- sessionId: "",
- eventSource: null,
- // SSE连接对象
- pollingInterval: null,
- // 轮询定时器
- isConnected: false,
- // SSE连接状态
- isDevelopment: true,
- // 控制测试按钮显示
- // 快捷问题
- quickQuestions: {
- "重庆深度5日游": {
- destination: "重庆",
- days: 5,
- budget: 3e3,
- preferences: ["景点", "美食", "文化"],
- transportation: "公共交通"
- },
- "推荐几个热门旅游城市": {
- requestType: "recommendation",
- preferences: ["热门", "旅游城市"]
- },
- "旅游注意事项有哪些": {
- requestType: "travelTips",
- preferences: ["安全", "注意事项"]
- }
- }
- };
- },
- computed: {
- // 用于显示除欢迎消息外的其他消息
- displayMessages() {
- if (this.messages.length === 0 || this.messages.length === 1 && this.messages[0].type === "ai") {
- return [];
- }
- if (this.messages.length > 1 && this.messages[0].type === "ai") {
- return this.messages.slice(1);
- }
- return this.messages;
- }
- },
- onLoad(options) {
- if (typeof __wxConfig !== "undefined") {
- this.isDevelopment = __wxConfig.envVersion === "develop" || __wxConfig.envVersion === "trial";
- }
- try {
- const savedMessages = common_vendor.index.getStorageSync("chat_messages");
- if (savedMessages) {
- this.messages = JSON.parse(savedMessages);
- } else {
- this.addMessage("您好,欢迎遇见Gooh旅记旅行规划师!我将为您设计专属行程,解答旅途中的各类问题,让旅行无忧。有任何想法,请随时告诉我~", "ai");
- }
- } catch (e) {
- common_vendor.index.__f__("error", "at pages/ai-assistant/index.vue:296", "加载消息历史失败:", e);
- this.addMessage("您好,欢迎遇见Gooh旅记旅行规划师!我将为您设计专属行程,解答旅途中的各类问题,让旅行无忧。有任何想法,请随时告诉我~", "ai");
- }
- this.startSession();
- },
- onUnload() {
- try {
- common_vendor.index.setStorageSync("chat_messages", JSON.stringify(this.messages));
- } catch (e) {
- common_vendor.index.__f__("error", "at pages/ai-assistant/index.vue:309", "保存消息历史失败:", e);
- }
- if (this.eventSource) {
- this.eventSource.close();
- }
- this.stopPolling();
- },
- methods: {
- // 返回旅游规划页面
- goBack() {
- common_vendor.index.switchTab({
- url: "/pages/planning/index"
- });
- },
- // 创建新对话
- async createNewChat() {
- if (this.eventSource) {
- try {
- this.eventSource.close();
- this.eventSource = null;
- } catch (e) {
- common_vendor.index.__f__("error", "at pages/ai-assistant/index.vue:337", "关闭SSE连接失败:", e);
- }
- }
- if (this.pollingInterval) {
- clearInterval(this.pollingInterval);
- this.pollingInterval = null;
- }
- this.messages = [];
- this.inputMessage = "";
- this.sessionId = "";
- await this.startSession();
- common_vendor.index.showToast({
- title: "已创建新对话",
- icon: "none"
- });
- },
- // 开始新会话
- async startSession() {
- try {
- const response = await pages_api_ai.startAISession();
- if (response && response.code === 200 && response.sessionId) {
- this.sessionId = response.sessionId;
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:368", "会话创建成功,sessionId:", this.sessionId);
- if (response.msg && this.messages.length === 0) {
- this.addMessage(response.msg, "ai");
- }
- if (this.sessionId) {
- await this.setupWebSocket();
- } else {
- throw new Error("未获取到有效的sessionId");
- }
- } else {
- throw new Error((response == null ? void 0 : response.msg) || "创建会话失败");
- }
- } catch (error) {
- common_vendor.index.__f__("error", "at pages/ai-assistant/index.vue:386", "创建会话失败:", error);
- common_vendor.index.showToast({
- title: error.message || "连接失败,请重试",
- icon: "none"
- });
- }
- },
- // 设置SSE连接
- async setupWebSocket() {
- if (!this.sessionId) {
- common_vendor.index.__f__("error", "at pages/ai-assistant/index.vue:397", "无法建立SSE连接:sessionId不存在");
- return;
- }
- const url = pages_api_ai.getSSEUrl(this.sessionId);
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:403", "尝试连接SSE:", url);
- if (this.eventSource) {
- try {
- this.eventSource.close();
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:409", "已关闭旧的SSE连接");
- } catch (e) {
- common_vendor.index.__f__("error", "at pages/ai-assistant/index.vue:411", "关闭旧SSE连接失败:", e);
- }
- this.eventSource = null;
- }
- return new Promise((resolve, reject) => {
- try {
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:418", "开始创建SSE连接...");
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:465", "在小程序环境下使用替代方法连接SSE");
- this.isConnected = true;
- this.setupPolling();
- resolve();
- setTimeout(() => {
- if (!this.isConnected) {
- common_vendor.index.__f__("error", "at pages/ai-assistant/index.vue:476", "SSE连接超时");
- reject(new Error("SSE连接超时"));
- }
- }, 1e4);
- } catch (error) {
- common_vendor.index.__f__("error", "at pages/ai-assistant/index.vue:488", "创建SSE连接时发生错误:", error);
- reject(error);
- }
- });
- },
- // 处理WebSocket消息
- handleSocketMessage(res) {
- try {
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:497", "开始处理WebSocket消息:", res.data);
- let data;
- try {
- data = JSON.parse(res.data);
- } catch (parseError) {
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:505", "WebSocket数据不是JSON格式,尝试作为纯文本处理");
- data = {
- type: "message",
- content: res.data
- };
- }
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:512", "处理后的WebSocket消息数据:", data);
- if (!data.type && data.content) {
- data.type = "message";
- }
- switch (data.type) {
- case "start":
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:521", "收到start类型消息");
- if (this.messages.length > 0 && this.messages[this.messages.length - 1].type === "user") {
- this.addMessage("AI正在思考...", "ai", true);
- }
- this.setupPolling();
- break;
- case "message":
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:532", "收到message类型消息:", data.content);
- if (this.messages.length > 0 && this.messages[this.messages.length - 1].isThinking) {
- this.messages.pop();
- }
- if (data.content) {
- const isPlanContent = this.checkIfTravelPlanContent(data.content);
- if (isPlanContent) {
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:540", "检测到旅游行程信息,解析为卡片显示");
- const planData = this.parseTravelPlanContent(data.content);
- this.addTravelPlanMessage(planData);
- } else {
- let formattedContent = this.formatAIResponse(data.content);
- this.addMessage(formattedContent, "ai");
- }
- }
- break;
- case "done":
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:550", "收到done类型消息");
- if (this.messages.length > 0 && this.messages[this.messages.length - 1].isThinking) {
- this.messages.pop();
- }
- this.isLoading = false;
- this.stopPolling();
- break;
- case "error":
- common_vendor.index.__f__("error", "at pages/ai-assistant/index.vue:561", "收到error类型消息:", data.content);
- if (this.messages.length > 0 && this.messages[this.messages.length - 1].isThinking) {
- this.messages.pop();
- }
- this.addMessage(`错误: ${data.content}`, "ai");
- this.isLoading = false;
- common_vendor.index.showToast({
- title: "发生错误,请重试",
- icon: "none"
- });
- this.stopPolling();
- break;
- default:
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:577", "收到未知类型消息:", data);
- if (data.content || typeof data === "string" && data) {
- const content = data.content || data;
- if (this.messages.length > 0 && this.messages[this.messages.length - 1].isThinking) {
- this.messages.pop();
- }
- let formattedContent = this.formatAIResponse(content);
- this.addMessage(formattedContent, "ai");
- }
- break;
- }
- } catch (error) {
- common_vendor.index.__f__("error", "at pages/ai-assistant/index.vue:590", "处理WebSocket消息失败:", error);
- this.isLoading = false;
- try {
- let content = res.data;
- if (typeof content === "string" && content.length > 0) {
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:598", "尝试直接显示原始消息内容");
- if (this.messages.length > 0 && this.messages[this.messages.length - 1].isThinking) {
- this.messages.pop();
- }
- let formattedContent = this.formatAIResponse(content);
- this.addMessage(formattedContent, "ai");
- return;
- }
- } catch (e) {
- common_vendor.index.__f__("error", "at pages/ai-assistant/index.vue:607", "尝试直接显示消息内容也失败:", e);
- }
- if (this.messages.length > 0 && this.messages[this.messages.length - 1].isThinking) {
- this.messages.pop();
- }
- this.addMessage("消息处理出错,请重试", "ai");
- }
- },
- // 设置轮询 - 仅当需要时才开始轮询,有结果就停止
- setupPolling() {
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:619", "设置轮询检查");
- this.stopPolling();
- this.pollingCount = 0;
- const maxPollingCount = 20;
- this.pollingInterval = setInterval(async () => {
- try {
- this.pollingCount++;
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:631", `第${this.pollingCount}次轮询检查`);
- if (!this.isLoading || this.pollingCount > maxPollingCount) {
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:635", "条件不满足,停止轮询:", {
- isLoading: this.isLoading,
- pollingCount: this.pollingCount
- });
- this.stopPolling();
- return;
- }
- const response = await pages_api_ai.getLastAIReply(this.sessionId);
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:645", "轮询结果:", response);
- if (response && response.code === 200 && response.reply) {
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:649", "轮询发现新消息");
- this.handleSocketMessage({
- data: JSON.stringify({
- type: "message",
- content: response.reply
- })
- });
- setTimeout(() => {
- this.handleSocketMessage({
- data: JSON.stringify({
- type: "done"
- })
- });
- }, 500);
- this.stopPolling();
- }
- } catch (err) {
- common_vendor.index.__f__("error", "at pages/ai-assistant/index.vue:670", "轮询出错:", err);
- this.pollingErrorCount = (this.pollingErrorCount || 0) + 1;
- if (this.pollingErrorCount > 3) {
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:675", "轮询错误太多,停止轮询");
- this.stopPolling();
- }
- }
- }, 3e3);
- },
- // 停止轮询
- stopPolling() {
- if (this.pollingInterval) {
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:685", "停止轮询");
- clearInterval(this.pollingInterval);
- this.pollingInterval = null;
- this.pollingCount = 0;
- this.pollingErrorCount = 0;
- }
- },
- // 发送消息
- async sendMessage() {
- if (!this.inputMessage.trim() || this.isLoading)
- return;
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:697", "准备发送消息:", this.inputMessage);
- if (!this.sessionId) {
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:701", "sessionId不存在,先创建新会话");
- try {
- const response = await pages_api_ai.startAISession();
- if (response && response.code === 200 && response.sessionId) {
- this.sessionId = response.sessionId;
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:706", "新会话创建成功,sessionId:", this.sessionId);
- } else {
- throw new Error("创建会话失败");
- }
- } catch (error) {
- common_vendor.index.__f__("error", "at pages/ai-assistant/index.vue:711", "创建会话失败:", error);
- common_vendor.index.showToast({
- title: "创建会话失败,请重试",
- icon: "none"
- });
- return;
- }
- }
- if (this.inputMessage.length > this.maxLength) {
- common_vendor.index.showToast({
- title: `消息不能超过${this.maxLength}字`,
- icon: "none"
- });
- return;
- }
- const messageToSend = this.inputMessage.trim();
- this.inputMessage = "";
- this.addMessage(messageToSend, "user");
- this.isLoading = true;
- try {
- if (!this.isConnected) {
- common_vendor.index.__f__("warn", "at pages/ai-assistant/index.vue:741", "SSE未连接,尝试重新连接");
- await this.setupWebSocket();
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:743", "SSE重连状态:", this.isConnected ? "已连接" : "未连接");
- }
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:747", "发送消息到服务器:", {
- sessionId: this.sessionId,
- message: messageToSend
- });
- const response = await pages_api_ai.chatWithAI(this.sessionId, messageToSend);
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:754", "服务器响应:", response);
- if (response.code === 200) {
- if (response.msg) {
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:759", "服务器返回的消息:", response.msg);
- let formattedContent = this.formatAIResponse(response.msg);
- this.addMessage(formattedContent, "ai");
- } else {
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:764", "服务器未返回消息,等待WebSocket响应");
- }
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:769", "小程序环境中启动轮询检查");
- this.setupPolling();
- } else if (response.code === 404) {
- common_vendor.index.__f__("warn", "at pages/ai-assistant/index.vue:773", "会话不存在,尝试重新开始会话");
- const newSessionResponse = await pages_api_ai.startAISession();
- if (newSessionResponse && newSessionResponse.code === 200 && newSessionResponse.sessionId) {
- this.sessionId = newSessionResponse.sessionId;
- const retryResponse = await pages_api_ai.chatWithAI(this.sessionId, messageToSend);
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:779", "重试响应:", retryResponse);
- if (retryResponse.code === 200) {
- if (retryResponse.msg) {
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:784", "重试后服务器返回的消息:", retryResponse.msg);
- let formattedContent = this.formatAIResponse(retryResponse.msg);
- this.addMessage(formattedContent, "ai");
- }
- } else {
- throw new Error(retryResponse.msg || "重试发送失败");
- }
- } else {
- throw new Error("重新创建会话失败");
- }
- } else {
- throw new Error(response.msg || "发送失败");
- }
- } catch (error) {
- common_vendor.index.__f__("error", "at pages/ai-assistant/index.vue:798", "发送消息失败:", error);
- common_vendor.index.showToast({
- title: error.message || "发送失败,请重试",
- icon: "none"
- });
- this.addMessage("抱歉,发送消息失败,请重试。", "ai");
- } finally {
- this.isLoading = false;
- }
- },
- // 处理快捷问题
- async handleQuickQuestion(question) {
- if (this.isLoading)
- return;
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:813", "处理快捷问题:", question);
- this.isLoading = true;
- try {
- if (!this.sessionId) {
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:819", "sessionId不存在,先创建新会话");
- try {
- const response2 = await pages_api_ai.startAISession();
- if (response2 && response2.code === 200 && response2.sessionId) {
- this.sessionId = response2.sessionId;
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:824", "新会话创建成功,sessionId:", this.sessionId);
- } else {
- throw new Error("创建会话失败");
- }
- } catch (error) {
- common_vendor.index.__f__("error", "at pages/ai-assistant/index.vue:829", "创建会话失败:", error);
- common_vendor.index.showToast({
- title: "创建会话失败,请重试",
- icon: "none"
- });
- this.isLoading = false;
- return;
- }
- }
- this.addMessage(question, "user");
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:843", "发送快捷问题到服务器:", {
- sessionId: this.sessionId,
- message: question
- });
- const response = await pages_api_ai.chatWithAI(this.sessionId, question);
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:849", "快捷问题服务器响应:", response);
- if (response.code === 200) {
- if (response.msg) {
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:854", "服务器返回的消息:", response.msg);
- let formattedContent = this.formatAIResponse(response.msg);
- this.addMessage(formattedContent, "ai");
- } else {
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:859", "服务器未返回消息,等待WebSocket响应");
- }
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:864", "小程序环境中启动轮询检查");
- this.setupPolling();
- } else if (response.code === 404) {
- common_vendor.index.__f__("warn", "at pages/ai-assistant/index.vue:868", "会话不存在,尝试重新开始会话");
- this.addMessage("会话已过期,正在重新创建会话...", "system");
- const newSessionResponse = await pages_api_ai.startAISession();
- if (newSessionResponse && newSessionResponse.code === 200 && newSessionResponse.sessionId) {
- this.sessionId = newSessionResponse.sessionId;
- this.handleQuickQuestion(question);
- } else {
- throw new Error("重新创建会话失败");
- }
- } else {
- throw new Error(response.msg || "发送失败");
- }
- } catch (error) {
- common_vendor.index.__f__("error", "at pages/ai-assistant/index.vue:883", "处理快捷问题失败:", error);
- common_vendor.index.showToast({
- title: error.message || "发送失败,请重试",
- icon: "none"
- });
- this.addMessage("抱歉,处理问题失败,请重试。", "ai");
- } finally {
- this.isLoading = false;
- }
- },
- // 添加消息
- addMessage(content, type, isThinking = false) {
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:896", "添加新消息:", { content, type, isThinking });
- if (type === "user") {
- content = content.replace(/</g, "<").replace(/>/g, ">");
- }
- const message = {
- type,
- content,
- isThinking,
- timestamp: Date.now()
- };
- if (!isThinking && this.messages.length > 0) {
- const lastMessage = this.messages[this.messages.length - 1];
- if (lastMessage.isThinking && lastMessage.type === type) {
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:914", "替换思考状态消息");
- this.messages.splice(-1, 1);
- }
- }
- this.messages.push(message);
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:920", "当前消息列表:", this.messages);
- try {
- common_vendor.index.setStorageSync("chat_messages", JSON.stringify(this.messages));
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:925", "消息已保存到本地存储");
- } catch (e) {
- common_vendor.index.__f__("error", "at pages/ai-assistant/index.vue:927", "保存消息到本地存储失败:", e);
- }
- this.$nextTick(() => {
- this.scrollToBottom();
- });
- },
- // 滚动到底部
- scrollToBottom() {
- const query = common_vendor.index.createSelectorQuery().in(this);
- query.select(".message-list").boundingClientRect((data) => {
- if (data) {
- this.scrollTop = data.height;
- }
- }).exec();
- },
- // 格式化时间
- formatTime(timestamp) {
- if (!timestamp)
- return "";
- const date = new Date(timestamp);
- const hours = date.getHours().toString().padStart(2, "0");
- const minutes = date.getMinutes().toString().padStart(2, "0");
- return `${hours}:${minutes}`;
- },
- // 加载更多消息
- loadMoreMessages() {
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:958", "加载更多消息");
- },
- // 格式化AI响应的方法增加日志
- formatAIResponse(content) {
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:963", "开始格式化AI响应:", content);
- content = content.replace(/\d+e\d+;margin:[\s\d]+px[\s\d]+px[\s\d]+;"/g, '"');
- content = content.replace(/\n/g, "<br/>");
- content = content.replace(/最佳旅行时间[::]/g, '<div class="enhanced-title"><span class="title-icon">🕒</span><span class="title-text">最佳旅行时间:</span></div>');
- content = content.replace(/景点推荐[::]/g, '<div class="enhanced-title"><span class="title-icon">🏞️</span><span class="title-text">景点推荐:</span></div>');
- content = content.replace(/美食推荐[::]/g, '<div class="enhanced-title"><span class="title-icon">🍜</span><span class="title-text">美食推荐:</span></div>');
- content = content.replace(/住宿推荐[::]/g, '<div class="enhanced-title"><span class="title-icon">🏨</span><span class="title-text">住宿推荐:</span></div>');
- content = content.replace(/交通指南[::]/g, '<div class="enhanced-title"><span class="title-icon">🚗</span><span class="title-text">交通指南:</span></div>');
- content = content.replace(/旅游贴士[::]/g, '<div class="enhanced-title"><span class="title-icon">💡</span><span class="title-text">旅游贴士:</span></div>');
- content = content.replace(/费用预算[::]/g, '<div class="enhanced-title"><span class="title-icon">💰</span><span class="title-text">费用预算:</span></div>');
- content = content.replace(/行程安排[::]/g, '<div class="enhanced-title"><span class="title-icon">📅</span><span class="title-text">行程安排:</span></div>');
- content = content.replace(/###\s*(.*?)(\*\*)?(\*\*)?(\<br\/\>|\s|$)/g, '<h3 class="md-heading">$1</h3>');
- content = content.replace(/##\s*(.*?)(\<br\/\>|\s|$)/g, '<h2 class="md-heading">$2</h2>');
- content = content.replace(/#\s*(.*?)(\<br\/\>|\s|$)/g, '<h1 class="md-heading">$1</h1>');
- content = content.replace(/---(\<br\/\>|\s|$)/g, '<hr style="border: 0; height: 1px; background: #eee; margin: 20px 0;">');
- content = content.replace(/- ([^<]+)(?:<br\/>|$)/g, '<div class="enhanced-list-item"><span class="item-bullet"></span>$1</div>');
- content = content.replace(/([^<::]+)[::](?=\s*亚洲|世界|有"|\w)/g, '<span class="spot-name">$1:</span>');
- content = content.replace(/\*\*(.*?)\*\*/g, "<b>$1</b>");
- content = content.replace(/【(.*?)】/g, '<span class="bracket-title">【$1】</span>');
- content = content.replace(/\*(.*?)\*/g, "<i>$1</i>");
- if (content.includes("行程") || content.includes("旅游") || content.includes("景点") || content.includes("住宿") || content.includes("Day") || content.includes("预算")) {
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:1007", "检测到旅游相关内容,进行格式化");
- content = content.replace(/¥(\d+)/g, '<span class="price-highlight">¥$1</span>');
- content = content.replace(/(\d+)元/g, '<span class="price-highlight">$1元</span>');
- content = content.replace(/¥(\d+)/g, '<span class="price-highlight">¥$1</span>');
- content = content.replace(/¥(\d+[-~]\d+)/g, '<span class="price-highlight">¥$1</span>');
- content = content.replace(/(Day\d+)[::]/g, '<span class="day-highlight">$1:</span>');
- content = content.replace(/(第\d+天)[::]/g, '<span class="day-highlight">$1:</span>');
- content = content.replace(/(上午|下午|晚上|早上|中午|傍晚)[::]/g, '<span class="time-highlight">$1:</span>');
- content = content.replace(/(著名|必去|推荐|特色|知名)/g, '<span class="keyword-highlight">$1</span>');
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:1023", "格式化后的内容:", content);
- }
- return content;
- },
- async testConnection() {
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:1030", "开始测试连接");
- try {
- common_vendor.index.showLoading({
- title: "测试连接中..."
- });
- if (!this.isConnected) {
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:1039", "WebSocket未连接,尝试重新连接");
- await this.startSession();
- }
- const testMessage = "测试消息:你好";
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:1045", "发送测试消息:", testMessage);
- this.addMessage("开始连接测试...", "system");
- await this.testWebSocketConnection();
- try {
- const response = await pages_api_ai.chatWithAI(this.sessionId, testMessage);
- if (response.code === 200) {
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:1057", "测试消息发送成功");
- } else {
- throw new Error(response.msg || "测试消息发送失败");
- }
- } catch (error) {
- throw new Error("测试消息发送失败: " + error.message);
- }
- common_vendor.index.hideLoading();
- common_vendor.index.showToast({
- title: "连接测试成功",
- icon: "success"
- });
- } catch (error) {
- common_vendor.index.__f__("error", "at pages/ai-assistant/index.vue:1075", "测试连接失败:", error);
- common_vendor.index.hideLoading();
- common_vendor.index.showToast({
- title: "测试失败",
- icon: "error"
- });
- }
- },
- // WebSocket连接测试
- async testWebSocketConnection() {
- return new Promise((resolve, reject) => {
- try {
- if (!this.isConnected) {
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:1092", "WebSocket未连接,无法发送测试消息");
- reject(new Error("WebSocket未连接"));
- return;
- }
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:1097", "尝试通过WebSocket发送测试消息");
- common_vendor.index.sendSocketMessage({
- data: JSON.stringify({ type: "test", message: "WebSocket测试消息" }),
- success: () => {
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:1103", "WebSocket测试消息发送成功");
- this.addMessage("WebSocket测试消息已发送,等待回应...", "system");
- setTimeout(() => {
- resolve();
- }, 1e3);
- },
- fail: (error) => {
- common_vendor.index.__f__("error", "at pages/ai-assistant/index.vue:1111", "WebSocket测试消息发送失败:", error);
- this.addMessage("WebSocket测试消息发送失败", "system");
- reject(error);
- }
- });
- } catch (error) {
- common_vendor.index.__f__("error", "at pages/ai-assistant/index.vue:1117", "WebSocket测试过程出错:", error);
- reject(error);
- }
- });
- },
- // 轮询获取AI回复
- async pollForAIReply(sessionId, maxAttempts = 3, interval = 5e3) {
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:1125", `开始轮询获取AI回复,最多${maxAttempts}次,间隔${interval}ms`);
- this.addMessage("正在获取AI回复...", "system");
- const lastMessage = this.messages[this.messages.length - 1];
- if (lastMessage && lastMessage.type === "ai" && !lastMessage.isThinking) {
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:1133", "已通过WebSocket收到回复,停止轮询");
- return;
- }
- let attempt = 0;
- const poll = async () => {
- if (attempt >= maxAttempts) {
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:1141", `已达到最大轮询次数(${maxAttempts}),停止轮询`);
- this.addMessage("获取AI回复超时,尝试从服务器直接获取回复...", "system");
- try {
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:1148", "尝试通过直接聊天请求获取回复");
- const response = await pages_api_ai.chatWithAI(sessionId, "获取上一条回复");
- if (response && response.data && response.data.reply) {
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:1152", "成功通过chatWithAI获取回复:", response.data.reply);
- if (this.messages.length > 0 && this.messages[this.messages.length - 1].isThinking) {
- this.messages.pop();
- }
- const isPlanContent = this.checkIfTravelPlanContent(response.data.reply);
- if (isPlanContent) {
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:1160", "轮询检测到旅游行程信息,解析为卡片显示");
- const planData = this.parseTravelPlanContent(response.data.reply);
- this.addTravelPlanMessage(planData);
- } else {
- let formattedContent = this.formatAIResponse(response.data.reply);
- this.addMessage(formattedContent, "ai");
- }
- this.isLoading = false;
- return;
- }
- } catch (error) {
- common_vendor.index.__f__("error", "at pages/ai-assistant/index.vue:1173", "通过直接聊天请求获取回复失败:", error);
- }
- if (this.messages.length > 0 && this.messages[this.messages.length - 1].isThinking) {
- this.messages.pop();
- this.addMessage("获取回复失败,请重新发送消息", "ai");
- }
- this.isLoading = false;
- return;
- }
- attempt++;
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:1186", `第${attempt}次轮询获取AI回复`);
- try {
- const response = await pages_api_ai.getLastAIReply(sessionId);
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:1190", `第${attempt}次轮询响应:`, response);
- if (response && response.code === 200 && response.reply) {
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:1193", "轮询成功获取到AI回复:", response.reply);
- const lastMessage2 = this.messages[this.messages.length - 1];
- if (lastMessage2 && lastMessage2.type === "ai" && !lastMessage2.isThinking && lastMessage2.content === response.reply) {
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:1199", "已有此消息,不重复显示");
- return;
- }
- const isPlanContent = this.checkIfTravelPlanContent(response.reply);
- if (isPlanContent) {
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:1206", "轮询检测到旅游行程信息,解析为卡片显示");
- const planData = this.parseTravelPlanContent(response.reply);
- this.addTravelPlanMessage(planData);
- } else {
- let formattedContent = this.formatAIResponse(response.reply);
- this.addMessage(formattedContent, "ai");
- }
- this.isLoading = false;
- return;
- }
- setTimeout(poll, interval);
- } catch (error) {
- common_vendor.index.__f__("error", "at pages/ai-assistant/index.vue:1222", `第${attempt}次轮询出错:`, error);
- setTimeout(poll, interval);
- }
- };
- poll();
- },
- async fetchServerResponse() {
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:1233", "开始获取服务器响应");
- try {
- common_vendor.index.showLoading({
- title: "获取服务器响应中..."
- });
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:1241", "发送HTTP请求到服务器:", {
- sessionId: this.sessionId,
- message: "获取服务器响应",
- baseUrl: pages_api_config.baseUrl,
- url: `${pages_api_config.baseUrl}/api/ai/travel/fetchServerResponse`
- });
- const response = await pages_api_ai.fetchServerResponse(this.sessionId);
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:1249", "服务器响应:", response);
- if (response.code === 200) {
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:1252", "服务器响应获取成功");
- if (this.messages.length > 0 && this.messages[this.messages.length - 1].isThinking) {
- this.messages.pop();
- }
- let formattedContent = this.formatAIResponse(response.data.reply);
- this.addMessage(formattedContent, "ai");
- } else {
- throw new Error(response.msg || "获取服务器响应失败");
- }
- } catch (error) {
- common_vendor.index.__f__("error", "at pages/ai-assistant/index.vue:1263", "获取服务器响应失败:", error);
- common_vendor.index.showToast({
- title: error.message || "获取服务器响应失败,请重试",
- icon: "none"
- });
- this.addMessage("抱歉,获取服务器响应失败,请重试。", "ai");
- } finally {
- this.isLoading = false;
- common_vendor.index.hideLoading();
- }
- },
- // 检查是否有未接收的消息
- async checkForMissedMessages() {
- if (!this.sessionId)
- return;
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:1280", "检查是否有未接收的消息");
- try {
- if (this.socketTask && this.isConnected) {
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:1285", "WebSocket已连接,发送请求获取最新消息");
- this.socketTask.send({
- data: JSON.stringify({
- type: "get_last_message",
- sessionId: this.sessionId
- }),
- success() {
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:1292", "请求最新消息发送成功");
- },
- fail(error) {
- common_vendor.index.__f__("error", "at pages/ai-assistant/index.vue:1295", "请求最新消息发送失败:", error);
- }
- });
- }
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:1301", "尝试通过HTTP接口获取最新回复");
- const response = await pages_api_ai.getLastAIReply(this.sessionId);
- if (response && response.code === 200 && response.reply) {
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:1305", "成功获取到最新回复:", response.reply);
- const lastMessage = this.messages[this.messages.length - 1];
- if (lastMessage && lastMessage.type === "ai" && !lastMessage.isThinking && lastMessage.content === response.reply) {
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:1311", "已有此消息,不重复显示");
- return;
- }
- const isPlanContent = this.checkIfTravelPlanContent(response.reply);
- if (isPlanContent) {
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:1318", "检测到旅游行程信息,解析为卡片显示");
- const planData = this.parseTravelPlanContent(response.reply);
- this.addTravelPlanMessage(planData);
- } else {
- let formattedContent = this.formatAIResponse(response.reply);
- this.addMessage(formattedContent, "ai");
- }
- }
- } catch (error) {
- common_vendor.index.__f__("error", "at pages/ai-assistant/index.vue:1328", "检查未接收消息失败:", error);
- }
- },
- // 检查是否是旅游行程信息
- checkIfTravelPlanContent(content) {
- const keywords = [
- "行程安排",
- "旅游计划",
- "旅行计划",
- "计划名称",
- "计划简介",
- "行程特色",
- "行程总花费",
- "行程总费用",
- "旅游地点",
- "¥",
- "¥",
- "预算",
- "第一天",
- "第二天",
- "第三天",
- "Day1",
- "Day2",
- "第.*天"
- ];
- const contentHasKeywords = keywords.some((keyword) => {
- const regex = new RegExp(keyword, "i");
- return regex.test(content);
- });
- const hasColonFormat = (content.match(/[::]/g) || []).length >= 3;
- const hasDayPattern = /第\s*\d+\s*天|Day\s*\d+/i.test(content);
- return contentHasKeywords && (hasColonFormat || hasDayPattern);
- },
- // 解析旅游行程信息
- parseTravelPlanContent(content) {
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:1358", "开始解析旅游行程内容");
- const planData = {
- name: "",
- description: "",
- features: "",
- price: "",
- currentDay: "1",
- city: "",
- attractions: "",
- food: "",
- arrangement: "",
- note: "",
- days: "5天4晚",
- spots: "12个地点"
- };
- const nameMatch = content.match(/计划名称[::]\s*([^\n]+)/);
- if (nameMatch)
- planData.name = nameMatch[1].trim();
- if (!planData.name) {
- const altNameMatch = content.match(/(.*)[日游|旅行|旅游计划]/);
- if (altNameMatch)
- planData.name = altNameMatch[1].trim();
- }
- if (!planData.name)
- planData.name = "旅游计划";
- const descMatch = content.match(/计划简介[::]\s*([^\n]+)/);
- if (descMatch)
- planData.description = descMatch[1].trim();
- else {
- const altDescMatch = content.match(/简介[::]\s*([^\n]+)/);
- if (altDescMatch)
- planData.description = altDescMatch[1].trim();
- }
- const featuresMatch = content.match(/行程特色[::]\s*([^\n]+)/);
- if (featuresMatch)
- planData.features = featuresMatch[1].trim();
- const priceMatch = content.match(/行程总花费[::]\s*([^\n]+)|预算[::]\s*([^\n]+)|总花费[::]\s*([^\n]+)|花费[::]\s*([^\n]+)/);
- if (priceMatch) {
- planData.price = (priceMatch[1] || priceMatch[2] || priceMatch[3] || priceMatch[4]).trim();
- planData.price = planData.price.replace(/[¥¥]/g, "");
- }
- const dayMatch = content.match(/第\s*(\d+)\s*天|Day\s*(\d+)/i);
- if (dayMatch) {
- planData.currentDay = dayMatch[1] || dayMatch[2];
- }
- const cityMatch = content.match(/城市[::]\s*([^\n]+)|地点[::]\s*([^\n]+)|国家[::]\s*([^\n]+)/);
- if (cityMatch)
- planData.city = (cityMatch[1] || cityMatch[2] || cityMatch[3]).trim();
- const attractionsMatch = content.match(/地点[::]\s*([^\n]+)|景点[::]\s*([^\n]+)|旅游地点[::]\s*([^\n]+)/);
- if (attractionsMatch)
- planData.attractions = (attractionsMatch[1] || attractionsMatch[2] || attractionsMatch[3]).trim();
- const foodMatch = content.match(/周边美食[::]\s*([^\n]+)|美食[::]\s*([^\n]+)|特色美食[::]\s*([^\n]+)/);
- if (foodMatch)
- planData.food = (foodMatch[1] || foodMatch[2] || foodMatch[3]).trim();
- const arrangementMatch = content.match(/安排说明[::]\s*([^\n]+)|安排[::]\s*([^\n]+)|行程安排[::]\s*([^\n]+)/);
- if (arrangementMatch)
- planData.arrangement = (arrangementMatch[1] || arrangementMatch[2] || arrangementMatch[3]).trim();
- const noteMatch = content.match(/备注[::]\s*([^\n]+)/);
- if (noteMatch)
- planData.note = noteMatch[1].trim();
- const totalDaysMatch = content.match(/(\d+)\s*天\s*(\d+)\s*晚|(\d+)\s*日\s*(\d+)\s*晚/);
- if (totalDaysMatch) {
- const days = totalDaysMatch[1] || totalDaysMatch[3];
- const nights = totalDaysMatch[2] || totalDaysMatch[4];
- planData.days = `${days}天${nights}晚`;
- }
- const spotsMatch = content.match(/(\d+)\s*个景点|(\d+)\s*个地点/);
- if (spotsMatch) {
- const count = spotsMatch[1] || spotsMatch[2];
- planData.spots = `${count}个地点`;
- }
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:1451", "解析后的旅游行程数据:", planData);
- return planData;
- },
- // 添加旅游行程消息
- addTravelPlanMessage(planData) {
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:1457", "添加旅游行程卡片消息:", planData);
- const message = {
- type: "ai-plan",
- planData,
- isThinking: false,
- timestamp: Date.now()
- };
- this.messages.push(message);
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:1467", "当前消息列表:", this.messages);
- try {
- common_vendor.index.setStorageSync("chat_messages", JSON.stringify(this.messages));
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:1472", "消息已保存到本地存储");
- } catch (e) {
- common_vendor.index.__f__("error", "at pages/ai-assistant/index.vue:1474", "保存消息到本地存储失败:", e);
- }
- this.$nextTick(() => {
- this.scrollToBottom();
- });
- },
- async testConsoleLog() {
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:1483", "开始测试控制台输出");
- try {
- common_vendor.index.showLoading({
- title: "测试控制台输出中..."
- });
- if (!this.sessionId) {
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:1492", "sessionId不存在,先创建新会话");
- const response2 = await pages_api_ai.startAISession();
- if (response2 && response2.code === 200 && response2.sessionId) {
- this.sessionId = response2.sessionId;
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:1496", "新会话创建成功,sessionId:", this.sessionId);
- } else {
- throw new Error("创建会话失败");
- }
- }
- const testMessage = "[CONSOLE_TEST] 这是一条测试控制台输出的消息 " + (/* @__PURE__ */ new Date()).toLocaleString();
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:1504", "发送测试消息:", testMessage);
- this.addMessage("开始测试控制台输出...", "system");
- const response = await pages_api_ai.chatWithAI(this.sessionId, testMessage);
- common_vendor.index.__f__("log", "at pages/ai-assistant/index.vue:1511", "测试消息发送结果:", response);
- if (response.code === 200) {
- this.addMessage("测试消息已发送到后端,请检查后端控制台输出", "system");
- } else {
- throw new Error(response.msg || "发送测试消息失败");
- }
- common_vendor.index.hideLoading();
- common_vendor.index.showToast({
- title: "消息已发送",
- icon: "success"
- });
- } catch (error) {
- common_vendor.index.__f__("error", "at pages/ai-assistant/index.vue:1530", "控制台输出测试失败:", error);
- common_vendor.index.hideLoading();
- common_vendor.index.showToast({
- title: "控制台输出测试失败",
- icon: "error"
- });
- this.addMessage(`控制台输出测试失败: ${error.message}`, "system");
- }
- }
- }
- };
- function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
- return common_vendor.e({
- a: common_vendor.o((...args) => $options.goBack && $options.goBack(...args)),
- b: common_vendor.o((...args) => $options.createNewChat && $options.createNewChat(...args)),
- c: $data.messages.length > 0 && $data.messages[0].type === "ai"
- }, $data.messages.length > 0 && $data.messages[0].type === "ai" ? {
- d: common_assets._imports_0$3,
- e: $data.messages[0].content,
- f: common_vendor.f($data.quickQuestions, (question, key, i0) => {
- return {
- a: common_vendor.t(key),
- b: key,
- c: common_vendor.o(($event) => $options.handleQuickQuestion(key), key)
- };
- })
- } : {}, {
- g: common_vendor.f($options.displayMessages, (msg, index, i0) => {
- return common_vendor.e({
- a: common_vendor.t($options.formatTime(msg.timestamp)),
- b: common_vendor.t(msg.type === "user" ? "我" : "AI助手"),
- c: msg.type === "user" ? "/static/images/user-avatar.png" : "/static/images/ai-avatar.png",
- d: !msg.isThinking && msg.type !== "ai-plan"
- }, !msg.isThinking && msg.type !== "ai-plan" ? {
- e: msg.content
- } : msg.type === "ai-plan" ? common_vendor.e({
- g: common_vendor.t(msg.planData.name),
- h: common_vendor.t(msg.planData.description),
- i: common_vendor.t(msg.planData.features),
- j: common_vendor.t(msg.planData.price),
- k: msg.planData.price.includes("-")
- }, msg.planData.price.includes("-") ? {} : {}, {
- l: common_vendor.t(msg.planData.currentDay),
- m: common_vendor.t(msg.planData.city),
- n: msg.planData.attractions
- }, msg.planData.attractions ? {
- o: common_vendor.t(msg.planData.attractions)
- } : {}, {
- p: msg.planData.food
- }, msg.planData.food ? {
- q: common_vendor.t(msg.planData.food)
- } : {}, {
- r: common_vendor.t(msg.planData.arrangement),
- s: common_vendor.t(msg.planData.days),
- t: common_vendor.t(msg.planData.price),
- v: common_vendor.t(msg.planData.spots),
- w: msg.planData.note
- }, msg.planData.note ? {
- x: common_vendor.t(msg.planData.note)
- } : {}) : {}, {
- f: msg.type === "ai-plan",
- y: index,
- z: common_vendor.n(msg.type)
- });
- }),
- h: $data.scrollTop,
- i: common_vendor.o((...args) => $options.loadMoreMessages && $options.loadMoreMessages(...args)),
- j: $data.isLoading,
- k: $data.maxLength,
- l: common_vendor.o((...args) => $options.sendMessage && $options.sendMessage(...args)),
- m: $data.inputMessage,
- n: common_vendor.o(($event) => $data.inputMessage = $event.detail.value),
- o: common_vendor.t($data.isLoading ? "发送中..." : "发送"),
- p: $data.isLoading || !$data.inputMessage.trim(),
- q: $data.isLoading ? 1 : "",
- r: common_vendor.o((...args) => $options.sendMessage && $options.sendMessage(...args))
- });
- }
- const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["render", _sfc_render]]);
- wx.createPage(MiniProgramPage);
- //# sourceMappingURL=../../../.sourcemap/mp-weixin/pages/ai-assistant/index.js.map
|