123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287 |
- <script setup lang="ts" generic="T extends Record<string, any>">
- import { computed, ref, useTemplateRef } from 'vue';
- import { useRouter } from 'vue-router';
- import SvgIcon from '@/components/SvgIcon.vue';
- import { t } from '@/i18n';
- import { addUnit } from '@/utils';
- import type { CSSProperties } from 'vue';
- import type { FormInstance } from 'ant-design-vue';
- import type { FormRules, UseGuideStepItem, UseGuideStepItemInstance } from '@/types';
- interface Props {
- title: string;
- steps: UseGuideStepItem[];
- form: T;
- rules: FormRules<T>;
- contentOffset?: number;
- headerMargin?: number;
- }
- const props = defineProps<Props>();
- const router = useRouter();
- const current = ref(0);
- const currentStep = computed(() => {
- return props.steps[current.value];
- });
- const isFirstStep = computed(() => {
- return current.value === 0;
- });
- const contentStyle = computed<CSSProperties>(() => {
- // const contentOffset = props.steps[current.value].contentOffset ?? props.contentOffset;
- return {
- // paddingLeft: addUnit(contentOffset ?? 312),
- };
- });
- const headerStyle = computed<CSSProperties>(() => {
- const dividerWidth = currentStep.value.hideHeaderDivider ? 0 : 1;
- const headerMargin = currentStep.value.headerMargin ?? props.headerMargin;
- return {
- borderBottomWidth: addUnit(dividerWidth),
- marginBottom: addUnit(headerMargin ?? 40),
- };
- });
- const goToStep = (index: number) => {
- current.value = index;
- };
- const goNextStep = () => {
- current.value++;
- };
- const goPrevStep = () => {
- if (isFirstStep.value) {
- router.replace('/first-usage');
- return;
- }
- current.value--;
- };
- const formRef = ref<FormInstance>();
- const stepRef = useTemplateRef<UseGuideStepItemInstance>('stepItem');
- const exportStepContent = async () => {
- await stepRef.value?.exportData?.();
- };
- const finishCurrentStep = async () => {
- try {
- await formRef.value?.validate();
- await stepRef.value?.finish?.();
- if (currentStep.value.isLastStep) {
- router.replace('/first-usage');
- } else {
- goNextStep();
- }
- } catch (err) {
- console.error(err);
- }
- };
- </script>
- <template>
- <ALayout class="use-guide-container">
- <ALayoutSider class="use-guide-sider" :width="268">
- <div class="use-guide-title">{{ title }}</div>
- <div class="use-guide-steps">
- <ASteps :current="current" :items="steps" direction="vertical" />
- </div>
- <AButton class="icon-button doc-button">
- <SvgIcon name="eye-o" />
- {{ $t('common.viewDoc') }}
- </AButton>
- </ALayoutSider>
- <ALayout>
- <ALayoutContent class="use-guide-main" :style="contentStyle">
- <div v-show="!currentStep.hideHeader" class="step-header" :style="headerStyle">
- <div class="step-title">{{ currentStep.stepTitle || currentStep.title }}</div>
- <div class="step-description">{{ currentStep.stepDescription }}</div>
- </div>
- <AForm
- ref="formRef"
- :model="form"
- :rules="rules"
- :layout="currentStep.formLayout"
- :label-align="currentStep.labelAlign"
- :label-col="currentStep.labelCol"
- :wrapper-col="currentStep.wrapperCol"
- :colon="false"
- >
- <component
- ref="stepItem"
- :is="currentStep.component"
- :form="form"
- :steps="steps"
- :step-index="current"
- :go-to-step="goToStep"
- />
- </AForm>
- </ALayoutContent>
- <ALayoutFooter class="use-guide-footer">
- <div class="use-guide-step-button-container">
- <div>
- <AButton type="text" :disabled="currentStep.isLastStep" @click="goNextStep">
- {{ $t('common.skip') }}
- </AButton>
- <AButton type="text" @click="goPrevStep">{{ $t('common.return') }}</AButton>
- </div>
- <div>
- <AButton v-if="currentStep.exportButtonShow" class="icon-button export-button" @click="exportStepContent">
- <SvgIcon name="download" />
- {{ $t('common.exportToLocal') }}
- </AButton>
- <AButton
- v-show="!currentStep.nextStepButtonHide"
- class="next-step-button"
- type="primary"
- :disabled="currentStep.nextStepButtonDisabled"
- @click="finishCurrentStep"
- >
- {{ currentStep.nextStepButtonText || t('common.nextStep') }}
- </AButton>
- </div>
- </div>
- </ALayoutFooter>
- </ALayout>
- </ALayout>
- </template>
- <style lang="scss" scoped>
- .use-guide-container {
- height: 100%;
- }
- .use-guide-sider {
- padding-top: 48px;
- padding-bottom: 40px;
- background: #f0f0f0;
- :deep(.ant-layout-sider-children) {
- display: flex;
- flex-direction: column;
- align-items: center;
- }
- .use-guide-title {
- margin-bottom: 32px;
- text-align: center;
- }
- .use-guide-steps {
- flex: 1;
- }
- :deep(.ant-steps) {
- .ant-steps-item {
- height: 80px;
- }
- .ant-steps-item-wait {
- .ant-steps-item-icon {
- background-color: initial;
- border-color: var(--antd-color-fill);
- }
- .ant-steps-icon {
- color: var(--antd-color-text-quaternary);
- }
- }
- .ant-steps-item-process {
- .ant-steps-item-title {
- font-weight: 500;
- }
- }
- .ant-steps-item-finish {
- .ant-steps-item-icon {
- background-color: initial;
- border-color: var(--antd-color-primary);
- }
- .ant-steps-item-title {
- color: var(--antd-color-text-secondary);
- }
- }
- .ant-steps-item-process,
- .ant-steps-item-wait {
- .ant-steps-item-tail::after {
- background-color: var(--antd-color-fill);
- }
- }
- }
- .doc-button {
- width: 120px;
- height: 40px;
- }
- }
- .use-guide-main {
- padding: 40px;
- padding-bottom: 0;
- overflow: auto;
- background: var(--antd-color-bg-base);
- .step-header {
- padding-bottom: 40px;
- border-bottom: 1px solid #e4e7ed;
- }
- .step-title {
- margin-bottom: 8px;
- font-size: 20px;
- font-weight: 500;
- line-height: 28px;
- color: var(--antd-color-text);
- }
- .step-description {
- min-height: 22px;
- font-size: 14px;
- line-height: 22px;
- color: var(--antd-color-text-secondary);
- }
- }
- .use-guide-footer {
- padding-inline: 40px;
- background: var(--antd-color-bg-base);
- }
- .use-guide-step-button-container {
- display: flex;
- justify-content: space-between;
- max-width: 686px;
- button {
- height: 40px;
- }
- .ant-btn-text {
- width: 80px;
- }
- .export-button {
- width: 136px;
- margin-right: 16px;
- }
- .next-step-button {
- width: 128px;
- font-size: 16px;
- }
- }
- </style>
|