张田田 4 ماه پیش
والد
کامیت
9facf64e93

+ 1 - 0
@types/typed-router.d.ts

@@ -54,6 +54,7 @@ declare module 'vue-router/auto-routes' {
     '/aio/flow-config/panel/StartPanel': RouteRecordInfo<'/aio/flow-config/panel/StartPanel', '/aio/flow-config/panel/StartPanel', Record<never, never>, Record<never, never>>,
     '/editor/': RouteRecordInfo<'/editor/', '/editor', Record<never, never>, Record<never, never>>,
     '/editor/preview': RouteRecordInfo<'/editor/preview', '/editor/preview', Record<never, never>, Record<never, never>>,
+    '/EditorDemo': RouteRecordInfo<'/EditorDemo', '/EditorDemo', Record<never, never>, Record<never, never>>,
     '/login': RouteRecordInfo<'/login', '/login', Record<never, never>, Record<never, never>>,
   }
 }

+ 14 - 7
public/editor/preview.html

@@ -40,15 +40,22 @@
           // if (!host) throw { message: `请求地址为空` };
           const hashParams = location.hash.split('?')[1];
           const queryString = location.search || (hashParams ? `?${hashParams}` : '');
+          // alert('queryString: ' + queryString);
           const request = await fetch(`${location.origin}/manager/fdhb-mobile/psarticle/getPsarticleDetailById${queryString}`);
+          if (!request.ok) {
+            throw new Error(`请求失败: ${request.status}`);
+          }
           const result = await request.json();
