Emit Vue3 Paramters passing between components (Child to Parent)

3 阅读2分钟

1 Chinld Component

<template>
  <div>
    <a-button type="link" @click="showModal">选择模板</a-button>
    <a-modal
      width="400px"
      v-model:open="visible"
      title="选择模板"
      @ok="handleOk"
    >
      <a-select
        v-model:value="value"
        :options="options"
        mode="tags"
        :size="size"
        placeholder="Please select"
        style="width: 200px"
      ></a-select>
    </a-modal>
  </div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import type { SelectProps } from 'ant-design-vue';
import request from '@/utils/request';

const emit = defineEmits(['selectionData']);

const queryByPageUrl = '/api/list';

const value = ref<string[]>([]);
const size = ref<SelectProps['size']>();
const options = ref<{ label: string; value: string; templateNumber: string }[]>(
  []
);
const reqParams: { [key: string]: any } = {
  pageNum: 1,
  pageSize: 999,
  status: 1,
};

const selectionData = ref<any>([]);

const selectionOption = () =>
  request
    .post(queryByPageUrl, reqParams)
    .then((res: any) => {
      if (res.code === 200) {
        const { data } = res;
        options.value = data.list?.map((item: any) => ({
          label: item.title,
          value: item.id,
          templateNumber: item.templateNumber,
        }));
      }
    })
    .catch((error: any) => {
      console.error('Error fetching data:', error);
    });
const visible = ref(false);
const showModal = () => {
  console.log('open selection modal');
  visible.value = true;
  selectionOption();
};

const handleOk = () => {
  console.log('selection value', value.value);
  console.log('options', options.value);
  visible.value = false;
  selectionData.value = options.value.filter((item: any) =>
    value.value.includes(item.value)
  );
  console.log('selectionData', selectionData.value);
  // Paraments passing
  emit('selectionData', selectionData.value);
};
</script>

## Parent Component

