接入可配置概览服务并支持自适应缩放
This commit is contained in:
@@ -185,6 +185,9 @@ eco-market:
|
|||||||
mcp:
|
mcp:
|
||||||
proxy-base-url: ${MCP_PROXY_URL:http://localhost:8020}
|
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:
|
intelligent-data:
|
||||||
service-url: ${INTELLIGENT_DATA_SERVICE_URL:http://192.168.2.106:3000/test-console}
|
service-url: ${INTELLIGENT_DATA_SERVICE_URL:http://192.168.2.106:3000/test-console}
|
||||||
|
|
||||||
|
|||||||
@@ -185,6 +185,9 @@ eco-market:
|
|||||||
mcp:
|
mcp:
|
||||||
proxy-base-url: ${MCP_PROXY_URL:http://localhost:18020}
|
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:
|
intelligent-data:
|
||||||
service-url: ${INTELLIGENT_DATA_SERVICE_URL:http://192.168.2.106:3000/test-console}
|
service-url: ${INTELLIGENT_DATA_SERVICE_URL:http://192.168.2.106:3000/test-console}
|
||||||
|
|
||||||
|
|||||||
@@ -182,6 +182,9 @@ eco-market:
|
|||||||
mcp:
|
mcp:
|
||||||
proxy-base-url: ${MCP_PROXY_URL:http://localhost:8020}
|
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:
|
intelligent-data:
|
||||||
service-url: ${INTELLIGENT_DATA_SERVICE_URL:http://192.168.2.106:3000/test-console}
|
service-url: ${INTELLIGENT_DATA_SERVICE_URL:http://192.168.2.106:3000/test-console}
|
||||||
|
|
||||||
|
|||||||
@@ -182,6 +182,9 @@ eco-market:
|
|||||||
mcp:
|
mcp:
|
||||||
proxy-base-url: ${MCP_PROXY_URL:http://localhost:8020}
|
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:
|
intelligent-data:
|
||||||
service-url: ${INTELLIGENT_DATA_SERVICE_URL:http://192.168.2.106:3000/test-console}
|
service-url: ${INTELLIGENT_DATA_SERVICE_URL:http://192.168.2.106:3000/test-console}
|
||||||
|
|
||||||
|
|||||||
@@ -179,6 +179,9 @@ public class TenantConfigDto implements Serializable {
|
|||||||
@Schema(description = "模型API根地址")
|
@Schema(description = "模型API根地址")
|
||||||
private String baseModelApiUrl;
|
private String baseModelApiUrl;
|
||||||
|
|
||||||
|
@Schema(description = "客户端概览服务地址")
|
||||||
|
private String overviewServiceUrl;
|
||||||
|
|
||||||
@Schema(description = "智能取数服务地址")
|
@Schema(description = "智能取数服务地址")
|
||||||
private String intelligentDataServiceUrl;
|
private String intelligentDataServiceUrl;
|
||||||
|
|
||||||
|
|||||||
@@ -72,6 +72,18 @@ public class TenantConfigServiceImpl extends ServiceImpl<TenantConfigMapper, Ten
|
|||||||
.required(true)
|
.required(true)
|
||||||
.sort(1)
|
.sort(1)
|
||||||
.build());
|
.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()
|
tenantConfigDefaultList.add(TenantConfig.builder()
|
||||||
.name("intelligentDataServiceUrl")
|
.name("intelligentDataServiceUrl")
|
||||||
.description("智能取数服务地址")
|
.description("智能取数服务地址")
|
||||||
@@ -82,7 +94,7 @@ public class TenantConfigServiceImpl extends ServiceImpl<TenantConfigMapper, Ten
|
|||||||
.inputType(TenantConfig.InputType.Input)
|
.inputType(TenantConfig.InputType.Input)
|
||||||
.dataType(TenantConfig.DataType.String)
|
.dataType(TenantConfig.DataType.String)
|
||||||
.required(true)
|
.required(true)
|
||||||
.sort(2)
|
.sort(3)
|
||||||
.build());
|
.build());
|
||||||
tenantConfigDefaultList.add(TenantConfig.builder()
|
tenantConfigDefaultList.add(TenantConfig.builder()
|
||||||
.name("documentEditorServiceUrl")
|
.name("documentEditorServiceUrl")
|
||||||
@@ -94,7 +106,7 @@ public class TenantConfigServiceImpl extends ServiceImpl<TenantConfigMapper, Ten
|
|||||||
.inputType(TenantConfig.InputType.Input)
|
.inputType(TenantConfig.InputType.Input)
|
||||||
.dataType(TenantConfig.DataType.String)
|
.dataType(TenantConfig.DataType.String)
|
||||||
.required(false)
|
.required(false)
|
||||||
.sort(3)
|
.sort(4)
|
||||||
.build());
|
.build());
|
||||||
tenantConfigDefaultList.add(TenantConfig.builder()
|
tenantConfigDefaultList.add(TenantConfig.builder()
|
||||||
.name("siteDescription")
|
.name("siteDescription")
|
||||||
|
|||||||
@@ -38,6 +38,9 @@ public class TenantController {
|
|||||||
@Value("${model-api-proxy.base-api-url:}")
|
@Value("${model-api-proxy.base-api-url:}")
|
||||||
private String baseApiUrl;
|
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}")
|
@Value("${intelligent-data.service-url:http://192.168.2.106:3000/test-console}")
|
||||||
private String intelligentDataServiceUrl;
|
private String intelligentDataServiceUrl;
|
||||||
|
|
||||||
@@ -75,6 +78,9 @@ public class TenantController {
|
|||||||
tenantConfigDto.setFaviconUrl(iFileAccessService.getFileUrlWithAk(tenantConfigDto.getFaviconUrl(), true));
|
tenantConfigDto.setFaviconUrl(iFileAccessService.getFileUrlWithAk(tenantConfigDto.getFaviconUrl(), true));
|
||||||
tenantConfigDto.setSquareBanner(iFileAccessService.getFileUrlWithAk(tenantConfigDto.getSquareBanner(), true));
|
tenantConfigDto.setSquareBanner(iFileAccessService.getFileUrlWithAk(tenantConfigDto.getSquareBanner(), true));
|
||||||
tenantConfigDto.setMaxFileSize(maxFileSize);
|
tenantConfigDto.setMaxFileSize(maxFileSize);
|
||||||
|
if (StringUtils.isBlank(tenantConfigDto.getOverviewServiceUrl())) {
|
||||||
|
tenantConfigDto.setOverviewServiceUrl(overviewServiceUrl);
|
||||||
|
}
|
||||||
if (StringUtils.isBlank(tenantConfigDto.getIntelligentDataServiceUrl())) {
|
if (StringUtils.isBlank(tenantConfigDto.getIntelligentDataServiceUrl())) {
|
||||||
tenantConfigDto.setIntelligentDataServiceUrl(intelligentDataServiceUrl);
|
tenantConfigDto.setIntelligentDataServiceUrl(intelligentDataServiceUrl);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -127,6 +127,7 @@ export default function BaseFormItem({
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
const serviceUrlFieldNames = [
|
const serviceUrlFieldNames = [
|
||||||
|
'overviewServiceUrl',
|
||||||
'intelligentDataServiceUrl',
|
'intelligentDataServiceUrl',
|
||||||
'documentEditorServiceUrl',
|
'documentEditorServiceUrl',
|
||||||
];
|
];
|
||||||
@@ -135,7 +136,7 @@ export default function BaseFormItem({
|
|||||||
)
|
)
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
required: props.name === 'intelligentDataServiceUrl',
|
required: props.name !== 'documentEditorServiceUrl',
|
||||||
message: `请输入${props.description}`,
|
message: `请输入${props.description}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -369,6 +369,8 @@ export interface TenantConfigDto {
|
|||||||
siteName?: string;
|
siteName?: string;
|
||||||
/** 站点描述 */
|
/** 站点描述 */
|
||||||
siteDescription?: string;
|
siteDescription?: string;
|
||||||
|
/** 客户端概览服务地址 */
|
||||||
|
overviewServiceUrl?: string;
|
||||||
/** 智能取数服务地址 */
|
/** 智能取数服务地址 */
|
||||||
intelligentDataServiceUrl?: string;
|
intelligentDataServiceUrl?: string;
|
||||||
/** 文档编辑服务地址 */
|
/** 文档编辑服务地址 */
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import SetupWizard from "./components/setup/SetupWizard";
|
|||||||
import LoginPage from "./components/setup/LoginPage";
|
import LoginPage from "./components/setup/LoginPage";
|
||||||
import SetupDependencies from "./components/setup/SetupDependencies";
|
import SetupDependencies from "./components/setup/SetupDependencies";
|
||||||
import ClientPage from "./components/pages/ClientPage";
|
import ClientPage from "./components/pages/ClientPage";
|
||||||
|
import OverviewPage from "./components/pages/OverviewPage";
|
||||||
import SettingsPage from "./components/pages/SettingsPage";
|
import SettingsPage from "./components/pages/SettingsPage";
|
||||||
import DependenciesPage from "./components/pages/DependenciesPage";
|
import DependenciesPage from "./components/pages/DependenciesPage";
|
||||||
import AboutPage from "./components/pages/AboutPage";
|
import AboutPage from "./components/pages/AboutPage";
|
||||||
@@ -103,6 +104,7 @@ export function useI18nLang(): I18nContextValue {
|
|||||||
|
|
||||||
// Tab 类型定义(对齐 Tauri 客户端)
|
// Tab 类型定义(对齐 Tauri 客户端)
|
||||||
type TabKey =
|
type TabKey =
|
||||||
|
| "overview"
|
||||||
| "client"
|
| "client"
|
||||||
| "sessions"
|
| "sessions"
|
||||||
| "legacySessions"
|
| "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 [activeNavKey, setActiveNavKey] = useState<NavKey>("overview");
|
||||||
const [sessionsAutoOpen, setSessionsAutoOpen] = useState(false);
|
const [sessionsAutoOpen, setSessionsAutoOpen] = useState(false);
|
||||||
const [aiAgentLaunchContext, setAiAgentLaunchContext] =
|
const [aiAgentLaunchContext, setAiAgentLaunchContext] =
|
||||||
@@ -1039,7 +1041,7 @@ function App() {
|
|||||||
await logout();
|
await logout();
|
||||||
await handleAuthChange();
|
await handleAuthChange();
|
||||||
setIsLoggedIn(false);
|
setIsLoggedIn(false);
|
||||||
setActiveTab("client");
|
setActiveTab("overview");
|
||||||
setActiveNavKey("overview");
|
setActiveNavKey("overview");
|
||||||
setServices([]);
|
setServices([]);
|
||||||
setServicesLoading(false);
|
setServicesLoading(false);
|
||||||
@@ -1057,7 +1059,7 @@ function App() {
|
|||||||
setActiveNavKey(key);
|
setActiveNavKey(key);
|
||||||
|
|
||||||
if (key === "overview") {
|
if (key === "overview") {
|
||||||
setActiveTab("client");
|
setActiveTab("overview");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1109,7 +1111,7 @@ function App() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setActiveTab("client");
|
setActiveTab("overview");
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleLegacySelect = useCallback((key: LegacyTabKey) => {
|
const handleLegacySelect = useCallback((key: LegacyTabKey) => {
|
||||||
@@ -1358,6 +1360,7 @@ function App() {
|
|||||||
<div
|
<div
|
||||||
className={
|
className={
|
||||||
webviewActions ||
|
webviewActions ||
|
||||||
|
activeTab === "overview" ||
|
||||||
activeTab === "document" ||
|
activeTab === "document" ||
|
||||||
activeTab === "dataFetch"
|
activeTab === "dataFetch"
|
||||||
? "app-content app-content-fullwidth"
|
? "app-content app-content-fullwidth"
|
||||||
@@ -1373,6 +1376,7 @@ function App() {
|
|||||||
background: "var(--color-bg-layout)",
|
background: "var(--color-bg-layout)",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
{activeTab === "overview" && <OverviewPage />}
|
||||||
{activeTab === "client" && (
|
{activeTab === "client" && (
|
||||||
<ClientPage
|
<ClientPage
|
||||||
onNavigate={(tab) => {
|
onNavigate={(tab) => {
|
||||||
|
|||||||
@@ -6,13 +6,16 @@ import styles from "../../styles/components/ConfiguredServicePage.module.css";
|
|||||||
interface ConfiguredServicePageProps {
|
interface ConfiguredServicePageProps {
|
||||||
title: string;
|
title: string;
|
||||||
loadServiceUrl: () => Promise<string>;
|
loadServiceUrl: () => Promise<string>;
|
||||||
|
fitContentWidth?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ConfiguredServicePage({
|
function ConfiguredServicePage({
|
||||||
title,
|
title,
|
||||||
loadServiceUrl,
|
loadServiceUrl,
|
||||||
|
fitContentWidth,
|
||||||
}: ConfiguredServicePageProps) {
|
}: ConfiguredServicePageProps) {
|
||||||
const webviewRef = useRef<HTMLElement | null>(null);
|
const webviewRef = useRef<HTMLElement | null>(null);
|
||||||
|
const webviewReadyRef = useRef(false);
|
||||||
const [serviceUrl, setServiceUrl] = useState<string | null>(null);
|
const [serviceUrl, setServiceUrl] = useState<string | null>(null);
|
||||||
const [loadingConfig, setLoadingConfig] = useState(true);
|
const [loadingConfig, setLoadingConfig] = useState(true);
|
||||||
const [loadingPage, setLoadingPage] = useState(false);
|
const [loadingPage, setLoadingPage] = useState(false);
|
||||||
@@ -37,14 +40,36 @@ function ConfiguredServicePage({
|
|||||||
void loadService();
|
void loadService();
|
||||||
}, [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(() => {
|
useEffect(() => {
|
||||||
const element = webviewRef.current as any;
|
const element = webviewRef.current as any;
|
||||||
if (!element || !serviceUrl) return;
|
if (!element || !serviceUrl) return;
|
||||||
|
|
||||||
|
webviewReadyRef.current = false;
|
||||||
|
|
||||||
const handleStart = () => {
|
const handleStart = () => {
|
||||||
setLoadingPage(true);
|
setLoadingPage(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
};
|
};
|
||||||
|
const handleReady = () => {
|
||||||
|
webviewReadyRef.current = true;
|
||||||
|
applyPageScale();
|
||||||
|
};
|
||||||
const handleStop = () => setLoadingPage(false);
|
const handleStop = () => setLoadingPage(false);
|
||||||
const handleFailure = (event: any) => {
|
const handleFailure = (event: any) => {
|
||||||
if (event.errorCode !== -3) {
|
if (event.errorCode !== -3) {
|
||||||
@@ -56,14 +81,24 @@ function ConfiguredServicePage({
|
|||||||
};
|
};
|
||||||
|
|
||||||
element.addEventListener("did-start-loading", handleStart);
|
element.addEventListener("did-start-loading", handleStart);
|
||||||
|
element.addEventListener("dom-ready", handleReady);
|
||||||
element.addEventListener("did-stop-loading", handleStop);
|
element.addEventListener("did-stop-loading", handleStop);
|
||||||
element.addEventListener("did-fail-load", handleFailure);
|
element.addEventListener("did-fail-load", handleFailure);
|
||||||
|
|
||||||
|
const resizeObserver = fitContentWidth
|
||||||
|
? new ResizeObserver(applyPageScale)
|
||||||
|
: null;
|
||||||
|
resizeObserver?.observe(element);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
|
resizeObserver?.disconnect();
|
||||||
|
webviewReadyRef.current = false;
|
||||||
element.removeEventListener("did-start-loading", handleStart);
|
element.removeEventListener("did-start-loading", handleStart);
|
||||||
|
element.removeEventListener("dom-ready", handleReady);
|
||||||
element.removeEventListener("did-stop-loading", handleStop);
|
element.removeEventListener("did-stop-loading", handleStop);
|
||||||
element.removeEventListener("did-fail-load", handleFailure);
|
element.removeEventListener("did-fail-load", handleFailure);
|
||||||
};
|
};
|
||||||
}, [serviceUrl, title]);
|
}, [applyPageScale, fitContentWidth, serviceUrl, title]);
|
||||||
|
|
||||||
const handleReload = useCallback(() => {
|
const handleReload = useCallback(() => {
|
||||||
const element = webviewRef.current as any;
|
const element = webviewRef.current as any;
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -7,12 +7,14 @@ import { getDomainTokenKey } from "@shared/utils/domain";
|
|||||||
import { normalizeServerHost } from "./auth";
|
import { normalizeServerHost } from "./auth";
|
||||||
|
|
||||||
const SUCCESS_CODE = "0000";
|
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";
|
const INTELLIGENT_DATA_FALLBACK_URL = "http://192.168.2.106:3000/test-console";
|
||||||
|
|
||||||
interface TenantConfigResponse {
|
interface TenantConfigResponse {
|
||||||
code: string;
|
code: string;
|
||||||
message?: string;
|
message?: string;
|
||||||
data?: {
|
data?: {
|
||||||
|
overviewServiceUrl?: string;
|
||||||
intelligentDataServiceUrl?: string;
|
intelligentDataServiceUrl?: string;
|
||||||
documentEditorServiceUrl?: string;
|
documentEditorServiceUrl?: string;
|
||||||
};
|
};
|
||||||
@@ -77,6 +79,14 @@ async function fetchTenantConfig(): Promise<TenantConfigResponse["data"]> {
|
|||||||
return result.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> {
|
export async function fetchIntelligentDataServiceUrl(): Promise<string> {
|
||||||
const config = await fetchTenantConfig();
|
const config = await fetchTenantConfig();
|
||||||
return normalizeServiceUrl(
|
return normalizeServiceUrl(
|
||||||
|
|||||||
Reference in New Issue
Block a user