-          alert('request status: ' + request.status + ', ok: ' + request.ok);
-          alert('request: ' + request);
-          // const result = await JSON.parse(request.text());
-          alert('result: ' + result);
-          document.title = result.title || `加载完成`;
-          document.querySelector('.w-e-text-container').innerHTML = result.content || `暂无内容`;
-          render(result);
+          if (!result.success || result.code !== 200) {
+            throw new Error(result.msg || '请求未成功');
+          }
+          // alert('request status: ' + request.status + ', ok: ' + request.ok);
+          // alert('request: ' + result);
+          const data = result.data;
+          // alert('result: ' + data.title);
+          document.title = data.title || `加载完成`;
+          document.querySelector('.w-e-text-container').innerHTML = decodeURIComponent(data?.content) || `暂无内容`;
+          render(data);
         } catch (e) {
           document.querySelector('.w-e-text-container').innerHTML = e.message || `请求未成功`;
         } finally {

+ 214 - 45
src/components/EditNotify.vue

@@ -1,8 +1,14 @@
 <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 dayjs, { type Dayjs } from 'dayjs';
 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<{
   data?: any;
 }>();
@@ -15,30 +21,52 @@ const emits = defineEmits<{
 const formRef = ref<FormInstance>();
 const loading = ref<boolean>(false);
 
-// 表单数据
-const form = reactive({
+// 表单数据(使用 Omit 排除 sendTime,然后添加自定义类型)
+const form = reactive<Partial<Omit<NotifyModel, 'sendTime'>> & { sendTime: Dayjs | undefined }>({
+  isSwitch: false, // 是否启用
   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 = {
@@ -47,16 +75,98 @@ const rules = {
   notificationChannel: [{ required: true, message: '请选择通知渠道', trigger: 'change' }],
   sendTime: [{ required: true, message: '请选择发送时间', trigger: 'change' }],
   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) {
-      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 {
     await formRef.value?.validate();
     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,
-      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) {
     console.error('表单验证失败', error);
   } finally {
@@ -82,6 +210,15 @@ const handleBack = () => {
   VxeUI.modal.close(`edit-notify-modal`);
 };
 
+// 监听查阅时长类型切换,切换为长期时清除天数
+watch(
+  () => form.isExpire,
+  (newVal) => {
+    if (!newVal) {
+      form.days = undefined;
+    }
+  }
+);
 </script>
 
 <template>
@@ -101,17 +238,21 @@ const handleBack = () => {
             <a-input v-model:value="form.name" placeholder="请输入" />
           </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 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>
         </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">
               {{ option.label }}
             </a-radio>
@@ -119,25 +260,53 @@ const handleBack = () => {
         </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-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 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 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>
     </div>
@@ -171,7 +340,7 @@ const handleBack = () => {
   display: flex;
   gap: 16px;
   align-items: flex-start;
-  margin-bottom: 24px;
+  // margin-bottom: 24px;
 }
 
 .form-row-item {

+ 40 - 0
src/model/notify.model.ts

@@ -0,0 +1,40 @@
+export interface NotifyModel {
+  id: string; //通知id
+  name: string; //名称
+  content: string; //企业微信发送内容
+  popularScienceArticleId:number;//健康宣教id
+  popularScienceArticleTitle:string;//健康宣教标题
+  expireTime:string;//过期时间
+  isExpire:boolean;//是否过期
+  days:number;//定期天数
+  sendCount:number;//已发条数
+  tagIds: string[] | string; //用户标签ID集合(新增/修改通知时,使用该字段传参)
+  tagNames: string[] | string; //用户标签名称集合
+  tagNameStr: string; //用户标签名称汇总
+  pushType: '0' | '1'; //推送类型:0-站内,1-企业微信
+  status:string; //状态:0-启用,1-禁用
+  scheduleConf:string;//发送时间配置
+  createTime: string; //创建时间
+  createBy: string; //创建人
+  isSwitch: boolean; //是否启用
+  sendTime:string;//发送时间
+  sendTimeStart:string;//发送开始时间
+  sendTimeEnd:string;//发送结束时间
+}
+
+export type NotifyQuery = Partial<NotifyModel>;
+
+export interface NotifyRecordModel {
+  noticeSendRecordId: string; //通知记录id
+  noticeName: string; //通知名称
+  sendTime: string; //发送时间
+  createBy: string; //发送人
+  isRead: boolean; //是否已读
+  patientName: string; //姓名
+  phone: string; //手机号码
+  popularScienceArticleId:string; //健康宣教ID
+  sendTimeStart:string;//发送开始时间
+  sendTimeEnd:string;//发送结束时间
+}
+
+export type NotifyRecordQuery = Partial<NotifyRecordModel>;

+ 54 - 0
src/model/satisfaction.model.ts

@@ -0,0 +1,54 @@
+export interface SatisfactionModel {
+  id: string; //	满意度调研问卷ID
+  name: string; //满意度调研问卷名称
+  satisfiesyContent: {
+    name: string; //问卷名称
+    startSentence: string; //开头语
+    endSentence: string; //结束语
+    suggestion: string; //其他意见
+    groups: Array<{
+      name: string; //	问题组名称
+      items: Array<{
+        name: string;
+        score: string;
+      }>;
+    }>;
+  };
+  satisfiesyPmr: {
+    triggerType: string; //触发方式 0-就诊后 1-调养付款后 2-调养结束后
+    triggerIntervalHours: number; //触发间隔小时数
+  };
+  triggerStr: string; //触发条件
+  pushType: string; //推送类型 0-站内 1-企业微信
+  status: string; //状态 0-启用 1-禁用
+  createTime: string; //创建时间
+  sendCount: number; //已发送条数
+}
+
+export type SatisfactionQuery = Partial<SatisfactionModel>;
+
+
+//发送记录
+export interface SatisfactionSendRecordModel {
+  id: string; //满意度调研问卷ID
+  name: string; //满意度调研问卷名称
+  sendTime: string; //发送时间
+  submitTime: string; //问卷提交时间
+  patientName: string; //姓名
+  phone: string; //手机号码
+  score: number; //得分
+
+}
+export type SatisfactionSendRecordQuery = Partial<SatisfactionSendRecordModel>;
+
+//满意度问卷统计
+export interface SatisfactionStatisticsModel {
+  id: string; //满意度调研问卷ID
+  ids: string[]; //满意度调研问卷ID集合
+  name: string; //满意度调研问卷名称
+  sendTimeStart: string; // 发送起始时间
+  sendTimeEnd: string; // 发送结束时间
+  xaxis:string[]; //x轴数据
+  yaxis:number[]; //y轴数据
+}
+export type SatisfactionStatisticsQuery = Partial<SatisfactionStatisticsModel>;

+ 22 - 21
src/pages/index/healthy/education.vue

@@ -7,7 +7,7 @@ import type { EducationModel, EducationQuery } from '@/model/education.model';
 
 // 接口数据
 import { deleteEducationMethod, educationMethod, updateEducationStatusMethod } from '@/request/api/education.api';
-import { usePagination, useRequest } from 'alova/client';
+import { usePagination } from 'alova/client';
 import { notification } from 'ant-design-vue';
 
 import { type VxeFormListeners, type VxeFormProps, type VxeGridInstance, type VxeGridListeners, type VxeGridProps, VxeUI } from 'vxe-pc-ui';
@@ -80,7 +80,7 @@ const searchFormProps = reactive<VxeFormProps<EducationQuery>>({
 });
 
 const searchFormEmits: VxeFormListeners<EducationQuery> = {
-  // 查询随访计划
+  // 查询
   submit({ data }) {
     model.value = { ...data };
   },
@@ -185,9 +185,10 @@ onSuccess(({ data: { data } }) => {
 
 onMounted(async () => {
   model.value = toRaw(searchFormProps.data);
-  const request = await fetch(`${location.origin}/manager/fdhb-mobile/psarticle/getPsarticleDetailById?popularScienceArticleId=7`);
+  const request = await fetch(`${location.origin}/manager/fdhb-mobile/psarticle/getPsarticleDetailById?shortToken=29a71cc5-6d7c-4ae8-84d7-a9d08107cf71`);
   const result = await request.json();
-  console.log(result, 'result===result');
+  console.log(result, 'result===result',result.data,decodeURIComponent(result.data?.content || ''));
+  console.log(result.status, 'result.text()===result.text()');
 });
 
 function updatePlanStatus(model: EducationModel, index: number, status: EducationModel['status']) {
@@ -245,23 +246,23 @@ function editEducation(model?: EducationModel, index?: number) {
             refresh(page.value);
             VxeUI.modal.close(`education-modal`);
           },
-          onPreview: (content: string) => {
-            // 打开预览弹窗
-            VxeUI.modal.open({
-              title: '预览宣教内容',
-              fullscreen: true,
-              escClosable: true,
-              destroyOnClose: true,
-              id: `education-preview-modal`,
-              slots: {
-                default: () => {
-                  return h(EducationPreview, {
-                    content: content,
-                  });
-                },
-              },
-            });
-          },
+          // onPreview: (content: string) => {
+          //   // 打开预览弹窗
+          //   VxeUI.modal.open({
+          //     title: '预览宣教内容',
+          //     fullscreen: true,
+          //     escClosable: true,
+          //     destroyOnClose: true,
+          //     id: `education-preview-modal`,
+          //     slots: {
+          //       default: () => {
+          //         return h(EducationPreview, {
+          //           content: content,
+          //         });
+          //       },
+          //     },
+          //   });
+          // },
         });
       },
     },

+ 0 - 1
src/pages/index/notify/manage.vue

@@ -1,6 +1,5 @@
 <script setup lang="ts">
 import { ref, unref, shallowReactive, defineAsyncComponent } from 'vue';
-import { usePermission } from '@/core/usePermission';
 
 const panels = shallowReactive([
   {

+ 8 - 2
src/request/api/education.api.ts

@@ -4,10 +4,16 @@ import type { EducationModel, EducationQuery } from '@/model/education.model';
 import request from '@/request/alova';
 
 // 获取宣教列表
-export function educationMethod(page: number, size: number, query?: EducationQuery) {
+export function educationMethod(page?: number, size?: number, query?: EducationQuery) {
+  const params: any = { ...query };
+  // 如果传了 page 和 size,就添加分页参数
+  if (page !== undefined && size !== undefined) {
+    params.pageNum = page;
+    params.pageSize = size;
+  }
   return request.Post<List<EducationModel>, List<any>>(`/fdhb-pc/psaManage/pagePsa`, query ?? {}, {
     hitSource: /education$/,
-    params: { pageNum: page, pageSize: size, ...query },
+    params,
   });
 }
 

+ 35 - 0
src/request/api/notify.api.ts

@@ -0,0 +1,35 @@
+import type { List } from '@/model';
+import type { NotifyModel, NotifyQuery } from '@/model/notify.model';
+
+import request from '@/request/alova';
+
+// 获取通知列表
+export function notifyMethod(page: number, size: number, query?: NotifyQuery) {
+  return request.Post<List<NotifyModel>, List<any>>(`/fdhb-pc/noticeManage/pageNotice`, query ?? {}, {
+    hitSource: /notify$/,
+    params: { pageNum: page, pageSize: size, ...query },
+  });
+}
+// 获取通知记录分页列表
+export function getNotifyRecordMethod(page: number, size: number, query?: NotifyQuery) {
+  return request.Post<List<NotifyModel>, List<any>>(`/fdhb-pc/noticeManage/selectNoticeSendRecord`, query ?? {}, {
+    hitSource: /notify-record$/,
+    params: { pageNum: page, pageSize: size, ...query },
+  });
+}
+// 新增/修改通知
+export function editNotifyMethod(data: Partial<NotifyModel>) {
+  return data.id ? request.Post(`/fdhb-pc/noticeManage/updateNotice`, data, { name: 'edit-notify' }) : request.Post(`/fdhb-pc/noticeManage/addNotice`, data, { name: 'add-notify' });
+}
+// 删除通知
+export function deleteNotifyMethod(data: Partial<NotifyModel>) {
+  return request.Post(`/fdhb-pc/noticeManage/deleteNotice/${data.id}`, { name: 'delete-notify' });
+}
+//启用-停用通知
+export function updateNotifyStatusMethod(data: Partial<NotifyModel>) {
+  return request.Get(`/fdhb-pc/noticeManage/updateStatus/${data.id}/${data.status}`, { name: 'update-notify-status' });
+}
+//根据通知ID获取通知详情
+export function getNotifyDetailMethod(id: string) {
+  return request.Post(`/fdhb-pc/noticeManage/detail/${id}`, { name: 'get-notify-detail' });
+}

+ 51 - 0
src/request/api/satisfaction.api.ts

@@ -0,0 +1,51 @@
+import type { List } from '@/model';
+import type {
+  SatisfactionSendRecordModel,
+  SatisfactionSendRecordQuery,
+  SatisfactionModel,
+  SatisfactionQuery,
+  SatisfactionStatisticsModel,
+  SatisfactionStatisticsQuery,
+} from '@/model/satisfaction.model';
+
+import request from '@/request/alova';
+
+// 获取满意度调研问卷发送记录分页列表
+export function satisfactionSendRecordMethod(page: number, size: number, query?: SatisfactionSendRecordQuery) {
+  return request.Post<List<SatisfactionSendRecordModel>, List<any>>(`/fdhb-pc/satisfiesyManage/selectSatisfiesySendRecord`, query ?? {}, {
+    hitSource: /satisfaction-send-record$/,
+    params: { pageNum: page, pageSize: size, ...query },
+  });
+}
+// 获取满意度调研问卷列表
+export function satisfactionMethod(page: number, size: number, query?: SatisfactionQuery) {
+  return request.Post<List<SatisfactionModel>, List<any>>(`/fdhb-pc/satisfiesyManage/pageSatisfiesy`, query ?? {}, {
+    hitSource: /satisfaction$/,
+    params: { pageNum: page, pageSize: size, ...query },
+  });
+}
+//满意度问卷统计
+export function satisfactionStatisticsMethod(query?: SatisfactionStatisticsQuery) {
+  return request.Post<SatisfactionStatisticsModel, List<any>>(`/fdhb-pc/satisfiesyManage/satisfiesyStatistics`, query ?? {}, {
+    hitSource: /satisfaction-statistics$/,
+    params: { ...query },
+  });
+}
+
+//根据满意度调研问卷ID获取满意度调研问卷详情
+export function getSatisfactionDetailMethod(id: string) {
+  return request.Post(`/fdhb-pc/satisfiesyManage/detail/${id}`, { name: 'get-satisfaction-detail' });
+}
+
+// 设置触发条件
+export function setSatisfactionTriggerMethod(data: Partial<SatisfactionModel>) {
+  return request.Post(
+    `/fdhb-pc/satisfiesyManage/setSatisfiesyTrigger/${data.id}/${data.status}/${data.pushType}`,
+    {},
+    { name: 'set-satisfaction-trigger', params: { triggerType: data.satisfiesyPmr?.triggerType, triggerIntervalHours: data.satisfiesyPmr?.triggerIntervalHours } }
+  );
+}
+//新增满意度调研问卷
+export function addSatisfactionMethod(data: Partial<SatisfactionModel>) {
+  return request.Post(`/fdhb-pc/satisfiesyManage/addContent`, data, { name: 'add-satisfaction' });
+}

+ 12 - 8
src/satisfaction/EditQuestionnaire.vue

@@ -51,6 +51,10 @@ const form = reactive({
 // 表单验证规则
 const rules = {
   name: [{ required: true, message: '请输入问卷名称', trigger: 'blur' }],
+  openingRemarks: [{ required: true, message: '请输入开头语', trigger: 'blur' }],
+  closingRemarks: [{ required: true, message: '请输入结束语', trigger: 'blur' }],
+  questionGroups: [{ required: true, message: '请输入问题分组', trigger: 'blur' }],
+  questions: [{ required: true, message: '请输入问题', trigger: 'blur' }],
 };
 
 // 初始化数据
@@ -124,12 +128,12 @@ const handleBack = () => {
         </a-form-item>
 
         <!-- 开头语 -->
-        <a-form-item label="开头语">
+        <a-form-item label="开头语" name="openingRemarks" required>
           <a-textarea v-model:value="form.openingRemarks" placeholder="请输入" :rows="4" />
         </a-form-item>
 
         <!-- 结束语 -->
-        <a-form-item label="结束语">
+        <a-form-item label="结束语" name="closingRemarks" required>
           <a-textarea v-model:value="form.closingRemarks" placeholder="请输入" :rows="4" />
         </a-form-item>
 
@@ -137,7 +141,7 @@ const handleBack = () => {
         <div class="question-groups-section">
           <div v-for="(group, groupIndex) in form.questionGroups" :key="group.id" class="question-group-item">
             <!-- 问题分组 -->
-            <a-form-item :label="`问题分组${groupIndex + 1}`">
+            <a-form-item :label="`问题分组${groupIndex + 1}`" name="questionGroups" required>
               <div class="group-input-wrapper">
                 <a-input v-model:value="group.name" placeholder="请输入" />
                 <button class="add-btn" @click="addQuestionGroup">
@@ -149,7 +153,7 @@ const handleBack = () => {
             <!-- 问题列表 -->
             <div class="questions-list">
               <div v-for="(question, questionIndex) in group.questions" :key="question.id" class="question-item">
-                <a-form-item :label="`问题${questionIndex + 1}`">
+                <a-form-item :label="`问题${questionIndex + 1}`" name="questions" required>
                   <div class="question-input-wrapper">
                     <a-input v-model:value="question.content" placeholder="请输入" />
                     <button class="add-btn" @click="addQuestion(group.id)">
@@ -166,18 +170,18 @@ const handleBack = () => {
 
     <!-- 底部按钮 -->
     <div class="footer-actions">
-      <a-button type="default" @click="handleBack">
+      <vxe-button type="default" @click="handleBack">
         <template #icon>
           <ArrowLeftOutlined />
         </template>
         取消
-      </a-button>
-      <a-button type="primary" @click="handleSave" :loading="loading">
+      </vxe-button>
+      <vxe-button type="primary" status="warning" @click="handleSave" :loading="loading">
         <template #icon>
           <SaveOutlined />
         </template>
         保存
-      </a-button>
+      </vxe-button>
     </div>
   </div>
 </template>

+ 28 - 149
src/satisfaction/SeeQuestionnaire.vue

@@ -1,9 +1,5 @@
 <script setup lang="ts">
 import { ref, reactive, onMounted } from 'vue';
-import type { FormInstance } from 'ant-design-vue';
-import { VxeUI } from 'vxe-pc-ui';
-import { PlusOutlined, DeleteOutlined, ArrowLeftOutlined, SaveOutlined } from '@ant-design/icons-vue';
-
 const props = defineProps<{
   data?: any;
 }>();
@@ -13,9 +9,6 @@ const emits = defineEmits<{
   back: [];
 }>();
 
-const formRef = ref<FormInstance>();
-const loading = ref<boolean>(false);
-
 // 问卷数据结构
 interface Question {
   id: string;
@@ -48,6 +41,7 @@ const form = reactive({
       ],
     },
   ] as Section[],
+  otherComments: '', // 其他意见
 });
 
 // 初始化数据
@@ -57,73 +51,9 @@ onMounted(() => {
     if (props.data.introduction) form.introduction = props.data.introduction;
     if (props.data.scoreExplanation) form.scoreExplanation = props.data.scoreExplanation;
     if (props.data.sections) form.sections = props.data.sections;
+    if (props.data.otherComments) form.otherComments = props.data.otherComments;
   }
 });
-
-// 添加问题
-const addQuestion = (sectionId: string) => {
-  const section = form.sections.find((s) => s.id === sectionId);
-  if (section) {
-    const newQuestion: Question = {
-      id: Date.now().toString(),
-      content: '',
-      required: true,
-    };
-    section.questions.push(newQuestion);
-  }
-};
-
-// 删除问题
-const removeQuestion = (sectionId: string, questionId: string) => {
-  const section = form.sections.find((s) => s.id === sectionId);
-  if (section) {
-    const index = section.questions.findIndex((q) => q.id === questionId);
-    if (index > -1) {
-      section.questions.splice(index, 1);
-    }
-  }
-};
-
-// 添加章节
-const addSection = () => {
-  const sectionNumber = form.sections.length + 1;
-  const sectionTitles = ['一', '二', '三', '四', '五', '六', '七', '八', '九', '十'];
-  const newSection: Section = {
-    id: Date.now().toString(),
-    title: `${sectionTitles[sectionNumber - 1] || sectionNumber}、`,
-    questions: [],
-  };
-  form.sections.push(newSection);
-};
-
-// 删除章节
-const removeSection = (sectionId: string) => {
-  const index = form.sections.findIndex((s) => s.id === sectionId);
-  if (index > -1 && form.sections.length > 1) {
-    form.sections.splice(index, 1);
-  }
-};
-
-// 保存
-const handleSave = async () => {
-  try {
-    loading.value = true;
-    const submitData = {
-      ...form,
-    };
-    emits('submit', submitData);
-  } catch (error) {
-    console.error('保存失败', error);
-  } finally {
-    loading.value = false;
-  }
-};
-
-// 返回
-const handleBack = () => {
-  emits('back');
-  VxeUI.modal.close(`edit-questionnaire-modal`);
-};
 </script>
 
 <template>
@@ -157,17 +87,6 @@ const handleBack = () => {
             <h2 class="section-title-input label">
               {{ section.title }}
             </h2>
-            <!-- <a-button
-                v-if="form.sections.length > 1"
-                type="text"
-                danger
-                @click="removeSection(section.id)"
-                class="delete-section-btn"
-              >
-                <DeleteOutlined />
-                删除章节
-              </a-button> -->
-            <!-- </div> -->
 
             <!-- 问题列表 -->
             <div class="questions-list">
@@ -177,16 +96,6 @@ const handleBack = () => {
                   <div class="question-input label">
                     {{ question.content }}
                   </div>
-                  <!-- <a-button
-                    v-if="section.questions.length > 1"
-                    type="text"
-                    danger
-                    size="small"
-                    @click="removeQuestion(section.id, question.id)"
-                    class="delete-question-btn"
-                  >
-                    <DeleteOutlined />
-                  </a-button> -->
                 </div>
                 <!-- 评分选项(1-5分) -->
                 <div class="score-options">
@@ -197,47 +106,24 @@ const handleBack = () => {
                     <a-radio :value="4">4分</a-radio>
                     <a-radio :value="5">5分</a-radio>
                   </a-radio-group>
-                  <!-- <span class="score-hint">(预览模式,实际填写时可选择)</span> -->
                 </div>
               </div>
-
-              <!-- 添加问题按钮 -->
-              <!-- <a-button
-                type="dashed"
-                block
-                @click="addQuestion(section.id)"
-                class="add-question-btn"
-              >
-                <PlusOutlined />
-                添加问题
-              </a-button> -->
             </div>
           </div>
+        </div>
 
-          <!-- 添加章节按钮 -->
-          <!-- <a-button type="dashed" block @click="addSection" class="add-section-btn">
-            <PlusOutlined />
-            添加章节
-          </a-button> -->
+        <!-- 其他意见 -->
+        <div class="other-comments-section">
+          <div class="other-comments-label">其他意见:</div>
+          <a-textarea
+            v-model:value="form.otherComments"
+            placeholder="请给我们留言您的其他意见"
+            :rows="4"
+            class="other-comments-input"
+          />
         </div>
       </a-form>
     </div>
-
-    <!-- 底部按钮 -->
-    <!-- <div class="footer-actions">
-      <vxe-button type="primary" status="primary" @click="handleBack">
-        <template #icon>
-          <ArrowLeftOutlined />
-        </template>
-        取消
-      </vxe-button>
-      <vxe-button type="submit" status="warning" @click="handleSave" :loading="loading">
-        <template #icon>
-          <SaveOutlined />
-        </template>
-        保存
-      </vxe-button>
-    </div> -->
   </div>
 </template>
 
@@ -451,31 +337,24 @@ const handleBack = () => {
   }
 }
 
-// 添加问题按钮
-.add-question-btn {
-  margin-top: 8px;
-}
-
-// 添加章节按钮
-.add-section-btn {
-  margin-top: 16px;
-}
-
-// 底部按钮
-.footer-actions {
-  display: flex;
-  justify-content: center;
-  gap: 12px;
-  padding-top: 16px;
-  border-top: 1px solid #f0f0f0;
+// 其他意见
+.other-comments-section {
+  margin-top: 32px;
+  padding: 16px;
+  border: 1px solid #e8e8e8;
+  border-radius: 4px;
+  background: #fff;
 
-  :deep(.vxe-button--status-warning) {
-    background: #ff7a45;
-    border-color: #ff7a45;
+  .other-comments-label {
+    font-size: 14px;
+    color: rgba(0, 0, 0, 0.85);
+    font-weight: 500;
+    margin-bottom: 12px;
+  }
 
-    &:hover {
-      background: #ff9256;
-      border-color: #ff9256;
+  .other-comments-input {
+    :deep(.ant-input) {
+      font-size: 14px;
     }
   }
 }

+ 14 - 3
src/satisfaction/SetQuestionnaire.vue

@@ -130,15 +130,15 @@ const handleBack = () => {
   <div class="satisfaction-form-container">
     <!-- 表单内容 -->
     <div class="form-content">
-      <a-form ref="formRef" :model="form" :rules="rules" layout="horizontal" :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }">
+      <a-form ref="formRef" :model="form" :rules="rules" layout="horizontal" >
         <!-- 问卷名称和启用 - 同一行 -->
         <div class="form-row">
-          <a-form-item label="问卷名称" name="name" class="form-row-item">
+          <a-form-item label="问卷名称" name="name" class="form-row-item form-row-item-name">
             <!-- <a-input v-model:value="form.name" placeholder="请输入" /> -->
             {{ form.name }}
           </a-form-item>
 
-          <a-form-item label="启用:" name="enabled" class="form-row-item" required>
+          <a-form-item label="启用:" name="enabled" class="form-row-item form-row-item-enabled" required>
             <a-switch v-model:checked="form.enabled" />
           </a-form-item>
         </div>
@@ -256,6 +256,17 @@ const handleBack = () => {
   margin-bottom: 0;
 }
 
+.form-row-item-name {
+  flex: 0 1 auto;
+  min-width: 400px;
+  max-width: 600px;
+}
+
+.form-row-item-enabled {
+  flex: 0 0 auto;
+  width: auto;
+}
+
 .form-row-item :deep(.ant-form-item) {
   margin-bottom: 0;
   display: flex;

+ 89 - 42
src/service/NotifyManageList.vue

@@ -1,21 +1,21 @@
 <script setup lang="ts">
 import { ref, reactive, shallowRef, onBeforeUnmount, onMounted } from 'vue';
-import type { SystemItemModel, SystemIteQuery } from '@/model/care.model';
+import type { NotifyModel, NotifyQuery } from '@/model/notify.model';
 import EditNotify from '@/components/EditNotify.vue';
 // 接口数据
 
-import { pageConfirmedCpMethod, deleteConfirmedCpMethod } from '@/request/api/care.api';
+import { notifyMethod, deleteNotifyMethod, updateNotifyStatusMethod } from '@/request/api/notify.api';
 import { usePagination, useRequest } from 'alova/client';
 import { notification } from 'ant-design-vue';
 import dayjs from 'dayjs';
 import { type VxeFormListeners, type VxeFormProps, type VxeGridInstance, type VxeGridListeners, type VxeGridProps, VxeUI } from 'vxe-pc-ui';
 
-const model = shallowRef<SystemIteQuery>();
-const searchFormProps = reactive<VxeFormProps<SystemIteQuery>>({
+const model = shallowRef<NotifyQuery>();
+const searchFormProps = reactive<VxeFormProps<NotifyQuery>>({
   titleWidth: 100,
   titleAlign: 'right',
   titleColon: true,
-  data: { types: ['1'] as any, status: '0' as any },
+  data: {},
   items: [
     {
       field: 'name',
@@ -26,7 +26,7 @@ const searchFormProps = reactive<VxeFormProps<SystemIteQuery>>({
     {
       field: 'sendTime',
       title: '发送时间',
-      span: 6,
+      span: 8,
       slots: {
         default: 'createTimes',
       },
@@ -52,9 +52,11 @@ const searchFormProps = reactive<VxeFormProps<SystemIteQuery>>({
     },
   ],
 });
-const searchFormEmits: VxeFormListeners<SystemIteQuery> = {
+const searchFormEmits: VxeFormListeners<NotifyQuery> = {
   // 查询随访计划
   submit({ data }) {
+    data.sendTimeStart = sendTimeStart.value ? dayjs(sendTimeStart.value).format('YYYY-MM-DD HH:mm') : '';
+    data.sendTimeEnd = sendTimeEnd.value ? dayjs(sendTimeEnd.value).format('YYYY-MM-DD HH:mm') : '';
     onSearch(data);
   },
 
@@ -63,22 +65,15 @@ const searchFormEmits: VxeFormListeners<SystemIteQuery> = {
     onSearch(data);
   },
 };
-function onSearch(data: SystemIteQuery) {
-  const types: string[] = (data as any).types?.length ? (data as any).types : ['1'];
-  const status: string = (data as any).status ?? '0';
-  const isForWrap: SystemIteQuery['isForWrap'] = types.includes('1') ? 'Y' : null;
-  const isForInfer: SystemIteQuery['isForInfer'] = types.includes('2') ? 'Y' : null;
+function onSearch(data: NotifyQuery) {
 
-  model.value = { ...data, types: [...types] as any, status: status as any, isForWrap, isForInfer };
+  model.value = { ...data };
   nextTick(() => {
-    (searchFormProps.data as any)!.types = [...types];
-    (searchFormProps.data as any)!.status = status;
-    (searchFormProps.data as any)!.isForInfer = isForInfer;
-    (searchFormProps.data as any)!.isForWrap = isForWrap;
+    (searchFormProps.data as any)! = { ...data };
   });
 }
-const gridRef = ref<VxeGridInstance<SystemItemModel>>();
-const gridOptions = reactive<VxeGridProps<SystemItemModel>>({
+const gridRef = ref<VxeGridInstance<NotifyModel>>();
+const gridOptions = reactive<VxeGridProps<NotifyModel>>({
   id: 'tag-list',
   border: true,
   showOverflow: true,
@@ -103,11 +98,30 @@ const gridOptions = reactive<VxeGridProps<SystemItemModel>>({
   columns: [
     { type: 'seq', width: 70, fixed: 'left' },
     { field: 'name', title: '名称' },
-    { field: 'conditioningProgramType', title: '通知渠道' },
-    { field: 'cpFixedPricingRule.unitPrice', title: '用户标签'},
-    { field: 'cpFixedPricingRule.pricingUnit', title: '发送时间'},
-    { field: 'cpFixedPricingRule.convertDose', title: '已发条数' },
-    { field: 'conditioningProgramSupplierName', title: '启用状态', slots: { default: 'conditioningProgramSupplierNameCell' } },
+    { field: 'pushType', title: '通知渠道', slots: { default: 'pushTypeCell' } },
+    { field: 'tagNames', title: '用户标签' },
+    { field: 'sendTime', title: '发送时间' },
+    { field: 'sendCount', title: '已发条数' },
+    {
+      field: 'status',
+      title: '启用状态',
+      align: 'center',
+      cellRender: {
+        name: 'VxeSwitch',
+        props: {
+          openLabel: '启用',
+          openValue: '0',
+          closeLabel: '禁用',
+          closeValue: '1',
+        },
+        events: {
+          change({ row, rowIndex }, { value }) {
+            row.status = { '1': '0', '0': '1' }[value as string] as any;
+            updatePlanStatus(row, rowIndex, value);
+          },
+        },
+      },
+    },
     {
       field: 'action',
       title: '操作',
@@ -154,7 +168,7 @@ const {
   refresh,
   remove,
   send: sendRefresh,
-} = usePagination((page, size) => pageConfirmedCpMethod(page, size, model.value), {
+} = usePagination((page, size) => notifyMethod(page, size, model.value), {
   initialData: { data: [], total: 0 },
   initialPage: 1,
   initialPageSize: 100,
@@ -168,16 +182,16 @@ onMounted(() => {
   onSearch(toRaw(searchFormProps.data) as any);
 });
 
-function deleteConfirmed(model: SystemItemModel, index: number) {
+function deleteConfirmed(model: NotifyModel, index: number) {
   const { name } = model;
   VxeUI.modal.confirm({
-    title: `删除项目`,
-    content: `确认要删除 ${name} 项目吗?`,
+    title: `删除通知`,
+    content: `确认要删除 ${name} 通知吗?`,
     showClose: false,
     onConfirm() {
-      deleteConfirmedCpMethod(model).then(() => {
+      deleteNotifyMethod(model).then(() => {
         notification.success({
-          message: `删除项目: ${name}`,
+          message: `删除通知: ${name}`,
           description: '操作成功',
         });
         refresh(page.value);
@@ -186,10 +200,10 @@ function deleteConfirmed(model: SystemItemModel, index: number) {
   });
 }
 
-function editConfirmed(model?: SystemItemModel, index?: number) {
+function editConfirmed(model?: NotifyModel, index?: number) {
   const addType = `itemsList`;
   VxeUI.modal.open({
-    title: model?.conditioningProgramType ?? '项目',
+    title: model?.name ?? '通知',
     fullscreen: true,
     escClosable: true,
     destroyOnClose: true,
@@ -203,7 +217,7 @@ function editConfirmed(model?: SystemItemModel, index?: number) {
             ...model,
             addType,
           },
-          onSubmit(data: SystemItemModel) {
+          onSubmit(data: NotifyModel) {
             refresh(page.value);
             VxeUI.modal.close(`edit-notify-modal`);
           },
@@ -212,8 +226,26 @@ function editConfirmed(model?: SystemItemModel, index?: number) {
     },
   });
 }
-
-function seeDetail(model?: SystemItemModel, index?: number) {
+function updatePlanStatus(model: NotifyModel, index: number, status: NotifyModel['status']) {
+  const { id, name } = model;
+  const label = { '1': '禁用', '0': '启用' }[status];
+  VxeUI.modal.confirm({
+    title: `启用状态`,
+    content: `确认要 ${label} ${name} 吗?`,
+    showClose: false,
+    onConfirm() {
+      updateNotifyStatusMethod({ id, status }).then(() => {
+        notification.success({
+          message: `${label}: ${name}`,
+          description: '操作成功',
+        });
+        model.status = status;
+        replace(model, index);
+      });
+    },
+  });
+}
+function seeDetail(model?: NotifyModel, index?: number) {
   // VxeUI.modal.open({
   //   title: model?.name,
   //   height: 500,
@@ -231,12 +263,12 @@ function seeDetail(model?: SystemItemModel, index?: number) {
   // });
 }
 // 日期验证
-const updateTimeStart = ref<string>('');
-const updateTimeEnd = ref<string>('');
+const sendTimeStart = ref<string>('');
+const sendTimeEnd = ref<string>('');
 // 禁用结束时间的日期(早于开始时间的日期)
 function disabledEndDate(current: any) {
-  if (!updateTimeStart.value) return false;
-  return current && current < dayjs(updateTimeStart.value);
+  if (!sendTimeStart.value) return false;
+  return current && current < dayjs(sendTimeStart.value);
 }
 defineExpose({
   send: sendRefresh,
@@ -248,22 +280,37 @@ defineExpose({
       <vxe-form v-bind="searchFormProps" v-on="searchFormEmits">
         <template #createTimes>
           <div class="date-range-container">
-            <a-date-picker v-model:value="updateTimeStart" placeholder="请选择开始时间" style="flex: 1" :show-time="{ format: 'HH:mm' }" />
+            <a-date-picker
+              v-model:value="sendTimeStart"
+              placeholder="请选择开始时间"
+              style="flex: 1"
+              format="YYYY-MM-DD HH:mm"
+              :show-time="{ format: 'HH:mm' }"
+            />
             <span class="date-separator">至</span>
-            <a-date-picker v-model:value="updateTimeEnd" placeholder="请选择结束时间" style="flex: 1" :disabledDate="disabledEndDate" :show-time="{ format: 'HH:mm' }" />
+            <a-date-picker
+              v-model:value="sendTimeEnd"
+              placeholder="请选择结束时间"
+              style="flex: 1"
+              format="YYYY-MM-DD HH:mm"
+              :disabledDate="disabledEndDate"
+              :show-time="{ format: 'HH:mm' }"
+            />
           </div>
         </template>
       </vxe-form>
     </header>
     <main class="flex-auto overflow-hidden">
       <vxe-grid ref="gridRef" v-bind="gridOptions" v-on="gridEvents" :loading="loading">
+        <template #pushTypeCell="{ row }">
+          {{ row.pushType === '0' ? '站内' : '企业微信' }}
+        </template>
         <template #conditioningProgramSupplierNameCell="{ row }">
           {{ row.conditioningProgramSupplierName == '0' ? '启用' : '禁用' }}
         </template>
         <template #toolbar-extra>
           <vxe-button style="margin-right: 12px" icon="vxe-icon-repeat" circle @click="refresh(page)"></vxe-button>
         </template>
-
       </vxe-grid>
     </main>
     <footer class="flex-none">

+ 46 - 205
src/service/NotifyManageRecord.vue

@@ -1,39 +1,48 @@
 <script setup lang="ts">
-import type { SystemItemModel, SystemIteQuery } from '@/model/care.model';
-import ServiceDetail from './ServiceDetail.vue';
-import AddItems from './AddItems.vue';
-import seeHealthEvaluation from './seeHealthEvaluation.vue';
-import HealthEvaluation from './HealthEvaluation.vue';
+import type { NotifyRecordModel, NotifyRecordQuery } from '@/model/notify.model';
 import dayjs from 'dayjs';
 // 接口数据
 
-import { pageConfirmedCpMethod, deleteConfirmedCpMethod } from '@/request/api/care.api';
-import { usePagination, useRequest } from 'alova/client';
-import { notification } from 'ant-design-vue';
+import { getNotifyRecordMethod } from '@/request/api/notify.api';
+import { usePagination } from 'alova/client';
 
 import { type VxeFormListeners, type VxeFormProps, type VxeGridInstance, type VxeGridListeners, type VxeGridProps, VxeUI } from 'vxe-pc-ui';
 
-const model = shallowRef<SystemIteQuery>();
-const searchFormProps = reactive<VxeFormProps<SystemIteQuery>>({
+const model = shallowRef<NotifyRecordQuery>();
+const searchFormProps = reactive<VxeFormProps<NotifyRecordQuery>>({
   titleWidth: 100,
   titleAlign: 'right',
   titleColon: true,
-  data: { types: ['1'] as any, status: '0' as any },
+  data: {},
   items: [
-  {
-      field: 'name',
+    {
+      field: 'noticeName',
       title: '名称',
       span: 6,
       itemRender: { name: 'VxeInput', props: { placeholder: '请输入通知名称' } },
     },
     {
       field: 'sendTime',
-      title: '发送时间',
+      title: '发送日期',
       span: 6,
       slots: {
         default: 'createTimes',
       },
     },
+    {
+      field: 'isRead',
+      title: '查看状态',
+      span: 6,
+      itemRender: {
+        name: 'VxeRadioGroup',
+        props: {
+          options: [
+            { label: '已查看', value: 'Y' },
+            { label: '未查看', value: 'N' },
+          ],
+        },
+      },
+    },
     {
       span: 6,
       itemRender: {
@@ -41,48 +50,29 @@ const searchFormProps = reactive<VxeFormProps<SystemIteQuery>>({
         options: [
           { name: 'submits', type: 'submit', content: '查询', status: 'primary' },
           { name: 'reset', type: 'reset', content: '重置', status: 'warning' },
-          // { name: 'add', content: '新增', status: 'primary' },
         ],
-        events: {
-          click(slotParams: any, { name }: any) {
-            if (name === 'add') {
-              // 新增
-              editConfirmed();
-            }
-          },
-        },
       },
     },
-   
   ],
 });
-const searchFormEmits: VxeFormListeners<SystemIteQuery> = {
+const searchFormEmits: VxeFormListeners<NotifyRecordQuery> = {
   // 查询随访计划
   submit({ data }) {
+    data.sendTimeStart = sendTimeStart.value ? dayjs(sendTimeStart.value).format('YYYY-MM-DD HH:mm') : '';
+    data.sendTimeEnd = sendTimeEnd.value ? dayjs(sendTimeEnd.value).format('YYYY-MM-DD HH:mm') : '';
     onSearch(data);
   },
-  
+
   // 重置
   reset({ data }) {
     onSearch(data);
   },
 };
-function onSearch(data: SystemIteQuery) {
-  const types: string[] = (data as any).types?.length ? (data as any).types : ['1'];
-  const status: string = (data as any).status ?? '0';
-  const isForWrap: SystemIteQuery['isForWrap'] = types.includes('1') ? 'Y' : null;
-  const isForInfer: SystemIteQuery['isForInfer'] = types.includes('2') ? 'Y' : null;
-
-  model.value = { ...data, types: [...types] as any, status: status as any, isForWrap, isForInfer };
-  nextTick(() => {
-    (searchFormProps.data as any)!.types = [...types];
-    (searchFormProps.data as any)!.status = status;
-    (searchFormProps.data as any)!.isForInfer = isForInfer;
-    (searchFormProps.data as any)!.isForWrap = isForWrap;
-  })
+function onSearch(data: NotifyRecordQuery) {
+  model.value = { ...data };
 }
-const gridRef = ref<VxeGridInstance<SystemItemModel>>();
-const gridOptions = reactive<VxeGridProps<SystemItemModel>>({
+const gridRef = ref<VxeGridInstance<NotifyRecordModel>>();
+const gridOptions = reactive<VxeGridProps<NotifyRecordModel>>({
   id: 'tag-list',
   border: true,
   showOverflow: true,
@@ -94,7 +84,6 @@ const gridOptions = reactive<VxeGridProps<SystemItemModel>>({
     custom: true,
     zoom: true,
     slots: {
-      // buttons: 'handle',
       tools: 'toolbar-extra',
     },
   },
@@ -106,13 +95,12 @@ const gridOptions = reactive<VxeGridProps<SystemItemModel>>({
   },
   columns: [
     { type: 'seq', width: 70, fixed: 'left' },
-    { field: 'name', title: '名称' },
-    { field: 'conditioningProgramType', title: '用户ID' },
-    { field: 'cpFixedPricingRule.unitPrice', title: '用户姓名', slots: { default: 'unitPriceCell' } },
-    { field: 'cpFixedPricingRule.pricingUnit', title: '手机号码', slots: { default: 'pricingUnitCell' } },
-    { field: 'conditioningProgramSupplierName', title: '发送时间' },
-    { field: 'conditioningProgramSupplierName', title: '查看状态' },
-  
+    { field: 'noticeName', title: '名称' },
+    { field: 'patientName', title: '用户ID' },
+    { field: 'patientName', title: '用户姓名' },
+    { field: 'phone', title: '手机号码' },
+    { field: 'sendTime', title: '发送时间' },
+    { field: 'isRead', title: '发送状态', slots: { default: 'isReadCell' } },
   ],
   data: [],
 });
@@ -128,7 +116,7 @@ const {
   refresh,
   remove,
   send: sendRefresh,
-} = usePagination((page, size) => pageConfirmedCpMethod(page, size, model.value), {
+} = usePagination((page, size) => getNotifyRecordMethod(page, size, model.value), {
   initialData: { data: [], total: 0 },
   initialPage: 1,
   initialPageSize: 100,
@@ -141,130 +129,13 @@ onSuccess((res: any) => {
 onMounted(() => {
   onSearch(toRaw(searchFormProps.data) as any);
 });
-
-function deleteConfirmed(model: SystemItemModel, index: number) {
-  const { name } = model;
-  VxeUI.modal.confirm({
-    title: `删除项目`,
-    content: `确认要删除 ${name} 项目吗?`,
-    showClose: false,
-    onConfirm() {
-      deleteConfirmedCpMethod(model).then(() => {
-        notification.success({
-          message: `删除项目: ${name}`,
-          description: '操作成功',
-        });
-        refresh(page.value);
-      });
-    },
-  });
-}
-
-function editConfirmed(model?: SystemItemModel, index?: number) {
-  const addType = `itemsList`;
-  if (model?.name === '健康咨询' || model?.name === '健康评估') {
-    VxeUI.modal.open({
-      title: model?.conditioningProgramType ?? '项目',
-      height: 400,
-      width: 750,
-      id: `health-consultation-modal`,
-      remember: true,
-      storage: true,
-      slots: {
-        default() {
-          return h(HealthEvaluation, <any>{
-            data: {
-              ...model,
-              addType,
-            },
-            onSubmit(data: SystemItemModel) {
-              refresh(page.value);
-              VxeUI.modal.close(`health-consultation-modal`);
-            },
-          });
-        },
-      },
-    });
-  } else {
-    VxeUI.modal.open({
-      title: model?.id ? `编辑项目` : `新增项目`,
-      height: 800,
-      width: 850,
-      // position: {
-      //   top: Math.min(100, window.innerHeight * 0.1),
-      // },
-      escClosable: true,
-      destroyOnClose: true,
-      id: `add-items-modal`,
-      remember: true,
-      storage: true,
-      slots: {
-        default() {
-          return h(AddItems, <any>{
-            data: { ...model, addType },
-            onSubmit(data: SystemItemModel) {
-              refresh(page.value);
-              VxeUI.modal.close(`add-items-modal`);
-            },
-          });
-        },
-      },
-    });
-  }
-}
-
-function seeDetail(model?: SystemItemModel, index?: number) {
-  if (model?.name === '健康咨询' || model?.name === '健康评估') {
-    VxeUI.modal.open({
-      title: model?.conditioningProgramType,
-      height: 500,
-      width: 750,
-      id: `see-health-evaluation-modal`,
-      remember: true,
-      storage: true,
-      slots: {
-        default() {
-          return h(seeHealthEvaluation, <any>{
-            data: model,
-          });
-        },
-      },
-    });
-  } else {
-    const addType = 'itemsList';
-    VxeUI.modal.open({
-      title: '查看',
-      height: 800,
-      width: 950,
-      escClosable: true,
-      destroyOnClose: true,
-      id: `service-detail-modal`,
-      remember: true,
-      storage: true,
-      slots: {
-        default() {
-          return h(ServiceDetail, <any>{
-            data: {
-              ...model,
-              addType,
-            },
-            onSubmit(data: SystemItemModel) {
-              refresh(page.value);
-              VxeUI.modal.close(`service-detail-modal`);
-            },
-          });
-        },
-      },
-    });
-  }
-}
 // 日期验证
-const updateTimeStart = ref<string>('');
-const updateTimeEnd = ref<string>('');
+const sendTimeStart = ref<string>('');
+const sendTimeEnd = ref<string>('');
 // 禁用结束时间的日期(早于开始时间的日期)
 function disabledEndDate(current: any) {
-  if (!updateTimeStart.value) return false;
-  return current && current < dayjs(updateTimeStart.value);
+  if (!sendTimeStart.value) return false;
+  return current && current < dayjs(sendTimeStart.value);
 }
 defineExpose({
   send: sendRefresh,
@@ -276,47 +147,17 @@ defineExpose({
       <vxe-form v-bind="searchFormProps" v-on="searchFormEmits">
         <template #createTimes>
           <div class="date-range-container">
-            <a-date-picker v-model:value="updateTimeStart" placeholder="请选择开始时间" style="flex: 1" :show-time="{ format: 'HH:mm' }" />
+            <a-date-picker v-model:value="sendTimeStart" placeholder="请选择开始日期" style="flex: 1" :show-time="{ format: 'HH:mm' }" />
             <span class="date-separator">至</span>
-            <a-date-picker v-model:value="updateTimeEnd" placeholder="请选择结束时间" style="flex: 1" :disabledDate="disabledEndDate" :show-time="{ format: 'HH:mm' }" />
+            <a-date-picker v-model:value="sendTimeEnd" placeholder="请选择结束日期" style="flex: 1" :disabledDate="disabledEndDate" :show-time="{ format: 'HH:mm' }" />
           </div>
         </template>
       </vxe-form>
     </header>
     <main class="flex-auto overflow-hidden">
       <vxe-grid ref="gridRef" v-bind="gridOptions" v-on="gridEvents" :loading="loading">
-        <template #isForWrapCell="{ row }">
-          {{
-            (() => {
-              const isWrap = row.isForWrap === 'Y';
-              const isInfer = row.isForInfer === 'Y';
-              if (isWrap && isInfer) {
-                return '服务包项目;调理方案项目';
-              } else if (isWrap) {
-                return '服务包项目';
-              } else if (isInfer) {
-                return '调理方案项目';
-              } else {
-                return '';
-              }
-            })()
-          }}
-        </template>
-        <template #pricingUnitCell="{ row }">
-          {{ row.cpFixedPricingRule?.pricingUnit ? row.cpFixedPricingRule?.pricingUnit : '次' }}
-        </template>
-        <template #unitPriceCell="{ row }">
-          {{ row.pricingType === '1' ? `` : row.cpFixedPricingRule?.unitPrice }}
-        </template>
-        <template #convertDoseCell="{ row }">
-          {{
-            row.pricingType === '1'
-              ? `当"穴位/经络/部位 ≤${row?.cpDynamicPricingRule ? row?.cpDynamicPricingRule[1]?.max || 0 : 0}个时,
-          单价为${row?.cpDynamicPricingRule ? row?.cpDynamicPricingRule[0]?.price || 0 : 0}元,
-          当"穴位/经络/部位 >${row?.cpDynamicPricingRule ? row?.cpDynamicPricingRule[1]?.max || 0 : 0}个时,
-          单价为${row?.cpDynamicPricingRule ? row?.cpDynamicPricingRule[1]?.price || 0 : 0}元`
-              : ''
-          }}
+        <template #isReadCell="{ row }">
+          <a-tag :color="row.isRead == 'Y' ? 'green' : 'red'">{{ row.isRead == 'Y' ? '已查看' : '未查看' }}</a-tag>
         </template>
         <template #toolbar-extra>
           <vxe-button style="margin-right: 12px" icon="vxe-icon-repeat" circle @click="refresh(page)"></vxe-button>