killapp/Assets/Scripts/UI/Pages/HomePage/HomePageDeviceItem.cs
“虞渠成” f0a6f0f764 feat: 完成设备置顶、重命名、分享功能迭代
本次提交包含以下核心更新:
1. 新增设备置顶功能,支持将设备在列表中优先展示
2. 实现设备重命名、分享、取消共享功能
3. 优化首页设备列表滑动交互,重构设备项UI菜单逻辑
4. 新增is_top设备字段并完善设备列表排序逻辑
5. 调整部分UI布局与服务器接口地址
6. 补充多语言文案与新增提示文本
7. 修复绑定设备错误提示适配逻辑
2026-08-19 17:08:15 +08:00

245 lines
9.0 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.Collections.Generic;
using DG.Tweening;
using Kill.Managers;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
namespace Kill.UI.Pages
{
/// <summary>
/// 首页设备列表项 - 显示单个设备信息
/// 支持左滑手势:左滑时内容元素向左滑出,露出右侧菜单,
/// 滑动超过菜单宽度1/3松手则完整展开否则缩回
/// </summary>
public class HomePageDeviceItem : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{
public Text deviceNameText;
/// <summary>
/// 置顶标识
/// </summary>
public GameObject topIcon;
public GameObject selectedIndicator;
/// <summary>菜单子物体锚点位于item最右侧内容左移后露出</summary>
public RectTransform menuRect;
// 当前设备信息
public DeviceInfo deviceInfo;
/// <summary>
/// 菜单按钮数组 0重命名 1分享 2删除 3置顶 4取消置顶
/// 最多显示3个按钮 拥有的设备显示分享 被分享的设备显示删除
/// 根据置顶状态显示置顶/取消置顶按钮
/// </summary>
public Button[] menuButtons;
/// <summary>当前展开菜单的item全局仅允许一个菜单展开</summary>
private static HomePageDeviceItem openItem;
private float menuWidth;
private float currentOffset;
private float dragStartOffset;
private bool isMenuOpen;
private bool isDragging;
private Vector3 menuInitPos;
private Tweener tweener;
private Button button;
private bool menuListenersBound;
/// <summary>除菜单外的内容子物体</summary>
private readonly List<RectTransform> contentRects = new List<RectTransform>();
/// <summary>各内容子物体的初始位置</summary>
private readonly List<Vector2> contentInitPositions = new List<Vector2>();
void Awake()
{
button = GetComponent<Button>();
// 点击item主体关闭其他item展开的菜单同时收起自己的
if (button != null)
button.onClick.AddListener(OnItemClicked);
InitSwipe();
}
/// <summary>
/// item主体点击全局仅保留一个菜单点击任意item时关闭其他item的展开菜单
/// </summary>
private void OnItemClicked()
{
if (openItem != null && openItem != this)
openItem.CloseMenu();
CloseMenu();
}
void OnDisable()
{
// 防止拖拽中被禁用且未恢复的情况
if (button != null) button.interactable = true;
}
void OnDestroy()
{
// 销毁时释放全局展开引用
if (openItem == this)
openItem = null;
}
public void InitDeviceItem(DeviceInfo device, bool isOwner)
{
deviceInfo = device;
deviceNameText.text = device.device_name;
selectedIndicator.SetActive(false);
if (topIcon != null) topIcon.SetActive(device.is_top);
SetupMenuButtons(isOwner);
BindMenuButtonClose();
InitSwipe();
}
/// <summary>
/// 根据设备归属和置顶状态显示/隐藏菜单按钮
/// 0重命名 1分享 2删除 3置顶 4取消置顶
/// 拥有者:重命名+分享+置顶/取消置顶;被分享者:删除+置顶/取消置顶
/// </summary>
private void SetupMenuButtons(bool isOwner)
{
if (menuButtons == null || menuButtons.Length < 5) return;
menuButtons[0].gameObject.SetActive(true); // 重命名仅拥有者
menuButtons[1].gameObject.SetActive(isOwner); // 分享仅拥有者
menuButtons[2].gameObject.SetActive(!isOwner); // 删除(移除共享)仅被分享者
menuButtons[3].gameObject.SetActive(!deviceInfo.is_top); // 置顶:未置顶时显示
menuButtons[4].gameObject.SetActive(deviceInfo.is_top); // 取消置顶:已置顶时显示
}
/// <summary>
/// 点击菜单内任意按钮时自动收起菜单
/// </summary>
private void BindMenuButtonClose()
{
if (menuListenersBound || menuRect == null) return;
menuListenersBound = true;
foreach (var b in menuRect.GetComponentsInChildren<Button>(true))
b.onClick.AddListener(CloseMenu);
}
public void SetSelectedState(bool isSelected)
{
// 设置选中状态
selectedIndicator.SetActive(isSelected);
}
/// <summary>
/// 初始化滑动数据:收集内容子物体、记录初始位置,重置为收起状态
/// </summary>
private void InitSwipe()
{
tweener?.Kill();
if (menuRect == null) return;
// 先复位已记录的内容再重新收集兼容item复用
ApplyOffset(0);
contentRects.Clear();
contentInitPositions.Clear();
foreach (Transform child in transform)
{
if (child == menuRect) continue;
RectTransform rt = child as RectTransform;
if (rt == null) continue;
contentRects.Add(rt);
contentInitPositions.Add(rt.anchoredPosition);
}
menuWidth = menuRect.sizeDelta.x;
menuInitPos = menuRect.anchoredPosition3D;
isMenuOpen = false;
currentOffset = 0;
ApplyOffset(0);
}
public void OnBeginDrag(PointerEventData eventData)
{
if (menuRect == null || menuWidth <= 0) return;
// 拖拽滑动时禁用按钮点击,避免与菜单滑动同时触发
if (button != null) button.interactable = false;
// 其他item的菜单展开时先收起保证全局只有一个菜单
if (openItem != null && openItem != this)
openItem.CloseMenu();
isDragging = true;
currentOffset = isMenuOpen ? -menuWidth : 0;
dragStartOffset = currentOffset;
ApplyOffset(currentOffset);
}
public void OnDrag(PointerEventData eventData)
{
if (!isDragging) return;
// 累加位移并限制在 [-菜单宽度, 0],只允许左滑露出菜单
currentOffset += eventData.delta.x;
currentOffset = Mathf.Clamp(currentOffset, -menuWidth, 0);
ApplyOffset(currentOffset);
}
public void OnEndDrag(PointerEventData eventData)
{
if (!isDragging) return;
isDragging = false;
// 恢复按钮点击
if (button != null) button.interactable = true;
// 右滑超过菜单宽度的1/5则直接缩回方便关闭已展开的菜单
float dragDistance = currentOffset - dragStartOffset;
if (dragDistance > menuWidth / 5f)
{
AnimateTo(0);
return;
}
// 左滑展示超过菜单宽度的1/3则完整滑出否则缩回
float target = currentOffset < -menuWidth / 3f ? -menuWidth : 0;
AnimateTo(target);
}
/// <summary>
/// 收起菜单(供菜单按钮点击等外部调用)
/// </summary>
public void CloseMenu()
{
if (!isMenuOpen) return;
AnimateTo(0);
}
/// <summary>
/// 将内容子物体和菜单一起平移到指定偏移(内容左移、菜单从屏幕外滑入露出)
/// </summary>
private void ApplyOffset(float offset)
{
for (int i = 0; i < contentRects.Count; i++)
{
Vector2 pos = contentInitPositions[i];
pos.x += offset;
contentRects[i].anchoredPosition = pos;
}
Vector3 menuPos = menuInitPos;
menuPos.x += offset;
menuRect.anchoredPosition3D = menuPos;
}
/// <summary>
/// 平滑动画到目标偏移
/// </summary>
private void AnimateTo(float target)
{
tweener?.Kill();
bool open = target < 0;
if (open)
{
// 已有其他item的菜单展开时先收起保证全局只有一个菜单
if (openItem != null && openItem != this)
openItem.CloseMenu();
openItem = this;
}
else if (openItem == this)
{
openItem = null;
}
isMenuOpen = open;
tweener = DOTween.To(() => currentOffset, v =>
{
currentOffset = v;
ApplyOffset(v);
}, target, 0.25f).SetEase(Ease.OutCubic);
}
}
}