From 195fe0ce2ef62facaa5bf836ebdf8866984e84a1 Mon Sep 17 00:00:00 2001
From: yyl <yyl>
Date: 星期一, 11 五月 2026 16:21:23 +0800
Subject: [PATCH] Merge branch 'master' into h5version

---
 Assets/Editor/UIComponent/ImageLanguageAdapterHelper.cs        |   67 ++
 Assets/Editor/UIComponent/ImageLanguageAdapterEditor.cs.meta   |   11 
 Assets/Editor/UIComponent/ImageLanguageAdapterScanTool.cs.meta |   11 
 Assets/Editor/UIComponent/ImageLanguageAdapterHelper.cs.meta   |   11 
 Assets/Editor/Logo/ryzj/SplashImage.png                        |    0 
 Assets/Editor/Logo/xssg/SplashImage.png                        |    0 
 Assets/Editor/UIComponent/TextLanguageAdapterScanTool.cs       |  150 ++++-
 Assets/Editor/AssetBundleBrowser/AssetBundleBuildTab.cs        |   74 ++
 Assets/Editor/UIComponent/ImageLanguageAdapterEditor.cs        |  217 ++++++++
 Assets/Editor/UIComponent/ImageLanguageAdapterScanTool.cs      |  594 +++++++++++++++++++++++
 Assets/Editor/UIComponent/TextLanguageAdapterEditor.cs         |   26 
 Assets/Editor/Tool/ClientPackage.cs                            |  282 +++++++++++
 Assets/Editor/Logo/wgyx/SplashImage.png                        |    0 
 Assets/Editor/Logo/test/SplashImage.png                        |    0 
 14 files changed, 1,408 insertions(+), 35 deletions(-)

diff --git a/Assets/Editor/AssetBundleBrowser/AssetBundleBuildTab.cs b/Assets/Editor/AssetBundleBrowser/AssetBundleBuildTab.cs
index 665f6cf..257eac6 100644
--- a/Assets/Editor/AssetBundleBrowser/AssetBundleBuildTab.cs
+++ b/Assets/Editor/AssetBundleBrowser/AssetBundleBuildTab.cs
@@ -90,6 +90,12 @@
             set { LocalSave.SetString("APKOutPutPath", value); }
         }
 
+        public string AndroidExportPath
+        {
+            get { return LocalSave.GetString("AndroidExportPath"); }
+            set { LocalSave.SetString("AndroidExportPath", value); }
+        }
+
         string m_Publishers;
         public string publishers
         {
@@ -230,6 +236,15 @@
             var fromVersionConfig = ClientPackage.GetVersionConfig(_publisher, out versionName);
 
             var versionConfig = await VersionConfig.GetAsync();
+
+            if (fromVersionConfig == null || versionConfig == null)
+            {
+                Debug.LogWarningFormat("SetVersionConfig failed: fromVersionConfig={0}, versionConfig={1}, publisher={2}",
+                    fromVersionConfig != null ? "ok" : "null",
+                    versionConfig != null ? "ok" : "null",
+                    _publisher);
+                return;
+            }
 
             VersionConfig.Copy(fromVersionConfig, versionConfig);
 
@@ -516,6 +531,32 @@
 #endif
 
             GUILayout.EndHorizontal();
+
+#if UNITY_ANDROID
+            EditorGUILayout.Space();
+            GUILayout.Label("Export Android Project", EditorStyles.boldLabel);
+            GUILayout.BeginHorizontal();
+            var newExportPath = EditorGUILayout.TextField("Export Path", AndroidExportPath);
+            if (!string.IsNullOrEmpty(newExportPath) && newExportPath != AndroidExportPath)
+            {
+                AndroidExportPath = newExportPath;
+            }
+            if (GUILayout.Button("Browse", GUILayout.MaxWidth(75f)))
+            {
+                BrowseForAndroidExport();
+            }
+            GUILayout.EndHorizontal();
+            GUILayout.BeginHorizontal();
+            if (GUILayout.Button("Export Android Project"))
+            {
+                EditorApplication.delayCall += ExecuteExportAndroidProject;
+            }
+            if (GUILayout.Button("Export Android Dev"))
+            {
+                EditorApplication.delayCall += ExecuteExportAndroidProjectDev;
+            }
+            GUILayout.EndHorizontal();
+#endif
 
             // GUILayout.Space(20);
             // if (GUILayout.Button("SwitchVersionConfig"))
@@ -966,6 +1007,30 @@
             ClientPackage.BuildPublishers(ClientPackage.SDK_PLUGIN_PROJECT, outputPath, ApkOutputPath, publishers, packageIndex, true, false);
         }
 
+        private void ExecuteExportAndroidProject()
+        {
+            if (string.IsNullOrEmpty(AndroidExportPath))
+            {
+                EditorUtility.DisplayDialog("鎻愮ず", "璇峰厛璁剧疆 Android Export Path", "纭畾");
+                return;
+            }
+            packageIndex++;
+            var assetBundlePath = Application.dataPath.Replace("Assets", m_UserData.m_OutputPath);
+            ClientPackage.ExportAndroidProject(publishers, packageIndex, false, AndroidExportPath, assetBundlePath);
+        }
+
+        private void ExecuteExportAndroidProjectDev()
+        {
+            if (string.IsNullOrEmpty(AndroidExportPath))
+            {
+                EditorUtility.DisplayDialog("鎻愮ず", "璇峰厛璁剧疆 Android Export Path", "纭畾");
+                return;
+            }
+            packageIndex++;
+            var assetBundlePath = Application.dataPath.Replace("Assets", m_UserData.m_OutputPath);
+            ClientPackage.ExportAndroidProject(publishers, packageIndex, true, AndroidExportPath, assetBundlePath);
+        }
+
         private void ExecuteBuildClientPackageStandalone()
         {
             packageIndex++;
@@ -1084,6 +1149,15 @@
             }
         }
 
+        private void BrowseForAndroidExport()
+        {
+            var newPath = EditorUtility.OpenFolderPanel("Android Export Folder", AndroidExportPath, string.Empty);
+            if (!string.IsNullOrEmpty(newPath))
+            {
+                AndroidExportPath = newPath;
+            }
+        }
+
         private void ResetPathToDefault()
         {
             m_UserData.m_UseDefaultPath = true;
diff --git a/Assets/Editor/Logo/ryzj/SplashImage.png b/Assets/Editor/Logo/ryzj/SplashImage.png
index 2d6a329..0d45e0a 100644
--- a/Assets/Editor/Logo/ryzj/SplashImage.png
+++ b/Assets/Editor/Logo/ryzj/SplashImage.png
Binary files differ
diff --git a/Assets/Editor/Logo/test/SplashImage.png b/Assets/Editor/Logo/test/SplashImage.png
index 2d6a329..0d45e0a 100644
--- a/Assets/Editor/Logo/test/SplashImage.png
+++ b/Assets/Editor/Logo/test/SplashImage.png
Binary files differ
diff --git a/Assets/Editor/Logo/wgyx/SplashImage.png b/Assets/Editor/Logo/wgyx/SplashImage.png
index 2d6a329..0d45e0a 100644
--- a/Assets/Editor/Logo/wgyx/SplashImage.png
+++ b/Assets/Editor/Logo/wgyx/SplashImage.png
Binary files differ
diff --git a/Assets/Editor/Logo/xssg/SplashImage.png b/Assets/Editor/Logo/xssg/SplashImage.png
index 2d6a329..0d45e0a 100644
--- a/Assets/Editor/Logo/xssg/SplashImage.png
+++ b/Assets/Editor/Logo/xssg/SplashImage.png
Binary files differ
diff --git a/Assets/Editor/Tool/ClientPackage.cs b/Assets/Editor/Tool/ClientPackage.cs
index 53b3c99..b7741a9 100644
--- a/Assets/Editor/Tool/ClientPackage.cs
+++ b/Assets/Editor/Tool/ClientPackage.cs
@@ -1,4 +1,4 @@
-锘縰sing System.Collections;
+using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 using System;
@@ -386,6 +386,286 @@
     }
 
     public static VersionConfig versionConfig1 = null;
