Unknown tag (c:forEach) 未知的标签

解决JSP中c:forEach标签问题
本文介绍了解决JSP页面中使用c:forEach标签出现未知标签错误的方法,通过引入正确的标签库来消除编译警告。

在用到<c:forEach>的时候发现有黄线感叹号, Unknown tag (c:forEach) 未知的标签

需要在

<%@ page language="Java" import="java.util.*" pageEncoding="utf-8"%>

下面加一句

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>


import React, { useEffect, useState, useRef } from "react"; import { Button, Popconfirm, message, Tag, Radio, Typography, Spin, Card } from "antd"; import { DeleteOutlined, WarningOutlined, CheckCircleOutlined, CloseCircleOutlined, QuestionCircleOutlined } from "@ant-design/icons"; import InferenceConfigSelector from "../../../components/InferenceConfigSelector"; import { PdpPathInfo, EvaluationCase, InferenceConfig } from "../../../types"; import { useParams } from "react-router-dom"; import { useAuth } from "../../../contexts/AuthContext"; import { useRequest } from "ahooks"; import axios from "axios"; const { Text } = Typography; interface TagListContainerProps { tagListData: any } export const TagListContainer: React.FC<TagListContainerProps> = ({ tagListData }) => { const { id } = useParams<{ id: string }>(); const { user, isAuthenticated } = useAuth(); // 获取当前用户信息 useEffect(() => { if (!isAuthenticated) { console.log(user); message.error("请先登录后再进行标注操作"); return; } }, [isAuthenticated]); const [isDirtyData, setIsDirtyData] = useState(false); const [pdpPaths, setPdpPaths] = useState<Record<number, PdpPathInfo>>({}); const [pathInPickle, setHasPdpPaths] = useState(true); // 是否有PDP路径数据 const [pklList, setPklList] = useState<EvaluationCase[]>([]); const [selectedConfig, setSelectedConfig] = useState<number | null>(null); const selectedPklRef = useRef<EvaluationCase | null>(null); const [annotationStats, setAnnotationStats] = useState({ total: 0, annotated: 0, good: 0, bad: 0, unknown: 0, }); const [highlightPathIndex, setHighlightPathIndex] = useState<number | null>( null, ); // 推理配置相关状态 const [configs, setConfigs] = useState<InferenceConfig[]>([]); const [configPagination, setConfigPagination] = useState({ current: 1, pageSize: 10, total: 0, }); const [loading, setLoading] = useState({ pklList: false, // paths: false, visualization: false, annotation: false, markDirty: false, configs: false, checkPkl: false, }); // 获取PDP路径 const getPdpPaths = (pklId: number) => { return axios.get(`/api/annotation/pdp-paths/${pklId}`, { params: { evaluation_set_id: pklId, }, }); }; // 带缓存获取PDP路径 const { run, loading: loadingPaths } = useRequest(getPdpPaths, { manual: true, debounceWait: 500, onSuccess: (response) => { if (!response.data.success) { // 没有PDP路径数据,需要通过配置获取 setHasPdpPaths(false); setPdpPaths({}); setAnnotationStats({ total: 0, annotated: 0, good: 0, bad: 0, unknown: 0, }); return; } processPdpPaths(response); }, onError: (error) => { console.error("Error fetching trajectories:", error); }, }); // 加载推理配置列表 const fetchConfigs = async ( page = configPagination.current, pageSize = configPagination.pageSize, ) => { setLoading((prev) => ({ ...prev, configs: true })); try { const response = await axios.get("/api/inference_config", { params: { page, per_page: pageSize }, }); const parsedConfigs = parseInferenceConfig( response.data.configs || response.data, ); setConfigs(parsedConfigs || []); // 如果API返回了总数,更新分页信息 if (response.data.total !== undefined) { setConfigPagination((prev) => ({ ...prev, total: response.data.total, })); } } catch (error) { console.error("Failed to fetch inference configs:", error); message.error("获取推理配置失败,请稍后再试"); } finally { setLoading((prev) => ({ ...prev, configs: false })); } }; // 初始加载 useEffect(() => { if (id) { fetchConfigs(); } }, [id]); // 处理配置分页 const handleConfigPageChange = async (page: number, pageSize: number) => { setConfigPagination((prev) => ({ ...prev, current: page, pageSize })); await fetchConfigs(page, pageSize); }; useEffect(() => { setHighlightPathIndex(null); // 清除之前的高亮 selectedPklRef.current = tagListData; run(tagListData.id); }, [tagListData]) // 高亮某条路径 const handleHighlightPath = (pathIndex: number) => { setHighlightPathIndex(pathIndex === highlightPathIndex ? null : pathIndex); }; // 处理配置选择 const handleConfigSelect = (configId: number) => { setSelectedConfig(configId); }; // 处理数据 const processPdpPaths = (response: any) => { if (response.data.paths && response.data.paths.length > 0) { const pathsDict: Record<number, PdpPathInfo> = {}; response.data.paths.forEach((path: PdpPathInfo) => { pathsDict[path.index] = path; }); setPdpPaths(pathsDict); setIsDirtyData(response.data.is_dirty || false); setHasPdpPaths(true); } else { setPdpPaths({}); setHasPdpPaths(false); setAnnotationStats({ total: 0, annotated: 0, good: 0, bad: 0, unknown: 0, }); message.error("加载PDP路径失败"); } // 计算标注统计信息 const paths = response.data.paths; const total = paths.length; let annotated = 0; let good = 0; let bad = 0; let unknown = 0; paths.forEach((path: PdpPathInfo) => { if (path.annotation) { annotated++; if (path.annotation.annotation === "good") { good++; } else if (path.annotation.annotation === "bad") { bad++; } else if (path.annotation.annotation === "unknown") { unknown++; } } }); setAnnotationStats({ total, annotated, good, bad, unknown, }); }; /** * 解析接口配置 */ const parseInferenceConfig = (data: any[][]): InferenceConfig[] => { return data.map((item) => ({ id: item[0], json_name: item[1], pth_name: item[2], pth_upload_time: item[3], })); } // 提交标注 const handleAnnotate = async (pathIndex: number, annotation: string) => { console.log("提交"); if (!tagListData) { message.error("请先选择PKL文件并输入标注者姓名"); return; } // 检查是否需要删除标注(点击了当前已选择的按钮) const currentAnnotation = pdpPaths[pathIndex]?.annotation?.annotation; const isDeleteAction = currentAnnotation === annotation; setLoading((prev) => ({ ...prev, annotation: true })); try { const response = await axios.post("/api/annotation/pdp-paths", { pkl_id: tagListData.id, path_index: pathIndex, annotation, delete_annotation: isDeleteAction, // 新增参数,标识是删除操作 inference_config_id: !pathInPickle ? selectedConfig : undefined, // 如果是从配置获取的路径,传递配置ID evaluation_set_id: id, employee_id: user?.employee_id || null, // 添加工号信息 }); if (response.data.success) { // 更新路径列表中的标注状态 const updatedPaths = { ...pdpPaths }; const oldAnnotation = updatedPaths[pathIndex].annotation?.annotation; // 如果是删除操作,设置annotation为null,否则更新为新的标注 if (isDeleteAction) { updatedPaths[pathIndex] = { ...updatedPaths[pathIndex], annotation: undefined, }; message.success("标注已删除"); } else { updatedPaths[pathIndex] = { ...updatedPaths[pathIndex], annotation: { annotation, employee_id: user?.employee_id || undefined, created_at: new Date().toISOString(), updated_at: new Date().toISOString(), }, }; tagListData.has_annotation = true; message.success("标注保存成功"); } setPdpPaths(updatedPaths); // 更新统计信息 setAnnotationStats((prev) => { const newStats = { ...prev }; // 如果是删除标注 if (isDeleteAction && oldAnnotation) { newStats.annotated--; if (oldAnnotation === "good") newStats.good--; else if (oldAnnotation === "bad") newStats.bad--; else if (oldAnnotation === "unknown") newStats.unknown--; } // 如果是新增标注 else if (!oldAnnotation && !isDeleteAction) { newStats.annotated++; if (annotation === "good") newStats.good++; else if (annotation === "bad") newStats.bad++; else if (annotation === "unknown") newStats.unknown++; } // 如果是修改标注 else if (oldAnnotation !== annotation && !isDeleteAction) { if (oldAnnotation === "good") newStats.good--; else if (oldAnnotation === "bad") newStats.bad--; else if (oldAnnotation === "unknown") newStats.unknown--; if (annotation === "good") newStats.good++; else if (annotation === "bad") newStats.bad++; else if (annotation === "unknown") newStats.unknown++; } if (newStats.annotated === 0) { tagListData.has_annotation = false; } return newStats; }); } else { message.error("操作失败"); } } catch (error) { console.error("Failed to save/delete annotation:", error); message.error("操作出错"); } finally { setLoading((prev) => ({ ...prev, annotation: false })); } }; // 处理标记为脏数据 const handleMarkAsDirty = async () => { console.log("标记了一处地点"); if (!tagListData) { message.error("请先选择PKL文件"); return; } setLoading((prev) => ({ ...prev, markDirty: true })); try { const response = await axios.post("/api/annotation/mark-dirty", { pkl_id: tagListData.id, is_dirty: !isDirtyData, // 反转当前状态 }); if (response.data.success) { setIsDirtyData(response.data.is_dirty); message.success( response.data.is_dirty ? "已标记为脏数据" : "已取消脏数据标记", ); // 更新列表中当前项的脏数据状态 const updatedPklList = pklList.map((pkl) => { if (pkl.id === tagListData.id) { return { ...pkl, dirty_data: response.data.is_dirty }; } return pkl; }); setPklList(updatedPklList); } else { message.error("操作失败"); } } catch (error) { console.error("Failed to mark as dirty data:", error); message.error("操作出错"); } finally { setLoading((prev) => ({ ...prev, markDirty: false })); } }; // 删除所有标注 const handleDeleteAnnotations = async () => { const currentPkl = selectedPklRef.current; if (!currentPkl) return; try { // 删除所有路径的标注 const pathIndices = Object.keys(pdpPaths).map(Number); const deletePromises = pathIndices.map((index) => axios.post("/api/annotation/pdp-paths", { pkl_id: currentPkl.id, path_index: index, annotation: "unknown", // 或者使用空字符串 delete_annotation: true, inference_config_id: !pathInPickle ? selectedConfig : undefined, // 如果是从配置获取的路径,传递配置ID evaluation_set_id: id, employee_id: user?.employee_id || null, }), ); await Promise.all(deletePromises); // 更新本地状态 const updatedPaths = { ...pdpPaths }; Object.keys(updatedPaths).forEach((key) => { const index = Number(key); updatedPaths[index] = { ...updatedPaths[index], annotation: undefined, }; }); currentPkl.has_annotation = false; setPdpPaths(updatedPaths); setAnnotationStats({ total: annotationStats.total, annotated: 0, good: 0, bad: 0, unknown: 0, }); message.success("所有标注已删除"); } catch (error) { console.error("删除标注失败:", error); message.error("删除标注失败"); } }; return ( <div className="h-full my-[16px] mx-[12px]"> <div className="flex flex-wrap justify-center gap-2 mb-[16px]"> <Button danger={!isDirtyData} type={isDirtyData ? "default" : "primary"} icon={<WarningOutlined />} onClick={handleMarkAsDirty} //loading={loading.markDirty} > {isDirtyData ? "取消脏数据标记" : "标记为脏数据"} </Button> <Popconfirm title="确定要删除所有标注结果吗?" onConfirm={handleDeleteAnnotations} > <Button danger type="default" icon={<DeleteOutlined />}> 删除所有标注 </Button> </Popconfirm> </div> {Object.keys(pdpPaths).length > 0 && ( <div className="mt-[8px] py-[8px] border-t border-b border-opacity-0.1"> <div className="flex gap-2 text-[14px]"> <Tag color="success" icon={<CheckCircleOutlined />}> 好: {annotationStats.good} </Tag> <Tag color="error" icon={<CloseCircleOutlined />}> 差: {annotationStats.bad} </Tag> <Tag color="processing" icon={<QuestionCircleOutlined />}> 未知: {annotationStats.unknown} </Tag> </div> </div> )} {loadingPaths ? ( <div className="loading-paths"> <Spin tip="加载路径数据..." /> </div> ) : !pathInPickle && Object.keys(pdpPaths).length === 0 ? ( <div className="config-selector-container"> <Text> 该PKL文件中没有PDP路径数据,请选择一个推理配置查看路径 </Text> <div className="config-selector"> <InferenceConfigSelector configs={configs} selectedConfig={selectedConfig} onConfigSelect={handleConfigSelect} loading={loading.configs} mode="single" pagination={{ current: configPagination.current, pageSize: configPagination.pageSize, total: configPagination.total, onChange: handleConfigPageChange, }} evaluationSetId={id} /> </div> </div> ) : Object.keys(pdpPaths).length === 0 ? ( <div className="no-paths"> <Text>未找到PDP路径数据</Text> </div> ) : ( <div className="flex-1 overflow-auto mt-[16px]"> {Object.values(pdpPaths) .sort((a, b) => a.index - b.index) .map((path) => ( <div className="path-card-container" key={path.index}> <Card className={ highlightPathIndex === path.index ? "path-card highlighted" : "path-card" } size="small" onClick={() => handleHighlightPath(path.index)} hoverable > <div className="path-info"> <div className="path-title"> {highlightPathIndex === path.index ? ( <Tag color="blue">路径 {path.index + 1}</Tag> ) : ( `路径${path.index + 1}` )} </div> <div className="path-actions" onClick={(e) => e.stopPropagation()} > <Radio.Group value={path.annotation?.annotation} onChange={(e) => { e.stopPropagation(); handleAnnotate(path.index, e.target.value); }} buttonStyle="solid" size="small" > <Radio.Button value="good" className={ path.annotation?.annotation === "good" ? "good-selected" : "" } onClick={(e) => { e.stopPropagation(); if (path.annotation?.annotation === "good") { handleAnnotate(path.index, "good"); } }} > <CheckCircleOutlined /> 好 </Radio.Button> <Radio.Button value="bad" className={ path.annotation?.annotation === "bad" ? "bad-selected" : "" } onClick={(e) => { e.stopPropagation(); if (path.annotation?.annotation === "bad") { handleAnnotate(path.index, "bad"); } }} > <CloseCircleOutlined /> 差 </Radio.Button> <Radio.Button value="unknown" className={ path.annotation?.annotation === "unknown" ? "unknown-selected" : "" } onClick={(e) => { e.stopPropagation(); if (path.annotation?.annotation === "unknown") { handleAnnotate(path.index, "unknown"); } }} > <QuestionCircleOutlined /> 未知 </Radio.Button> </Radio.Group> </div> </div> </Card> </div> ))} </div> )} </div> ) } 将组件进行拆分
最新发布
10-18
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值