form-content.vue 9.48 KB
<script setup lang="ts">
  import { computed, onBeforeUnmount, ref, shallowRef, watch } from 'vue';
  import useNotificationApi from '@/http/notification';
  import ImageUpload from '@/components/image-upload/index.vue';
  import UserTable from '@/views/operation/notification/components/user-table.vue';
  import ActivityTable from '@/views/operation/notification/components/activity-table.vue';
  import ProjectTable from '@/views/operation/notification/components/project-table.vue';
  import dayjs from 'dayjs';
  import { range } from 'lodash';
  import useLoading from '@/hooks/loading';
  import { onBeforeRouteLeave, useRouter } from 'vue-router';

  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  import { Editor, Toolbar } from '@wangeditor/editor-for-vue';
  import { Boot } from '@wangeditor/editor';

  import { Notification } from '@/utils/model';

  import '@/views/operation/notification/module';
  import PublishUserSelect from '@/views/operation/notification/components/publish-user-select.vue';

  type ModelType = Pick<
    Notification,
    | 'title'
    | 'type'
    | 'cover'
    | 'content'
    | 'rich_content'
    | 'link_type'
    | 'link_id'
    | 'publish_type'
    | 'publish_at'
    | 'publish_to'
    | 'is_alert'
  > & { publish_count: number; link_name: '' };

  const props = defineProps<{
    modelValue: ModelType;
    disabled?: boolean;
    hideSubmit?: boolean;
    submit?: (value: unknown) => Promise<unknown>;
  }>();
  const emits = defineEmits<{ (e: 'update:modelValue', value: unknown): void }>();

  const { typeOption, publishTypeOption, linkTypeOption, alertOption } = useNotificationApi;
  const { loading, setLoading } = useLoading(false);

  const formRef = ref();
  const editorRef = shallowRef();
  const textareaRef = ref();

  Boot.setEditorConfig({ readOnly: props.disabled });

  const onCreateEditor = (editor: unknown) => {
    editorRef.value = editor;
  };

  onBeforeUnmount(() => {
    const editor = editorRef.value;
    if (editor == null) return;
    editor.destroy();
  });

  const formValue = computed({
    get: () => props.modelValue,
    set: (val) => emits('update:modelValue', val),
  });

  const textareaRow = computed(() => {
    let row = 3;
    if (formValue.value.type === 2) {
      row += 6;
    }
    if (formValue.value.publish_type === 2) {
      row += 2;
    }

    return { minRows: row, maxRows: row };
  });

  watch(
    () => textareaRow.value,
    () => textareaRef.value?.handleResize()
  );

  const formRule = computed(() => {
    return {
      title: [{ required: true, message: '请输入标题' }],
      type: [{ required: true, message: '请选择类型' }],
      content: [{ required: true, message: '请输入内容' }],
      cover: formValue.value.type === 2 ? [{ required: true, message: '请上传图片' }] : [],
      link_type: [{ required: true, message: '请选择交互类型' }],
      link_name: [{ required: ['user', 'activity', 'project'].includes(formValue.value.link_type), message: '请选择交互对象' }],
      publish_type: [{ required: true, message: '请选择发布设置' }],
      publish_at: [{ required: formValue.value.publish_type === 2, message: '请选择发布时间' }],
      publish_to: [{ required: true, message: '请选择通知对象' }],
      is_alert: [{ required: true, message: '请选择是否弹窗' }],
    };
  });

  const onChangeValue = (value: object) => Object.assign(formValue.value, value);
  const disabledDate = (current?: Date) => dayjs(current).isBefore(dayjs().subtract(1, 'day'));
  const disabledTime = (current?: Date) => {
    return {
      disabledHours: () => range(0, 23).filter((item) => dayjs().isSame(dayjs(current), 'day') && item < dayjs().hour()),
      disabledMinutes: () => range(0, 59).filter((item) => dayjs().isSame(dayjs(current), 'day') && item < dayjs().minute()),
    };
  };
  const openDate = (visible: boolean) => {
    if (visible && formValue.value.publish_at?.length === 0) {
      formValue.value.publish_at = dayjs().format('YYYY-MM-DD hh:mm:ss');
    }
  };

  const router = useRouter();
  const onBack = () => router.replace({ name: 'operation-notification' });
  const onSubmit = () => {
    setLoading(true);
    props.submit?.(formValue.value)?.finally(() => setLoading(false));
  };

  onBeforeRouteLeave((to, from) => {
    if (from.meta.from === to.name) {
      to.meta.reload = false;
    }
  });
</script>

