Container.DataItem的含义

本文详细介绍了ASP.NET中各种数据绑定的方法,包括绑定到简单属性、集合、表达式、方法返回值等多种情况,并对比了不同绑定方式的性能差异。

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

今天有同事问我这样一个问题:

后台:this.GridView.DataSource=new String[]{"AA","BB","CC"};this.GridView.DataBind();

前台:不知道怎么绑定才能显示初数组的值?

这个问题其实很简单,前台只需要<%#Container.DataItem%>即可,可能最主要问题是大家对这里Container.DataItem不是很理解,一下我做一简单解释:

绑定到简单属性:<%#UserName%>
绑定到集合:<asp:ListBox id="ListBox1" datasource='<%# myArray%>' runat="server">
绑定到表达式:<%#(class1.property1.ToString() + "," + class1.property2.ToString())%>
绑定到方法返回值:<%# GetSafestring(str) %>
绑定到Hashtable:<%# ((DictionaryEntry)Container.DataItem).Key%>
绑定到ArrayList:<%#Container.DataItem %>

若数组里里放的是对象则可能要进行必要的转换后再绑定如:
<%#((对象类型)Container.DataItem).属性%>

绑定到DataView,DataTable,DataSet:
<%#((DataRowView)Container.DataItem)["字段名"]%>或
<%#((DataRowView)Container.DataItem).Rows[0]["字段名"]%>
要格式化则:
<%#string.Format("格式",((DataRowView)Container.DataItem)["字段名"])%>
<%#DataBinder.Eval(Container.DataItem,"字段名","格式")%>

绑定到DataReader:
<%#((IDataReader)Container.DataItem).字段名%>

当然为了方便一般使用最多的就是DataBinder类的Eval方法了.不过这样对于同时要绑定大量的数据效率要低一些

在绑定数据时经常会用到这个句程序:<%# DataBinder.Eval(Container.DataItem,"xxxx")%>或者<%# DataBinder.Eval(Container,"DataItem.xxxx")%>
今天又学到一种,而且微软也说这种方法的效率要比以上两种高。

<%# ((DataRowView)Container.DataItem)["xxxx"]%>

很有用的,这样可以在前台页面做好多事情了。

还要记住要这样用必须要在前台页面导入名称空间System.Data,否则会生成错误信息。

<%@ Import namespace="System.Data" %>

这种用法其实和<%# ((DictionaryEntry)Container.DataItem).Key%>是一个道理。

绑定到DataSet、DataTable时:

<%#((System.Data.DataRowView)Container.DataItem)["字段名"]%>
<%#((System.Data.DataRowView)Container.DataItem)[索引]%>


绑定到DataReader时:
<%#((System.Data.Common.DbDataRecord)Container.DataItem)[索引]%>
<%#((System.Data.Common.DbDataRecord)Container.DataItem)["字段名"]%>

关键是Container这个东西,它比较神秘。它的名称空间是System.ComponentModel。对于它我还需要进一步理解。

初学.NET,现在在看DataGrid控件,在ItemTemplate显示数据时,
DataBinder.Eval(Container.DataItem,"Name")和Container.DataItem("Name")有什么区别?


DataBinder是System.Web里面的一个静态类,它提供了Eval方法用于简化数据绑定表达式的编写,但是它使用的方式是通过 Reflection等开销比较大的方法来达到易用性,因此其性能并不是最好的。而Container则根本不是任何一个静态的对象或方法,它是 ASP.NET页面编译器在数据绑定事件处理程序内部声明的局部变量,其类型是可以进行数据绑定的控件的数据容器类型(如在Repeater内部的数据绑定容器叫RepeaterItem),在这些容器类中基本都有DataItem属性,因此你可以写Container.DataItem,这个属性返回的是你正在被绑定的数据源中的那个数据项。如果你的数据源是DataTable,则这个数据项的类型实际是DataRowView。

最后感谢:http://hi.baidu.com/superxxpp/blog/item/a8c69e4a59e8732a08f7ef5f.html