+
+    /// <summary>
+    /// 瀵煎嚭Android宸ョ▼缁橝S鎵撳寘
+    /// Unity鍙礋璐e鍑簎nityLibrary锛孲DK/Gradle/绛惧悕绛夊潎鐢盇S宸ョ▼澶勭悊
+    /// </summary>
+    public static void ExportAndroidProject(string _publisher, int _buildIndex, bool _development, string _outputPath, string _assetBundlePath)
+    {
+        if (string.IsNullOrEmpty(_outputPath) || !Directory.Exists(_outputPath))
+        {
+            Debug.LogError("瀵煎嚭璺緞鏃犳晥锛岃璁剧疆鏈夋晥鐨� Android Export Path: " + _outputPath);
+            return;
+        }
+
+        PreBuild(_publisher, _buildIndex);
+
+        var versionName = string.Empty;
+        var versionConfig = GetVersionConfig(_publisher, out versionName);
+
+        PlayerSettings.Android.bundleVersionCode = VersionConfig.GetVersionNumber(versionConfig.version);
+        PlayerSettings.enableInternalProfiler = _development;
+
+        // 鍏堟竻鐞哠treamingAssets锛屽啀鏍规嵁assetAccess閰嶇疆鎷疯礉璧勬簮鍒癝treamingAssets
+        var streamingPath = ResourcesPath.Instance.StreamingAssetPath;
+        if (Directory.Exists(streamingPath))
+            Directory.Delete(streamingPath, true);
+
+        if (!string.IsNullOrEmpty(_assetBundlePath) && Directory.Exists(_assetBundlePath))
+        {
+            switch (versionConfig.assetAccess)
+            {
+                case InstalledAsset.NullAsset:
+                    CopyNullAssetResources(_assetBundlePath, streamingPath);
+                    break;
+                case InstalledAsset.HalfAsset:
+                    CopyHalfAssetResources(_assetBundlePath, streamingPath);
+                    break;
+                case InstalledAsset.FullAsset:
+                case InstalledAsset.IngoreDownLoad:
+                    CopyFullAssetResources(_assetBundlePath, streamingPath);
+                    break;
+            }
+            Debug.LogFormat("璧勬簮鎷疯礉瀹屾垚({0})锛屼粠 {1} 鍒� {2}", versionConfig.assetAccess, _assetBundlePath, streamingPath);
+        }
+        else
+        {
+            Debug.LogWarningFormat("AssetBundle璺緞涓虹┖鎴栦笉瀛樺湪: {0}锛屽鍑哄伐绋嬪皢涓嶅寘鍚父鎴忚祫婧�", _assetBundlePath);
+        }
+
+        // 鍏抽敭璁剧疆锛氬鍑轰负Gradle宸ョ▼鑰岄潪鐩存帴鎵揂PK
+        EditorUserBuildSettings.exportAsGoogleAndroidProject = true;
+        EditorUserBuildSettings.androidBuildSystem = AndroidBuildSystem.Gradle;
+        EditorUserBuildSettings.development = _development;
+
+        // 瀵煎嚭璺緞
+        _outputPath = Path.GetFullPath(_outputPath);
+        string outputDir = Path.Combine(_outputPath, versionName + "_" + versionConfig.clientPackageFlag);
+
+        if (Directory.Exists(outputDir))
+        {
+            Directory.Delete(outputDir, true);
+        }
+
+        Debug.LogFormat("寮�濮嬪鍑篈ndroid宸ョ▼锛岃緭鍑鸿矾寰�: {0}", outputDir);
+
+        BuildPipeline.BuildPlayer(baseLevels, outputDir, BuildTarget.Android,
+            _development ? BuildOptions.AcceptExternalModificationsToPlayer | BuildOptions.Development | BuildOptions.AllowDebugging
+                         : BuildOptions.AcceptExternalModificationsToPlayer);
+
+        Debug.LogFormat("瀵煎嚭Android宸ョ▼瀹屾垚锛佽緭鍑鸿矾寰�: {0}", outputDir);
+
+        // 瀵煎嚭瀹屾垚鍚庢仮澶嶈缃�
+        EditorUserBuildSettings.exportAsGoogleAndroidProject = false;
+        EditorUserBuildSettings.development = false;
+    }
+
+    /// <summary>
+    /// 灏忓寘鎷疯礉锛氫粎鎷疯礉builtin璧勬簮
+    /// </summary>
+    static void CopyNullAssetResources(string _assetBundlePath, string _streamingPath)
+    {
+        var files = new List<FileInfo>();
+        var builtInFiles = new List<FileInfo>();
+
+        FileExtersion.GetAllDirectoryFileInfos(_assetBundlePath, files);
+        foreach (var file in files)
+        {
+            if (file.FullName.Contains("builtin"))
+            {
+                builtInFiles.Add(file);
+            }
+        }
+
+        foreach (var item in builtInFiles)
+        {
+            var extension = Path.GetExtension(item.FullName);
+            if (extension == ".meta")
+                continue;
+
+            var relativePath = FileExtersion.GetFileRelativePath(_assetBundlePath, item.FullName);
+            var to = StringUtility.Concat(_streamingPath, relativePath);
+            var directory = Path.GetDirectoryName(to);
+            if (!Directory.Exists(directory))
+                Directory.CreateDirectory(directory);
+
+            File.Copy(item.FullName, to, true);
+        }
+    }
+
+    /// <summary>
+    /// 鍗婂寘鎷疯礉锛氭牴鎹甈riorBundleConfig鍓旈櫎浼樺厛绾т綆鐨刪ero/maps/audio/uieffect/video璧勬簮锛�
+    /// 浠ュ強config鍜寀i璧勬簮锛堝彇鍐充簬includeConfig/includeUI寮�鍏筹級
+    /// </summary>
+    static void CopyHalfAssetResources(string _assetBundlePath, string _streamingPath)
+    {
+        PriorBundleConfig.LazyInit();
+
+        var fromFiles = new List<FileInfo>();
+        FileExtersion.GetAllDirectoryFileInfos(_assetBundlePath, fromFiles);
+
+        var excludeFileFullNames = new List<string>();
+
+        // 鍓旈櫎浼樺厛绾т綆鐨刪ero璧勬簮
+        var tempFiles = new List<FileInfo>();
+        FileExtersion.GetAllDirectoryFileInfos(StringUtility.Concat(_assetBundlePath, "/hero"), tempFiles);
+        foreach (var file in tempFiles)
+        {
+            var fileName = Path.GetFileNameWithoutExtension(file.FullName);
+            var prior = PriorBundleConfig.GetAssetPrior(AssetVersion.AssetCategory.Mob, AssetVersionUtility.DecodeFileName(fileName));
+            if (prior > AssetPrior)
+            {
+                excludeFileFullNames.Add(file.FullName);
+            }
+        }
+
+        // 鍓旈櫎浼樺厛绾т綆鐨刴aps璧勬簮
+        tempFiles.Clear();
+        FileExtersion.GetAllDirectoryFileInfos(StringUtility.Concat(_assetBundlePath, "/maps"), tempFiles);
+        foreach (var file in tempFiles)
+        {
+            var fileName = Path.GetFileNameWithoutExtension(file.FullName);
+            var prior = PriorBundleConfig.GetAssetPrior(AssetVersion.AssetCategory.Scene, AssetVersionUtility.DecodeFileName(fileName));
+            if (prior > AssetPrior)
+            {
+                excludeFileFullNames.Add(file.FullName);
+            }
+        }
+
+        // 鍓旈櫎浼樺厛绾т綆鐨刟udio璧勬簮
+        tempFiles.Clear();
+        FileExtersion.GetAllDirectoryFileInfos(StringUtility.Concat(_assetBundlePath, "/audio"), tempFiles);
+        foreach (var file in tempFiles)
+        {
+            var fileName = Path.GetFileNameWithoutExtension(file.FullName);
+            var prior = PriorBundleConfig.GetAssetPrior(AssetVersion.AssetCategory.Audio, AssetVersionUtility.DecodeFileName(fileName));
+            if (prior > AssetPrior)
+            {
+                excludeFileFullNames.Add(file.FullName);
+            }
+        }
+
+        // 鍓旈櫎浼樺厛绾т綆鐨剈ieffect璧勬簮
+        tempFiles.Clear();
+        FileExtersion.GetAllDirectoryFileInfos(StringUtility.Concat(_assetBundlePath, "/uieffect"), tempFiles);
+        foreach (var file in tempFiles)
+        {
+            var fileName = Path.GetFileNameWithoutExtension(file.FullName);
+            var prior = PriorBundleConfig.GetAssetPrior(AssetVersion.AssetCategory.Effect, AssetVersionUtility.DecodeFileName(fileName));
+            if (prior > AssetPrior)
+            {
+                excludeFileFullNames.Add(file.FullName);
+            }
+        }
+
+        // 鍓旈櫎瑙嗛璧勬簮
+        tempFiles.Clear();
+        var videoPath = StringUtility.Concat(_assetBundlePath, "/video");
+        if (Directory.Exists(videoPath))
+        {
+            FileExtersion.GetAllDirectoryFileInfos(videoPath, tempFiles);
+            foreach (var file in tempFiles)
+            {
+                var fileName = Path.GetFileNameWithoutExtension(file.FullName);
+                var prior = PriorBundleConfig.GetAssetPrior(AssetVersion.AssetCategory.Video, AssetVersionUtility.DecodeFileName(fileName));
+                if (prior > AssetPrior)
+                {
+                    excludeFileFullNames.Add(file.FullName);
+                }
+            }
+        }
+
+        // 鍓旈櫎琛ㄨ祫婧�
+        if (!includeConfig)
+        {
+            tempFiles.Clear();
+            FileExtersion.GetAllDirectoryFileInfos(StringUtility.Concat(_assetBundlePath, "/config"), tempFiles);
+            foreach (var file in tempFiles)
+            {
+                excludeFileFullNames.Add(file.FullName);
+            }
+        }
+
+        // 鍓旈櫎UI璧勬簮
+        if (!includeUI)
+        {
+            tempFiles.Clear();
+            FileExtersion.GetAllDirectoryFileInfos(StringUtility.Concat(_assetBundlePath, "/ui"), tempFiles);
+            foreach (var file in tempFiles)
+            {
+                excludeFileFullNames.Add(file.FullName);
+            }
+        }
+        else
+        {
+            tempFiles.Clear();
+            FileExtersion.GetAllDirectoryFileInfos(StringUtility.Concat(_assetBundlePath, "/ui"), tempFiles);
+            foreach (var file in tempFiles)
+            {
+                var fileName = Path.GetFileNameWithoutExtension(file.FullName);
+                var prior = PriorBundleConfig.GetAssetPrior(AssetVersion.AssetCategory.UI, fileName);
+                if (prior > AssetPrior)
+                {
+                    excludeFileFullNames.Add(file.FullName);
+                }
+            }
+        }
+
+        // 浠庢枃浠跺垪琛ㄤ腑绉婚櫎闇�瑕佹帓闄ょ殑鏂囦欢
+        for (int i = fromFiles.Count - 1; i >= 0; i--)
+        {
+            if (excludeFileFullNames.Contains(fromFiles[i].FullName))
+            {
+                fromFiles.RemoveAt(i);
+            }
+        }
+
+        foreach (var item in fromFiles)
+        {
+            var extension = Path.GetExtension(item.FullName);
+            if (extension == ".meta")
+                continue;
+
+            var relativePath = FileExtersion.GetFileRelativePath(_assetBundlePath, item.FullName);
+            if (relativePath.StartsWith("patch"))
+                continue;
+
+            var to = StringUtility.Concat(_streamingPath, relativePath);
+            var directory = Path.GetDirectoryName(to);
+            if (!Directory.Exists(directory))
+                Directory.CreateDirectory(directory);
+
+            File.Copy(item.FullName, to, true);
+        }
+    }
+
+    /// <summary>
+    /// 鍏ㄥ寘鎷疯礉锛氭嫹璐濇墍鏈夎祫婧愶紙鎺掗櫎patch鍜�.meta锛�
+    /// </summary>
+    static void CopyFullAssetResources(string _assetBundlePath, string _streamingPath)
+    {
+        var files = new List<FileInfo>();
+        FileExtersion.GetAllDirectoryFileInfos(_assetBundlePath, files);
+        foreach (var file in files)
+        {
+            var extension = Path.GetExtension(file.FullName);
+            if (extension == ".meta")
+                continue;
+
+            var relativePath = FileExtersion.GetFileRelativePath(_assetBundlePath, file.FullName);
+            if (relativePath.StartsWith("patch"))
+                continue;
+
+            var to = StringUtility.Concat(_streamingPath, relativePath);
+            var directory = Path.GetDirectoryName(to);
+            if (!Directory.Exists(directory))
+                Directory.CreateDirectory(directory);
+
+            File.Copy(file.FullName, to, true);
+        }
+    }
+
     public static void BuildIpa(string _sdkPath, string _publisher, int _buildIndex, bool _replace)
     {
         PreBuild(_publisher, _buildIndex);
diff --git a/Assets/Editor/UIComponent/ImageLanguageAdapterEditor.cs b/Assets/Editor/UIComponent/ImageLanguageAdapterEditor.cs
new file mode 100644
index 0000000..7d02575
--- /dev/null
+++ b/Assets/Editor/UIComponent/ImageLanguageAdapterEditor.cs
@@ -0,0 +1,217 @@
+using System.Collections.Generic;
+using UnityEditor;
+using UnityEngine;
+using UnityEngine.UI;
+
+[CustomEditor(typeof(ImageLanguageAdapter), true)]
+[CanEditMultipleObjects]
+public class ImageLanguageAdapterEditor : Editor
+{
+    private Dictionary<string, bool> m_FoldoutStates = new Dictionary<string, bool>();
+
+    private void OnEnable()
+    {
+        ImageLanguageAdapterHelper.Initialize();
+
+        if (target is ImageLanguageAdapter adapter)
+        {
+            if (!adapter.HasConfig(ImageLanguageAdapter.DefaultLangId))
+            {
+                adapter.ReadCurrentToConfig(ImageLanguageAdapter.DefaultLangId);
+                EditorUtility.SetDirty(adapter);
+            }
+            foreach (var langId in adapter.GetConfiguredLanguages()) m_FoldoutStates.TryAdd(langId, false);
+        }
+    }
+
+    public override void OnInspectorGUI()
+    {
+        var adapter = target as ImageLanguageAdapter;
+
+        DrawBasicInfo(adapter);
+        EditorGUILayout.Space();
+        DrawLanguageConfigs(adapter);
+
+        if (GUI.changed) EditorUtility.SetDirty(target);
+    }
+
+    private void DrawBasicInfo(ImageLanguageAdapter adapter)
+    {
+        using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox))
+        {
+            EditorGUILayout.LabelField("鍩烘湰淇℃伅", EditorStyles.boldLabel);
+
+            EditorGUI.BeginChangeCheck();
+
+            Component newTarget = (Component)EditorGUILayout.ObjectField("鐩爣缁勪欢", adapter.TargetImageComponent, typeof(Component), true);
+            ImageComponentType newType = (ImageComponentType)EditorGUILayout.EnumPopup("缁勪欢绫诲瀷", adapter.TargetImageType);
+
+            if (EditorGUI.EndChangeCheck())
+            {
+                Undo.RecordObject(adapter, "Update Basic Info");
+
+                if (newTarget != null && newTarget != adapter.TargetImageComponent)
+                {
+                    string typeName = newTarget.GetType().Name;
+                    if (typeName == "ImageEx") newType = ImageComponentType.ImageEx;
+                    else if (newTarget is Image) newType = ImageComponentType.Image;
+                }
+
+                adapter.TargetImageComponent = newTarget;
+                adapter.TargetImageType = newType;
+            }
+
+            EditorGUILayout.HelpBox("璇锋墜鍔ㄦ嫋鎷借閫傞厤鐨勫浘鐗囩粍浠讹紝骞剁‘璁ょ粍浠剁被鍨嬫槸鍚︽纭��", MessageType.Info);
+        }
+    }
+
+    private void DrawLanguageConfigs(ImageLanguageAdapter adapter)
+    {
+        using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox))
+        {
+            EditorGUILayout.LabelField("璇█鎺掔増閰嶇疆", EditorStyles.boldLabel);
+            EditorGUILayout.HelpBox("default浠呯敤浜庣紪杈戞椂鎭㈠鍒濆鐘舵��,鏃犲搴旇瑷�閰嶇疆鏃剁敤缁勪欢褰撳墠鐘舵��", MessageType.Info);
+
+            foreach (var langId in adapter.GetConfiguredLanguages()) DrawLanguageConfigItem(adapter, langId);
+
+            EditorGUILayout.Space();
+            DrawAddLanguageConfig(adapter);
+        }
+    }
+
+    private void DrawLanguageConfigItem(ImageLanguageAdapter adapter, string langId)
+    {
+        m_FoldoutStates.TryAdd(langId, false);
+
+        int presetIndex = System.Array.IndexOf(ImageLanguageAdapterHelper.PresetLanguageIds, langId);
+        string name = presetIndex >= 0 ? ImageLanguageAdapterHelper.PresetLanguageNames[presetIndex] : ImageLanguageAdapter.GetLanguageShowName(langId);
+        string label = string.IsNullOrEmpty(name) ? langId : $"{langId} ({name})";
+
+        EditorGUI.indentLevel++;
+        if (langId == ImageLanguageAdapter.DefaultLangId) GUI.backgroundColor = new Color(1f, 0.9f, 0.7f);
+
+        m_FoldoutStates[langId] = EditorGUILayout.Foldout(m_FoldoutStates[langId], label, true);
+        GUI.backgroundColor = Color.white;
+
+        if (m_FoldoutStates[langId])
+        {
+            EditorGUI.indentLevel++;
+            var config = adapter.LanguageConfigs.Get(langId);
+            if (config != null)
+            {
+                DrawConfigItem(config);
+                EditorGUILayout.Space();
+                DrawConfigItemActions(adapter, langId);
+            }
+            EditorGUI.indentLevel--;
+        }
+        EditorGUI.indentLevel--;
+    }
+
+    private void DrawConfigItem(ImageLanguageConfigItem cfg)
+    {
+        EditorGUILayout.LabelField("RectTransform 閰嶇疆", EditorStyles.miniBoldLabel);
+        EditorGUI.BeginChangeCheck();
+
+        cfg.anchoredPosition = EditorGUILayout.Vector2Field("Anchored Position", cfg.anchoredPosition);
+        cfg.sizeDelta = EditorGUILayout.Vector2Field("Size Delta", cfg.sizeDelta);
+        cfg.anchorMin = EditorGUILayout.Vector2Field("Anchor Min", cfg.anchorMin);
+        cfg.anchorMax = EditorGUILayout.Vector2Field("Anchor Max", cfg.anchorMax);
+        cfg.pivot = EditorGUILayout.Vector2Field("Pivot", cfg.pivot);
+        cfg.localScale = EditorGUILayout.Vector3Field("Local Scale", cfg.localScale);
+        cfg.localRotation = EditorGUILayout.Vector3Field("Local Rotation", cfg.localRotation);
+
+        EditorGUILayout.Space();
+        EditorGUILayout.LabelField("Image 閰嶇疆", EditorStyles.miniBoldLabel);
+
+        cfg.enabled = EditorGUILayout.Toggle("鏄剧ず", cfg.enabled);
+        cfg.color = EditorGUILayout.ColorField("Color", cfg.color);
+        cfg.type = (Image.Type)EditorGUILayout.EnumPopup("Type", cfg.type);
+
+        if (cfg.type == Image.Type.Sliced || cfg.type == Image.Type.Tiled)
+        {
+            cfg.fillCenter = EditorGUILayout.Toggle("Fill Center", cfg.fillCenter);
+        }
+
+        if (cfg.type == Image.Type.Filled)
+        {
+            cfg.fillMethod = (Image.FillMethod)EditorGUILayout.EnumPopup("Fill Method", cfg.fillMethod);
+            cfg.fillAmount = EditorGUILayout.Slider("Fill Amount", cfg.fillAmount, 0f, 1f);
+            cfg.fillOrigin = EditorGUILayout.IntField("Fill Origin", cfg.fillOrigin);
+        }
+
+        cfg.preserveAspect = EditorGUILayout.Toggle("Preserve Aspect", cfg.preserveAspect);
+        cfg.useSpriteMesh = EditorGUILayout.Toggle("Use Sprite Mesh", cfg.useSpriteMesh);
+        cfg.pixelsPerUnitMultiplier = EditorGUILayout.FloatField("Pixels Per Unit Multiplier", cfg.pixelsPerUnitMultiplier);
+
+        if (EditorGUI.EndChangeCheck()) EditorUtility.SetDirty(target);
+    }
+
+    private void DrawConfigItemActions(ImageLanguageAdapter adapter, string langId)
+    {
+        using (new EditorGUILayout.HorizontalScope())
+        {
+            GUILayout.Space(EditorGUI.indentLevel * 15f);
+
+            if (GUILayout.Button("浠庡綋鍓嶈鍙�", GUILayout.Width(80)))
+            {
+                Undo.RecordObject(adapter, "Read Current Config");
+                adapter.ReadCurrentToConfig(langId);
+            }
+
+            if (GUILayout.Button("搴旂敤", GUILayout.Width(60)))
+            {
+                Undo.RecordObject(adapter, "Apply Config");
+                adapter.ApplyConfig(langId);
+            }
+
+            using (new EditorGUI.DisabledScope(langId == ImageLanguageAdapter.DefaultLangId))
+            {
+                if (GUILayout.Button("鍒犻櫎", GUILayout.Width(50)))
+                {
+                    Undo.RecordObject(adapter, "Remove Config");
+                    adapter.RemoveConfig(langId);
+                    m_FoldoutStates.Remove(langId);
+                }
+            }
+        }
+    }
+
+    private void DrawAddLanguageConfig(ImageLanguageAdapter adapter)
+    {
+        EditorGUILayout.BeginHorizontal();
+        GUILayout.Space(15f);
+        EditorGUILayout.LabelField("娣诲姞棰勮璇█:", GUILayout.Width(100));
+
+        float viewWidth = EditorGUIUtility.currentViewWidth - 30f;
+        float currentWidth = 115f;
+        float buttonWidth = 64f;
+
+        for (int i = 0; i < ImageLanguageAdapterHelper.PresetLanguageIds.Length; i++)
+        {
+            string langId = ImageLanguageAdapterHelper.PresetLanguageIds[i];
+            if (!adapter.HasConfig(langId))
+            {
+                if (currentWidth + buttonWidth > viewWidth)
+                {
+                    EditorGUILayout.EndHorizontal();
+                    EditorGUILayout.BeginHorizontal();
+                    GUILayout.Space(115f);
+                    currentWidth = 115f;
+                }
+
+                if (GUILayout.Button(ImageLanguageAdapterHelper.PresetLanguageNames[i], GUILayout.Width(60)))
+                {
+                    Undo.RecordObject(adapter, "Add Language Config");
+                    var newConfig = adapter.HasConfig(ImageLanguageAdapter.DefaultLangId)
+                        ? adapter.LanguageConfigs.Get(ImageLanguageAdapter.DefaultLangId).Clone()
+                        : new ImageLanguageConfigItem();
+                    adapter.SetConfig(langId, newConfig);
+                    m_FoldoutStates[langId] = true;
+                }
+                currentWidth += buttonWidth;
+            }
+        }
+        EditorGUILayout.EndHorizontal();
+    }
+}
\ No newline at end of file
diff --git a/Assets/Editor/UIComponent/ImageLanguageAdapterEditor.cs.meta b/Assets/Editor/UIComponent/ImageLanguageAdapterEditor.cs.meta
new file mode 100644
index 0000000..3695ef5
--- /dev/null
+++ b/Assets/Editor/UIComponent/ImageLanguageAdapterEditor.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 6f2905447a660d641abe91b18a29680a
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Editor/UIComponent/ImageLanguageAdapterHelper.cs b/Assets/Editor/UIComponent/ImageLanguageAdapterHelper.cs
new file mode 100644
index 0000000..50dc412
--- /dev/null
+++ b/Assets/Editor/UIComponent/ImageLanguageAdapterHelper.cs
@@ -0,0 +1,67 @@
+using System.Collections.Generic;
+using System.IO;
+using LitJson;
+using UnityEngine;
+
+/// <summary>
+/// 缂栬緫鍣ㄥ浘鐗囪瑷�閰嶇疆璇诲彇鍔╂墜
+/// 璐熻矗缁熶竴瑙f瀽 InitialFunction.txt
+/// </summary>
+public static class ImageLanguageAdapterHelper
+{
+    public static string[] PresetLanguageIds { get; private set; }
+    public static string[] PresetLanguageNames { get; private set; }
+    private static bool s_Initialized;
+
+    public static void Initialize()
+    {
+        if (s_Initialized) return;
+
+        var idList = new List<string> { ImageLanguageAdapter.DefaultLangId };
+        var nameList = new List<string> { "榛樿" };
+
+        string configPath = Path.Combine(Application.dataPath, "ResourcesOut/Config/InitialFunction.txt");
+        if (File.Exists(configPath))
+        {
+            try
+            {
+                string[] lines = File.ReadAllLines(configPath);
+                for (int i = 3; i < lines.Length; i++)
+                {
+                    string line = lines[i];
+                    if (string.IsNullOrWhiteSpace(line)) continue;
+
+                    int index = line.IndexOf('\t');
+                    if (index == -1) continue;
+
+                    string key = line.Substring(0, index);
+                    if (key == "LanguageEx")
+                    {
+                        string[] fields = line.Split('\t');
+                        if (fields.Length > 1 && !string.IsNullOrEmpty(fields[1]))
+                        {
+                            var dict = JsonMapper.ToObject<Dictionary<string, string>>(fields[1]);
+                            if (dict != null)
+                            {
+                                foreach (var kvp in dict)
+                                {
+                                    idList.Add(kvp.Key);
+                                    nameList.Add(kvp.Value);
+                                }
+                            }
+                        }
+                        break;
+                    }
+                }
+            }
+            catch (System.Exception ex)
+            {
+                Debug.LogWarning($"[ImageLanguageAdapterHelper] 璇诲彇閰嶇疆澶辫触: {ex.Message}");
+            }
+        }
+
+        PresetLanguageIds = idList.ToArray();
+        PresetLanguageNames = nameList.ToArray();
+        s_Initialized = true;
+    }
+}
\ No newline at end of file
diff --git a/Assets/Editor/UIComponent/ImageLanguageAdapterHelper.cs.meta b/Assets/Editor/UIComponent/ImageLanguageAdapterHelper.cs.meta
new file mode 100644
index 0000000..8556591
--- /dev/null
+++ b/Assets/Editor/UIComponent/ImageLanguageAdapterHelper.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: dd266766e23062e4eb668f7102620a06
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Editor/UIComponent/ImageLanguageAdapterScanTool.cs b/Assets/Editor/UIComponent/ImageLanguageAdapterScanTool.cs
new file mode 100644
index 0000000..6590bc4
--- /dev/null
+++ b/Assets/Editor/UIComponent/ImageLanguageAdapterScanTool.cs
@@ -0,0 +1,594 @@
+using System.Collections.Generic;
+using System.IO;
+using UnityEditor;
+using UnityEditor.IMGUI.Controls;
+using UnityEngine;
+
+public class ImageScanResultItem
+{
+    public string PrefabPath { get; }
+    public string GameObjectPath { get; }
+    public List<string> MissingLanguages { get; set; } = new List<string>();
+    public ImageComponentType ComponentType { get; }
+    public string PrefabGUID { get; }
+
+    public ImageScanResultItem(string path, string goPath, ImageComponentType type, string guid)
+    {
+        PrefabPath = path;
+        GameObjectPath = goPath;
+        ComponentType = type;
+        PrefabGUID = guid;
+    }
+
+    public string GetDisplayName() => $"{GameObjectPath} (缂哄皯: {string.Join(", ", MissingLanguages)}) [{ComponentType}]";
+}
+
+public class ImagePrefabScanResult
+{
+    public string PrefabPath { get; }
+    public string PrefabGUID { get; }
+    public List<ImageScanResultItem> Items { get; } = new List<ImageScanResultItem>();
+
+    public ImagePrefabScanResult(string path, string guid)
+    {
+        PrefabPath = path;
+        PrefabGUID = guid;
+    }
+}
+
+public class ImageScanResultSummary
+{
+    public string ScanDirectory { get; }
+    public int TotalPrefabsScanned { get; set; }
+    public int TotalAdaptersFound { get; set; }
+    public int AdaptersWithMissingConfig { get; private set; }
+    public int PrefabsWithIssueCount { get; private set; }
+    public int PrefabsWithoutIssueCount { get; private set; }
+    public List<ImagePrefabScanResult> PrefabResults { get; } = new List<ImagePrefabScanResult>();
+    public List<ImagePrefabScanResult> PrefabResultsWithoutIssue { get; } = new List<ImagePrefabScanResult>();
+
+    public ImageScanResultSummary(string dir) => ScanDirectory = dir;
+
+    public void AddResult(ImageScanResultItem item)
+    {
+        var prefabResult = PrefabResults.Find(p => p.PrefabPath == item.PrefabPath);
+        if (prefabResult == null)
+        {
+            prefabResult = new ImagePrefabScanResult(item.PrefabPath, item.PrefabGUID);
+            PrefabResults.Add(prefabResult);
+            PrefabsWithIssueCount++;
+        }
+        prefabResult.Items.Add(item);
+        
+        if (item.MissingLanguages.Count > 0)
+        {
+            AdaptersWithMissingConfig++;
+        }
+    }
+
+    public void AddPrefabWithoutIssue(string path, string guid, List<ImageScanResultItem> items)
+    {
+        var prefabResult = new ImagePrefabScanResult(path, guid);
+        prefabResult.Items.AddRange(items);
+        PrefabResultsWithoutIssue.Add(prefabResult);
+        PrefabsWithoutIssueCount++;
+    }
+}
+
+public enum ImageScanResultFilterMode
+{
+    鍏ㄩ儴,
+    浠呮樉绀烘湁闂,
+    浠呮樉绀烘棤闂
+}
+
+public class ImageMetadataTreeViewItem : TreeViewItem
+{
+    public object Metadata { get; }
+    public ImageMetadataTreeViewItem(int id, string name, object meta) : base(id, 0, name) => Metadata = meta;
+}
+
+public class ImageScanResultTreeView : TreeView
+{
+    private ImageScanResultSummary m_Summary;
+    private ImageScanResultFilterMode m_FilterMode;
+
+    public ImageScanResultTreeView(TreeViewState state, ImageScanResultSummary summary, ImageScanResultFilterMode filterMode) : base(state)
+    {
+        m_Summary = summary;
+        m_FilterMode = filterMode;
+        Reload();
+        ExpandAll();
+    }
+
+    protected override TreeViewItem BuildRoot()
+    {
+        var root = new TreeViewItem { id = 0, depth = -1, displayName = "Root" };
+
+        root.children = new List<TreeViewItem>();
+
+        if (m_Summary == null)
+            return root;
+
+        int itemId = 1;
+
+        bool showWithIssue = m_FilterMode == ImageScanResultFilterMode.鍏ㄩ儴 || 
+                            m_FilterMode == ImageScanResultFilterMode.浠呮樉绀烘湁闂;
+        bool showWithoutIssue = m_FilterMode == ImageScanResultFilterMode.鍏ㄩ儴 || 
+                               m_FilterMode == ImageScanResultFilterMode.浠呮樉绀烘棤闂;
+
+        if (showWithIssue)
+        {
+            foreach (var prefabResult in m_Summary.PrefabResults)
+            {
+                string prefabName = Path.GetFileNameWithoutExtension(prefabResult.PrefabPath);
+                
+                int issueCount = 0;
+                foreach (var item in prefabResult.Items)
+                {
+                    if (item.MissingLanguages != null && item.MissingLanguages.Count > 0)
+                        issueCount++;
+                }
+
+                var prefabItem = new ImageMetadataTreeViewItem(itemId++, $"{prefabName} ({issueCount}涓棶棰�)", prefabResult);
+
+                foreach (var adapterItem in prefabResult.Items)
+                {
+                    string displayName;
+                    if (adapterItem.MissingLanguages.Count > 0)
+                        displayName = adapterItem.GetDisplayName();
+                    else
+                        displayName = $"{adapterItem.GameObjectPath} (閰嶇疆瀹屾暣)";
+                    var adapterTreeItem = new ImageMetadataTreeViewItem(itemId++, displayName, adapterItem);
+                    prefabItem.AddChild(adapterTreeItem);
+                }
+                root.AddChild(prefabItem);
+            }
+        }
+
+        if (showWithoutIssue)
+        {
+            foreach (var prefabResult in m_Summary.PrefabResultsWithoutIssue)
+            {
+                string prefabName = Path.GetFileNameWithoutExtension(prefabResult.PrefabPath);
+                var prefabItem = new ImageMetadataTreeViewItem(itemId++, $"{prefabName} (鏃犻棶棰�)", prefabResult);
+
+                foreach (var adapterItem in prefabResult.Items)
+                {
+                    var displayName = $"{adapterItem.GameObjectPath} (閰嶇疆瀹屾暣)";
+                    var adapterTreeItem = new ImageMetadataTreeViewItem(itemId++, displayName, adapterItem);
+                    prefabItem.AddChild(adapterTreeItem);
+                }
+                root.AddChild(prefabItem);
+            }
+        }
+
+        SetupDepthsFromParentsAndChildren(root);
+
+        return root;
+    }
+
+    protected override void RowGUI(RowGUIArgs args)
+    {
+        var item = args.item as ImageMetadataTreeViewItem;
+        if (item != null && item.Metadata is ImagePrefabScanResult)
+            GUI.Label(args.rowRect, item.displayName, EditorStyles.boldLabel);
+        else if (item != null && item.Metadata is ImageScanResultItem adapterItem)
+        {
+            if (adapterItem.MissingLanguages.Count > 0)
+                GUI.Label(args.rowRect, $"{adapterItem.GameObjectPath} (缂哄皯: {string.Join(", ", adapterItem.MissingLanguages)})");
+            else
+                GUI.Label(args.rowRect, item.displayName);
+        }
+        else
+            base.RowGUI(args);
+    }
+
+    protected override void DoubleClickedItem(int id)
+    {
+        var item = FindItem(id, rootItem) as ImageMetadataTreeViewItem;
+        if (item == null) return;
+
+        if (item.Metadata is ImageScanResultItem adapterItem)
+            PingGameObject(adapterItem.PrefabPath, adapterItem.GameObjectPath);
+        else if (item.Metadata is ImagePrefabScanResult prefabResult)
+        {
+            var obj = AssetDatabase.LoadAssetAtPath<GameObject>(prefabResult.PrefabPath);
+            if (obj != null)
+            {
+                Selection.activeObject = obj;
+                EditorGUIUtility.PingObject(obj);
+            }
+        }
+    }
+
+    private void PingGameObject(string prefabPath, string gameObjectPath)
+    {
+        var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
+        if (prefab == null) return;
+
+        Transform target = prefab.transform.Find(gameObjectPath);
+        if (target != null)
+        {
+            Selection.activeObject = target.gameObject;
+            EditorGUIUtility.PingObject(target.gameObject);
+        }
+        else
+        {
+            Selection.activeObject = prefab;
+            EditorGUIUtility.PingObject(prefab);
+            Debug.LogWarning($"[ImageScanTool] 鎵句笉鍒拌矾寰� '{gameObjectPath}'锛屽凡閫変腑鏁翠釜棰勫埗浣�");
+        }
+    }
+}
+
+public class ImageLanguageAdapterScanTool : EditorWindow
+{
+    private string m_ScanDirectory = "Assets";
+    private Vector2 m_ScrollPosition;
+    private ImageScanResultSummary m_ScanResult;
+    private bool m_IsScanning;
+    private float m_ScanProgress;
+    private string m_ScanStatus;
+
+    private ImageScanResultTreeView m_TreeView;
+    private TreeViewState m_TreeViewState;
+    private ImageScanResultFilterMode m_ResultFilterMode = ImageScanResultFilterMode.鍏ㄩ儴;
+
+    private int m_SourceLangIndex = 0;
+    private int m_TargetLangIndex = 0;
+    private bool m_OverwriteExisting = false;
+
+    [MenuItem("绋嬪簭/ImageLanguageAdapter鎵弿涓庣鐞嗗伐鍏�")]
+    public static void ShowWindow()
+    {
+        var window = GetWindow<ImageLanguageAdapterScanTool>("鍥剧墖璇█閫傞厤鍣ㄦ壂鎻�");
+        window.minSize = new Vector2(600f, 500f);
+    }
+
+    private void OnEnable() => ImageLanguageAdapterHelper.Initialize();
+
+    private void OnGUI()
+    {
+        DrawHeader();
+        EditorGUILayout.Space(5f);
+        DrawScanSettings();
+        EditorGUILayout.Space(5f);
+        DrawScanButton();
+        EditorGUILayout.Space(5f);
+        DrawResults();
+        EditorGUILayout.Space(5f);
+        DrawBatchOperations();
+    }
+
+    private void DrawHeader()
+    {
+        using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox))
+        {
+            EditorGUILayout.LabelField("ImageLanguageAdapter 閰嶇疆缂哄け鎵弿涓庢壒閲忔搷浣滃伐鍏�", EditorStyles.boldLabel);
+            EditorGUILayout.HelpBox("鎵弿鎸囧畾鐩綍涓嬫墍鏈夐鍒朵綋锛屾娴嬬粍浠剁殑璇█閰嶇疆鏄惁瀹屾暣锛屾垨鎵ц鎵归噺璇█閰嶇疆澶嶅埗銆�", MessageType.Info);
+        }
+    }
+
+    private void DrawScanSettings()
+    {
+        using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox))
+        {
+            EditorGUILayout.LabelField("鎵弿璁剧疆", EditorStyles.boldLabel);
+
+            using (new EditorGUILayout.HorizontalScope())
+            {
+                EditorGUILayout.LabelField("鐩爣鐩綍:", GUILayout.Width(80f));
+                m_ScanDirectory = EditorGUILayout.TextField(m_ScanDirectory);
+                if (GUILayout.Button("閫夋嫨...", GUILayout.Width(70f)))
+                {
+                    string path = EditorUtility.OpenFolderPanel("閫夋嫨鎵弿鐩綍", Application.dataPath, "");
+                    if (!string.IsNullOrEmpty(path))
+                        m_ScanDirectory = path.StartsWith(Application.dataPath) ? "Assets" + path.Substring(Application.dataPath.Length) : path;
+                }
+            }
+
+            EditorGUILayout.Space(5f);
+            EditorGUILayout.LabelField($"棰勮璇█:");
+
+            EditorGUILayout.BeginHorizontal();
+            GUILayout.Space(15f);
+            int displayCount = 0;
+            foreach (var langId in ImageLanguageAdapterHelper.PresetLanguageIds)
+            {
+                if (langId == ImageLanguageAdapter.DefaultLangId) continue;
+                EditorGUILayout.LabelField(langId, GUILayout.Width(50f));
+                if (++displayCount % 8 == 0)
+                {
+                    EditorGUILayout.EndHorizontal();
+                    EditorGUILayout.BeginHorizontal();
+                    GUILayout.Space(15f);
+                }
+            }
+            EditorGUILayout.EndHorizontal();
+        }
+    }
+
+    private void DrawScanButton()
+    {
+        using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox))
+        {
+            using (new EditorGUILayout.HorizontalScope())
+            {
+                using (new EditorGUI.DisabledScope(m_IsScanning || string.IsNullOrEmpty(m_ScanDirectory)))
+                {
+                    if (GUILayout.Button("寮�濮嬫壂鎻�", GUILayout.Height(30f))) StartScan();
+                }
+
+                if (m_IsScanning)
+                {
+                    EditorGUILayout.LabelField("鎵弿涓�...", GUILayout.Width(100f));
+                    m_ScanProgress = EditorGUILayout.Slider(m_ScanProgress, 0f, 1f);
+                    Repaint();
+                }
+            }
+            if (!string.IsNullOrEmpty(m_ScanStatus)) EditorGUILayout.LabelField(m_ScanStatus);
+        }
+    }
+
+    private void DrawResults()
+    {
+        using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox))
+        {
+            EditorGUILayout.LabelField("鎵弿缁撴灉", EditorStyles.boldLabel);
+
+            if (m_ScanResult != null)
+            {
+                EditorGUILayout.LabelField($"棰勫埗浣撴�绘暟: {m_ScanResult.TotalPrefabsScanned} | 鏈堿dapter鐨勯鍒朵綋: {m_ScanResult.PrefabsWithIssueCount + m_ScanResult.PrefabsWithoutIssueCount} | Adapter鎬绘暟: {m_ScanResult.TotalAdaptersFound} | 缂哄け閰嶇疆: {m_ScanResult.AdaptersWithMissingConfig} | 鏈夐棶棰橀鍒朵綋: {m_ScanResult.PrefabsWithIssueCount} | 鏃犻棶棰橀鍒朵綋: {m_ScanResult.PrefabsWithoutIssueCount}");
+                EditorGUILayout.Space(5f);
+
+                using (new EditorGUILayout.HorizontalScope())
+                {
+                    EditorGUILayout.LabelField("鏄剧ず妯″紡:", GUILayout.Width(60f));
+                    EditorGUI.BeginChangeCheck();
+                    m_ResultFilterMode = (ImageScanResultFilterMode)EditorGUILayout.EnumPopup(m_ResultFilterMode);
+                    if (EditorGUI.EndChangeCheck() && m_TreeView != null)
+                    {
+                        m_TreeViewState ??= new TreeViewState();
+                        m_TreeView = new ImageScanResultTreeView(m_TreeViewState, m_ScanResult, m_ResultFilterMode);
+                    }
+                }
+                EditorGUILayout.Space(5f);
+
+                if (m_TreeView != null)
+                {
+                    m_ScrollPosition = EditorGUILayout.BeginScrollView(m_ScrollPosition, GUILayout.MinHeight(150f));
+                    var rect = EditorGUILayout.GetControlRect(false, m_TreeView.totalHeight);
+                    m_TreeView.OnGUI(rect);
+                    EditorGUILayout.EndScrollView();
+                }
+
+                EditorGUILayout.Space(5f);
+                using (new EditorGUILayout.HorizontalScope())
+                {
+                    if (GUILayout.Button("灞曞紑鍏ㄩ儴")) m_TreeView?.ExpandAll();
+                    if (GUILayout.Button("鎶樺彔鍏ㄩ儴")) m_TreeView?.CollapseAll();
+                }
+            }
+            else
+            {
+                EditorGUILayout.HelpBox("鐐瑰嚮銆屽紑濮嬫壂鎻忋�嶆寜閽繘琛屾壂鎻�", MessageType.None);
+            }
+        }
+    }
+
+    private void DrawBatchOperations()
+    {
+        using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox))
+        {
+            EditorGUILayout.LabelField("鎵归噺鎿嶄綔 (閽堝鐩爣鐩綍涓嬬殑鎵�鏈夐鍒朵綋涓婄殑ImageLanguageAdapter缁勪欢)", EditorStyles.boldLabel);
+
+            if (ImageLanguageAdapterHelper.PresetLanguageIds == null || ImageLanguageAdapterHelper.PresetLanguageIds.Length == 0)
+                return;
+
+            using (new EditorGUILayout.HorizontalScope())
+            {
+                EditorGUILayout.LabelField("鏃ц瑷�:", GUILayout.Width(60f));
+                m_SourceLangIndex = EditorGUILayout.Popup(m_SourceLangIndex, ImageLanguageAdapterHelper.PresetLanguageNames);
+
+                GUILayout.Space(20f);
+
+                EditorGUILayout.LabelField("鏂拌瑷�:", GUILayout.Width(60f));
+                m_TargetLangIndex = EditorGUILayout.Popup(m_TargetLangIndex, ImageLanguageAdapterHelper.PresetLanguageNames);
+            }
+
+            m_OverwriteExisting = EditorGUILayout.Toggle("瑕嗙洊宸插瓨鍦ㄧ殑鐩爣閰嶇疆", m_OverwriteExisting);
+
+            EditorGUILayout.Space(5f);
+
+            bool isSameLanguage = m_SourceLangIndex == m_TargetLangIndex;
+            using (new EditorGUI.DisabledScope(isSameLanguage || m_IsScanning || string.IsNullOrEmpty(m_ScanDirectory)))
+            {
+                if (GUILayout.Button("鎵归噺澶嶅埗閰嶇疆", GUILayout.Height(30f)))
+                {
+                    string sourceLang = ImageLanguageAdapterHelper.PresetLanguageIds[m_SourceLangIndex];
+                    string targetLang = ImageLanguageAdapterHelper.PresetLanguageIds[m_TargetLangIndex];
+
+                    if (EditorUtility.DisplayDialog("楂樺嵄鎿嶄綔纭",
+                        $"姝ゆ搷浣滃皢閬嶅巻銆恵m_ScanDirectory}銆戜笅鎵�鏈夐鍒朵綋銆俓n\n" +
+                        $"鎶婂畠浠殑 [{sourceLang}] 閰嶇疆澶嶅埗骞跺簲鐢ㄥ埌 [{targetLang}] 閰嶇疆涓娿�俓n\n" +
+                        $"姝ゆ搷浣滀笉鍙挙閿�锛佸缓璁彁鍓嶄娇鐢� Git/SVN 鎻愪氦浠g爜銆俓n纭畾瑕佺户缁悧锛�",
+                        "纭畾鎵ц", "鍙栨秷"))
+                    {
+                        ExecuteBatchCopy(sourceLang, targetLang);
+                    }
+                }
+            }
+
+            if (isSameLanguage)
+            {
+                EditorGUILayout.HelpBox("鏃ц瑷�涓庢柊璇█涓嶈兘鐩稿悓", MessageType.Warning);
+            }
+        }
+    }
+
+    private void ExecuteBatchCopy(string sourceLang, string targetLang)
+    {
+        string[] prefabGuids = AssetDatabase.FindAssets("t:Prefab", new[] { m_ScanDirectory });
+        if (prefabGuids.Length == 0) return;
+
+        int modifiedPrefabCount = 0;
+        int modifiedAdapterCount = 0;
+
+        try
+        {
+            for (int i = 0; i < prefabGuids.Length; i++)
+            {
+                string path = AssetDatabase.GUIDToAssetPath(prefabGuids[i]);
+                EditorUtility.DisplayProgressBar("鎵归噺澶嶅埗閰嶇疆", $"澶勭悊涓� ({i + 1}/{prefabGuids.Length}): {path}", (float)i / prefabGuids.Length);
+
+                GameObject prefabAsset = AssetDatabase.LoadAssetAtPath<GameObject>(path);
+                bool isModified = false;
+
+                foreach (var adapter in prefabAsset.GetComponentsInChildren<ImageLanguageAdapter>(true))
+                {
+                    if (adapter.HasConfig(sourceLang))
+                    {
+                        if (m_OverwriteExisting || !adapter.HasConfig(targetLang))
+                        {
+                            Undo.RecordObject(adapter, "Batch Copy Language Config");
+
+                            var clonedConfig = adapter.GetConfig(sourceLang).Clone();
+                            adapter.SetConfig(targetLang, clonedConfig);
+
+                            EditorUtility.SetDirty(adapter);
+                            isModified = true;
+                            modifiedAdapterCount++;
+                        }
+                    }
+                }
+
+                if (isModified)
+                {
+                    modifiedPrefabCount++;
+                }
+            }
+        }
+        finally
+        {
+            EditorUtility.ClearProgressBar();
+            AssetDatabase.SaveAssets();
+
+            PerformScan();
+
+            EditorUtility.DisplayDialog("鎵归噺鎿嶄綔瀹屾垚",
+                $"鎵归噺澶嶅埗缁撴潫锛乗n\n淇敼鐨勯鍒朵綋鏁伴噺: {modifiedPrefabCount} 涓猏n鏇存柊鐨勯�傞厤鍣ㄩ厤缃暟閲�: {modifiedAdapterCount} 涓�",
+                "纭");
+        }
+    }
+
+    private void StartScan()
+    {
+        m_IsScanning = true;
+        m_ScanProgress = 0f;
+        m_ScanStatus = "鍑嗗鎵弿...";
+
+        EditorApplication.CallbackFunction updateCallback = null;
+        updateCallback = () =>
+        {
+            if (!m_IsScanning)
+            {
+                EditorApplication.update -= updateCallback;
+                return;
+            }
+            PerformScan();
+            m_IsScanning = false;
+            EditorApplication.update -= updateCallback;
+        };
+        EditorApplication.update += updateCallback;
+    }
+
+    private void PerformScan()
+    {
+        m_ScanResult = new ImageScanResultSummary(m_ScanDirectory);
+        string[] prefabGuids = AssetDatabase.FindAssets("t:Prefab", new[] { m_ScanDirectory });
+        m_ScanResult.TotalPrefabsScanned = prefabGuids.Length;
+
+        if (prefabGuids.Length == 0)
+        {
+            m_ScanStatus = "鏈壘鍒颁换浣曢鍒朵綋";
+            return;
+        }
+
+        for (int i = 0; i < prefabGuids.Length; i++)
+        {
+            string path = AssetDatabase.GUIDToAssetPath(prefabGuids[i]);
+            ScanPrefab(path, prefabGuids[i]);
+
+            m_ScanProgress = (float)(i + 1) / prefabGuids.Length;
+            m_ScanStatus = $"姝e湪鎵弿: {Path.GetFileName(path)} ({i + 1}/{prefabGuids.Length})";
+
+            if (i % 10 == 0) Repaint();
+        }
+
+        m_TreeViewState ??= new TreeViewState();
+        m_TreeView = new ImageScanResultTreeView(m_TreeViewState, m_ScanResult, m_ResultFilterMode);
+        m_ScanStatus = $"鎵弿瀹屾垚! 鍙戠幇 {m_ScanResult.AdaptersWithMissingConfig} 涓己澶遍厤缃�";
+        Repaint();
+    }
+
+    private void ScanPrefab(string path, string guid)
+    {
+        GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(path);
+        if (prefab == null) return;
+
+        var adapters = prefab.GetComponentsInChildren<ImageLanguageAdapter>(true);
+        if (adapters.Length == 0) return;
+
+        m_ScanResult.TotalAdaptersFound += adapters.Length;
+
+        bool hasIssue = false;
+        List<ImageScanResultItem> allItems = new List<ImageScanResultItem>();
+
+        foreach (var adapter in adapters)
+        {
+            List<string> missing = new List<string>();
+            foreach (var langId in ImageLanguageAdapterHelper.PresetLanguageIds)
+            {
+                if (langId == ImageLanguageAdapter.DefaultLangId) continue;
+                if (!adapter.HasConfig(langId)) missing.Add(langId);
+            }
+
+            var item = new ImageScanResultItem(path, GetGameObjectPath(adapter.gameObject, prefab), adapter.TargetImageType, guid)
+            {
+                MissingLanguages = missing
+            };
+
+            allItems.Add(item);
+
+            if (missing.Count > 0)
+            {
+                hasIssue = true;
+            }
+        }
+
+        if (hasIssue)
+        {
+            foreach (var item in allItems)
+            {
+                m_ScanResult.AddResult(item);
+            }
+        }
+        else
+        {
+            m_ScanResult.AddPrefabWithoutIssue(path, guid, allItems);
+        }
+    }
+
+    private string GetGameObjectPath(GameObject go, GameObject root)
+    {
+        var parts = new List<string>();
+        Transform curr = go.transform;
+        while (curr != null && curr != root.transform)
+        {
+            parts.Insert(0, curr.name);
+            curr = curr.parent;
+        }
+        return string.Join("/", parts);
+    }
+}
\ No newline at end of file
diff --git a/Assets/Editor/UIComponent/ImageLanguageAdapterScanTool.cs.meta b/Assets/Editor/UIComponent/ImageLanguageAdapterScanTool.cs.meta
new file mode 100644
index 0000000..67cda7e
--- /dev/null
+++ b/Assets/Editor/UIComponent/ImageLanguageAdapterScanTool.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 7d753da3934048046828299f1ee02998
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Editor/UIComponent/TextLanguageAdapterEditor.cs b/Assets/Editor/UIComponent/TextLanguageAdapterEditor.cs
index 8c2934b..828752a 100644
--- a/Assets/Editor/UIComponent/TextLanguageAdapterEditor.cs
+++ b/Assets/Editor/UIComponent/TextLanguageAdapterEditor.cs
@@ -40,14 +40,30 @@
         {
             EditorGUILayout.LabelField("鍩烘湰淇℃伅", EditorStyles.boldLabel);
             
-            using (new EditorGUI.DisabledScope(true))
+            EditorGUI.BeginChangeCheck();
+            
+            // 鍏佽鎵嬪姩鎷栨嫿鐩爣缁勪欢鍜岃缃被鍨�
+            Component newTarget = (Component)EditorGUILayout.ObjectField("鐩爣缁勪欢", adapter.TargetTextComponent, typeof(Component), true);
+            TextComponentType newType = (TextComponentType)EditorGUILayout.EnumPopup("缁勪欢绫诲瀷", adapter.TargetTextType);
+            
+            if (EditorGUI.EndChangeCheck())
             {
-                EditorGUILayout.ObjectField("鐩爣缁勪欢", adapter.LanguageConfigs.keys.Count > 0 ? adapter.TargetTextComponent : null, typeof(Component), true);
-                EditorGUILayout.EnumPopup("缁勪欢绫诲瀷", adapter.TargetTextType);
+                Undo.RecordObject(adapter, "Update Basic Info");
+                
+                // 鏅鸿兘杈呭姪锛氬綋鐢ㄦ埛鎷栨嫿鏂扮粍浠舵椂锛屽皾璇曡嚜鍔ㄥ尮閰嶄竴涓嬬被鍨嬶紝浣嗕篃鍏佽鎵嬪姩瑕嗙洊
+                if (newTarget != null && newTarget != adapter.TargetTextComponent)
+                {
+                    string typeName = newTarget.GetType().Name;
+                    if (typeName == "GradientText") newType = TextComponentType.GradientText;
+                    else if (typeName == "TextEx") newType = TextComponentType.TextEx;
+                    else if (newTarget is UnityEngine.UI.Text) newType = TextComponentType.Text;
+                }
+
+                adapter.TargetTextComponent = newTarget;
+                adapter.TargetTextType = newType;
             }
 
-            if (GUILayout.Button("鍒锋柊缁勪欢妫�娴�", GUILayout.Width(120))) adapter.Editor_ForceRefreshDetection();
-            EditorGUILayout.HelpBox("缁勪欢鎴栫被鍨嬪彉鏇村悗闇�鎵ц鍒锋柊缁勪欢妫�娴�", MessageType.Info);
+            EditorGUILayout.HelpBox("璇锋墜鍔ㄦ嫋鎷借閫傞厤鐨勬枃鏈粍浠讹紝骞剁‘璁ょ粍浠剁被鍨嬫槸鍚︽纭��", MessageType.Info);
         }
     }
 
