2026-06-08 08:55:10 +08:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections;
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.IO;
|
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
using Kill.Managers;
|
|
|
|
|
|
using Kill.Network;
|
|
|
|
|
|
using Kill.UI.Components;
|
|
|
|
|
|
using Unity.VisualScripting;
|
|
|
|
|
|
using UnityEngine;
|
2026-06-10 15:04:14 +08:00
|
|
|
|
using UnityEngine.Networking;
|
2026-06-08 08:55:10 +08:00
|
|
|
|
using UnityEngine.UI;
|
|
|
|
|
|
namespace Kill.UI.Pages
|
|
|
|
|
|
{
|
|
|
|
|
|
public class FeedbackPage : MonoBehaviour
|
|
|
|
|
|
{
|
|
|
|
|
|
public enum FeedbackType
|
|
|
|
|
|
{
|
|
|
|
|
|
TechnicalIssue,
|
|
|
|
|
|
ProductSuggestion,
|
|
|
|
|
|
UseConsultation,
|
|
|
|
|
|
OtherIssues
|
|
|
|
|
|
}
|
|
|
|
|
|
public Button[] issueTypeButtons;
|
|
|
|
|
|
public InputField issueInput;
|
|
|
|
|
|
public Text textLimit;
|
|
|
|
|
|
public Text screenShotLimit;
|
|
|
|
|
|
FeedbackType feedbackType;
|
|
|
|
|
|
Action callback;
|
|
|
|
|
|
public Button back;
|
|
|
|
|
|
public void Init(Action callback)
|
|
|
|
|
|
{
|
|
|
|
|
|
feedbackType = FeedbackType.OtherIssues;
|
|
|
|
|
|
ChooseType(0);
|
|
|
|
|
|
issueInput.text = "";
|
|
|
|
|
|
this.callback=callback;
|
|
|
|
|
|
back.onClick.RemoveAllListeners();
|
|
|
|
|
|
back.onClick.AddListener(Back);
|
|
|
|
|
|
UIManager.Instance.RegisterBackAction(Back);
|
|
|
|
|
|
}
|
|
|
|
|
|
public void Back()
|
|
|
|
|
|
{
|
|
|
|
|
|
callback?.Invoke();
|
|
|
|
|
|
Destroy(gameObject);
|
|
|
|
|
|
}
|
|
|
|
|
|
private Texture2D selectedImage;
|
|
|
|
|
|
List<Texture2D> selectedImageList = new List<Texture2D>();
|
|
|
|
|
|
public void ChooseType(int index)
|
|
|
|
|
|
{
|
|
|
|
|
|
feedbackType = (FeedbackType)index;
|
|
|
|
|
|
for (int i = 0; i < issueTypeButtons.Length; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (i == index)
|
|
|
|
|
|
{
|
|
|
|
|
|
issueTypeButtons[i].interactable = false;
|
|
|
|
|
|
issueTypeButtons[i].transform.Find("选中").gameObject.SetActive(true);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
issueTypeButtons[i].interactable = true;
|
|
|
|
|
|
issueTypeButtons[i].transform.Find("选中").gameObject.SetActive(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
public void OnTextValueChanged(String text)
|
|
|
|
|
|
{
|
|
|
|
|
|
textLimit.text=text.Length+"/300";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void SelectPhotoFromGallery()
|
|
|
|
|
|
{
|
|
|
|
|
|
#if UNITY_EDITOR
|
|
|
|
|
|
// 在编辑器中测试时,加载一个默认图片
|
|
|
|
|
|
string testImagePath = UnityEditor.EditorUtility.OpenFilePanel("Select Image", "", "jpg,png,jpeg");
|
|
|
|
|
|
if (!string.IsNullOrEmpty(testImagePath))
|
|
|
|
|
|
{
|
|
|
|
|
|
byte[] fileData = File.ReadAllBytes(testImagePath);
|
|
|
|
|
|
selectedImage = new Texture2D(2, 2);
|
|
|
|
|
|
selectedImage.LoadImage(fileData);
|
|
|
|
|
|
ShowSelectImage();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
// 使用NativeGallery插件从相册选择图片
|
|
|
|
|
|
bool permission = NativeGallery.CheckPermission(NativeGallery.PermissionType.Read, NativeGallery.MediaType.Image);
|
|
|
|
|
|
if (permission)
|
|
|
|
|
|
{
|
|
|
|
|
|
PickImage();
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
NativeGallery.RequestPermissionAsync((perm) =>
|
|
|
|
|
|
{
|
|
|
|
|
|
if (perm == NativeGallery.Permission.Granted)
|
|
|
|
|
|
{
|
|
|
|
|
|
PickImage();
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogWarning("没有获得相册访问权限");
|
|
|
|
|
|
}
|
|
|
|
|
|
}, NativeGallery.PermissionType.Read, NativeGallery.MediaType.Image);
|
|
|
|
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
|
|
private void PickImage()
|
|
|
|
|
|
{
|
|
|
|
|
|
NativeGallery.GetImageFromGallery((path) =>
|
|
|
|
|
|
{
|
|
|
|
|
|
if (path != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 加载选中的图片,并确保纹理是可读的
|
|
|
|
|
|
selectedImage = NativeGallery.LoadImageAtPath(path, -1, false); // 设置 markTextureNonReadable 为 false
|
|
|
|
|
|
if (selectedImage != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
ShowSelectImage();
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError("无法加载选定的图片");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}, "");
|
|
|
|
|
|
}
|
|
|
|
|
|
public GameObject selectImagePrefab;
|
|
|
|
|
|
public Transform imageParent;
|
|
|
|
|
|
public List<GameObject> selectImageObjs;
|
|
|
|
|
|
public GameObject addImageButton;
|
|
|
|
|
|
public void ShowSelectImage()
|
|
|
|
|
|
{
|
|
|
|
|
|
GameObject i=Instantiate(selectImagePrefab,imageParent);
|
|
|
|
|
|
i.GetComponent<RawImage>().texture = selectedImage;
|
|
|
|
|
|
i.GetComponentInChildren<Button>().onClick.AddListener(()=>{DelImageFromList(i);});
|
|
|
|
|
|
i.SetActive(true);
|
|
|
|
|
|
selectImageObjs.Add(i);
|
|
|
|
|
|
selectedImageList.Add(selectedImage);
|
|
|
|
|
|
if(selectedImageList.Count>=3)
|
|
|
|
|
|
{
|
|
|
|
|
|
addImageButton.SetActive(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
screenShotLimit.text=$"({selectImageObjs.Count}/3)";
|
|
|
|
|
|
}
|
|
|
|
|
|
public void DelImageFromList(GameObject g)
|
|
|
|
|
|
{
|
|
|
|
|
|
int index=selectImageObjs.IndexOf(g);
|
|
|
|
|
|
Destroy(selectImageObjs[index]);
|
|
|
|
|
|
selectImageObjs.RemoveAt(index);
|
|
|
|
|
|
selectedImageList.RemoveAt(index);
|
|
|
|
|
|
if(selectedImageList.Count<3)
|
|
|
|
|
|
{
|
|
|
|
|
|
addImageButton.SetActive(true);
|
|
|
|
|
|
}
|
|
|
|
|
|
screenShotLimit.text=$"({selectImageObjs.Count}/3)";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 提交反馈(包含文本和图片)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public async void SubmitFeedback()
|
|
|
|
|
|
{
|
|
|
|
|
|
LoadingUI.Show();
|
|
|
|
|
|
|
|
|
|
|
|
// 准备表单数据
|
2026-06-10 15:04:14 +08:00
|
|
|
|
List<IMultipartFormSection> formData = new List<IMultipartFormSection>();
|
2026-06-08 08:55:10 +08:00
|
|
|
|
|
|
|
|
|
|
// 添加文本字段
|
2026-06-10 15:04:14 +08:00
|
|
|
|
formData.Add(new MultipartFormDataSection("device_ble_mac",DataManager.Instance.selectedDevice.ble_mac));
|
|
|
|
|
|
formData.Add(new MultipartFormDataSection("phone_model",SystemInfo.deviceModel));
|
|
|
|
|
|
formData.Add(new MultipartFormDataSection("os_version",SystemInfo.operatingSystem));
|
|
|
|
|
|
formData.Add(new MultipartFormDataSection("app_version",Application.version));
|
|
|
|
|
|
formData.Add(new MultipartFormDataSection("description",issueInput.text));
|
|
|
|
|
|
formData.Add(new MultipartFormDataSection("category",feedbackType.ToString()));
|
|
|
|
|
|
Debug.Log($"信息:{SystemInfo.deviceModel},{SystemInfo.operatingSystem},{Application.version},{feedbackType.ToString()}");
|
2026-06-08 08:55:10 +08:00
|
|
|
|
// 添加图片文件(如果有)
|
|
|
|
|
|
if (selectedImageList != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
for (int i = 0; i < selectedImageList.Count; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
byte[] imageBytes = selectedImageList[i].EncodeToJPG();
|
2026-06-10 15:04:14 +08:00
|
|
|
|
formData.Add(new MultipartFormFileSection("images", imageBytes, $"image_{i}.jpg", "image/png"));
|
2026-06-08 08:55:10 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 发送请求
|
2026-06-10 15:04:14 +08:00
|
|
|
|
var response = await NetworkCtrl.Instance.PostFormData<FeedbackResponse>("/api/v1/feedback/submit", formData);
|
2026-06-08 08:55:10 +08:00
|
|
|
|
|
|
|
|
|
|
ResponseCodeHandler.HandleResponse(response,
|
|
|
|
|
|
onSuccess: (data) =>
|
|
|
|
|
|
{
|
2026-06-10 15:04:14 +08:00
|
|
|
|
Debug.Log(response.Data.message);
|
2026-06-08 08:55:10 +08:00
|
|
|
|
ToastUI.Show("100280");
|
|
|
|
|
|
Back();
|
|
|
|
|
|
},
|
|
|
|
|
|
onError: (code, msg) =>
|
|
|
|
|
|
{
|
|
|
|
|
|
ToastUI.ShowText(code.ToString());
|
|
|
|
|
|
}
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
LoadingUI.Hide();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 响应数据结构
|
|
|
|
|
|
public class FeedbackResponse
|
|
|
|
|
|
{
|
|
|
|
|
|
public string code;
|
|
|
|
|
|
public string message;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|