如何使用Flex AddItemAction effect

本文介绍了一个使用Flex框架创建的应用示例,演示了如何通过AddItemAction和RemoveItemAction来实现数据项的动态添加与删除效果。该示例应用包含一个TileList组件,用于显示动态变化的数据集合。

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

<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<!--
如何使用Flex AddItemAction
MyShareBook.cn 翻译
-->
<mx:Script>
<![CDATA[
import mx.effects.easing.Elastic;
import mx.collections.ArrayCollection;

[Bindable]
private var myDP:ArrayCollection = new ArrayCollection(
['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P']);

private function deleteItem():void {
// As each item is removed, the index of the other items changes.
// So first get the items to delete, then determine their indices
// as you remove them.
var toRemove:Array = [];
for (var i:int = 0; i < tlist0.selectedItems.length; i++)
toRemove.push(tlist0.selectedItems[i]);
for (i = 0; i < toRemove.length; i++)
myDP.removeItemAt(myDP.getItemIndex(toRemove[i]));
}

private var zcount:int = 0;
private function addItem():void {
// Always add the new item after the third item,
// or after the last item if the length is less than 3.
myDP.addItemAt("Z"+zcount++,Math.min(3,myDP.length));
}
]]>
</mx:Script>

<!-- Define a custom data effect as a Sequence effect. -->
<mx:Sequence id="itemsChangeEffect1">
<mx:Blur
blurYTo="12" blurXTo="12"
duration="300"
perElementOffset="150"
filter="removeItem"/>
<mx:Parallel>
<mx:Move
duration="750"
easingFunction="{Elastic.easeOut}"
perElementOffset="20"/>
<mx:RemoveItemAction
startDelay="400"
filter="removeItem"/>
<mx:AddItemAction
startDelay="400"
filter="addItem"/>
<mx:Blur
startDelay="410"
blurXFrom="18" blurYFrom="18" blurXTo="0" blurYTo="0"
duration="300"
filter="addItem"/>
</mx:Parallel>
</mx:Sequence>

<mx:Panel title="AddItemEffect/RemoveItemEffect Example" width="75%" height="75%"
paddingTop="10" paddingLeft="10" paddingRight="10" paddingBottom="10">

<!-- This TileList uses a custom data change effect -->
<mx:TileList id="tlist0"
height="100%" width="100%"
fontSize="18" fontStyle="bold"
columnCount="4" rowCount="4"
direction="horizontal"
dataProvider="{myDP}"
allowMultipleSelection="true"
offscreenExtraRowsOrColumns="4"
itemsChangeEffect="{itemsChangeEffect1}"/>

<mx:Button
label="Delete selected item(s)"
click="deleteItem();"/>
<mx:Button
label="Add item"
click="addItem();"/>

