接入可配置概览服务并支持自适应缩放

This commit is contained in:
baiyanyun
2026-07-13 15:53:42 +08:00
parent 041964132a
commit c8a7bdc0f8
13 changed files with 108 additions and 8 deletions

View File

@@ -185,6 +185,9 @@ eco-market:
mcp:
proxy-base-url: ${MCP_PROXY_URL:http://localhost:8020}
overview-page:
service-url: ${OVERVIEW_SERVICE_URL:http://127.0.0.1:5173/_app/digital}
intelligent-data:
service-url: ${INTELLIGENT_DATA_SERVICE_URL:http://192.168.2.106:3000/test-console}

View File

@@ -185,6 +185,9 @@ eco-market:
mcp:
proxy-base-url: ${MCP_PROXY_URL:http://localhost:18020}
overview-page:
service-url: ${OVERVIEW_SERVICE_URL:http://127.0.0.1:5173/_app/digital}
intelligent-data:
service-url: ${INTELLIGENT_DATA_SERVICE_URL:http://192.168.2.106:3000/test-console}

View File

@@ -182,6 +182,9 @@ eco-market:
mcp:
proxy-base-url: ${MCP_PROXY_URL:http://localhost:8020}
overview-page:
service-url: ${OVERVIEW_SERVICE_URL:http://127.0.0.1:5173/_app/digital}
intelligent-data:
service-url: ${INTELLIGENT_DATA_SERVICE_URL:http://192.168.2.106:3000/test-console}

View File

@@ -182,6 +182,9 @@ eco-market:
mcp:
proxy-base-url: ${MCP_PROXY_URL:http://localhost:8020}
overview-page:
service-url: ${OVERVIEW_SERVICE_URL:http://127.0.0.1:5173/_app/digital}
intelligent-data:
service-url: ${INTELLIGENT_DATA_SERVICE_URL:http://192.168.2.106:3000/test-console}

View File

@@ -179,6 +179,9 @@ public class TenantConfigDto implements Serializable {
@Schema(description = "模型API根地址")
private String baseModelApiUrl;
@Schema(description = "客户端概览服务地址")
private String overviewServiceUrl;
@Schema(description = "智能取数服务地址")
private String intelligentDataServiceUrl;

View File

@@ -72,6 +72,18 @@ public class TenantConfigServiceImpl extends ServiceImpl<TenantConfigMapper, Ten
.required(true)
.sort(1)
.build());
tenantConfigDefaultList.add(TenantConfig.builder()
.name("overviewServiceUrl")
.description("客户端概览服务地址")
.value("http://127.0.0.1:5173/_app/digital")
.notice("客户端点击概览时加载的 HTTP 或 HTTPS 服务地址")
.placeholder("请输入客户端概览服务地址")
.category(TenantConfig.ConfigCategory.BaseConfig)
.inputType(TenantConfig.InputType.Input)
.dataType(TenantConfig.DataType.String)
.required(true)
.sort(2)
.build());
tenantConfigDefaultList.add(TenantConfig.builder()
.name("intelligentDataServiceUrl")
.description("智能取数服务地址")
@@ -82,7 +94,7 @@ public class TenantConfigServiceImpl extends ServiceImpl<TenantConfigMapper, Ten
.inputType(TenantConfig.InputType.Input)
.dataType(TenantConfig.DataType.String)
.required(true)
.sort(2)
.sort(3)
.build());
tenantConfigDefaultList.add(TenantConfig.builder()
.name("documentEditorServiceUrl")
@@ -94,7 +106,7 @@ public class TenantConfigServiceImpl extends ServiceImpl<TenantConfigMapper, Ten
.inputType(TenantConfig.InputType.Input)
.dataType(TenantConfig.DataType.String)
.required(false)
.sort(3)
.sort(4)
.build());
tenantConfigDefaultList.add(TenantConfig.builder()
.name("siteDescription")

View File

@@ -38,6 +38,9 @@ public class TenantController {
@Value("${model-api-proxy.base-api-url:}")
private String baseApiUrl;
@Value("${overview-page.service-url:http://127.0.0.1:5173/_app/digital}")
private String overviewServiceUrl;
@Value("${intelligent-data.service-url:http://192.168.2.106:3000/test-console}")
private String intelligentDataServiceUrl;
@@ -75,6 +78,9 @@ public class TenantController {
tenantConfigDto.setFaviconUrl(iFileAccessService.getFileUrlWithAk(tenantConfigDto.getFaviconUrl(), true));
tenantConfigDto.setSquareBanner(iFileAccessService.getFileUrlWithAk(tenantConfigDto.getSquareBanner(), true));
tenantConfigDto.setMaxFileSize(maxFileSize);
if (StringUtils.isBlank(tenantConfigDto.getOverviewServiceUrl())) {
tenantConfigDto.setOverviewServiceUrl(overviewServiceUrl);
}
if (StringUtils.isBlank(tenantConfigDto.getIntelligentDataServiceUrl())) {
tenantConfigDto.setIntelligentDataServiceUrl(intelligentDataServiceUrl);
}

View File

@@ -127,6 +127,7 @@ export default function BaseFormItem({
],
};
const serviceUrlFieldNames = [
'overviewServiceUrl',
'intelligentDataServiceUrl',
'documentEditorServiceUrl',
];
@@ -135,7 +136,7 @@ export default function BaseFormItem({
)
? [
{
required: props.name === 'intelligentDataServiceUrl',
required: props.name !== 'documentEditorServiceUrl',
message: `请输入${props.description}`,
},
{

View File

@@ -369,6 +369,8 @@ export interface TenantConfigDto {
siteName?: string;
/** 站点描述 */
siteDescription?: string;
/** 客户端概览服务地址 */
overviewServiceUrl?: string;
/** 智能取数服务地址 */
intelligentDataServiceUrl?: string;
/** 文档编辑服务地址 */

View File

@@ -37,6 +37,7 @@ import SetupWizard from "./components/setup/SetupWizard";
import LoginPage from "./components/setup/LoginPage";
import SetupDependencies from "./components/setup/SetupDependencies";
import ClientPage from "./components/pages/ClientPage";
import OverviewPage from "./components/pages/OverviewPage";
import SettingsPage from "./components/pages/SettingsPage";
import DependenciesPage from "./components/pages/DependenciesPage";
import AboutPage from "./components/pages/AboutPage";
@@ -103,6 +104,7 @@ export function useI18nLang(): I18nContextValue {
// Tab 类型定义(对齐 Tauri 客户端)
type TabKey =
| "overview"
| "client"
| "sessions"
| "legacySessions"
@@ -336,7 +338,7 @@ function App() {
// ============================================
// 核心状态
// ============================================
const [activeTab, setActiveTab] = useState<TabKey>("client");
const [activeTab, setActiveTab] = useState<TabKey>("overview");
const [activeNavKey, setActiveNavKey] = useState<NavKey>("overview");
const [sessionsAutoOpen, setSessionsAutoOpen] = useState(false);
const [aiAgentLaunchContext, setAiAgentLaunchContext] =
@@ -1039,7 +1041,7 @@ function App() {
await logout();
await handleAuthChange();
setIsLoggedIn(false);
setActiveTab("client");
setActiveTab("overview");
setActiveNavKey("overview");
setServices([]);
setServicesLoading(false);
@@ -1057,7 +1059,7 @@ function App() {
setActiveNavKey(key);
if (key === "overview") {
setActiveTab("client");
setActiveTab("overview");
return;
}
@@ -1109,7 +1111,7 @@ function App() {
return;
}
setActiveTab("client");
setActiveTab("overview");
}, []);
const handleLegacySelect = useCallback((key: LegacyTabKey) => {
@@ -1358,6 +1360,7 @@ function App() {
<div
className={
webviewActions ||
activeTab === "overview" ||
activeTab === "document" ||
activeTab === "dataFetch"
? "app-content app-content-fullwidth"
@@ -1373,6 +1376,7 @@ function App() {
background: "var(--color-bg-layout)",
}}
>
{activeTab === "overview" && <OverviewPage />}
{activeTab === "client" && (
<ClientPage
onNavigate={(tab) => {

View File

@@ -6,13 +6,16 @@ import styles from "../../styles/components/ConfiguredServicePage.module.css";
interface ConfiguredServicePageProps {
title: string;
loadServiceUrl: () => Promise<string>;
fitContentWidth?: number;
}
function ConfiguredServicePage({
title,
loadServiceUrl,
fitContentWidth,
}: ConfiguredServicePageProps) {
const webviewRef = useRef<HTMLElement | null>(null);
const webviewReadyRef = useRef(false);
const [serviceUrl, setServiceUrl] = useState<string | null>(null);
const [loadingConfig, setLoadingConfig] = useState(true);
const [loadingPage, setLoadingPage] = useState(false);
@@ -37,14 +40,36 @@ function ConfiguredServicePage({
void loadService();
}, [loadService]);
const applyPageScale = useCallback(() => {
if (!fitContentWidth || !webviewReadyRef.current) return;
const element = webviewRef.current as any;
const availableWidth = element?.clientWidth;
if (!availableWidth || typeof element?.setZoomFactor !== "function") {
return;
}
const zoomFactor = Math.max(
0.35,
Math.min(1, availableWidth / fitContentWidth),
);
element.setZoomFactor(Number(zoomFactor.toFixed(3)));
}, [fitContentWidth]);
useEffect(() => {
const element = webviewRef.current as any;
if (!element || !serviceUrl) return;
webviewReadyRef.current = false;
const handleStart = () => {
setLoadingPage(true);
setError(null);
};
const handleReady = () => {
webviewReadyRef.current = true;
applyPageScale();
};
const handleStop = () => setLoadingPage(false);
const handleFailure = (event: any) => {
if (event.errorCode !== -3) {
@@ -56,14 +81,24 @@ function ConfiguredServicePage({
};
element.addEventListener("did-start-loading", handleStart);
element.addEventListener("dom-ready", handleReady);
element.addEventListener("did-stop-loading", handleStop);
element.addEventListener("did-fail-load", handleFailure);
const resizeObserver = fitContentWidth
? new ResizeObserver(applyPageScale)
: null;
resizeObserver?.observe(element);
return () => {
resizeObserver?.disconnect();
webviewReadyRef.current = false;
element.removeEventListener("did-start-loading", handleStart);
element.removeEventListener("dom-ready", handleReady);
element.removeEventListener("did-stop-loading", handleStop);
element.removeEventListener("did-fail-load", handleFailure);
};
}, [serviceUrl, title]);
}, [applyPageScale, fitContentWidth, serviceUrl, title]);
const handleReload = useCallback(() => {
const element = webviewRef.current as any;

View File

@@ -0,0 +1,15 @@
import React from "react";
import { fetchOverviewServiceUrl } from "../../services/core/servicePages";
import ConfiguredServicePage from "./ConfiguredServicePage";
function OverviewPage() {
return (
<ConfiguredServicePage
title="概览"
loadServiceUrl={fetchOverviewServiceUrl}
fitContentWidth={1920}
/>
);
}
export default OverviewPage;

View File

@@ -7,12 +7,14 @@ import { getDomainTokenKey } from "@shared/utils/domain";
import { normalizeServerHost } from "./auth";
const SUCCESS_CODE = "0000";
const OVERVIEW_FALLBACK_URL = "http://127.0.0.1:5173/_app/digital";
const INTELLIGENT_DATA_FALLBACK_URL = "http://192.168.2.106:3000/test-console";
interface TenantConfigResponse {
code: string;
message?: string;
data?: {
overviewServiceUrl?: string;
intelligentDataServiceUrl?: string;
documentEditorServiceUrl?: string;
};
@@ -77,6 +79,14 @@ async function fetchTenantConfig(): Promise<TenantConfigResponse["data"]> {
return result.data;
}
export async function fetchOverviewServiceUrl(): Promise<string> {
const config = await fetchTenantConfig();
return normalizeServiceUrl(
config?.overviewServiceUrl || OVERVIEW_FALLBACK_URL,
"概览",
);
}
export async function fetchIntelligentDataServiceUrl(): Promise<string> {
const config = await fetchTenantConfig();
return normalizeServiceUrl(