diff --git a/Assets/Editor/UIComponent/TextLanguageAdapterScanTool.cs b/Assets/Editor/UIComponent/TextLanguageAdapterScanTool.cs
index c72f011..4960ad9 100644
--- a/Assets/Editor/UIComponent/TextLanguageAdapterScanTool.cs
+++ b/Assets/Editor/UIComponent/TextLanguageAdapterScanTool.cs
@@ -44,7 +44,10 @@
     public int TotalPrefabsScanned { get; set; }
     public int TotalAdaptersFound { get; set; } 
     public int AdaptersWithMissingConfig { get; private set; }
+    public int PrefabsWithIssueCount { get; private set; }
+    public int PrefabsWithoutIssueCount { get; private set; }
     public List<PrefabScanResult> PrefabResults { get; } = new List<PrefabScanResult>();
+    public List<PrefabScanResult> PrefabResultsWithoutIssue { get; } = new List<PrefabScanResult>();
 
     public ScanResultSummary(string dir) => ScanDirectory = dir;
 
@@ -55,13 +58,33 @@
         {
             prefabResult = new PrefabScanResult(item.PrefabPath, item.PrefabGUID);
             PrefabResults.Add(prefabResult);
+            PrefabsWithIssueCount++;
         }
         prefabResult.Items.Add(item);
