Explorar o código

fix data rendering issue (#689)

Signed-off-by: ryjiang <jiangruiyi@gmail.com>
ryjiang hai 5 meses
pai
achega
921f7cf085

+ 1 - 27
.github/workflows/dev.yml

@@ -13,7 +13,7 @@ jobs:
     runs-on: ubuntu-latest
     runs-on: ubuntu-latest
     strategy:
     strategy:
       matrix:
       matrix:
-        platform: 
+        platform:
           - linux/amd64
           - linux/amd64
           - linux/arm64
           - linux/arm64
           - linux/arm/v7
           - linux/arm/v7
@@ -49,29 +49,3 @@ jobs:
           build-args: |
           build-args: |
             VERSION=dev
             VERSION=dev
           push: true
           push: true
-
-  manifest:
-    runs-on: ubuntu-latest
-    needs: build
-    steps:
-      - name: Checkout Code
-        uses: actions/checkout@v4
-
-      - name: Set up Docker Buildx
-        uses: docker/setup-buildx-action@v3
-        with:
-          install: true
-
-      - name: Login to DockerHub
-        uses: docker/login-action@v1
-        with:
-          username: ${{ secrets.DOCKER_USERNAME }}
-          password: ${{ secrets.DOCKER_PWD }}
-
-      - name: Create and Push Docker Manifest
-        run: |
-          docker manifest create zilliz/attu:dev \
-            zilliz/attu:dev-linux/amd64 \
-            zilliz/attu:dev-linux/arm64 \
-            zilliz/attu:dev-linux/arm/v7
-          docker manifest push zilliz/attu:dev

+ 32 - 0
client/src/components/DataView/DataView.tsx

@@ -0,0 +1,32 @@
+import { Typography } from '@mui/material';
+import MediaPreview from '../MediaPreview/MediaPreview';
+
+const DataView = (props: { type: string; value: any }) => {
+  const { type, value } = props;
+
+  switch (type) {
+    case 'VarChar':
+      return <MediaPreview value={value} />;
+    case 'JSON':
+    case 'Array':
+    case 'SparseFloatVector':
+    case 'BFloat16Vector':
+    case 'FloatVector':
+    case 'Float16Vector':
+      const stringValue = JSON.stringify(value, null, 2);
+      // remove escape characters
+      const formattedValue = stringValue
+        .replace(/\\n/g, '\n')
+        .replace(/\\t/g, '\t')
+        .replace(/\\"/g, '"');
+
+      // remove first and last double quotes if present
+      const trimmedValue = formattedValue.replace(/^"|"$/g, '');
+      return <Typography title={trimmedValue}>{trimmedValue}</Typography>;
+
+    default:
+      return <Typography title={value}>{value}</Typography>;
+  }
+};
+
+export default DataView;

+ 8 - 5
client/src/components/MediaPreview/MediaPreview.tsx

@@ -1,4 +1,5 @@
 import React, { useState, useEffect } from 'react';
 import React, { useState, useEffect } from 'react';
+import { Typography } from '@mui/material';
 import icons from '../icons/Icons';
 import icons from '../icons/Icons';
 
 
 const MediaPreview = (props: { value: string }) => {
 const MediaPreview = (props: { value: string }) => {
@@ -100,13 +101,15 @@ const MediaPreview = (props: { value: string }) => {
       >
       >
         {isImg ? (
         {isImg ? (
           <>
           <>
-            <icons.img />{' '}
-            <a href={value} target="_blank">
-              {value}
-            </a>
+            <icons.img />
+            <Typography title={value}>
+              <a href={value} target="_blank">
+                {value}
+              </a>
+            </Typography>
           </>
           </>
         ) : (
         ) : (
-          value
+          <Typography title={value}>{value}</Typography>
         )}
         )}
       </div>
       </div>
       {showImage && (
       {showImage && (

+ 2 - 13
client/src/pages/databases/collections/data/CollectionData.tsx

@@ -1,5 +1,4 @@
 import { useState, useEffect, useRef, useContext } from 'react';
 import { useState, useEffect, useRef, useContext } from 'react';
-import { Typography } from '@mui/material';
 import { useTranslation } from 'react-i18next';
 import { useTranslation } from 'react-i18next';
 import { rootContext, dataContext } from '@/context';
 import { rootContext, dataContext } from '@/context';
 import { DataService } from '@/http';
 import { DataService } from '@/http';
@@ -30,7 +29,7 @@ import StatusIcon, { LoadingType } from '@/components/status/StatusIcon';
 import CustomInput from '@/components/customInput/CustomInput';
 import CustomInput from '@/components/customInput/CustomInput';
 import CustomMultiSelector from '@/components/customSelector/CustomMultiSelector';
 import CustomMultiSelector from '@/components/customSelector/CustomMultiSelector';
 import CollectionColHeader from '../CollectionColHeader';
 import CollectionColHeader from '../CollectionColHeader';
-import MediaPreview from '@/components/MediaPreview/MediaPreview';
+import DataView from '@/components/DataView/DataView';
 
 
 export interface CollectionDataProps {
 export interface CollectionDataProps {
   collectionName: string;
   collectionName: string;
@@ -510,17 +509,7 @@ const CollectionData = (props: CollectionDataProps) => {
 
 
                   const fieldType = field?.data_type || 'JSON'; // dynamic
                   const fieldType = field?.data_type || 'JSON'; // dynamic
 
 
-                  switch (fieldType) {
-                    case 'VarChar':
-                      return <MediaPreview value={cellData} />;
-                    case 'JSON':
-                      const value = JSON.stringify(cellData);
-                      return <Typography title={value}>{value}</Typography>;
-                    default:
-                      return (
-                        <Typography title={cellData}>{cellData}</Typography>
-                      );
-                  }
+                  return <DataView type={fieldType} value={cellData} />;
                 },
                 },
                 headerFormatter: v => {
                 headerFormatter: v => {
                   return (
                   return (

+ 7 - 8
client/src/pages/databases/collections/search/Search.tsx

@@ -31,7 +31,6 @@ import {
   buildSearchParams,
   buildSearchParams,
   buildSearchCode,
   buildSearchCode,
   getColumnWidth,
   getColumnWidth,
-  detectItemType,
 } from '@/utils';
 } from '@/utils';
 import SearchParams from '../../../search/SearchParams';
 import SearchParams from '../../../search/SearchParams';
 import DataExplorer, { formatMilvusData } from './DataExplorer';
 import DataExplorer, { formatMilvusData } from './DataExplorer';
@@ -46,7 +45,7 @@ import { ColDefinitionsType } from '@/components/grid/Types';
 import { CollectionObject, CollectionFullObject } from '@server/types';
 import { CollectionObject, CollectionFullObject } from '@server/types';
 import CodeDialog from '@/pages/dialogs/CodeDialog';
 import CodeDialog from '@/pages/dialogs/CodeDialog';
 import CollectionColHeader from '../CollectionColHeader';
 import CollectionColHeader from '../CollectionColHeader';
-import MediaPreview from '@/components/MediaPreview/MediaPreview';
+import DataView from '@/components/DataView/DataView';
 
 
 export interface CollectionDataProps {
 export interface CollectionDataProps {
   collectionName: string;
   collectionName: string;
@@ -333,12 +332,12 @@ const Search = (props: CollectionDataProps) => {
                   f => f.name === key
                   f => f.name === key
                 );
                 );
 
 
-                switch (field?.data_type) {
-                  case 'VarChar':
-                    return <MediaPreview value={cellData} />;
-                  default:
-                    return <Typography title={cellData}>{cellData}</Typography>;
-                }
+                return (
+                  <DataView
+                    type={field?.data_type || 'JSON'}
+                    value={cellData}
+                  />
+                );
               },
               },
               getStyle: d => {
               getStyle: d => {
                 const field = collection.schema.fields.find(
                 const field = collection.schema.fields.find(