killapp/Assets/Scripts/UI/Components/SelectCountry.cs
“虞渠成” 8f20291be6 feat: 添加国家/地区选择与展示功能
1. 新增多语言国家配置项,完善语言包
2. 实现国家选择弹窗组件,支持搜索、滚动定位和返回逻辑
3. 个人页面添加国家选择入口与展示逻辑
4. 排行榜列表新增国家名称展示
5. 优化数据变更检测逻辑,过滤无意义空值
6. 调整部分UI布局适配新增元素
2026-07-13 14:17:12 +08:00

251 lines
7.5 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using Kill.Managers;
using Kill.Network;
using Kill.UI;
public class SelectCountry : MonoBehaviour
{
public GameObject countryPrefab;
public Transform countryParent;
public Button submitButton;
public Button backButton;
public InputField searchInput;
public ScrollRect scrollRect;
LanguageManager.Country selectCountry;
private bool _hasSelection;
private string _originalCode;
/// <summary>
/// 未选中 选中
/// </summary>
public Color[] textColors;
private List<SelectCountryItem> _items = new List<SelectCountryItem>();
/// <summary>
/// 确认回调code 为选中的国家代码null 表示返回/取消
/// </summary>
public System.Action<string> onConfirm;
/// <summary>
/// 初始化
/// </summary>
/// <param name="code">当前选中的国家代码</param>
/// <param name="showBackButton">是否显示返回按钮(主页不显示)</param>
public void Init(string code, bool showBackButton = true)
{
_originalCode = code;
countryPrefab.SetActive(false);
// 清理旧 item
foreach (var item in _items)
Destroy(item.gameObject);
_items.Clear();
_hasSelection = false;
// 返回按钮
if (backButton != null)
{
backButton.gameObject.SetActive(showBackButton);
backButton.onClick.RemoveAllListeners();
backButton.onClick.AddListener(OnBack);
if (showBackButton)
UIManager.Instance.RegisterBackAction(OnBack);
}
if (LanguageManager.Instance == null || LanguageManager.Instance.countryList == null)
return;
foreach (var country in LanguageManager.Instance.countryList)
{
var go = Instantiate(countryPrefab, countryParent);
var item = go.AddComponent<SelectCountryItem>();
item.Init(this, country);
_items.Add(item);
item.gameObject.SetActive(true);
}
// 绑定搜索
if (searchInput != null)
{
searchInput.onValueChanged.RemoveAllListeners();
searchInput.onValueChanged.AddListener(OnSearchChanged);
}
// 设置默认选中code 为空时不选中)
if (!string.IsNullOrEmpty(code))
SelectCountryByCode(code);
// 自动滚动到选中项
ScrollToSelected();
UpdateSubmitButton();
}
/// <summary>
/// 返回按钮点击
/// </summary>
private void OnBack()
{
onConfirm?.Invoke(null);
}
/// <summary>
/// 滚动到当前选中的国家项(居中显示)
/// </summary>
private void ScrollToSelected()
{
if (scrollRect == null || !_hasSelection) return;
int index = _items.FindIndex(i => i.Country.code == selectCountry.code);
if (index < 0) return;
Canvas.ForceUpdateCanvases();
var itemRect = _items[index].GetComponent<RectTransform>();
float contentHeight = scrollRect.content.rect.height;
float viewportHeight = scrollRect.viewport.rect.height;
if (contentHeight <= viewportHeight) return;
// 计算目标滚动位置:使选中项居中
float maxScrollY = contentHeight - viewportHeight;
float itemCenterY = Mathf.Abs(itemRect.anchoredPosition.y) + itemRect.rect.height / 2;
float targetScrollY = itemCenterY - viewportHeight / 2;
targetScrollY = Mathf.Clamp(targetScrollY, 0, maxScrollY);
scrollRect.verticalNormalizedPosition = 1f - targetScrollY / maxScrollY;
}
private void OnSearchChanged(string keyword)
{
foreach (var item in _items)
{
item.gameObject.SetActive(item.MatchKeyword(keyword));
}
}
public void SelectCountryByCode(string code)
{
_hasSelection = !string.IsNullOrEmpty(code);
foreach (var item in _items)
{
bool isSelected = _hasSelection && item.Country.code == code;
item.SetSelected(isSelected);
if (isSelected)
selectCountry = item.Country;
}
UpdateSubmitButton();
}
public string GetSelectedCode()
{
return _hasSelection ? selectCountry.code : null;
}
private void UpdateSubmitButton()
{
if (submitButton != null)
{
submitButton.gameObject.SetActive(_hasSelection);
submitButton.onClick.RemoveAllListeners();
submitButton.onClick.AddListener(OnSubmit);
}
}
private async void OnSubmit()
{
if (!_hasSelection) return;
await UpdateCountryCodeAsync(selectCountry.code);
}
/// <summary>
/// 更新用户国家代码
/// </summary>
private async System.Threading.Tasks.Task UpdateCountryCodeAsync(string code)
{
try
{
var requestData = new CountryCodeRequest
{
country_code = code
};
var response = await NetworkCtrl.Instance.Put<NoDataResponse>("/api/v1/user/country-code", requestData);
ResponseCodeHandler.HandleResponse(response,
onSuccess: (data) =>
{
if (DataManager.Instance.userInfo != null)
DataManager.Instance.userInfo.country_code = code;
onConfirm?.Invoke(code);
},
onError: null
);
}
catch (Exception ex)
{
Debug.LogError($"更新国家代码失败: {ex.Message}");
}
}
}
internal class SelectCountryItem : MonoBehaviour
{
public LanguageManager.Country Country { get; private set; }
private SelectCountry _parent;
private Text _nameText;
private GameObject _selectGo;
private Color _selectedColor;
private Color _unselectedColor;
public void Init(SelectCountry parent, LanguageManager.Country country)
{
_parent = parent;
Country = country;
_nameText = transform.Find("name")?.GetComponent<Text>();
_selectGo = transform.Find("选中")?.gameObject;
_selectedColor = parent.textColors.Length > 0 ? parent.textColors[0] : Color.white;
_unselectedColor = parent.textColors.Length > 1 ? parent.textColors[1] : Color.gray;
// 显示国家名称(根据当前语言)
bool isChinese = LanguageManager.Instance != null && LanguageManager.Instance.languageType == LanguageManager.LanguageType.Chinese;
if (_nameText != null)
_nameText.text = isChinese ? country.name_zh : country.name_en;
// 点击事件
var btn = GetComponent<Button>();
if (btn == null)
btn = gameObject.AddComponent<Button>();
btn.onClick.AddListener(OnClick);
}
public void SetSelected(bool selected)
{
if (_nameText != null)
_nameText.color = selected ? _selectedColor : _unselectedColor;
if (_selectGo != null)
_selectGo.SetActive(selected);
}
public bool MatchKeyword(string keyword)
{
if (string.IsNullOrEmpty(keyword))
return true;
var k = keyword.ToLower();
return Country.name_zh.ToLower().Contains(k)
|| Country.name_en.ToLower().Contains(k)
|| Country.code.ToLower().Contains(k);
}
private void OnClick()
{
_parent.SelectCountryByCode(Country.code);
}
}