-        AdaptersWithMissingConfig++;
+        
+        if (item.MissingLanguages.Count > 0)
+        {
+            AdaptersWithMissingConfig++;
+        }
+    }
+
+    public void AddPrefabWithoutIssue(string path, string guid, List<ScanResultItem> items)
+    {
+        var prefabResult = new PrefabScanResult(path, guid);
+        prefabResult.Items.AddRange(items);
+        PrefabResultsWithoutIssue.Add(prefabResult);
+        PrefabsWithoutIssueCount++;
     }
 }
 
 // ======================== TreeView 瀹炵幇 ========================
+
+public enum ScanResultFilterMode
+{
+    鍏ㄩ儴,
+    浠呮樉绀烘湁闂,
+    浠呮樉绀烘棤闂
+}
 
 public class MetadataTreeViewItem : TreeViewItem
 {
@@ -72,10 +95,12 @@
 public class ScanResultTreeView : TreeView
 {
     private ScanResultSummary m_Summary;
+    private ScanResultFilterMode m_FilterMode;
 
-    public ScanResultTreeView(TreeViewState state, ScanResultSummary summary) : base(state)
+    public ScanResultTreeView(TreeViewState state, ScanResultSummary summary, ScanResultFilterMode filterMode) : base(state)
     {
         m_Summary = summary;
+        m_FilterMode = filterMode;
         Reload();
         ExpandAll();
     }
@@ -84,28 +109,64 @@
     {
         var root = new TreeViewItem { id = 0, depth = -1, displayName = "Root" };
         
-        // 銆愬叧閿慨澶� 1銆戯細蹇呴』鍒濆鍖� children 鍒楄〃銆�
-        // 鍚﹀垯褰撴壂鎻忕粨鏋滃畬缇庯紙0涓敊璇級鏃讹紝root.children 涓� null 浼氬鑷� Unity 鎶ラ敊
         root.children = new List<TreeViewItem>();
 
-        if (m_Summary == null || m_Summary.PrefabResults.Count == 0) 
+        if (m_Summary == null) 
             return root;
 
         int itemId = 1;
-        foreach (var prefabResult in m_Summary.PrefabResults)
-        {
-            string prefabName = Path.GetFileNameWithoutExtension(prefabResult.PrefabPath);
-            var prefabItem = new MetadataTreeViewItem(itemId++, $"{prefabName} ({prefabResult.Items.Count}涓棶棰�)", prefabResult);
 
-            foreach (var adapterItem in prefabResult.Items)
+        bool showWithIssue = m_FilterMode == ScanResultFilterMode.鍏ㄩ儴 || 
+                            m_FilterMode == ScanResultFilterMode.浠呮樉绀烘湁闂;
+        bool showWithoutIssue = m_FilterMode == ScanResultFilterMode.鍏ㄩ儴 || 
+                               m_FilterMode == ScanResultFilterMode.浠呮樉绀烘棤闂;
+
+        if (showWithIssue)
+        {
+            foreach (var prefabResult in m_Summary.PrefabResults)
             {
-                var adapterTreeItem = new MetadataTreeViewItem(itemId++, adapterItem.GetDisplayName(), adapterItem);
-                prefabItem.AddChild(adapterTreeItem);
+                string prefabName = Path.GetFileNameWithoutExtension(prefabResult.PrefabPath);
+                
+                int issueCount = 0;
+                foreach (var item in prefabResult.Items)
+                {
+                    if (item.MissingLanguages != null && item.MissingLanguages.Count > 0)
+                        issueCount++;
+                }
+
+                var prefabItem = new MetadataTreeViewItem(itemId++, $"{prefabName} ({issueCount}涓棶棰�)", prefabResult);
+
+                foreach (var adapterItem in prefabResult.Items)
+                {
+                    string displayName;
+                    if (adapterItem.MissingLanguages.Count > 0)
+                        displayName = adapterItem.GetDisplayName();
+                    else
+                        displayName = $"{adapterItem.GameObjectPath} (閰嶇疆瀹屾暣)";
+                    var adapterTreeItem = new MetadataTreeViewItem(itemId++, displayName, adapterItem);
+                    prefabItem.AddChild(adapterTreeItem);
+                }
+                root.AddChild(prefabItem);
             }
-            root.AddChild(prefabItem);
         }
 
-        // 銆愬叧閿慨澶� 2銆戯細Unity 瀹樻柟瑙勮寖瑕佹眰锛屾墜鍔ㄤ娇鐢� AddChild 鏋勫缓鏍戜箣鍚庯紝蹇呴』璋冪敤姝ゆ柟娉曞埛鏂版繁搴﹀拰灞傜骇鍏崇郴
+        if (showWithoutIssue)
+        {
+            foreach (var prefabResult in m_Summary.PrefabResultsWithoutIssue)
+            {
+                string prefabName = Path.GetFileNameWithoutExtension(prefabResult.PrefabPath);
+                var prefabItem = new MetadataTreeViewItem(itemId++, $"{prefabName} (鏃犻棶棰�)", prefabResult);
+
+                foreach (var adapterItem in prefabResult.Items)
+                {
+                    var displayName = $"{adapterItem.GameObjectPath} (閰嶇疆瀹屾暣)";
+                    var adapterTreeItem = new MetadataTreeViewItem(itemId++, displayName, adapterItem);
+                    prefabItem.AddChild(adapterTreeItem);
+                }
+                root.AddChild(prefabItem);
+            }
+        }
+
         SetupDepthsFromParentsAndChildren(root);
 
         return root;
@@ -117,7 +178,12 @@
         if (item != null && item.Metadata is PrefabScanResult)
             GUI.Label(args.rowRect, item.displayName, EditorStyles.boldLabel);
         else if (item != null && item.Metadata is ScanResultItem adapterItem)
-            GUI.Label(args.rowRect, $"{adapterItem.GameObjectPath} (缂哄皯: {string.Join(", ", adapterItem.MissingLanguages)})");
+        {
+            if (adapterItem.MissingLanguages.Count > 0)
+                GUI.Label(args.rowRect, $"{adapterItem.GameObjectPath} (缂哄皯: {string.Join(", ", adapterItem.MissingLanguages)})");
+            else
+                GUI.Label(args.rowRect, item.displayName);
+        }
         else
             base.RowGUI(args);
     }
@@ -173,8 +239,8 @@
     
     private ScanResultTreeView m_TreeView;
     private TreeViewState m_TreeViewState;
+    private ScanResultFilterMode m_ResultFilterMode = ScanResultFilterMode.鍏ㄩ儴;
 
-    // 鎵归噺鎿嶄綔鐨刄I鐘舵��
     private int m_SourceLangIndex = 0;
     private int m_TargetLangIndex = 0;
     private bool m_OverwriteExisting = false;
@@ -279,7 +345,20 @@
 
             if (m_ScanResult != null)
             {
-                EditorGUILayout.LabelField($"棰勫埗浣撴�绘暟: {m_ScanResult.TotalPrefabsScanned} | Adapter鎬绘暟: {m_ScanResult.TotalAdaptersFound} | 缂哄け閰嶇疆: {m_ScanResult.AdaptersWithMissingConfig}");
+                EditorGUILayout.LabelField($"棰勫埗浣撴�绘暟: {m_ScanResult.TotalPrefabsScanned} | 鏈堿dapter鐨勯鍒朵綋: {m_ScanResult.PrefabsWithIssueCount + m_ScanResult.PrefabsWithoutIssueCount} | Adapter鎬绘暟: {m_ScanResult.TotalAdaptersFound} | 缂哄け閰嶇疆: {m_ScanResult.AdaptersWithMissingConfig} | 鏈夐棶棰橀鍒朵綋: {m_ScanResult.PrefabsWithIssueCount} | 鏃犻棶棰橀鍒朵綋: {m_ScanResult.PrefabsWithoutIssueCount}");
+                EditorGUILayout.Space(5f);
+
+                using (new EditorGUILayout.HorizontalScope())
+                {
+                    EditorGUILayout.LabelField("鏄剧ず妯″紡:", GUILayout.Width(60f));
+                    EditorGUI.BeginChangeCheck();
+                    m_ResultFilterMode = (ScanResultFilterMode)EditorGUILayout.EnumPopup(m_ResultFilterMode);
+                    if (EditorGUI.EndChangeCheck() && m_TreeView != null)
+                    {
+                        m_TreeViewState ??= new TreeViewState();
+                        m_TreeView = new ScanResultTreeView(m_TreeViewState, m_ScanResult, m_ResultFilterMode);
+                    }
+                }
                 EditorGUILayout.Space(5f);
 
                 if (m_TreeView != null)
@@ -304,7 +383,6 @@
         }
     }
 
-    // ======================== 鏂板锛氭壒閲忔搷浣滃姛鑳� ========================
     private void DrawBatchOperations()
     {
         using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox))
@@ -370,7 +448,6 @@
                 string path = AssetDatabase.GUIDToAssetPath(prefabGuids[i]);
                 EditorUtility.DisplayProgressBar("鎵归噺澶嶅埗閰嶇疆", $"澶勭悊涓� ({i + 1}/{prefabGuids.Length}): {path}", (float)i / prefabGuids.Length);
 
-                // 銆愭牳蹇冧慨鏀广�戯細鐩存帴鍔犺浇璧勪骇鍐呭瓨锛屼笉浣跨敤 LoadPrefabContents 杩涜瀹炰緥鍖�
                 GameObject prefabAsset = AssetDatabase.LoadAssetAtPath<GameObject>(path);
                 bool isModified = false;
 
@@ -380,7 +457,6 @@
                     {
                         if (m_OverwriteExisting || !adapter.HasConfig(targetLang))
                         {
-                            // 蹇呴』鏍囪瀵硅薄涓鸿剰锛屽惁鍒� Unity 涓嶄細鎶婅祫浜х殑淇敼瀛樼洏
                             Undo.RecordObject(adapter, "Batch Copy Language Config");
                             
                             var clonedConfig = adapter.GetConfig(sourceLang).Clone();
@@ -402,7 +478,6 @@
         finally
         {
             EditorUtility.ClearProgressBar();
-            // 缁熶竴淇濆瓨鎵�鏈夋爣璁颁负 Dirty 鐨勮祫浜т慨鏀�
             AssetDatabase.SaveAssets();
             
             PerformScan();
@@ -412,8 +487,6 @@
                 "纭");
         }
     }
-
-    // ======================== 鏍稿績鎵弿閫昏緫 ========================
 
     private void StartScan()
     {
@@ -460,7 +533,7 @@
         }
 
         m_TreeViewState ??= new TreeViewState();
-        m_TreeView = new ScanResultTreeView(m_TreeViewState, m_ScanResult);
+        m_TreeView = new ScanResultTreeView(m_TreeViewState, m_ScanResult, m_ResultFilterMode);
         m_ScanStatus = $"鎵弿瀹屾垚! 鍙戠幇 {m_ScanResult.AdaptersWithMissingConfig} 涓己澶遍厤缃�";
         Repaint();
     }
