flex 无法将“<mx:>”解析为组件执行.解决办法:

本文解决Flex项目导入早期版本时无法解析组件执行的问题。通过导入命名空间及改头部导入,确保正确调用F3组件。关键步骤包括使用mx1前缀替换原始前缀,并调整默认导入的命名空间。

flex项目导入早期版本 无法将“<mx:******>”解析为组件执行.解决办法:

1.导入命名空间:注意:【xmlns:mx1="library://ns.adobe.com/flex/mx"】这个是f3组件的命名控件

<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/halo" xmlns:mx1="library://ns.adobe.com/flex/mx">

 

 

 

 使用时由于命名前缀换成 mx1 用所以后面的f3组件的前缀应变成

<mx1:Button />

2.改头部导入命名空间

<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx"">

  

 

默认导入的是 xmlns:mx="library://ns.adobe.com/flex/halo" schema 命名空间。。

 

<template> <view :style="colorStyle"> <view class="h-120 flex-between-center px-20"> <view class="h-72 bg--w111-f5f5f5 rd-36rpx px-32 flex-1 flex-y-center relative"> <text class="iconfont icon-ic_search text--w111-999"></text> <input type="text" confirm-type="search" placeholder="请输入要查询的内容" @confirm="inputConfirm" @input="setValue" class="fs-28 w-438 text--w111-333 pl-18 line1" v-model="searchValue" /> <text class="iconfont icon-ic_close1 fs-28 text--w111-999 z-10" v-show="searchValue" @tap="clearSearchVal"></text> </view> <view class="fs-28 text--w111-333 ml-32" @tap='searchBut'>搜索</view> </view> <view class="px-20" v-if="history.length"> <view class="flex-between-center mt-16 mb-24"> <view class="fs-28 lh-40rpx fw-500 text--w111-333">历史搜索</view> <text class="iconfont icon-ic_delete fs-28 text--w111-999" @tap="clear"></text> </view> <view class="flex flex-wrap"> <text class="flex-center h-56 line1 rd-28rpx bg--w111-f5f5f5 px-24 fs-24 text--w111-666 mr-24 mb-16" v-for="(item,index) in isShowMore ? history : history.slice(0,7)" :key="index" @tap='setHotSearchValue(item.keyword)' v-if="item.keyword">{{item.keyword}}</text> <view class="w-56 h-56 rd-28rpx bg--w111-f5f5f5 flex-center text--w111-666" v-if="history.length > 7" @tap="isShowMore = !isShowMore"> <text class="iconfont fs-24" :class="isShowMore ? 'icon-ic_uparrow' : 'icon-ic_downarrow'"></text> </view> </view> </view> <view class="px-20"> <view class="flex-between-center mt-40 mb-24"> <view class="fs-28 lh-40rpx fw-500 text--w111-333">热门搜索</view> <text class="iconfont icon-ic_Refresh fs-28 text--w111-999" :class="isSpin ? 'spin' : ''" @tap="refresh"></text> </view> <view class="flex flex-wrap"> <view class="flex-center search_item h-56 rd-28rpx px-24 fs-24 mr-24 mb-16" v-for="(item,index) in hotSearchList" :key="index" :style="{'color':item.color,'background-color':item.bg_color,border: item.border_color ? '1px solid ' + item.border_color : 'none'}" @tap='setHotSearchValue(item.name)'> <view class="flex-y-center"> <image v-if="item.icon" :src="item.icon" class="w-24 h-24 rd-4rpx mr-8"></image> <text>{{item.name}}</text> </view> </view> </view> </view> <view> <scroll-view scroll-x="true" class="white-nowrap vertical-middle w-730 mt-32 pl-20" show-scrollbar="false"> <view class="inline-block mr-20" v-if="salesRecommendList.length"> <view class="w-454 rd-24rpx gradient_bg pt-22 pb-8"> <view class="fs-28 font-red lh-40rpx fw-600 pl-24 mb-18 flex-y-center" @tap="goRank(1)"> <image src="@/static/img/sales_icon.png" class="w-40 h-40"></image> <text class="pl-8">销量</text> </view> <view class="mx-8 rd-24rpx bg--w111-fff h-748 p-20"> <view class="flex-y-center" v-if="salesRecommendList[0]" @tap="goDetail(salesRecommendList[0])"> <view class="relative w-108 h-108"> <image :src="salesRecommendList[0].image" class="w-108 h-108 rd-16rpx"></image> <view class="rank-count rank-count1 fs-24 text--w111-fff flex-center">1</view> </view> <view class="flex-1 h-108 flex-col flex-x-center ml-16"> <view class="w-260 fs-26 text--w111-333 lh-40rpx line1">{{salesRecommendList[0].store_name}}</view> <view class="w-260 fs-22 text--w111-999 lh-30rpx mt-8 line1">{{salesRecommendList[0].store_info}}</view> </view> </view> <view class="flex-y-center mt-16" v-if="salesRecommendList[1]" @tap="goDetail(salesRecommendList[1])"> <view class="relative w-108 h-108"> <image :src="salesRecommendList[1].image" class="w-108 h-108 rd-16rpx"></image> <view class="rank-count rank-count2 fs-24 text--w111-fff flex-center">2</view> </view> <view class="flex-1 h-108 flex-col flex-x-center ml-16"> <view class="flex-between-center"> <view class="w-260 fs-26 text--w111-333 lh-40rpx line1">{{salesRecommendList[1].store_name}}</view> </view> <view class="w-260 fs-22 text--w111-999 lh-30rpx mt-8 line1">{{salesRecommendList[1].store_info}}</view> </view> </view> <view class="flex-y-center mt-16 mb-34" v-if="salesRecommendList[2]" @tap="goDetail(salesRecommendList[2])"> <view class="relative w-108 h-108"> <image :src="salesRecommendList[2].image" class="w-108 h-108 rd-16rpx"></image> <view class="rank-count rank-count3 fs-24 text--w111-fff flex-center">3</view> </view> <view class="flex-1 h-108 flex-col flex-x-center ml-16"> <view class="flex-between-center"> <view class="w-260 fs-26 text--w111-333 lh-40rpx line1">{{salesRecommendList[2].store_name}}</view> </view> <view class="w-260 fs-22 text--w111-999 lh-30rpx mt-8 line1">{{salesRecommendList[2].store_info}}</view> </view> </view> <view class="flex-between-center scroll_cell" v-for="(item,index) in salesRecommendList.slice(3)" :key="index" @tap="goDetail(item)"> <view class="flex-y-center"> <text class="inline-block w-30 h-32 lh-32rpx text--w111-fff text-center fs-24 rank_4">{{index + 4}}</text> <text class="fs-26 text--w111-333 ml-20 w-360 line1"> <image v-if="item.delivery_type.indexOf('2') > -1" style="height:32.6rpx;width:116.08rpx;margin-right:10rpx;vertical-align:middle;" src="https://www.ytzhan.com/uploads/uniapp/2-glodzt.png"></image> <image v-if="item.delivery_type.indexOf('1') > -1" style="height:32.6rpx;width:116.08rpx;margin-right: 10rpx;vertical-align:middle;" src="https://www.ytzhan.com/uploads/uniapp/2-glodkd.png"></image><text style="vertical-align:middle;">{{item.store_name}}</text></text> </view> </view> </view> </view> </view> <view class="inline-block mr-20" v-if="scoreRecommendList.length"> <view class="w-454 rd-24rpx gradient_bg pt-22 pb-8"> <view class="fs-28 font-red lh-40rpx fw-600 pl-24 mb-18 flex-y-center" @tap="goRank(2)"> <image src="@/static/img/sales_icon.png" class="w-40 h-40"></image> <text class="pl-8">评分</text> </view> <view class="mx-8 rd-24rpx bg--w111-fff h-748 p-20"> <view class="flex-y-center" v-if="scoreRecommendList[0]" @tap="goDetail(scoreRecommendList[0])"> <view class="relative w-108 h-108"> <image :src="scoreRecommendList[0].image" class="w-108 h-108 rd-16rpx"></image> <view class="rank-count rank-count1 fs-24 text--w111-fff flex-center">1</view> </view> <view class="flex-1 h-108 flex-col flex-x-center ml-16"> <view class="flex-between-center"> <view class="w-260 fs-26 text--w111-333 lh-40rpx line1">{{scoreRecommendList[0].store_name}}</view> </view> <view class="w-260 fs-22 text--w111-999 lh-30rpx mt-8 line1">{{scoreRecommendList[0].store_info}}</view> </view> </view> <view class="flex-y-center mt-16" v-if="scoreRecommendList[1]" @tap="goDetail(scoreRecommendList[1])"> <view class="relative w-108 h-108"> <image :src="scoreRecommendList[1].image" class="w-108 h-108 rd-16rpx"></image> <view class="rank-count rank-count2 fs-24 text--w111-fff flex-center">2</view> </view> <view class="flex-1 h-108 flex-col flex-x-center ml-16"> <view class="flex-between-center"> <view class="w-260 fs-26 text--w111-333 lh-40rpx line1">{{scoreRecommendList[1].store_name}}</view> </view> <view class="w-260 fs-22 text--w111-999 lh-30rpx mt-8 line1">{{scoreRecommendList[1].store_info}}</view> </view> </view> <view class="flex-y-center mt-16 mb-34" v-if="scoreRecommendList[2]" @tap="goDetail(scoreRecommendList[2])"> <view class="relative w-108 h-108"> <image :src="scoreRecommendList[2].image" class="w-108 h-108 rd-16rpx"></image> <view class="rank-count rank-count3 fs-24 text--w111-fff flex-center">3</view> </view> <view class="flex-1 h-108 flex-col flex-x-center ml-16"> <view class="flex-between-center"> <view class="w-260 fs-26 text--w111-333 lh-40rpx line1">{{scoreRecommendList[2].store_name}}</view> </view> <view class="w-260 fs-22 text--w111-999 lh-30rpx mt-8 line1">{{scoreRecommendList[2].store_info}}</view> </view> </view> <view class="flex-between-center scroll_cell" v-for="(item,index) in scoreRecommendList.slice(3)" :key="index" @tap="goDetail(item)"> <view class="flex-y-center"> <text class="inline-block w-30 h-32 lh-32rpx text--w111-fff text-center fs-24 rank_4">{{index + 4}}</text> <text class="fs-26 text--w111-333 ml-20 w-360 line1"> <image v-if="item.delivery_type.indexOf('2') > -1" style="height:32.6rpx;width:116.08rpx;margin-right:10rpx;vertical-align:middle;" src="https://www.ytzhan.com/uploads/uniapp/2-glodzt.pngwww.ytzhan.com/uploads/uniapp/2-glodzt.png"></image> <image v-if="item.delivery_type.indexOf('1') > -1" style="height:32.6rpx;width:116.08rpx;margin-right: 10rpx;vertical-align:middle;" src="https://www.ytzhan.com/uploads/uniapp/2-glodzt.pngwww.ytzhan.com/uploads/uniapp/2-glodkd.png"></image>{{item.store_name}}</text> </view> </view> </view> </view> </view> <view class="inline-block mr-20" v-if="collectRecommendList.length"> <view class="w-454 rd-24rpx gradient_bg pt-22 pb-8"> <view class="fs-28 font-red lh-40rpx fw-600 pl-24 mb-18 flex-y-center" @tap="goRank(3)"> <image src="@/static/img/sales_icon.png" class="w-40 h-40"></image> <text class="pl-8">收藏</text> </view> <view class="mx-8 rd-24rpx bg--w111-fff h-748 p-20"> <view class="flex-y-center" v-if="collectRecommendList[0]" @tap="goDetail(collectRecommendList[0])"> <view class="relative w-108 h-108"> <image :src="collectRecommendList[0].image" class="w-108 h-108 rd-16rpx"></image> <view class="rank-count rank-count1 fs-24 text--w111-fff flex-center">1</view> </view> <view class="flex-1 h-108 flex-col flex-x-center ml-16"> <view class="flex-between-center"> <view class="w-260 fs-26 text--w111-333 lh-40rpx line1">{{collectRecommendList[0].store_name}}</view> </view> <view class="w-260 fs-22 text--w111-999 lh-30rpx mt-8 line1">{{collectRecommendList[0].store_info}}</view> </view> </view> <view class="flex-y-center mt-16" v-if="collectRecommendList[1]" @tap="goDetail(collectRecommendList[1])"> <view class="relative w-108 h-108"> <image :src="collectRecommendList[1].image" class="w-108 h-108 rd-16rpx"></image> <view class="rank-count rank-count2 fs-24 text--w111-fff flex-center">2</view> </view> <view class="flex-1 h-108 flex-col flex-x-center ml-16"> <view class="flex-between-center"> <view class="w-260 fs-26 text--w111-333 lh-40rpx line1">{{collectRecommendList[1].store_name}}</view> </view> <view class="w-260 fs-22 text--w111-999 lh-30rpx mt-8 line1">{{collectRecommendList[1].store_info}}</view> </view> </view> <view class="flex-y-center mt-16 mb-34" v-if="collectRecommendList[2]" @tap="goDetail(collectRecommendList[2])"> <view class="relative w-108 h-108"> <image :src="collectRecommendList[2].image" class="w-108 h-108 rd-16rpx"></image> <view class="rank-count rank-count3 fs-24 text--w111-fff flex-center">3</view> </view> <view class="flex-1 h-108 flex-col flex-x-center ml-16"> <view class="flex-between-center"> <view class="w-260 fs-26 text--w111-333 lh-40rpx line1">{{collectRecommendList[2].store_name}}</view> </view> <view class="w-260 fs-22 text--w111-999 lh-30rpx mt-8 line1">{{collectRecommendList[2].store_info}}</view> </view> </view> <view class="flex-between-center scroll_cell" v-for="(item,index) in collectRecommendList.slice(3)" :key="index" @tap="goDetail(item)"> <view class="flex-y-center"> <text class="inline-block w-30 h-32 lh-32rpx text--w111-fff text-center fs-24 rank_4">{{index + 4}}</text> <text class="fs-26 text--w111-333 ml-20 w-360 line1"> <image v-if="item.delivery_type.indexOf('2') > -1" style="height:32.6rpx;width:116.08rpx;margin-right:10rpx;vertical-align:middle;" src="https://www.ytzhan.com/uploads/uniapp/2-glodzt.pngwww.ytzhan.com/uploads/uniapp/2-glodzt.png"></image> <image v-if="item.delivery_type.indexOf('1') > -1" style="height:32.6rpx;width:116.08rpx;margin-right: 10rpx;vertical-align:middle;" src="https://www.ytzhan.com/uploads/uniapp/2-glodzt.pngwww.ytzhan.com/uploads/uniapp/2-glodkd.png"></image><text style="vertical-align:middle;">{{item.store_name}}</text></text> </view> </view> </view> </view> </view> </scroll-view> </view> <scroll-view scroll-y="true" class="fuzzy_modal" :style="{height:fuzzyHeight + 'px',top: 60 + 'px'}" v-if="newPartyList.length" @touchmove.stop.prevent="moveStop"> <view class="cell" v-for="(item,index) in newPartyList" :key="index" @tap="setCommentSearch(item.id)"> <view class="keyword line1" v-html="item.keyword"></view> </view> </scroll-view> </view> </template> <script> let sysHeight = uni.getSystemInfoSync().statusBarHeight; import home from '@/components/home/index.vue' import { getSearchKeyword, getSearchRecommendApi, getHotWordApi } from '@/api/store.js'; import { searchList, clearSearch } from '@/api/api.js'; import recommend from '@/components/recommend'; import colors from "@/mixins/color"; import { goShopDetail } from '@/libs/order.js'; import { HTTP_REQUEST_URL } from '@/config/app.js'; import { Debounce } from '@/utils/validate.js' export default { components: { recommend, home }, mixins: [colors], data() { return { hostProduct: [], searchValue: '', focus: true, bastList: [], hotSearchList: [], isSpin:false, first: 0, limit: 8, page: 1, loading: false, loadend: false, loadTitle: '加载更多', hotPage: 1, isScroll: true, history: [], imgHost: HTTP_REQUEST_URL, salesRecommendList:[], scoreRecommendList:[], collectRecommendList:[], sysHeight:sysHeight, newPartyList: [], //模糊搜索关键词列表 isShowMore: false }; }, computed: { fuzzyHeight() { let screenHeight = uni.getSystemInfoSync().screenHeight; return screenHeight - this.sysHeight - 56; } }, onLoad(e) { this.searchValue = e.searchVal || ''; this.getSearchHotKeywords(); this.getSearchRecommend(); setTimeout(()=>{ this.newPartySearch(); },1000) }, onShow: function(e) { uni.removeStorageSync('form_type_cart'); this.searchList(); }, methods: { refresh(){ this.isSpin = true; this.getSearchHotKeywords(); }, // 获取搜索热词 getSearchHotKeywords(){ getHotWordApi().then(res=>{ this.hotSearchList = res.data; setTimeout(_=>{ this.isSpin = false; },1000) }) }, searchList() { searchList({ page: 1, limit: 10 }).then(res => { this.history = res.data; }); }, clear() { let that = this; clearSearch().then(res => { uni.showToast({ title: res.msg, success() { that.history = []; } }); }); }, inputConfirm: function(event) { if (event.detail.value) { uni.hideKeyboard(); this.setHotSearchValue(event.detail.value); } }, setValue: Debounce(function(e){ this.newPartySearch(); }), newPartySearch: function() { getSearchKeyword({keyword:this.searchValue}).then(res => { this.newPartyList = res.data.list; this.newPartyList.map((item) => { this.$set(item,'keyword',this.brightKeyword(item.store_name,res.data.keyword)); }); }); }, brightKeyword(val,keyword) { if (val.indexOf(keyword) > -1) { val = `<p class="line1">${val}</p>` return val.replace(keyword, `<span style="color: #C9771E;">${keyword}</span>`); } else { return val; } }, setCommentSearch(id) { uni.navigateTo({ url:'/pages/goods/goods_list/index?productId=' + id }) }, setHotSearchValue: function(event) { this.$set(this, 'searchValue', event); this.focus = false; this.searchBut(); }, searchBut: function() { let that = this; that.focus = false; if (that.searchValue.length > 0) { this.newPartyList = []; uni.navigateTo({ url:'/pages/goods/goods_list/index?searchValue=' + that.searchValue }) } else { return this.$util.Tips({ title: '请输入要搜索的商品', icon: 'none', duration: 1000, mask: true, }); } }, getSearchRecommend(){ getSearchRecommendApi(1).then(res=>{ this.salesRecommendList = res.data; }) getSearchRecommendApi(2).then(res=>{ this.scoreRecommendList= res.data; }) getSearchRecommendApi(3).then(res=>{ this.collectRecommendList= res.data; }) }, // 去详情页 goDetail(item) { goShopDetail(item, this.uid).catch(res => { uni.navigateTo({ url: `/pages/goods_details/index?id=${item.id}` }); }); }, goRank(type){ uni.navigateTo({ url:`/pages/columnGoods/rank/index?type=${type}` }) }, clearSearchVal(){ this.searchValue = ''; this.newPartyList = []; } } } </script>排行榜有数据但是salesRecommendList、scoreRecommendList、collectRecommendList.length都为0且 即便res.data里有数据 打印出的length仍旧为0
06-24
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
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值