vue中el-table配合el-tag实现多选/单选编辑、清除回显

本文描述了一个需求,如何在表格中实现多选功能,带有数量限制,并保持跨页和搜索后的勾选状态同步。同时,用户操作会影响标签显示,且能清除勾选状态以实现回显。通过详细的HTML、JS代码示例展示了这一功能的实现过程。

在工作时,遇到这样一个需求,表格中内容需要可以勾选或多选,且有数量限制,并且将选中的一行中的指定数据,以标签展示。要求跨页或者搜索后均保持回显,且表格勾选状态与tag状态保持一致,即清除tag取消对应行的勾选状态,取消行勾选状态清除tag,  先看效果图:

搜索后回显:

上代码:

表格HTML部分

标签HTML部分

js代码

<!-- 📚📚📚 Pro-Table 文档: https://juejin.cn/post/7166068828202336263 --> <template> <!-- 查询表单 --> <SearchForm v-show="isShowSearch" :search="_search" :reset="_reset" :columns="searchColumns" :search-param="searchParam" :search-col="searchCol" /> <!-- 表格主体 --> <div class="card table-main"> <!-- 表格头部 操作按钮 --> <div class="table-header"> <div class="header-button-lf"> <slot name="tableHeader" :selected-list="selectedList" :selected-list-ids="selectedListIds" :is-selected="isSelected" /> </div> <div v-if="toolButton" class="header-button-ri"> <slot name="toolButton"> <el-button v-if="showToolButton('refresh')" :icon="Refresh" circle @click="refreshData" /> <el-button v-if="showToolButton('setting') && columns.length" :icon="Operation" circle @click="openColSetting" /> <el-button v-if="showToolButton('search') && searchColumns?.length" :icon="Search" circle @click="isShowSearch = !isShowSearch" /> </slot> </div> </div> <!-- 表格上方的提示信息 --> <div v-if="tableTipsFlag"> <slot name="tableTips"></slot> </div> <!-- 表格主体 --> <el-table ref="tableRef" v-bind="$attrs" :data="processTableData" :border="border" :row-key="rowKey" @selection-change="selectionChange" > <!-- 默认插槽 --> <slot /> <template v-for="item in tableColumns" :key="item"> <!-- selection || radio || index || expand || sort --> <el-table-column v-if="item.type && columnTypes.includes(item.type)" v-bind="item" :align="item.align ?? 'center'" :reserve-selection="item.type == 'selection'" :selectable="item.isSelectable"> <template #default="scope"> <!-- expand --> <template v-if="item.type == 'expand'"> <component :is="item.render" v-bind="scope" v-if="item.render" /> <slot v-else :name="item.type" v-bind="scope" /> </template> <!-- radio --> <el-radio v-if="item.type == 'radio'" v-model="radio" :label="scope.row[rowKey]"> <!-- <i></i> --> </el-radio> <!-- sort --> <el-tag v-if="item.type == 'sort'" class="move"> <el-icon> <DCaret /></el-icon> </el-tag> </template> </el-table-column> <!-- other --> <TableColumn v-if="!item.type && item.prop && item.isShow" :column="item"> <template v-for="slot in Object.keys($slots)" #[slot]="scope"> <slot :name="slot" v-bind="scope" /> </template> </TableColumn> </template> <!-- 插入表格最后一行之后的插槽 --> <template #append> <slot name="append" /> </template> <!-- 无数据 --> <template #empty> <div class="table-empty"> <slot name="empty"> <img src="@/assets/images/notData.png" alt="notData" /> <div>暂无数据</div> </slot> </div> </template> </el-table> <!-- 分页组件 --> <slot name="pagination"> <Pagination v-if="pagination" :pageable="pageable" :handle-size-change="handleSizeChange" :handle-current-change="handleCurrentChange" /> </slot> </div> <!-- 列设置 --> <ColSetting v-if="toolButton" ref="colRef" v-model:col-setting="colSetting" /> </template> <script setup lang="ts" name="ProTable"> import { ref, watch, provide, onMounted, unref, computed, reactive } from "vue"; import { ElTable } from "element-plus"; import { useTable } from "@/hooks/useTable"; import { useSelection } from "@/hooks/useSelection"; import { BreakPoint } from "@/components/Grid/interface"; import { ColumnProps, TypeProps } from "@/components/ProTable/interface"; import { Refresh, Operation, Search } from "@element-plus/icons-vue"; import { handleProp } from "@/utils"; import SearchForm from "@/components/SearchForm/index.vue"; import Pagination from "./components/Pagination.vue"; import ColSetting from "./components/ColSetting.vue"; import TableColumn from "./components/TableColumn.vue"; import Sortable from "sortablejs"; export interface ProTableProps { columns: ColumnProps[]; // 列配置项 ==> 必传 data?: any[]; // 静态 table data 数据,若存在则不会使用 requestApi 返回的 data ==> 非必传 requestApi?: (params: any) => Promise<any>; // 请求表格数据的 api ==> 非必传 requestAuto?: boolean; // 是否自动执行请求 api ==> 非必传(默认为true) requestError?: (params: any) => void; // 表格 api 请求错误监听 ==> 非必传 dataCallback?: (data: any) => any; // 返回数据的回调函数,可以对数据进行处理 ==> 非必传 title?: string; // 表格标题 ==> 非必传 pagination?: boolean; // 是否需要分页组件 ==> 非必传(默认为true) initParam?: any; // 初始化请求参数 ==> 非必传(默认为{}) border?: boolean; // 是否带有纵向边框 ==> 非必传(默认为true) toolButton?: ("refresh" | "setting" | "search")[] | boolean; // 是否显示表格功能按钮 ==> 非必传(默认为true) rowKey?: string; // 行数据的 Key,用来优化 Table 的渲染,当表格数据时,所指定的 id ==> 非必传(默认为 id) searchCol?: number | Record<BreakPoint, number>; // 表格搜索项 每列占比配置 ==> 非必传 { xs: 1, sm: 2, md: 2, lg: 3, xl: 4 } tableTipsFlag?: boolean; // 表格上方的提示信息 ==> 非必传 (默认为false) } // 接受父组件参数,配置默认值 const props = withDefaults(defineProps<ProTableProps>(), { columns: () => [], requestAuto: true, pagination: true, initParam: {}, border: true, toolButton: true, rowKey: "id", searchCol: () => ({ xs: 1, sm: 3, md: 3, lg: 4, xl: 5 }), tableTipsFlag: false }); // table 实例 const tableRef = ref<InstanceType<typeof ElTable>>(); // column 列类型 const columnTypes: TypeProps[] = ["selection", "radio", "index", "expand", "sort"]; // 是否显示搜索模块 const isShowSearch = ref(true); // 控制 ToolButton 显示 const showToolButton = (key: "refresh" | "setting" | "search") => { return Array.isArray(props.toolButton) ? props.toolButton.includes(key) : props.toolButton; }; // 单选值 const radio = ref(""); // 表格 Hooks const { selectionChange, selectedList, selectedListIds, isSelected } = useSelection(props.rowKey); // 表格操作 Hooks const { tableData, pageable, searchParam, searchInitParam, getTableList, search, reset, handleSizeChange, handleCurrentChange } = useTable(props.requestApi, props.initParam, props.pagination, props.dataCallback, props.requestError, tableRef); // 清空中数据列表 const clearSelection = () => tableRef.value!.clearSelection(); // 初始化表格数据 && 拖拽排序 onMounted(() => { dragSort(); props.requestAuto && getTableList(); props.data && (pageable.value.total = props.data.length); }); // 处理表格数据 const processTableData = computed(() => { if (!props.data) return tableData.value; if (!props.pagination) return props.data; return props.data.slice( (pageable.value.pageNum - 1) * pageable.value.pageSize, pageable.value.pageSize * pageable.value.pageNum ); }); // 监听页面 initParam 改化,重新获取表格数据 watch(() => props.initParam, () => { // 将初始化initParam参数赋值给表单 searchParam.value = { ...searchParam.value, ...props.initParam, }; getTableList() }, { deep: true }); // 接收 columns 并设置为响应式 const tableColumns = reactive<ColumnProps[]>(props.columns); // 扁平化 columns const flatColumns = computed(() => flatColumnsFunc(tableColumns)); // 定义 enumMap 存储 enum 值(避免异步请求无法格式化单元格内容 || 无法填充搜索下拉择) const enumMap = ref(new Map<string, { [key: string]: any }[]>()); const setEnumMap = async ({ prop, enum: enumValue }: ColumnProps) => { if (!enumValue) return; // 如果当前 enumMap 存在相同的值 return if (enumMap.value.has(prop!) && (typeof enumValue === "function" || enumMap.value.get(prop!) === enumValue)) return; // 当前 enum 为静态数据,则直接存储到 enumMap if (typeof enumValue !== "function") return enumMap.value.set(prop!, unref(enumValue!)); // 为了防止接口执行慢,而存储慢,导致重复请求,所以预先存储为[],接口返回后再二次存储 enumMap.value.set(prop!, []); // 当前 enum 为后台数据需要请求数据,则调用该请求接口,并存储到 enumMap const { data } = await enumValue(); enumMap.value.set(prop!, data); }; const refreshData = () => { pageable.value.pageNum = 1; getTableList(); }; // 注入 enumMap provide("enumMap", enumMap); // 扁平化 columns 的方法 const flatColumnsFunc = (columns: ColumnProps[], flatArr: ColumnProps[] = []) => { columns.forEach(async col => { if (col._children?.length) flatArr.push(...flatColumnsFunc(col._children)); flatArr.push(col); // column 添加默认 isShow && isFilterEnum 属性值 col.isShow = col.isShow ?? true; col.isFilterEnum = col.isFilterEnum ?? true; // 设置 enumMap await setEnumMap(col); }); return flatArr.filter(item => !item._children?.length); }; // 过滤需要搜索的配置项 && 排序 const searchColumns = computed(() => { return flatColumns.value ?.filter(item => item.search?.el || item.search?.render) .sort((a, b) => a.search!.order! - b.search!.order!); }); // 设置 搜索表单默认排序 && 搜索表单项的默认值 searchColumns.value?.forEach((column, index) => { column.search!.order = column.search?.order ?? index + 2; const key = column.search?.key ?? handleProp(column.prop!); const defaultValue = column.search?.defaultValue; if (defaultValue !== undefined && defaultValue !== null) { searchInitParam.value[key] = defaultValue; searchParam.value[key] = defaultValue; } }); // 列设置 ==> 需要过滤掉不需要设置的列 const colRef = ref(); const colSetting = tableColumns!.filter(item => { const { type, prop, isShow } = item; return !columnTypes.includes(type!) && prop !== "operation" && isShow; }); const openColSetting = () => colRef.value.openColSetting(); // 定义 emit 事件 const emit = defineEmits<{ search: []; reset: []; dargSort: [{ newIndex?: number; oldIndex?: number }]; }>(); const _search = () => { search(); emit("search"); }; const _reset = () => { reset(); emit("reset"); }; // 拖拽排序 const dragSort = () => { const tbody = document.querySelector(".el-table__body-wrapper tbody") as HTMLElement; Sortable.create(tbody, { handle: ".move", animation: 300, onEnd({ newIndex, oldIndex }) { const [removedItem] = processTableData.value.splice(oldIndex!, 1); processTableData.value.splice(newIndex!, 0, removedItem); emit("dargSort", { newIndex, oldIndex }); } }); }; // 暴露给父组件的参数和方法 (外部需要什么,都可以从这里暴露出去) defineExpose({ element: tableRef, tableData: processTableData, radio, pageable, searchParam, searchInitParam, getTableList, search, reset, handleSizeChange, handleCurrentChange, clearSelection, enumMap, isSelected, selectedList, selectedListIds, refreshData: getTableList // 暴露刷新方法 }); </script> import { ref, computed } from "vue"; /** * @description 表格数据操作 * @param {String} rowKey 当表格可以时,所指定的 id * */ export const useSelection = (rowKey: string = "id") => { const isSelected = ref<boolean>(false); const selectedList = ref<{ [key: string]: any }[]>([]); // 当前中的所有 ids 数组 const selectedListIds = computed((): string[] => { let ids: string[] = []; selectedList.value.forEach(item => ids.push(item[rowKey])); return ids; }); /** * @description 操作 * @param {Array} rowArr 当前择的所有数据 * @return void */ const selectionChange = (rowArr: { [key: string]: any }[]) => { rowArr.length ? (isSelected.value = true) : (isSelected.value = false); selectedList.value = rowArr; }; return { isSelected, selectedList, selectedListIds, selectionChange }; }; <template> <div class="table-box"> <ProTable ref="proTable" :columns="columns" :request-api="getTableList" :data-callback="dataCallback"> <!-- 表格 header 按钮 --> <template #tableHeader="scope"> <el-button type="primary" v-if="hasBtnPermission('asset:inventory:save')" @click="addNewData('新增资产','add',{})">新增资产</el-button> <el-button type="primary" v-if="hasBtnPermission('asset:inventory:update')" @click="batcEdit(scope.selectedListIds)">批量编辑</el-button> <el-button type="primary" v-if="hasBtnPermission('asset:inventory:delete')" @click="batchDelete(scope.selectedListIds)">批量删除</el-button> <el-dropdown style="margin-left: 10px" v-if="hasBtnPermission('asset:inventory:downloadData')" @command="batchExport"> <el-button type="primary"> 批量导出<i class="el-icon-arrow-down el-icon--right"></i> </el-button> <template #dropdown> <el-dropdown-menu> <el-dropdown-item command="1">导出所数据</el-dropdown-item> <el-dropdown-item command="2">导出全部</el-dropdown-item> </el-dropdown-menu> </template> </el-dropdown> <el-button type="primary" @click="openTagPrint(scope.selectedListIds)" v-if="hasBtnPermission('asset:inventory:printMark')" style="margin-left: 10px">打印资产标签</el-button> </template> <el-table-column type="selection" width="55"></el-table-column> <!-- 图片 --> <el-table-column prop="imageUrl" label="图片" minWidth="100px" class-name="is-center"> <template #default="{ row }"> <div class="more_imgs" v-if="row.imageUrlList && row.imageUrlList.length > 0"> <viewer :images="row.imageUrlList"> <span class="viewImage" v-for="(itemImg, index) in row.imageUrlList" :key="index"> <img v-if="itemImg" :src="itemImg" style="width: 100%; height: 100%" /> </span> </viewer> </div> </template> </el-table-column> <template #expand="scope"> {{ scope.row }} </template> <template #operation="scope" v-if="hasBtnPermission('asset:inventory:update')" > <el-button type="primary" link @click="editData('编辑资产','edit', scope.row)">编辑</el-button> </template> </ProTable> <!-- 择新增资产类型 --> <div class="new-Dialog-type" v-if="dialogFormVisible"> <el-dialog v-model="dialogFormVisible" title="择资产类型" width="450" draggable > <el-form ref="ruleFormRef" :model="form" :rules="rules" label-width="93px"> <el-form-item label="类型" prop="type"> <el-select v-model="form.type" placeholder="请择"> <el-option v-for="item in assetType" :key="item.value" :label="item.label" :value="item.value"></el-option> </el-select> </el-form-item> <el-form-item label="非标准资产" v-if="form.type === 2" prop="nonStandardAssetsId"> <el-select v-model="form.nonStandardAssetsId" placeholder="请择"> <el-option v-for="item in nonstandardData" :key="item.id" :label="item.name" :value="item.id" :disabled="item.status == 0" ></el-option> </el-select> </el-form-item> </el-form> <template #footer> <div class="dialog-footer"> <el-button @click="closeDialog">取消</el-button> <el-button type="primary" @click="nextTips('新增资产','add',{})">下一步</el-button> </div> </template> </el-dialog> </div> <!-- 编辑,批量编辑,新增组件 --> <addAsset ref="addAssetRef" @previous-step="handlePreviousStep"/> <!-- 详情公共组件 --> <assetInfo ref="assetInfoRef"></assetInfo> </div> </template> <script setup lang="tsx" name="assetInventory"> import { ref, reactive, inject, onMounted,nextTick } from "vue"; import ProTable from "@/components/ProTable/index.vue"; import { assetListData ,queryGroupList,addAssetList,deleteAssetList,assetListDown,editBatchAssetList,assetListInfo,editAssetList} from "@/api/modules/assetAllocation"; import { ProTableInstance } from "@/components/ProTable/interface"; import { setTreeData,formatToTree } from "@/utils/tools"; import { assetClassificationList,getPositionList,setValueClildList,nonstandardList,getOrgSubjectList,getInstitution } from '@/api/modules/public'; import { getUserDepartment } from "@/api/modules/user"; import { assetListType,assetType,markStatusType } from "@/utils/dict"; import { da, fa, pa } from "element-plus/es/locale"; import addAsset from "./mode/addAsset.vue"; import assetInfo from "./mode/assetInfo.vue"; import { useHandleData } from "@/hooks/useHandleData"; import { ElMessage, FormInstance } from "element-plus"; import { printAssetMark } from "@/api/modules/assetAllocation"; import moment from "moment"; const hasBtnPermission: any = inject('hasBtnPermission'); // ProTable 实例 const proTable = ref<ProTableInstance>(); // 子界面需要用到的设置值 const configuration = reactive({ assetCategory: [], // 资产分类 positionList: [], // 存放地点 departmentList: [], // 使用部门 sourceList: [], // 资产来源 nonstandardList: [], // 非标准资产 unitList:[], // 计量单位 institutionalEntity:[], //机构主体 assentityList:[], // 主体 assinstitutional:[],//机构 }) const selectedIds = ref<string[]>([]); // 在组件顶部定义 // 非标准资产 const nonstandardData = ref([]); const assentityList = ref([]) const assinstitutional = ref([]) // 资产类型弹窗 const dialogFormVisible = ref(false) const form = reactive({ type:'', nonStandardAssetsId:'' }) const rules = reactive({ type: [{ required: true, message: "请择资产类型", trigger: ["blur", "change"] }], nonStandardAssetsId: [{ required: true, message: "请择非标准资产", trigger: ["blur", "change"] }], }) const getTableList = (params: any) => { if (params.purchaseDate) { params.purchaseDateStart = params.purchaseDate[0] + ' 00:00:00'; params.purchaseDateEnd= params.purchaseDate[1] + ' 23:59:59'; delete params.purchaseDate; } if (params.maintenanceExpirationDate) { params.maintenanceExpirationDateStart = params.maintenanceExpirationDate[0] + ' 00:00:00'; params.maintenanceExpirationDateEnd = params.maintenanceExpirationDate[1] + ' 23:59:59'; delete params.maintenanceExpirationDate; } if(params.useUserNameData) { params.useUserNameList = params.useUserNameData.split('\n'); delete params.useUserNameData } if(params.assetCodeData) { params.assetCodeList = params.assetCodeData.split('\n'); delete params.assetCodeData } return assetListData(params) } const refreshTable = () => { proTable.value!.pageable.pageNum = 1; proTable.value?.refreshData(); // 清除表格勾项 if (proTable.value) { proTable.value.clearSelection(); } }; const dataCallback = (data: any) => { const dataList = data?.dataList || []; const processedList = dataList.map(item => { try { return { ...item, imageUrlList: typeof item?.imageUrl === 'string' ? item.imageUrl.split(',') : [], purchaseDate: item.purchaseDate ? item.purchaseDate.split(" ")[0] : '' }; } catch (error) { return { ...item, imageUrlList: [] }; } }); return { list: processedList, total: data?.totalCount, pageNum: data?.page, pageSize: data?.pageSize }; }; // 查询计量单位 const getUnitList = async () => { const response = await setValueClildList({dictCode: 'UNIT_MEASUREMENT'}); const data = response.data || []; configuration.unitList = data } // 查询机构主体 const getOrgSubjectListData = async() => { // 机构 const responseAss = await getInstitution({ id: 'ASS_INSTITUTIONAL' }); const data = responseAss.data || []; if(data && data.length > 0) { configuration.assinstitutional = data assinstitutional.value = data } // 主体 const response = await getInstitution({ id: 'OFFICIAL_SEAL_ORG' }); const data1 = response.data || []; if(data1 && data1.length > 0) { configuration.assentityList = data1 assentityList.value = data1 } // 机构主体(二和一接口),用来把主体,机构以及部门三者关联起来,单独调用上面接口,主要是为了排序好看,无语子...... const res = await getOrgSubjectList({}); const data2 = res.data || []; if(data && data.length > 0) { configuration.institutionalEntity = data2 } } const formatToDepTree = (arr, pid = 0) =>{ let result = []; for (let i = 0; i < arr.length; i++) { if (arr[i].pid === pid) { arr[i].label = arr[i].name let children = formatToDepTree(arr, arr[i].workOADepartmentId); if (children.length > 0) { arr[i].children = children; } result.push(arr[i]); } } return result; } // 表格配置项 const columns = reactive([ { prop: "assetStatus", label: "资产状态", fixed: "left", minWidth:100 , enum: assetListType, search: { el: "select", props: { filterable: true } }, render: scope => { if (scope.row.assetStatus == '1') { return ( <span style="color: #49c625">空闲</span> ); } else if (scope.row.assetStatus == '2') { return ( <span style="color: #ff7f00">在用</span> ); } else if (scope.row.assetStatus == '3') { return ( <span style="color: #1890ff">已处置</span> ); } } }, { prop: "markStatus", label: "资产标记", isShow:false, enum:markStatusType, search: { el: "select", props: { filterable: true } }, fieldNames: { label: "label", value: "value" } }, { prop: "markTagsName", label: "资产标记", fixed: "left", minWidth:100 , render: scope => { if (scope.row.markTags == '0') { return ( <span style="color: #49c625">派发待领用</span> ); } else if (scope.row.markTags == '1') { return ( <span style="color: #ff7f00">领用审批中</span> ); } else if (scope.row.markTags == '2') { return ( <span style="color: #ff7f00">退还审批中</span> ); } else if (scope.row.markTags == '3') { return ( <span style="color: #ff7f00">借用审批中</span> ); } else if (scope.row.markTags == '4') { return ( <span style="color: #1890ff">借用</span> ); } else if (scope.row.markTags == '5') { return ( <span style="color: #ff7f00">调拨审批中</span> ); } else if (scope.row.markTags == '6') { return ( <span style="color: #ff7f00">维修审批中</span> ); } else if (scope.row.markTags == '7') { return ( <span style="color: #ff7f00">处置审批中</span> ); } else if (scope.row.markTags == '8') { return ( <span style="color: #ff0000">待处理</span> ); } else if (scope.row.markTags == '9') { return ( <span style="color: #ff7f00">归还审批中</span> ); } } }, { prop: "assetCodeData", label: "资产编码", isShow:false, search: { el: "input", type: 'textarea', placeholder: '个编码请换行' } , minWidth:100, }, { prop: "assetCode", label: "资产编码", fixed: "left", minWidth:100, render: (scope) => { return ( <span style="color: #49c625" onClick={() => handleAssetCodeClick(scope.row)}> {scope.row.assetCode} </span> ); } }, { prop: "assetName", label: "资产名称", fixed: "left", search: { el: "input" },minWidth:100 }, { prop: "assetCategoryName", label: "资产分类", minWidth:100 , }, { prop: "assetCategoryIdList", label: "资产分类", isShow:false, enum: async () => { // 获取资产分类数据,扁平数据 const { data } = await assetClassificationList({}); const treeData = formatToTree(data); configuration.assetCategory = treeData; return { data: treeData } }, fieldNames: { label: "categoryName", value: "id" }, search: { el: "tree-select", props: { filterable: true, multiple: true, checkStrictly: true, // 允许择父节点 nodeKey: "id", // 每个节点的唯一标识字段 props: { label: "label", value: "id", children: "children" } } }, }, { prop: "nonStandardAssetsId", label: "资产类型", minWidth:100 , isShow:false, }, { prop: "type", label: "资产类型", minWidth:100 ,enum: assetType, search: { el: "select", props: { filterable: true } },}, { prop: "useUserName", label: "使用人" }, { prop: "useUserNameData", label: "使用人", isShow:false, search: { el: "input", type: 'textarea', placeholder: '个使用人请换行' } }, { prop: "useOrgIdList", label: "使用机构", search: { el: "select", props: { filterable: true, multiple: true}}, minWidth:100 , enum: assinstitutional, isShow:false, fieldNames: { label: "name", value: "detailCode" }, }, { prop: "useOrgName", label: "使用机构", minWidth:100 , }, { prop: "useSubjectId", label: "使用主体", search: { el: "select" } , minWidth:100 , enum: assentityList, isShow:false, fieldNames: { label: "remarks", value: "detailCode" }, }, { prop: "useSubjectName", label: "使用主体", minWidth:100 , }, { prop: "useDepartmentId", label: "使用部门", minWidth:100 , enum: async () => { // 获取部门数据 const { data } = await getUserDepartment(); data.forEach(item => { item.pid = item.extMap.parentWorkOADepartmentId item.workOADepartmentId = item.value item.id = item.value item.name = item.label }) const treeData = formatToDepTree(data); configuration.departmentList = treeData return { data: treeData } }, fieldNames: { label: "label", value: "value" }, search: { el: "tree-select", props: { filterable: true, multiple: true, checkStrictly: true, // 允许择父节点 nodeKey: "value", // 每个节点的唯一标识字段 props: { label: "label", value: "value", children: "children" } } } }, { prop: "useDepartmentName", label: "使用部门", isShow:false, minWidth:100 , }, { prop: "storageLocationIdList", label: "存放地点", minWidth:100 , isShow:false, enum: async () => { // 获取存放地点 const { data } = await getPositionList({ pageNum: 1, pageSize: 9999 }); const deepCopy = JSON.parse(JSON.stringify(data['dataList'])); configuration.positionList = deepCopy; return { data: data['dataList'] }; }, fieldNames: { label: "position", value: "id" }, search: { el: "select", props: { filterable: true , multiple: true} }, }, { prop: "storageLocationName", label: "存放地点", minWidth:100 , }, { prop: "adminName", label: "管理员", search: { el: "input" } }, { prop: "affiliatedInstitutionName", label: "所属机构",minWidth:100 }, { prop: "affiliatedInstitutionIdList", label: "所属机构", isShow:false, search: { el: "select" , props: { filterable: true , multiple: true}},minWidth:100 ,enum: assinstitutional, fieldNames: { label: "name", value: "detailCode" }, }, { prop: "affiliatedSubjectName", label: "所属主体",minWidth:100 }, { prop: "affiliatedSubjectId", label: "所属主体", isShow:false, search: { el: "select" },minWidth:100,enum: assentityList, fieldNames: { label: "remarks", value: "detailCode" } }, { prop: "assetSourceTypeList", label: "资产来源", isShow:false, enum: async () => { // 获取资产来源 const data = await setValueClildList({dictCode:'SOURCE_ASSETS'}); configuration.sourceList = data['data'] return { data: data['data'] } }, fieldNames: { label: "itemLabel", value: "itemValue" }, search: { el: "select", props: { filterable: true , multiple: true}}, }, { prop: "sourceCode", label: "资产来源", minWidth:100 , }, { prop: "brand", label: "品牌", search: { el: "input" }, }, { prop: "specificationModel", label: "规格型号", search: { el: "input" },minWidth:100 }, { prop: "serialNumber", label: "序列号", search: { el: "input" } }, { prop: "measurementUnit", label: "计量单位",minWidth:100 }, { prop: "remarks", label: "备注",search: { el: "input" } }, { prop: "supplierName", label: "供应商", search: { el: "input" }, }, { prop: "inBoundNo", label: "入库单号",minWidth:100 }, { prop: "nonStandardAssetsId", label: "非标准资产", enum: async () => { // 获取非标准资产 const data = await nonstandardList({}); nonstandardData.value = data configuration.nonstandardList = data return { data: data } }, isShow: false, fieldNames: { label: "name", value: "id" }, search: { el: "select", props: { filterable: true } }, }, { prop: "purchaseDate", label: "购入日期", minWidth:148 , search: { el: "date-picker", span: 2, props: { type: "daterange", valueFormat: "YYYY-MM-DD" }, }, }, { prop: "maintenanceExpirationDate", label: "维保到期日期", isShow: false, search: { el: "date-picker", span: 2, props: { type: "daterange", valueFormat: "YYYY-MM-DD" }, }, }, { prop: "operation", label: "操作",fixed: "right", isShow: true, sortable: false } ]) // 批量导出 const batchExport = async (command: any) => { try { const selectedIds = proTable.value?.selectedListIds || []; // 验证择(如果command不是2,则需要择数据) if (command != 2 && selectedIds.length === 0) { ElMessage.error({ message: `请择要操作的数据` }); return; } const params = { idList: command === 2 ? [] : selectedIds // command=2表示导出全部 }; // 1. 获取文件数据(确保response是Blob或ArrayBuffer) const response = await assetListDown(params); // 2. 检查响应数据是否有效 if (!response) { ElMessage.error("导出失败:未获取到文件数据"); return; } // 3. 创建Blob对象(明确指定MIME类型) const blob = new Blob([response], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8" }); // 4. 创建下载链接 const url = window.URL.createObjectURL(blob); const link = document.createElement("a"); link.href = url; link.download = `资产清单_${new Date().toLocaleDateString()}.xlsx`; // 添加日期避免重复 // 5. 触发下载 document.body.appendChild(link); link.click(); // 6. 清理 setTimeout(() => { document.body.removeChild(link); window.URL.revokeObjectURL(url); }, 100); } catch (error) { console.error("导出失败:", error); } }; // 批量删除 const batchDelete = async (ids: string[]) => { if(ids && ids.length === 0) { ElMessage.error({ message: `请择要操作的数据` }); return } await useHandleData(deleteAssetList, { idList: ids }, `确认删除`); refreshTable() } // 批量编辑 const batcEdit = async (ids: string[]) => { if (ids && ids.length === 0) { ElMessage.error({ message: `请择要操作的数据` }); return; } // 从表格中获取当前所有中的行数据 const selectedRows = proTable.value?.selectedList || []; const types = selectedRows.map(row => row.type); const uniqueTypes = [...new Set(types)]; if (uniqueTypes.length > 1) { ElMessage.warning("只能择相同类型的资产进行批量编辑"); return; } selectedIds.value = ids; editBatchData('批量编辑', 'batchEdit', {}); } // 打印标签 const openTagPrint = async (ids: string[]) => { if(ids && ids.length === 0) { ElMessage.error({ message: `请择要操作的数据` }); return } const data = await printAssetMark({ idList: ids }); if (data.code == 0) { ElMessage.success({ message: data.msg }); refreshTable() } else { ElMessage.error({ message: data.msg }); } } const closeDialog = () => { dialogFormVisible.value = false // 清除表格勾项 if (proTable.value) { proTable.value.clearSelection(); } } // 子组件的上一步操作 const handlePreviousStep = () => { dialogFormVisible.value = true; // 重新打开对话框 // if( Type.value == 'batchEdit') {} // 回显之前择的数据(form 已在 openDrawer 时保存) nextTick(() => { ruleFormRef.value?.clearValidate(); // 清除校验状态 }); proTable.value!.setCheckedRows(proTable.value?.selectedList); // console.log('proTable.value',proTable.value?.selectedList) // proTable.value!.toggleRowSelection(proTable.value?.selectedList[0]) // rows.forEach((row) => { // multipleTableRef.value!.toggleRowSelection( // row, // undefined, // ignoreSelectable // ) // }) }; const Title = ref(""); const Type = ref('add') const Row = ref({}) // 新增 const addNewData = (title: string,type:string, row: any = {}) => { Title.value = title Type.value = type Row.value = row // 清空表单值 form.type = ''; form.nonStandardAssetsId = ''; // 重置表单校验状态 nextTick(() => { ruleFormRef.value?.resetFields(); }); dialogFormVisible.value = true } // 编辑 const editData = async(title: string,type:string, row: any = {}) => { const {code , data ,msg} = await assetListInfo({ id: row.id }); if(code == 0) { form.type = row.type form.nonStandardAssetsId = '' let listData = [data] Title.value = title Type.value = type Row.value = listData openDrawer() } else { ElMessage.error(msg); } } // 批量编辑 const editBatchData = (title: string,type:string, row: any = {}) => { Title.value = title Type.value = type Row.value = row // 清空表单值 form.type = ''; form.nonStandardAssetsId = ''; // 重置表单校验状态 nextTick(() => { ruleFormRef.value?.resetFields(); }); dialogFormVisible.value = true } // 查看详情 const assetInfoRef = ref<InstanceType<typeof addAsset> | null>(null); const handleAssetCodeClick = async(row: any) => { const params = { row:{...row}, api:deleteAssetList, configuration:configuration, refreshTable: () => { proTable.value!.pageable.pageNum = 1; proTable.value?.refreshData(); } } assetInfoRef.value?.acceptParams(params) } // 下一步 const nextTips = () => { ruleFormRef.value!.validate(async valid => { if (!valid) return; try { openDrawer() } catch (error) { console.log(error); } }) } // 新增/编辑 const ruleFormRef = ref<FormInstance>(); const addAssetRef = ref<InstanceType<typeof addAsset> | null>(null); const openDialog = () => { // 清空表单值 form.type = ''; form.nonStandardAssetsId = ''; // 重置表单校验状态 nextTick(() => { ruleFormRef.value?.resetFields(); }); dialogFormVisible.value = true } const openDrawer = () => { if(Type.value === 'add') { dialogFormVisible.value = false const params = { title:Title.value, type:Type.value, row:{...Row.value}, form:{...form}, configuration:configuration, isView:false, api: addAssetList, refreshTable: () => { proTable.value!.pageable.pageNum = 1; proTable.value?.refreshData(); } } addAssetRef.value?.acceptParams(params) } else if(Type.value === 'edit'){ const params = { title:Title.value, type:Type.value, configuration:configuration, isView:false, row:{}, form:{...form}, infoRow:{...Row.value}, api: editAssetList, refreshTable: () => { proTable.value!.pageable.pageNum = 1; proTable.value?.refreshData(); } } addAssetRef.value?.acceptParams(params) } else { dialogFormVisible.value = false const params = { title:Title.value, type:Type.value, configuration:configuration, isView:false, form:{...form}, row:{selectedIds:selectedIds.value}, api: editBatchAssetList, refreshTable: () => { proTable.value!.pageable.pageNum = 1; proTable.value?.refreshData(); } } addAssetRef.value?.acceptParams(params) } } onMounted(() => { getUnitList(); getOrgSubjectListData(); }) </script> <style lang="scss" scoped> .more_imgs{ div { display: inline-flex; } .viewImage{ width: 25px; height: 25px; cursor: pointer; } } /* 或仅针对特定列 */ .el-table .el-table__header th.is-center .cell { text-align: center; } /* 确保择列可见 */ ::v-deep .el-table__fixed-left { .el-table__cell.is-hidden > * { visibility: visible !important; } .el-checkbox { display: inline-block; } } </style>点击handlePreviousStep后,回显的数据
最新发布
08-26
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值