diff --git a/Unity/Assets/Scripts/TH1_UI/View/Outside/UIOutsideMenuView.cs b/Unity/Assets/Scripts/TH1_UI/View/Outside/UIOutsideMenuView.cs index 02093a60d..83e97d314 100644 --- a/Unity/Assets/Scripts/TH1_UI/View/Outside/UIOutsideMenuView.cs +++ b/Unity/Assets/Scripts/TH1_UI/View/Outside/UIOutsideMenuView.cs @@ -142,7 +142,7 @@ namespace TH1_UI.View.Outside StoryButton.onClick.RemoveAllListeners(); StoryButton.onClick.AddListener(OnStoryClicked); BindTransReportButton(); - HideQuestionnaireButton(); + BindQuestionnaireButton(); SettingButton.onClick.RemoveAllListeners(); SettingButton.onClick.AddListener(OnSettingClicked); @@ -282,6 +282,7 @@ namespace TH1_UI.View.Outside } if (QuestionnaireButton == null) return; + QuestionnaireButton.gameObject.SetActive(true); RestoreButtonTargetAlpha(QuestionnaireButton); SetQuestionnaireButtonText(); QuestionnaireButton.onClick.RemoveAllListeners(); diff --git a/Unity/Assets/Scripts/TH1_UI/View/Outside/UIOutsideQuestionnaireOptionMono.cs b/Unity/Assets/Scripts/TH1_UI/View/Outside/UIOutsideQuestionnaireOptionMono.cs index b21cffcb2..eb4fc4751 100644 --- a/Unity/Assets/Scripts/TH1_UI/View/Outside/UIOutsideQuestionnaireOptionMono.cs +++ b/Unity/Assets/Scripts/TH1_UI/View/Outside/UIOutsideQuestionnaireOptionMono.cs @@ -39,6 +39,14 @@ namespace TH1_UI.View.Outside Toggle?.SetIsOnWithoutNotify(value); } + public void SetInteractable(bool value) + { + if (Toggle != null) + { + Toggle.interactable = value; + } + } + private void OnToggleChanged(bool value) { _onValueChanged?.Invoke(this, value); diff --git a/Unity/Assets/Scripts/TH1_UI/View/Outside/UIOutsideQuestionnaireQuestionMono.cs b/Unity/Assets/Scripts/TH1_UI/View/Outside/UIOutsideQuestionnaireQuestionMono.cs index 7506b40a4..fe081ef9b 100644 --- a/Unity/Assets/Scripts/TH1_UI/View/Outside/UIOutsideQuestionnaireQuestionMono.cs +++ b/Unity/Assets/Scripts/TH1_UI/View/Outside/UIOutsideQuestionnaireQuestionMono.cs @@ -103,6 +103,19 @@ namespace TH1_UI.View.Outside _onAnswerChanged?.Invoke(); } + public void SetReadOnly(bool readOnly) + { + if (OpenInput != null) + { + OpenInput.interactable = !readOnly; + } + + foreach (var item in _optionItems) + { + item.SetInteractable(!readOnly); + } + } + public bool HasAnswer() { if (QuestionInfo == null) return false; diff --git a/Unity/Assets/Scripts/TH1_UI/View/Outside/UIOutsideQuestionnaireView.cs b/Unity/Assets/Scripts/TH1_UI/View/Outside/UIOutsideQuestionnaireView.cs index 5a218055e..a9e98c7f3 100644 --- a/Unity/Assets/Scripts/TH1_UI/View/Outside/UIOutsideQuestionnaireView.cs +++ b/Unity/Assets/Scripts/TH1_UI/View/Outside/UIOutsideQuestionnaireView.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Linq; using Logic.CrashSight; @@ -37,10 +38,25 @@ namespace TH1_UI.View.Outside public ViDelegateAssisstant.Dele OnBtnCloseClick; private readonly List _questionItems = new List(); + private readonly List _listItems = new List(); + private QuestionnaireDataAssets _dataAssets; private QuestionnaireInfo _questionnaireInfo; private QuestionnaireAnswerSheet _currentSheet; private bool _isUploading; + private bool _layoutBuilt; + private bool _showExpired; + + private Transform _sidebarContent; + private Transform _rightBody; + private Transform _rightFooter; + private TextMeshProUGUI _expiredToggleText; + private TextMeshProUGUI _rightHeaderTitle; + private TextMeshProUGUI _rightHeaderSubtitle; + private Button _recordsButton; + private Button _startButton; + private TextMeshProUGUI _recordsButtonText; + private TextMeshProUGUI _startButtonText; protected override void OnInit() { @@ -53,46 +69,25 @@ namespace TH1_UI.View.Outside EnsureLayout(); LoadAssetsIfNeeded(); - _questionnaireInfo = null; - if (_dataAssets != null) - { - if (!string.IsNullOrEmpty(evt.QuestionnaireId)) - { - _dataAssets.GetQuestionnaireInfo(evt.QuestionnaireId, out _questionnaireInfo); - } - - _questionnaireInfo ??= _dataAssets.GetDefaultQuestionnaire(); - } - - if (_questionnaireInfo == null) - { - Debug.LogError("[UIOutsideQuestionnaireView] QuestionnaireDataAssets is missing questionnaire data."); - SetStatus(string.Empty); - return; - } - - TitleText.text = ResolveText(_questionnaireInfo.Title); - DescriptionText.text = ResolveText(_questionnaireInfo.Description); - SubmitButtonText.text = ResolveText(_questionnaireInfo.SubmitButtonText); - RefillButtonText.text = ResolveText(_questionnaireInfo.ResubmitButtonText); - CloseButtonText.text = ResolveText(_questionnaireInfo.CloseButtonText); - - SubmitButton.onClick.RemoveAllListeners(); - SubmitButton.onClick.AddListener(OnSubmitClicked); - RefillButton.onClick.RemoveAllListeners(); - RefillButton.onClick.AddListener(OnRefillClicked); CloseButton.onClick.RemoveAllListeners(); CloseButton.onClick.AddListener(OnCloseClicked); SetUploading(false); - BuildQuestions(); - _currentSheet = QuestionnaireAnswerStore.Instance.GetAnswerSheet(_questionnaireInfo.QuestionnaireId); - ApplyPreviousSheet(_currentSheet); + var selected = ResolveInitialQuestionnaire(evt); + if (selected != null && selected.GetEffectiveStatus(DateTime.UtcNow) == QuestionnaireStatus.Expired) + { + _showExpired = true; + } - var hasSubmitted = _currentSheet != null; - RefillButton.gameObject.SetActive(hasSubmitted); - var submittedMessage = ResolveSubmittedMessage(_currentSheet); - SetStatus(hasSubmitted ? submittedMessage : string.Empty); + BuildQuestionnaireList(); + + if (selected == null) + { + ShowEmptyState("暂无可填写问卷"); + return; + } + + SelectQuestionnaire(selected); } public void OnCloseView() @@ -110,7 +105,8 @@ namespace TH1_UI.View.Outside return raw; } - public static TextMeshProUGUI CreateText(Transform parent, string objectName, string text, float fontSize, Color color, TextAlignmentOptions alignment) + public static TextMeshProUGUI CreateText(Transform parent, string objectName, string text, float fontSize, + Color color, TextAlignmentOptions alignment) { var go = new GameObject(objectName, typeof(RectTransform), typeof(TextMeshProUGUI)); go.transform.SetParent(parent, false); @@ -142,23 +138,425 @@ namespace TH1_UI.View.Outside } } - private void BuildQuestions() + private QuestionnaireInfo ResolveInitialQuestionnaire(ShowUIOutsideQuestionnaire evt) { - foreach (Transform child in QuestionListContent) + if (_dataAssets == null) { - Destroy(child.gameObject); + Debug.LogError("[UIOutsideQuestionnaireView] QuestionnaireDataAssets is missing."); + return null; } - _questionItems.Clear(); - if (_questionnaireInfo.Questions == null) return; - - for (var i = 0; i < _questionnaireInfo.Questions.Count; i++) + QuestionnaireInfo selected = null; + if (!string.IsNullOrEmpty(evt.QuestionnaireId)) { - var question = _questionnaireInfo.Questions[i]; + _dataAssets.GetQuestionnaireInfo(evt.QuestionnaireId, out selected); + } + + selected ??= _dataAssets.GetDefaultQuestionnaire(); + if (selected != null && selected.IsVisibleAt(DateTime.UtcNow)) return selected; + + return GetVisibleQuestionnaires().FirstOrDefault(); + } + + private List GetVisibleQuestionnaires() + { + return _dataAssets != null + ? _dataAssets.GetVisibleQuestionnaires(DateTime.UtcNow) + : new List(); + } + + private void SelectQuestionnaire(QuestionnaireInfo info) + { + if (info == null) + { + ShowEmptyState("暂无可填写问卷"); + return; + } + + _questionnaireInfo = info; + _currentSheet = QuestionnaireAnswerStore.Instance.GetLatestAnswerSheet(info.QuestionnaireId); + if (info.GetEffectiveStatus(DateTime.UtcNow) == QuestionnaireStatus.Expired && !_showExpired) + { + _showExpired = true; + BuildQuestionnaireList(); + } + + UpdateListSelection(); + ShowDetail(); + } + + private void BuildQuestionnaireList() + { + if (_sidebarContent == null) return; + + DestroyChildren(_sidebarContent); + _listItems.Clear(); + + var items = GetVisibleQuestionnaires(); + var featured = items.Where(item => item.GetEffectiveStatus(DateTime.UtcNow) == QuestionnaireStatus.Featured).ToList(); + var longTerm = items.Where(item => item.GetEffectiveStatus(DateTime.UtcNow) == QuestionnaireStatus.LongTerm).ToList(); + var expired = items.Where(item => item.GetEffectiveStatus(DateTime.UtcNow) == QuestionnaireStatus.Expired).ToList(); + + CreateSectionHeader(_sidebarContent, "当期问卷"); + CreateQuestionnaireListItems(featured, "暂无当期问卷"); + CreateDivider(_sidebarContent); + + CreateSectionHeader(_sidebarContent, "长期问卷"); + CreateQuestionnaireListItems(longTerm, "暂无长期问卷"); + CreateDivider(_sidebarContent); + + CreateExpiredHeader(expired.Count); + if (_showExpired) + { + CreateQuestionnaireListItems(expired, "暂无往期问卷"); + } + else if (expired.Count > 0) + { + var hint = CreateText(_sidebarContent, "ExpiredHiddenHint", $"{expired.Count} 份往期问卷已收起", 17f, + new Color(0.62f, 0.66f, 0.64f, 1f), TextAlignmentOptions.Left); + hint.enableWordWrapping = true; + } + + if (items.Count == 0) + { + var empty = CreateText(_sidebarContent, "EmptyList", "暂无可显示问卷", 18f, + new Color(0.74f, 0.76f, 0.72f, 1f), TextAlignmentOptions.Left); + empty.enableWordWrapping = true; + } + } + + private void CreateQuestionnaireListItems(List infos, string emptyText) + { + if (infos == null || infos.Count == 0) + { + var empty = CreateText(_sidebarContent, "Empty", emptyText, 17f, + new Color(0.62f, 0.66f, 0.64f, 1f), TextAlignmentOptions.Left); + empty.enableWordWrapping = true; + return; + } + + foreach (var info in infos) + { + CreateQuestionnaireListItem(info); + } + } + + private void CreateQuestionnaireListItem(QuestionnaireInfo info) + { + var row = new GameObject("QuestionnaireItem", typeof(RectTransform), typeof(Image), typeof(Button), + typeof(VerticalLayoutGroup), typeof(LayoutElement)); + row.transform.SetParent(_sidebarContent, false); + + var image = row.GetComponent(); + image.color = new Color(1f, 1f, 1f, 0.055f); + + var layout = row.GetComponent(); + layout.padding = new RectOffset(12, 12, 10, 10); + layout.spacing = 4f; + layout.childControlWidth = true; + layout.childControlHeight = true; + layout.childForceExpandWidth = true; + layout.childForceExpandHeight = false; + + var layoutElement = row.GetComponent(); + layoutElement.minHeight = 76f; + layoutElement.preferredHeight = 82f; + + var button = row.GetComponent