Explorar el Código

添加入住办理的审核组件

chenjun hace 1 mes
padre
commit
baa32b0eeb

+ 2 - 2
src/views/elderly/contracts/ContractManageForm.vue

@@ -958,9 +958,9 @@ const renderAttachmentContent = (key: string) => {
   /* 3. A4 页:保留屏幕内边距(10mm 14mm),由 @page margin 控制物理边距
      各组件打印样式已统一为 width:100%,不会再溢出 */
   .print-container .a4-page {
-    width: 100% !important;
+    width: 210mm !important;
     margin: 0 !important;
-    padding: 10mm 14mm !important;
+    padding: 0 !important;
     background: #fff !important;
     box-shadow: none !important;
     box-sizing: border-box !important;

+ 14 - 11
src/views/elderly/contracts/components/AttachmentPage.vue

@@ -24,16 +24,9 @@ const hasData = computed(() => {
 <template>
   <div class="attachment-page" :class="{ 'is-filled': hasData }">
     <div class="attachment-header">
-      <div class="attachment-title">
-        附件{{ attachmentNo }}:{{ title }}
-      </div>
+      <div class="attachment-title"> 附件{{ attachmentNo }}:{{ title }} </div>
       <div v-if="!isTextMode" class="attachment-actions">
-        <el-button
-          type="primary"
-          size="small"
-          plain
-          @click="emit('select')"
-        >
+        <el-button type="primary" size="small" plain @click="emit('select')">
           从列表中选择
         </el-button>
         <el-button
@@ -106,6 +99,7 @@ const hasData = computed(() => {
 
 .attachment-body {
   padding: 4px 0;
+
 }
 
 .attachment-empty {
@@ -114,17 +108,26 @@ const hasData = computed(() => {
 
 @media print {
   .attachment-page {
-    width: 100% !important;
+    width: 210mm !important;
     min-height: auto !important;
     height: auto !important;
     margin: 0 !important;
-    padding: 10mm 0mm !important;
+    padding: 10mm 0 !important;
     box-shadow: none !important;
     box-sizing: border-box !important;
     page-break-after: always;
     break-after: page;
   }
 
+  .attachment-content {
+    width: 210mm !important;
+  }
+
+  .attachment-body {
+    width: 210mm !important;
+
+  }
+
   /* 保留内边距,避免内容溢出可打印区域;不要在这里清 0 */
   .attachment-page.is-filled {
     background: transparent !important;

+ 1 - 1
src/views/elderly/contracts/components/ChangeConfirmationFormBody.vue

@@ -416,7 +416,7 @@ defineExpose({ form })
 
   @media print {
     width: 210mm !important;
-    max-width: 100%;
+
     padding: 0;
     border: none;
     margin: 0;

+ 24 - 28
src/views/elderly/contracts/components/CheckInInformationBody.vue

@@ -1,14 +1,21 @@
 <script setup lang="ts">
-const props = withDefaults(defineProps<{
-  isTextMode?: boolean
-}>(), {
-  isTextMode: false
-})
+const props = withDefaults(
+  defineProps<{
+    isTextMode?: boolean
+  }>(),
+  {
+    isTextMode: false
+  }
+)
 </script>
 
 <template>
   <div class="check-in-information">
-    <div class="attachment-no" style="font-size: 14px;width: 100%;text-align: left;margin-top: 16px;">附件3</div>
+    <div
+      class="attachment-no"
+      style="font-size: 14px; width: 100%; text-align: left; margin-top: 16px"
+      >附件3</div
+    >
     <div class="attachment-title">《入住须知》</div>
 
     <div class="content">
@@ -27,20 +34,15 @@ const props = withDefaults(defineProps<{
         <li>
           洗漱用品:请依据个人需求自行准备牙刷、牙膏、口杯、须刨、香皂、洗发水、淋浴露等物品。
         </li>
-        <li>
-          日用品包含:水杯、润肤露、爽身粉、洗洁精、梳子、纸巾以及浴巾。
-        </li>
-        <li>
-          衣物标签:入住之前,请家属完成“长者名字”的衣物标签缝制工作。
-        </li>
+        <li> 日用品包含:水杯、润肤露、爽身粉、洗洁精、梳子、纸巾以及浴巾。 </li>
+        <li> 衣物标签:入住之前,请家属完成“长者名字”的衣物标签缝制工作。 </li>
         <li>
           药品:对于生活无法自理的长者,若自带药品,在附有医生医嘱的情况下,可交由我院工作人员。我院会依据长者的护理级别及医嘱,按时提醒长者服用药品。
         </li>
+        <li> 其他辅助器具:助听器、轮椅、拐杖、助行器、气垫床、坐便椅等。 </li>
         <li>
-          其他辅助器具:助听器、轮椅、拐杖、助行器、气垫床、坐便椅等。
-        </li>
-        <li>
-          请勿将贵重物品及钱财带入养老院,院方不承担相关保管责任,长者可预留少量备用现金(金额在 500 元以下)。
+          请勿将贵重物品及钱财带入养老院,院方不承担相关保管责任,长者可预留少量备用现金(金额在 500
+          元以下)。
         </li>
       </ol>
 
@@ -50,14 +52,13 @@ const props = withDefaults(defineProps<{
           本养老服务中心已为入住长者配备房间内必备的家电及家具用品,若长者需自行携带相关物品,须经本院同意后方可带入。
         </li>
         <li>
-          应遵守各项规章制度,服从管理人员的管理。进出院区以及亲属进行探访时,需在前台进行登记。接待来访的时间为:9:00-11:00 时,14:30-17:00 时。
+          应遵守各项规章制度,服从管理人员的管理。进出院区以及亲属进行探访时,需在前台进行登记。接待来访的时间为:9:00-11:00
+          时,14:30-17:00 时。
         </li>
         <li>
           原则上,禁止携带各类宠物入住。严禁携带对长者安全存在影响的物品(如具有刺激性、怪气味的物品,锐器,易燃、易爆及挥发性物品)进入院区。
         </li>
-        <li>
-          请妥善爱护房间及院区内的公共财物,若有损坏,需按照物品价值进行赔偿。
-        </li>
+        <li> 请妥善爱护房间及院区内的公共财物,若有损坏,需按照物品价值进行赔偿。 </li>
         <li>
           若长者需要持有房间钥匙,请妥善保管。若钥匙遗失,需缴纳重新配制钥匙的费用。离开房间时,请关闭好门窗及水电。
         </li>
@@ -68,9 +69,7 @@ const props = withDefaults(defineProps<{
         <li>
           入住长者的户口本(户主页与本人页)以及身份证(正面与反面)复印件,《托养服务合同》丙方(监护人)的户口本(户主页与本人页)以及身份证(正面与反面)复印件。
         </li>
-        <li>
-          入住长者近期免冠一寸彩色照片三张。
-        </li>
+        <li> 入住长者近期免冠一寸彩色照片三张。 </li>
         <li>
           入住时需提交长者在三个月内由二级或以上医院出具的体检报告,检查项目包括但不限于详见以下《体验项目单》:
         </li>
@@ -117,9 +116,7 @@ const props = withDefaults(defineProps<{
       </table>
 
       <h3>四、本《入住须知》最终解释和修订权归本公司所有。</h3>
-      <p class="indent">
-        本单位将竭诚为您提供服务,若有需求,请与前台联系。
-      </p>
+      <p class="indent"> 本单位将竭诚为您提供服务,若有需求,请与前台联系。 </p>
     </div>
   </div>
 </template>
@@ -138,8 +135,7 @@ const props = withDefaults(defineProps<{
 
   /* 打印模式优化 */
   @media print {
-    width: 210mm !important;
-    max-width: 210mm;
+    width: 100% !important;
     padding: 0;
     border: none;
     margin: 0;

+ 1 - 1
src/views/elderly/contracts/components/CheckInRegistrationBody.vue

@@ -915,7 +915,7 @@ const onInlineInput = (key: keyof CheckInRegistrationForm, e: Event) => {
   /* 打印模式优化:A4纸尺寸 */
   @media print {
     width: 210mm !important;
-    max-width: 210mm;
+
     padding: 0;
     border: none;
     margin: 0;

+ 50 - 12
src/views/elderly/contracts/components/ContractBody.vue

@@ -182,7 +182,11 @@ const contractForm = reactive({
   partyBSign: '',
   partyBDate: '',
   partyCSign: '',
-  partyCDate: ''
+  partyCDate: '',
+  // 费用明细列表
+  monthlyExpenses: [] as any[],
+  oneTimeExpenses: [] as any[],
+  stageExpenses: [] as any[]
 })
 
 const parseIdCardBirth = (idCard: string): string => {
@@ -197,6 +201,27 @@ const parseIdCardBirth = (idCard: string): string => {
   return ''
 }
 
+const parseChineseDate = (dateStr: string): Date | null => {
+  if (!dateStr) return null
+  const match = dateStr.match(/^(\d{4})年(\d{2})月(\d{2})日$/)
+  if (!match) return null
+  const year = Number(match[1])
+  const month = Number(match[2]) - 1
+  const day = Number(match[3])
+  return new Date(year, month, day)
+}
+
+const calcMonthsBetween = (startStr: string, endStr: string): number | null => {
+  const start = parseChineseDate(startStr)
+  const end = parseChineseDate(endStr)
+  if (!start || !end || end < start) return null
+  let months = (end.getFullYear() - start.getFullYear()) * 12 + (end.getMonth() - start.getMonth())
+  if (end.getDate() < start.getDate()) {
+    months--
+  }
+  return Math.max(0, months)
+}
+
 const isIdCardType = (papersType: any): boolean => {
   const label = getDictLabel(DICT_TYPE.PAPERS_TYPE, papersType)
   return !!label && (label.includes('身份证') || label.includes('居民身份'))
@@ -286,10 +311,6 @@ const fillContractForm = (data: any) => {
 
   contractForm.nurseLevelName = data.nurseLevelName || ''
 
-  if (contract.contractTerm) {
-    contractForm.contractMonths = String(contract.contractTerm)
-  }
-
   if (data.checkInTime) {
     contractForm.startDate = formatBackendDateTime(data.checkInTime, 'YYYY年MM月DD日')
   }
@@ -340,6 +361,10 @@ const fillContractForm = (data: any) => {
     contractForm.roomType = data.bedType
   }
 
+  contractForm.monthlyExpenses = data.monthlyExpenses || []
+  contractForm.oneTimeExpenses = data.oneTimeExpenses || []
+  contractForm.stageExpenses = data.stageExpenses || []
+
   const oneTime = data.oneTimeExpenses || []
   const deposit = findItem(oneTime, '押金')
   if (deposit) {
@@ -421,10 +446,11 @@ const fetchContractData = async (row: any) => {
   loading.value = true
   try {
     const res = await getElderlyContractById(props.recordRow.id)
-    console.log('合同信息接口:', res)
+    //console.log('合同信息接口:', res)
+    //console.log('合同信息接口:', JSON.parse(res.contractInfo))
     if (res) {
       contractFormUpdate.value = res
-      console.log('AA', contractFormUpdate.value.contractInfo)
+     // console.log('接口获取合同信息:', contractFormUpdate.value.contractInfo)
       contractFormUpdate.value.id = props.recordRow.id
       if (contractFormUpdate.value.contractInfo) {
         try {
@@ -439,13 +465,13 @@ const fetchContractData = async (row: any) => {
         const detailPromises: Promise<any>[] = []
         Object.keys(attachmentConfig).forEach((key) => {
           const config = attachmentConfig[key]
-          console.log('config', config, key)
+          //console.log('config', config, key)
           let id = undefined
           try {
              id = contractForm.attachments[key].id
           }catch (e) {
           }
-          console.log('id', id)
+          //console.log('id', id)
           if (id) {
             detailPromises.push(
               config
@@ -593,9 +619,9 @@ const displayDate = computed(() => {
 })
 
 // 费用相关动态列表
-const monthlyExpensesList = computed(() => detailData.value?.monthlyExpenses || [])
-const oneTimeExpensesList = computed(() => detailData.value?.oneTimeExpenses || [])
-const stageExpensesList = computed(() => detailData.value?.stageExpenses || [])
+const monthlyExpensesList = computed(() => contractForm.monthlyExpenses || [])
+const oneTimeExpensesList = computed(() => contractForm.oneTimeExpenses || [])
+const stageExpensesList = computed(() => contractForm.stageExpenses || [])
 
 const formatExpenseAmount = (val: any): string => {
   if (val == null || val === '') return ''
@@ -768,6 +794,18 @@ watch(
   },
   { deep: true }
 )
+
+// 合同期限根据起止日期自动计算
+watch(
+  [() => contractForm.startDate, () => contractForm.endDate],
+  ([start, end]) => {
+    const months = calcMonthsBetween(start, end)
+    if (months !== null) {
+      contractForm.contractMonths = String(months)
+    }
+  },
+  { immediate: true }
+)
 </script>
 
 <template>

+ 1 - 1
src/views/elderly/contracts/components/DelegationAgentConfirmationBody.vue

@@ -265,7 +265,7 @@ defineExpose({ form })
 
   @media print {
     width: 210mm !important;
-    max-width: 100%;
+
     padding: 0;
     border: none;
     margin: 0;

+ 0 - 1
src/views/elderly/contracts/components/DocumentAttachmentBody.vue

@@ -166,7 +166,6 @@ defineExpose({ form })
 
   @media print {
     width: 210mm !important;
-    max-width: 100%;
     padding: 0;
     border: none;
     margin: 0;

+ 1 - 1
src/views/elderly/contracts/components/ElderSafetyCommitmentBody.vue

@@ -132,7 +132,7 @@ defineExpose({ form })
   /* 打印模式优化 */
   @media print {
     width: 210mm !important;
-    max-width: 100%;
+
     padding: 0;
     border: none;
     margin: 0;

+ 0 - 1
src/views/elderly/contracts/components/ExplanationExaminationReportBody.vue

@@ -161,7 +161,6 @@ defineExpose({ form })
 
   @media print {
     width: 210mm !important;
-    max-width: 100%;
     padding: 0;
     border: none;
     margin: 0;

+ 1 - 1
src/views/elderly/contracts/components/FirstServiceProjectConfirmationBody.vue

@@ -498,7 +498,7 @@ defineExpose({ form })
 
   @media print {
     width: 210mm !important;
-    max-width: 100%;
+
     padding: 0;
     border: none;
     margin: 0;

+ 1 - 1
src/views/elderly/contracts/components/OutingConfirmationBody.vue

@@ -215,7 +215,7 @@ defineExpose({ form })
   /* 打印模式优化 */
   @media print {
     width: 210mm !important;
-    max-width: 210mm;
+
     padding: 0;
     border: none;
     margin: 0;

+ 1 - 1
src/views/elderly/contracts/components/PowerOfAttorneyBody.vue

@@ -220,7 +220,7 @@ defineExpose({ form })
 
   @media print {
     width: 210mm !important;
-    max-width: 100%;
+
     padding: 0;
     border: none;
     margin: 0;

+ 1 - 1
src/views/elderly/contracts/components/RiskDisclosureBody.vue

@@ -281,7 +281,7 @@ defineExpose({ form })
   @media print {
     &.a4-page {
       width: 100% !important;
-      max-width: 100%;
+
       padding: 0 !important;
       border: none;
       margin: 0 !important;

+ 1 - 1
src/views/elderly/contracts/components/RoomFacilitiesEquipmentBody.vue

@@ -315,7 +315,7 @@ defineExpose({ form })
 
   @media print {
     width: 210mm !important;
-    max-width: 100%;
+
     padding: 0;
     border: none;
     margin: 0;

+ 1 - 1
src/views/elderly/contracts/components/ServiceChargingStandardsBody.vue

@@ -152,7 +152,7 @@ defineExpose({ form })
 
   @media print {
     width: 210mm !important;
-    max-width: 100%;
+
     padding: 0;
     border: none;
     margin: 0;

+ 1 - 1
src/views/elderly/contracts/components/SignatureProxyApplicationBody.vue

@@ -162,7 +162,7 @@ defineExpose({ form })
 
   @media print {
     width: 210mm !important;
-    max-width: 100%;
+
     padding: 0;
     border: none;
     margin: 0;

+ 154 - 3
src/views/elderly/elder/family-appointment/time-setting/index.vue

@@ -63,7 +63,7 @@
               <el-form-item label="提前预约天数" prop="advanceDays">
                 <el-input-number
                   v-model="formData.advanceDays"
-                  :min="0"
+                  :min="1"
                   :max="30"
                   style="width: 100%"
                   :disabled="isDisabled"
@@ -136,6 +136,14 @@
                         自定义
                       </el-button>
                     </div>
+                    <el-switch
+                      v-model="formData.appointmentEnabled"
+                      active-text="启用"
+                      size="small"
+                      style="margin-right: 1vw"
+                      inactive-text="关闭"
+                    />
+
                     <el-button
                       type="danger"
                       link
@@ -669,6 +677,20 @@ const isOverlap = (item1: AppointmentItem, item2: AppointmentItem): boolean => {
   return s1 < e2 && s2 < e1
 }
 
+const getDateDayOfMonth = (dateStr: string): number => {
+  const parts = dateStr.split('-')
+  if (parts.length !== 3) return -1
+  return Number(parts[2])
+}
+
+const getDateWeekDay = (dateStr: string): number => {
+  const parts = dateStr.split('-').map(Number)
+  if (parts.length !== 3) return -1
+  const date = new Date(parts[0], parts[1] - 1, parts[2])
+  const day = date.getDay()
+  return day === 0 ? 7 : day
+}
+
 const checkTimeOverlap = (group: AppointmentGroup, changedIndex: number) => {
   const changedItem = group.items[changedIndex]
   if (!changedItem.startTime || !changedItem.endTime) return
@@ -698,7 +720,7 @@ const addAppointmentGroup = () => {
       {
         id: itemIdCounter,
         startTime: '08:00',
-        endTime: '12:00',
+        endTime: '09:00',
         limitCount: 100
       }
     ]
@@ -861,10 +883,21 @@ const removePersonLimit = (index: number) => {
   personLimits.value.splice(index, 1)
 }
 
+const validateAdvanceDays = (_rule: any, value: any, callback: any) => {
+  if (!value || value < 1) {
+    callback(new Error('提前预约天数最少为1'))
+  } else {
+    callback()
+  }
+}
+
 const formRules: FormRules = reactive({
   ruleType: [{ required: true, message: '请选择放号规则', trigger: 'change' }],
   startDay: [{ required: true, message: '请选择开始预约日', trigger: 'change' }],
-  advanceDays: [{ required: true, message: '请输入提前预约天数', trigger: 'change' }]
+  advanceDays: [
+    { required: true, message: '请输入提前预约天数', trigger: 'change' },
+    { validator: validateAdvanceDays, trigger: 'change' }
+  ]
 })
 
 const changeTen = (i: any[]) => {
@@ -963,15 +996,131 @@ const getSetting = async () => {
   }
 }
 
+const validateGroups = (): string | null => {
+  if (appointmentGroups.value.length === 0) {
+    return '请至少添加一个预约组'
+  }
+
+  const dailyGroups = appointmentGroups.value.filter((g) => g.cycleType === 'daily')
+  if (dailyGroups.length > 0 && appointmentGroups.value.length > 1) {
+    return '选择每天时,只能有一个预约组'
+  }
+
+  const weeklyGroups = appointmentGroups.value.filter((g) => g.cycleType === 'weekly')
+  const monthlyGroups = appointmentGroups.value.filter((g) => g.cycleType === 'monthly')
+
+  if (weeklyGroups.length > 0 && monthlyGroups.length > 0) {
+    return '每周和每月预约组不能同时存在'
+  }
+
+  if (weeklyGroups.length > 1) {
+    const usedDays = new Set<number>()
+    for (const group of weeklyGroups) {
+      for (const day of group.weeklyDays) {
+        if (usedDays.has(day)) {
+          return '不同每周预约组存在重复的星期'
+        }
+        usedDays.add(day)
+      }
+    }
+  }
+
+  if (monthlyGroups.length > 1) {
+    const usedDays = new Set<number>()
+    for (const group of monthlyGroups) {
+      for (const day of group.monthlyDays) {
+        if (usedDays.has(day)) {
+          return '不同每月预约组存在重复的日期'
+        }
+        usedDays.add(day)
+      }
+    }
+  }
+
+  const customGroups = appointmentGroups.value.filter((g) => g.cycleType === 'custom')
+  if (customGroups.length > 1) {
+    const usedDates = new Set<string>()
+    for (const group of customGroups) {
+      for (const date of group.customDates) {
+        if (usedDates.has(date)) {
+          return '不同自定义预约组存在重复的日期'
+        }
+        usedDates.add(date)
+      }
+    }
+  }
+
+  if (monthlyGroups.length > 0 && customGroups.length > 0) {
+    const usedDays = new Set<number>()
+    for (const group of monthlyGroups) {
+      for (const day of group.monthlyDays) {
+        usedDays.add(day)
+      }
+    }
+    for (const group of customGroups) {
+      for (const date of group.customDates) {
+        const day = getDateDayOfMonth(date)
+        if (day > 0 && usedDays.has(day)) {
+          return `自定义日期 ${date} 与每月预约的 ${day}号冲突`
+        }
+      }
+    }
+  }
+
+  if (weeklyGroups.length > 0 && customGroups.length > 0) {
+    const usedDays = new Set<number>()
+    for (const group of weeklyGroups) {
+      for (const day of group.weeklyDays) {
+        usedDays.add(day)
+      }
+    }
+    for (const group of customGroups) {
+      for (const date of group.customDates) {
+        const day = getDateWeekDay(date)
+        if (day > 0 && usedDays.has(day)) {
+          return `自定义日期 ${date} 与每周预约冲突`
+        }
+      }
+    }
+  }
+
+  for (const group of appointmentGroups.value) {
+    if (group.cycleType === 'weekly' && group.weeklyDays.length === 0) {
+      return '存在未设置每周可预约日期的预约组'
+    }
+    if (group.cycleType === 'monthly' && group.monthlyDays.length === 0) {
+      return '存在未设置每月可预约日期的预约组'
+    }
+    if (group.cycleType === 'custom' && group.customDates.length === 0) {
+      return '存在未设置自定义日期的预约组'
+    }
+  }
+
+  return null
+}
+
 const handleSubmit = async () => {
   if (!formRef.value) return
   if (!organizationId) {
     message.error('请选择机构!')
     return
   }
+  if (!formData.advanceDays || formData.advanceDays < 1) {
+    message.error('提前预约天数最少为1')
+    return
+  }
+  const groupError = validateGroups()
+  if (groupError) {
+    message.error(groupError)
+    return
+  }
   for (const group of appointmentGroups.value) {
     for (let i = 0; i < group.items.length; i++) {
       const item = group.items[i]
+      if (!item.limitCount || item.limitCount <= 0) {
+        message.error('限制人数必须大于0')
+        return
+      }
       if (
         item.startTime &&
         item.endTime &&
@@ -1334,6 +1483,8 @@ onMounted(() => {
   align-items: center;
 }
 
+
+
 :deep(.date-selector-dialog) {
   .date-selector-body {
     padding: 6px 0;