diff --git a/src/views/Favorites/dialogs/AvatarImportDialog.vue b/src/views/Favorites/dialogs/AvatarImportDialog.vue index 7770f00c..8bb1b4b5 100644 --- a/src/views/Favorites/dialogs/AvatarImportDialog.vue +++ b/src/views/Favorites/dialogs/AvatarImportDialog.vue @@ -103,57 +103,13 @@

         
-        
-            
-                
-            
-            
-                
-            
-            
-                
-            
-            
-                
-            
-            
-                
-            
-        
+        
     
 
 
@@ -161,17 +117,19 @@
     import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
     import { computed, ref, watch } from 'vue';
     import { Button } from '@/components/ui/button';
+    import { DataTableLayout } from '@/components/ui/data-table';
     import { InputGroupTextareaField } from '@/components/ui/input-group';
     import { Loading } from '@element-plus/icons-vue';
-    import { Trash2 } from 'lucide-vue-next';
     import { storeToRefs } from 'pinia';
     import { toast } from 'vue-sonner';
     import { useI18n } from 'vue-i18n';
 
     import { useAvatarStore, useFavoriteStore, useGalleryStore, useUserStore } from '../../../stores';
     import { avatarRequest, favoriteRequest } from '../../../api';
+    import { createColumns } from './avatarImportColumns.jsx';
     import { getNextDialogIndex } from '../../../shared/utils/base/ui';
     import { removeFromArray } from '../../../shared/utils';
+    import { useVrcxVueTable } from '../../../lib/table/useVrcxVueTable';
 
     const emit = defineEmits(['update:avatarImportDialogInput']);
     const { t } = useI18n();
@@ -207,6 +165,30 @@
         layout: 'table'
     });
 
+    const tableStyle = { maxHeight: '400px' };
+
+    const rows = computed(() =>
+        Array.isArray(avatarImportTable.value?.data) ? avatarImportTable.value.data.slice() : []
+    );
+
+    const columns = computed(() =>
+        createColumns({
+            onShowAvatar: showAvatarDialog,
+            onShowUser: showUserDialog,
+            onShowFullscreenImage: showFullscreenImageDialog,
+            onDelete: deleteItemAvatarImport
+        })
+    );
+
+    const { table } = useVrcxVueTable({
+        persistKey: 'avatarImportDialog',
+        data: rows,
+        columns: columns.value,
+        getRowId: (row) => String(row?.id ?? ''),
+        enablePagination: false,
+        enableSorting: false
+    });
+
     const avatarImportDialogIndex = ref(2000);
 
     const isVisible = computed({
diff --git a/src/views/Favorites/dialogs/FriendImportDialog.vue b/src/views/Favorites/dialogs/FriendImportDialog.vue
index d0f04629..4202494f 100644
--- a/src/views/Favorites/dialogs/FriendImportDialog.vue
+++ b/src/views/Favorites/dialogs/FriendImportDialog.vue
@@ -84,34 +84,13 @@
             

{{ t('dialog.friend_import.errors') }}


         
-        
-            
-                
-            
-            
-                
-            
-            
-                
-            
-        
+        
     
 
 
@@ -119,9 +98,9 @@
     import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
     import { computed, ref, watch } from 'vue';
     import { Button } from '@/components/ui/button';
+    import { DataTableLayout } from '@/components/ui/data-table';
     import { InputGroupTextareaField } from '@/components/ui/input-group';
     import { Loading } from '@element-plus/icons-vue';
-    import { Trash2 } from 'lucide-vue-next';
     import { storeToRefs } from 'pinia';
     import { toast } from 'vue-sonner';
     import { useI18n } from 'vue-i18n';
@@ -129,7 +108,9 @@
     import { removeFromArray, userImage, userImageFull } from '../../../shared/utils';
     import { useFavoriteStore, useGalleryStore, useUserStore } from '../../../stores';
     import { favoriteRequest, userRequest } from '../../../api';
+    import { createColumns } from './friendImportColumns.jsx';
     import { getNextDialogIndex } from '../../../shared/utils/base/ui';
+    import { useVrcxVueTable } from '../../../lib/table/useVrcxVueTable';
 
     const { t } = useI18n();
 
@@ -163,6 +144,31 @@
         layout: 'table'
     });
 
