FirstServiceProjectConfirmationBody.vue 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683
  1. <script setup lang="ts">
  2. import { reactive, watch } from 'vue'
  3. import FieldItem from './FieldItem.vue'
  4. import SignatureItem from './SignatureItem.vue'
  5. interface ServiceItem {
  6. selected: string[]
  7. other: string
  8. remark?: string
  9. }
  10. interface FormData {
  11. partyBName?: string
  12. gender?: string
  13. roomNo?: string
  14. bedNo?: string
  15. assessDate?: string
  16. careLevel?: string
  17. agentName?: string
  18. emergencyContact?: string
  19. year?: string
  20. month?: string
  21. day?: string
  22. signDate?: string
  23. basicServices?: Record<string, ServiceItem>
  24. lifeCare?: Record<string, any>
  25. elderlyNursing?: Record<string, any>
  26. partyBSign?: string
  27. partyCSign?: string
  28. }
  29. const props = withDefaults(defineProps<{
  30. isTextMode?: boolean
  31. modelValue?: FormData
  32. }>(), {
  33. isTextMode: false,
  34. modelValue: () => ({})
  35. })
  36. const emit = defineEmits<{
  37. (e: 'update:modelValue', val: FormData): void
  38. }>()
  39. const defaultBasic = (): ServiceItem => ({ selected: [], other: '', remark: '' })
  40. const form = reactive<FormData>({
  41. partyBName: '',
  42. gender: '',
  43. roomNo: '',
  44. bedNo: '',
  45. assessDate: '',
  46. careLevel: '',
  47. agentName: '',
  48. emergencyContact: '',
  49. year: '',
  50. month: '',
  51. day: '',
  52. signDate: '',
  53. basicServices: {
  54. consultation: defaultBasic(),
  55. mealService: defaultBasic(),
  56. deliveryService: defaultBasic(),
  57. medicalService: defaultBasic(),
  58. chronicManagement: defaultBasic(),
  59. accompanyMedical: defaultBasic(),
  60. mentalSupport: defaultBasic(),
  61. safetyProtection: defaultBasic(),
  62. recreation: defaultBasic(),
  63. education: defaultBasic(),
  64. commission: defaultBasic(),
  65. sanitation: defaultBasic(),
  66. laundry: defaultBasic(),
  67. shopping: defaultBasic(),
  68. maintenance: defaultBasic(),
  69. communication: defaultBasic(),
  70. transportation: defaultBasic(),
  71. infrastructure: defaultBasic(),
  72. },
  73. lifeCare: {
  74. personalHygiene: { selected: [], other: '' },
  75. dressing: { selected: [], other: '' },
  76. grooming: { selected: [], other: '' },
  77. dietaryCare: { selected: [], other: '' },
  78. toiletCare: { selected: [], other: '' },
  79. oralCare: { selected: [], other: '' },
  80. skinCleaning: { selected: [], other: '' },
  81. positionTransfer: { selected: [], other: '' },
  82. excretionCare: { selected: [], other: '' },
  83. skinCare: { selected: [], other: '' },
  84. other: ''
  85. },
  86. elderlyNursing: {
  87. comprehensiveAssessment: false,
  88. chronicDisease: { selected: [], other: '' },
  89. diseaseNursing: { selected: [], other: '' },
  90. nursingTech: { selected: [], other: '' },
  91. healthGuidance: false,
  92. drugManagement: false,
  93. other: '',
  94. assistMedicalNursing: { selected: [], other: '' },
  95. rehabilitation: { selected: [], other: '' },
  96. palliativeCare: { selected: [], other: '' },
  97. otherAgreed: ''
  98. },
  99. partyBSign: '',
  100. partyCSign: ''
  101. })
  102. watch(() => props.modelValue, (val) => {
  103. if (!val) return
  104. const newVal = JSON.parse(JSON.stringify(val))
  105. const mergedBasic = { ...form.basicServices }
  106. const mergedLife = { ...form.lifeCare }
  107. const mergedElderly = { ...form.elderlyNursing }
  108. if (newVal.basicServices) {
  109. Object.keys(newVal.basicServices).forEach(key => {
  110. if (mergedBasic[key] && typeof mergedBasic[key] === 'object' && !Array.isArray(mergedBasic[key])) {
  111. mergedBasic[key] = { ...mergedBasic[key], ...newVal.basicServices[key] }
  112. } else {
  113. mergedBasic[key] = newVal.basicServices[key]
  114. }
  115. })
  116. }
  117. if (newVal.lifeCare) {
  118. Object.keys(newVal.lifeCare).forEach(key => {
  119. if (mergedLife[key] && typeof mergedLife[key] === 'object' && !Array.isArray(mergedLife[key])) {
  120. mergedLife[key] = { ...mergedLife[key], ...newVal.lifeCare[key] }
  121. } else {
  122. mergedLife[key] = newVal.lifeCare[key]
  123. }
  124. })
  125. }
  126. if (newVal.elderlyNursing) {
  127. Object.keys(newVal.elderlyNursing).forEach(key => {
  128. if (mergedElderly[key] && typeof mergedElderly[key] === 'object' && !Array.isArray(mergedElderly[key])) {
  129. mergedElderly[key] = { ...mergedElderly[key], ...newVal.elderlyNursing[key] }
  130. } else {
  131. mergedElderly[key] = newVal.elderlyNursing[key]
  132. }
  133. })
  134. }
  135. Object.assign(form, newVal, {
  136. basicServices: mergedBasic,
  137. lifeCare: mergedLife,
  138. elderlyNursing: mergedElderly
  139. })
  140. if (!form.signDate && form.year && form.month && form.day) {
  141. form.signDate = `${form.year}年${form.month}月${form.day}日`
  142. } else if (form.signDate) {
  143. const m = form.signDate.match(/(\d{4})[年/\-.](\d{1,2})[月/\-.](\d{1,2})/)
  144. if (m) {
  145. form.year = m[1]
  146. form.month = m[2]
  147. form.day = m[3]
  148. }
  149. }
  150. }, { immediate: true, deep: true })
  151. const updateForm = () => {
  152. if (form.signDate) {
  153. const m = form.signDate.match(/(\d{4})[年/\-.](\d{1,2})[月/\-.](\d{1,2})/)
  154. if (m) {
  155. form.year = m[1]
  156. form.month = m[2]
  157. form.day = m[3]
  158. }
  159. }
  160. emit('update:modelValue', JSON.parse(JSON.stringify(form)))
  161. }
  162. const toggleBasic = (key: string, option: string) => {
  163. const item = form.basicServices![key]
  164. const idx = item.selected.indexOf(option)
  165. if (idx > -1) item.selected.splice(idx, 1)
  166. else item.selected.push(option)
  167. updateForm()
  168. }
  169. const toggleLife = (key: string, option: string) => {
  170. const item = form.lifeCare![key]
  171. const idx = item.selected.indexOf(option)
  172. if (idx > -1) item.selected.splice(idx, 1)
  173. else item.selected.push(option)
  174. updateForm()
  175. }
  176. const toggleElderly = (key: string, option: string) => {
  177. const item = form.elderlyNursing![key]
  178. const idx = item.selected.indexOf(option)
  179. if (idx > -1) item.selected.splice(idx, 1)
  180. else item.selected.push(option)
  181. updateForm()
  182. }
  183. const isChecked = (obj: any, option: string) => (obj?.selected || []).includes(option)
  184. const basicServices = [
  185. { name: '咨询服务', key: 'consultation', options: ['入住咨询', '法律咨询', '心理咨询', '医疗咨询', '护理咨询', '康复咨询', '教育咨询', '服务咨询'], other: '其他咨询' },
  186. { name: '膳食服务', key: 'mealService', options: ['食谱定制', '营养配餐', '食品加工与制作'], other: '其他膳食服务' },
  187. { name: '送餐服务', key: 'deliveryService', options: ['定期订餐', '按时送餐'], other: '其他送餐服务' },
  188. { name: '医疗服务', key: 'medicalService', options: ['疾病诊治', '健康指导', '预防保健、体检', '药事管理', '医疗相关风险管理及告知', '转诊转院'], other: '其他医疗服务', sub: { name: '慢病管理', key: 'chronicManagement', options: ['慢病监测', '健康咨询', '用药指导'], other: '其他慢病管理服务' } },
  189. { name: '陪同就医服务', key: 'accompanyMedical', options: ['陪同指定医院就医', '协助挂号', '协助完成各项检查', '协助取药'], other: '其他陪同就医服务' },
  190. { name: '心理/精神支持服务', key: 'mentalSupport', options: ['情绪疏导', '心理支持', '危机干预', '老年人家属心理支持服务'], other: '其他心理/精神支持服务' },
  191. { name: '安全保护服务', key: 'safetyProtection', options: ['安全风险评估', '安全教育', '提供安全设备设置'], other: '其他安全保护服务' },
  192. { name: '休闲娱乐服务', key: 'recreation', options: ['文体活动', '棋牌娱乐', '健身活动', '游艺活动', '影视观看服务', '参观游览'], other: '其他娱乐休闲服务' },
  193. { name: '教育服务', key: 'education', options: ['读书', '讲座', '老年大学'], other: '其他教育服务' },
  194. { name: '委托服务', key: 'commission', options: ['代读、代写书信', '代领物品', '代缴费用'], other: '其他委托服务' },
  195. { name: '环境卫生服务', key: 'sanitation', options: ['机构公共区域清洁', '老年人居室清洁'], other: '其他环境卫生服务', extra: ['频次', '频次'] },
  196. { name: '洗涤服务', key: 'laundry', options: ['老年人衣物洗涤', '老年人居室布草洗涤'], other: '其他洗涤服务', extra: ['频次', '频次'] },
  197. { name: '购物服务', key: 'shopping', options: ['代购日常生活物品', '陪同外出购物', '协助老年人使用网络购物'], other: '其他购物服务' },
  198. { name: '维修服务', key: 'maintenance', options: ['公共设施设备维修', '老年人居室设备维修'], other: '其他维修服务' },
  199. { name: '通信服务', key: 'communication', options: ['提供电话设备', '提供互联网上网服务'], other: '其他通信服务' },
  200. { name: '交通服务', key: 'transportation', options: ['提供交通工具运送老年人', '联系交通工具运送老年人'], other: '其他交通服务' },
  201. { name: '基础设施服务', key: 'infrastructure', options: ['提供居室内基本用电用水服务', '提供有线电视收视服务', '提供空调服务', '提供冬季取暖服务', '提供生活饮用水服务', '呼叫应答服务'], other: '其他基础设施服务' },
  202. ]
  203. const lifeCareItems = [
  204. { name: '个人清洁卫生服务', key: 'personalHygiene', options: ['洗脸', '洗手', '洗头', '洗脚', '按摩', '拍背', '协助整理个人物品', '清洁整理床铺', '更换床单位'], other: '其他个人清洁卫生服务' },
  205. { name: '穿衣服服务', key: 'dressing', options: ['更换上衣、裤子', '协助穿衣', '整理衣物'], other: '其他穿衣服务' },
  206. { name: '修饰服务', key: 'grooming', options: ['梳头', '剃须', '剪指/趾甲', '化妆'], other: '其他修饰服务' },
  207. { name: '饮食照料服务', key: 'dietaryCare', options: ['经口喂食或水', '鼻胃管喂食或水', '协助用膳'], other: '其他饮食服务' },
  208. { name: '如厕照料服务', key: 'toiletCare', options: ['定时提醒如厕', '使用便盆', '使用尿壶', '协助入厕排便、排尿'], other: '其他如厕服务' },
  209. { name: '口腔清洁服务', key: 'oralCare', options: ['刷牙', '漱口', '清洁义齿', '口腔擦拭'], other: '其他口腔清洁服务' },
  210. { name: '皮肤清洁服务', key: 'skinCleaning', options: ['淋浴', '床上擦浴', '清洗会阴'], other: '其他皮肤清洁服务' },
  211. { name: '体位转移服务', key: 'positionTransfer', options: ['进行床上体位转换', '床与轮椅转移', '床与平车转移'], other: '其他体位转移服务' },
  212. { name: '便溺照料服务', key: 'excretionCare', options: ['协助进行床上排便', '人工排便', '药物及辅助用品肛注排便', '床上排尿', '更换一次性尿垫', '更换一次性尿裤'], other: '其他便溺服务' },
  213. { name: '皮肤护理服务', key: 'skinCare', options: ['卧床老年人预防压疮', '老年人皮肤观察', '定时更换体位', '清洁皮肤', '使用预防压疮的器具'], other: '其他皮肤护理服务' },
  214. ]
  215. const elderlyNursingItems = [
  216. { name: '慢病管理服务', key: 'chronicDisease', options: ['老年人慢性非传染性疾病情况制定护理计划', '实施维持性治疗', '观察老年人症状变化、定期检测', '生活方式干预', '健康教育'], other: '其他慢病管理服务' },
  217. { name: '病症护理服务', key: 'diseaseNursing', options: ['常见病症进行观察', '按照医嘱针对护理'], other: '其他病症护理服务' },
  218. { name: '护理技术操作服务', key: 'nursingTech', options: ['清洁与舒适管理', '营养与排泄护理', '常见症状护理', '皮肤、伤口、造口护理', '气道护理', '引流护理', '生命体征监测', '急救技术', '常用标本采集', '给药治疗与护理'], other: '其他护理服务' },
  219. { name: '协助医疗护理服务', key: 'assistMedicalNursing', options: ['老年人日常生活观察', '协助或指导老年人使用辅助器具', '化验标本的收集送检'], other: '其他协助医疗护理服务' },
  220. { name: '康复服务', key: 'rehabilitation', options: ['康复评定和制定计划', '物理治疗', '作业治疗', '言语治疗', '中医康复治疗'], other: '其他康复服务' },
  221. { name: '安宁服务', key: 'palliativeCare', options: ['疼痛及其他症状护理服务', '舒适照护'], other: '其他安宁服务' },
  222. ]
  223. defineExpose({ form })
  224. </script>
  225. <template>
  226. <div class="first-service-project">
  227. <div class="doc-title">首次服务项目确认表</div>
  228. <!-- 基本信息 -->
  229. <table class="info-table">
  230. <tbody>
  231. <tr>
  232. <td class="label">乙方姓名</td>
  233. <td><FieldItem v-model="form.partyBName" :is-text-mode="isTextMode" width="100%" placeholder="" @update:model-value="updateForm" /></td>
  234. <td class="label">性别</td>
  235. <td><FieldItem v-model="form.gender" :is-text-mode="isTextMode" width="100%" placeholder="" @update:model-value="updateForm" /></td>
  236. <td class="label">房间号/床号</td>
  237. <td><FieldItem v-model="form.bedNo" :is-text-mode="isTextMode" width="100%" placeholder="" @update:model-value="updateForm" /></td>
  238. </tr>
  239. <tr>
  240. <td class="label">评估日期</td>
  241. <td><FieldItem v-model="form.assessDate" :is-text-mode="isTextMode" type="date" width="100%" placeholder="" @update:model-value="updateForm" /></td>
  242. <td class="label">照料护理等级</td>
  243. <td colspan="3"><FieldItem v-model="form.careLevel" :is-text-mode="isTextMode" width="100%" placeholder="" @update:model-value="updateForm" /></td>
  244. </tr>
  245. <tr>
  246. <td class="label">代理人姓名</td>
  247. <td><FieldItem v-model="form.agentName" :is-text-mode="isTextMode" width="100%" placeholder="" @update:model-value="updateForm" /></td>
  248. <td class="label">紧急联系人姓名</td>
  249. <td colspan="3"><FieldItem v-model="form.emergencyContact" :is-text-mode="isTextMode" width="100%" placeholder="" @update:model-value="updateForm" /></td>
  250. </tr>
  251. </tbody>
  252. </table>
  253. <!-- 基础服务 -->
  254. <table class="service-table">
  255. <thead>
  256. <tr>
  257. <th colspan="3" class="section-title">基础服务</th>
  258. </tr>
  259. <tr>
  260. <th class="col-service">服务项目</th>
  261. <th class="col-content">服务内容</th>
  262. <th class="col-remark">备注</th>
  263. </tr>
  264. </thead>
  265. <tbody>
  266. <tr v-for="svc in basicServices" :key="svc.key">
  267. <td class="service-name">{{ svc.name }}</td>
  268. <td class="service-content">
  269. <template v-if="!isTextMode">
  270. <label v-for="(opt, idx) in svc.options" :key="opt" class="chk-label">
  271. <input type="checkbox" :checked="isChecked(form.basicServices![svc.key], opt)" @change="toggleBasic(svc.key, opt)" />
  272. <span>{{ opt }}</span>
  273. <span v-if="svc.extra && svc.extra[idx]" class="extra-label">{{ svc.extra[idx] }}:<FieldItem v-model="(form.basicServices as any)[svc.key]['extra' + idx]" :is-text-mode="isTextMode" width="60px" placeholder="" @update:model-value="updateForm" /></span>
  274. </label>
  275. <div v-if="svc.other" class="other-row">
  276. <span>{{ svc.other }}:</span>
  277. <FieldItem v-model="(form.basicServices as any)[svc.key].other" :is-text-mode="isTextMode" width="200px" placeholder="" @update:model-value="updateForm" />
  278. </div>
  279. <div v-if="svc.sub" class="sub-service">
  280. <div class="sub-name">{{ svc.sub.name }}</div>
  281. <label v-for="opt in svc.sub.options" :key="opt" class="chk-label">
  282. <input type="checkbox" :checked="isChecked(form.basicServices![svc.sub.key], opt)" @change="toggleBasic(svc.sub.key, opt)" />
  283. <span>{{ opt }}</span>
  284. </label>
  285. <div v-if="svc.sub.other" class="other-row">
  286. <span>{{ svc.sub.other }}:</span>
  287. <FieldItem v-model="(form.basicServices as any)[svc.sub.key].other" :is-text-mode="isTextMode" width="200px" placeholder="" @update:model-value="updateForm" />
  288. </div>
  289. </div>
  290. </template>
  291. <template v-else>
  292. <span v-for="opt in svc.options" :key="opt" class="text-chk">
  293. {{ isChecked(form.basicServices![svc.key], opt) ? '☑' : '□' }}{{ opt }}
  294. <span v-if="svc.extra && svc.extra[svc.options.indexOf(opt)]" class="extra-text">{{ svc.extra[svc.options.indexOf(opt)] }}:{{ (form.basicServices as any)[svc.key]['extra' + svc.options.indexOf(opt)] || '____' }}</span>
  295. </span>
  296. <span v-if="(form.basicServices as any)[svc.key].other" class="text-chk">☑{{ svc.other }}:{{ (form.basicServices as any)[svc.key].other }}</span>
  297. <span v-else-if="svc.other" class="text-chk">□{{ svc.other }}:____</span>
  298. <div v-if="svc.sub" class="sub-service">
  299. <div class="sub-name">{{ svc.sub.name }}</div>
  300. <span v-for="opt in svc.sub.options" :key="opt" class="text-chk">
  301. {{ isChecked(form.basicServices![svc.sub.key], opt) ? '☑' : '□' }}{{ opt }}
  302. </span>
  303. <span v-if="(form.basicServices as any)[svc.sub.key].other" class="text-chk">☑{{ svc.sub.other }}:{{ (form.basicServices as any)[svc.sub.key].other }}</span>
  304. <span v-else-if="svc.sub.other" class="text-chk">□{{ svc.sub.other }}:____</span>
  305. </div>
  306. </template>
  307. </td>
  308. <td class="service-remark">
  309. <FieldItem v-model="(form.basicServices as any)[svc.key].remark" :is-text-mode="isTextMode" width="100%" placeholder="" @update:model-value="updateForm" />
  310. </td>
  311. </tr>
  312. </tbody>
  313. </table>
  314. <!-- 等级护理照料服务 -->
  315. <div class="section-title-bar">等级护理照料服务</div>
  316. <!-- 生活照料服务 -->
  317. <table class="service-table no-remark">
  318. <thead>
  319. <tr>
  320. <th class="col-service">服务项目</th>
  321. <th class="col-content">服务内容</th>
  322. </tr>
  323. </thead>
  324. <tbody>
  325. <tr v-for="svc in lifeCareItems" :key="svc.key">
  326. <td class="service-name">{{ svc.name }}</td>
  327. <td class="service-content">
  328. <template v-if="!isTextMode">
  329. <label v-for="opt in svc.options" :key="opt" class="chk-label">
  330. <input type="checkbox" :checked="isChecked(form.lifeCare![svc.key], opt)" @change="toggleLife(svc.key, opt)" />
  331. <span>{{ opt }}</span>
  332. </label>
  333. <div v-if="svc.other" class="other-row">
  334. <span>{{ svc.other }}:</span>
  335. <FieldItem v-model="(form.lifeCare as any)[svc.key].other" :is-text-mode="isTextMode" width="200px" placeholder="" @update:model-value="updateForm" />
  336. </div>
  337. </template>
  338. <template v-else>
  339. <span v-for="opt in svc.options" :key="opt" class="text-chk">
  340. {{ isChecked(form.lifeCare![svc.key], opt) ? '☑' : '□' }}{{ opt }}
  341. </span>
  342. <span v-if="(form.lifeCare as any)[svc.key].other" class="text-chk">☑{{ svc.other }}:{{ (form.lifeCare as any)[svc.key].other }}</span>
  343. <span v-else-if="svc.other" class="text-chk">□{{ svc.other }}:____</span>
  344. </template>
  345. </td>
  346. </tr>
  347. <tr>
  348. <td class="service-name"></td>
  349. <td class="service-content">
  350. <template v-if="!isTextMode">
  351. <label class="chk-label">
  352. <input type="checkbox" v-model="form.lifeCare!.otherChecked" @change="updateForm" />
  353. <span>其他生活照料服务:</span>
  354. </label>
  355. <FieldItem v-model="form.lifeCare!.other" :is-text-mode="isTextMode" width="300px" placeholder="" @update:model-value="updateForm" />
  356. </template>
  357. <template v-else>
  358. <span class="text-chk">{{ form.lifeCare!.otherChecked ? '☑' : '□' }}其他生活照料服务:{{ form.lifeCare!.other || '____' }}</span>
  359. </template>
  360. </td>
  361. </tr>
  362. </tbody>
  363. </table>
  364. <!-- 老年护理服务 -->
  365. <table class="service-table no-remark">
  366. <thead>
  367. <tr>
  368. <th class="col-service">服务项目</th>
  369. <th class="col-content">服务内容</th>
  370. </tr>
  371. </thead>
  372. <tbody>
  373. <tr>
  374. <td class="service-name">老年人综合评估</td>
  375. <td class="service-content">
  376. <template v-if="!isTextMode">
  377. <label class="chk-label">
  378. <input type="checkbox" v-model="form.elderlyNursing!.comprehensiveAssessment" @change="updateForm" />
  379. <span>老年人综合评估</span>
  380. </label>
  381. </template>
  382. <template v-else>
  383. <span class="text-chk">{{ form.elderlyNursing!.comprehensiveAssessment ? '☑' : '□' }}老年人综合评估</span>
  384. </template>
  385. </td>
  386. </tr>
  387. <tr v-for="svc in elderlyNursingItems" :key="svc.key">
  388. <td class="service-name">{{ svc.name }}</td>
  389. <td class="service-content">
  390. <template v-if="!isTextMode">
  391. <label v-for="opt in svc.options" :key="opt" class="chk-label">
  392. <input type="checkbox" :checked="isChecked(form.elderlyNursing![svc.key], opt)" @change="toggleElderly(svc.key, opt)" />
  393. <span>{{ opt }}</span>
  394. </label>
  395. <div v-if="svc.other" class="other-row">
  396. <span>{{ svc.other }}:</span>
  397. <FieldItem v-model="(form.elderlyNursing as any)[svc.key].other" :is-text-mode="isTextMode" width="200px" placeholder="" @update:model-value="updateForm" />
  398. </div>
  399. </template>
  400. <template v-else>
  401. <span v-for="opt in svc.options" :key="opt" class="text-chk">
  402. {{ isChecked(form.elderlyNursing![svc.key], opt) ? '☑' : '□' }}{{ opt }}
  403. </span>
  404. <span v-if="(form.elderlyNursing as any)[svc.key].other" class="text-chk">☑{{ svc.other }}:{{ (form.elderlyNursing as any)[svc.key].other }}</span>
  405. <span v-else-if="svc.other" class="text-chk">□{{ svc.other }}:____</span>
  406. </template>
  407. </td>
  408. </tr>
  409. <tr>
  410. <td class="service-name"></td>
  411. <td class="service-content">
  412. <template v-if="!isTextMode">
  413. <label class="chk-label">
  414. <input type="checkbox" v-model="form.elderlyNursing!.healthGuidance" @change="updateForm" />
  415. <span>健康指导</span>
  416. </label>
  417. <label class="chk-label">
  418. <input type="checkbox" v-model="form.elderlyNursing!.drugManagement" @change="updateForm" />
  419. <span>药品管理服务</span>
  420. </label>
  421. <div class="other-row">
  422. <span>其他老年护理服务:</span>
  423. <FieldItem v-model="form.elderlyNursing!.other" :is-text-mode="isTextMode" width="300px" placeholder="" @update:model-value="updateForm" />
  424. </div>
  425. </template>
  426. <template v-else>
  427. <span class="text-chk">{{ form.elderlyNursing!.healthGuidance ? '☑' : '□' }}健康指导</span>
  428. <span class="text-chk">{{ form.elderlyNursing!.drugManagement ? '☑' : '□' }}药品管理服务</span>
  429. <span class="text-chk">其他老年护理服务:{{ form.elderlyNursing!.other || '____' }}</span>
  430. </template>
  431. </td>
  432. </tr>
  433. <tr>
  434. <td class="service-name">其他约定服务</td>
  435. <td class="service-content">
  436. <FieldItem v-model="form.elderlyNursing!.otherAgreed" :is-text-mode="isTextMode" width="100%" placeholder="" @update:model-value="updateForm" />
  437. </td>
  438. </tr>
  439. </tbody>
  440. </table>
  441. <!-- 签名 -->
  442. <div class="sign-row">
  443. <div class="sign-block">
  444. <span class="sign-label">乙方(签名):</span>
  445. <SignatureItem v-model="form.partyBSign" :is-text-mode="isTextMode" width="200" height="80" placeholder="(签字处)" title="乙方签字" @update:model-value="updateForm" />
  446. </div>
  447. <div class="sign-block">
  448. <span class="sign-label">丙方(签名或盖章):</span>
  449. <SignatureItem v-model="form.partyCSign" :is-text-mode="isTextMode" width="200" height="80" placeholder="(签字处)" title="丙方签字" @update:model-value="updateForm" />
  450. </div>
  451. </div>
  452. <div class="date-row">
  453. 日期:
  454. <FieldItem v-model="form.signDate" :is-text-mode="isTextMode" type="date" width="220px" placeholder="" @update:model-value="updateForm" />
  455. </div>
  456. <!-- 备注 -->
  457. <div class="notice">
  458. <p>注:1. 根据乙方的照料护理等级确定相应服务内容。</p>
  459. <p>  2. “服务内容”一栏在确认内容后的“□”内打“√”,未选的在“□”内打“×”。</p>
  460. </div>
  461. </div>
  462. </template>
  463. <style scoped lang="scss">
  464. .first-service-project {
  465. width: 210mm;
  466. max-width: 100%;
  467. margin: 0 auto;
  468. font-family: 'SimSun', 'Microsoft YaHei', serif;
  469. font-size: 13px;
  470. color: #333;
  471. background: #fff;
  472. padding: 5mm;
  473. @media print {
  474. width: 210mm !important;
  475. max-width: 100%;
  476. padding: 0;
  477. border: none;
  478. margin: 0;
  479. box-sizing: border-box;
  480. }
  481. }
  482. .doc-title {
  483. text-align: center;
  484. font-size: 20px;
  485. font-weight: bold;
  486. margin-bottom: 8px;
  487. }
  488. .doc-date {
  489. text-align: center;
  490. margin-bottom: 12px;
  491. font-size: 14px;
  492. display: flex;
  493. align-items: center;
  494. justify-content: center;
  495. gap: 4px;
  496. }
  497. .info-table,
  498. .service-table {
  499. width: 100%;
  500. border-collapse: collapse;
  501. margin-bottom: 12px;
  502. th,
  503. td {
  504. border: 1px solid #333;
  505. padding: 6px 8px;
  506. vertical-align: top;
  507. }
  508. th {
  509. background: #f5f5f5;
  510. font-weight: bold;
  511. text-align: center;
  512. }
  513. .label {
  514. background: #f5f5f5;
  515. font-weight: bold;
  516. text-align: center;
  517. white-space: nowrap;
  518. width: 100px;
  519. }
  520. }
  521. .service-table {
  522. .section-title {
  523. background: #f5f5f5;
  524. font-weight: bold;
  525. text-align: center;
  526. font-size: 14px;
  527. }
  528. .col-service {
  529. width: 120px;
  530. text-align: center;
  531. }
  532. .col-content {
  533. min-width: 400px;
  534. }
  535. .col-remark {
  536. width: 100px;
  537. }
  538. .service-name {
  539. text-align: center;
  540. font-weight: bold;
  541. }
  542. .service-content {
  543. .chk-label {
  544. display: inline-flex;
  545. align-items: center;
  546. margin-right: 12px;
  547. margin-bottom: 4px;
  548. cursor: pointer;
  549. input[type='checkbox'] {
  550. margin-right: 4px;
  551. }
  552. }
  553. .text-chk {
  554. display: inline-block;
  555. margin-right: 12px;
  556. margin-bottom: 4px;
  557. }
  558. .other-row {
  559. margin-top: 4px;
  560. display: flex;
  561. align-items: center;
  562. gap: 4px;
  563. }
  564. .sub-service {
  565. margin-top: 6px;
  566. padding-left: 8px;
  567. border-left: 2px solid #ccc;
  568. .sub-name {
  569. font-weight: bold;
  570. margin-bottom: 4px;
  571. }
  572. }
  573. .extra-label,
  574. .extra-text {
  575. margin-left: 4px;
  576. }
  577. }
  578. }
  579. .section-title-bar {
  580. text-align: center;
  581. font-weight: bold;
  582. font-size: 14px;
  583. padding: 8px;
  584. border: 1px solid #333;
  585. border-bottom: none;
  586. background: #f5f5f5;
  587. }
  588. .sign-row {
  589. display: flex;
  590. justify-content: space-between;
  591. margin-top: 20px;
  592. padding: 0 20px;
  593. .sign-block {
  594. display: flex;
  595. align-items: flex-start;
  596. gap: 8px;
  597. .sign-label {
  598. white-space: nowrap;
  599. padding-top: 4px;
  600. }
  601. }
  602. }
  603. .date-row {
  604. text-align: right;
  605. margin-top: 12px;
  606. padding-right: 40px;
  607. display: flex;
  608. align-items: center;
  609. justify-content: flex-end;
  610. gap: 4px;
  611. }
  612. .notice {
  613. margin-top: 16px;
  614. font-size: 12px;
  615. color: #666;
  616. p {
  617. margin: 4px 0;
  618. }
  619. }
  620. @media print {
  621. .first-service-project {
  622. font-size: 12px;
  623. }
  624. .service-table {
  625. .col-content {
  626. min-width: auto;
  627. }
  628. }
  629. }
  630. </style>