| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349 |
- import { Component, OnInit, HostListener, ChangeDetectorRef, AfterViewInit, ElementRef, Renderer2, OnDestroy, ViewChild } from '@angular/core';
- import { StickyHeaderComponent } from '../../components/sticky-header/sticky-header.component';
- import { CommonModule } from '@angular/common';
- import { MatCard, MatCardContent } from '@angular/material/card';
-
- import { MatTableModule } from '@angular/material/table';
- import { MatProgressSpinner } from '@angular/material/progress-spinner';
- import { ConfigMetaService } from '../../services/config-meta.service';
- import { ConfigService } from '../../services/config.service';
- import { ToastService } from '../../services/toast.service';
- import { ConfigMeta } from '../../models/config-meta.model';
- import { HttpErrorResponse } from '@angular/common/http';
-
- @Component({
- selector: 'app-service-register-config',
- imports: [CommonModule, MatCard, MatCardContent, MatTableModule, MatProgressSpinner, StickyHeaderComponent],
- templateUrl: './service-register-config.component.html',
- styleUrl: './service-register-config.component.scss'
- })
- export class ServiceRegisterConfigComponent implements OnInit, AfterViewInit, OnDestroy {
- title = '注册服务配置';
- hintText = '点击"注册"按钮将同步所有配置元信息到数据库,并显示配置列表。';
- displayedColumns: string[] = ['configName', 'fieldName', 'fieldType', 'yamlName', 'fieldDesc'];
- configMetaList: ConfigMeta[] = [];
- loading = false;
- registering = false;
- isScrolled = false; // 向后兼容
- isLocked = false; // 是否锁定在顶部
- isCompact = false; // 是否缩小状态
- scrollThreshold = 2; // 锁定阈值
- compactThreshold = 50; // 缩小阈值(锁定后进一步滚动多少像素开始缩小)
-
- private scrollContainer: HTMLElement | Window = window;
- private scrollListener: (() => void) | null = null;
- private rafId: number | null = null;
-
- debugInfo = {
- dataSource: '',
- recordCount: 0,
- lastUpdated: '',
- useMockData: false
- };
-
- @ViewChild('matCard', { read: ElementRef }) matCardRef!: ElementRef;
- headerWidth: number | null = null;
- headerLeft: number | null = null;
- private resizeListener: (() => void) | null = null;
-
- lastRegisterUrl = '';
- lastListUrl = '';
-
- constructor(
- private configMetaService: ConfigMetaService,
- private configService: ConfigService,
- private toastService: ToastService,
- private cdRef: ChangeDetectorRef,
- private elementRef: ElementRef,
- private renderer: Renderer2
- ) {}
-
- ngOnInit() {
- this.loadConfigMeta();
- }
-
- ngAfterViewInit() {
- this.findScrollContainer();
- this.setupScrollListener();
- this.checkScroll();
-
- // 添加窗口resize监听器
- this.resizeListener = () => this.onResize();
- window.addEventListener('resize', this.resizeListener, { passive: true });
- }
-
- private onResize() {
- // 只在锁定状态时更新尺寸
- if (this.isLocked && this.matCardRef?.nativeElement) {
- console.log('窗口大小变化,更新标题栏尺寸');
- this.updateHeaderDimensions();
- }
- }
-
- ngOnDestroy() {
- this.cleanupScrollListener();
-
- // 清理resize监听器
- if (this.resizeListener) {
- window.removeEventListener('resize', this.resizeListener);
- this.resizeListener = null;
- }
- }
-
- @HostListener('window:scroll', [])
- onWindowScroll() {
- // 保留window滚动监听作为备用
- if (this.scrollContainer === window) {
- this.onScroll();
- }
- }
-
- private findScrollContainer() {
- // 向上查找最近的滚动容器
- let element: HTMLElement | null = this.elementRef.nativeElement.parentElement;
-
- while (element && element !== document.body) {
- const style = getComputedStyle(element);
- const hasOverflow = style.overflow === 'auto' || style.overflow === 'scroll' ||
- style.overflowY === 'auto' || style.overflowY === 'scroll';
-
- if (hasOverflow) {
- console.log('找到滚动容器:', element, 'clientHeight:', element.clientHeight, 'scrollHeight:', element.scrollHeight);
- this.scrollContainer = element;
- return;
- }
-
- element = element.parentElement;
- }
-
- // 未找到合适的容器,使用window
- console.log('未找到滚动容器,使用window');
- this.scrollContainer = window;
- }
-
- private setupScrollListener() {
- if (this.scrollContainer === window) {
- this.scrollListener = () => this.onScroll();
- window.addEventListener('scroll', this.scrollListener, { passive: true });
- } else {
- this.scrollListener = () => this.onScroll();
- (this.scrollContainer as HTMLElement).addEventListener('scroll', this.scrollListener, { passive: true });
- }
- }
-
- private cleanupScrollListener() {
- if (this.scrollListener) {
- if (this.scrollContainer === window) {
- window.removeEventListener('scroll', this.scrollListener);
- } else {
- (this.scrollContainer as HTMLElement).removeEventListener('scroll', this.scrollListener);
- }
- this.scrollListener = null;
- }
-
- if (this.rafId !== null) {
- cancelAnimationFrame(this.rafId);
- this.rafId = null;
- }
- }
-
- private onScroll() {
- if (this.rafId !== null) {
- cancelAnimationFrame(this.rafId);
- }
-
- this.rafId = requestAnimationFrame(() => {
- this.checkScroll();
- });
- }
-
- private updateHeaderDimensions() {
- if (!this.matCardRef?.nativeElement) return;
-
- const matCard = this.matCardRef.nativeElement;
- const rect = matCard.getBoundingClientRect();
-
- // 计算mat-card在视口中的位置和宽度
- this.headerWidth = rect.width;
- this.headerLeft = rect.left;
-
- console.log('更新标题栏尺寸:', {
- width: this.headerWidth,
- left: this.headerLeft,
- matCardRect: rect
- });
-
- this.cdRef.detectChanges();
- }
-
- private checkScroll() {
- if (!this.scrollContainer) return;
-
- let scrollTop = 0;
- let containerType = 'unknown';
-
- if (this.scrollContainer === window) {
- scrollTop = window.scrollY || document.documentElement.scrollTop;
- containerType = 'window';
- } else {
- scrollTop = (this.scrollContainer as HTMLElement).scrollTop;
- containerType = 'element';
- }
-
- // 向后兼容:保持isScrolled逻辑
- const scrolled = scrollTop > this.scrollThreshold;
-
-
-
- // 阶段1: 检测是否锁定(开始滚动就锁定)
- const shouldLock = scrollTop > 0;
-
- // 阶段2: 检测是否缩小(锁定后继续滚动)
- const shouldCompact = shouldLock && scrollTop > this.compactThreshold;
-
- console.log('滚动检测:', {
- container: containerType,
- scrollTop,
- scrollThreshold: this.scrollThreshold,
- compactThreshold: this.compactThreshold,
- shouldLock,
- shouldCompact,
- scrolled, // 向后兼容
- isLocked: this.isLocked,
- isCompact: this.isCompact
- });
-
- // 更新状态
- let needsUpdate = false;
-
- if (this.isScrolled !== scrolled) {
- this.isScrolled = scrolled;
- needsUpdate = true;
- }
-
- if (this.isLocked !== shouldLock) {
- this.isLocked = shouldLock;
- needsUpdate = true;
- console.log('锁定状态变化:', this.isLocked);
-
- if (this.isLocked) {
- // 锁定状态:计算并更新标题栏尺寸
- this.updateHeaderDimensions();
- } else {
- // 解锁状态:清除尺寸,让标题栏恢复原始定位
- this.headerWidth = null;
- this.headerLeft = null;
- }
- }
-
- if (this.isCompact !== shouldCompact) {
- this.isCompact = shouldCompact;
- needsUpdate = true;
- console.log('缩小状态变化:', this.isCompact);
- }
-
- if (needsUpdate) {
- this.cdRef.detectChanges();
- }
- }
-
- onRegister() {
- this.lastRegisterUrl = this.configMetaService.getInitUrl();
- console.debug('[注册] 请求URL:', this.lastRegisterUrl);
- this.registering = true;
- this.configMetaService.initConfigMeta().subscribe({
- next: (response) => {
- this.registering = false;
- if (response.success) {
- this.toastService.success('配置元信息注册成功');
- this.loadConfigMeta();
- } else {
- const errorMsg = this.extractErrorMessageFromResponse(response);
- this.toastService.error(`注册失败:${errorMsg}`);
- }
- },
- error: (error) => {
- this.registering = false;
- const errorMsg = this.extractErrorMessage(error);
- this.toastService.error(`注册失败:${errorMsg}`);
- console.debug('注册配置元信息失败', error);
- }
- });
- }
-
- loadConfigMeta() {
- this.lastListUrl = this.configMetaService.getListUrl();
- console.debug('[加载] 请求URL:', this.lastListUrl);
- this.loading = true;
- this.configMetaService.listConfigMeta().subscribe({
- next: (list) => {
- this.configMetaList = list;
- this.loading = false;
- // 更新调试信息
- const source = this.configService.useMockData ? '模拟数据' : 'API数据';
- this.updateDebugInfo(list, source);
- },
- error: (error) => {
- this.loading = false;
- const errorMsg = this.extractErrorMessage(error);
- this.toastService.error(`加载配置元信息失败:${errorMsg}`);
- console.debug('加载配置元信息失败', error);
- // 错误时也更新调试信息
- this.updateDebugInfo([], `API调用失败: ${errorMsg}`);
- }
- });
- }
-
- private updateDebugInfo(list: ConfigMeta[], source: string) {
- // 轻量级调试信息更新,不影响性能
- this.debugInfo = {
- dataSource: source,
- recordCount: list.length,
- lastUpdated: new Date().toLocaleTimeString(),
- useMockData: this.configService.useMockData
- };
-
- // 控制台调试日志(仅在开发时查看)
- console.debug('[调试] 数据源:', source);
- console.debug('[调试] 记录数:', list.length);
- console.debug('[调试] 使用模拟数据:', this.configService.useMockData);
- if (list.length > 0) {
- console.debug('[调试] 首条数据示例:', list[0]);
- }
- }
-
- private extractErrorMessage(error: any): string {
- if (error instanceof HttpErrorResponse) {
- // HTTP错误
- const status = error.status;
- const errorBody = error.error;
- console.debug(`[错误详情] HTTP ${status} ${error.url}`);
- console.debug(`[错误详情] 错误响应:`, errorBody);
-
- if (errorBody?.error) {
- return `HTTP ${status}: ${errorBody.error}`;
- } else if (errorBody?.message) {
- return `HTTP ${status}: ${errorBody.message}`;
- } else if (error.message) {
- return `HTTP ${status}: ${error.message}`;
- } else {
- return `HTTP错误 ${status}`;
- }
- } else if (error?.error) {
- // 可能已经是QueryResult格式
- return error.error.error || error.error.message || error.message || '未知错误';
- } else if (error?.message) {
- return error.message;
- }
- return '未知错误';
- }
-
- private extractErrorMessageFromResponse(response: any): string {
- if (response?.error) {
- return response.error;
- } else if (response?.message) {
- return response.message;
- }
- return '未知错误';
- }
- }
|