+    const tableStyle = { maxHeight: '400px' };
+
+    const rows = computed(() =>
+        Array.isArray(friendImportTable.value?.data) ? friendImportTable.value.data.slice() : []
+    );
+
+    const columns = computed(() =>
+        createColumns({
+            userImage,
+            userImageFull,
+            onShowFullscreenImage: showFullscreenImageDialog,
+            onShowUser: showUserDialog,
+            onDelete: deleteItemFriendImport
+        })
+    );
+
+    const { table } = useVrcxVueTable({
+        persistKey: 'friendImportDialog',
+        data: rows,
+        columns: columns.value,
+        getRowId: (row) => String(row?.id ?? ''),
+        enablePagination: false,
+        enableSorting: false
+    });
+
     const friendImportDialogIndex = ref(2000);
 
     const isVisible = computed({
diff --git a/src/views/Favorites/dialogs/WorldImportDialog.vue b/src/views/Favorites/dialogs/WorldImportDialog.vue
index 60b104c4..b18f0805 100644
--- a/src/views/Favorites/dialogs/WorldImportDialog.vue
+++ b/src/views/Favorites/dialogs/WorldImportDialog.vue
@@ -108,54 +108,13 @@
             
             

         
-        
-            
-                
-            
-            
-                
-            
-            
-                
-            
-            
-                
-            
-            
-                
-            
-        
+        
     
 
 
@@ -163,17 +122,19 @@
     import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
     import { computed, ref, watch } from 'vue';
     import { Button } from '@/components/ui/button';
+    import { DataTableLayout } from '@/components/ui/data-table';
     import { InputGroupTextareaField } from '@/components/ui/input-group';
     import { Loading } from '@element-plus/icons-vue';
-    import { Trash2 } from 'lucide-vue-next';
     import { storeToRefs } from 'pinia';
     import { toast } from 'vue-sonner';
     import { useI18n } from 'vue-i18n';
 
     import { useFavoriteStore, useGalleryStore, useUserStore, useWorldStore } from '../../../stores';
     import { favoriteRequest, worldRequest } from '../../../api';
+    import { createColumns } from './worldImportColumns.jsx';
     import { getNextDialogIndex } from '../../../shared/utils/base/ui';
     import { removeFromArray } from '../../../shared/utils';
+    import { useVrcxVueTable } from '../../../lib/table/useVrcxVueTable';
 
     const { showUserDialog } = useUserStore();
     const { favoriteWorldGroups, worldImportDialogInput, worldImportDialogVisible, localWorldFavoriteGroups } =
@@ -213,6 +174,30 @@
         layout: 'table'
     });
 