解读SELECT sum(CASE WHEN jthgl.data_value1 = '不合格品' THEN car_num ELSE 0 END )AS bhg, sum(CASE WHEN jthgl.data_value1 != '不合格品' THEN car_num ELSE 0 END )AS hgp, sum(CASE WHEN jthgl.data_value1 IS NOT NULL THEN car_num ELSE 0 END )AS zongshu, (sum(CASE WHEN jthgl.data_value1 != '不合格品' THEN car_num ELSE 0 END )/ sum(CASE WHEN jthgl.data_value1 IS NOT NULL THEN car_num ELSE 0 END ))* 100 AS HEGELV FROM ( SELECT * FROM ( SELECT a.*, b.data_value1, trunc((rn - 1) / 5) rshang, MOD((rn - 1), 5) rmod, CASE WHEN a.CONTAINER_NR IS NOT NULL THEN listagg(a.CONTAINER_NR, ',') WITHIN GROUP( ORDER BY a.CONTAINER_NR) OVER(PARTITION BY a.COMM_NR) END carorCon FROM ( SELECT t.COMM_NR, --t.CAR_NUM, t.mat_nr, t.mat_desc, t.mat_nr || '-' || t.mat_desc matCode, trunc(t.created_dt) post_dt, t.vendor_desc, t.level_result, a.CREATED_BY_NAME, a.ORIGIN_DESC, CASE WHEN b.CONTAINER_NR IS NULL THEN b.CAR_NO ELSE b.CONTAINER_NR END CONTAINER_NR, --b.CAR_NO, b.ms_bill_nr, ROW_NUMBER() OVER(PARTITION BY a.comm_nr ORDER BY trunc(t.created_dt) DESC) rn FROM IR_QL_INSP_COMM T, IR_BU_STOCK_BUSI a, IR_BU_STOCK_ITEM b WHERE t.comm_nr = a.comm_nr AND a.STOCK_BUSI_NR = b.STOCK_BUSI_NR AND T.MAT_NR IN ('R0000056', 'R0000229') AND t.comm_nr IN ( SELECT DISTINCT comm_nr FROM IR_QL_INSP_ITEM b WHERE to_char(b.end_dt,'yyyy-mm-dd') = ?)) a LEFT JOIN ( SELECT T.KEY_VALUE1, T.DATA_VALUE1 FROM IR_MD_REFERENCE_DATA_ITEM T WHERE T.REFERENCE_DATA_ID = 'LEVEL_RESULT') b ON a.level_result = b.KEY_VALUE1) x LEFT JOIN ( SELECT t.comm_nr comm_nr_c, count(b.car_no) car_num FROM IR_QL_INSP_COMM T, IR_BU_STOCK_BUSI a, IR_BU_STOCK_ITEM b WHERE t.comm_nr = a.comm_nr AND a.STOCK_BUSI_NR = b.STOCK_BUSI_NR AND T.MAT_NR IN ('R0000056', 'R0000229') AND t.comm_nr IN ( SELECT DISTINCT comm_nr FROM IR_QL_INSP_ITEM b WHERE to_char(b.end_dt,'yyyy-mm-dd') = ?) GROUP BY t.comm_nr) y ON x.comm_nr = y.comm_nr_c)jthgl
最新发布
08-05
<template> <div class="conversation-container"> <div class="conversation-list"> <div class="conversation-list-item"> <div class="conversation-list-title">待接入 {{ pendingConversations.length }}</div> <div class="conversation-list-body"> <router-link tag="div" :to="chat(conversation)" replace v-for="(conversation, key) in pendingConversations" :key="key"> <div class="conversation-item"> <div class="conversation-item-head"> <img :src="conversation.data.avatar" class="conversation-item-avatar" /> </div> <div class="item-info"> <div class="item-info-name">{{ conversation.data.name }}</div> <div v-if="conversation.lastMessage.type === 'text'" class="item-info-message"> {{ conversation.lastMessage.payload.text }} </div> <div v-else-if="conversation.lastMessage.type === 'image'" class="item-info-message">[图片消息]</div> <div v-else-if="conversation.lastMessage.type === 'video'" class="item-info-message">[视频消息]</div> <div v-else-if="conversation.lastMessage.type === 'audio'" class="item-info-message">[语音消息]</div> <div v-else-if="conversation.lastMessage.type === 'order'" class="item-info-message">[自定义消息:订单] </div> <div v-else-if="conversation.lastMessage.type === 'CS_END'" class="item-info-message">会话已结束</div> <div v-else-if="conversation.lastMessage.type === 'CS_ACCEPT'" class="item-info-message">接入成功</div> <div v-else-if="conversation.lastMessage.type === 'CS_TRANSFER'" class="item-info-message"> {{ conversation.lastMessage.senderId === currentAgent.id ? `已转接给` + conversation.lastMessage.payload.transferTo.data.name : '已接入来自' + conversation.lastMessage.senderData.name + '的转接' }} </div> <div v-else class="item-info-message">[未识别内容]</div> </div> </div> </router-link> </div> </div> <div class="conversation-list-item"> <div class="conversation-list-title">已接入 {{ conversations.length }}</div> <div v-if="conversations.length" class="conversation-list-body"> <router-link tag="div" :to="chat(conversation)" replace v-for="(conversation, key) in conversations" :key="key"> <div class="conversation-item" @contextmenu.prevent.stop="e => showRightClickMenu(e, conversation)"> <div class="conversation-item-head"> <img :src="conversation.data.avatar" class="conversation-item-avatar" /> <span v-if="conversation.unread" class="conversation-item-unread">{{ conversation.unread }}</span> </div> <div class="conversation-item-info"> <div class="item-info-top"> <div class="item-info-name">{{ conversation.data.name }}</div> <div class="item-info-time">{{ formatDate(conversation.lastMessage.timestamp) }}</div> </div> <div class="item-info-bottom"> <div v-if="conversation.lastMessage.status === 'sending'" class="item-info-sending"></div> <div v-if="conversation.lastMessage.status === 'fail'" class="item-info-failed"></div> <div v-if="conversation.lastMessage.type === 'text'" class="item-info-message"> {{ conversation.lastMessage.senderId === currentAgent.id ? '你' : conversation.lastMessage.senderData.name }}: {{ conversation.lastMessage.payload.text }} </div> <div v-else-if="conversation.lastMessage.type === 'image'" class="item-info-message">[图片消息]</div> <div v-else-if="conversation.lastMessage.type === 'video'" class="item-info-message">[视频消息]</div> <div v-else-if="conversation.lastMessage.type === 'audio'" class="item-info-message">[语音消息]</div> <div v-else-if="conversation.lastMessage.type === 'order'" class="item-info-message">[自定义消息:订单] </div> <div v-else-if="conversation.lastMessage.type === 'CS_END'" class="item-info-message">会话已结束</div> <div v-else-if="conversation.lastMessage.type === 'CS_ACCEPT'" class="item-info-message">接入成功</div> <div v-else-if="conversation.lastMessage.type === 'CS_TRANSFER'" class="item-info-message"> {{ conversation.lastMessage.senderId === currentAgent.id ? `已转接给` + conversation.lastMessage.payload.transferTo.data.name : '已接入来自' + conversation.lastMessage.senderData.name + '的转接' }} </div> <div v-else class="item-info-message">[未识别内容]</div> </div> </div> </div> </router-link> </div> </div> <div v-if="rightClickMenu.visible" :style="{ 'left': rightClickMenu.x + 'px', 'top': rightClickMenu.y + 'px' }" class="action-box"> <div class="action-item" @click="topConversation">{{ rightClickMenu.conversation.top ? '取消置顶' : '置顶' }}</div> <div class="action-item" @click="deleteConversation">删除聊天</div> </div> </div> <div class="conversation-main"> <router-view :key="$route.params.id"></router-view> </div> </div> </template> <script setup> import { ref, onMounted, onBeforeUnmount } from 'vue'; import { useRouter, useRoute } from 'vue-router'; import { formatDate } from '../utils/utils.js'; import GoEasy from 'goeasy'; // 引入真实的 GoEasy SDK import { useCounterStore } from '../stores/counter.js' const router = useRouter(); const route = useRoute(); // 定义响应式数据 const pendingConversations = ref([]); const conversations = ref([]); const rightClickMenu = ref({ conversation: null, visible: false, x: null, y: null, }); const currentAgent = ref(null); // 初始化 GoEasy const goEasy = GoEasy.getInstance({ host: 'hangzhou.goeasy.io', //应用所在的区域地址: 【hangzhou.goeasy.io |singapore.goeasy.io】 appkey: 'BC-f00d5ee0720d4dab888ca1b5536483ab', // common key, modules: ['im'], }); console.log('GoEasy initialized:', goEasy); console.log('GoEasy IM module:', goEasy.im); // 检查 IM 模块是否存在 // 其他代码保持不变... const loadConversations = () => { goEasy.im.pendingConversations({ onSuccess: (result) => { console.log('待接入会话:', result.content.conversations); renderPendingConversations(result.content); }, onFailed: (error) => { console.log('获取待接入列表失败:', error); }, }); goEasy.im.latestConversations({ onSuccess: (result) => { console.log('已接入会话:', result.content.conversations); renderLatestConversations(result.content); }, onFailed: (error) => { console.log('获取已接入列表失败:', error); }, }); }; const listenConversationUpdate = () => { // 监听会话列表变化 goEasy.im.on(goEasy.IM_EVENT.CONVERSATIONS_UPDATED, renderLatestConversations); goEasy.im.on(goEasy.IM_EVENT.PENDING_CONVERSATIONS_UPDATED, renderPendingConversations); }; const renderPendingConversations = (content) => { pendingConversations.value = content.conversations; }; const renderLatestConversations = (content) => { conversations.value = content.conversations; }; const chat = (conversation) => { return { path: `/conversations/chat/${conversation.id}`, query: { name: conversation.data.name, avatar: conversation.data.avatar, }, }; }; const showRightClickMenu = (e, conversation) => { rightClickMenu.value.conversation = conversation; rightClickMenu.value.visible = true; rightClickMenu.value.x = e.pageX; rightClickMenu.value.y = e.pageY; }; const hideRightClickMenu = () => { rightClickMenu.value.visible = false; }; const topConversation = () => { const conversation = rightClickMenu.value.conversation; const description = conversation.top ? '取消置顶' : '置顶'; goEasy.im.topConversation({ top: !conversation.top, conversation: conversation, onSuccess: () => { console.log(description, '成功'); }, onFailed: (error) => { console.log(description, '失败:', error); }, }); }; const deleteConversation = () => { if (!rightClickMenu.value.conversation.ended) { alert('删除失败:会话尚未结束'); return; } if (confirm('确认要删除这条会话吗?')) { goEasy.im.removeConversation({ conversation: rightClickMenu.value.conversation, onSuccess: () => { console.log('删除会话成功'); }, onFailed: (error) => { console.log(error); }, }); } }; onMounted(() => { // 隐藏 Conversation 右键菜单 document.addEventListener('click', hideRightClickMenu); // 获取当前客服信息 currentAgent.value = useCounterStore().currentAgent; console.log(currentAgent.value); // 监听 GoEasy 连接状态 goEasy.connect({ onSuccess: () => { console.log('GoEasy connected successfully'); // 确保用户信息存在 if (!currentAgent.value || !currentAgent.value.id || !currentAgent.value.name || !currentAgent.value.avatar) { console.error('Agent information is incomplete'); return; } // 登录IM goEasy.im.login({ id: currentAgent.value.id, // 必须 data: { // 必须 name: currentAgent.value.name, avatar: currentAgent.value.avatar, // 添加其他必要字段 }, onSuccess: () => { console.log('IM login success'); listenConversationUpdate(); loadConversations(); }, onFailed: (error) => { console.error('IM login failed:', error); } }); }, onFailed: (error) => { console.error('GoEasy connection failed:', error); } }); }); </script> <style scoped> .conversation-container { width: 100%; height: 100%; display: flex; background: #FFFFFF; } .conversation-list { width: 220px; border-right: 1px solid #eee; display: flex; flex-direction: column; padding: 10px; position: relative; } .conversation-list-title { font-size: 15px; margin: 5px 10px; color: rgba(0, 0, 0, 0.9); } .conversation-list-body { overflow-y: auto; max-height: 350px; scrollbar-width: none; -ms-overflow-style: none; } .conversation-list-body::-webkit-scrollbar { display: none; } .action-box { width: 100px; height: 60px; background: #ffffff; border: 1px solid #cccccc; position: fixed; z-index: 100; border-radius: 5px; } .action-item { padding-left: 15px; line-height: 30px; font-size: 13px; color: #262628; cursor: pointer; } .action-item:hover { background: #dddddd; } .conversation-item { display: flex; padding: 10px; cursor: pointer; } .conversation-item-head { position: relative; margin-right: 5px; } .conversation-item-avatar { width: 40px; height: 40px; border-radius: 4px; } .conversation-item-unread { position: absolute; top: -9px; right: -9px; width: 18px; height: 18px; line-height: 18px; border-radius: 50%; text-align: center; color: #fff; font-size: 12px; background-color: #fa5151; } .conversation-item-info { flex: 1; display: flex; flex-direction: column; justify-content: space-between; } .item-info-top { display: flex; justify-content: space-between; align-items: center; } .item-info-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; word-break: break-all; font-size: 15px; width: 80px; line-height: 25px; color: #333333; } .item-info-time { color: #666666; font-size: 12px; } .item-info-bottom { display: flex; align-items: center; } .item-info-message { font-size: 12px; line-height: 20px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; width: 150px; color: #606266; } .item-info-failed { background: url("../assets/images/failed.png") no-repeat center; background-size: 12px; width: 12px; height: 12px; margin-right: 2px; } .item-info-sending { background: url("../assets/images/pending.gif") no-repeat center; background-size: 12px; width: 12px; height: 12px; margin-right: 2px; } .router-link-active { background: #eeeeee; border-radius: 5px; } .conversation-main { flex: 1; background: #FFFFFF; } </style>我在用GoEasy实现即时通讯时报错code: 400, content: 'data: id and data are required for IM module
06-10
<template> <div class="app-container"> <el-row :gutter="20"> <!-- 项目树菜单 --> <el-col :span="6" :xs="24"> <div class="head-container"> <el-input v-model="projectName" placeholder="请输入项目名称" clearable size="mini" prefix-icon="el-icon-search" style="margin-bottom: 20px" @input="filterProjectTree" /> </div> <el-row :gutter="10" class="mb8"> <el-col :span="1.5"> <el-button type="info" plain icon="el-icon-sort" size="mini" @click="toggleExpandAll" >{{ isExpandAll ? '折叠' : '展开' }}</el-button> </el-col> <el-col :span="1.5"> <el-button type="primary" plain icon="el-icon-folder-add" size="mini" @click="handleAddProject" v-hasPermi="['cms:project:add']" >新增项目</el-button> </el-col> </el-row> <el-tree v-if="refreshTable" style="margin-top: 0.8rem;" :data="filteredProjectTreeData" :props="defaultProps" :expand-on-click-node="false" :filter-node-method="filterNode" :highlight-current="true" :default-expand-all="isExpandAll" ref="projectTree" empty-text="加载中,请稍候" node-key="id" @node-click="handleProjectClick" > <span class="custom-tree-node" slot-scope="{ node, data }"> <div class="tree-node-content"> <el-icon class="tree-icon" :size="16"> <svg-icon v-if="node.expanded" icon-class="folder-open" /> <svg-icon v-else icon-class="folder" /> </el-icon> <div v-if="node.label && node.label.length > 25" class="node-label"> <el-tooltip :show-after="300" :content="node.label" placement="top-start"> <span>{{ ellipsis(node.label, 25) }}</span> </el-tooltip> </div> <div v-else class="node-label"> <span>{{ node.label }}</span> </div> </div> <span class="node-actions"> <el-button type="text" size="mini" icon="el-icon-edit" v-hasPermi="['cms:project:update']" @click.stop="() => handleEditProject(data)" ></el-button> <el-button type="text" size="mini" icon="el-icon-plus" v-hasPermi="['cms:project:add']" @click.stop="() => handleAddSubProject(data)" ></el-button> <el-button type="text" size="mini" icon="el-icon-delete" v-hasPermi="['cms:project:delete']" @click.stop="() => handleDeleteProject(data)" ></el-button> </span> </span> </el-tree> </el-col> <!-- 文档区域 --> <el-col :span="18" :xs="24"> <!-- 文档列表视图 --> <div v-if="activeView === 'list'" class="doc-list-container"> <el-form :model="docQueryParams" ref="docQueryForm" :inline="true" label-width="68px"> <el-form-item label="文档标题" prop="title"> <el-input v-model="docQueryParams.title" placeholder="请输入文档标题" clearable size="mini" @keyup.enter.native="getDocumentList" prefix-icon="el-icon-search" /> </el-form-item> <el-form-item label="状态" prop="status"> <el-select v-model="docQueryParams.status" placeholder="状态" size="mini" clearable> <el-option label="编制中✍" value="0" /> <el-option label="待评审✊" value="1" /> <el-option label="已评审👍" value="2" /> <el-option label="修改中🔧" value="3" /> <el-option label="开发中💪" value="4" /> <el-option label="已完成开发🆗" value="5" /> </el-select> </el-form-item> <el-form-item> <el-button type="primary" icon="el-icon-search" size="mini" @click="getDocumentList">搜索</el-button> <el-button icon="el-icon-refresh" size="mini" @click="resetDocQuery">重置</el-button> </el-form-item> </el-form> <div class="batch-actions"> <el-button type="success" icon="el-icon-download" size="mini" :disabled="selectedDocs.length === 0" @click="handleBatchExport" > 批量导出 ({{ selectedDocs.length }}) </el-button> </div> <el-table v-loading="docLoading" :data="documentList" highlight-current-row @row-click="handleRowClick" @selection-change="handleSelectionChange" ref="docTable" > <el-table-column type="selection" width="55" align="center" /> <el-table-column label="序号" type="index" width="50" align="center" /> <el-table-column label="编号" align="center" prop="serialNum" /> <el-table-column label="文档标题" prop="title" align="center"/> <el-table-column label="进度" prop="status" width="100"> <template slot-scope="scope"> <el-tag v-if="scope.row.status === '0'" type="info" size="small">编制中✍</el-tag> <el-tag v-if="scope.row.status === '1'" type="warning" size="small">待评审✊</el-tag> <el-tag v-if="scope.row.status === '2'" type="success" size="small">已评审👍</el-tag> <el-tag v-if="scope.row.status === '3'" type="success" size="small">修改中🔧</el-tag> <el-tag v-if="scope.row.status === '4'" type="success" size="small">开发中💪</el-tag> <el-tag v-if="scope.row.status === '5'" type="success" size="small">已完成开发🆗</el-tag> </template> </el-table-column> <el-table-column label="创建人" prop="createBy" width="100" /> <el-table-column label="负责人" prop="director" width="100"/> <el-table-column label="创建时间" prop="createTime" width="140" /> <el-table-column label="操作" width="180" align="center"> <template slot-scope="scope"> <el-button size="mini" type="text" icon="el-icon-edit" @click.stop="handleEditDocument(scope.row)"></el-button> <el-button size="mini" type="text" icon="el-icon-download" @click.stop="handleExportHttp(scope.row, true)" title="导出详细" ></el-button> <el-button size="mini" type="text" icon="el-icon-download" @click.stop="handleExportHttp(scope.row, false)" title="导出简版" ></el-button> <el-button size="mini" type="text" icon="el-icon-delete" @click.stop="handleDeleteDocument(scope.row)" v-hasPermi="['cms:project:deleteArticleFromProject']"></el-button> </template> </el-table-column> </el-table> <pagination v-show="docTotal>0" :total="docTotal" :page.sync="docQueryParams.pageNum" :limit.sync="docQueryParams.pageSize" @pagination="getDocumentList" /> </div> <!-- 文档详情视图 --> <div v-else-if="activeView === 'detail'" class="doc-detail-container"> <div class="doc-header"> <el-button type="text" icon="el-icon-back" @click="backToList">返回文档列表</el-button> <h2 class="doc-title"> <el-icon class="title-icon"><svg-icon icon-class="document" /></el-icon> {{ currentDocument.title }} </h2> </div> <!-- 富文本编辑器 --> <el-form :model="currentDocument" ref="docForm" label-width="80px"> <el-form-item label="文档内容"> <Tinymce :height='600' v-model='currentDocument.content'></Tinymce> </el-form-item> <el-form-item> <el-button type="primary" icon="el-icon-check" @click="saveDocument">保存</el-button> <el-button icon="el-icon-close" @click="backToList">取消</el-button> </el-form-item> </el-form> </div> </el-col> </el-row> <!-- 项目编辑对话框 --> <el-dialog :title="projectDialogTitle" :visible.sync="projectDialogVisible" width="50%"> <el-form :model="projectForm" ref="projectForm" label-width="100px"> <el-form-item label="项目名称" prop="name" required> <el-input v-model="projectForm.name" placeholder="请输入项目名称" prefix-icon="el-icon-folder" /> </el-form-item> <el-form-item label="上级项目" prop="parentId"> <treeselect v-model="projectForm.parentId" :options="projectTreeData" :normalizer="normalizer" placeholder="选择上级项目" /> </el-form-item> <el-form-item label="项目描述" prop="description"> <el-input type="textarea" v-model="projectForm.description" :rows="3" /> </el-form-item> </el-form> <div slot="footer"> <el-button @click="projectDialogVisible = false">取消</el-button> <el-button type="primary" icon="el-icon-check" @click="saveProject">保存</el-button> </div> </el-dialog> </div> </template> <script> import { listData, getData, addData, updateData } from "@/api/cms/data"; import { getProjectTree, saveProject, updateProject, deleteProject, removeMenusFromProject } from "@/api/cms/articleProject"; import Tinymce from '@/components/Tinymce'; import Treeselect from "@riophae/vue-treeselect"; import "@riophae/vue-treeselect/dist/vue-treeselect.css"; import axios from 'axios'; export default { name: "ProjectManagement", components: { Treeselect, Tinymce }, data() { return { // 项目树相关数据 projectName: '', projectTreeData: [], filteredProjectTreeData: [], refreshTable: true, isExpandAll: true, defaultProps: { children: "children", label: "label" }, // 项目对话框相关 projectDialogVisible: false, projectDialogTitle: '', projectForm: { id: null, name: '', parentId: null, description: '' }, // 文档列表相关 activeView: 'list', // 'list' 或 'detail' docQueryParams: { menuIds: null, title: '', status: '', pageNum: 1, pageSize: 10 }, documentList: [], docTotal: 0, docLoading: false, selectedDocs: [], // 选中的文档 ids: [], // 选中文档ID集合 // 文档详情相关 currentDocument: { id: null, menuId: null, title: '', content: '', status: '0' }, // 当前选中的项目ID(用于删除操作) currentProjectId: null, // 导出相关数据 form: { ids: [], // 导出的文档ID集合 notesExportFlag: 1 // 默认导出详细 } }; }, created() { this.getProjectTree(); }, methods: { // ================= 项目树方法 ================= ellipsis(text, maxLength) { return text.length > maxLength ? text.substring(0, maxLength) + '...' : text; }, toggleExpandAll() { this.refreshTable = false; this.isExpandAll = !this.isExpandAll; this.$nextTick(() => { this.refreshTable = true; }); }, filterNode(value, data) { if (!value) return true; return data.label.toLowerCase().includes(value.toLowerCase()); }, filterProjectTree() { this.$refs.projectTree.filter(this.projectName); }, handleProjectClick(data) { // 保存当前选中的项目ID this.currentProjectId = data.id; // 将menuIds字符串转换为数组 const menuIdsArray = data.menuIds ? data.menuIds.split(',').map(id => id.trim()) : []; // 将数组转换回逗号分隔的字符串用于查询 this.docQueryParams.menuIds = menuIdsArray.join(','); this.getDocumentList(); }, // ================= 项目管理方法 ================= handleAddProject() { this.projectForm = { id: null, name: '', parentId: null, description: '' }; this.projectDialogTitle = '新增项目'; this.projectDialogVisible = true; }, handleAddSubProject(data) { this.projectForm = { id: null, name: '', parentId: data.id, description: '' }; this.projectDialogTitle = '新增子项目'; this.projectDialogVisible = true; }, handleEditProject(data) { this.projectForm = { id: data.id, name: data.name, parentId: data.parentId, description: data.description || '' }; this.projectDialogTitle = '编辑项目'; this.projectDialogVisible = true; }, handleDeleteProject(data) { this.$confirm(`确定删除项目 "${data.name}" 及其所有子项目吗?`, '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { // 使用deleteProject API删除项目 deleteProject(data.id).then(response => { if (response.code === 200) { this.$message.success('删除成功'); this.getProjectTree(); } else { this.$message.error(response.msg || '删除失败'); } }).catch(error => { this.$message.error('删除失败: ' + error.message); }); }); }, saveProject() { this.$refs.projectForm.validate(valid => { if (valid) { const saveMethod = this.projectForm.id ? updateProject : saveProject; saveMethod(this.projectForm).then(response => { if (response.code === 200) { this.$message.success('保存成功'); this.projectDialogVisible = false; this.getProjectTree(); } else { this.$message.error(response.msg || '保存失败'); } }).catch(error => { this.$message.error('保存失败: ' + error.message); }); } }); }, normalizer(node) { return { id: node.id, label: node.name, children: node.children && node.children.length > 0 ? node.children : undefined }; }, // 获取项目树数据 getProjectTree() { getProjectTree().then(response => { if (response.code === 200 && response.data) { // 处理根节点 const rootNode = response.data; // 转换数据结构 this.projectTreeData = this.transformTreeData([rootNode]); this.filteredProjectTreeData = [...this.projectTreeData]; // 默认展开根节点 this.$nextTick(() => { if (this.projectTreeData.length > 0) { this.$refs.projectTree.setCurrentKey(rootNode.id); this.handleProjectClick(rootNode); } }); } else { this.$message.error('获取项目树失败: ' + (response.msg || '未知错误')); } }).catch(error => { console.error("获取项目树失败:", error); this.$message.error('获取项目树失败: ' + error.message); }); }, // 转换数据结构为el-tree需要的格式 transformTreeData(nodes) { if (!nodes || !Array.isArray(nodes)) return []; return nodes.map(node => ({ id: node.id, label: node.name, name: node.name, parentId: node.parentId, menuIds: node.menuIds, description: node.description, createBy: node.createBy, createTime: node.createTime, updateBy: node.updateBy, updateTime: node.updateTime, children: this.transformTreeData(node.children || []), rawData: node })); }, // ================= 文档管理方法 ================= getDocumentList() { if (!this.docQueryParams.menuIds) { return; } this.docLoading = true; listData(this.docQueryParams).then(response => { if (response.code === 200) { this.documentList = response.rows; this.docTotal = response.total; this.selectedDocs = []; // 清空选择 this.ids = []; // 清空ID集合 } else { this.$message.error(response.msg || '获取文档列表失败'); } this.docLoading = false; }).catch(error => { this.$message.error('获取文档列表失败: ' + error.message); this.docLoading = false; }); }, resetDocQuery() { this.docQueryParams.title = ''; this.docQueryParams.status = ''; this.getDocumentList(); }, handleEditDocument(row) { getData(row.id).then(response => { if (response.code === 200) { this.currentDocument = { id: response.data.id, menuId: response.data.menuId, title: response.data.title, content: response.data.content, status: response.data.status }; this.activeView = 'detail'; } else { this.$message.error(response.msg || '获取文档详情失败'); } }).catch(error => { this.$message.error('获取文档详情失败: ' + error.message); }); }, handleDeleteDocument(row) { this.$confirm(`确定从项目中移除文档 "${row.title}" 吗?`, '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { // 使用removeMenusFromProject API从项目中移除文档 // 参数: { projectId: 当前项目ID, menusIds: [文档ID] } const params = { projectId: this.currentProjectId, menusIds: [row.id] // 使用文档ID数组 }; removeMenusFromProject(params).then(response => { if (response.code === 200) { this.$message.success('文档已从项目中移除'); this.getDocumentList(); } else { this.$message.error(response.msg || '移除文档失败'); } }).catch(error => { this.$message.error('移除文档失败: ' + error.message); }); }); }, handleRowClick(row) { this.handleEditDocument(row); }, backToList() { this.activeView = 'list'; }, saveDocument() { const saveMethod = this.currentDocument.id ? updateData : addData; saveMethod(this.currentDocument).then(response => { if (response.code === 200) { this.$message.success('保存成功'); this.getDocumentList(); this.backToList(); } else { this.$message.error(response.msg || '保存失败'); } }).catch(error => { this.$message.error('保存失败: ' + error.message); }); }, // ================= 导出功能方法 ================= // 多选处理 handleSelectionChange(selection) { this.selectedDocs = selection; this.ids = selection.map(item => item.id); }, // 导出文档(单篇) handleExportHttp(row, isDetail) { const params = { ids: row.id, notesExportFlag: isDetail ? 1 : 0 }; this.download( 'cms/data/createArticleOutputHttp', params, `word文档_${row.title}_${new Date().getTime()}.docx`, { timeout: 60000 } ); }, // 批量导出文档 handleBatchExport() { if (this.selectedDocs.length === 0) { this.$message.warning('请选择要导出的文档'); return; } // 弹出选择导出类型的对话框 this.$confirm('请选择导出方式', '提示', { distinguishCancelAndClose: true, confirmButtonText: '导出详细', cancelButtonText: '导出简版', type: 'info' }).then(() => { // 导出详细 this.batchExportHttp(true); }).catch(action => { if (action === 'cancel') { // 导出简版 this.batchExportHttp(false); } }); }, // 批量导出文档实现 batchExportHttp(isDetail) { const params = { ids: this.ids, notesExportFlag: isDetail ? 1 : 0 }; this.download( 'cms/data/createArticleOutputHttp', params, `批量文档_${new Date().getTime()}.zip`, { timeout: 120000 } // 批量导出可能需要更长时间 ); }, // 下载方法实现 download(url, params, fileName, config = {}) { // 显示加载提示 const loading = this.$loading({ lock: true, text: '正在生成文档,请稍候...', spinner: 'el-icon-loading', background: 'rgba(0, 0, 0, 0.7)' }); // 发送请求 axios.get(url, { params: params, responseType: 'blob', timeout: config.timeout || 30000, headers: { 'Authorization': `Bearer ${this.$store.getters.token}` } }) .then(response => { // 创建下载链接 const blob = new Blob([response.data], { type: response.headers['content-type'] }); const downloadUrl = window.URL.createObjectURL(blob); // 创建下载链接 const link = document.createElement('a'); link.href = downloadUrl; link.download = fileName; document.body.appendChild(link); // 触发下载 link.click(); // 清理资源 window.URL.revokeObjectURL(downloadUrl); document.body.removeChild(link); this.$message.success('文档导出成功'); }) .catch(error => { console.error('导出失败:', error); // 处理错误响应 if (error.response) { if (error.response.status === 500) { this.$message.error('导出失败:服务器内部错误'); } else if (error.response.status === 401) { this.$message.error('导出失败:未授权访问'); } else if (error.response.status === 404) { this.$message.error('导出失败:API接口不存在'); } else { this.$message.error(`导出失败:服务器错误 (${error.response.status})`); } } else if (error.message.includes('timeout')) { this.$message.error('导出超时,请稍后再试'); } else { this.$message.error('导出失败:' + (error.message || '未知错误')); } }) .finally(() => { loading.close(); }); }, // 排序格式化方法 sortableFormatter(row, column) { if (this.ids.includes(row.id)) { return 1; } else { return 2; } } } }; </script> <style scoped> .app-container { padding: 20px; background-color: #f5f7fa; } .head-container { padding: 10px; background-color: #ffffff; border-radius: 4px; box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); } .custom-tree-node { flex: 1; display: flex; align-items: center; justify-content: space-between; font-size: 14px; padding-right: 8px; } .tree-node-content { display: flex; align-items: center; } .tree-icon { margin-right: 8px; color: #409EFF; } .node-label { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .node-actions { display: flex; align-items: center; } .doc-list-container { background-color: #ffffff; padding: 20px; border-radius: 4px; box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); } .batch-actions { margin-bottom: 15px; } .doc-detail-container { background-color: #ffffff; padding: 20px; border-radius: 4px; box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); } .doc-header { display: flex; align-items: center; margin-bottom: 20px; } .doc-title { display: flex; align-items: center; margin-left: 15px; margin-bottom: 0; font-size: 18px; color: #303133; } .title-icon { margin-right: 10px; color: #409EFF; } .doc-title .doc-icon { margin-right: 8px; color: #909399; } .el-tree { border: 1px solid #ebeef5; border-radius: 4px; padding: 10px; max-height: 70vh; overflow-y: auto; background-color: #ffffff; box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); } .custom-tree-node .el-button { padding: 4px; margin-left: 5px; } .el-table { margin-top: 10px; border-radius: 4px; overflow: hidden; box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); } .el-form-item { margin-bottom: 18px; } .el-tag { margin: 2px; } /* 响应式调整 */ @media (max-width: 768px) { .el-col-xs-24 { width: 100%; margin-bottom: 20px; } .doc-list-container, .doc-detail-container { padding: 10px; } .doc-header h2 { font-size: 16px; } } </style> 原本代码是这样的,如何改
08-02
以下mrviewer.vue頁面在control-panel增加一個相關病態的切換按鈕,點擊時將mrrelate.vue中的功能載入到relative-area區塊之中。mrrelate.vue參照mrviewer.vue範例代碼中的比對功能,將與DNTag/?format=json比對后得到的字串以原式樣顯示: <template> <div class="container"> <!-- 控制面板 --> <div class="control-panel"> <button @click="fetchData" class="refresh-btn">刷新數據</button> <input v-model="searchQuery" placeholder="搜索..." class="search-input" /> <div class="pagination-controls"> <span>每頁顯示:</span> <select v-model.number="pageSize" class="page-size-select"> <option value="1">1筆</option> <option value="4">4筆</option> <option value="10">10筆</option> </select> <button @click="prevPage" :disabled="currentPage === 1">上一页</button> <span>第</span> <!-- 实时跳转的页码输入框 --> <input type="number" v-model.number="inputPage" min="1" :max="totalPages" class="page-input" @input="handlePageInput"> <span>頁 / 共 {{ totalPages }} 頁</span> <button @click="nextPage" :disabled="currentPage === totalPages">下一頁</button> <span>醫案閱讀器</span> </div> </div> <!-- 主内容区域 --> <div class="content-area"> <!-- 水平排列的数据展示 --> <div class="horizontal-records" v-if="filteredData.length > 0"> <div v-for="(item, index) in paginatedData" :key="item.id" class="record-card"> <div class="record-header"> <h3>醫案 #{{ (currentPage - 1) * pageSize + index + 1 }}</h3> </div> <div class="record-body"> <!-- 使用v-for遍历处理后的字段对象 --> <div v-for="(value, key) in processFieldNames(item)" :key="key" class="record-field"> <div class="field-name">{{ key }}:</div> <div class="field-value"> <!-- 处理dntag字段的特殊显示 --> <div v-if="key === '病態名稱' && Array.isArray(value)" class="dntag-value"> {{ formatDntagValue(value) }} </div> <!-- 其他字段的正常显示 --> <div v-else-if="Array.isArray(value)" class="array-value"> <span v-for="(subItem, subIndex) in value" :key="subIndex"> <span v-html="formatValue(subItem, key)"></span><span v-if="subIndex < value.length - 1">;</span> </span> </div> <div v-else v-html="formatValue(value, key)"></div> </div> </div> </div> </div> </div> <div v-else class="no-data"> 沒有找到匹配的數據 </div> </div> <div class="relative-area"> <span>相關病態</span> </div> </div> </template> <script> export default { name: 'mrviewer', // 必须设置组件名称用于keep-alive data() { return { api1Data: [], api2Data: [], mergedData: [], currentPage: 1, pageSize: 1, searchQuery: '', sortKey: '', sortOrders: {}, inputPage: 1, // 页码输入框绑定的值 // 字段名称映射表 fieldNames: { 'mrcase': '醫案全文', 'mrname': '醫案命名', 'mrposter': '醫案提交者', 'mrlasttime': '最後編輯時間', 'mreditnumber': '編輯次數', 'mrreadnumber': '閱讀次數', 'mrpriority': '重要性', 'dntag': '病態名稱' }, inputTimeout: null, // 用于输入防抖 dnNames: [], // 存储所有病态名称 stateVersion: '1.0' // 状态版本控制 }; }, computed: { filteredData() { const query = this.searchQuery.trim(); // 判斷是否為數字(即搜尋 ID) if (query && /^\d+$/.test(query)) { const idToSearch = parseInt(query, 10); return this.mergedData.filter(item => item.id === idToSearch); } // 一般搜索邏輯 if (!query) return this.mergedData; const lowerQuery = query.toLowerCase(); return this.mergedData.filter(item => { return Object.values(item).some(value => { if (value === null || value === undefined) return false; // 处理数组类型的值 if (Array.isArray(value)) { return value.some(subValue => { if (typeof subValue === 'object' && subValue !== null) { return JSON.stringify(subValue).toLowerCase().includes(lowerQuery); } return String(subValue).toLowerCase().includes(lowerQuery); }); } // 处理对象类型的值 if (typeof value === 'object' && value !== null) { return JSON.stringify(value).toLowerCase().includes(lowerQuery); } return String(value).toLowerCase().includes(lowerQuery); }); }); }, sortedData() { if (!this.sortKey) return this.filteredData; const order = this.sortOrders[this.sortKey] || 1; return [...this.filteredData].sort((a, b) => { const getValue = (obj) => { const val = obj[this.sortKey]; if (Array.isArray(val)) { return JSON.stringify(val); } return val; }; const aValue = getValue(a); const bValue = getValue(b); if (aValue === bValue) return 0; return aValue > bValue ? order : -order; }); }, paginatedData() { const start = (this.currentPage - 1) * Number(this.pageSize); const end = start + Number(this.pageSize); return this.sortedData.slice(start, end); }, totalPages() { return Math.ceil(this.filteredData.length / this.pageSize) || 1; } }, watch: { // 监控分页大小变化 pageSize() { // 重置到第一页 this.currentPage = 1; this.inputPage = 1; // 同步输入框的值 this.saveState(); // 保存状态 }, // 监控当前页码变化 currentPage(newVal) { // 同步输入框的值 this.inputPage = newVal; this.saveState(); // 保存状态 }, // 监控过滤数据变化 filteredData() { // 确保当前页码有效 if (this.currentPage > this.totalPages) { this.currentPage = Math.max(1, this.totalPages); } // 同步输入框的值 this.inputPage = this.currentPage; }, // 监控搜索查询变化 searchQuery() { this.saveState(); // 保存状态 } }, methods: { // 保存当前状态 saveState() { const state = { version: this.stateVersion, currentPage: this.currentPage, pageSize: this.pageSize, searchQuery: this.searchQuery, timestamp: new Date().getTime() }; sessionStorage.setItem('mrviewerState', JSON.stringify(state)); }, // 恢复状态 restoreState() { const savedState = sessionStorage.getItem('mrviewerState'); if (!savedState) return; try { const state = JSON.parse(savedState); // 检查状态版本 if (state.version !== this.stateVersion) { console.warn('状态版本不匹配,跳过恢复'); return; } // 恢复状态 this.currentPage = state.currentPage || 1; this.pageSize = state.pageSize || 1; this.searchQuery = state.searchQuery || ''; this.inputPage = this.currentPage; console.log('医案阅读器状态已恢复'); } catch (e) { console.error('状态恢复失败', e); sessionStorage.removeItem('mrviewerState'); } }, // 清除保存的状态 clearState() { sessionStorage.removeItem('mrviewerState'); }, async fetchData() { try { const api1Response = await fetch("MRInfo/?format=json"); this.api1Data = await api1Response.json(); const api2Response = await fetch("DNTag/?format=json"); this.api2Data = await api2Response.json(); // 提取所有dnname this.dnNames = this.api2Data.map(item => item.dnname).filter(name => name && name.trim()); // 按长度降序排序(确保长字符串优先匹配) this.dnNames.sort((a, b) => b.length - a.length); this.mergeData(); this.currentPage = 1; this.inputPage = 1; // 重置输入框 this.saveState(); // 保存状态 } catch (error) { console.error("獲取數據失敗:", error); alert("數據加載失敗,請稍後重試"); } }, mergeData() { this.mergedData = this.api1Data.map((item) => { const newItem = { ...item }; if (newItem.dntag && Array.isArray(newItem.dntag)) { newItem.dntag = newItem.dntag.map((tagId) => { const matchedItem = this.api2Data.find( (api2Item) => api2Item.id === tagId ); return matchedItem || { id: tagId, dnname: "未找到匹配的數據" }; }); } return newItem; }); this.sortOrders = {}; if (this.mergedData.length > 0) { Object.keys(this.mergedData[0]).forEach(key => { this.sortOrders[key] = 1; }); } }, // 处理字段名称映射 processFieldNames(item) { const result = {}; for (const key in item) { // 使用映射表转换字段名,如果没有映射则使用原字段名 const newKey = this.fieldNames[key] || key; result[newKey] = item[key]; } return result; }, // 高亮匹配文本的方法 highlightMatches(text) { if (!text || typeof text !== 'string' || this.dnNames.length === 0) { return text; } // 创建正则表达式(转义特殊字符) const pattern = new RegExp( this.dnNames .map(name => name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')) .join('|'), 'gi' ); // 替换匹配文本 return text.replace(pattern, match => `<span style="color: rgb(212, 107, 8); font-weight: bold;">${match}</span>` ); }, formatValue(value, fieldName) { if (value === null || value === undefined) return ''; // 医案全文字段特殊处理 if (fieldName === '醫案全文' && typeof value === 'string') { return this.highlightMatches(value); } // 其他字段保持原逻辑 if (typeof value === 'string' && value.startsWith('http')) { return `<a href="${value}" target="_blank">${value}</a>`; } return value; }, // 专门处理dntag字段的显示格式 formatDntagValue(dntagArray) { return dntagArray.map(tagObj => { // 只显示name属性,隐藏id等其他属性 return tagObj.dnname || tagObj.name || '未命名標籤'; }).join(';'); // 使用大写分号分隔 }, sortBy(key) { // 需要从中文名称映射回原始字段名进行排序 const originalKey = Object.keys(this.fieldNames).find( origKey => this.fieldNames[origKey] === key ) || key; this.sortKey = originalKey; this.sortOrders[originalKey] = this.sortOrders[originalKey] * -1; this.saveState(); // 保存状态 }, prevPage() { if (this.currentPage > 1) { this.currentPage--; this.saveState(); // 保存状态 } }, nextPage() { if (this.currentPage < this.totalPages) { this.currentPage++; this.saveState(); // 保存状态 } }, // 实时处理页码输入 handlePageInput() { // 清除之前的定时器 clearTimeout(this.inputTimeout); // 设置新的定时器(防抖处理) this.inputTimeout = setTimeout(() => { this.goToPage(); this.saveState(); // 保存状态 }, 300); // 300毫秒后执行 }, // 跳转到指定页码 goToPage() { // 处理空值情况 if (this.inputPage === null || this.inputPage === undefined || this.inputPage === '') { this.inputPage = this.currentPage; return; } // 转换为整数 const page = parseInt(this.inputPage); // 处理非数字情况 if (isNaN(page)) { this.inputPage = this.currentPage; return; } // 确保页码在有效范围内 if (page < 1) { this.currentPage = 1; } else if (page > this.totalPages) { this.currentPage = this.totalPages; } else { this.currentPage = page; } // 同步输入框显示 this.inputPage = this.currentPage; } }, mounted() { this.restoreState(); // 恢复状态 this.fetchData(); }, // 添加keep-alive生命周期钩子 activated() { console.log('醫案閱讀器被激活'); this.restoreState(); // 恢复状态 }, deactivated() { console.log('醫案閱讀器被停用'); this.saveState(); // 保存状态 } }; </script> <style scoped> .container { max-width: 1200px; margin: 0px; padding: 0px; } .control-panel { margin-bottom: 0px; display: flex; flex-wrap: wrap; gap: 10px; justify-content: flex-end; align-items: center; position: fixed; bottom: 0; left: 0; width: 100%; background-color: #ffd800ff; z-index: 999; padding: 10px 20px; box-sizing: border-box; } .content-area { position: fixed; top: 56px; bottom: 45px; /* 位于底部按钮上方 */ left: 0; width: 70%; background: white; padding: 1px; z-index: 100; overflow-y: auto; } .relative-area { position: fixed; top: 56px; bottom: 45px; /* 位于底部按钮上方 */ right: 0; width: 30%; background: lightblue; padding: 1px; z-index: 100; overflow-y: auto; } .refresh-btn { padding: 4px; background-color: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer; } .refresh-btn:hover { background-color: #45a049; } .search-input { padding: 8px; border: 1px solid #ddd; border-radius: 4px; flex-grow: 1; max-width: 300px; } .pagination-controls { display: flex; align-items: center; gap: 5px; } .page-size-select { padding: 4px; border-radius: 4px; width: 70px; } /* 页码输入框样式 */ .page-input { width: 50px; padding: 4px; border: 1px solid #ddd; border-radius: 4px; text-align: center; } /* 水平记录样式 */ .horizontal-records { display: flex; flex-direction: column; gap: 20px; } .record-card { border: 1px solid #ddd; border-radius: 4px; overflow: hidden; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } .record-header { padding: 12px 16px; background-color: #f5f5f5; border-bottom: 1px solid #ddd; } .record-header h3 { margin: 0; font-size: 1.1em; } .record-body { padding: 16px; } .record-field { display: flex; margin-bottom: 12px; line-height: 1.5; } .record-field:last-child { margin-bottom: 0; } .field-name { font-weight: bold; min-width: 120px; color: #555; } .field-value { flex-grow: 1; display: flex; flex-wrap: wrap; gap: 8px; } .dntag-value { display: flex; flex-wrap: wrap; gap: 8px; } .array-value { display: flex; flex-wrap: wrap; gap: 8px; } .no-data { padding: 20px; text-align: center; color: #666; font-style: italic; } button:disabled { opacity: 0.5; cursor: not-allowed; } </style>
07-19
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值