feat: add service order component
- add CreateServiceOrder component and associated icon assets - create reusable resolveChatSocketUrl utility with comprehensive test cases - update development env config to use production websocket endpoint - fix ChatCardAi layout by replacing inline-block with flex-1 class - refactor ChatMainList websocket initialization to use the new socket utility - switch to using environment variable for access token instead of getAccessToken - correct relative import path for CreateServiceOrder in ChatMainList
This commit is contained in:
@@ -8,7 +8,7 @@ VITE_API_BASE_URL = '/ingress'
|
||||
VITE_API_TIMEOUT_MS = 10000
|
||||
|
||||
# Socket 基础 URL
|
||||
VITE_SOCKET_BASE_URL = "/ingress/agent/ws/chat"
|
||||
VITE_SOCKET_BASE_URL = "wss://onefeel.brother7.cn/ingress/agent/ws/chat"
|
||||
|
||||
# Client ID
|
||||
VITE_CLIENT_ID = "6"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<!-- 外层行容器:接收来自父组件的对齐类(如 flex flex-justify-start)并占满宽度 -->
|
||||
<div class="w-full flex items-center justify-start">
|
||||
<div class="inline-block align-middle max-w-full overflow-x-hidden">
|
||||
<div class="align-middle flex-1 overflow-x-hidden">
|
||||
<div class="px-3 min-w-0 max-w-full overflow-hidden break-all">
|
||||
<div class="flex items-center max-w-full">
|
||||
<img v-if="isLoading" class="mr-2 w-7.5 h-6.25" src="https://oss.nianxx.cn/mp/static/chat_msg_loading.gif" />
|
||||
|
||||
@@ -168,13 +168,14 @@ import OpenMapComponent from "../OpenMapComponent/index.vue";
|
||||
import AnswerComponent from "../AnswerComponent/index.vue";
|
||||
import GeneratorPhotoComponent from "../GeneratorPhotoComponent/index.vue";
|
||||
import LongTextGuideCardPreview from "../LongTextGuideCardPreview/index.vue";
|
||||
import CreateServiceOrder from "@/components/CreateServiceOrder/index.vue";
|
||||
import CreateServiceOrder from "../CreateServiceOrder/index.vue";
|
||||
import Feedback from "@/components/Feedback/index.vue";
|
||||
import AddCarCrad from "@/components/AddCarCrad/index.vue";
|
||||
import SurveyQuestionnaire from "@/components/SurveyQuestionnaire/index.vue";
|
||||
import { mainPageData, conversationMsgList, recentConversation } from "@/api/home";
|
||||
import WebSocketManager from "@/utils/WebSocketManager";
|
||||
import { IdUtils } from "@/utils/IdUtils";
|
||||
import { resolveChatSocketUrl } from "@/utils/socketUrl";
|
||||
import { appendLongTextChunk, createLongTextData } from "@/constants/longTextCard";
|
||||
import { checkToken } from "@/hooks/useGoLogin";
|
||||
import { useAppStore } from "@/store";
|
||||
@@ -543,18 +544,9 @@ const initWebSocket = async () => {
|
||||
}
|
||||
|
||||
// 使用配置的WebSocket服务器地址
|
||||
const token = getAccessToken();
|
||||
const rawWsUrl = appStore.serverConfig.wssUrl;
|
||||
const resolvedWsUrl =
|
||||
typeof rawWsUrl === "string" &&
|
||||
(rawWsUrl.startsWith("ws://") || rawWsUrl.startsWith("wss://"))
|
||||
? rawWsUrl
|
||||
: `${window.location.protocol === "https:" ? "wss" : "ws"}://${window.location.host}${typeof rawWsUrl === "string"
|
||||
? rawWsUrl.startsWith("/")
|
||||
? rawWsUrl
|
||||
: `/${rawWsUrl}`
|
||||
: ""
|
||||
}`;
|
||||
// const token = getAccessToken();
|
||||
const token = import.meta.env.VITE_TOKEN;
|
||||
const resolvedWsUrl = resolveChatSocketUrl(appStore.serverConfig.wssUrl);
|
||||
const wsUrl = `${resolvedWsUrl}?access_token=${token}`;
|
||||
|
||||
// 初始化WebSocket管理器
|
||||
|
||||
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 844 B After Width: | Height: | Size: 844 B |
@@ -51,15 +51,14 @@
|
||||
联系方式: {{ contactPhone }}
|
||||
</div>
|
||||
<div class="text-[12px] text-ink-600 leading-[20px] ellipsis-2">
|
||||
需求描述: {{ contactspan }}
|
||||
需求描述: {{ contactText }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<img v-if="contentImgUrl" class="w-[88px] h-[88px] rounded-[6px]" :src="contentImgUrl" mode="aspectFill" />
|
||||
<img v-if="contentImgUrl" class="w-[88px] h-[88px] rounded-[6px]" :src="contentImgUrl" />
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="h-[44px] rounded-[5px] text-white bg-linear-[90deg,#f0f8f3_0%,#4de4ff_100%] flex items-center justify-center mx-[12px] mb-[12px]"
|
||||
<div class="h-[44px] rounded-full text-white bg-[#4de4ff] flex items-center justify-center mx-[12px] mb-[12px]"
|
||||
@click="handleCall">
|
||||
{{ isCallSuccess ? "查看服务" : "立即呼叫" }}
|
||||
</div>
|
||||
@@ -100,7 +99,7 @@ const contactPhone = ref("");
|
||||
// 是否用户已编辑过手机号(一旦编辑则不再脱敏)
|
||||
const hasEditedPhone = ref(false);
|
||||
// 需求信息描述:使用可写的 ref,并从工具结果初始化
|
||||
const contactspan = ref("");
|
||||
const contactText = ref("");
|
||||
|
||||
// 手机号脱敏:138****1234(仅对11位数字进行处理)
|
||||
const maskPhone = (phone) => {
|
||||
41
src/utils/socketUrl.test.ts
Normal file
41
src/utils/socketUrl.test.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { describe, it } from "node:test";
|
||||
import { resolveChatSocketUrl } from "./socketUrl.ts";
|
||||
|
||||
const locationLike = {
|
||||
protocol: "http:",
|
||||
host: "localhost:5174",
|
||||
};
|
||||
|
||||
describe("resolveChatSocketUrl", () => {
|
||||
it("keeps a complete ws chat endpoint", () => {
|
||||
assert.equal(
|
||||
resolveChatSocketUrl(
|
||||
"wss://onefeel.brother7.cn/ingress/agent/ws/chat",
|
||||
locationLike,
|
||||
),
|
||||
"wss://onefeel.brother7.cn/ingress/agent/ws/chat",
|
||||
);
|
||||
});
|
||||
|
||||
it("adds the chat path to a configured websocket origin", () => {
|
||||
assert.equal(
|
||||
resolveChatSocketUrl("wss://onefeel.brother7.cn", locationLike),
|
||||
"wss://onefeel.brother7.cn/ingress/agent/ws/chat",
|
||||
);
|
||||
});
|
||||
|
||||
it("adds the chat path to an ingress base path", () => {
|
||||
assert.equal(
|
||||
resolveChatSocketUrl("/ingress", locationLike),
|
||||
"ws://localhost:5174/ingress/agent/ws/chat",
|
||||
);
|
||||
});
|
||||
|
||||
it("uses the current host when config is empty", () => {
|
||||
assert.equal(
|
||||
resolveChatSocketUrl("", locationLike),
|
||||
"ws://localhost:5174/ingress/agent/ws/chat",
|
||||
);
|
||||
});
|
||||
});
|
||||
47
src/utils/socketUrl.ts
Normal file
47
src/utils/socketUrl.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
const CHAT_SOCKET_PATH = "/ingress/agent/ws/chat";
|
||||
|
||||
type LocationLike = {
|
||||
protocol: string;
|
||||
host: string;
|
||||
};
|
||||
|
||||
const toWsProtocol = (protocol: string) => {
|
||||
if (protocol === "https:" || protocol === "wss:") return "wss:";
|
||||
return "ws:";
|
||||
};
|
||||
|
||||
const resolveChatPath = (pathname: string) => {
|
||||
const normalizedPath = pathname === "/" ? "" : pathname.replace(/\/$/, "");
|
||||
|
||||
if (!normalizedPath) return CHAT_SOCKET_PATH;
|
||||
if (normalizedPath === CHAT_SOCKET_PATH) return CHAT_SOCKET_PATH;
|
||||
if (CHAT_SOCKET_PATH.startsWith(`${normalizedPath}/`)) return CHAT_SOCKET_PATH;
|
||||
|
||||
return `${normalizedPath}${CHAT_SOCKET_PATH}`;
|
||||
};
|
||||
|
||||
export const resolveChatSocketUrl = (
|
||||
rawSocketBaseUrl: unknown,
|
||||
currentLocation: LocationLike = window.location,
|
||||
) => {
|
||||
const socketBaseUrl =
|
||||
typeof rawSocketBaseUrl === "string" ? rawSocketBaseUrl.trim() : "";
|
||||
const fallbackProtocol = toWsProtocol(currentLocation.protocol);
|
||||
|
||||
if (!socketBaseUrl) {
|
||||
return `${fallbackProtocol}//${currentLocation.host}${CHAT_SOCKET_PATH}`;
|
||||
}
|
||||
|
||||
if (/^(https?|wss?):\/\//.test(socketBaseUrl)) {
|
||||
const url = new URL(socketBaseUrl);
|
||||
url.protocol = toWsProtocol(url.protocol);
|
||||
url.pathname = resolveChatPath(url.pathname);
|
||||
return url.toString().replace(/\/$/, "");
|
||||
}
|
||||
|
||||
const normalizedPath = socketBaseUrl.startsWith("/")
|
||||
? socketBaseUrl
|
||||
: `/${socketBaseUrl}`;
|
||||
|
||||
return `${fallbackProtocol}//${currentLocation.host}${resolveChatPath(normalizedPath)}`;
|
||||
};
|
||||
Reference in New Issue
Block a user