feat(personal-center): 重构个人事项详情并复用任务工作日志组件
This commit is contained in:
@@ -1,12 +1,13 @@
|
||||
import { computed, defineComponent, ref } from 'vue';
|
||||
import type { PropType } from 'vue';
|
||||
import { ElButton, ElPopover } from 'element-plus';
|
||||
import { computed, defineComponent, h, ref } from 'vue';
|
||||
import type { Component, PropType } from 'vue';
|
||||
import { ElButton, ElPopover, ElTooltip } from 'element-plus';
|
||||
import { $t } from '@/locales';
|
||||
|
||||
export type BusinessTableAction = {
|
||||
key: string;
|
||||
label: string;
|
||||
buttonType?: 'primary' | 'success' | 'warning' | 'danger' | 'info';
|
||||
icon?: Component;
|
||||
disabled?: boolean;
|
||||
onClick: () => void | Promise<void>;
|
||||
};
|
||||
@@ -17,12 +18,20 @@ export default defineComponent({
|
||||
actions: {
|
||||
type: Array as PropType<BusinessTableAction[]>,
|
||||
required: true
|
||||
},
|
||||
variant: {
|
||||
type: String as PropType<'button' | 'icon'>,
|
||||
default: 'button'
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const popoverVisible = ref(false);
|
||||
|
||||
const directActions = computed(() => {
|
||||
if (props.variant === 'icon') {
|
||||
return props.actions;
|
||||
}
|
||||
|
||||
if (props.actions.length <= 2) {
|
||||
return props.actions;
|
||||
}
|
||||
@@ -31,6 +40,10 @@ export default defineComponent({
|
||||
});
|
||||
|
||||
const moreActions = computed(() => {
|
||||
if (props.variant === 'icon') {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (props.actions.length <= 2) {
|
||||
return [];
|
||||
}
|
||||
@@ -47,21 +60,86 @@ export default defineComponent({
|
||||
await action.onClick();
|
||||
}
|
||||
|
||||
return () => (
|
||||
<div class="business-table-action-cell" onClick={event => event.stopPropagation()}>
|
||||
{directActions.value.map(action => (
|
||||
function renderIcon(action: BusinessTableAction) {
|
||||
if (!action.icon) return null;
|
||||
|
||||
return h(action.icon, { class: 'business-table-action-icon' });
|
||||
}
|
||||
|
||||
function renderButtonAction(action: BusinessTableAction) {
|
||||
return (
|
||||
<ElButton
|
||||
key={action.key}
|
||||
plain
|
||||
size="small"
|
||||
type={action.buttonType}
|
||||
disabled={action.disabled}
|
||||
class="business-table-action-button"
|
||||
onClick={() => handleAction(action)}
|
||||
>
|
||||
{action.label}
|
||||
</ElButton>
|
||||
);
|
||||
}
|
||||
|
||||
function renderIconAction(action: BusinessTableAction) {
|
||||
return (
|
||||
<ElTooltip key={action.key} content={action.label} placement="top">
|
||||
<ElButton
|
||||
key={action.key}
|
||||
plain
|
||||
link
|
||||
size="small"
|
||||
type={action.buttonType}
|
||||
disabled={action.disabled}
|
||||
class="business-table-action-button"
|
||||
class="business-table-action-icon-button"
|
||||
aria-label={action.label}
|
||||
onClick={() => handleAction(action)}
|
||||
>
|
||||
{action.label}
|
||||
{renderIcon(action)}
|
||||
</ElButton>
|
||||
))}
|
||||
</ElTooltip>
|
||||
);
|
||||
}
|
||||
|
||||
function renderMenuButton(action: BusinessTableAction) {
|
||||
if (props.variant === 'icon') {
|
||||
return (
|
||||
<ElButton
|
||||
key={action.key}
|
||||
link
|
||||
size="small"
|
||||
type={action.buttonType}
|
||||
disabled={action.disabled}
|
||||
class="business-table-action-menu__link"
|
||||
onClick={() => handleAction(action)}
|
||||
>
|
||||
<span class="business-table-action-menu__item">
|
||||
{renderIcon(action)}
|
||||
<span>{action.label}</span>
|
||||
</span>
|
||||
</ElButton>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ElButton
|
||||
key={action.key}
|
||||
plain
|
||||
size="small"
|
||||
type={action.buttonType}
|
||||
disabled={action.disabled}
|
||||
class="business-table-action-menu__button"
|
||||
onClick={() => handleAction(action)}
|
||||
>
|
||||
{action.label}
|
||||
</ElButton>
|
||||
);
|
||||
}
|
||||
|
||||
return () => (
|
||||
<div class="business-table-action-cell" onClick={event => event.stopPropagation()}>
|
||||
{directActions.value.map(action =>
|
||||
props.variant === 'icon' ? renderIconAction(action) : renderButtonAction(action)
|
||||
)}
|
||||
|
||||
{moreActions.value.length > 0 && (
|
||||
<ElPopover
|
||||
@@ -74,32 +152,28 @@ export default defineComponent({
|
||||
{{
|
||||
reference: () => (
|
||||
<ElButton
|
||||
plain
|
||||
link={props.variant === 'icon'}
|
||||
plain={props.variant !== 'icon'}
|
||||
size="small"
|
||||
class="business-table-action-button"
|
||||
class={
|
||||
props.variant === 'icon' ? 'business-table-action-icon-button' : 'business-table-action-button'
|
||||
}
|
||||
aria-label={$t('common.more')}
|
||||
onClick={event => event.stopPropagation()}
|
||||
>
|
||||
<span class="inline-flex items-center gap-4px">
|
||||
{$t('common.more')}
|
||||
<icon-mdi-chevron-down class="text-14px" />
|
||||
</span>
|
||||
{props.variant === 'icon' ? (
|
||||
<icon-mdi-dots-horizontal class="business-table-action-icon" />
|
||||
) : (
|
||||
<span class="inline-flex items-center gap-4px">
|
||||
{$t('common.more')}
|
||||
<icon-mdi-chevron-down class="text-14px" />
|
||||
</span>
|
||||
)}
|
||||
</ElButton>
|
||||
),
|
||||
default: () => (
|
||||
<div class="business-table-action-menu">
|
||||
{moreActions.value.map(action => (
|
||||
<ElButton
|
||||
key={action.key}
|
||||
plain
|
||||
size="small"
|
||||
type={action.buttonType}
|
||||
disabled={action.disabled}
|
||||
class="business-table-action-menu__button"
|
||||
onClick={() => handleAction(action)}
|
||||
>
|
||||
{action.label}
|
||||
</ElButton>
|
||||
))}
|
||||
{moreActions.value.map(action => renderMenuButton(action))}
|
||||
</div>
|
||||
)
|
||||
}}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { computed, watch } from 'vue';
|
||||
import { useDictStore } from '@/store/modules/dict';
|
||||
import { useDict } from '@/hooks/business/dict';
|
||||
|
||||
defineOptions({ name: 'DictSelect' });
|
||||
|
||||
const ensuredEmptyDictCodes = new Set<string>();
|
||||
|
||||
interface Props {
|
||||
dictCode: string;
|
||||
placeholder?: string;
|
||||
@@ -34,6 +37,7 @@ const model = defineModel<string | number | Array<string | number> | null | unde
|
||||
default: undefined
|
||||
});
|
||||
|
||||
const dictStore = useDictStore();
|
||||
const { enabledDictData, dictData } = useDict(() => props.dictCode);
|
||||
|
||||
const dictOptions = computed(() => {
|
||||
@@ -53,6 +57,19 @@ const selectedColorType = computed<string | null>(() => {
|
||||
if (value === null || value === undefined || value === '') return null;
|
||||
return dictOptions.value.find(opt => opt.value === value)?.colorType ?? null;
|
||||
});
|
||||
|
||||
watch(
|
||||
() => [props.dictCode, dictOptions.value.length, dictStore.initialized, dictStore.loading] as const,
|
||||
async ([dictCode, optionCount, initialized, loading]) => {
|
||||
if (!dictCode || optionCount > 0 || !initialized || loading || ensuredEmptyDictCodes.has(dictCode)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ensuredEmptyDictCodes.add(dictCode);
|
||||
await dictStore.ensureDictData(dictCode, true);
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
Reference in New Issue
Block a user