@@ -471,8 +544,12 @@
         if (prefab == null) return;
 
         var adapters = prefab.GetComponentsInChildren<TextLanguageAdapter>(true);
-        
+        if (adapters.Length == 0) return;
+
         m_ScanResult.TotalAdaptersFound += adapters.Length;
+
+        bool hasIssue = false;
+        List<ScanResultItem> allItems = new List<ScanResultItem>();
 
         foreach (var adapter in adapters)
         {
@@ -483,15 +560,30 @@
                 if (!adapter.HasConfig(langId)) missing.Add(langId);
             }
 
+            var item = new ScanResultItem(path, GetGameObjectPath(adapter.gameObject, prefab), adapter.TargetTextType, guid)
+            {
+                MissingLanguages = missing
+            };
+            
+            allItems.Add(item);
+
             if (missing.Count > 0)
             {
-                var item = new ScanResultItem(path, GetGameObjectPath(adapter.gameObject, prefab), adapter.TargetTextType, guid)
-                {
-                    MissingLanguages = missing
-                };
+                hasIssue = true;
+            }
+        }
+
+        if (hasIssue)
+        {
+            foreach (var item in allItems)
+            {
                 m_ScanResult.AddResult(item);
             }
         }
+        else
+        {
+            m_ScanResult.AddPrefabWithoutIssue(path, guid, allItems);
+        }
     }
 
     private string GetGameObjectPath(GameObject go, GameObject root)

--
Gitblit v1.8.0