fix:完善proTablev2组件
This commit is contained in:
@@ -40,10 +40,12 @@
|
||||
|
||||
<script setup lang="tsx">
|
||||
import { ref, computed, onMounted, onUnmounted, nextTick } from "vue";
|
||||
import { ElTag, ElButton, ElText } from "element-plus";
|
||||
import { ElTag, ElButton, ElText,ElTooltip } from "element-plus";
|
||||
import { debounce } from "lodash-es";
|
||||
import dayjs from "dayjs";
|
||||
import { DictManage } from "@/dict";
|
||||
import { usePermission } from "@/utils/permission";
|
||||
const { checkPermission } = usePermission();
|
||||
|
||||
// 组件属性
|
||||
const props = defineProps({
|
||||
@@ -111,11 +113,20 @@ const RenderFactory = {
|
||||
},
|
||||
// 文本自动省略
|
||||
ellipsis: (scope: any, col: any) => {
|
||||
const val = scope.rowData[col.prop] || "-";
|
||||
const val = scope.rowData[col.prop] ?? "-";
|
||||
return (
|
||||
<div class="mj-ellipsis" title={val}>
|
||||
<ElTooltip
|
||||
effect="dark"
|
||||
content={String(val)}
|
||||
placement="top"
|
||||
// 只有当内容不是 "-" 且达到截断条件时才显示(可选优化)
|
||||
fallback-placements={['bottom', 'top']}
|
||||
enterable={false}
|
||||
>
|
||||
<div class="mj-ellipsis-cell">
|
||||
{val}
|
||||
</div>
|
||||
</ElTooltip>
|
||||
);
|
||||
},
|
||||
};
|
||||
@@ -195,6 +206,37 @@ const adaptedColumns = computed(() => {
|
||||
fixed: col.fixed,
|
||||
align: col.align || "left",
|
||||
cellRenderer: (scope: any) => {
|
||||
const isOp = col.prop === 'actions' || col.valueType === 'actions';
|
||||
const cellClass = isOp ? 'operation-column-cell' : '';
|
||||
// 2. 如果是操作列,包裹一层带 class 的 div
|
||||
if (isOp && col.actions) {
|
||||
return (
|
||||
<div class={cellClass}>
|
||||
<div class="v2-operation-btns">
|
||||
{col.actions.map(btn => {
|
||||
// --- 权限校验开始 ---
|
||||
if (btn.permission) {
|
||||
const hasAuth = checkPermission(btn.permission);
|
||||
if (!hasAuth) return null; // 没权限,直接不渲染
|
||||
}
|
||||
|
||||
// 2. 现有的 show 逻辑判断(业务逻辑显隐)
|
||||
const isShow = typeof btn.show === 'function' ? btn.show(scope.rowData) : true;
|
||||
if (!isShow) return null;
|
||||
const { onClick, label, permission, show, ...otherProps } = btn;
|
||||
return (
|
||||
<ElButton
|
||||
{...otherProps}
|
||||
onClick={() => btn.onClick(scope.rowData)}
|
||||
>
|
||||
{btn.label}
|
||||
</ElButton>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (typeof col.render === "function") return col.render(scope);
|
||||
if (col.valueType && RenderFactory[col.valueType]) {
|
||||
return RenderFactory[col.valueType](scope, col);
|
||||
@@ -228,7 +270,7 @@ const fetchTableData = async (isReset = false) => {
|
||||
|
||||
innerData.value = isReset ? records : [...innerData.value, ...records];
|
||||
total.value = res?.total || 0;
|
||||
if (innerData.value.length >= total.value || newList.length < props.pageSize) {
|
||||
if (innerData.value.length >= total.value || records.length < props.pageSize) {
|
||||
noMore.value = true;
|
||||
}
|
||||
pageNo.value++;
|
||||
@@ -275,6 +317,7 @@ defineExpose({
|
||||
updateRow,
|
||||
removeRow,
|
||||
addRow,
|
||||
updateSize,
|
||||
getCurrentParams: () => ({
|
||||
pageNo: pageNo.value - 1, // 因为 fetch 完后 pageNo 会自增,所以要减 1 才是当前页
|
||||
pageSize: props.pageSize,
|
||||
@@ -368,4 +411,26 @@ defineExpose({
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.operation-column-cell) {
|
||||
// 默认隐藏内容,但保留位置
|
||||
.v2-operation-btns {
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 当鼠标移入行时,显示该行内的操作按钮
|
||||
:deep(.el-table-v2__row:hover) {
|
||||
background-color: #f5f7fa; // 顺便加个行高亮
|
||||
|
||||
.operation-column-cell .v2-operation-btns {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -45,12 +45,15 @@ export const useTableAction = (tableRef: any) => {
|
||||
* @param deleteApi 删除接口函数
|
||||
*/
|
||||
const handleDelete = async (
|
||||
id: string | number,
|
||||
deleteApi: (id: any) => Promise<any>
|
||||
deleteApi: (id: any) => Promise<any>,
|
||||
...args: any[]
|
||||
) => {
|
||||
try {
|
||||
const res = await deleteApi(id);
|
||||
const res = await deleteApi(...args);
|
||||
const id = args[0];
|
||||
if(id){
|
||||
tableRef.value?.removeRow(id);
|
||||
}
|
||||
return res;
|
||||
} catch (error) {
|
||||
// 捕获取消行为或接口错误
|
||||
|
||||
@@ -86,33 +86,19 @@
|
||||
<!-- Table列表 -->
|
||||
<CommonTable
|
||||
ref="tableRef"
|
||||
v-model:data="list"
|
||||
:data="list"
|
||||
:columns="columns"
|
||||
pagination
|
||||
height="calc(100vh - 186px)"
|
||||
:immediate="false"
|
||||
:total="total"
|
||||
:request-api="fetchData"
|
||||
>
|
||||
<!-- 名称点击 -->
|
||||
<template #labelName="{ row }">
|
||||
<!-- <template #labelName="{ row }">
|
||||
<el-button link type="primary" @click="onLevelNext(row)" v-if="!hasChild">{{
|
||||
row.label
|
||||
}}</el-button>
|
||||
|
||||
<span v-else>{{ row.label }}</span>
|
||||
</template>
|
||||
<!-- 状态插槽 -->
|
||||
<template #status="{ row }">
|
||||
<div
|
||||
class="mj-status-dot"
|
||||
:style="{
|
||||
'--data-status-color': DictManage.statusDictColor[row.status],
|
||||
}"
|
||||
@click="handleDictStatus(row)"
|
||||
>
|
||||
{{ dicts.permission_list_enable_disable.find(item=>item.value == row.status)?.label }}
|
||||
</div>
|
||||
</template>
|
||||
</template> -->
|
||||
</CommonTable>
|
||||
<!-- 新增字段 -->
|
||||
<dictFieldLevelManage
|
||||
@@ -133,12 +119,15 @@
|
||||
</el-drawer>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import CommonTable from "@/components/proTable/index.vue";
|
||||
import { h } from "vue";
|
||||
import CommonTable from "@/components/proTable/proTablev2.vue";
|
||||
import dayjs from "dayjs";
|
||||
import dictFieldLevelManage from "./dictFieldLevelManage.vue";
|
||||
import { DictManage } from "@/dict";
|
||||
import { useDict } from '@/hooks/useDictData';
|
||||
const { dicts,refresh } = useDict('permission_list_enable_disable');
|
||||
import { useTableAction } from "@/hooks/useTableAction";
|
||||
import { useDict } from "@/hooks/useDictData";
|
||||
const { dicts, refresh } = useDict("permission_list_enable_disable");
|
||||
|
||||
import {
|
||||
getDictTypeValue,
|
||||
deleteDictTypeValue,
|
||||
@@ -157,26 +146,35 @@ const filterForm = reactive({
|
||||
});
|
||||
const size = ref<string>(""); //抽屉大小
|
||||
const tableRef = ref(null);
|
||||
const { handleAction, handleDelete: runDelete } = useTableAction(tableRef);
|
||||
const visible = ref<boolean>(false);
|
||||
const parentId = ref<string>("");
|
||||
const total = ref(0);
|
||||
const list = ref([]);
|
||||
const hasChild = ref<boolean>(false); //是否是子级弹窗
|
||||
const childId = ref<string|number>(''); // 子集的id
|
||||
const childId = ref<string | number>(""); // 子集的id
|
||||
const childModals = ref([]); //子弹窗的列表
|
||||
const childModalRefs = ref({}); // 子弹窗的引用
|
||||
const onCloseCallback = ref<Function | null>(null); // 关闭回调函数
|
||||
const columns = computed(() => [
|
||||
{
|
||||
prop: "id",
|
||||
align:'center',
|
||||
label: "字典编码",
|
||||
},
|
||||
{
|
||||
prop: "label",
|
||||
label: "字典名称",
|
||||
align: "center",
|
||||
showOverflowTooltip:true,
|
||||
slot: "labelName",
|
||||
valueType: "ellipsis",
|
||||
render: ({ rowData }: any) => {
|
||||
if (hasChild.value) {
|
||||
return h("span", { class: "v2-cell-text" }, rowData.label);
|
||||
}
|
||||
return h(ElButton, { type: "primary", link:true,onClick:()=>{
|
||||
onLevelNext(rowData);
|
||||
} }, () => rowData.label);
|
||||
}
|
||||
},
|
||||
{
|
||||
prop: "value",
|
||||
@@ -188,7 +186,11 @@ const columns = computed(()=>[
|
||||
prop: "status",
|
||||
label: "状态",
|
||||
align: "center",
|
||||
slot: "status",
|
||||
valueType: "status",
|
||||
options: computed(() => dicts.value.permission_list_enable_disable),
|
||||
onClick: ({ cellValue, rowData }) => {
|
||||
handleDictStatus(rowData);
|
||||
},
|
||||
},
|
||||
{
|
||||
prop: "sort",
|
||||
@@ -199,18 +201,14 @@ const columns = computed(()=>[
|
||||
prop: "updateTime",
|
||||
label: "更新时间",
|
||||
align: "center",
|
||||
showOverflowTooltip: true,
|
||||
formatter: (val) => {
|
||||
return val.updateTime
|
||||
? dayjs(val.updateTime).format("YYYY-MM-DD HH:mm")
|
||||
: "-";
|
||||
},
|
||||
valueType: "date",
|
||||
format: "YYYY-MM-DD HH:mm",
|
||||
},
|
||||
{
|
||||
prop: "actions",
|
||||
label: "操作",
|
||||
align: "right",
|
||||
width:200,
|
||||
width: 300,
|
||||
actions: [
|
||||
{
|
||||
label: "添加二级字段",
|
||||
@@ -218,7 +216,7 @@ const columns = computed(()=>[
|
||||
link: true,
|
||||
permission: ["edit"],
|
||||
show: () => {
|
||||
return !hasChild.value
|
||||
return !hasChild.value;
|
||||
},
|
||||
onClick: (row) => handleAddNext(row),
|
||||
},
|
||||
@@ -236,9 +234,9 @@ const columns = computed(()=>[
|
||||
permission: ["delete"],
|
||||
onClick: (row) => handleDelete(row),
|
||||
},
|
||||
]
|
||||
],
|
||||
},
|
||||
])
|
||||
]);
|
||||
|
||||
// 设置子弹窗引用
|
||||
const setChildModalRef = (el, key) => {
|
||||
@@ -248,7 +246,9 @@ const setChildModalRef = (el, key) => {
|
||||
};
|
||||
// 点击获取二级菜单数据
|
||||
const onLevelNext = (row) => {
|
||||
const childKey = `child-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
||||
const childKey = `child-${Date.now()}-${Math.random()
|
||||
.toString(36)
|
||||
.substr(2, 9)}`;
|
||||
childModals.value.push({
|
||||
key: childKey,
|
||||
data: row,
|
||||
@@ -262,7 +262,7 @@ const onLevelNext = (row) => {
|
||||
...row,
|
||||
parentId: parentId.value,
|
||||
hasChild: true,
|
||||
onClose: () => removeChildModal(childKey)
|
||||
onClose: () => removeChildModal(childKey),
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -270,7 +270,7 @@ const onLevelNext = (row) => {
|
||||
|
||||
// 移除当前组件
|
||||
const removeChildModal = (key: string) => {
|
||||
const index = childModals.value.findIndex(child => child.key === key);
|
||||
const index = childModals.value.findIndex((child) => child.key === key);
|
||||
if (index !== -1) {
|
||||
childModals.value.splice(index, 1);
|
||||
}
|
||||
@@ -285,9 +285,11 @@ const fetchData = async (params) => {
|
||||
...params,
|
||||
keyword: searchQuery.value,
|
||||
...filterForm,
|
||||
}
|
||||
};
|
||||
|
||||
const response = hasChild.value ? await getNextDictMenu(parentId.value,childId.value,queryParams) : await getDictTypeValue(parentId.value, queryParams);
|
||||
const response = hasChild.value
|
||||
? await getNextDictMenu(parentId.value, childId.value, queryParams)
|
||||
: await getDictTypeValue(parentId.value, queryParams);
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.log("getTableData Error", error);
|
||||
@@ -318,13 +320,20 @@ const addFields = () => {
|
||||
value: "",
|
||||
sort: 0,
|
||||
status: 1,
|
||||
remark:''
|
||||
})
|
||||
remark: "",
|
||||
});
|
||||
};
|
||||
|
||||
// 确定刷新数据
|
||||
const onConfirmSuccess = () => {
|
||||
tableRef.value && tableRef.value.refresh();
|
||||
if (selectItem.id) {
|
||||
handleAction(Promise.resolve(true), selectItem.id, getTableData, {
|
||||
showMsg: false,
|
||||
silentUpdate: true,
|
||||
});
|
||||
} else {
|
||||
tableRef.value?.refresh();
|
||||
}
|
||||
};
|
||||
|
||||
// 关闭popover 重置数据信息
|
||||
@@ -358,6 +367,8 @@ const handleDelete = async (item) => {
|
||||
try {
|
||||
await deleteDictTypeValue(parentId.value, item.id);
|
||||
tableRef.value && tableRef.value.refresh();
|
||||
|
||||
await runDelete();
|
||||
} catch (error) {
|
||||
console.log("fetch error", error);
|
||||
}
|
||||
@@ -382,7 +393,7 @@ defineExpose({
|
||||
}
|
||||
await nextTick();
|
||||
if (tableRef.value) {
|
||||
await tableRef.value.refresh();
|
||||
await tableRef.value.updateSize();
|
||||
}
|
||||
},
|
||||
close() {
|
||||
|
||||
@@ -101,7 +101,6 @@ import { ElMessage } from "element-plus";
|
||||
import { useTableAction } from "@/hooks/useTableAction";
|
||||
|
||||
import { useDict } from "@/hooks/useDictData";
|
||||
import { render } from "vue";
|
||||
const { dicts, refresh } = useDict("permission_list_enable_disable");
|
||||
|
||||
defineOptions({ name: "Dictionary" });
|
||||
@@ -183,37 +182,29 @@ const columns = [
|
||||
label: "操作",
|
||||
align: "right",
|
||||
width: "300",
|
||||
render: ({ rowData }: any) => {
|
||||
return h("div", { class: "space-x-2" }, [
|
||||
h(
|
||||
ElButton,
|
||||
actions: [
|
||||
{
|
||||
link: true,
|
||||
label: "编辑",
|
||||
type: "primary",
|
||||
onClick: () => handleEdit(rowData),
|
||||
},
|
||||
() => "编辑"
|
||||
),
|
||||
h(
|
||||
ElButton,
|
||||
{
|
||||
link:true,
|
||||
permission: ["edit"],
|
||||
onClick: (row) => handleEdit(row),
|
||||
},
|
||||
{
|
||||
label: "字段配置",
|
||||
type: "primary",
|
||||
onClick: () => handlefieldsConfig(rowData),
|
||||
},
|
||||
() => "字段配置"
|
||||
),
|
||||
h(
|
||||
ElButton,
|
||||
{
|
||||
link:true,
|
||||
permission: ["config"],
|
||||
onClick: (row) => handlefieldsConfig(row),
|
||||
},
|
||||
{
|
||||
label: "删除",
|
||||
type: "danger",
|
||||
onClick: () => handleDelete(rowData),
|
||||
},
|
||||
() => "删除"
|
||||
),
|
||||
]);
|
||||
},
|
||||
link:true,
|
||||
permission: ["delete"],
|
||||
onClick: (row) => handleDelete(row),
|
||||
}
|
||||
]
|
||||
},
|
||||
];
|
||||
|
||||
@@ -302,7 +293,7 @@ const handleDelete = async (item) => {
|
||||
.then(async () => {
|
||||
try {
|
||||
// await deleteDictValue(item.id);
|
||||
await runDelete(item.id, deleteDictValue);
|
||||
await runDelete(deleteDictValue,item.id);
|
||||
} catch (error) {
|
||||
console.log("fetch error", error);
|
||||
}
|
||||
|
||||
@@ -196,3 +196,12 @@ body {
|
||||
height: 34px;
|
||||
}
|
||||
}
|
||||
|
||||
.mj-ellipsis-cell {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
word-break: break-all;
|
||||
cursor: default;
|
||||
}
|
||||
Reference in New Issue
Block a user