选中脚本Selection<T>

本文介绍了一种在Unity中优化子节点选择操作的方法,通过创建泛型类Selection来简化父节点与子节点间的事件绑定,提高代码复用性和效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  1. 项目中可能有很多选中的操作,需要对新选中的子节点做一些选中操作,对之前选中的子节点做从选中态到非选中态的转换操作
  2. 很多的操作是在子节点中建立一个委托,然后在父节点批量生成子节点的时候绑定事件,然后还要在父节点中建立一个子节点变量用来记录之前选中的是哪个,以便于后续的操作。
public class Parent : Monobehaviour
{
	Son previous = null;
	
	public void Init()
	{
		for(int i = 0; i < 10; i++)
		{
			Son s = new Son();
			s.SonSelected += sonSelected;
		}
	}
	
	public void sonSelected(Son s)
	{
		if(previous != null) previous.Deselected();
		s.Selected();
		previous = s;
	}
}

public class Son : Monobehaviour
{
	public event System.Action<Son> SonSelected;
	
	private void button_click
	{
		if(SonSelected != null) SonSelected(this);
	}
	
	public void Selected()
	{
		//todo
	}
	
	public void Deselected()
	{
		//todo
	}
}
  1. 现在我们可以把父类中持有的子节点和子类中声明的事件给提取出来,建立一个泛型类Selection
public delegate void SelectedHandle<T>(Selection<T> self, T oldItem, T newItem) where T : class;

public class Selection<T> where T : class
{
	public event SelectedHandle<T> Selected;
	
	private T _item;
	
	public T Item
	{
		get{ return _item;}
		set
		{
			var oldItem = _item;
			_item = value;
			
			if(Selected != null) 
				Selected(this, oldItem, _item);
				
			//CallSelected(oldItem);
		}
	}
	
	//private void CallSelected(T oldItem)
	//{
		//if(Selected != null) Selected(this, oldItem, _item);
	//}
}
  1. 这样我们用起来只需要在父类中创建Selection对象,绑定一次Selected事件。然后将这个Selection对象传递给子类,子类选中的时候子需要对Selection对象的Item属性赋值即可。
