完成了 BOM 管理和生产管理,完成部分发料单、采购计划和调拨单。
This commit is contained in:
229
src/components/base/base-table-form/BaseTableForm.vue
Normal file
229
src/components/base/base-table-form/BaseTableForm.vue
Normal file
@@ -0,0 +1,229 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import type { TableInstance } from "element-plus";
|
||||
|
||||
// Props 定义
|
||||
const props = defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: "列表选择",
|
||||
},
|
||||
// 表格数据
|
||||
tableData: {
|
||||
type: Array as () => any[],
|
||||
default: () => [],
|
||||
},
|
||||
// 是否显示加载状态
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
// 用于行勾选的唯一键,默认 'id'
|
||||
rowKey: {
|
||||
type: String,
|
||||
default: "id",
|
||||
},
|
||||
// 是否默认全选(可选)
|
||||
defaultSelectAll: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
// 新增:是否显示底部统计信息
|
||||
showSummary: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
// 新增:自定义统计文本前缀,默认 "共"
|
||||
summaryText: {
|
||||
type: String,
|
||||
default: "共",
|
||||
},
|
||||
// 【新增】是否禁用所有行的选中功能
|
||||
// true: 所有行不可选 (复选框变灰)
|
||||
// false: 所有行可选 (默认)
|
||||
disabledSelection: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
useAuth: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
selectable: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
});
|
||||
|
||||
const visible = defineModel<boolean>("visible");
|
||||
|
||||
// Emits 定义
|
||||
const emit = defineEmits<{
|
||||
"update:visible": [value: boolean];
|
||||
"selection-change": [selection: any[]];
|
||||
"dialog-button-click": [eventName: string, buttonConfig: globalThis.ButtonProp, selectedRows: any[]];
|
||||
"filter-refresh": []; // 当父组件在 slot 中点击查询时,可触发此事件刷新数据
|
||||
}>();
|
||||
|
||||
const route = useRoute();
|
||||
const tableRef = ref<TableInstance>();
|
||||
|
||||
// 获取底部对话框按钮权限
|
||||
// 假设权限数据结构与参考代码类似,但来源是 dialogButtonAuth
|
||||
const dialogButtonList = computed<globalThis.ButtonProp[]>(() => {
|
||||
if (!props.useAuth) return [];
|
||||
const list = (route.meta.dialogButtonAuth as globalThis.ButtonProp[]) || [];
|
||||
// 按 rank 排序,rank 越小越靠前,没有 rank 的排后面
|
||||
return list.sort((a, b) => (a.rank ?? 999) - (b.rank ?? 999));
|
||||
});
|
||||
|
||||
// 内部状态
|
||||
const selectedRows = ref<any[]>([]);
|
||||
|
||||
// 监听弹窗打开,如果需要默认全选
|
||||
watch(
|
||||
() => visible,
|
||||
val => {
|
||||
if (val && props.defaultSelectAll && tableRef.value) {
|
||||
// 下一帧执行以确保 DOM 渲染完成
|
||||
setTimeout(() => {
|
||||
tableRef.value?.toggleAllSelection();
|
||||
}, 0);
|
||||
}
|
||||
if (!val) {
|
||||
// 关闭时清空选中
|
||||
selectedRows.value = [];
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// 处理表格勾选变化
|
||||
const handleSelectionChange = (selection: any[]) => {
|
||||
selectedRows.value = selection;
|
||||
emit("selection-change", selection);
|
||||
};
|
||||
|
||||
// 处理底部按钮点击
|
||||
const handleDialogButtonClick = (btn: globalThis.ButtonProp) => {
|
||||
emit("dialog-button-click", btn.eventName, btn, selectedRows.value);
|
||||
};
|
||||
|
||||
// 暴露方法给父组件(例如手动刷新表格或清空选中)
|
||||
const clearSelection = () => {
|
||||
tableRef.value?.clearSelection();
|
||||
};
|
||||
const toggleRowSelection = (row: any, selected?: boolean) => {
|
||||
tableRef.value?.toggleRowSelection(row, selected);
|
||||
};
|
||||
const checkPermission = (buttonEventName?: string) => {
|
||||
if (!buttonEventName) return true;
|
||||
return dialogButtonList.value.find(item => item.eventName === buttonEventName) !== undefined;
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
clearSelection,
|
||||
toggleRowSelection,
|
||||
tableRef,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-dialog v-model="visible" :title="title" :close-on-click-modal="false" width="80%">
|
||||
<!-- 1. 顶部筛选区域 (Slot) -->
|
||||
<div class="dialog-filter-area">
|
||||
<slot name="filter" :refresh="() => emit('filter-refresh')" :selected-rows="selectedRows">
|
||||
<!-- 默认空内容,由父组件填充 -->
|
||||
</slot>
|
||||
</div>
|
||||
|
||||
<!-- 2. 中部表格区域 -->
|
||||
<div class="dialog-table-area">
|
||||
<el-table
|
||||
ref="tableRef"
|
||||
:data="tableData"
|
||||
:row-key="rowKey"
|
||||
border
|
||||
stripe
|
||||
style="width: 100%"
|
||||
:loading="loading"
|
||||
max-height="50vh"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<!-- 勾选列 -->
|
||||
<el-table-column type="selection" width="55" :reserve-selection="true" v-if="selectable" />
|
||||
|
||||
<!-- 父组件自定义列 -->
|
||||
<slot name="table-columns"></slot>
|
||||
|
||||
<!-- 如果没有权限按钮且父组件也没传操作列,可以留空或显示提示 -->
|
||||
</el-table>
|
||||
|
||||
<!-- 提示信息:当无数据时 -->
|
||||
<el-empty v-if="!loading && tableData.length === 0" description="暂无数据" />
|
||||
</div>
|
||||
|
||||
<!-- 3. 底部区域 (修改部分) -->
|
||||
<template #footer>
|
||||
<div class="dialog-footer-container">
|
||||
<!-- 左侧:统计信息 -->
|
||||
<div class="footer-summary" v-if="showSummary">
|
||||
<span class="total-count">{{ summaryText }} {{ tableData.length }} 条</span>
|
||||
<span v-if="selectedRows.length > 0" class="selected-count">(已选 {{ selectedRows.length }} 条)</span>
|
||||
</div>
|
||||
|
||||
<!-- 右侧:操作按钮 -->
|
||||
<div class="footer-actions">
|
||||
<slot name="attachment" :selected-rows="selectedRows" :checkPermission="checkPermission">
|
||||
<!-- 默认内容为空,不影响原有布局 -->
|
||||
</slot>
|
||||
<el-button
|
||||
v-for="btn in dialogButtonList"
|
||||
:key="btn.buttonName"
|
||||
:type="btn.colorType || 'primary'"
|
||||
:disabled="selectedRows.length === 0 && !['cancel'].includes(btn.eventName)"
|
||||
@click="handleDialogButtonClick(btn)"
|
||||
>
|
||||
{{ $t("_button." + btn.eventName) || btn.text }}
|
||||
</el-button>
|
||||
|
||||
<el-button @click="visible = false">
|
||||
{{ $t("_button.cancel") || "取消" }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.footer-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px; /* 确保附加组件和按钮之间有间距 */
|
||||
}
|
||||
.dialog-footer-container {
|
||||
display: flex;
|
||||
justify-content: space-between; /* 左右两端对齐 */
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
.dialog-filter-area {
|
||||
margin-bottom: 16px;
|
||||
padding: 10px;
|
||||
background-color: var(--el-fill-color-lighter);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.dialog-table-area {
|
||||
min-height: 200px;
|
||||
max-height: 60vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.dialog-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user