668 lines
19 KiB
Vue
668 lines
19 KiB
Vue
<script setup lang="tsx">
|
|
import { computed, markRaw, nextTick, onActivated, reactive, ref } from 'vue';
|
|
import type { TableInstance } from 'element-plus';
|
|
import { ElButton, ElMessageBox, ElTag, ElTooltip } from 'element-plus';
|
|
import { useBoolean } from '@sa/hooks';
|
|
import {
|
|
fetchBatchDeletePersonalItems,
|
|
fetchBindPersonalItemsToExecution,
|
|
fetchChangePersonalItemStatus,
|
|
fetchDeletePersonalItem,
|
|
fetchGetPersonalItemDetail,
|
|
fetchGetPersonalItemPage
|
|
} from '@/service/api';
|
|
import { useAuthStore } from '@/store/modules/auth';
|
|
import { useUIPaginatedTable } from '@/hooks/common/table';
|
|
import PersonalItemBindExecutionDialog from './modules/personal-item-bind-execution-dialog.vue';
|
|
import PersonalItemDetailDialog from './modules/personal-item-detail-dialog.vue';
|
|
import PersonalItemOperateDialog from './modules/personal-item-operate-dialog.vue';
|
|
import PersonalItemSearch from './modules/personal-item-search.vue';
|
|
import PersonalItemStatusActionDialog from './modules/personal-item-status-action-dialog.vue';
|
|
import {
|
|
formatPersonalItemDateRange,
|
|
formatPersonalItemDateTime,
|
|
formatPersonalItemOwnerName,
|
|
formatPersonalItemProgress,
|
|
getPersonalItemStatusLabel,
|
|
resolvePersonalItemStatusTagType
|
|
} from './modules/personal-item-shared';
|
|
import IconMdiCheckCircleOutline from '~icons/mdi/check-circle-outline';
|
|
import IconMdiClipboardEditOutline from '~icons/mdi/clipboard-edit-outline';
|
|
import IconMdiCloseCircleOutline from '~icons/mdi/close-circle-outline';
|
|
import IconMdiDeleteOutline from '~icons/mdi/delete-outline';
|
|
import IconMdiPause from '~icons/mdi/pause';
|
|
import IconMdiPencilOutline from '~icons/mdi/pencil-outline';
|
|
import IconMdiPlay from '~icons/mdi/play';
|
|
import IconMdiRestart from '~icons/mdi/restart';
|
|
import IconMdiSync from '~icons/mdi/sync';
|
|
|
|
defineOptions({ name: 'MyItem' });
|
|
|
|
type DetailTab = 'worklog';
|
|
type PersonalItemOperateType = UI.TableOperateType | 'view';
|
|
|
|
interface PersonalItemRowAction {
|
|
key: string;
|
|
tooltip: string;
|
|
icon: object;
|
|
type: 'primary' | 'success' | 'warning' | 'danger';
|
|
disabled?: boolean;
|
|
onClick: () => void | Promise<void>;
|
|
}
|
|
|
|
const lifecycleActionIconMap: Record<string, object> = {
|
|
start: markRaw(IconMdiPlay),
|
|
pause: markRaw(IconMdiPause),
|
|
resume: markRaw(IconMdiRestart),
|
|
reopen: markRaw(IconMdiRestart),
|
|
cancel: markRaw(IconMdiCloseCircleOutline),
|
|
complete: markRaw(IconMdiCheckCircleOutline)
|
|
};
|
|
|
|
const lifecycleActionTypeMap: Record<string, PersonalItemRowAction['type']> = {
|
|
cancel: 'danger',
|
|
pause: 'warning',
|
|
complete: 'success',
|
|
resume: 'primary',
|
|
reopen: 'primary',
|
|
start: 'primary'
|
|
};
|
|
|
|
const lifecycleActionOrder: Record<string, number> = {
|
|
pause: 1,
|
|
cancel: 2,
|
|
complete: 3,
|
|
resume: 4,
|
|
reopen: 5,
|
|
start: 6
|
|
};
|
|
|
|
const authStore = useAuthStore();
|
|
const currentUserId = computed(() => {
|
|
const rawUserId = authStore.userInfo.userId;
|
|
|
|
return rawUserId ? String(rawUserId) : '';
|
|
});
|
|
|
|
function getInitSearchParams(): Api.PersonalItem.PersonalItemSearchParams {
|
|
return {
|
|
pageNo: 1,
|
|
pageSize: 10,
|
|
keyword: undefined,
|
|
ownerId: currentUserId.value || undefined,
|
|
statusCode: undefined,
|
|
updateTime: undefined
|
|
};
|
|
}
|
|
|
|
function transformPageResult(
|
|
response: Awaited<ReturnType<typeof fetchGetPersonalItemPage>>,
|
|
pageNo: number,
|
|
pageSize: number
|
|
) {
|
|
if (!response.error) {
|
|
return {
|
|
data: response.data.list,
|
|
pageNum: pageNo,
|
|
pageSize,
|
|
total: response.data.total
|
|
};
|
|
}
|
|
|
|
return {
|
|
data: [],
|
|
pageNum: pageNo,
|
|
pageSize,
|
|
total: 0
|
|
};
|
|
}
|
|
|
|
const searchParams = reactive(getInitSearchParams());
|
|
const tableRef = ref<TableInstance>();
|
|
const checkedRowIds = ref<string[]>([]);
|
|
const bindExecutionSubmitting = ref(false);
|
|
|
|
const selectedCount = computed(() => checkedRowIds.value.length);
|
|
|
|
const { columns, columnChecks, data, loading, getDataByPage, mobilePagination } = useUIPaginatedTable({
|
|
paginationProps: {
|
|
currentPage: searchParams.pageNo,
|
|
pageSize: searchParams.pageSize
|
|
},
|
|
api: () => fetchGetPersonalItemPage(searchParams),
|
|
transform: response => transformPageResult(response, searchParams.pageNo ?? 1, searchParams.pageSize ?? 10),
|
|
onPaginationParamsChange: params => {
|
|
searchParams.pageNo = params.currentPage ?? 1;
|
|
searchParams.pageSize = params.pageSize ?? 10;
|
|
},
|
|
columns: () => [
|
|
{ prop: 'selection', type: 'selection', width: 48 },
|
|
{ prop: 'index', type: 'index', label: '序号', width: 64 },
|
|
{
|
|
prop: 'taskTitle',
|
|
label: '事项标题',
|
|
minWidth: 260,
|
|
showOverflowTooltip: true
|
|
},
|
|
{
|
|
prop: 'ownerName',
|
|
label: '负责人',
|
|
minWidth: 140,
|
|
formatter: row => formatPersonalItemOwnerName(row)
|
|
},
|
|
{
|
|
prop: 'statusCode',
|
|
label: '状态',
|
|
width: 120,
|
|
align: 'center',
|
|
formatter: row => (
|
|
<ElTag type={resolvePersonalItemStatusTagType(row.statusCode)}>
|
|
{getPersonalItemStatusLabel(row.statusCode)}
|
|
</ElTag>
|
|
)
|
|
},
|
|
{
|
|
prop: 'progressRate',
|
|
label: '进度',
|
|
width: 100,
|
|
align: 'center',
|
|
formatter: row => formatPersonalItemProgress(row.progressRate)
|
|
},
|
|
{
|
|
prop: 'plannedDateRange',
|
|
label: '计划日期',
|
|
minWidth: 220,
|
|
formatter: row => formatPersonalItemDateRange(row.plannedStartDate, row.plannedEndDate)
|
|
},
|
|
{
|
|
prop: 'actualDateRange',
|
|
label: '实际日期',
|
|
minWidth: 220,
|
|
formatter: row => formatPersonalItemDateRange(row.actualStartDate, row.actualEndDate)
|
|
},
|
|
{
|
|
prop: 'updateTime',
|
|
label: '最近更新',
|
|
minWidth: 180,
|
|
formatter: row => formatPersonalItemDateTime(row.updateTime)
|
|
},
|
|
{
|
|
prop: 'operate',
|
|
label: '操作',
|
|
width: 240,
|
|
align: 'center',
|
|
fixed: 'right',
|
|
formatter: row => renderRowActions(row)
|
|
}
|
|
]
|
|
});
|
|
|
|
const { bool: operateVisible, setTrue: openOperateDialog, setFalse: closeOperateDialog } = useBoolean();
|
|
const { bool: detailVisible, setTrue: openDetailDialog } = useBoolean();
|
|
const {
|
|
bool: bindExecutionVisible,
|
|
setTrue: openBindExecutionDialog,
|
|
setFalse: closeBindExecutionDialog
|
|
} = useBoolean();
|
|
const { bool: statusActionVisible, setTrue: openStatusActionDialog, setFalse: closeStatusActionDialog } = useBoolean();
|
|
|
|
const operateType = ref<PersonalItemOperateType>('add');
|
|
const editingData = ref<Api.PersonalItem.PersonalItem | null>(null);
|
|
const detailData = ref<Api.PersonalItem.PersonalItem | null>(null);
|
|
const detailDefaultTab = ref<DetailTab>('worklog');
|
|
const currentStatusAction = ref<Api.PersonalItem.PersonalItemLifecycleAction | null>(null);
|
|
const currentStatusItem = ref<Api.PersonalItem.PersonalItem | null>(null);
|
|
|
|
async function openDetail(row: Api.PersonalItem.PersonalItem, defaultTab: DetailTab = 'worklog') {
|
|
const { error, data: latestDetail } = await fetchGetPersonalItemDetail(row.id);
|
|
|
|
detailData.value = error || !latestDetail ? row : latestDetail;
|
|
detailDefaultTab.value = defaultTab;
|
|
openDetailDialog();
|
|
}
|
|
|
|
function openView(row: Api.PersonalItem.PersonalItem) {
|
|
operateType.value = 'view';
|
|
editingData.value = row;
|
|
openOperateDialog();
|
|
}
|
|
|
|
// function createLifecycleAction(
|
|
// fallback: {
|
|
// key: string;
|
|
// tooltip: string;
|
|
// icon: object;
|
|
// type: PersonalItemRowAction['type'];
|
|
// actionCode: string;
|
|
// },
|
|
// action: Api.PersonalItem.PersonalItemLifecycleAction | null
|
|
// ): PersonalItemRowAction {
|
|
// return {
|
|
// key: fallback.key,
|
|
// tooltip: action?.actionName ?? fallback.tooltip,
|
|
// icon: fallback.icon,
|
|
// type: fallback.type,
|
|
// disabled: !action,
|
|
// onClick: async () =>
|
|
// handleStatusAction(currentStatusItem.value!, {
|
|
// actionCode: action?.actionCode ?? fallback.actionCode,
|
|
// actionName: action?.actionName ?? fallback.tooltip,
|
|
// needReason: action?.needReason ?? false
|
|
// })
|
|
// };
|
|
// }
|
|
|
|
function buildRowActions(row: Api.PersonalItem.PersonalItem): PersonalItemRowAction[] {
|
|
currentStatusItem.value = row;
|
|
|
|
const rawLifecycleActions = [...(row.availableActions ?? [])];
|
|
const pauseAction = rawLifecycleActions.find(action => action.actionCode === 'pause') ?? null;
|
|
const cancelAction = rawLifecycleActions.find(action => action.actionCode === 'cancel') ?? null;
|
|
const completeAction = rawLifecycleActions.find(action => action.actionCode === 'complete') ?? null;
|
|
|
|
const lifecycleActions = rawLifecycleActions
|
|
.filter(action => !['pause', 'cancel', 'complete'].includes(action.actionCode))
|
|
.sort(
|
|
(left, right) => (lifecycleActionOrder[left.actionCode] ?? 99) - (lifecycleActionOrder[right.actionCode] ?? 99)
|
|
)
|
|
.map(action => ({
|
|
key: `status-${action.actionCode}`,
|
|
tooltip: action.actionName,
|
|
icon: markRaw(lifecycleActionIconMap[action.actionCode] ?? IconMdiSync),
|
|
type: lifecycleActionTypeMap[action.actionCode] ?? 'primary',
|
|
onClick: async () => handleStatusAction(row, action)
|
|
}));
|
|
|
|
return [
|
|
{
|
|
key: 'worklog',
|
|
tooltip: '填报',
|
|
icon: markRaw(IconMdiClipboardEditOutline),
|
|
type: 'primary',
|
|
onClick: async () => openDetail(row, 'worklog')
|
|
},
|
|
{
|
|
key: 'edit',
|
|
tooltip: '编辑',
|
|
icon: markRaw(IconMdiPencilOutline),
|
|
type: 'primary',
|
|
onClick: async () => {
|
|
operateType.value = 'edit';
|
|
editingData.value = row;
|
|
openOperateDialog();
|
|
}
|
|
},
|
|
{
|
|
key: 'delete',
|
|
tooltip: '删除',
|
|
icon: markRaw(IconMdiDeleteOutline),
|
|
type: 'danger',
|
|
onClick: async () => handleDelete(row)
|
|
},
|
|
{
|
|
key: 'status-pause',
|
|
tooltip: pauseAction?.actionName ?? '暂停',
|
|
icon: markRaw(IconMdiPause),
|
|
type: 'warning',
|
|
disabled: !pauseAction,
|
|
onClick: async () =>
|
|
handleStatusAction(row, {
|
|
actionCode: pauseAction?.actionCode ?? 'pause',
|
|
actionName: pauseAction?.actionName ?? '暂停',
|
|
needReason: pauseAction?.needReason ?? false
|
|
})
|
|
},
|
|
// {
|
|
// key: 'status-cancel',
|
|
// tooltip: cancelAction?.actionName ?? '取消',
|
|
// icon: markRaw(IconMdiCloseCircleOutline),
|
|
// type: 'danger',
|
|
// disabled: !cancelAction,
|
|
// onClick: async () =>
|
|
// handleStatusAction(row, {
|
|
// actionCode: cancelAction?.actionCode ?? 'cancel',
|
|
// actionName: cancelAction?.actionName ?? '取消',
|
|
// needReason: cancelAction?.needReason ?? false
|
|
// })
|
|
// },
|
|
...lifecycleActions,
|
|
{
|
|
key: 'status-complete',
|
|
tooltip: completeAction?.actionName ?? '完成',
|
|
icon: markRaw(IconMdiCheckCircleOutline),
|
|
type: 'success',
|
|
disabled: !completeAction,
|
|
onClick: async () =>
|
|
handleStatusAction(row, {
|
|
actionCode: completeAction?.actionCode ?? 'complete',
|
|
actionName: completeAction?.actionName ?? '完成',
|
|
needReason: completeAction?.needReason ?? false
|
|
})
|
|
}
|
|
];
|
|
}
|
|
|
|
function renderRowActions(row: Api.PersonalItem.PersonalItem) {
|
|
return (
|
|
<div class="personal-item-row-actions" onClick={event => event.stopPropagation()}>
|
|
{buildRowActions(row).map(action => {
|
|
const Icon = action.icon as any;
|
|
|
|
return (
|
|
<ElTooltip key={action.key} content={action.tooltip}>
|
|
<span class="inline-flex">
|
|
<ElButton
|
|
link
|
|
type={action.type}
|
|
class="personal-item-row-action-btn"
|
|
disabled={action.disabled}
|
|
onClick={event => {
|
|
event.stopPropagation();
|
|
if (action.disabled) {
|
|
return;
|
|
}
|
|
action.onClick();
|
|
}}
|
|
>
|
|
<Icon class="text-15px" />
|
|
</ElButton>
|
|
</span>
|
|
</ElTooltip>
|
|
);
|
|
})}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function openAdd() {
|
|
operateType.value = 'add';
|
|
editingData.value = null;
|
|
openOperateDialog();
|
|
}
|
|
|
|
function handleSelectionChange(rows: Api.PersonalItem.PersonalItem[]) {
|
|
checkedRowIds.value = rows.map(item => item.id);
|
|
}
|
|
|
|
function resolveReloadPageAfterRemove() {
|
|
const currentPage = searchParams.pageNo ?? 1;
|
|
|
|
if (currentPage > 1 && data.value.length > 0 && checkedRowIds.value.length >= data.value.length) {
|
|
return currentPage - 1;
|
|
}
|
|
|
|
return currentPage;
|
|
}
|
|
|
|
async function reloadTable(page = searchParams.pageNo ?? 1) {
|
|
checkedRowIds.value = [];
|
|
await getDataByPage(page);
|
|
await nextTick();
|
|
tableRef.value?.clearSelection();
|
|
}
|
|
|
|
function resetSearchParams() {
|
|
Object.assign(searchParams, getInitSearchParams());
|
|
reloadTable(1);
|
|
}
|
|
|
|
function handleSearch() {
|
|
reloadTable(1);
|
|
}
|
|
|
|
function handleSubmitted() {
|
|
closeOperateDialog();
|
|
reloadTable(searchParams.pageNo ?? 1);
|
|
}
|
|
|
|
function handleDetailChanged(latestItem: Api.PersonalItem.PersonalItem) {
|
|
detailData.value = latestItem;
|
|
|
|
const targetIndex = data.value.findIndex(item => item.id === latestItem.id);
|
|
|
|
if (targetIndex >= 0) {
|
|
data.value.splice(targetIndex, 1, latestItem);
|
|
}
|
|
}
|
|
|
|
function handleStatusAction(row: Api.PersonalItem.PersonalItem, action: Api.PersonalItem.PersonalItemLifecycleAction) {
|
|
currentStatusItem.value = row;
|
|
currentStatusAction.value = action;
|
|
openStatusActionDialog();
|
|
}
|
|
|
|
async function handleStatusActionSubmit(reason: string | null) {
|
|
if (!currentStatusItem.value || !currentStatusAction.value) {
|
|
return;
|
|
}
|
|
|
|
const { error } = await fetchChangePersonalItemStatus(currentStatusItem.value.id, {
|
|
actionCode: currentStatusAction.value.actionCode,
|
|
reason
|
|
});
|
|
|
|
if (error) {
|
|
return;
|
|
}
|
|
|
|
closeStatusActionDialog();
|
|
window.$message?.success(`${currentStatusAction.value.actionName}成功`);
|
|
await reloadTable(searchParams.pageNo ?? 1);
|
|
}
|
|
|
|
async function handleDelete(row: Api.PersonalItem.PersonalItem) {
|
|
try {
|
|
await ElMessageBox.confirm(`确定删除个人事项“${row.taskTitle}”吗?`, '删除确认', {
|
|
type: 'warning',
|
|
confirmButtonText: '确定',
|
|
cancelButtonText: '取消'
|
|
});
|
|
} catch {
|
|
return;
|
|
}
|
|
|
|
const { error } = await fetchDeletePersonalItem(row.id);
|
|
|
|
if (error) {
|
|
return;
|
|
}
|
|
|
|
window.$message?.success('删除成功');
|
|
await reloadTable(searchParams.pageNo ?? 1);
|
|
}
|
|
|
|
async function handleBatchDelete() {
|
|
if (!checkedRowIds.value.length) {
|
|
window.$message?.warning('请先选择个人事项');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
await ElMessageBox.confirm(`确定删除选中的 ${selectedCount.value} 条个人事项吗?`, '删除确认', {
|
|
type: 'warning',
|
|
confirmButtonText: '确定',
|
|
cancelButtonText: '取消'
|
|
});
|
|
} catch {
|
|
return;
|
|
}
|
|
|
|
const targetPage = resolveReloadPageAfterRemove();
|
|
const { error } = await fetchBatchDeletePersonalItems({ ids: [...checkedRowIds.value] });
|
|
|
|
if (error) {
|
|
return;
|
|
}
|
|
|
|
window.$message?.success('批量删除成功');
|
|
await reloadTable(targetPage);
|
|
}
|
|
|
|
function handleOpenBindExecution() {
|
|
if (!checkedRowIds.value.length) {
|
|
window.$message?.warning('请先选择个人事项');
|
|
return;
|
|
}
|
|
|
|
openBindExecutionDialog();
|
|
}
|
|
|
|
async function handleBindExecutionSubmit(payload: { executionId: string }) {
|
|
bindExecutionSubmitting.value = true;
|
|
|
|
const targetPage = resolveReloadPageAfterRemove();
|
|
const { error } = await fetchBindPersonalItemsToExecution({
|
|
ids: [...checkedRowIds.value],
|
|
executionId: payload.executionId
|
|
});
|
|
|
|
bindExecutionSubmitting.value = false;
|
|
|
|
if (error) {
|
|
return;
|
|
}
|
|
|
|
closeBindExecutionDialog();
|
|
window.$message?.success('批量关联执行成功');
|
|
await reloadTable(targetPage);
|
|
}
|
|
|
|
onActivated(() => {
|
|
searchParams.ownerId = currentUserId.value || undefined;
|
|
reloadTable(searchParams.pageNo ?? 1);
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<div class="flex-col-stretch gap-16px overflow-hidden">
|
|
<PersonalItemSearch v-model:model="searchParams" @reset="resetSearchParams" @search="handleSearch" />
|
|
|
|
<ElCard class="flex-1-hidden card-wrapper" body-class="business-table-card-body">
|
|
<template #header>
|
|
<div class="flex items-center justify-between gap-12px">
|
|
<div class="flex items-center gap-10px">
|
|
<p>个人事项</p>
|
|
<ElTag effect="plain">{{ mobilePagination.total || data.length }}</ElTag>
|
|
</div>
|
|
<TableHeaderOperation v-model:columns="columnChecks" :loading="loading" @refresh="reloadTable">
|
|
<template #default>
|
|
<ElButton plain type="danger" :disabled="selectedCount === 0" @click="handleBatchDelete">
|
|
<template #icon>
|
|
<icon-ic-round-delete class="text-icon" />
|
|
</template>
|
|
批量删除
|
|
</ElButton>
|
|
<ElButton plain :disabled="selectedCount === 0" @click="handleOpenBindExecution">
|
|
<template #icon>
|
|
<icon-mdi-link-variant class="text-icon" />
|
|
</template>
|
|
批量关联执行
|
|
</ElButton>
|
|
<ElButton plain type="primary" @click="openAdd">
|
|
<template #icon>
|
|
<icon-ic-round-plus class="text-icon" />
|
|
</template>
|
|
新增
|
|
</ElButton>
|
|
</template>
|
|
</TableHeaderOperation>
|
|
</div>
|
|
</template>
|
|
|
|
<div class="flex-1">
|
|
<ElTable
|
|
ref="tableRef"
|
|
v-loading="loading"
|
|
height="100%"
|
|
border
|
|
row-key="id"
|
|
:data="data"
|
|
@selection-change="handleSelectionChange"
|
|
>
|
|
<template v-for="col in columns" :key="String(col.prop)">
|
|
<ElTableColumn v-if="col.prop === 'taskTitle'" v-bind="col">
|
|
<template #default="{ row }">
|
|
<ElButton link type="primary" class="personal-item-title-link" @click.stop="openView(row)">
|
|
{{ row.taskTitle || '--' }}
|
|
</ElButton>
|
|
</template>
|
|
</ElTableColumn>
|
|
<ElTableColumn v-else v-bind="col" />
|
|
</template>
|
|
</ElTable>
|
|
</div>
|
|
|
|
<div class="mt-20px flex justify-end">
|
|
<ElPagination
|
|
v-if="mobilePagination.total"
|
|
layout="total,prev,pager,next,sizes"
|
|
v-bind="mobilePagination"
|
|
@current-change="mobilePagination['current-change']"
|
|
@size-change="mobilePagination['size-change']"
|
|
/>
|
|
</div>
|
|
</ElCard>
|
|
|
|
<PersonalItemOperateDialog
|
|
v-model:visible="operateVisible"
|
|
:operate-type="operateType"
|
|
:row-data="editingData"
|
|
@submitted="handleSubmitted"
|
|
/>
|
|
|
|
<PersonalItemDetailDialog
|
|
v-model:visible="detailVisible"
|
|
:row-data="detailData"
|
|
:default-tab="detailDefaultTab"
|
|
@changed="handleDetailChanged"
|
|
/>
|
|
|
|
<PersonalItemBindExecutionDialog
|
|
v-model:visible="bindExecutionVisible"
|
|
:selected-count="selectedCount"
|
|
:submit-loading="bindExecutionSubmitting"
|
|
@submit="handleBindExecutionSubmit"
|
|
/>
|
|
|
|
<PersonalItemStatusActionDialog
|
|
v-model:visible="statusActionVisible"
|
|
:action="currentStatusAction"
|
|
@submit="handleStatusActionSubmit"
|
|
/>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped lang="scss">
|
|
.personal-item-row-actions {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 4px;
|
|
}
|
|
|
|
.personal-item-row-actions :deep(.el-button + .el-button) {
|
|
margin-left: 0;
|
|
}
|
|
|
|
:deep(.personal-item-row-action-btn) {
|
|
padding: 3px;
|
|
min-width: auto;
|
|
height: auto;
|
|
line-height: 1;
|
|
}
|
|
|
|
:deep(.personal-item-title-link) {
|
|
max-width: 100%;
|
|
padding: 0;
|
|
vertical-align: baseline;
|
|
}
|
|
|
|
:deep(.personal-item-title-link > span) {
|
|
display: inline-block;
|
|
max-width: 100%;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
</style>
|