<!-- 📚📚📚 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后,回显勾选的数据