tmagic-editor/packages/table/src/ActionsColumn.vue
2026-07-27 06:52:18 +00:00

203 lines
5.2 KiB
Vue

<template>
<template v-for="(action, actionIndex) in config.actions" :key="actionIndex">
<ActionPopconfirm
v-if="action.popconfirm"
:action="action"
:row="row"
:index="index"
:visible="display(action.display, row) && !editState[index]"
@confirm="actionHandler"
/>
<TMagicPopover
v-else-if="action.type === 'sub-actions'"
v-model:visible="popoverVisible"
trigger="click"
:placement="action.subActionConfig?.placement || 'bottom'"
:width="action.subActionConfig?.popoverWidth"
:popper-class="action.subActionConfig?.popoverClass"
:destroy-on-close="action.subActionConfig?.popoverDestroyOnClose ?? true"
@clickoutside="popoverVisible = false"
>
<template #reference>
<TMagicButton
v-show="action.subActionConfig?.items?.length && display(action.display, row) && !editState[index]"
class="action-btn"
link
size="small"
:type="action.buttonType || 'primary'"
:icon="popoverVisible ? ArrowDown : ArrowRight"
@click.stop="togglePopover"
></TMagicButton>
</template>
<div class="tmagic-table-sub-actions">
<template v-for="(subAction, subIndex) in action.subActionConfig?.items" :key="subIndex">
<ActionPopconfirm
v-if="subAction.popconfirm"
:action="subAction"
:row="row"
:index="index"
btn-class="sub-action-btn"
:visible="display(subAction.display, row)"
@confirm="actionHandler"
/>
<ActionButton
v-else
:action="subAction"
:row="row"
:index="index"
btn-class="sub-action-btn"
:visible="display(subAction.display, row)"
@click="actionHandler"
/>
</template>
</div>
</TMagicPopover>
<ActionButton
v-else
:action="action"
:row="row"
:index="index"
:visible="display(action.display, row) && !editState[index]"
@click="actionHandler"
/>
</template>
<TMagicButton
class="action-btn"
v-show="editState[index]"
link
type="primary"
size="small"
@click="save(index, config)"
>保存</TMagicButton
>
<TMagicButton
class="action-btn"
v-show="editState[index]"
link
type="danger"
size="small"
@click="cancel(index, config)"
>取消</TMagicButton
>
</template>
<script lang="ts">
import { ref } from 'vue';
// 所有行共享:同一时间仅允许一个 sub-actions 气泡展开,激活新的会自动关闭上一个
const activePopoverId = ref<symbol | null>(null);
</script>
<script lang="ts" setup>
import { watch } from 'vue';
import { ArrowDown, ArrowRight } from '@element-plus/icons-vue';
import { cloneDeep } from 'lodash-es';
import { TMagicButton, tMagicMessage, TMagicPopover } from '@tmagic/design';
import ActionButton from './ActionButton.vue';
import { display } from './actionHelpers';
import ActionPopconfirm from './ActionPopconfirm.vue';
import { ColumnActionConfig, ColumnConfig } from './schema';
defineOptions({
name: 'MTableActionsColumn',
});
const props = withDefaults(
defineProps<{
columns: ColumnConfig[];
config: ColumnConfig;
rowkeyName?: string;
editState?: any;
row: any;
index: number;
}>(),
{
columns: () => [],
config: () => ({}),
rowkeyName: 'c_id',
editState: () => [],
},
);
const popoverId = Symbol('sub-actions-popover');
const popoverVisible = ref(false);
watch(popoverVisible, (visible) => {
if (visible) {
activePopoverId.value = popoverId;
} else if (activePopoverId.value === popoverId) {
activePopoverId.value = null;
}
});
watch(activePopoverId, (id) => {
if (id !== popoverId && popoverVisible.value) {
popoverVisible.value = false;
}
});
const togglePopover = () => {
popoverVisible.value = !popoverVisible.value;
};
const emit = defineEmits<{
'after-action': [{ index: number }];
'after-action-cancel': [{ index: number }];
}>();
const actionHandler = async (action: ColumnActionConfig, row: any, index: number) => {
await action.before?.(row, index);
if (action.type === 'edit') {
props.editState[index] = cloneDeep(row);
} else {
await action.handler?.(row, index);
}
await action.after?.(row, index);
popoverVisible.value = false;
};
const save = async (index: number, config: ColumnConfig) => {
const action = config.actions?.find((item) => item.type === 'edit')?.action;
if (!action) return;
const res: any = await action({
data: props.editState[index],
index,
});
if (res) {
if (res.ret === 0) {
tMagicMessage.success('保存成功');
props.editState[index] = undefined;
emit('after-action', { index });
} else {
tMagicMessage.error(res.msg || '保存失败');
}
} else {
props.editState[index] = undefined;
emit('after-action', { index });
}
};
const cancel = async (index: number, config: ColumnConfig) => {
props.editState[index] = undefined;
const cancel = config.actions?.find((item) => item.type === 'edit')?.cancel;
if (cancel) {
await cancel({ index });
}
emit('after-action-cancel', { index });
};
defineExpose({
actionHandler,
save,
cancel,
});
</script>