using System.Collections.Generic; using UnityEngine; using TMPro; using System.Collections; using UnityEngine.UI; using UnityEngine.SceneManagement; using UnityEngine.EventSystems; public class DialogueManager : MonoBehaviour { public enum DialogueState { Before, Win, Lost } public static DialogueManager Instance; [Header("UI Elements")] public TextMeshProUGUI tmpText; public float typingSpeed = 0.05f; public TMP_FontAsset fallbackFont; // 备用字体 [Header("Dialogue Content")] public List<string> beforeBattleDialogues = new List<string>(); public List<string> winDialogues = new List<string>(); public List<string> lostDialogues = new List<string>(); [Header("场景布置")] public GameObject image; public GameObject image1; public GameObject image2; public TextMeshProUGUI text; [Header("按钮设置")] public Button startButton; public Button loseButton; public Button winButton; [Header("UI 元素路径")] public string tmpTextPath = "Canvas/DialoguePanel/TMPText"; public string startButtonPath = "Canvas/Buttons/StartButton"; public string winButtonPath = "Canvas/Buttons/WinButton"; public string loseButtonPath = "Canvas/Buttons/LoseButton"; public string imagePath = "Canvas/Background"; private bool isTyping = false; private Coroutine typingCoroutine; private string currentSentence; public DialogueState currentState = DialogueState.Before; public int currentDialogueIndex = 0; private List<string> currentDialogueList; private TextMeshProUGUI _currentTmpText; private Button _currentStartButton; private Button _currentWinButton; private Button _currentLoseButton; private GameObject _currentImage; void Awake() { // 单例模式实现 if (Instance == null) { Instance = this; DontDestroyOnLoad(gameObject); SceneManager.sceneLoaded += OnSceneLoaded; Debug.Log("对话管理器初始化完成"); } else { Destroy(gameObject); return; } } void Start() { // 初始状态加载 LoadInitialState(); } void OnDestroy() { SceneManager.sceneLoaded -= OnSceneLoaded; } private void LoadInitialState() { // 确保对话列表初始化 InitializeDialogueLists(); Debug.Log("加载初始对话状态..."); // 检查存档中的对话状态 if (SaveSystem.Instance != null && SaveSystem.Instance.SaveExists()) { DialogueState savedState = SaveSystem.Instance.GetDialogueState(); int savedIndex = SaveSystem.Instance.GetDialogueIndex(); // 使用SetDialogueState确保正确初始化 SetDialogueState(savedState); currentDialogueIndex = savedIndex; Debug.Log($"从存档加载对话状态: {savedState} 索引: {savedIndex}"); } else { // 检查战斗结果 int result = PlayerPrefs.GetInt("LastBattleResult", -1); // 使用状态设置方法确保列表初始化 if (result == 1) SetDialogueState(DialogueState.Win); else if (result == 0) SetDialogueState(DialogueState.Lost); else SetDialogueState(DialogueState.Before); } // 清除状态 PlayerPrefs.DeleteKey("LastBattleResult"); } private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { // 仅初始化UI但不启动对话 StartCoroutine(InitializeUIWithoutDialogue()); } private IEnumerator InitializeUIWithoutDialogue() { yield return null; Debug.Log("初始化UI组件但不启动对话..."); // 仅执行UI初始化逻辑 // 不包含StartStateDialogue()调用 } private IEnumerator ReinitializeUIAfterFrame() { yield return null; // 等待一帧确保场景完全加载 Debug.Log("重新初始化UI组件..."); // 1. 重新获取当前场景的UI引用 _currentTmpText = GameObject.Find(tmpTextPath)?.GetComponent<TextMeshProUGUI>(); _currentStartButton = GameObject.Find(startButtonPath)?.GetComponent<Button>(); _currentWinButton = GameObject.Find(winButtonPath)?.GetComponent<Button>(); _currentLoseButton = GameObject.Find(loseButtonPath)?.GetComponent<Button>(); _currentImage = GameObject.Find(imagePath); // 2. 更新公共引用 if (_currentTmpText != null) { tmpText = _currentTmpText; EnsureFontLoaded(); // 确保字体加载 } if (_currentStartButton != null) startButton = _currentStartButton; if (_currentWinButton != null) winButton = _currentWinButton; if (_currentLoseButton != null) loseButton = _currentLoseButton; if (_currentImage != null) image = _currentImage; // 3. 重新绑定按钮事件 if (startButton != null) { startButton.onClick.RemoveAllListeners(); startButton.onClick.AddListener(OnStartClick); startButton.gameObject.SetActive(false); } if (loseButton != null) { loseButton.onClick.RemoveAllListeners(); loseButton.onClick.AddListener(OnLoseClick); loseButton.gameObject.SetActive(false); } if (winButton != null) { winButton.onClick.RemoveAllListeners(); winButton.onClick.AddListener(OnWinClick); winButton.gameObject.SetActive(false); } // 4. 确保事件系统存在 EnsureEventSystemExists(); // 5. 恢复UI状态 if (tmpText != null) { tmpText.gameObject.SetActive(false); } // 6. 继续之前的对话状态 StartStateDialogue(); } // 确保字体加载 private void EnsureFontLoaded() { if (tmpText == null) return; // 如果主字体丢失,使用备用字体 if (tmpText.font == null || tmpText.font.name == "Fallback") { if (fallbackFont != null) { tmpText.font = fallbackFont; Debug.Log("使用备用字体"); } else { Debug.LogError("主字体和备用字体均不可用!"); } } // 确保材质正确 if (tmpText.fontSharedMaterial == null) { Debug.LogWarning("字体材质丢失,使用默认材质"); tmpText.fontSharedMaterial = fallbackFont.material; } } void Update() { if (tmpText == null) { Debug.LogWarning("tmpText引用为空!"); return; } // 添加跳过功能:按E键跳过当前打字效果 if (Input.GetKeyDown(KeyCode.E) && isTyping) { SkipTyping(); } // 按E键继续下一句对话 else if (Input.GetKeyDown(KeyCode.E) && !isTyping && tmpText.gameObject.activeSelf) { NextSentence(); } } // 设置当前对话状态 public void SetDialogueState(DialogueState newState) { currentState = newState; currentDialogueIndex = 0; switch (newState) { case DialogueState.Before: currentDialogueList = beforeBattleDialogues; break; case DialogueState.Win: currentDialogueList = winDialogues; break; case DialogueState.Lost: currentDialogueList = lostDialogues; break; } Debug.Log($"状态已切换至: {newState}"); // 保存状态 SaveDialogueState(); } // 保存对话状态 private void SaveDialogueState() { if (SaveSystem.Instance != null) { SaveSystem.Instance.SaveDialogueState(currentState, currentDialogueIndex); } } private void InitializeDialogueLists() { // 确保所有对话列表都已初始化 if (beforeBattleDialogues == null) beforeBattleDialogues = new List<string>(); if (winDialogues == null) winDialogues = new List<string>(); if (lostDialogues == null) lostDialogues = new List<string>(); // 添加默认对话防止空列表 if (beforeBattleDialogues.Count == 0) beforeBattleDialogues.Add("默认对话:请添加战斗前对话内容"); if (winDialogues.Count == 0) winDialogues.Add("默认对话:请添加胜利后对话内容"); if (lostDialogues.Count == 0) lostDialogues.Add("默认对话:请添加失败后对话内容"); } // 启动当前状态的对话 public void StartStateDialogue() { if (currentDialogueList == null || currentDialogueList.Count == 0) { Debug.LogWarning($"当前对话列表为空! 状态: {currentState}"); // 自动恢复默认列表 SetDialogueState(currentState); } // 确保索引在有效范围内 currentDialogueIndex = Mathf.Clamp(currentDialogueIndex, 0, currentDialogueList.Count - 1); currentSentence = currentDialogueList[currentDialogueIndex]; Debug.Log($"开始对话: {currentSentence}"); // 暂停玩家移动 playercontrol player = FindObjectOfType<playercontrol>(); if (player != null) player.canMove = false; // 锁定玩家输入 Cursor.lockState = CursorLockMode.None; Cursor.visible = true; // 显示对话框UI tmpText.gameObject.SetActive(true); BringDialogueToFront(); // 确保在最前层 // 启动打字效果协程 if (isTyping) { StopCoroutine(typingCoroutine); } typingCoroutine = StartCoroutine(TypeSentence(currentSentence)); } // 确保对话框在最前层 private void BringDialogueToFront() { if (tmpText != null) { tmpText.transform.SetAsLastSibling(); Canvas canvas = tmpText.GetComponentInParent<Canvas>(); if (canvas != null) { canvas.sortingOrder = 100; // 设为最高层级 Debug.Log($"设置Canvas层级: {canvas.sortingOrder}"); } } } // 显示下一句对话 public void NextSentence() { currentDialogueIndex++; if (currentDialogueIndex < currentDialogueList.Count) { StartStateDialogue(); // 保存当前进度 SaveDialogueState(); } else { // 对话结束 EndDialogue(); } } IEnumerator TypeSentence(string sentence) { isTyping = true; tmpText.text = ""; // 确保文本颜色可见 tmpText.color = new Color(tmpText.color.r, tmpText.color.g, tmpText.color.b, 1f); // 逐字显示 foreach (char letter in sentence.ToCharArray()) { tmpText.text += letter; yield return new WaitForSeconds(typingSpeed); } isTyping = false; } // 跳过当前打字效果 public void SkipTyping() { if (isTyping && typingCoroutine != null) { StopCoroutine(typingCoroutine); tmpText.text = currentSentence; isTyping = false; } } public void EndDialogue() { Debug.Log("对话结束"); if (isTyping && typingCoroutine != null) { StopCoroutine(typingCoroutine); isTyping = false; } // 恢复玩家控制 playercontrol player = FindObjectOfType<playercontrol>(); if (player != null) player.canMove = true; // 隐藏对话框 if (tmpText != null) { tmpText.gameObject.SetActive(false); } // 根据状态显示对应按钮 switch (currentState) { case DialogueState.Before: if (startButton != null) { startButton.gameObject.SetActive(true); EventSystem.current.SetSelectedGameObject(startButton.gameObject); } break; case DialogueState.Win: if (winButton != null) { winButton.gameObject.SetActive(true); EventSystem.current.SetSelectedGameObject(winButton.gameObject); } break; case DialogueState.Lost: if (loseButton != null) { loseButton.gameObject.SetActive(true); EventSystem.current.SetSelectedGameObject(loseButton.gameObject); } break; } // 确保事件系统可用 EnsureEventSystemExists(); // 保存完成状态 SaveDialogueState(); } private void EnsureEventSystemExists() { EventSystem eventSystem = FindObjectOfType<EventSystem>(); if (eventSystem == null) { GameObject eventSystemObj = new GameObject("EventSystem"); eventSystem = eventSystemObj.AddComponent<EventSystem>(); eventSystemObj.AddComponent<StandaloneInputModule>(); Debug.Log("创建新事件系统"); } else if (eventSystem.GetComponent<StandaloneInputModule>() == null) { eventSystem.gameObject.AddComponent<StandaloneInputModule>(); Debug.Log("添加输入模块到事件系统"); } } public void OnMainMenuClick() { Debug.Log("主菜单按钮点击"); SceneManager.LoadScene("MainMenu"); } public void OnStartClick() { Debug.Log("开始作战"); SceneManager.LoadScene("Fight"); } public void OnLoseClick() { Debug.Log("失败按钮点击"); SceneManager.LoadScene("Fight"); } public void OnWinClick() { if (currentState != DialogueState.Win) { Debug.LogWarning("尝试在非胜利状态执行胜利逻辑"); return; } playercontrol player = FindObjectOfType<playercontrol>(); if (player != null) player.canMove = true; Debug.Log("执行胜利逻辑"); if (image != null) image.SetActive(false); if (image1 != null) image1.SetActive(false); if (image2 != null) image2.SetActive(false); if (text != null) text.gameObject.SetActive(false); if (tmpText != null) tmpText.gameObject.SetActive(false); if (winButton != null) winButton.gameObject.SetActive(false); // 清除对话存档 if (SaveSystem.Instance != null) { SaveSystem.Instance.SaveDialogueState(DialogueState.Before, 0); } } } 不要改我功能,我想让你解决button失灵问题
08-09
<template>   <RtsPageContainer ref="mainPageRef">     <template #search>       <RtsSearchForm         ref="queryFormRef"         v-model="state.queryForm"         :columns="state.queryColumns"         inline       >         <el-form-item>           <el-button type="primary" icon="search" v-preventReClick @click="onQuery">             {{ $t('common.queryBtn') }}           </el-button>           <el-button icon="refresh" @click="onResetQueryForm">             {{ $t('common.resetBtn') }}           </el-button>         </el-form-item>       </RtsSearchForm>     </template>     <div class="tabs">       <div         v-for="tab in state.tabs"         :key="tab.id"         :class="['tab-title', { active: state.currentTab === tab.id }]"         @click="switchTab(tab.id)"       >         {{ tab.title }}       </div>     </div>     <div class="tab-content">       <div v-if="state.currentTab === 'tab1'">         <el-row style="margin-bottom: 10px">               <el-col>                 <el-button type="primary" icon="el-icon-plus" @click="handleAdd">                   新建申请                 </el-button>                 <el-button                   :disabled="state.multiple"                   @click="handleDelete(state.selectObjs)"                   icon="Delete"                   type="primary"                 >                   删除                 </el-button>               </el-col>             </el-row>         <RtsTableList               ref="tableRef"               :fetch-option="state.fetchOption"               :columns="state.tableColumns"               index               row-key="id"               selection               @selection-change="handleSelectionChange"             >             <template #requestCustName="{ row }">               <el-link type="primary" :underline="false" @click="onViewOrg(row)">                  {{ row.requestCustName }}               </el-link>            </template>           </RtsTableList>       </div>       <div v-if="state.currentTab === 'tab2'">          <el-row>            <el-col>              <el-button icon="upload-filled" type="primary" @click="onExport" plain>                {{ $t('common.exportBtn') }}              </el-button>            </el-col>          </el-row>           <RtsTableList                 ref="tableRef"                 :fetch-option="state.fetchOption"                 :columns="state.tableColumns"                 index                 row-key="id"                 selection                 @selection-change="handleSelectionChange"               >               <template #requestCustName="{ row }">                   <el-link type="primary" :underline="false" @click="onViewOrg(row)">                      {{ row.requestCustName }}                   </el-link>               </template>             </RtsTableList>       </div>       <div v-if="state.currentTab === 'tab3'">          <el-row>            <el-col>              <el-button icon="upload-filled" type="primary" @click="onExport" plain>                {{ $t('common.exportBtn') }}              </el-button>                 <el-button                   :disabled="state.multiple"                   @click="handleDelete(state.selectObjs)"                   icon="Delete"                   type="primary"                 >                   重拼报告                 </el-button>            </el-col>          </el-row>           <RtsTableList                 ref="tableRef"                 :fetch-option="state.fetchOption"                 :columns="state.tableColumns"                 index                 row-key="id"                 selection                 @selection-change="handleSelectionChange"               >               <template #requestCustName="{ row }">                   <el-link type="primary" :underline="false" @click="onViewOrg(row)">                      {{ row.requestCustName }}                   </el-link>               </template>             </RtsTableList>       </div>     </div>     <template #subpage>       <ItemForm :init-data="state.formData" @confirm="onQuery"></ItemForm>     </template>     <!-- 多个子页面, 页面切换时使用需在 mainPageRef.value.switchSubPage 中指明 pageName -->     <!-- <template #pages="data">       <! -- 本页替换 -- >       <RtsSubPage         pageName="formPage"         displayMode="inner"         :activePage="data?.activePage"         :navList="data?.navList"       >         <template #main>           <ItemForm :init-data="state.formData" @confirm="onQuery"></ItemForm>         </template>       </RtsSubPage>       <! -- dialog -- >       <RtsSubPage         pageName="previewPage"         displayMode="dialog"         :activePage="data?.activePage"         :navList="data?.navList"       >         <template #main>           <ItemForm :init-data="state.formData"></ItemForm>         </template>       </RtsSubPage>     </template> -->   </RtsPageContainer> </template> <style scoped> .tabs {   display: flex;   border-bottom: 1px solid #ccc; } .tab-title {   padding: 10px 15px;   cursor: pointer;   border-bottom: 2px solid transparent; } .tab-title.active {   border-bottom: 2px solid #42b983;   font-weight: bold; } .tab-content {   padding: 20px; } </style> <script lang="ts" setup> import { ref, reactive, computed, watch, onMounted } from 'vue' import RtsPageContainer from '@rtscloud3/components/PageContainer' import RtsTableList from '@rtscloud3/components/TableList' import RtsSearchForm from '@rtscloud3/components/Form/search-form' import RtsOrgSelect from '@rtscloud3/components/OrgSelect' import { useI18n } from 'vue-i18n' import { useRoute } from 'vue-router' import { useMessageBox, useMessage } from '@rtscloud3/core/hooks/message' import { useCommonDicts } from '@rtscloud3/core/hooks/dict' import { useFileHelper } from '@rtscloud3/core/hooks/file' import { getPredicate } from '@rtscloud3/core/api/predicate' import { getPersonalApplyPage } from '/@/api/cqms/riskcredit/personalApply' import ItemForm from './form.vue' const i18n = useI18n() const { t } = i18n const route = useRoute() const mainPageRef = ref() const queryFormRef = ref() const tableRef = ref() const { preloadDicts, formatDictMapByKey, getDictItems } = useCommonDicts() const { setupTags, removeTags, simpleExport, simpleImport } = useFileHelper() // 定义查询字典 const state = reactive({   currentTab: 'tab1',   selectObjs: [],   multiple: true,   tabs: [     { id: 'tab1', title: '拟稿' },     { id: 'tab2', title: '审批中' },     { id: 'tab3', title: '已完成' }   ],   queryForm: {     requestCustName:'',     certTypeCrc:'',     certNo:'',     startDate:'',     endDate:'',     queryVersion:'',     procinStstatus:'DRAFT'   },   fetchOption: {     firstRequest: true,     dataType: 'promise',     data: getPersonalApplyPage,     params: { }   } as FetchOption,   tableColumns: [     {       label: '客户名称',       prop: 'requestCustName',       showOverflowTooltip: true,       template: true     },     {       label: '证件类型',       prop: 'certTypeCrc',       dictType: 'A01020',     },     {       label: '证件号码',       prop: 'certNo',       width: 200     },     {       label: '查询原因',       prop: 'queryReason',       dictType: 'A01009',       width: 150     },     {       label: '发起时间',       prop: 'applyTime',       width: 160     },     {       label: '最新操作时间',       prop: 'lastModifyTime',       width: 160     },     {       label: '机构',       prop: 'branchName',       width: 160     },     {       label: '部门',       prop: 'deptName',       width: 160     },     {       label: '用户名称',       prop: 'createUserName'     },     {       label: '用户账号',       prop: 'createUserCode'     },     {       label: '所属城市',       prop: 'cityCode'     },     {       label: '业务类型',       prop: 'businessTypeCode',       dictType: 'BUSINESS_TYPE_CODE'     }   ],   formData: {} }) function switchTab(tabId) {   state.currentTab = tabId   state.selectObjs = []   state.multiple = true   onQuery() } function onQuery() {   if(state.currentTab ==='tab1'){      console.log(tableRef.value)      tableRef.value.loadData(state.queryForm)   }   if(state.currentTab ==='tab2'){        console.log(tableRef.value)        tableRef.value.loadData(state.queryForm)   }   if(state.currentTab ==='tab3'){          console.log(tableRef.value)          tableRef.value.loadData(state.queryForm)     } } function onResetQueryForm() {   queryFormRef.value.resetFields() } function onExport(type: String) {   let params = {     ...state.queryForm   }   if (type === 'currentPage') {     // 导出当前页时,不能从初始化的对象里取值,因为mixin和table-list组件隔离,因此无法获取该值     const { current, size } = tableRef.value.getPagination()     params = { ...params, current, size }   }   //后续补充   simpleExport({     mode: 'blob',     url: getPredicate('cqms')+'/overDailyLimitUserLock/export',     params: params,     filename: '单日超上限用户锁定数据导出'   }).catch((err) => {   }) } function onViewOrg(row) {   state.formData = { ...row }   mainPageRef.value.switchSubPage({     pageName: 'formPage',     navList: [{ name: t('common.viewBtn') }]   }) } function handleAdd() {   state.queryAccountManageInfo = {}   mainPageRef.value.switchSubPage({ navList: [{ name: "新增" }] }) } function handleSelectionChange(objs: { applyId: string }[]) {   state.selectObjs = objs.map(({ applyId }) => applyId)   state.multiple = !objs.length } onMounted(() => {   setupTags() }) onBeforeUnmount(() => {   removeTags() }) </script> 帮我改一下代码 点击拟稿 审核中 还有已完成 调用不同的接口
最新发布
08-15
<template> <el-container style="height: 100vh; overflow: hidden;"> <!-- 侧边栏 --> <el-aside width="200px" style="background-color: #304156;"> <el-menu default-active="1" class="el-menu-vertical-demo" background-color="#304156" text-color="#bfcbd9" active-text-color="#409EFF"> <el-menu-item index="1"> <i class="el-icon-user"></i> <span>员工管理</span> </el-menu-item> </el-menu> </el-aside> <!-- 主内容区 --> <el-container> <!-- 头部 --> <el-header class="app-header"> <h2 class="app-title">员工管理系统</h2> </el-header> <!-- 主要内容 --> <el-main class="app-content"> <!-- 查询条件 --> <div class="query-section"> <el-row :gutter="20"> <el-col :span="4"> <el-input v-model="queryParams.name" placeholder="请输入姓名" clearable /> </el-col> <el-col :span="4"> <el-select v-model="queryParams.gender" placeholder="请选择性别" clearable> <el-option label="男" :value="0" /> <el-option label="女" :value="1" /> </el-select> </el-col> <el-col :span="4"> <el-select v-model="queryParams.job" placeholder="请选择职位" clearable> <el-option v-for="item in jobOptions" :key="item.value" :label="item.label" :value="item.value" /> </el-select> </el-col> <el-col :span="6"> <el-date-picker v-model="queryParams.dateRange" type="daterange" start-placeholder="开始日期" end-placeholder="结束日期" format="YYYY-MM-DD" value-format="YYYY-MM-DD" /> </el-col> <el-col :span="6" class="action-buttons"> <el-button type="primary" @click="fetchData" icon="el-icon-search">搜索</el-button> <el-button type="success" @click="showAddDialog" icon="el-icon-plus">新增</el-button> <el-button type="danger" @click="batchDelete" icon="el-icon-delete">批量删除</el-button> </el-col> </el-row> </div> <!-- 数据表格 --> <div class="table-section"> <el-table :data="tableData" border stripe style="width: 100%" @selection-change="handleSelectionChange"> <el-table-column type="selection" width="55" align="center" /> <el-table-column prop="id" label="ID" width="80" align="center" /> <el-table-column prop="name" label="姓名" width="120" /> <el-table-column label="头像" width="100" align="center"> <template #default="scope"> <el-avatar v-if="scope.row.image" :src="scope.row.image" :size="50" shape="square" :preview-src-list="[scope.row.image]" /> <el-avatar v-else :size="50" shape="square"> <i class="el-icon-user-solid" style="font-size: 24px;" /> </el-avatar> </template> </el-table-column> <el-table-column prop="gender" label="性别" width="80" align="center"> <template #default="scope"> <el-tag :type="scope.row.gender === 0 ? 'primary' : 'danger'"> {{ scope.row.gender === 0 ? '男' : '女' }} </el-tag> </template> </el-table-column> <el-table-column prop="job" label="职位" width="150"> <template #default="scope"> <el-tag :type="getJobTagType(scope.row.job)"> {{ jobMap[scope.row.job] || '未知' }} </el-tag> </template> </el-table-column> <el-table-column prop="entrydate" label="入职时间" width="120" align="center" /> <!-- 优化时间显示 --> <el-table-column prop="createTime" label="创建时间" width="180" align="center" /> <el-table-column prop="updateTime" label="最后修改时间" width="180" align="center" /> <el-table-column label="操作" width="200" align="center" fixed="right"> <template #default="scope"> <el-button size="small" type="primary" icon="el-icon-edit" @click="handleEdit(scope.row)">编辑</el-button> <el-button size="small" type="danger" icon="el-icon-delete" @click="handleDelete(scope.row.id)">删除</el-button> </template> </el-table-column> </el-table> </div> <!-- 分页组件 --> <div class="pagination-section"> <el-pagination background layout="total, prev, pager, next, sizes" :total="total" :page-sizes="[5, 10, 20, 50]" :page-size="queryParams.pageSize" v-model:current-page="queryParams.pageNum" @current-change="fetchData" @size-change="handleSizeChange" /> </div> </el-main> </el-container> </el-container> <!-- 新增/编辑对话框 --> <el-dialog v-model="dialogVisible" :title="dialogTitle" width="700px" :close-on-click-modal="false"> <el-form :model="form" ref="formRef" :rules="formRules" label-width="100px" label-position="left"> <el-row :gutter="20"> <el-col :span="12"> <el-form-item label="姓名" prop="name"> <el-input v-model="form.name" /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="用户名" prop="username"> <el-input v-model="form.username" /> </el-form-item> </el-col> </el-row> <el-row :gutter="20"> <el-col :span="12"> <el-form-item label="性别" prop="gender"> <el-select v-model="form.gender" placeholder="请选择性别" style="width: 100%"> <el-option label="男" :value="0" /> <el-option label="女" :value="1" /> </el-select> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="职位" prop="job"> <el-select v-model="form.job" placeholder="请选择职位" style="width: 100%"> <el-option v-for="item in jobOptions" :key="item.value" :label="item.label" :value="item.value" /> </el-select> </el-form-item> </el-col> </el-row> <el-row :gutter="20"> <el-col :span="12"> <el-form-item label="密码" prop="password" v-if="!isEdit"> <el-input v-model="form.password" show-password /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="入职时间" prop="entrydate"> <el-date-picker v-model="form.entrydate" type="date" placeholder="选择日期" format="YYYY-MM-DD" value-format="YYYY-MM-DD" style="width: 100%" /> </el-form-item> </el-col> </el-row> <el-form-item label="头像链接" prop="image"> <el-input v-model="form.image" placeholder="请输入图片URL" /> <div class="avatar-preview" v-if="form.image"> <el-image :src="form.image" fit="cover" style="width: 100px; height: 100px; margin-top: 10px;" :preview-src-list="[form.image]" /> </div> </el-form-item> <el-form-item class="form-actions"> <el-button type="primary" @click="submitForm">提交</el-button> <el-button @click="dialogVisible = false">取消</el-button> </el-form-item> </el-form> </el-dialog> </template> <script setup> import { ref, reactive, onMounted } from 'vue'; import axios from 'axios'; import { ElMessage, ElMessageBox } from 'element-plus'; // 设置 axios 实例 const apiClient = axios.create({ baseURL: 'http://localhost:8080/emps', timeout: 5000, }); // 表格数据 const tableData = ref([]); const total = ref(0); const selectedRows = ref([]); const formRef = ref(null); // 查询参数 const queryParams = reactive({ pageNum: 1, pageSize: 5, name: '', gender: null, job: null, dateRange: [] }); // 表单数据 const form = reactive({ id: null, username: '', password: '', name: '', gender: null, job: null, image: '', entrydate: '' }); // 表单验证规则 const formRules = { name: [{ required: true, message: '请输入姓名', trigger: 'blur' }], username: [{ required: true, message: '请输入用户名', trigger: 'blur' }], gender: [{ required: true, message: '请选择性别', trigger: 'change' }], job: [{ required: true, message: '请选择职位', trigger: 'change' }], password: [{ required: true, message: '请输入密码', trigger: 'blur' }], entrydate: [{ required: true, message: '请选择入职日期', trigger: 'change' }] }; // 对话框控制 const dialogVisible = ref(false); const dialogTitle = ref('新增员工'); const isEdit = ref(false); // 职位映射表 const jobMap = { 1: '班主任', 2: '讲师', 3: '学工主管', 4: '教研主管', 5: '咨询师' }; // 职位选项 const jobOptions = [ { label: '班主任', value: 1 }, { label: '讲师', value: 2 }, { label: '学工主管', value: 3 }, { label: '教研主管', value: 4 }, { label: '咨询师', value: 5 } ]; // 获取职位标签类型 const getJobTagType = (job) => { const types = ['', 'success', 'warning', 'danger', 'info', 'primary']; return types[job] || 'info'; }; // 获取数据 const fetchData = async () => { const params = { page: queryParams.pageNum, pageSize: queryParams.pageSize, name: queryParams.name, gender: queryParams.gender, job: queryParams.job, begin: queryParams.dateRange[0] || '', end: queryParams.dateRange[1] || '' }; try { const res = await apiClient.get('', { params }); if (res.data.code === 1) { tableData.value = res.data.data.rows.map(item => ({ ...item, // 统一格式化时间显示 createTime: formatTime(item.createTime, 'YYYY-MM-DD HH:mm:ss'), updateTime: formatTime(item.updateTime, 'YYYY-MM-DD HH:mm:ss') })); total.value = res.data.data.total; } else { ElMessage.error('获取数据失败:' + res.data.msg); } } catch (error) { console.error('请求出错:', error); ElMessage.error('网络请求失败,请检查后端是否运行正常'); } }; // 优化后的时间格式化函数 const formatTime = (timeStr, format = 'YYYY-MM-DD HH:mm:ss') => { if (!timeStr) return ''; try { // 处理带T的时间格式 const normalizedTime = timeStr.includes('T') ? timeStr.replace('T', ' ') : timeStr; const date = new Date(normalizedTime); // 直接格式化为目标格式 const padZero = num => num.toString().padStart(2, '0'); const year = date.getFullYear(); const month = padZero(date.getMonth() + 1); const day = padZero(date.getDate()); const hours = padZero(date.getHours()); const minutes = padZero(date.getMinutes()); const seconds = padZero(date.getSeconds()); return format .replace('YYYY', year) .replace('MM', month) .replace('DD', day) .replace('HH', hours) .replace('mm', minutes) .replace('ss', seconds); } catch (e) { console.error('时间格式化错误:', e); return timeStr; // 返回原始字符串 } }; // 显示新增对话框 const showAddDialog = () => { dialogTitle.value = '新增员工'; isEdit.value = false; Object.assign(form, { id: null, username: '', password: '', name: '', gender: null, job: null, image: '', entrydate: '' }); dialogVisible.value = true; }; // 显示编辑对话框 const handleEdit = (row) => { dialogTitle.value = '编辑员工'; isEdit.value = true; Object.assign(form, { ...row }); dialogVisible.value = true; }; // 提交表单 const submitForm = async () => { try { await formRef.value.validate(); if (isEdit.value) { await apiClient.put('', form); ElMessage.success('员工信息更新成功'); } else { await apiClient.post('', form); ElMessage.success('员工添加成功'); } dialogVisible.value = false; fetchData(); } catch (error) { if (error.name !== 'Error') { console.error('保存失败:', error); ElMessage.error('操作失败:' + (error.response?.data?.message || error.message)); } } }; // 删除员工 const handleDelete = async (id) => { try { await ElMessageBox.confirm(`确认删除ID为 ${id} 的员工吗?`, '删除确认', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }); await apiClient.delete(`/${[id]}`); ElMessage.success('删除成功'); fetchData(); } catch (error) { if (error !== 'cancel') { console.error('删除失败:', error); ElMessage.error('删除失败:' + (error.response?.data?.message || error.message)); } } }; // 批量删除 const batchDelete = async () => { if (selectedRows.value.length === 0) { ElMessage.warning('请至少选择一条记录'); return; } try { const ids = selectedRows.value.map(item => item.id); await ElMessageBox.confirm(`确认删除选中的 ${ids.length} 条员工吗?`, '批量删除确认', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }); await apiClient.delete(`/${ids}`); ElMessage.success(`成功删除 ${ids.length} 条记录`); fetchData(); } catch (error) { if (error !== 'cancel') { console.error('批量删除失败:', error); ElMessage.error('删除失败:' + (error.response?.data?.message || error.message)); } } }; // 表格选择监听 const handleSelectionChange = (rows) => { selectedRows.value = rows; }; // 处理分页大小变化 const handleSizeChange = (size) => { queryParams.pageSize = size; fetchData(); }; // 初始化加载数据 onMounted(() => { fetchData(); }); </script> <style scoped> /* 全局样式 */ .app-header { background-color: #fff; box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08); display: flex; align-items: center; padding: 0 20px; z-index: 1; } .app-title { margin: 0; font-size: 18px; font-weight: 600; color: #333; } .app-content { padding: 20px; background-color: #f0f2f5; height: calc(100vh - 60px); overflow-y: auto; } /* 卡片式布局 */ .query-section, .table-section, .pagination-section { background: #fff; border-radius: 8px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); margin-bottom: 16px; padding: 16px; } .action-buttons { display: flex; justify-content: flex-end; gap: 10px; } .form-actions { display: flex; justify-content: center; margin-top: 20px; } .avatar-preview { display: flex; justify-content: center; margin-top: 10px; } /* 响应式布局 */ @media (max-width: 992px) { .el-col { margin-bottom: 15px; } .action-buttons { justify-content: flex-start; } } /* 表格样式优化 */ .el-table { font-size: 14px; } .el-table .el-button { margin: 0 5px; font-size: 13px; } /* 对话框样式优化 */ :deep(.el-dialog__body) { padding: 20px 25px; } </style> 优化一下表单布局,现在未铺满,
07-15
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值