+    const tableStyle = { maxHeight: '400px' };
+
+    const rows = computed(() =>
+        Array.isArray(worldImportTable.value?.data) ? worldImportTable.value.data.slice() : []
+    );
+
+    const columns = computed(() =>
+        createColumns({
+            onShowWorld: showWorldDialog,
+            onShowUser: showUserDialog,
+            onShowFullscreenImage: showFullscreenImageDialog,
+            onDelete: deleteItemWorldImport
+        })
+    );
+
+    const { table } = useVrcxVueTable({
+        persistKey: 'worldImportDialog',
+        data: rows,
+        columns: columns.value,
+        getRowId: (row) => String(row?.id ?? ''),
+        enablePagination: false,
+        enableSorting: false
+    });
+
     const isVisible = computed({
         get() {
             return worldImportDialogVisible.value;
diff --git a/src/views/Favorites/dialogs/avatarImportColumns.jsx b/src/views/Favorites/dialogs/avatarImportColumns.jsx
new file mode 100644
index 00000000..d4ee12fc
--- /dev/null
+++ b/src/views/Favorites/dialogs/avatarImportColumns.jsx
@@ -0,0 +1,137 @@
+import { Trash2 } from 'lucide-vue-next';
+
+import { Button } from '../../../components/ui/button';
+import { i18n } from '../../../plugin';
+
+const { t } = i18n.global;
+
+export const createColumns = ({ onShowAvatar, onShowUser, onDelete, onShowFullscreenImage }) => [
+    {
+        id: 'image',
+        header: () => t('table.import.image'),
+        enableSorting: false,
+        size: 70,
+        cell: ({ row }) => {
+            const original = row.original;
+            const thumb = original?.thumbnailImageUrl;
+            const full = original?.imageUrl;
+
+            return (
+                 (
+                            
+                        )
+                    }}
+                >
+                     {
+                            e.stopPropagation();
+                            if (full) {
+                                onShowFullscreenImage?.(full);
+                            }
+                        }}
+                    />
+                
+            );
+        }
+    },
+    {
+        id: 'name',
+        accessorKey: 'name',
+        header: () => t('table.import.name'),
+        meta: {
+            stretch: true
+        },
+        cell: ({ row }) => {
+            const original = row.original;
+            return (
+                 {
+                        e.stopPropagation();
+                        onShowAvatar?.(original?.id);
+                    }}
+                >
+                    {original?.name}
+                
+            );
+        }
+    },
+    {
+        id: 'author',
+        header: () => t('table.import.author'),
+        size: 120,
+        enableSorting: false,
+        cell: ({ row }) => {
+            const original = row.original;
+            return (
+                 {
+                        e.stopPropagation();
+                        onShowUser?.(original?.authorId);
+                    }}
+                >
+                    {original?.authorName}
+                
+            );
+        }
+    },
+    {
+        id: 'status',
+        header: () => t('table.import.status'),
+        size: 70,
+        enableSorting: false,
+        cell: ({ row }) => {
+            const status = String(row.original?.releaseStatus ?? '');
+            const label = status
+                ? status.charAt(0).toUpperCase() + status.slice(1)
+                : '';
+            const color =
+                status === 'public'
+                    ? '#67c23a'
+                    : status === 'private'
+                      ? '#f56c6c'
+                      : undefined;
+            return {label};
+        }
+    },
+    {
+        id: 'action',
+        header: () => t('table.import.action'),
+        size: 90,
+        enableSorting: false,
+        meta: {
+            tdClass: 'text-right'
+        },
+        cell: ({ row }) => {
+            const original = row.original;
+            return (
+                
+            );
+        }
+    }
+];
diff --git a/src/views/Favorites/dialogs/friendImportColumns.jsx b/src/views/Favorites/dialogs/friendImportColumns.jsx
new file mode 100644
index 00000000..b10036f7
--- /dev/null
+++ b/src/views/Favorites/dialogs/friendImportColumns.jsx
@@ -0,0 +1,96 @@
+import { Trash2 } from 'lucide-vue-next';
+
+import { Button } from '../../../components/ui/button';
+import { i18n } from '../../../plugin';
+
+const { t } = i18n.global;
+
+export const createColumns = ({ userImage, userImageFull, onShowFullscreenImage, onShowUser, onDelete }) => [
+    {
+        id: 'image',
+        header: () => t('table.import.image'),
+        enableSorting: false,
+        size: 70,
+        cell: ({ row }) => {
+            const original = row.original;
+            const thumb = userImage?.(original);
+            const full = userImageFull?.(original);
+
+            return (
+                 (
+                            
+                        )
+                    }}
+                >
+                     {
+                            e.stopPropagation();
+                            if (full) {
+                                onShowFullscreenImage?.(full);
+                            }
+                        }}
+                    />
+                
+            );
+        }
+    },
+    {
+        id: 'name',
+        accessorKey: 'displayName',
+        header: () => t('table.import.name'),
+        meta: {
+            stretch: true
+        },
+        cell: ({ row }) => {
+            const original = row.original;
+            return (
+                 {
+                        e.stopPropagation();
+                        onShowUser?.(original?.id);
+                    }}
+                >
+                    {original?.displayName}
+                
+            );
+        }
+    },
+    {
+        id: 'action',
+        header: () => t('table.import.action'),
+        size: 90,
+        enableSorting: false,
+        meta: {
+            tdClass: 'text-right'
+        },
+        cell: ({ row }) => {
+            const original = row.original;
+            return (
+                
+            );
+        }
+    }
+];
diff --git a/src/views/Favorites/dialogs/worldImportColumns.jsx b/src/views/Favorites/dialogs/worldImportColumns.jsx
new file mode 100644
index 00000000..1e6a39ce
--- /dev/null
+++ b/src/views/Favorites/dialogs/worldImportColumns.jsx
@@ -0,0 +1,138 @@
+import { Trash2 } from 'lucide-vue-next';
+
+import { Button } from '../../../components/ui/button';
+import { i18n } from '../../../plugin';
+
+const { t } = i18n.global;
+
+export const createColumns = ({ onShowWorld, onShowUser, onDelete, onShowFullscreenImage }) => [
+    {
+        id: 'image',
+        header: () => t('table.import.image'),
+        enableSorting: false,
+        size: 70,
+        cell: ({ row }) => {
+            const original = row.original;
+            const thumb = original?.thumbnailImageUrl;
+            const full = original?.imageUrl;
+
+            return (
+                 (
+                            
+                        )
+                    }}
+                >
+                     {
+                            e.stopPropagation();
+                            if (full) {
+                                onShowFullscreenImage?.(full);
+                            }
+                        }}
+                    />
+                
+            );
+        }
+    },
+    {
+        id: 'name',
+        accessorKey: 'name',
+        header: () => t('table.import.name'),
+        meta: {
+            stretch: true
+        },
+        cell: ({ row }) => {
+            const original = row.original;
+            return (
+                 {
+                        e.stopPropagation();
+                        onShowWorld?.(original?.id);
+                    }}
+                >
+                    {original?.name}
+                
+            );
+        }
+    },
+    {
+        id: 'author',
+        header: () => t('table.import.author'),
+        size: 120,
+        enableSorting: false,
+        cell: ({ row }) => {
+            const original = row.original;
+            return (
+                 {
+                        e.stopPropagation();
+                        onShowUser?.(original?.authorId);
+                    }}
+                >
+                    {original?.authorName}
+                
+            );
+        }
+    },
+    {
+        id: 'status',
+        header: () => t('table.import.status'),
+        size: 70,
+        enableSorting: false,
+        cell: ({ row }) => {
+            const status = String(row.original?.releaseStatus ?? '');
+            const label = status
+                ? status.charAt(0).toUpperCase() + status.slice(1)
+                : '';
+            const color =
+                status === 'public'
+                    ? '#67c23a'
+                    : status === 'private'
+                      ? '#f56c6c'
+                      : undefined;
+            return {label};
+        }
+    },
+    {
+        id: 'action',
+        header: () => t('table.import.action'),
+        size: 90,
+        enableSorting: false,
+        meta: {
+            tdClass: 'text-right'
+        },
+        cell: ({ row }) => {
+            const original = row.original;
+            return (
+                
+            );
+        }
+    }
+];