|
@@ -1,8 +1,14 @@
|
|
|
<script setup lang="ts">
|
|
<script setup lang="ts">
|
|
|
-import { ref, reactive, onMounted } from 'vue';
|
|
|
|
|
|
|
+import { ref, reactive, onMounted, watch } from 'vue';
|
|
|
import type { FormInstance } from 'ant-design-vue';
|
|
import type { FormInstance } from 'ant-design-vue';
|
|
|
import dayjs, { type Dayjs } from 'dayjs';
|
|
import dayjs, { type Dayjs } from 'dayjs';
|
|
|
import { VxeUI } from 'vxe-pc-ui';
|
|
import { VxeUI } from 'vxe-pc-ui';
|
|
|
|
|
+import { useRequest } from 'alova/client';
|
|
|
|
|
+import { searchTagsFromSelectableMethod } from '@/request/api/patient.api';
|
|
|
|
|
+import { educationMethod } from '@/request/api/education.api';
|
|
|
|
|
+import { editNotifyMethod, getNotifyDetailMethod } from '@/request/api/notify.api';
|
|
|
|
|
+import { notification } from 'ant-design-vue';
|
|
|
|
|
+import type { NotifyModel } from '@/model/notify.model';
|
|
|
const props = defineProps<{
|
|
const props = defineProps<{
|
|
|
data?: any;
|
|
data?: any;
|
|
|
}>();
|
|
}>();
|
|
@@ -15,30 +21,52 @@ const emits = defineEmits<{
|
|
|
const formRef = ref<FormInstance>();
|
|
const formRef = ref<FormInstance>();
|
|
|
const loading = ref<boolean>(false);
|
|
const loading = ref<boolean>(false);
|
|
|
|
|
|
|
|
-// 表单数据
|
|
|
|
|
-const form = reactive({
|
|
|
|
|
|
|
+// 表单数据(使用 Omit 排除 sendTime,然后添加自定义类型)
|
|
|
|
|
+const form = reactive<Partial<Omit<NotifyModel, 'sendTime'>> & { sendTime: Dayjs | undefined }>({
|
|
|
|
|
+ isSwitch: false, // 是否启用
|
|
|
name: '', // 名称
|
|
name: '', // 名称
|
|
|
- userTag1: '', // 用户标签1
|
|
|
|
|
- enabled: false, // 启用状态
|
|
|
|
|
- notificationChannel: '1', // 通知渠道:1-企业微信
|
|
|
|
|
- wechatContent: '1', // 微信内容:1-站内
|
|
|
|
|
- sendTime: null as Dayjs | null, // 发送时间
|
|
|
|
|
- sendContent: '', // 发送内容
|
|
|
|
|
- articleLink: '', // 宣教文章链接
|
|
|
|
|
|
|
+ status: '0', // 启用状态
|
|
|
|
|
+ pushType: '0' as '0' | '1', // 通知渠道:1-企业微信
|
|
|
|
|
+ sendTime: undefined as Dayjs | undefined, // 发送时间
|
|
|
|
|
+ content: '', // 发送内容
|
|
|
|
|
+ popularScienceArticleTitle: '', // 宣教文章标题
|
|
|
|
|
+ popularScienceArticleId: 0, //宣教id
|
|
|
|
|
+ tagIds: [] as string[], // 用户标签ID
|
|
|
|
|
+ tagNameStr: '',
|
|
|
|
|
+ tagNames: [] as string[],
|
|
|
|
|
+ isExpire: false, // 是否定期
|
|
|
|
|
+ days: 0, // 定期天数
|
|
|
});
|
|
});
|
|
|
|
|
+// 新增或者修改
|
|
|
|
|
+const { loading: updating, send: submitUpdate } = useRequest(editNotifyMethod, { immediate: false });
|
|
|
|
|
+// 获取用户标签
|
|
|
|
|
+const { data: selectable, loading: tagsLoading } = useRequest(searchTagsFromSelectableMethod, { initialData: [] });
|
|
|
|
|
|
|
|
-// 用户标签选项(示例数据,实际应从API获取)
|
|
|
|
|
-const userTagOptions = ref([
|
|
|
|
|
- { label: '标签1', value: '1' },
|
|
|
|
|
- { label: '标签2', value: '2' },
|
|
|
|
|
- { label: '标签3', value: '3' },
|
|
|
|
|
-]);
|
|
|
|
|
|
|
+// 获取宣教列表(不传page和size,获取全部数据)
|
|
|
|
|
+const educationList = ref<any[]>([]);
|
|
|
|
|
+const { loading: educationLoading, onSuccess: onEducationSuccess } = useRequest(() => educationMethod(undefined, undefined, {}), {
|
|
|
|
|
+ initialData: { data: [], total: 0 },
|
|
|
|
|
+ immediate: true,
|
|
|
|
|
+});
|
|
|
|
|
+onEducationSuccess(({ data: { data } }) => {
|
|
|
|
|
+ educationList.value = data || [];
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+// 自定义搜索过滤函数,让搜索匹配 title 字段
|
|
|
|
|
+const filterOption = (input: string, option: any) => {
|
|
|
|
|
+ if (!input) return true;
|
|
|
|
|
+ // 通过 option.value (即 id) 找到对应的原始数据
|
|
|
|
|
+ const item = educationList.value.find((item) => item.id === option.value);
|
|
|
|
|
+ if (item && item.title) {
|
|
|
|
|
+ return item.title.toLowerCase().includes(input.toLowerCase());
|
|
|
|
|
+ }
|
|
|
|
|
+ return false;
|
|
|
|
|
+};
|
|
|
|
|
|
|
|
// 通知渠道选项
|
|
// 通知渠道选项
|
|
|
-const notificationChannelOptions = [{ label: '企业微信', value: '1' }];
|
|
|
|
|
-// 微信内容
|
|
|
|
|
-const wechatContentOptions = [
|
|
|
|
|
- { label: '站内', value: '1' },
|
|
|
|
|
|
|
+const notificationChannelOptions = [
|
|
|
|
|
+ // { label: '企业微信', value: '1' },
|
|
|
|
|
+ { label: '站内', value: '0' },
|
|
|
];
|
|
];
|
|
|
// 表单验证规则
|
|
// 表单验证规则
|
|
|
const rules = {
|
|
const rules = {
|
|
@@ -47,16 +75,98 @@ const rules = {
|
|
|
notificationChannel: [{ required: true, message: '请选择通知渠道', trigger: 'change' }],
|
|
notificationChannel: [{ required: true, message: '请选择通知渠道', trigger: 'change' }],
|
|
|
sendTime: [{ required: true, message: '请选择发送时间', trigger: 'change' }],
|
|
sendTime: [{ required: true, message: '请选择发送时间', trigger: 'change' }],
|
|
|
sendContent: [{ required: true, message: '请输入发送内容', trigger: 'blur' }],
|
|
sendContent: [{ required: true, message: '请输入发送内容', trigger: 'blur' }],
|
|
|
|
|
+ isExpire: [
|
|
|
|
|
+ { required: true, message: '请选择查阅时长', trigger: 'change' },
|
|
|
|
|
+ {
|
|
|
|
|
+ validator: () => {
|
|
|
|
|
+ if (form.isExpire && (!form.days || form.days <= 0)) {
|
|
|
|
|
+ return Promise.reject('请输入天数');
|
|
|
|
|
+ }
|
|
|
|
|
+ return Promise.resolve();
|
|
|
|
|
+ },
|
|
|
|
|
+ trigger: 'change',
|
|
|
|
|
+ },
|
|
|
|
|
+ ],
|
|
|
|
|
+ days: [
|
|
|
|
|
+ {
|
|
|
|
|
+ validator: () => {
|
|
|
|
|
+ if (form.isExpire && (!form.days || form.days <= 0)) {
|
|
|
|
|
+ return Promise.reject('请输入天数');
|
|
|
|
|
+ }
|
|
|
|
|
+ return Promise.resolve();
|
|
|
|
|
+ },
|
|
|
|
|
+ trigger: 'blur',
|
|
|
|
|
+ },
|
|
|
|
|
+ ],
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-// 初始化数据
|
|
|
|
|
-onMounted(() => {
|
|
|
|
|
- if (props.data) {
|
|
|
|
|
- Object.assign(form, props.data);
|
|
|
|
|
|
|
+// 禁用早于当前时间的日期
|
|
|
|
|
+const disabledDate = (current: Dayjs) => {
|
|
|
|
|
+ // 禁用今天之前的日期
|
|
|
|
|
+ return current && current.isBefore(dayjs(), 'day');
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+// 禁用早于当前时间的时间
|
|
|
|
|
+const disabledTime = (current: Dayjs | null) => {
|
|
|
|
|
+ if (!current) return {};
|
|
|
|
|
+ const now = dayjs();
|
|
|
|
|
+ // 如果选择的是今天,则禁用当前时间之前的时间
|
|
|
|
|
+ if (current.isSame(now, 'day')) {
|
|
|
|
|
+ return {
|
|
|
|
|
+ disabledHours: () => {
|
|
|
|
|
+ const hours = [];
|
|
|
|
|
+ for (let i = 0; i < now.hour(); i++) {
|
|
|
|
|
+ hours.push(i);
|
|
|
|
|
+ }
|
|
|
|
|
+ return hours;
|
|
|
|
|
+ },
|
|
|
|
|
+ disabledMinutes: (selectedHour: number) => {
|
|
|
|
|
+ if (selectedHour === now.hour()) {
|
|
|
|
|
+ const minutes = [];
|
|
|
|
|
+ for (let i = 0; i <= now.minute(); i++) {
|
|
|
|
|
+ minutes.push(i);
|
|
|
|
|
+ }
|
|
|
|
|
+ return minutes;
|
|
|
|
|
+ }
|
|
|
|
|
+ return [];
|
|
|
|
|
+ },
|
|
|
|
|
+ disabledSeconds: (selectedHour: number, selectedMinute: number) => {
|
|
|
|
|
+ if (selectedHour === now.hour() && selectedMinute === now.minute()) {
|
|
|
|
|
+ const seconds = [];
|
|
|
|
|
+ for (let i = 0; i <= now.second(); i++) {
|
|
|
|
|
+ seconds.push(i);
|
|
|
|
|
+ }
|
|
|
|
|
+ return seconds;
|
|
|
|
|
+ }
|
|
|
|
|
+ return [];
|
|
|
|
|
+ },
|
|
|
|
|
+ };
|
|
|
|
|
+ }
|
|
|
|
|
+ return {};
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+const getNotifyDetail = async (id?: string) => {
|
|
|
|
|
+ const res: any = await getNotifyDetailMethod(id || '');
|
|
|
|
|
+ if (res) {
|
|
|
|
|
+ Object.assign(form, res);
|
|
|
|
|
+ form.isSwitch = form.status === '0' ? true : false;
|
|
|
if (form.sendTime) {
|
|
if (form.sendTime) {
|
|
|
- form.sendTime = dayjs(form.sendTime);
|
|
|
|
|
|
|
+ form.sendTime = typeof form.sendTime === 'string' ? dayjs(form.sendTime) : form.sendTime;
|
|
|
|
|
+ }
|
|
|
|
|
+ // 如果标签为空,则清空标签
|
|
|
|
|
+ if (!form.tagIds || form.tagIds.length === 0) {
|
|
|
|
|
+ form.tagIds = [];
|
|
|
|
|
+ form.tagNames = [];
|
|
|
|
|
+ form.tagNameStr = '';
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+};
|
|
|
|
|
+// 初始化数据
|
|
|
|
|
+onMounted(() => {
|
|
|
|
|
+ console.log(props.data, 'props.data');
|
|
|
|
|
+ if (props.data && props.data.id) {
|
|
|
|
|
+ getNotifyDetail(props.data.id);
|
|
|
|
|
+ }
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
// 保存
|
|
// 保存
|
|
@@ -64,11 +174,29 @@ const handleSave = async () => {
|
|
|
try {
|
|
try {
|
|
|
await formRef.value?.validate();
|
|
await formRef.value?.validate();
|
|
|
loading.value = true;
|
|
loading.value = true;
|
|
|
- const submitData = {
|
|
|
|
|
|
|
+ const tagIdsArray = Array.isArray(form.tagIds) ? form.tagIds : form.tagIds ? [form.tagIds] : [];
|
|
|
|
|
+ const tagNames = tagIdsArray.map((id: string) => selectable.value.find((option: any) => option.id === id)?.name || '');
|
|
|
|
|
+ const status = form.isSwitch ? '0' : '1';
|
|
|
|
|
+ const submitData: Partial<NotifyModel> = {
|
|
|
...form,
|
|
...form,
|
|
|
- sendTime: form.sendTime ? form.sendTime.format('YYYY-MM-DD HH:mm:ss') : null,
|
|
|
|
|
|
|
+ status: status,
|
|
|
|
|
+ sendTime: form.sendTime ? form.sendTime.format('YYYY-MM-DD HH:mm:ss') : undefined,
|
|
|
|
|
+ tagNameStr: tagNames.join(',') || '',
|
|
|
|
|
+ tagNames: tagNames,
|
|
|
};
|
|
};
|
|
|
- emits('submit', submitData);
|
|
|
|
|
|
|
+ console.log(submitData, 'submitData');
|
|
|
|
|
+ submitUpdate({
|
|
|
|
|
+ ...submitData,
|
|
|
|
|
+ }).then(() => {
|
|
|
|
|
+ // 保存成功后,同步更新 form 的 status,保持 isSwitch 不变
|
|
|
|
|
+ if (submitData.status) {
|
|
|
|
|
+ form.status = submitData.status;
|
|
|
|
|
+ }
|
|
|
|
|
+ notification.success({
|
|
|
|
|
+ message: '保存成功',
|
|
|
|
|
+ });
|
|
|
|
|
+ emits('submit', { ...form });
|
|
|
|
|
+ });
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
console.error('表单验证失败', error);
|
|
console.error('表单验证失败', error);
|
|
|
} finally {
|
|
} finally {
|
|
@@ -82,6 +210,15 @@ const handleBack = () => {
|
|
|
VxeUI.modal.close(`edit-notify-modal`);
|
|
VxeUI.modal.close(`edit-notify-modal`);
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+// 监听查阅时长类型切换,切换为长期时清除天数
|
|
|
|
|
+watch(
|
|
|
|
|
+ () => form.isExpire,
|
|
|
|
|
+ (newVal) => {
|
|
|
|
|
+ if (!newVal) {
|
|
|
|
|
+ form.days = undefined;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+);
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|
|
|
<template>
|
|
<template>
|
|
@@ -101,17 +238,21 @@ const handleBack = () => {
|
|
|
<a-input v-model:value="form.name" placeholder="请输入" />
|
|
<a-input v-model:value="form.name" placeholder="请输入" />
|
|
|
</a-form-item>
|
|
</a-form-item>
|
|
|
|
|
|
|
|
- <a-form-item label="用户标签" name="userTag1" class="form-row-item">
|
|
|
|
|
- <a-select v-model:value="form.userTag1" placeholder="请选择" :options="userTagOptions" style="width: 100%" allow-clear />
|
|
|
|
|
|
|
+ <a-form-item label="用户标签" name="tagIds" class="form-row-item mr-10 ml-10">
|
|
|
|
|
+ <a-select v-model:value="form.tagIds" placeholder="请选择" style="width: 100%" mode="multiple" show-search>
|
|
|
|
|
+ <a-select-option v-for="option in selectable" :key="option.id" :value="option.id">
|
|
|
|
|
+ {{ option.name }}
|
|
|
|
|
+ </a-select-option>
|
|
|
|
|
+ </a-select>
|
|
|
</a-form-item>
|
|
</a-form-item>
|
|
|
|
|
|
|
|
- <a-form-item label="启用" name="enabled" required class="form-row-item">
|
|
|
|
|
- <a-switch v-model:checked="form.enabled" />
|
|
|
|
|
|
|
+ <a-form-item label="启用" name="status" required class="form-row-item">
|
|
|
|
|
+ <a-switch v-model:checked="form.isSwitch" />
|
|
|
</a-form-item>
|
|
</a-form-item>
|
|
|
</div>
|
|
</div>
|
|
|
<!-- 通知渠道 -->
|
|
<!-- 通知渠道 -->
|
|
|
- <a-form-item label="通知渠道" name="notificationChannel" required class="form-row-item">
|
|
|
|
|
- <a-radio-group v-model:value="form.notificationChannel">
|
|
|
|
|
|
|
+ <a-form-item label="通知渠道" name="pushType" required class="form-row-item">
|
|
|
|
|
+ <a-radio-group v-model:value="form.pushType">
|
|
|
<a-radio v-for="option in notificationChannelOptions" :key="option.value" :value="option.value">
|
|
<a-radio v-for="option in notificationChannelOptions" :key="option.value" :value="option.value">
|
|
|
{{ option.label }}
|
|
{{ option.label }}
|
|
|
</a-radio>
|
|
</a-radio>
|
|
@@ -119,25 +260,53 @@ const handleBack = () => {
|
|
|
</a-form-item>
|
|
</a-form-item>
|
|
|
|
|
|
|
|
<!-- 微信内容标签 -->
|
|
<!-- 微信内容标签 -->
|
|
|
- <a-form-item v-if="form.notificationChannel === '1'" label="微信内容" class="form-row-item">
|
|
|
|
|
- <a-radio v-for="option in wechatContentOptions" :key="option.value" :value="option.value">
|
|
|
|
|
- {{ option.label }}
|
|
|
|
|
- </a-radio>
|
|
|
|
|
- </a-form-item>
|
|
|
|
|
|
|
+ <div class="mb-4 font-bold">{{ form.pushType === '1' ? '微信内容' : '内容' }}</div>
|
|
|
|
|
|
|
|
<!-- 发送时间 -->
|
|
<!-- 发送时间 -->
|
|
|
<a-form-item label="发送时间" name="sendTime" required class="form-row-item">
|
|
<a-form-item label="发送时间" name="sendTime" required class="form-row-item">
|
|
|
- <a-date-picker v-model:value="form.sendTime" placeholder="请输入" show-time format="YYYY-MM-DD HH:mm:ss" style="width: 100%" />
|
|
|
|
|
|
|
+ <a-date-picker
|
|
|
|
|
+ v-model:value="form.sendTime"
|
|
|
|
|
+ placeholder="请输入"
|
|
|
|
|
+ show-time
|
|
|
|
|
+ format="YYYY-MM-DD HH:mm"
|
|
|
|
|
+ style="width: 100%"
|
|
|
|
|
+ :disabled-date="disabledDate"
|
|
|
|
|
+ :disabled-time="disabledTime"
|
|
|
|
|
+ />
|
|
|
|
|
+ </a-form-item>
|
|
|
|
|
+ <!-- 查阅时长-->
|
|
|
|
|
+ <a-form-item label="查阅时长" name="isExpire" required class="form-row-item">
|
|
|
|
|
+ <div style="display: flex; align-items: center; gap: 16px; flex-wrap: wrap">
|
|
|
|
|
+ <a-radio-group v-model:value="form.isExpire">
|
|
|
|
|
+ <a-radio :value="false">长期</a-radio>
|
|
|
|
|
+ <a-radio :value="true">定期</a-radio>
|
|
|
|
|
+ </a-radio-group>
|
|
|
|
|
+ <template v-if="form.isExpire">
|
|
|
|
|
+ <a-input-number v-model:value="form.days" :min="1" placeholder="请输入" style="width: 120px" @change="() => formRef?.validateFields(['readTimeType', 'days'])" />
|
|
|
|
|
+ <span>天</span>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </div>
|
|
|
</a-form-item>
|
|
</a-form-item>
|
|
|
-
|
|
|
|
|
<!-- 发送内容 -->
|
|
<!-- 发送内容 -->
|
|
|
- <a-form-item label="发送内容" name="sendContent" required class="form-row-item">
|
|
|
|
|
- <a-textarea v-model:value="form.sendContent" placeholder="请输入" allow-clear />
|
|
|
|
|
|
|
+ <a-form-item label="发送内容" name="sendContent" required class="form-row-item" v-if="form.pushType === '1'">
|
|
|
|
|
+ <a-textarea v-model:value="form.content" placeholder="请输入" allow-clear />
|
|
|
</a-form-item>
|
|
</a-form-item>
|
|
|
|
|
|
|
|
<!-- 宣教文章链接 -->
|
|
<!-- 宣教文章链接 -->
|
|
|
<a-form-item label="宣教文章链接" name="articleLink" class="form-row-item">
|
|
<a-form-item label="宣教文章链接" name="articleLink" class="form-row-item">
|
|
|
- <a-input v-model:value="form.articleLink" placeholder="请输入搜索" allow-clear />
|
|
|
|
|
|
|
+ <a-select
|
|
|
|
|
+ v-model:value="form.popularScienceArticleId"
|
|
|
|
|
+ placeholder="请输入搜索"
|
|
|
|
|
+ allow-clear
|
|
|
|
|
+ :loading="educationLoading"
|
|
|
|
|
+ show-search
|
|
|
|
|
+ clear-icon
|
|
|
|
|
+ :filter-option="filterOption"
|
|
|
|
|
+ >
|
|
|
|
|
+ <a-select-option v-for="option in educationList" :key="option.id" :value="Number(option.id)" :title="option.title">
|
|
|
|
|
+ {{ option.title }}
|
|
|
|
|
+ </a-select-option>
|
|
|
|
|
+ </a-select>
|
|
|
</a-form-item>
|
|
</a-form-item>
|
|
|
</a-form>
|
|
</a-form>
|
|
|
</div>
|
|
</div>
|
|
@@ -171,7 +340,7 @@ const handleBack = () => {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
gap: 16px;
|
|
gap: 16px;
|
|
|
align-items: flex-start;
|
|
align-items: flex-start;
|
|
|
- margin-bottom: 24px;
|
|
|
|
|
|
|
+ // margin-bottom: 24px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.form-row-item {
|
|
.form-row-item {
|