<template>
  <a-form
    ref="formRef"
    :disabled="disabled || loading"
    label-align="right"
    :model="formValue"
    :rules="formRule"
    :auto-label-width="true"
    :label-col-props="{ span: 6 }"
    :wrapper-col-props="{ span: 18 }"
    @submit-success="onSubmit"
  >
    <a-row :gutter="16">
      <a-col :span="24" style="margin-bottom: 8px">
        <a-alert banner style="margin-bottom: 8px">
          关联对象: {{ formValue.link_type === 'rich' ? 'H5页面' : formValue.link_name || '无' }}, 通知人数:{{ formValue.publish_count }}


          <template #action>
            <a-button v-if="!hideSubmit" type="primary" :loading="loading" html-type="submit">发布</a-button>
            <a-button style="margin-left: 8px" @click="onBack">{{ hideSubmit ? '返回' : '取消' }}</a-button>
          </template>
        </a-alert>
      </a-col>
      <a-col :span="12">
        <a-form-item label="通知标题" field="title" show-colon>
          <a-input v-model="formValue.title" placeholder="请输入" :max-length="30" show-word-limit />
        </a-form-item>
        <a-form-item label="通知对象" field="publish_to" show-colon>
          <publish-user-select v-model="formValue.publish_to" v-model:count="formValue.publish_count" :max-tag-count="4" />
        </a-form-item>
        <a-form-item label="通知正文" field="content" show-colon>
          <a-textarea ref="textareaRef" v-model="formValue.content" :auto-size="textareaRow" :max-length="200" show-word-limit />
        </a-form-item>
      </a-col>
      <a-col :span="8">
        <a-form-item label="类型" field="type" show-colon>
          <a-select v-model="formValue.type" :options="typeOption" placeholder="请选择" @change="() => onChangeValue({ cover: '' })" />
        </a-form-item>
        <a-form-item v-show="formValue.type === 2" label="图片" field="cover" show-colon>
          <image-upload v-model="formValue.cover" :width="331" :height="118" fit="fill" />
        </a-form-item>
        <a-form-item label="弹窗提醒" field="is_alert" show-colon>
          <a-select v-model="formValue.is_alert" :options="alertOption" placeholder="请选择" />
        </a-form-item>
        <a-form-item label="发布设置" field="publish_type" show-colon>
          <a-select
            v-model.number="formValue.publish_type"
            :options="publishTypeOption"
            placeholder="请选择"
            @change="(value:number) => (value === 1 ? (formValue.publish_at = '') : void 0)"
          />
        </a-form-item>
        <a-form-item v-show="formValue.publish_type === 2" label="发布时间" field="publish_at" show-colon>
          <a-date-picker
            v-model="formValue.publish_at"
            style="width: 100%"
            show-time
            value-format="YYYY-MM-DD HH:mm:ss"
            format="YYYY-MM-DD HH:mm"
            :disabled-date="disabledDate"
            :disabled-time="disabledTime"
            :day-start-of-week="1"
            @popup-visible-change="openDate"
          />
        </a-form-item>
        <a-form-item label="点击交互" field="link_type" show-colon>
          <a-select
            v-model="formValue.link_type"
            :options="linkTypeOption"
            placeholder="请选择"
            @change="() => onChangeValue({ link_id: 0, link_name: '', rich_content: '' })"
          />
        </a-form-item>
      </a-col>
      <a-col :span="4" />
    </a-row>
  </a-form>

  <template v-if="formValue.link_type !== 'none'">
    <a-divider style="margin-top: 8px" />
    <user-table
      v-if="!disabled && formValue.link_type === 'user'"
      :body-style="{ padding: 0 }"
      :select-key="formValue.link_id"
      @on-change="onChangeValue"
    />
    <activity-table
      v-if="!disabled && formValue.link_type === 'activity'"
      :body-style="{ padding: 0 }"
      :select-key="formValue.link_id"
      @on-change="onChangeValue"
    />
    <project-table
      v-if="!disabled && formValue.link_type === 'project'"
      :body-style="{ padding: 0 }"
      :select-key="formValue.link_id"
      @on-change="onChangeValue"
    />
    <a-card v-if="formValue.link_type === 'rich'" :body-style="{ padding: 0 }" style="border: 1px solid #ccc">
      <Toolbar style="border-bottom: 1px solid #ccc" :editor="editorRef" />
      <a-layout>
        <a-layout-content>
          <Editor v-model="formValue.rich_content" style="min-height: 400px" @on-created="onCreateEditor" />
        </a-layout-content>
        <a-layout-sider class="rich" :width="400">
          <!-- eslint-disable vue/no-v-html -->
          <div class="content" v-html="formValue.rich_content" />
          <!--eslint-enable-->
        </a-layout-sider>
      </a-layout>
    </a-card>
  </template>
</template>

<style scoped lang="less">
  .rich {
    box-shadow: unset;
    margin-left: 10px;
    border-left: 1px solid #ccc;

    .content {
      overflow-y: auto;
      //height: 400px;
      padding: 0 10px;

      :deep(img) {
        height: auto !important;
        max-width: 100%;
        object-fit: contain;
      }

      :deep(video) {
        max-width: 100%;
        height: auto !important;
        display: block;
        margin: 0 auto;
      }
    }
  }
</style>