Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 30 additions & 22 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,37 +1,45 @@
# Flexmodel CI/CD Pipeline
# Runs tests for all modules, then builds and pushes Docker images.
# Deploys to production on push to main.
# Flexmodel UI CI
# 可手动触发选择:只跑测试、只构建镜像、或两者都跑。
# push / PR 默认只跑测试(lint + 类型检查)。
# 镜像版本号取自 VERSION 文件:${VERSION}、${VERSION}.${run_number} 等

name: Run Tests
name: Flexmodel UI

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
workflow_dispatch:
inputs:
run_tests:
description: "执行测试(lint + 类型检查)"
type: boolean
default: true
run_build:
description: "执行构建并推送 Docker 镜像"
type: boolean
default: false

jobs:

push-ui-image:
name: Push UI docker image
ui-test:
name: UI tests (lint + type-check)
runs-on: ubuntu-latest
if: ${{ github.event_name != 'workflow_dispatch' || github.event.inputs.run_tests == 'true' }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
submodules: 'recursive'
token: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push UI image
uses: docker/build-push-action@v6
- name: Set up Node
uses: actions/setup-node@v5
with:
context: .
push: true
tags: cjbi/flexmodel-ui:latest
cache-from: type=gha
cache-to: type=gha,mode=max
node-version: '22'
cache: 'npm'
cache-dependency-path: 'package-lock.json'
- name: Install dependencies
run: npm ci
- name: Lint
run: npm run lint
- name: Type check
run: npx tsc --noEmit
7 changes: 6 additions & 1 deletion eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,12 @@ export default tseslint.config(
'warn',
{ allowConstantExport: true },
],
"@typescript-eslint/no-explicit-any": ["off"]
"@typescript-eslint/no-explicit-any": ["off"],
"@typescript-eslint/no-unused-vars": ["error", {
"argsIgnorePattern": "^_",
"varsIgnorePattern": "^_",
"ignoreRestSiblings": true
}]
},
},
)
34 changes: 17 additions & 17 deletions src/components/console/Console.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const Console: React.FC<ConsoleProps> = ({ onToggle, projectId }) => {
const [searchKeyword, setSearchKeyword] = useState('');
const [debouncedSearchKeyword, setDebouncedSearchKeyword] = useState('');
const [fontSize, setFontSize] = useState(12);
const [displayLimit, setDisplayLimit] = useState<number>(100); // 默认显示100条
const [displayLimit, setDisplayLimit] = useState<number>(100); // 默认显示100�

// 使用WebSocket Hook
const {
Expand All @@ -44,35 +44,35 @@ const Console: React.FC<ConsoleProps> = ({ onToggle, projectId }) => {
logsEndRef,
setAutoScrollEnabled
} = useConsoleLogs({
maxLogs: 1000, // 减少到1000条以提高性能
maxLogs: 1000, // 减少�000条以提高性能
autoScroll: true,
projectId,
});

// 始终置底开关:选中后不管是否滚动,始终保持在底部
// 始终置底开关:选中后不管是否滚动,始终保持在底�
const [stayAtBottom, setStayAtBottom] = useState(false);

// 选中“始终置底”时,确保自动滚动始终开启
// 选中“始终置底”时,确保自动滚动始终开�
useEffect(() => {
if (stayAtBottom) {
setAutoScrollEnabled(true);
}
}, [stayAtBottom, setAutoScrollEnabled]);

// 根据滚动位置更新自动滚动开关
// 根据滚动位置更新自动滚动开�
const handleContentScroll = useCallback((e: React.UIEvent<HTMLDivElement>) => {
if (stayAtBottom) {
// 强制保持在底部
// 强制保持在底�
setAutoScrollEnabled(true);
if (logsEndRef.current) {
// 使用同步滚动,避免视觉抖动
// 使用同步滚动,避免视觉抖�
logsEndRef.current.scrollIntoView({ behavior: 'auto' });
}
return;
}

const target = e.currentTarget;
const threshold = 16; // 离底部阈值
const threshold = 16; // 离底部阈�
const distanceToBottom = target.scrollHeight - target.scrollTop - target.clientHeight;
const isNearBottom = distanceToBottom <= threshold;
setAutoScrollEnabled(isNearBottom);
Expand All @@ -92,7 +92,7 @@ const Console: React.FC<ConsoleProps> = ({ onToggle, projectId }) => {
}
}, [isConnected, reconnect, t]);

// 手动滚动到底部
// 手动滚动到底�
const scrollToBottom = useCallback(() => {
if (logsEndRef.current) {
logsEndRef.current.scrollIntoView({ behavior: 'smooth' });
Expand All @@ -108,15 +108,15 @@ const Console: React.FC<ConsoleProps> = ({ onToggle, projectId }) => {
return () => clearTimeout(timer);
}, [searchKeyword]);

// 过滤日志 - 只按关键词过滤
// 过滤日志 - 只按关键词过�
const filteredLogs = useMemo(() => {
if (!debouncedSearchKeyword) {
if (displayLimit === -1 || logs.length <= displayLimit) return logs;
return logs.slice(-displayLimit);
}

const keyword = debouncedSearchKeyword.toLowerCase();
let result = logs.filter(log =>
const result = logs.filter(log =>
log.message.toLowerCase().includes(keyword)
);

Expand All @@ -129,17 +129,17 @@ const Console: React.FC<ConsoleProps> = ({ onToggle, projectId }) => {
useEffect(() => {
if (import.meta.env.DEV) {
console.log('Console组件 - 当前日志数量:', logs.length);
console.log('Console组件 - 连接状态:', isConnected);
console.log('Console组件 - 过滤后日志数量:', filteredLogs.length);
console.log('Console组件 - 连接状�', isConnected);
console.log('Console组件 - 过滤后日志数�', filteredLogs.length);

// 性能警告
if (logs.length > 400) {
console.warn(`Console日志数量较多 (${logs.length}条),可能影响性能`);
console.warn(`Console日志数量较多 (${logs.length}�,可能影响性能`);
}
}
}, [logs, isConnected, filteredLogs]);

// 当过滤条件或显示限制变化时,滚动到底部
// 当过滤条件或显示限制变化时,滚动到底�
useEffect(() => {
if (filteredLogs.length > 0 && logsEndRef.current) {
// 使用setTimeout确保DOM更新后再滚动
Expand All @@ -151,7 +151,7 @@ const Console: React.FC<ConsoleProps> = ({ onToggle, projectId }) => {
}
}, [filteredLogs.length, displayLimit, debouncedSearchKeyword, logsEndRef]);

// 打开时滚动到底部(交由父组件控制可见性,这里仅在有日志时尝试ï¼
// 打开时滚动到底部(交由父组件控制可见性,这里仅在有日志时尝试�
useEffect(() => {
if (filteredLogs.length > 0) {
setTimeout(() => {
Expand All @@ -169,7 +169,7 @@ const Console: React.FC<ConsoleProps> = ({ onToggle, projectId }) => {
borderTop: `1px solid ${token.colorBorder}`,
background: token.colorBgContainer
}}>
{/* 控制栏 */}
{/* 控制�*/}
<div style={{
padding: '8px 16px',
borderBottom: `1px solid ${token.colorBorder}`,
Expand Down
11 changes: 10 additions & 1 deletion src/components/layouts/PlatformSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useLocation, useNavigate } from "react-router-dom";
import { useTranslation } from "react-i18next";
import { platformRoutes } from "@/routes";
import { useSidebar } from "@/store/appStore";
import {useConfig} from "@/store/appStore";
import { MenuFoldOutlined, MenuUnfoldOutlined } from "@ant-design/icons";

const PlatformSidebar: React.FC = () => {
Expand All @@ -12,6 +13,8 @@ const PlatformSidebar: React.FC = () => {
const navigate = useNavigate();
const location = useLocation();
const { isSidebarCollapsed, toggleSidebar } = useSidebar();
const {config} = useConfig();
const version = config.version || '';
const [openKeys, setOpenKeys] = useState<string[]>(() => {
const pathname = location.pathname.replace(/\/$/, '');
const initialOpenKeys: string[] = [];
Expand Down Expand Up @@ -128,9 +131,15 @@ const PlatformSidebar: React.FC = () => {
<div style={{
padding: token.padding,
display: "flex",
justifyContent: isSidebarCollapsed ? "center" : "right",
alignItems: "center",
justifyContent: isSidebarCollapsed ? "center" : "space-between",
backgroundColor: token.colorBgContainer
}}>
{!isSidebarCollapsed && (
<span style={{fontSize: token.fontSizeSM, color: token.colorTextTertiary}}>
{version}
</span>
)}
<Space>
<Button
type="text"
Expand Down
11 changes: 10 additions & 1 deletion src/components/layouts/ProjectSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useLocation, useNavigate, useParams } from "react-router-dom";
import { useTranslation } from "react-i18next";
import { routes } from "@/routes";
import { useSidebar } from "@/store/appStore";
import {useConfig} from "@/store/appStore";
import { MenuFoldOutlined, MenuUnfoldOutlined } from "@ant-design/icons";

const ProjectSidebar: React.FC = () => {
Expand All @@ -12,6 +13,8 @@ const ProjectSidebar: React.FC = () => {
const navigate = useNavigate();
const location = useLocation();
const { isSidebarCollapsed, toggleSidebar } = useSidebar();
const {config} = useConfig();
const version = config.version || '';
const { projectId } = useParams<{ projectId: string }>();
const [openKeys, setOpenKeys] = useState<string[]>(() => {
const pathname = location.pathname.replace(/\/$/, '');
Expand Down Expand Up @@ -139,9 +142,15 @@ const ProjectSidebar: React.FC = () => {
<div style={{
padding: token.padding,
display: "flex",
justifyContent: isSidebarCollapsed ? "center" : "right",
alignItems: "center",
justifyContent: isSidebarCollapsed ? "center" : "space-between",
backgroundColor: token.colorBgContainer
}}>
{!isSidebarCollapsed && (
<span style={{fontSize: token.fontSizeSM, color: token.colorTextTertiary}}>
{version}
</span>
)}
<Space>
<Button
type="text"
Expand Down
2 changes: 1 addition & 1 deletion src/pages/Authentication/components/ProvidersTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const ProvidersTab: React.FC = () => {
setLoadingModels(true);
const list = await getModelList(projectId);
const names = list
.filter((m) => (m as EntitySchema).type === "entity" || (m as NativeQuerySchema).type === "native_query")
.filter((m) => (m as EntitySchema).type === "Entity" || (m as NativeQuerySchema).type === "NativeQuery")
.map((m) => m.name)
.filter((n): n is string => !!n);
setModelNames(names);
Expand Down
Loading
Loading