```<script lang="ts">
import { defineComponent, reactive, ref, nextTick, onMounted } from 'vue';
import Sortable from 'sortablejs';
import { getDrawConfig, saveDrawConfig } from './service';
import { DrawConfigInter, TemplateInter } from './interface';
import TemplateList from './TemplateList.vue';
import { message } from 'ant-design-vue';
import dayjs, { Dayjs } from 'dayjs';
import LottorySelection from './components/LottorySelection.vue';

export default defineComponent({
  components: {
    TemplateList,
    LottorySelection,
  },
  setup() {
    const formState = reactive<Partial<DrawConfigInter>>({
      ruleName: '',
      longTerm: true,
      startTime: '',
      endTime: '',
      templateConfig: [],
      winnerLimit: 0,
      rewardType: 1,
      rewardProperties: {
        starValidityTimeUnit: '',
        starValidityDuration: 0,
        starCount: 0,
      },
    });

    const pageType = ref<string>('detail');

    const templateListItems = ref<Array<TemplateInter>>([]);
    const dateRange = ref<Dayjs[]>([]);

    const questionnaireTemplateRef = ref(null);
    let sortableInstance: any = null;

    const initSortable = () => {
      const el = questionnaireTemplateRef.value;
      if (el) {
        sortableInstance = Sortable.create(el, {
          animation: 150,
          onEnd: (evt: any) => {
            const { oldIndex, newIndex } = evt;
            if (oldIndex !== null && newIndex !== null) {
              const item = formState.templateConfig?.splice(oldIndex, 1)[0];
              if (item) {
                formState.templateConfig?.splice(newIndex, 0, item);
              }
            }
          },
        });
      }
    };

    onMounted(async () => {
      try {
        const res: any = await getDrawConfig();
        if (res.code === 200) {
          const data = res.data as DrawConfigInter;
          formState.ruleName = data.ruleName;
          formState.longTerm = data.longTerm;
          if (!data.longTerm) {
            dateRange.value = [dayjs(data.startTime), dayjs(data.endTime)];
          }
          formState.templateConfig = data.templateConfig || [];
          templateListItems.value = data.templateConfig || [];
          formState.winnerLimit = data.winnerLimit;
          formState.rewardType = data.rewardType;
          // 确保 rewardProperties 存在且字段完整
          formState.rewardProperties = {
            ...(data.rewardProperties || {}),
            starValidityTimeUnit:
              data.rewardProperties?.starValidityTimeUnit || '',
            starValidityDuration:
              data.rewardProperties?.starValidityDuration || 0,
            starCount: data.rewardProperties?.starCount || 0,
          };
        }
      } catch (error) {
        console.error('Failed to fetch draw config:', error);
      }
      // 在接口数据加载完成后初始化可拖动功能
      nextTick(() => {
        initSortable();
      });
    });

    const handleSubmit = async () => {
      try {
        const payload: Partial<DrawConfigInter> = {
          ruleName: formState.ruleName,
          longTerm: formState.longTerm,
          startTime: dayjs(dateRange.value?.[0]).format(),
          endTime: dayjs(dateRange.value?.[1]).format(),
          templateConfig: templateListItems.value,
          winnerLimit: formState.winnerLimit,
          rewardType: formState.rewardType,
          rewardProperties: formState.rewardProperties,
        };
        console.log('payload', payload);
        const res: any = await saveDrawConfig(payload);
        if (res.code === 200) {
          message.success('保存成功');
        } else {
          message.error('保存失败');
        }
      } catch (error) {
        console.error('Failed to save draw config:', error);
        message.error('保存失败');
      }
    };

    return {
      pageType,
      dateRange,
      formState,
      templateListItems,
      questionnaireTemplateRef,
      handleSubmit,
    };
  },
});
</script>

<template>
  <div class="draw-config">
    <a-form :model="formState" layout="vertical">
      <a-form-item label="活动名称:" required>
        <a-input v-model:value="formState.ruleName" />
      </a-form-item>

      <a-form-item label="活动有效期:" required>
        <a-radio-group v-model:value="formState.longTerm">
          <a-radio :value="true">长期有效</a-radio>
          <a-radio :value="false">固定有效期</a-radio>
        </a-radio-group>
        <a-range-picker
          v-if="formState.longTerm === false"
          v-model:value="dateRange"
          :placeholder="['开始日期', '结束日期']"
          :rangeSeparator="'-'"
          style="margin-top: 8px"
        />
      </a-form-item>
      <a-row>
      
        <LottorySelection
          ref="templateSelection"
          // accept paramter from the chile component
          @selectionData="(data) => { 
          console.log('processFormData', data); 
          data.map((item: any) => {
            templateListItems.push({
              title: item.label,
              templateNumber: item.templateNumber,
            });
          });
        }"
        />
      </a-row>
      <a-form-item label="参与抽奖问卷模版:" required>
        <TemplateList
          ref="compInfoRef"
          :pageType="pageType"
          :tableData="templateListItems"
        />
      </a-form-item>

      <a-form-item label="中奖数量配置:" required>
        <a-input-number v-model:value="formState.winnerLimit" />
      </a-form-item>

      <a-form-item label="奖励类型:" required>
        <a-radio-group v-model:value="formState.rewardType">
          <a-radio :value="1">好礼星星</a-radio>
        </a-radio-group>
      </a-form-item>

      <a-form-item
        label="好礼星星有效期:"
        required
        v-if="formState.rewardType === 1"
      >
        <a-select
          v-model:value="formState.rewardProperties!.starValidityTimeUnit"
        >
          <a-select-option value="day">天</a-select-option>
          <a-select-option value="month">月</a-select-option>
          <a-select-option value="year">年</a-select-option>
        </a-select>
        <a-input-number
          v-model:value="formState.rewardProperties!.starValidityDuration"
          style="margin-left: 8px"
        />
      </a-form-item>

      <a-form-item
        label="好礼星星数量:"
        required
        v-if="formState.rewardType === 1"
      >
        <a-input-number v-model:value="formState.rewardProperties!.starCount" />
      </a-form-item>

      <a-form-item>
        <a-button type="primary" @click="handleSubmit">保存</a-button>
      </a-form-item>
    </a-form>
  </div>
</template>

<style scoped>
.questionnaire-template-list {
  list-style: none;
  padding: 0;
}

.questionnaire-template-item {
  display: flex;
  align-items: center;
  margin-bottom: 8px;
  background-color: #f5f5f5;
  padding: 4px 8px;
  border-radius: 4px;
}

.drag-btn {
  cursor: move;
  margin-right: 10px;
}
</style>