feat(工作台): 工作台卡片增加骨架屏loading

This commit is contained in:
xinxin.wu 2024-11-25 17:18:54 +08:00 committed by 刘瑞斌
parent 4039bab50e
commit 2c40fc7757
13 changed files with 624 additions and 474 deletions

View File

@ -1,53 +1,60 @@
<template>
<div class="card-wrapper card-min-height">
<div class="flex items-center justify-between">
<a-tooltip :content="t(props.item.label)" position="tl">
<div class="title one-line-text"> {{ t(props.item.label) }} </div>
</a-tooltip>
<div>
<MsSelect
v-model:model-value="projectId"
:options="appStore.projectList"
allow-search
value-key="id"
label-key="name"
:search-keys="['name']"
class="!w-[200px]"
:prefix="t('workbench.homePage.project')"
@change="changeProject"
>
</MsSelect>
</div>
</div>
<div class="mt-[16px]">
<div class="case-count-wrapper">
<div class="case-count-item">
<div v-for="(ele, index) of executionTimeValue" :key="index" class="case-count-item-content">
<div class="case-count-item-title">{{ ele.name }}</div>
<div class="case-count-item-number">{{ hasPermission ? addCommasToNumber(ele.count as number) : '-' }}</div>
</div>
</div>
<div class="case-count-item">
<div v-for="(ele, index) of apiCountValue" :key="index" class="case-count-item-content">
<div class="case-count-item-title">{{ ele.name }}</div>
<div class="case-count-item-number">{{ hasPermission ? addCommasToNumber(ele.count as number) : '-' }}</div>
</div>
<CardSkeleton v-if="showSkeleton" :show-skeleton="showSkeleton" />
<div v-else>
<div class="flex items-center justify-between">
<a-tooltip :content="t(props.item.label)" position="tl">
<div class="title one-line-text"> {{ t(props.item.label) }} </div>
</a-tooltip>
<div>
<MsSelect
v-model:model-value="projectId"
:options="appStore.projectList"
allow-search
value-key="id"
label-key="name"
:search-keys="['name']"
class="!w-[200px]"
:prefix="t('workbench.homePage.project')"
@change="changeProject"
>
</MsSelect>
</div>
</div>
<div class="case-ratio-wrapper mt-[16px]">
<div class="case-ratio-item">
<RatioPie
:has-permission="hasPermission"
:loading="loading"
:data="coverData"
:rate-config="coverTitleConfig"
/>
<div class="mt-[16px]">
<div class="case-count-wrapper">
<div class="case-count-item">
<div v-for="(ele, index) of executionTimeValue" :key="index" class="case-count-item-content">
<div class="case-count-item-title">{{ ele.name }}</div>
<div class="case-count-item-number">
{{ hasPermission ? addCommasToNumber(ele.count as number) : '-' }}
</div>
</div>
</div>
<div class="case-count-item">
<div v-for="(ele, index) of apiCountValue" :key="index" class="case-count-item-content">
<div class="case-count-item-title">{{ ele.name }}</div>
<div class="case-count-item-number">
{{ hasPermission ? addCommasToNumber(ele.count as number) : '-' }}
</div>
</div>
</div>
</div>
<div class="case-ratio-item">
<RatioPie :has-permission="hasPermission" :data="caseExecuteData" :rate-config="executeTitleConfig" />
</div>
<div class="case-ratio-item">
<RatioPie :has-permission="hasPermission" :data="casePassData" :rate-config="casePassTitleConfig" />
<div class="case-ratio-wrapper mt-[16px]">
<div class="case-ratio-item">
<RatioPie
:has-permission="hasPermission"
:loading="loading"
:data="coverData"
:rate-config="coverTitleConfig"
/>
</div>
<div class="case-ratio-item">
<RatioPie :has-permission="hasPermission" :data="caseExecuteData" :rate-config="executeTitleConfig" />
</div>
<div class="case-ratio-item">
<RatioPie :has-permission="hasPermission" :data="casePassData" :rate-config="casePassTitleConfig" />
</div>
</div>
</div>
</div>
@ -62,6 +69,7 @@
import { cloneDeep } from 'lodash-es';
import MsSelect from '@/components/business/ms-select';
import CardSkeleton from './cardSkeleton.vue';
import RatioPie from './ratioPie.vue';
import { workApiCaseCountDetail, workApiCountCoverRage, workScenarioCaseCountDetail } from '@/api/modules/workbench';
@ -244,8 +252,11 @@
}
}
const showSkeleton = ref(false);
async function initApiOrScenarioCount() {
try {
showSkeleton.value = true;
coverData.value = cloneDeep(initCoverRate);
const { startTime, endTime, dayNumber } = timeForm.value;
@ -292,6 +303,8 @@
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
} finally {
showSkeleton.value = false;
}
}

View File