</mx:Panel>
</mx:Application>
<script setup lang="ts"> import { ref, reactive, onMounted, computed } from 'vue' import { ElMessage, ElMessageBox, type FormInstance } from 'element-plus' import { queryPageApi, addApi, queryInfoApi, updateApi, deleteApi } from '@/api/clazz' // 类型定义 interface Clazz { id?: number name: string room: string beginDate: string endDate: string masterId: number | null subject: number masterName?: string status?: string } // 表单引用 const formRef = ref<FormInstance>() // 响应式数据 const loading = ref(false) const dialogVisible = ref(false) const dialogTitle = ref('新增班级') const currentPage = ref(1) const pageSize = ref(10) const total = ref(0) const selectedIds = ref<number[]>([]) // 存储选中的班级ID // 查询条件 const queryParams = reactive({ begin: '', end: '', name: '', }) // 班级表单数据 const clazzForm = reactive<Clazz>({ name: '', room: '', beginDate: '', endDate: '', masterId: null, subject: 1, }) // 班级列表 const clazzList = ref<Clazz[]>([]) // 学科选项 const subjectOptions = [ { label: 'Java', value: 1 }, { label: '前端', value: 2 }, { label: '大数据', value: 3 }, { label: 'Python', value: 4 }, { label: 'Go', value: 5 }, { label: '嵌入式', value: 6 }, ] // 计算是否选择了班级 const hasSelection = computed(() => selectedIds.value.length > 0) // 加载班级数据 const loadClazzData = async () => { loading.value = true try { const res = await queryPageApi( queryParams.begin, queryParams.end, queryParams.name, currentPage.value, pageSize.value, ) if (res.code === 1) { clazzList.value = res.data.rows total.value = res.data.total // 重置选中状态 selectedIds.value = [] } else { ElMessage.error(res.msg || '数据加载失败') } } catch (error) { console.error('加载数据出错:', error) ElMessage.error('网络请求异常') } finally { loading.value = false } } // 处理查询 const handleQuery = () => { currentPage.value = 1 loadClazzData() } // 重置查询 const resetQuery = () => { queryParams.begin = '' queryParams.end = '' queryParams.name = '' handleQuery() } // 打开新增对话框 const openAddDialog = () => { dialogTitle.value = '新增班级' Object.assign(clazzForm, { id: undefined, name: '', room: '', beginDate: '', endDate: '', masterId: null, subject: 1, }) dialogVisible.value = true } // 打开编辑对话框 const openEditDialog = async (id: number) => { dialogTitle.value = '编辑班级' try { const res = await queryInfoApi(id) if (res.code === 1) { Object.assign(clazzForm, res.data) dialogVisible.value = true } else { ElMessage.error('获取班级信息失败') } } catch (error) { console.error('获取班级信息出错:', error) ElMessage.error('网络请求异常') } } // 提交表单 const submitForm = async () => { if (!formRef.value) return try { await formRef.value.validate() if (clazzForm.id) { // 更新 const res = await updateApi(clazzForm) if (res.code === 1) { ElMessage.success('更新成功') dialogVisible.value = false loadClazzData() } else { ElMessage.error(res.msg || '更新失败') } } else { // 新增 const res = await addApi(clazzForm) if (res.code === 1) { ElMessage.success('新增成功') dialogVisible.value = false loadClazzData() } else { ElMessage.error(res.msg || '新增失败') } } } catch (error) { console.error('表单验证失败:', error) } } // 处理选择变化 const handleSelectionChange = (selection: Clazz[]) => { selectedIds.value = selection.map((item) => item.id!) as number[] } // 删除班级(单个或批量) const handleDelete = (ids: number | number[]) => { const deleteIds = Array.isArray(ids) ? ids : [ids] const message = deleteIds.length > 1 ? `确定要删除选中的 ${deleteIds.length} 个班级吗?` : '确定要删除该班级吗?' ElMessageBox.confirm(message, '警告', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning', }) .then(async () => { const res = await deleteApi(deleteIds) if (res.code === 1) { ElMessage.success('删除成功') loadClazzData() } else { ElMessage.error(res.msg || '删除失败') } }) .catch(() => {}) } // 分页变化处理 const handleSizeChange = (val: number) => { pageSize.value = val loadClazzData() } const handleCurrentChange = (val: number) => { currentPage.value = val loadClazzData() } // 初始化加载数据 onMounted(() => { loadClazzData() }) </script> <template> <div class="app-container"> <div class="header-container"> <h2 class="page-title">班级管理</h2> <div class="operation-container"> <!-- 查询表单 --> <el-form :model="queryParams" inline class="query-form"> <el-form-item label="班级名称"> <el-input v-model="queryParams.name" placeholder="请输入班级名称" clearable @keyup.enter="handleQuery" /> </el-form-item> <el-form-item label="开课时间"> <el-date-picker v-model="queryParams.begin" type="date" placeholder="选择开始日期" value-format="YYYY-MM-DD" /> </el-form-item> <el-form-item label="结课时间"> <el-date-picker v-model="queryParams.end" type="date" placeholder="选择结束日期" value-format="YYYY-MM-DD" /> </el-form-item> <el-form-item> <el-button type="primary" @click="handleQuery">查询</el-button> <el-button @click="resetQuery">重置</el-button> </el-form-item> </el-form> <!-- 操作按钮 --> <div class="action-buttons"> <el-button type="primary" @click="openAddDialog"> <i class="el-icon-plus"></i> 新增班级 </el-button> <el-button type="danger" :disabled="!hasSelection" @click="handleDelete(selectedIds)"> <i class="el-icon-delete"></i> 批量删除 </el-button> </div> </div> </div> <!-- 班级表格 --> <el-table :data="clazzList" v-loading="loading" border stripe highlight-current-row @selection-change="handleSelectionChange" class="data-table" > <el-table-column type="selection" width="55" align="center" /> <el-table-column type="index" label="序号" width="80" align="center" /> <el-table-column prop="name" label="班级名称" min-width="150" align="center" /> <el-table-column prop="room" label="教室" width="120" align="center" /> <el-table-column prop="beginDate" label="开课时间" width="120" align="center" /> <el-table-column prop="endDate" label="结课时间" width="120" align="center" /> <el-table-column prop="masterName" label="班主任" width="120" align="center" /> <el-table-column label="学科" width="100" align="center"> <template #default="{ row }"> <el-tag effect="plain" type="info"> {{ subjectOptions.find((item) => item.value === row.subject)?.label || '未知' }} </el-tag> </template> </el-table-column> <el-table-column label="状态" width="100" align="center"> <template #default="{ row }"> <el-tag :type="row.status || '未开始'"> {{ row.status || '未开始' }} </el-tag> </template> </el-table-column> <el-table-column label="操作" width="180" align="center" fixed="right"> <template #default="{ row }"> <el-button type="primary" size="small" @click="openEditDialog(row.id)" ><el-icon><edit /></el-icon>编辑</el-button > <el-button type="danger" size="small" @click="handleDelete(row.id)" ><el-delete><edit /></el-delete>删除</el-button > </template> </el-table-column> </el-table> <!-- 分页组件 --> <div class="pagination-container"> <el-pagination v-model:current-page="currentPage" v-model:page-size="pageSize" :page-sizes="[5, 10, 20, 50]" layout="total, sizes, prev, pager, next, jumper" :total="total" @size-change="handleSizeChange" @current-change="handleCurrentChange" /> </div> <!-- 新增/编辑对话框 --> <el-dialog v-model="dialogVisible" :title="dialogTitle" width="600px" @closed="formRef?.resetFields()" > <el-form ref="formRef" :model="clazzForm" label-width="100px" :rules="{ name: [{ required: true, message: '请输入班级名称', trigger: 'blur' }], room: [{ required: true, message: '请输入教室', trigger: 'blur' }], beginDate: [{ required: true, message: '请选择开课时间', trigger: 'change' }], endDate: [{ required: true, message: '请选择结课时间', trigger: 'change' }], subject: [{ required: true, message: '请选择学科', trigger: 'change' }], }" > <el-form-item label="班级名称" prop="name"> <el-input v-model="clazzForm.name" placeholder="请输入班级名称" /> </el-form-item> <el-form-item label="教室" prop="room"> <el-input v-model="clazzForm.room" placeholder="请输入教室" /> </el-form-item> <el-form-item label="开课时间" prop="beginDate"> <el-date-picker v-model="clazzForm.beginDate" type="date" placeholder="选择开课日期" value-format="YYYY-MM-DD" style="width: 100%" /> </el-form-item> <el-form-item label="结课时间" prop="endDate"> <el-date-picker v-model="clazzForm.endDate" type="date" placeholder="选择结课日期" value-format="YYYY-MM-DD" style="width: 100%" /> </el-form-item> <el-form-item label="学科" prop="subject"> <el-select v-model="clazzForm.subject" placeholder="请选择学科" style="width: 100%"> <el-option v-for="item in subjectOptions" :key="item.value" :label="item.label" :value="item.value" /> </el-select> </el-form-item> <el-form-item label="班主任ID"> <el-input v-model.number="clazzForm.masterId" type="number" placeholder="请输入班主任ID" /> </el-form-item> </el-form> <template #footer> <el-button @click="dialogVisible = false">取消</el-button> <el-button type="primary" @click="submitForm">确定</el-button> </template> </el-dialog> </div> </template> <style scoped> .app-container { padding: 20px; background-color: #f5f7fa; } .header-container { background: #fff; border-radius: 8px; padding: 20px; margin-bottom: 20px; box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05); } .page-title { margin: 0 0 20px 0; padding-bottom: 15px; border-bottom: 1px solid #ebeef5; color: #303133; font-size: 18px; } .operation-container { display: flex; flex-direction: column; gap: 15px; } .query-form { display: flex; flex-wrap: wrap; gap: 10px; } .action-buttons { display: flex; gap: 10px; } .data-table { width: 100%; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05); } .pagination-container { display: flex; justify-content: flex-end; margin-top: 20px; padding: 15px; background: #fff; border-radius: 8px; box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05); } .el-form-item { margin-bottom: 18px; } .el-table :deep(.el-table__cell) { padding: 12px 0; } .el-tag { font-weight: 500; } </style> 优化一下整体代码逻辑,使代码更简洁,优化一下样式,美化一下页面
07-21
<!-- 工艺模板 --> <template> <el-container v-loading="loading"> <!-- 工具栏 --> <el-header> <!-- 左侧工具栏 --> <!-- <div class="left-panel"> <el-button type="primary" icon="el-icon-plus" @click="edit()">新增</el-button> </div> --> <!-- 右侧工具栏 --> <div class="right-panel"> <div class="right-panel-search"> <el-input v-model="procedureData.name" placeholder="名称" clearable /> <el-button icon="el-icon-refresh" @click="refreshProcedure">查看所有</el-button> <el-button type="primary" icon="el-icon-search" @click="getProcedure(0)">搜索</el-button> <el-button type="primary" icon="el-icon-plus" @click="editProcedure()">新增</el-button> </div> </div> </el-header> <!-- 列表 --> <el-main class="nopadding"> <scTable :data="procedureData.data" style="width: 100%" row-key="id" :getList="getProcedure" hidePagination :header-cell-style="{ background: '#F1F3F0', color: '#7B7571' }"> <el-table-column label="ID" prop="id" width="50"></el-table-column> <el-table-column label="编号" prop="rule_number"></el-table-column> <el-table-column label="工艺名称" prop="name"></el-table-column> <el-table-column label="操作人" prop="user_name"></el-table-column> <el-table-column label="创建时间" prop="create_time"></el-table-column> <el-table-column label="操作" fixed="right" width="150"> <template #default="scope"> <el-button plain type="primary" size="small" @click="editProcedure(scope.row)">编辑</el-button> <el-button plain type="primary" size="small" @click="delProcedure(scope.row)">删除</el-button> </template> </el-table-column> <template #pagination> <el-pagination layout="prev, pager, next, total, sizes" :current-page="procedureData.current_page" :page-size="procedureData.per_page" :total="procedureData.total" :page-sizes="[20, 50, 100]" @size-change=" (num) => { procedureData.per_page = num; getProcedure(1); } " @current-change="getProcedure" /> </template> </scTable> </el-main> <!-- 工艺工序新增编辑模板 --> <el-dialog v-model="ditVisibleGy" :close-on-click-modal="false" width="80%" :fullscreen="isFullscreen" class="dialogGxgy"> <!-- 自定义标题栏 --> <template #title> <div class="dialogtitle"> <h4 style="font-size: 17px;">{{ procedureForm.id ? '编辑工艺模板' : '新增工艺模板' }}</h4> <div class="dialog-icons"> <!-- 全屏图标 --> <el-icon @click="toggleFullscreen" class="fullscreen-icon"> <component :is="isFullscreen ? ZoomOut : FullScreen" /> </el-icon> </div> </div> </template> <el-header> <div class="gybs"> <div class="gyheader"> <h4>模板名称:</h4> <el-input v-model="procedureForm.name" placeholder="请输入模板名称" style="width: 200px" /> </div> <!-- <el-upload class="sc-file-select__upload" :action="action" multiple :show-file-list="false" :accept="accept" :before-upload="uploadBefore" :on-success="uploadSuccess" :on-error="uploadError" :headers="uploadHeaders" :data="uploadData"> <el-button type="primary" icon="el-icon-upload">上传图纸</el-button> </el-upload> --> <!-- 图片列表,横向滚动 --> </div> </el-header> <el-container v-loading="gymbLoading"> <el-aside width="250px" height="400px"> <el-card class="card_gy"> <!-- 拖拽排序 --> <div class="tzpx"> <fcDraggable v-model="itemsgy" :sort="true" handle=".icon-drag" itemKey="gongxumingcheng" direction="vertical" :animation="300" @end="onDragEnd"> <template #item="{ element, index }"> <div class="slx"> <div class="sortable-item" @click="selectItem(element)" :class="{ 'selected': selectedItem === element }"> <div class="item_left"> <i class="fc-icon icon-drag"></i> </div> <div class="item_content"> {{ element.gongxumingcheng }} </div> <div class="item_right"> <i class="fc-icon icon-delete" @click.stop="removeField(index)"></i> </div> </div> </div> </template> </fcDraggable> <el-empty description="暂无工序" :image-size="120" v-if="itemsgy.length === 0" /> </div> <el-button-group class="btgp"> <el-button type="primary" @click="addProcedure">添加</el-button> <el-button type="primary" @click="changeGx">选择工序</el-button> </el-button-group> </el-card> </el-aside> <el-container class="" v-loading="bomLoading" v-if="itemsgy.length > 0"> <el-header> <H3 class="ht">工序参数</H3> </el-header> <div class="formbody"> <div class="gyform" v-if="isFormFilled"> <form-create v-model:api="api" :rule="formRule" :option="formCreateOptions" /> </div> <el-empty description="请先选择工序" v-show="!isFormFilled" /> <div class="gycs" v-if="itemsgy.length > 1"> <div class="dowt"> <H3>工艺路线图</H3> </div> <div class="route"> <el-steps :active="itemsgy.length" :space="150" align-center finish-status="primary" process-status="primary"> <el-step v-for="(item, index) in itemsgy" :key="index" :title="item.gongxumingcheng" /> </el-steps> </div> </div> </div> </el-container> <div v-else style="width: 100%;"> <el-empty description="请先选择工序模板" /> </div> <el-aside width="200px" class="lf_aside"> <div class="image-preview-list"> <el-scrollbar height="400px"> <el-upload class="uploadTz" :action="action" multiple :show-file-list="false" :on-preview="handlePictureCardPreview" :before-upload="uploadBefore" :on-success="uploadSuccess" :on-error="uploadError" :headers="uploadHeaders" :data="uploadData" > <el-button type="primary">上传图纸</el-button> </el-upload> <div class="upbox"> <div class="wdg" v-for="(item, index) in fileList" :key="index"> <div class="wg_lf" @click="handlePictureCardPreview(item)"> <el-icon size="18"> <DocumentRemove /> </el-icon> <el-tooltip class="box-item" effect="dark" :content="item.original_name" placement="top"> <div class="weds">{{ item.original_name }}</div> </el-tooltip> </div> <div> <el-button type="danger" :icon="Delete" circle size="small" @click="delImg(index)"/> </div> </div> </div> <el-dialog v-model="dialogVisibleImg"> <img w-full :src="dialogImageUrl" alt="Preview Image" /> </el-dialog> </el-scrollbar> </div> </el-aside> </el-container> <template #footer> <span class="dialog-footer"> <el-button @click="ditVisibleGy = false">关闭</el-button> <el-button type="primary" @click="submitProcesses" :loading="gyloading"> 保存工艺模板 </el-button> </span> </template> </el-dialog> <!-- 选择工序弹窗 --> <el-dialog v-model="dialogVisiblexzgx" title="选择工序" width="60%"> <el-container> <el-aside width="200px"> <el-tree ref="dic" class="menu" node-key="id" :data="gxtypeList" :props="{ label: 'name', }" :highlight-current="true" :expand-on-click-node="false" :filter-node-method="dicFilterNode" @node-click="selectgxClass"> <template #default="{ node, data }"> <span class="custom-tree-node"> <span class="label">{{ node.label }}</span> <span class="code">{{ data.code }}</span> </span> </template> </el-tree> </el-aside> <el-main v-loading="tableloading"> <el-header v-if="processesList.data.length > 0"> <div class="right-panel"> <el-input v-model="processesList.name" placeholder="标题" class="titlese" /> <el-button icon="el-icon-refresh" @click="refresprocessesList">查看所有</el-button> <el-button type="primary" icon="el-icon-search" @click="getTemplate(1)"></el-button> </div> </el-header> <el-main> <scTable :data="processesList.data" ref="tableRef" style="width: 100%" row-key="id" :getList="getitem" @row-click="handleRowClick" @selection-change="handleSelectionChange"> <el-table-column type="selection" width="50"></el-table-column> <el-table-column prop="id" label="ID" width="50" /> <el-table-column prop="gongxumingcheng" label="工序名称" fixed="right" /> <el-table-column prop="user_name" label="创建人" fixed="right" /> <el-table-column prop="create_time" label="创建时间" fixed="right" /> <template #pagination> <el-pagination layout="prev, pager, next, total, sizes" :current-page="processesList.current_page" :page-size="processesList.per_page" :total="processesList.total" :page-sizes="[20, 50, 100]" @size-change=" (num) => { processesList.per_page = num; getTemplate(1); } " @current-change="getTemplate" /> </template> </scTable> </el-main> </el-main> </el-container> <template #footer> <div class="dialog-footer"> <el-button @click="dialogVisiblexzgx = false">取消</el-button> <el-button type="primary" @click="selectProcess" :loading="selectProcessLoading"> 确定 </el-button> </div> </template> </el-dialog> <!-- 新增工序 公共页面弹窗新增 --> <el-dialog v-model="ditVisibleProcedure" :close-on-click-modal="false" width="70%" title="工序新增"> <form-create v-model:api="api" :rule="formRuleAdd" :option="formCreateOptions" /> </el-dialog> </el-container> </template> <script setup> import { ref, watch, reactive, nextTick } from "vue"; import request from "@/utils/httpRequest"; import { ElMessage,ElMessageBox } from "element-plus"; import fcDraggable from "vuedraggable"; import formCreate from "@form-create/element-ui"; import { getFormRuleDescription } from "@/components/formCreate/src/utils/index"; // js工具 import utils from "./components/utils"; import tool from "@/utils/tool"; import config from "@/config" import { DocumentRemove, Delete } from "@element-plus/icons-vue"; // 页面loading let loading = ref(false); //上传地址 const token = tool.cookie.get("TOKEN"); const action = ref(config.BASEURL + "/system/upload/upload"); const fileList = ref([]); //上传文件列表 const dialogVisibleImg = ref(false) const dialogImageUrl = ref('') //设置请求头 const uploadHeaders = ref({ token: token, // 添加 token 到 headers }); // 设置额外字段 const uploadData = ref({ is_annex: 1, // 添加额外字段 is_annex group_id: 0, }); // 列表数据 const procedureData = ref({ current_page: 1, per_page: 10, total: 0, data: [], name: '',//名称查询 }) // #region 定义工艺模板数据字段 const itemsgy = ref([])//工序列表 const formRule = ref([])//formcreate 组件 const selectedItem = ref({})// 记录当前选中工序的索引 const processLoading = ref(false)//loading const isEditor = ref(false)//用于判断是否走编辑接口 const ditVisibleGy = ref(false)//工艺模板弹窗 const formCreateOptions = ref({});//表单组件参数 //工艺form const procedureForm = ref({ good_id: '',//绑定产品id name: '',//工艺名称 // type_id: 0,//工艺分类 goods_name: '',//绑定产品名称 item: ''//工序节点 }); // #endregion // #region 数据列表 function refreshProcedure() { procedureData.value.name = '' getProcedure() } async function getProcedure(page = 1) { if (page) { procedureData.value.current_page = page; } try { loading.value = true; let data = await request.post("/system/workmanship.workmanship_template/index", { page: procedureData.value.current_page, limit: procedureData.value.per_page, name: procedureData.value.name }); procedureData.value.current_page = data.data.current_page procedureData.value.total = data.data.total procedureData.value.data = data.data.data loading.value = false; } catch (error) { loading.value = false; } } getProcedure() // #endregion // #region 新增编辑 //点击新增 编辑 async function editProcedure(row) { itemsgy.value = [] formRule.value = [] selectedItem.value = {} fileList.value = [] if (row) { try { loading.value = true; let data = await request.post("/system/workmanship.workmanship_template/read", { id: row.id }); loading.value = false if( data.data.data.file){ fileList.value = data.data.data.file } procedureForm.value = { // goods_id: data.data.data.goods_id,//绑定产品id name: data.data.data.name,//工艺名称 // type_id: '',//工艺分类 // goods_name: data.data.data.goods_name,//绑定产品名称 id: row.id } itemsgy.value = data.data.data.item for (const item of itemsgy.value) { try { let data = await request.post("/system/workmanship.workmanship_template/getItem", { id: item.id }); //value是组件的值 let content = formCreate.parseJson(data.data.form.content); const value = utils.decodeFormat(content, data.data.item, 2); item.options = value item.option = content //options是页面参数配置 formCreate.parseJson // const options = (data.data.form.options); const options = formCreate.parseJson(data.data.form.options) formCreateOptions.value = options formCreateOptions.value.submitBtn.show = false loading.value = false } catch (error) { loading.value = false; } } isEditor.value = true processLoading.value = false } catch (error) { processLoading.value = false } } else { itemsgy.value = [] procedureForm.value = { name: '',//工艺名称 // type_id: '',//工艺分类 goods_name: 24,//绑定产品名称 item: '',//工序节点 id: '' } isEditor.value = false } ditVisibleGy.value = true; } // #endregion // #region 获取工序分类 const dialogVisiblexzgx = ref(false); const gymbLoading = ref(false) const gxcclist = ref([])//存储当前选中的工序列表 const tableRef = ref(null); // 绑定 scTable 组件 const gxId = ref('')//工序id // 打开工序选择弹窗,并获取工序类型数据= function changeGx() { getgxtype() } // 获取工序模板分类数据(工序类别) async function getgxtype() { try { gymbLoading.value = true let data = await request.post("/system/dictionary/getChildListByCode", { code: 'gongxu_type' }); gxtypeList.value = data.data gxcclist.value = [] dialogVisiblexzgx.value = true; tableRef.value.clearSelection(); // 清空选中 gymbLoading.value = false; } catch (error) { gymbLoading.value = false; } } const gxtypeList = ref([])//已选中的工序 列表多选 //选择工序分类 function selectgxClass(row) { gxId.value = row.value getTemplate(1) } // #endregion // #region 获取工序模板 const tableloading = ref(false) const processesList = ref({ current_page: 1, per_page: 10, total: 0, data: [], name: '' })//工序模板列表 //工序form表单 const formGy = reactive({ workmanship_id: '',//工艺id name: '',//工序名称 piece_type: '',//计件方式 dept: '',//报工部门 process_quota: '',//工序定额1启用2停用 price_type: '',//计酬方式1计件2计工时 process_price: 0,//工序单价 deduction: 0,//不合格扣款 sort: 0,//排序 }); const bomLoading = ref(false) // 根据工序分类 ID 获取对应的工序列表 async function getTemplate(page = 1) { tableloading.value = true if (page) { processesList.value.current_page = page; } let data = await request.post("/system/processes.processes_template/index", { gongxufenlei_ids: gxId.value, limit: processesList.value.per_page, page: processesList.value.current_page, name: processesList.value.name }); tableloading.value = false processesList.value.current_page = data.data.current_page processesList.value.total = data.data.total // 更新工序列表 processesList.value.data = data.data.data nextTick(() => { recoverSelection(); // 拉完数据恢复勾选 }); } // 监听工序表格的多选事件,并更新已选中的工序数据 function handleSelectionChange(selection) { // gxcclist.value = selection // ⚡ selection 是当前页面选中的 // 我们需要同步到 gxcclist 里面,保证全局勾选 // 先删除本页数据在 gxcclist 里面的 const pageIds = processesList.value.data.map(item => item.id); gxcclist.value = gxcclist.value.filter(item => !pageIds.includes(item.id)); // 再把最新的 selection 加进去 gxcclist.value = [...gxcclist.value, ...selection]; } // 恢复勾选 function recoverSelection() { if (!tableRef.value) return; const tableData = processesList.value.data tableData.forEach((row) => { const isSelected = gxcclist.value.some(item => item.id === row.id) || itemsgy.value.some(item => item.id === row.id); tableRef.value.toggleRowSelection(row, isSelected); }); } // 是否有数据 const isFormFilled = ref(false); function handleRowClick(row) { tableRef.value.toggleRowSelection(row); } // 监听 formGy 变化 watch( formGy, (newVal) => { isFormFilled.value = Object.values(newVal).some( (value) => value !== "" && value !== null && value !== undefined ); }, { deep: true } ); const selectProcessLoading = ref(false)//loading 按钮 // 确认选择工序,并关闭弹窗 async function selectProcess() { itemsgy.value = gxcclist.value refreshGx() dialogVisiblexzgx.value = false } // #endregion //#region 刷新工序展示列表最新数据 async function refreshGx() { for (const item of itemsgy.value) { let form = {} const res = await request.post("/system/crud/read", { crud_id: 105, id: item.id }); // 格式化表单数据 const value = utils.decodeFormat( formCreate.parseJson(res.data.form.content), res.data.data, 2 ); // 表单渲染规则 form = value; const options = formCreate.parseJson(res.data.form.options); //默认给子表加上一条 form.forEach((item) => { if (item.field) { if (item.field.includes("sub_")) { if (item.value.length === 0) { item.value = [{}]; } } } }); // 编辑时删除默认数据,转而使用表单数据 // delete options.form; // 获取所有数据组件参数 const formData = formCreate.parseJson( formCreate.toJson(getFormRuleDescription(form)) ); formCreateOptions.value = options formCreateOptions.value.submitBtn.show = false // 整理格式 const postData = utils.formatForm(formData); // const psData = formData // **根据 id 匹配 itemsgy.value 数组的元素,并存入新字段 `options`** const targetItem = itemsgy.value.find(el => el.id === item.id); if (targetItem) { targetItem.options = postData; // 存入 options 字段 targetItem.option = form } } } // 记录用户点击的工序项(用于高亮显示) async function selectItem(item) { bomLoading.value = true selectedItem.value = item; // 更新选中状态 formRule.value = item.option;//选中 赋给表单 isFormFilled.value = true bomLoading.value = false } //删除对应已选工序模板 function removeField(index) { itemsgy.value.splice(index, 1); } // #endregion //#region 新增工序 弹出公共页面的新增 const ditVisibleProcedure = ref(false) const api = ref({}) const formRuleAdd = ref([]);//用于新增模板 async function addProcedure() { let res = await request.post("/system/crud/getCrudForm", { crud_id: 105 }); // 表单渲染规则 formRuleAdd.value = formCreate.parseJson(res.data.content) // 表单配置项 formCreateOptions.value = { onSubmit: async () => { // 表单验证通过后的提交方法 // 获取所有数据组件参数 let formData = formCreate.parseJson( formCreate.toJson(getFormRuleDescription(formRuleAdd.value)) ); // 整理格式 let postData = utils.formatForm(formData); await request.post("/system/crud/add", { crud_id: 105, form_data: formCreate.toJson(postData) }); ElMessage({ type: "success", message: "操作成功", }); ditVisibleProcedure.value = false }, ...formCreate.parseJson(res.data.options) }; ditVisibleProcedure.value = true } // #endregion const imageUrls = ref('') //#region 提交新增工艺 const gyloading = ref(false)//保存按钮loading async function submitProcesses() { if (gyloading.value) { return } // ✅ 防止双击或重复点击 if (!procedureForm.value.name) { ElMessage.error('请先填写工艺名称') return; } let newArray = []; // 先定义 newArray,确保在整个作用域内可访问 if (itemsgy.value.length > 0) { // 处理 itemsgy.value 数组,将 options 转换为 option 并删除 options 字段 newArray = itemsgy.value.map(item => { // 获取所有数据组件参数 let formData = formCreate.parseJson( formCreate.toJson(getFormRuleDescription(item.option)) ); const newItem = { ...item, option: utils.formatForm(formData)// 将 options 字段格式化后存入 option }; delete newItem.options;// 删除原 options 字段 return newItem;// 返回处理后的新对象 }) } if (fileList.value.length > 0) { // 提取所有上传图片的 URL imageUrls.value = fileList.value.map(file => file.full_path).join(','); } try { // 发送 POST 请求,将数据提交到后端接口 // let data = await request.post("/system/workmanship.workmanship_template/add", ); gymbLoading.value = true gyloading.value = true let url = procedureForm.value.id ? "/system/workmanship.workmanship_template/update" : "/system/workmanship.workmanship_template/add"; // 尝试提交 let data = await request.post(url, { name: procedureForm.value.name, // 工艺模板名称 // goods_id: procedureForm.value.goods_id, // 关联的商品 ID goods_name: procedureForm.value.goods_name, // 关联的商品名称 item: JSON.stringify(newArray), // 处理后的工艺项数据,转换为 JSON 字符串 id: procedureForm.value.id, file: imageUrls.value }); if (data.code === 1) { ElMessage({ message: '操作成功', type: 'success', }) await getProcedure() gymbLoading.value = false gyloading.value = false ditVisibleGy.value = false } } catch (error) { // positionLoading.value = false; } } // #endregion //#region 上传附件 // 上传之前的处理 const uploadBefore = (file) => { const isImage = file.type.startsWith('image/'); const isSizeOk = file.size / 1024 / 1024 < 20; // 限制文件大小最大为 5MB if (!isImage) { ElMessage.error('只能上传图片文件!') return false; } if (!isSizeOk) { ElMessage.error('图片大小不能超过 5MB!') return false; } gymbLoading.value = true; // 开启 loading return true; }; // 预览图片 const handlePictureCardPreview = (file) => { dialogImageUrl.value = file.full_path; // 设置预览的图片地址 dialogVisibleImg.value = true; // 显示对话框 }; //上传成功的回调 const uploadSuccess = (response) => { fileList.value.push({ original_name: response.data.original_name, path: response.data.fullurl, // 带域名的完整路径 full_path: response.data.url // 相对路径 }) ElMessage({ type: "success", message: "操作成功", }); gymbLoading.value = false }; // 上传失败的回调 const uploadError = (error, file) => { console.error("上传失败:", error, file); }; // 处理文件删除 // const handleRemove = (file, files) => { // fileList.value = files; // 更新文件列表 // ElMessage.info(`已删除文件: ${file.name}`); // }; //移除上传图纸 function delImg (index){ fileList.value.splice(index, 1); } // #endregion //#region 删除工艺模板 async function delProcedure(row) { ElMessageBox.confirm("确定要删除吗?这可能导致该数据无法找回", "警告", { confirmButtonText: "确定", cancelButtonText: "取消", type: "warning", }) .then(async () => { try { loading.value = true; await request.post("/system/workmanship.workmanship_template/delete", { id: row.id, }); ElMessage({ type: "success", message: "操作成功", }); getProcedure(); } catch (error) { loading.value = false; } }) .catch(() => { }); } // #endregion </script> <style scoped> .item { cursor: pointer; float: left; background: #fff; width: 100px; height: 100px; line-height: 100px; text-align: center; margin: 0 15px 15px 0; border: 1px solid #e6e6e6; display: block; transition: all 0.3s; /* 添加过渡效果 */ } /* 选中时的样式 */ .selected { border-color: #409eff; /* 选中时的边框颜色 */ background-color: #e6f7ff; /* 选中时的背景颜色 */ } .cwxzlist { display: flex; } .changeCheckbox { display: block; margin-bottom: 20px; } .addwarehouse { margin-right: 10px; } :deep(.dialogGxgy .el-dialog__headerbtn) { height: 57px !important; } .dialogtitle { display: flex; justify-content: space-between; align-items: center; } .fullscreen-icon { cursor: pointer; font-size: 18px; color: #606266; transition: color 0.2s; } .fullscreen-icon:hover { color: #409eff; } .dialog-icons { display: flex; align-items: center; gap: 10px; } .gybs { display: flex; align-items: center; width: 100%; } .gyheader { margin-right: 20px; display: flex; align-items: center; width: 24%; } .card_gy { margin: 10px; min-height: 500px; position: relative; } ::v-deep.card_gy .el-card__body { width: 100%; } .tzpx { height: 400px; overflow-y: scroll; scrollbar-width: none; /* Firefox 隐藏滚动条 */ } .slx { display: flex; } .sortable-item { width: 100%; padding: 15px 20px; background: #ffffff; border: 1px solid #e4e7ed; border-radius: 8px; cursor: move; margin-bottom: 12px; transition: all 0.3s ease; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); display: flex; align-items: center; justify-content: center; font-size: 14px; color: #333; } .sortable-item:hover { background-color: #f5f5f5; border-color: #dcdfe6; color: #409eff; } .sortable-item:active { transform: scale(0.98); } .sortable-item .drag-handle { cursor: move; font-size: 18px; color: #999; margin-right: 12px; } .sortable-item .drag-handle:hover { color: #409eff; } .selected { background: #409eff; /* 选中状态变蓝色 */ color: white; font-weight: bold; } .item_left { margin-right: 8px; } .item_content { flex: 1; /* 占据中间可用空间 */ overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } .item_right { margin-left: 8px; margin-left: auto; /* 自动将右侧图标推到最右 */ cursor: pointer; } .btgp { display: flex; justify-content: center; position: absolute; bottom: 3%; left: 50%; transform: translateX(-50%); } .gyform { padding: 10px; } .dowt { display: flex; justify-content: space-between; align-items: center; border-top: 1px solid #e4e7ed; border-bottom: 1px solid #e4e7ed; padding: 12px 15px; } .route { margin-top: 30px; } .image-preview-list { width: 100%; text-align: center; padding-top: 30px; /* overflow-x: scroll; */ /* overflow: scroll; */ } .image-preview-list::-webkit-scrollbar { display: none; /* Safari 和 Chrome 隐藏滚动条 */ } /* ::v-deep .el-upload-list { display: flex; } */ .lf_aside { border-left: 1px solid #e4e7ed; display: flex; justify-content: center; } .upbox { width: 100%; display: flex; /* flex-wrap: wrap; */ justify-content: center; flex-direction: column-reverse; align-items: center; } .wdg { width: 90%; border: 1px solid #e4e7ed; height: 50px; border-radius: 5px; display: flex; align-items: center; padding: 0 10px; justify-content: space-between; margin-top: 10px; } .wg_lf { display: flex; align-items: center; cursor: pointer; } .weds { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; width: 80px; display: flex; margin-left: 5px; } </style> 这是我的代码 我的代码现在 handleSelectionChange 勾选列表有问题 如果我勾选多个 然后重新获取列表就会加载不上已勾选的状态
最新发布
08-20
请你帮我把这个运行成可下载的PDF:<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Research Hypothesis: MgSO₄ in Chemotherapy-Induced Neuropathic Pain</title> <script src="https://cdn.jsdelivr.net/npm/mermaid@10.6.1/dist/mermaid.min.js"></script> <style> body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background-color: #f8f9fa; margin: 0; padding: 20px; display: flex; flex-direction: column; align-items: center; } .container { max-width: 1200px; width: 100%; background-color: white; border-radius: 10px; box-shadow: 0 4px 20px rgba(0,0,0,0.1); padding: 30px; margin-top: 20px; } header { text-align: center; margin-bottom: 30px; border-bottom: 2px solid #3498db; padding-bottom: 20px; } h1 { color: #2c3e50; margin-bottom: 10px; } .subtitle { color: #7f8c8d; font-size: 1.2em; margin-top: 0; } .diagram-container { width: 100%; min-height: 500px; background-color: #f5f7fa; border-radius: 8px; padding: 20px; margin: 20px 0; } .legend { display: flex; justify-content: center; flex-wrap: wrap; gap: 15px; margin: 20px 0; padding: 15px; background-color: #e8f4fc; border-radius: 8px; } .legend-item { display: flex; align-items: center; gap: 8px; font-size: 14px; } .color-box { width: 20px; height: 20px; border-radius: 4px; } .key-points { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px; margin-top: 30px; } .card { background-color: #f8f9fa; border-left: 4px solid #3498db; padding: 15px; border-radius: 4px; } .card h3 { margin-top: 0; color: #2c3e50; } footer { text-align: center; margin-top: 30px; color: #7f8c8d; font-size: 0.9em; padding-top: 20px; border-top: 1px solid #ecf0f1; } @media print { body { padding: 0; margin: 0; } .container { box-shadow: none; padding: 10px; } .no-print { display: none; } } </style> </head> <body> <div class="container"> <header> <h1>Research Hypothesis: MgSO₄ in Chemotherapy-Induced Neuropathic Pain (CINP)</h1> <p class="subtitle">Mechanism of Action and Protective Effects</p> </header> <div class="diagram-container"> <div class="mermaid"> flowchart TD %% Chemotherapy Induction A[Oxaliplatin<br>Chemotherapy] --> B %% Core Mechanism Pathway subgraph B[Chemotherapy-Induced Neuropathic Pain] direction TB C[TNF-α Release] --> D[TNFR1 Activation] D --> E[Fyn Kinase Activation] E --> F[NR2B Phosphorylation<br>(pTyr1472)] F --> G[NMDA Receptor<br>Hyperactivation] G --> H[Ca²⁺ Influx] H --> I[Neuronal Apoptosis] H --> J[Autophagy Flux<br>Disruption] I --> K[Neuropathic Pain] J --> K end %% Magnesium Intervention L[MgSO₄ Intervention] -->|Inhibits NF-κB Pathway| C L -->|Blocks Ion Channel| G %% Pain Outcome K --> M((Persistent<br>Neuropathic Pain)) %% Style enhancements classDef chem fill:#f8d7da,stroke:#721c24,color:#721c24; classDef process fill:#cfe2ff,stroke:#052c65,color:#052c65; classDef effect fill:#fff3cd,stroke:#856404,color:#856404; classDef pain fill:#d1ecf1,stroke:#0c5460,color:#0c5460; classDef intervention fill:#d4edda,stroke:#155724,color:#155724; class A chem; class B process; class C,D,E,F,G process; class H,I,J effect; class K,M pain; class L intervention; </div> </div> <div class="legend"> <div class="legend-item"> <div class="color-box" style="background-color: #f8d7da;"></div> <span>Chemotherapy Induction</span> </div> <div class="legend-item"> <div class="color-box" style="background-color: #cfe2ff;"></div> <span>Core Molecular Processes</span> </div> <div class="legend-item"> <div class="color-box" style="background-color: #fff3cd;"></div> <span>Cellular Effects</span> </div> <div class="legend-item"> <div class="color-box" style="background-color: #d1ecf1;"></div> <span>Pain Outcome</span> </div> <div class="legend-item"> <div class="color-box" style="background-color: #d4edda;"></div> <span>MgSO₄ Intervention</span> </div> </div> <div class="key-points"> <div class="card"> <h3>Core Pathogenic Mechanism</h3> <p>The "TNF-α → pNR2B-NMDA → Cellular Damage" axis is central to chemotherapy-induced neuropathic pain development:</p> <ul> <li>TNF-α activates TNFR1 receptors</li> <li>Fyn kinase phosphorylates NR2B subunit (pTyr1472)</li> <li>NMDA receptor hyperfunction causes Ca²⁺ overload</li> <li>Leads to neuronal apoptosis & autophagy disruption</li> </ul> </div> <div class="card"> <h3>MgSO₄ Dual-Action Intervention</h3> <p>Magnesium sulfate provides dual protective effects:</p> <ul> <li><strong>Anti-inflammatory action:</strong> Inhibits TNF-α release via NF-κB pathway suppression</li> <li><strong>Receptor modulation:</strong> Blocks NMDA receptor ion channel as non-competitive antagonist</li> <li>Targets both arms of the pathogenic pathway</li> </ul> </div> <div class="card"> <h3>Research Implications</h3> <p>This study will validate:</p> <ul> <li>The causal relationship in the TNF-α/pNR2B pathway</li> <li>MgSO₄ efficacy in preventing CINP development</li> <li>Downstream cellular protection mechanisms</li> <li>Potential for clinical translation</li> </ul> </div> </div> </div> <footer class="no-print"> <p>This diagram illustrates the proposed mechanism of chemotherapy-induced neuropathic pain and the protective effects of magnesium sulfate</p> <p>To save as PDF: Use browser's Print function and select "Save as PDF"</p> </footer> <script> mermaid.initialize({ startOnLoad: true, theme: 'default', flowchart: { useMaxWidth: true, curve: 'linear', nodeSpacing: 50, rankSpacing: 70 }, securityLevel: 'loose' }); </script> </body> </html>
07-22
<template> <div :class="`${prefixCls}`"> <el-tabs v-model="tableDescIndex" editable :class="`${prefixCls}__tabs`" @tab-change="handleTabChange" @edit="handleTabsEdit" > <el-tab-pane v-for="(item, index) in routeDescTabs" :key="index" :label="item.routeAbbr" :name="index" > <template #label> <span @dblclick="handleTabDblClick(index)" class="tab-title"> <el-tooltip class="box-item" effect="dark" :content="item.lineDesc" placement="top" v-if="item.lineDesc" > {{ item.routeAbbr }} </el-tooltip> <span v-else>{{ item.routeAbbr }}</span> </span> </template> <el-table :data="item.planStopList" :class="`${prefixCls}__table`" :header-cell-style="headerCellStyle" :cell-style="cellStyle" > <el-table-column prop="sort" :label="t('lineEdit.sort')" width="90" align="center"> <template #default="scope"> <div> {{ scope.$index + 1 }} <el-icon @click="addColumnAfterRow(scope.$index)" class="add-icon"> <Plus /> </el-icon> <el-icon @click="removeRow(scope.$index)" class="add-icon"> <Delete /> </el-icon> </div> </template> </el-table-column> <el-table-column prop="stopId" :label="t('lineMapEdit.stationName')" align="center"> <template #default="scope"> <el-select v-model="scope.row.stopDesc" filterable :filter-method="handleSearch" virtual-scroll :virtual-scroll-item-size="40" :virtual-scroll-visible-items="15" v-select-loadmore="loadMoreData" placeholder="请选择或搜索" > <el-option v-for="item in visibleOptions" :key="item.stopId" :label="item.stopDesc" :value="item.stopId" /> </el-select> </template> </el-table-column> </el-table> </el-tab-pane> </el-tabs> </div> </template> <script lang="ts" setup> import { headerCellStyle, cellStyle } from '@/components/PlanningComps/common' import { Plus, Delete } from '@element-plus/icons-vue' import { ref } from 'vue' import { debounce } from 'lodash-es' import { getAllStopList } from '@/api/planning/stop/index' import { RouteTab, lineRouteInfoItem } from '@/api/planning/line/type' import { ElMessageBox } from 'element-plus' defineOptions({ name: 'RouteDesc' }) const props = defineProps<{ lineRouteInfoList: lineRouteInfoItem[] }>() const emit = defineEmits(['update:tabIndex', 'update-line-detail', 'stop-added', 'stop-deleted']) const { getPrefixCls } = useDesign() const prefixCls = getPrefixCls('route-desc') const message = useMessage() const { t } = useI18n() const tableDescIndex = ref(0) // 当前选中的选项 const routeDescTabs = ref<lineRouteInfoItem[]>([]) // 新增线路点 const wayPoint = ref([]) // 初始化响应式引用 const allOptions = ref([]) // 所有选项 const filteredOptions = ref([]) // 过滤后的选项 const currentPage = ref(1) // 当前页码 const pageSize = 50 // 每页大小 const hasMore = ref(true) // 是否还有更多数据 const isLoading = ref(false) // 加载状态 const selectKey = ref(0) const searchQuery = ref('') const loadedCount = ref(100) // 初始加载100条 // 新增对话框删除tab const handleTabsEdit = (targetName: TabPaneName | undefined, action: 'remove' | 'add') => { if (action === 'add') { ElMessageBox.prompt('Please enter the line name', 'Prompt', { confirmButtonText: 'Confirm', cancelButtonText: 'Cancel', inputPattern: /.+/, // 正则验证 inputErrorMessage: 'The input cannot be empty' }) .then(({ value }) => { const title = value.trim() routeDescTabs.value.push({ lineId: '', lineDesc: '', routeAbbr: title, planStopList: [{ stopId: '', stopDesc: '', sort: 1, lng: '', lat: '' }], routeGeometry: {} }) tableDescIndex.value = routeDescTabs.value.length - 1 handleTabChange() // lineRouteInfoItem 添加线路描述 emit('update-line-detail', routeDescTabs.value) }) .catch(() => { console.log('User cancellation') }) } else if (action === 'remove') { const tabs = routeDescTabs.value let activeName = tableDescIndex.value if (activeName === targetName) { tabs.forEach((tab, index) => { if (tab.name === targetName) { const nextTab = tabs[index + 1] || tabs[index - 1] if (nextTab) { activeName = nextTab.name } } }) } tableDescIndex.value = activeName routeDescTabs.value = tabs.filter((tab) => tab.name !== targetName) } } // 删除 const removeTab = (targetName: string) => { const tabs = routeDescTabs.value let activeName = tableDescIndex.value if (activeName === targetName) { tabs.forEach((tab, index) => { if (tab.name === targetName) { const nextTab = tabs[index + 1] || tabs[index - 1] if (nextTab) { activeName = nextTab.name } } }) } tableDescIndex.value = activeName routeDescTabs.value = tabs.filter((tab) => tab.name !== targetName) } // 判定索引位置 const determinePositionType = (index: number) => { const stops = routeDescTabs.value[tableDescIndex.value].planStopList if (index === 0) return 'start' if (index === stops.length - 1) return 'end' return 'middle' } // 新增行index 索引(新增包含lineID站点-保存新增索引-判断位置-传递事件-构建几何请求-重绘线路) const addColumnAfterRow = (index: number) => { const newIndex = index + 1 routeDescTabs.value[tableDescIndex.value].planStopList.splice(newIndex, 0, { sort: `${newIndex + 1}`, stopId: '', stopDesc: '', lng: '', lat: '' }) // 更新所有行的 No 值 routeDescTabs.value[tableDescIndex.value].planStopList.forEach((row, i) => { row.sort = `${i + 1}` }) } // 监听删除操作后重排序 const removeRow = (index) => { if (routeDescTabs.value[tableDescIndex.value].planStopList.length <= 1) return message.warning('Only one value cannot be deleted') // 触发线路更新事件,携带被删除站点的类型信息 if (routeDescTabs.value[tableDescIndex.value].lineId != '') { emit('stop-deleted', { delectIndex: index, // 删除站点索引 positionType: determinePositionType(index) // 删除站点位置类型 }) } routeDescTabs.value[tableDescIndex.value].planStopList.splice(index, 1) // 重置编号 routeDescTabs.value[tableDescIndex.value].planStopList.forEach((row, i) => { row.sort = `${i + 1}` }) message.success('Delete successfully') } // 获取站点列表 const getAllStopsList = async () => { try { const data = (await getAllStopList()) as StopListItem[] allOptions.value = data } catch (error) { console.error('获取站点列表失败:', error) allOptions.value = [] filteredOptions.value = [] } } // 防抖远程搜索 const handleSearch = debounce( (query) => { debugger if (!query) { // filteredOptions.value = [] return } try { searchQuery.value = query.toLowerCase() } catch (error) { console.error('搜索站点失败:', error) filteredOptions.value = [] } }, 300, { leading: true, trailing: true } ) // 局部注册自定义滚动指令翻页加载 const vSelectLoadmore = { mounted(el, binding) { // 使用nextTick确保DOM渲染完成 setTimeout(() => { const SELECTWRAP_DOM = el.querySelector('.el-select-dropdown .el-select-dropdown__wrap') if (SELECTWRAP_DOM) { SELECTWRAP_DOM.addEventListener('scroll', () => { // 精确计算是否滚动到底部 const isBottom = Math.ceil(SELECTWRAP_DOM.scrollTop + SELECTWRAP_DOM.clientHeight) >= SELECTWRAP_DOM.scrollHeight - 1 if (isBottom) { binding.value() // 触发绑定的加载函数 } }) } }, 100) } } // 分页加载函数 const loadMoreData = () => { if (loadedCount.value < allOptions.value.length) { loadedCount.value += 50 // 每次加载50条 } } // 动态计算可见选项(结合搜索和分页) const visibleOptions = computed(() => { debugger let result = allOptions.value if (searchQuery.value) { result = result.filter((item) => item.stopDesc.toLowerCase().includes(searchQuery.value.toLowerCase()) ) } return result.slice(0, loadedCount.value) // 只返回当前加载的数据 }) // 双击修改方案名 const handleTabDblClick = (index: number) => { const currentTab = routeDescTabs.value[index] if (!currentTab) return ElMessageBox.prompt('Please enter the new line description', 'Modify the line name', { confirmButtonText: 'Confirm', cancelButtonText: 'Cancel', inputPattern: /.+/, inputErrorMessage: 'Please enter a valid line name', inputValue: currentTab.routeAbbr // 初始值为当前名称 }) .then(({ value }) => { // 更新 tab 的 lineDesc 字段 routeDescTabs.value[index].routeAbbr = value.trim() }) .catch(() => { console.log('User cancels modification') }) } const handleChangeSelectStops = (value, sort) => { console.log(value, sort, 'dddd') // debugger filteredOptions.value.some((item, index) => { if (item.stopId == value) { const tabIndex = tableDescIndex.value const stops = routeDescTabs.value[tabIndex].planStopList // // 获取新增索引 const addedIndex = sort - 1 // 获取原始 stopId // const oldStop = stops[addedIndex]?.stopId // 设置当前站点信息 routeDescTabs.value[tabIndex].planStopList[addedIndex] = { ...stops[addedIndex], stopId: item.stopId, stopDesc: item.stopDesc, lng: item.lng, lat: item.lat } // 新增及替换点的更改 if (routeDescTabs.value[tableDescIndex.value].lineId != '') { emit('stop-added', { stopIndex: addedIndex, positionType: determinePositionType(addedIndex), behindStation: addedIndex > 0 ? stops[addedIndex] : null }) } } }) } const handleTabChange = () => { emit('update:tabIndex', tableDescIndex.value) } // 计算当前选项卡的有效 stopId 数量 const validStopCount = computed(() => { const currentTab = routeDescTabs.value[tableDescIndex.value] if (!currentTab?.planStopList) return 0 return currentTab.planStopList.filter((stop) => stop.stopId).length }) // 监听站点数量变化并触发更新 watch( validStopCount, (newCount, oldCount) => { if (newCount !== oldCount) { // console.log('站点数量变化', newCount, oldCount, tableDescIndex.value, routeDescTabs.value) const currentTab = routeDescTabs.value[tableDescIndex.value] // 新增 if (currentTab && currentTab.lineId == '') { // debugger // console.log(routeDescTabs.value, 'routeDescTabs.value') emit('update-line-detail', routeDescTabs.value) } } }, { immediate: true } ) watchEffect(() => { if (props.lineRouteInfoList && props.lineRouteInfoList.length > 0) { routeDescTabs.value = [...props.lineRouteInfoList] console.log(routeDescTabs.value, 'routeDescTabs.value') } }) onBeforeMount(() => { getAllStopsList() }) onMounted(() => {}) </script> <style lang="scss" scoped> $prefix-cls: #{$namespace}-route-desc; .#{$prefix-cls} { .el-select-dropdown { max-height: 300px; } width: 458px; background: rgba(255, 255, 255, 0.7); border-radius: 4px; padding: 0px 16px; overflow-y: auto; &__tabs { height: calc(100vh - 110px); .tab-title { display: flex; align-items: center; gap: 6px; padding: 0 8px; &:hover .el-icon { opacity: 1; } .el-icon { opacity: 0; transition: opacity 0.2s; &:hover { color: #409eff; } } } } &__table { height: calc(100vh - 175px); // 表头圆角 :deep(.el-table__header) { border-radius: 6px; overflow: hidden; } // 表内容行间距 :deep(.el-table__body) { border-collapse: separate !important; border-spacing: 0 7px !important; /* 第二个值控制行间距 */ overflow: hidden !important; } // 表内容每一行的首尾单元格具有圆角效果 :deep(.el-table__body-wrapper table) { border-collapse: separate; overflow: hidden; } :deep(.el-table__body tr td:first-child), :deep(.el-table__body tr td:last-child) { position: relative; } :deep(.el-table__body tr td:first-child) { border-top-left-radius: 6px; border-bottom-left-radius: 6px; } :deep(.el-table__body tr td:last-child) { border-top-right-radius: 6px; border-bottom-right-radius: 6px; } .add-icon { opacity: 0; transition: opacity 0.2s ease; } tr:hover .add-icon { opacity: 1; cursor: pointer; margin-left: 6px; } } } </style> <style lang="scss"> $prefix-cls: #{$namespace}-route-desc; .#{$prefix-cls} { } </style> 当前页vSelectLoadmore 在搜索数据出来后无法获取到滚动事件的原因
07-25
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值