@ -1,47 +1,50 @@
<template>
<div class="card-wrapper card-min-height">
<div class="flex items-center justify-between">
<a-tooltip :content="t(props.item.label)" position="tl">
<div class="title one-line-text"> {{ t(props.item.label) }} </div>
</a-tooltip>
<div>
<MsSelect
v-model:model-value="projectId"
:options="appStore.projectList"
allow-search
value-key="id"
label-key="name"
:search-keys="['name']"
class="!w-[200px]"
:prefix="t('workbench.homePage.project')"
@change="changeProject"
>
</MsSelect>
<CardSkeleton v-if="showSkeleton" :show-skeleton="showSkeleton" />
<div v-else>
<div class="flex items-center justify-between">
<a-tooltip :content="t(props.item.label)" position="tl">
<div class="title one-line-text"> {{ t(props.item.label) }} </div>
</a-tooltip>
<div>
<MsSelect
v-model:model-value="projectId"
:options="appStore.projectList"
allow-search
value-key="id"
label-key="name"
:search-keys="['name']"
class="!w-[200px]"
:prefix="t('workbench.homePage.project')"
@change="changeProject"
>
</MsSelect>
</div>
</div>
<div class="mt-[16px]">
<MsBaseTable
v-bind="propsRes"
:action-config="{
baseAction: [],
moreAction: [],
}"
class="mt-[16px]"
v-on="propsEvent"
>
<template #num="{ record }">
<MsButton type="text" @click="openDetail(record)">{{ record.num || '-' }}</MsButton>
</template>
<template v-if="isNoPermission" #empty>
<div class="w-full">
<slot name="empty">
<div class="flex h-[40px] flex-col items-center justify-center">
<span class="text-[14px] text-[var(--color-text-4)]">{{ t('common.noResource') }}</span>
</div>
</slot>
</div>
</template>
</MsBaseTable>
</div>
</div>
<div class="mt-[16px]">
<MsBaseTable
v-bind="propsRes"
:action-config="{
baseAction: [],
moreAction: [],
}"
class="mt-[16px]"
v-on="propsEvent"
>
<template #num="{ record }">
<MsButton type="text" @click="openDetail(record)">{{ record.num || '-' }}</MsButton>
</template>
<template v-if="isNoPermission" #empty>
<div class="w-full">
<slot name="empty">
<div class="flex h-[40px] flex-col items-center justify-center">
<span class="text-[14px] text-[var(--color-text-4)]">{{ t('common.noResource') }}</span>
</div>
</slot>
</div>
</template>
</MsBaseTable>
</div>
</div>
</template>
@ -58,6 +61,7 @@
import { MsTableColumn } from '@/components/pure/ms-table/type';
import useTable from '@/components/pure/ms-table/useTable';
import MsSelect from '@/components/business/ms-select';
import CardSkeleton from './cardSkeleton.vue';
import { workApiChangeList } from '@/api/modules/workbench';
import { useI18n } from '@/hooks/useI18n';
@ -168,7 +172,9 @@
}
const isNoPermission = ref<boolean>(false);
const showSkeleton = ref(false);
async function initData() {
showSkeleton.value = true;
try {
const { startTime, endTime, dayNumber } = timeForm.value;
setLoadListParams({
@ -185,6 +191,8 @@
isNoPermission.value = error === 'no_project_permission';
// eslint-disable-next-line no-console
console.log(error);
} finally {
showSkeleton.value = false;
}
}

View File

@ -1,39 +1,42 @@
<template>
<div class="card-wrapper api-count-wrapper card-min-height">
<div class="flex items-center justify-between">
<a-tooltip :content="t(props.item.label)" position="tl">
<div class="title one-line-text"> {{ t(props.item.label) }} </div>
</a-tooltip>
<div>
<MsSelect
v-model:model-value="projectId"
:options="appStore.projectList"
allow-search
value-key="id"
label-key="name"
:search-keys="['name']"
class="!w-[200px]"
:prefix="t('workbench.homePage.project')"
@change="changeProject"
>
</MsSelect>
<CardSkeleton v-if="showSkeleton" :show-skeleton="showSkeleton" />
<div v-else>
<div class="flex items-center justify-between">
<a-tooltip :content="t(props.item.label)" position="tl">
<div class="title one-line-text"> {{ t(props.item.label) }} </div>
</a-tooltip>
<div>
<MsSelect
v-model:model-value="projectId"
:options="appStore.projectList"
allow-search
value-key="id"
label-key="name"
:search-keys="['name']"
class="!w-[200px]"
:prefix="t('workbench.homePage.project')"
@change="changeProject"
>
</MsSelect>
</div>
</div>
</div>
<div class="my-[16px]">
<TabCard :content-tab-list="apiCountTabList" not-has-padding hidden-border min-width="296px">
<template #item="{ item: tabItem }">
<PassRatePie
:tooltip-text="tabItem.tooltip"
:options="tabItem.options"
:size="60"
:loading="tabItem.value === 'cover' ? loading : undefined"
:has-permission="hasPermission"
:value-list="tabItem.valueList"
/>
</template>
</TabCard>
<div class="h-[148px]">
<MsChart :options="apiCountOptions" />
<div class="my-[16px]">
<TabCard :content-tab-list="apiCountTabList" not-has-padding hidden-border min-width="296px">
<template #item="{ item: tabItem }">
<PassRatePie
:tooltip-text="tabItem.tooltip"
:options="tabItem.options"
:size="60"
:loading="tabItem.value === 'cover' ? loading : undefined"
:has-permission="hasPermission"
:value-list="tabItem.valueList"
/>
</template>
</TabCard>
<div class="h-[148px]">
<MsChart :options="apiCountOptions" />
</div>
</div>
</div>
</div>
@ -47,6 +50,7 @@
import MsChart from '@/components/pure/chart/index.vue';
import MsSelect from '@/components/business/ms-select';
import CardSkeleton from './cardSkeleton.vue';
import PassRatePie from './passRatePie.vue';
import TabCard from './tabCard.vue';
@ -144,8 +148,6 @@
coverOptions.value = { ...covOptions };
}
async function initApiCountRate() {
console.log(hasPermission.value);
try {
loading.value = true;
const detail = await workApiCountCoverRage(projectId.value);
@ -158,8 +160,12 @@
}
}
const showSkeleton = ref(false);
async function initApiCount() {
try {
showSkeleton.value = true;
const { startTime, endTime, dayNumber } = timeForm.value;
const detail = await workApiCountDetail({
current: 1,
@ -187,6 +193,8 @@
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
} finally {
showSkeleton.value = false;
}
}

View File

@ -0,0 +1,40 @@
<template>
<a-skeleton v-if="props.showSkeleton" :loading="props.showSkeleton" :animation="true">
<div class="flex gap-[16px]">
<div class="flex-1">
<a-skeleton-line :rows="1" :line-height="props.skeletonLine" />
</div>
<div class="flex-1">
<a-skeleton-line :rows="1" :line-height="props.skeletonLine" />
</div>
</div>
<div>
<div v-for="index in props.showLineNumber" :key="index" class="mt-[16px] flex flex-col">
<div class="flex gap-4">
<div :style="{ width: `${index * 12}%` }">
<a-skeleton-line :rows="1" :line-height="props.skeletonLine" />
</div>
<div :style="{ width: `calc(100% - ${index * 12}%)` }">
<a-skeleton-line :rows="1" :line-height="props.skeletonLine" />
</div>
</div>
</div>
</div>
</a-skeleton>
</template>
<script setup lang="ts">
const props = withDefaults(
defineProps<{
showSkeleton: boolean;
skeletonLine?: number;
showLineNumber?: number;
}>(),
{
skeletonLine: 24,
showLineNumber: 7,
}
);
</script>
<style scoped></style>

View File

@ -1,40 +1,43 @@
<template>
<div class="card-wrapper card-min-height">
<div class="flex items-center justify-between">
<a-tooltip :content="t(props.item.label)" position="tl">
<div class="title one-line-text"> {{ t(props.item.label) }} </div>
</a-tooltip>
<div>
<MsSelect
v-model:model-value="projectId"
:options="appStore.projectList"
allow-search
value-key="id"
label-key="name"
:search-keys="['name']"
class="!w-[200px]"
:prefix="t('workbench.homePage.project')"
@change="changeProject"
>
</MsSelect>
<CardSkeleton v-if="showSkeleton" :show-skeleton="showSkeleton" />
<div v-else>
<div class="flex items-center justify-between">
<a-tooltip :content="t(props.item.label)" position="tl">
<div class="title one-line-text"> {{ t(props.item.label) }} </div>
</a-tooltip>
<div>
<MsSelect
v-model:model-value="projectId"
:options="appStore.projectList"
allow-search
value-key="id"
label-key="name"
:search-keys="['name']"
class="!w-[200px]"
:prefix="t('workbench.homePage.project')"
@change="changeProject"
>
</MsSelect>
</div>
</div>
</div>
<div class="mt-[16px]">
<TabCard :content-tab-list="caseCountTabList" not-has-padding hidden-border min-width="270px">
<template #item="{ item: tabItem }">
<div class="w-full">
<PassRatePie
:options="tabItem.options"
:tooltip-text="tabItem.tooltip"
:size="60"
:value-list="tabItem.valueList"
:has-permission="hasPermission"
/>
</div>
</template>
</TabCard>
<div class="h-[148px]">
<MsChart :options="caseCountOptions" />
<div class="mt-[16px]">
<TabCard :content-tab-list="caseCountTabList" not-has-padding hidden-border min-width="270px">
<template #item="{ item: tabItem }">
<div class="w-full">
<PassRatePie
:options="tabItem.options"
:tooltip-text="tabItem.tooltip"
:size="60"
:value-list="tabItem.valueList"
:has-permission="hasPermission"
/>
</div>
</template>
</TabCard>
<div class="h-[148px]">
<MsChart :options="caseCountOptions" />
</div>
</div>
</div>
</div>
@ -48,6 +51,7 @@
import MsChart from '@/components/pure/chart/index.vue';
import MsSelect from '@/components/business/ms-select';
import CardSkeleton from './cardSkeleton.vue';
import PassRatePie from './passRatePie.vue';
import TabCard from './tabCard.vue';
@ -114,9 +118,11 @@
const hasPermission = ref<boolean>(false);
const caseCountOptions = ref<Record<string, any>>({});
const showSkeleton = ref(false);
async function initCaseCount() {
try {
showSkeleton.value = true;
const { startTime, endTime, dayNumber } = timeForm.value;
const params = {
current: 1,
@ -152,6 +158,8 @@
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
} finally {
showSkeleton.value = false;
}
}

View File

@ -1,38 +1,41 @@
<template>
<div class="card-wrapper card-min-height">
<div class="flex items-center justify-between">
<a-tooltip :content="t(props.item.label)" position="tl">
<div class="title one-line-text"> {{ t(props.item.label) }} </div>
</a-tooltip>
<div>
<MsSelect
v-model:model-value="projectId"
:options="appStore.projectList"
allow-search
value-key="id"
label-key="name"
:search-keys="['name']"
class="!w-[200px]"
:prefix="t('workbench.homePage.project')"
@change="changeProject"
>
</MsSelect>
</div>
</div>
<div class="mt-[16px]">
<div class="case-count-wrapper mb-[16px]">
<div class="case-count-item">
<PassRatePie
:options="options"
tooltip-text="workbench.homePage.caseReviewCoverRateTooltip"
:size="60"
:value-list="coverValueList"
:has-permission="hasPermission"
/>
<CardSkeleton v-if="showSkeleton" :show-skeleton="showSkeleton" />
<div v-else>
<div class="flex items-center justify-between">
<a-tooltip :content="t(props.item.label)" position="tl">
<div class="title one-line-text"> {{ t(props.item.label) }} </div>
</a-tooltip>
<div>
<MsSelect
v-model:model-value="projectId"
:options="appStore.projectList"
allow-search
value-key="id"
label-key="name"
:search-keys="['name']"
class="!w-[200px]"
:prefix="t('workbench.homePage.project')"
@change="changeProject"
>
</MsSelect>
</div>
</div>
<div class="h-[148px]">
<MsChart :options="caseReviewCountOptions" />
<div class="mt-[16px]">
<div class="case-count-wrapper mb-[16px]">
<div class="case-count-item">
<PassRatePie
:options="options"
tooltip-text="workbench.homePage.caseReviewCoverRateTooltip"
:size="60"
:value-list="coverValueList"
:has-permission="hasPermission"
/>
</div>
</div>
<div class="h-[148px]">
<MsChart :options="caseReviewCountOptions" />
</div>
</div>
</div>
</div>
@ -46,6 +49,7 @@
import MsChart from '@/components/pure/chart/index.vue';
import MsSelect from '@/components/business/ms-select';
import CardSkeleton from './cardSkeleton.vue';
import PassRatePie from './passRatePie.vue';
import { workCaseReviewDetail } from '@/api/modules/workbench';
@ -101,19 +105,22 @@
const caseReviewCountOptions = ref<Record<string, any>>({});
const hasPermission = ref<boolean>(false);
const showSkeleton = ref(false);
async function initReviewCount() {
const { startTime, endTime, dayNumber } = timeForm.value;
const params = {
current: 1,
pageSize: 5,
startTime: dayNumber ? null : startTime,
endTime: dayNumber ? null : endTime,
dayNumber: dayNumber ?? null,
projectIds: innerProjectIds.value,
organizationId: appStore.currentOrgId,
handleUsers: [],
};
try {
showSkeleton.value = true;
const { startTime, endTime, dayNumber } = timeForm.value;
const params = {
current: 1,
pageSize: 5,
startTime: dayNumber ? null : startTime,
endTime: dayNumber ? null : endTime,
dayNumber: dayNumber ?? null,
projectIds: innerProjectIds.value,
organizationId: appStore.currentOrgId,
handleUsers: [],
};
const detail: PassRateDataType = await workCaseReviewDetail(params);
hasPermission.value = detail.errorCode !== 109001;
@ -130,6 +137,8 @@
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
} finally {
showSkeleton.value = false;
}
}

View File

@ -1,38 +1,41 @@
<template>
<div class="card-wrapper card-min-height">
<div class="flex items-center justify-between">
<a-tooltip :content="t(props.item.label)" position="tl">
<div class="title one-line-text"> {{ t(props.item.label) }} </div>
</a-tooltip>
<div>
<MsSelect
v-model:model-value="projectId"
:options="appStore.projectList"
allow-search
value-key="id"
label-key="name"
:search-keys="['name']"
class="!w-[200px]"
:prefix="t('workbench.homePage.project')"
@change="changeProject"
>
</MsSelect>
</div>
</div>
<div class="mt-[16px]">
<div class="case-count-wrapper">
<div class="case-count-item mb-[16px]">
<PassRatePie
:has-permission="hasPermission"
:tooltip-text="tooltip"
:options="legacyOptions"
:size="60"
:value-list="legacyValueList"
/>
<CardSkeleton v-if="showSkeleton" :show-skeleton="showSkeleton" />
<div v-else>
<div class="flex items-center justify-between">
<a-tooltip :content="t(props.item.label)" position="tl">
<div class="title one-line-text"> {{ t(props.item.label) }} </div>
</a-tooltip>
<div>
<MsSelect
v-model:model-value="projectId"
:options="appStore.projectList"
allow-search
value-key="id"
label-key="name"
:search-keys="['name']"
class="!w-[200px]"
:prefix="t('workbench.homePage.project')"
@change="changeProject"
>
</MsSelect>
</div>
</div>
<div class="h-[148px]">
<MsChart :options="countOptions" />
<div class="mt-[16px]">
<div class="case-count-wrapper">
<div class="case-count-item mb-[16px]">
<PassRatePie
:has-permission="hasPermission"
:tooltip-text="tooltip"
:options="legacyOptions"
:size="60"
:value-list="legacyValueList"
/>
</div>
</div>
<div class="h-[148px]">
<MsChart :options="countOptions" />
</div>
</div>
</div>
</div>
@ -46,6 +49,7 @@
import MsChart from '@/components/pure/chart/index.vue';
import MsSelect from '@/components/business/ms-select';
import CardSkeleton from './cardSkeleton.vue';
import PassRatePie from './passRatePie.vue';
import {
@ -115,8 +119,11 @@
}[props.item.key as SelectedBugCountKeys];
const hasPermission = ref<boolean>(false);
const showSkeleton = ref(false);
async function initCount() {
try {
showSkeleton.value = true;
const { startTime, endTime, dayNumber } = timeForm.value;
const params = {
current: 1,
@ -161,6 +168,8 @@
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
} finally {
showSkeleton.value = false;
}
}

View File

@ -1,41 +1,44 @@
<template>
<div class="card-wrapper">
<div class="flex items-center justify-between">
<a-tooltip :content="t(props.item.label)" position="tl">
<div class="title one-line-text"> {{ t(props.item.label) }} </div>
</a-tooltip>
<div class="flex items-center gap-[8px]">
<MsSelect
v-model:model-value="projectId"
:options="appStore.projectList"
allow-search
value-key="id"
label-key="name"
:search-keys="['name']"
class="!w-[200px]"
:prefix="t('workbench.homePage.project')"
@change="changeProject"
>
</MsSelect>
<MsSelect
:key="props.refreshKey"
v-model:model-value="innerHandleUsers"
:options="memberOptions"
allow-clear
allow-search
:search-keys="['label']"
class="!w-[200px]"
:prefix="t('workbench.homePage.staff')"
:multiple="true"
:has-all-select="true"
:default-all-select="innerHandleUsers.length === 0"
@change="changeMember"
>
</MsSelect>
<CardSkeleton v-if="showSkeleton" :show-skeleton="showSkeleton" />
<div v-else>
<div class="flex items-center justify-between">
<a-tooltip :content="t(props.item.label)" position="tl">
<div class="title one-line-text"> {{ t(props.item.label) }} </div>
</a-tooltip>
<div class="flex items-center gap-[8px]">
<MsSelect
v-model:model-value="projectId"
:options="appStore.projectList"
allow-search
value-key="id"
label-key="name"
:search-keys="['name']"
class="!w-[200px]"
:prefix="t('workbench.homePage.project')"
@change="changeProject"
>
</MsSelect>
<MsSelect
:key="props.refreshKey"
v-model:model-value="innerHandleUsers"
:options="memberOptions"
allow-clear
allow-search
:search-keys="['label']"
class="!w-[200px]"
:prefix="t('workbench.homePage.staff')"
:multiple="true"
:has-all-select="true"
:default-all-select="innerHandleUsers.length === 0"
@change="changeMember"
>
</MsSelect>
</div>
</div>
<div class="mt-[16px]">
<MsChart height="260px" :options="options" />
</div>
</div>
<div class="mt-[16px]">
<MsChart height="260px" :options="options" />
</div>
</div>
</template>
@ -48,6 +51,7 @@
import MsChart from '@/components/pure/chart/index.vue';
import MsSelect from '@/components/business/ms-select';
import CardSkeleton from './cardSkeleton.vue';
import { workBugHandlerDetail, workHandleUserOptions } from '@/api/modules/workbench';
import { useI18n } from '@/hooks/useI18n';
@ -142,9 +146,11 @@
});
options.value.yAxis[0].max = maxAxis < 100 ? 50 : maxAxis + 50;
}
const showSkeleton = ref(false);
async function getDefectMemberDetail() {
try {
showSkeleton.value = true;
const { startTime, endTime, dayNumber } = timeForm.value;
const detail = await workBugHandlerDetail({
current: 1,
@ -161,6 +167,8 @@
handleData(detail);
} catch (error) {
console.log(error);
} finally {
showSkeleton.value = false;
}
}

View File

@ -1,38 +1,41 @@
<template>
<div class="card-wrapper">
<div class="flex items-center justify-between">
<a-tooltip :content="t(props.item.label)" position="tl">
<div class="title one-line-text"> {{ t(props.item.label) }} </div>
</a-tooltip>
<div>
<MsSelect
v-model:model-value="innerProjectIds"
:options="appStore.projectList"
allow-clear
allow-search
value-key="id"
label-key="name"
:search-keys="['name']"
class="!w-[200px]"
:prefix="t('workbench.homePage.project')"
:multiple="true"
:has-all-select="true"
:default-all-select="innerSelectAll"
:at-least-one="true"
@change="changeProject"
>
</MsSelect>
</div>
</div>
<div class="my-[16px]">
<TabCard
:content-tab-list="cardModuleList"
:no-permission-text="hasPermission ? '' : 'workbench.homePage.notHasResPermission'"
/>
</div>
<!-- 概览图 -->
<CardSkeleton v-if="showSkeleton" :show-skeleton="showSkeleton" />
<div>
<MsChart height="280px" :options="options" />
<div class="flex items-center justify-between">
<a-tooltip :content="t(props.item.label)" position="tl">
<div class="title one-line-text"> {{ t(props.item.label) }} </div>
</a-tooltip>
<div>
<MsSelect
v-model:model-value="innerProjectIds"
:options="appStore.projectList"
allow-clear
allow-search
value-key="id"
label-key="name"
:search-keys="['name']"
class="!w-[200px]"
:prefix="t('workbench.homePage.project')"
:multiple="true"
:has-all-select="true"
:default-all-select="innerSelectAll"
:at-least-one="true"
@change="changeProject"
>
</MsSelect>
</div>
</div>
<div class="my-[16px]">
<TabCard
:content-tab-list="cardModuleList"
:no-permission-text="hasPermission ? '' : 'workbench.homePage.notHasResPermission'"
/>
</div>
<!-- 概览图 -->
<div>
<MsChart height="280px" :options="options" />
</div>
</div>
</div>
</template>
@ -45,6 +48,7 @@
import MsChart from '@/components/pure/chart/index.vue';
import MsSelect from '@/components/business/ms-select';
import CardSkeleton from './cardSkeleton.vue';
import TabCard from './tabCard.vue';
import { workMyCreatedDetail, workProOverviewDetail } from '@/api/modules/workbench';
@ -190,9 +194,12 @@
});
options.value.yAxis[0].max = maxAxis < 100 ? 100 : maxAxis + 50;
}
const showSkeleton = ref(false);
async function initOverViewDetail() {
try {
showSkeleton.value = true;
const { startTime, endTime, dayNumber } = timeForm.value;
const params = {
current: 1,
@ -217,6 +224,8 @@
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
} finally {
showSkeleton.value = false;
}
}
const isInit = ref(true);

View File

@ -1,45 +1,48 @@
<template>
<div class="card-wrapper">
<div class="flex items-center justify-between">
<a-tooltip :content="t(props.item.label)" position="tl">
<div class="title one-line-text"> {{ t(props.item.label) }} </div>
</a-tooltip>
<div class="flex items-center gap-[8px]">
<MsSelect
v-model:model-value="projectId"
:options="appStore.projectList"
allow-search
value-key="id"
label-key="name"
:search-keys="['name']"
class="!w-[200px]"
:prefix="t('workbench.homePage.project')"
@change="changeProject"
>
</MsSelect>
<CardSkeleton v-if="showSkeleton" :show-skeleton="showSkeleton" />
<div v-else>
<div class="flex items-center justify-between">
<a-tooltip :content="t(props.item.label)" position="tl">
<div class="title one-line-text"> {{ t(props.item.label) }} </div>
</a-tooltip>
<div class="flex items-center gap-[8px]">
<MsSelect
v-model:model-value="projectId"
:options="appStore.projectList"
allow-search
value-key="id"
label-key="name"
:search-keys="['name']"
class="!w-[200px]"
:prefix="t('workbench.homePage.project')"
@change="changeProject"
>
</MsSelect>
<MsSelect
:key="props.refreshKey"
v-model:model-value="innerHandleUsers"
:options="memberOptions"
allow-search
allow-clear
value-key="value"
label-key="label"
:search-keys="['label']"
class="!w-[200px]"
:prefix="t('workbench.homePage.staff')"
:multiple="true"
:has-all-select="true"
:default-all-select="innerHandleUsers.length === 0"
@change="changeMember"
>
</MsSelect>
<MsSelect
:key="props.refreshKey"
v-model:model-value="innerHandleUsers"
:options="memberOptions"
allow-search
allow-clear
value-key="value"
label-key="label"
:search-keys="['label']"
class="!w-[200px]"
:prefix="t('workbench.homePage.staff')"
:multiple="true"
:has-all-select="true"
:default-all-select="innerHandleUsers.length === 0"
@change="changeMember"
>
</MsSelect>
</div>
</div>
<!-- 概览图 -->
<div class="mt-[16px]">
<MsChart height="300px" :options="options" />
</div>
</div>
<!-- 概览图 -->
<div class="mt-[16px]">
<MsChart height="300px" :options="options" />
</div>
</div>
</template>
@ -52,6 +55,7 @@
import MsChart from '@/components/pure/chart/index.vue';
import MsSelect from '@/components/business/ms-select';
import CardSkeleton from './cardSkeleton.vue';
import { workMemberViewDetail, workProjectMemberOptions } from '@/api/modules/workbench';
import { contentTabList } from '@/config/workbench';
@ -148,9 +152,12 @@
});
options.value.yAxis[0].max = maxAxis < 100 ? 100 : maxAxis + 50;
}
const showSkeleton = ref(false);
async function initOverViewMemberDetail() {
try {
showSkeleton.value = true;
const { startTime, endTime, dayNumber } = timeForm.value;
const params = {
current: 1,
@ -168,6 +175,8 @@
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
} finally {
showSkeleton.value = false;
}
}

View File

@ -1,33 +1,36 @@
<template>
<div :class="`card-wrapper ${props.item.fullScreen ? '' : 'card-min-height'}`">
<div class="flex items-center justify-between">
<a-tooltip :content="t(props.item.label)" position="tl">
<div class="title one-line-text"> {{ t(props.item.label) }} </div>
</a-tooltip>
<div>
<MsSelect
v-model:model-value="projectId"
:options="appStore.projectList"
value-key="id"
label-key="name"
:search-keys="['name']"
class="!w-[200px]"
:prefix="t('workbench.homePage.project')"
@change="changeProject"
>
</MsSelect>
<CardSkeleton v-if="showSkeleton" :show-skeleton="showSkeleton" />
<div v-else>
<div class="flex items-center justify-between">
<a-tooltip :content="t(props.item.label)" position="tl">
<div class="title one-line-text"> {{ t(props.item.label) }} </div>
</a-tooltip>
<div>
<MsSelect
v-model:model-value="projectId"
:options="appStore.projectList"
value-key="id"
label-key="name"
:search-keys="['name']"
class="!w-[200px]"
:prefix="t('workbench.homePage.project')"
@change="changeProject"
>
</MsSelect>
</div>
</div>
</div>
<div class="mt-[16px]">
<div class="case-count-wrapper">
<div class="case-count-item">
<PassRatePie
:options="relatedOptions"
:has-permission="hasPermission"
tooltip-text="workbench.homePage.associateCaseCoverRateTooltip"
:size="60"
:value-list="coverRateValueList"
/>
<div class="mt-[16px]">
<div class="case-count-wrapper">
<div class="case-count-item">
<PassRatePie
:options="relatedOptions"
:has-permission="hasPermission"
tooltip-text="workbench.homePage.associateCaseCoverRateTooltip"
:size="60"
:value-list="coverRateValueList"
/>
</div>
</div>
</div>
</div>
@ -41,6 +44,7 @@
import { ref } from 'vue';
import MsSelect from '@/components/business/ms-select';
import CardSkeleton from './cardSkeleton.vue';
import PassRatePie from './passRatePie.vue';
import { workAssociateCaseDetail } from '@/api/modules/workbench';
@ -95,8 +99,11 @@
},
]);
const showSkeleton = ref(false);
async function getRelatedCaseCount() {
try {
showSkeleton.value = true;
const { startTime, endTime, dayNumber } = timeForm.value;
const detail: PassRateDataType = await workAssociateCaseDetail({
current: 1,
@ -123,6 +130,8 @@
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
} finally {
showSkeleton.value = false;
}
}

View File

@ -1,39 +1,42 @@
<template>
<div class="card-wrapper card-min-height">
<div class="flex items-center justify-between">
<a-tooltip :content="t(props.item.label)" position="tl">
<div class="title one-line-text"> {{ t(props.item.label) }} </div>
</a-tooltip>
<div>
<MsSelect
v-model:model-value="projectId"
:options="appStore.projectList"
allow-search
value-key="id"
label-key="name"
:search-keys="['name']"
class="!w-[200px]"
:prefix="t('workbench.homePage.project')"
@change="changeProject"
>
</MsSelect>
<CardSkeleton v-if="showSkeleton" :show-skeleton="showSkeleton" />
<div v-else>
<div class="flex items-center justify-between">
<a-tooltip :content="t(props.item.label)" position="tl">
<div class="title one-line-text"> {{ t(props.item.label) }} </div>
</a-tooltip>
<div>
<MsSelect
v-model:model-value="projectId"
:options="appStore.projectList"
allow-search
value-key="id"
label-key="name"
:search-keys="['name']"
class="!w-[200px]"
:prefix="t('workbench.homePage.project')"
@change="changeProject"
>
</MsSelect>
</div>
</div>
</div>
<div class="mt-[16px]">
<TabCard :content-tab-list="testPlanTabList" not-has-padding hidden-border min-width="290px">
<template #item="{ item: tabItem }">
<div class="w-full">
<PassRatePie
:has-permission="hasPermission"
:options="tabItem.options"
:size="60"
:value-list="tabItem.valueList"
/>
</div>
</template>
</TabCard>
<div class="h-[148px]">
<MsChart :options="testPlanCountOptions" />
<div class="mt-[16px]">
<TabCard :content-tab-list="testPlanTabList" not-has-padding hidden-border min-width="290px">
<template #item="{ item: tabItem }">
<div class="w-full">
<PassRatePie
:has-permission="hasPermission"
:options="tabItem.options"
:size="60"
:value-list="tabItem.valueList"
/>
</div>
</template>
</TabCard>
<div class="h-[148px]">
<MsChart :options="testPlanCountOptions" />
</div>
</div>
</div>
</div>
@ -47,6 +50,7 @@
import MsChart from '@/components/pure/chart/index.vue';
import MsSelect from '@/components/business/ms-select';
import CardSkeleton from './cardSkeleton.vue';
import PassRatePie from './passRatePie.vue';
import TabCard from './tabCard.vue';
@ -119,8 +123,12 @@
const testPlanCountOptions = ref({});
//
const hasPermission = ref<boolean>(false);
const showSkeleton = ref(false);
async function initTestPlanCount() {
try {
showSkeleton.value = true;
const { startTime, endTime, dayNumber } = timeForm.value;
const params: WorkTestPlanDetail = {
startTime: dayNumber ? null : startTime,
@ -212,6 +220,8 @@
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
} finally {
showSkeleton.value = false;
}
}

View File

@ -1,87 +1,92 @@
<template>
<div class="card-wrapper">
<div class="flex items-center justify-between">
<a-tooltip :content="t(props.item.label)" position="tl">
<div class="title one-line-text"> {{ t(props.item.label) }} </div>
</a-tooltip>
<div>
<MsSelect
v-model:model-value="projectId"
:options="appStore.projectList"
allow-search
value-key="id"
label-key="name"
:search-keys="['name']"
class="!w-[200px]"
:prefix="t('workbench.homePage.project')"
@change="changeProject"
>
</MsSelect>
</div>
</div>
<div>
<MsBaseTable
v-bind="propsRes"
:action-config="{
baseAction: [],
moreAction: [],
}"
class="mt-[16px]"
v-on="propsEvent"
>
<template #num="{ record }">
<a-tooltip :content="`${record.num}`">
<a-button type="text" class="px-0 !text-[14px] !leading-[22px]" @click="openDetail(record.id)">
<div class="one-line-text max-w-[168px]">{{ record.num }}</div>
</a-button>
</a-tooltip>
</template>
<template #passRateColumn>
<div class="flex items-center text-[var(--color-text-3)]">
{{ t('caseManagement.caseReview.passRate') }}
<a-tooltip :content="t('caseManagement.caseReview.passRateTip')" position="right">
<icon-question-circle
class="ml-[4px] text-[var(--color-text-4)] hover:text-[rgb(var(--primary-5))]"
size="16"
/>
</a-tooltip>
</div>
</template>
<template #passRate="{ record }">
<div class="mr-[8px] w-[100px]">
<passRateLine :review-detail="record" height="5px" />
</div>
<div class="text-[var(--color-text-1)]">
{{ `${record.passRate}%` }}
</div>
</template>
<template #reviewPassRule="{ record }">
<a-tag
:color="record.reviewPassRule === 'SINGLE' ? 'rgb(var(--success-2))' : 'rgb(var(--link-2))'"
:class="record.reviewPassRule === 'SINGLE' ? '!text-[rgb(var(--success-6))]' : '!text-[rgb(var(--link-6))]'"
<CardSkeleton v-if="showSkeleton" :show-skeleton="showSkeleton" />
<div v-else>
<div class="flex items-center justify-between">
<a-tooltip :content="t(props.item.label)" position="tl">
<div class="title one-line-text"> {{ t(props.item.label) }} </div>
</a-tooltip>
<div>
<MsSelect
v-model:model-value="projectId"
:options="appStore.projectList"
allow-search
value-key="id"
label-key="name"
:search-keys="['name']"
class="!w-[200px]"
:prefix="t('workbench.homePage.project')"
@change="changeProject"
>
{{
record.reviewPassRule === 'SINGLE'
? t('caseManagement.caseReview.single')
: t('caseManagement.caseReview.multi')
}}
</a-tag>
</template>
<template #createUserName="{ record }">
<a-tooltip :content="`${record.createUserName}`" position="tl">
<div class="one-line-text">{{ record.createUserName }}</div>
</a-tooltip>
</template>
<template v-if="isNoPermission" #empty>
<div class="w-full">
<slot name="empty">
<div class="flex h-[40px] flex-col items-center justify-center">
<span class="text-[14px] text-[var(--color-text-4)]">{{ t('common.noResource') }}</span>
</div>
</slot>
</div>
</template>
</MsBaseTable>
</MsSelect>
</div>
</div>
<div>
<MsBaseTable
v-bind="propsRes"
:action-config="{
baseAction: [],
moreAction: [],
}"
class="mt-[16px]"
v-on="propsEvent"
>
<template #num="{ record }">
<a-tooltip :content="`${record.num}`">
<a-button type="text" class="px-0 !text-[14px] !leading-[22px]" @click="openDetail(record.id)">
<div class="one-line-text max-w-[168px]">{{ record.num }}</div>
</a-button>
</a-tooltip>
</template>
<template #passRateColumn>
<div class="flex items-center text-[var(--color-text-3)]">
{{ t('caseManagement.caseReview.passRate') }}
<a-tooltip :content="t('caseManagement.caseReview.passRateTip')" position="right">
<icon-question-circle
class="ml-[4px] text-[var(--color-text-4)] hover:text-[rgb(var(--primary-5))]"
size="16"
/>
</a-tooltip>
</div>
</template>
<template #passRate="{ record }">
<div class="mr-[8px] w-[100px]">
<passRateLine :review-detail="record" height="5px" />
</div>
<div class="text-[var(--color-text-1)]">
{{ `${record.passRate}%` }}
</div>
</template>
<template #reviewPassRule="{ record }">
<a-tag
:color="record.reviewPassRule === 'SINGLE' ? 'rgb(var(--success-2))' : 'rgb(var(--link-2))'"
:class="
record.reviewPassRule === 'SINGLE' ? '!text-[rgb(var(--success-6))]' : '!text-[rgb(var(--link-6))]'
"
>
{{
record.reviewPassRule === 'SINGLE'
? t('caseManagement.caseReview.single')
: t('caseManagement.caseReview.multi')
}}
</a-tag>
</template>
<template #createUserName="{ record }">
<a-tooltip :content="`${record.createUserName}`" position="tl">
<div class="one-line-text">{{ record.createUserName }}</div>
</a-tooltip>
</template>
<template v-if="isNoPermission" #empty>
<div class="w-full">
<slot name="empty">
<div class="flex h-[40px] flex-col items-center justify-center">
<span class="text-[14px] text-[var(--color-text-4)]">{{ t('common.noResource') }}</span>
</div>
</slot>
</div>
</template>
</MsBaseTable>
</div>
</div>
</div>
</template>
@ -96,6 +101,7 @@
import { MsTableColumn } from '@/components/pure/ms-table/type';
import useTable from '@/components/pure/ms-table/useTable';
import MsSelect from '@/components/business/ms-select';
import CardSkeleton from './cardSkeleton.vue';
import passRateLine from '@/views/case-management/caseReview/components/passRateLine.vue';
import { workReviewList } from '@/api/modules/workbench';
@ -192,9 +198,11 @@
});
const isNoPermission = ref<boolean>(false);
const showSkeleton = ref(false);
async function initData() {
try {
showSkeleton.value = true;
const { startTime, endTime, dayNumber } = timeForm.value;
setLoadListParams({
startTime: dayNumber ? null : startTime,
@ -209,6 +217,8 @@
} catch (error) {
isNoPermission.value = error === 'no_project_permission';
// eslint-disable-next-line no-console
} finally {
showSkeleton.value = false;
}
}