AddForm.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792
  1. <template>
  2. <el-drawer
  3. v-model="dialogVisible"
  4. :title="title"
  5. resizable
  6. :close-on-click-modal="false"
  7. :close-on-press-escape="false"
  8. :destroy-on-close="true"
  9. size="70%"
  10. :before-close="handleClosed"
  11. >
  12. <div class="attack-risk-form">
  13. <h1 class="form-title">老年人能力综合评估结果(附件)</h1>
  14. <!-- 基本信息 -->
  15. <el-row :gutter="40">
  16. <el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="12" class="row">
  17. <text>长者姓名</text>
  18. <search-the-elderly ref="selectElderRef" :disabled="isDetail" @update_elder="elderUp" v-model="dataForm.elderName" :tId="dataForm.tenantId"/>
  19. </el-col>
  20. <el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="12" class="row">
  21. <text>档案号</text>
  22. <el-input v-if="!isDetail" v-model="dataForm.contractNumber" disabled />
  23. <el-text v-else disabled="">{{dataForm.contractNumber}}</el-text>
  24. </el-col>
  25. </el-row>
  26. <el-row :gutter="40">
  27. <el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="12" class="row">
  28. <text>入院日期</text>
  29. <el-input v-if="!isDetail" :model-value="dayjs(dataForm.checkInTime).format('YYYY-MM-DD')=='Invalid Date'?'':dayjs(dataForm.checkInTime).format('YYYY-MM-DD')" disabled />
  30. <el-text v-else disabled="">{{dayjs(dataForm.checkInTime).format('YYYY-MM-DD')}}</el-text>
  31. </el-col>
  32. <el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="12" class="row">
  33. <text>床位号</text>
  34. <el-input v-if="!isDetail" v-model="dataForm.bedName" disabled />
  35. <el-text v-else disabled="">{{dataForm.bedName}}</el-text>
  36. </el-col>
  37. </el-row>
  38. <el-row :gutter="40">
  39. <el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="12" class="row">
  40. <text>记录人</text>
  41. <el-input :disabled="isDetail" v-model="form.assessor" />
  42. </el-col>
  43. <el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="12" class="row">
  44. <text>记录日期</text>
  45. <el-date-picker :disabled="isDetail" v-model="form.assessDate" type="date" style="width: 100%;"/>
  46. </el-col>
  47. </el-row>
  48. <!-- 内容区域 -->
  49. <div class="form-body">
  50. <DocumentAttachmentBody
  51. v-model="attachmentForm"
  52. :is-text-mode="isDetail"
  53. title="老年人能力综合评估结果"
  54. />
  55. </div>
  56. </div>
  57. <template #footer>
  58. <el-button @click="handleClosed">关闭</el-button>
  59. <el-button style="margin-left: 22px;margin-right: 30px" v-loading="formLoading" type="primary" v-show="!isDetail" @click="submitForm">确定</el-button>
  60. </template>
  61. </el-drawer>
  62. </template>
  63. <script lang="ts" setup>
  64. import { computed, ref, watch, reactive } from 'vue'
  65. import dayjs from 'dayjs'
  66. import DocumentAttachmentBody from '../components/DocumentAttachmentBody.vue'
  67. import { attackRiskCreate, attackRiskGetById, attackRiskUpdate, attackRiskGetByElderId } from "@/api/social-work";
  68. const message = useMessage() // 消息弹窗
  69. const { t } = useI18n() // 国际化
  70. const title = ref('')
  71. const dialogVisible = ref(false) // 弹窗
  72. const formRef = ref() // 表单 Ref
  73. const selectElderRef = ref() // 表单 Ref
  74. const attachmentForm = reactive({
  75. images: []
  76. })
  77. const isDetail = ref(false) // 是否详情打开
  78. const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
  79. let dataForm = ref({
  80. // 表单字段
  81. id: undefined,
  82. idCard: '',
  83. contractNumber: '', //档案号
  84. elderName: '',//长者姓名
  85. bedName: '', //床位号
  86. elderAge: '', //年龄
  87. elderSex: '', //性别
  88. checkInTime: '', //入院日期
  89. elderId: '',
  90. tenantId: undefined
  91. })
  92. const elderUp = (e) => {
  93. dataForm.value.elderName = e.elderName
  94. dataForm.value.elderId = e.id
  95. dataForm.value.elderSex = e.elderSex === 1 ? '男' : '女'
  96. dataForm.value.bedName = e.bedName || ''
  97. dataForm.value.checkInTime = e.checkInTime
  98. dataForm.value.contractNumber = e.contractNumber||e.fileNumber
  99. dataForm.value.elderAge = e.elderAge
  100. }
  101. // ========== 攻击风险因素评估量表 表单序列化方法 ==========
  102. /** 风险程度样式类 */
  103. const riskLevelClass = computed(() => {
  104. const level = form.attackLevel
  105. if (!level || level === 0) return 'risk-none'
  106. if (level === 1) return 'risk-low'
  107. if (level === 2) return 'risk-medium'
  108. return 'risk-high'
  109. })
  110. /** 自动判断风险等级 */
  111. const autoJudgeRiskLevel = () => {
  112. const level = form.attackLevel
  113. if (!level || level === 0) form.riskLevel = 'none'
  114. else if (level === 1) form.riskLevel = 'low'
  115. else if (level === 2) form.riskLevel = 'medium'
  116. else form.riskLevel = 'high'
  117. }
  118. /** 将表单数据序列化为 JSON 对象 */
  119. const serializeFormData = () => {
  120. return {
  121. // 基本信息
  122. assessor: form.assessor || '',
  123. assessDate: form.assessDate ? dayjs(form.assessDate).format('YYYY-MM-DD') : '',
  124. // 攻击风险等级(1=I级,2=II级,3=III级,4=IV级)
  125. attackLevel: form.attackLevel || 0,
  126. // 各级别选择的具体项
  127. attackLevel1Items: form.attackLevel1Items || [],
  128. attackLevel2Items: form.attackLevel2Items || [],
  129. attackLevel3Items: form.attackLevel3Items || [],
  130. attackLevel4Items: form.attackLevel4Items || [],
  131. // 风险程度
  132. riskLevel: form.riskLevel || '',
  133. // 预防措施
  134. preventiveMeasures: form.preventiveMeasures || [],
  135. preventiveMeasuresOther: form.preventiveMeasuresOther || '',
  136. // 签名
  137. familySignature: form.familySignature || '',
  138. familySignDate: form.familySignDate ? dayjs(form.familySignDate).format('YYYY-MM-DD') : '',
  139. // 风险知情书内容
  140. riskDisclosure: {
  141. companyName: riskDisclosureForm.companyName || '',
  142. partyBSign: riskDisclosureForm.partyBSign || '',
  143. partyBDate: riskDisclosureForm.partyBDate || '',
  144. partyCSign: riskDisclosureForm.partyCSign || '',
  145. partyCDate: riskDisclosureForm.partyCDate || ''
  146. },
  147. // 附件图片内容
  148. attachment: { ...attachmentForm }
  149. }
  150. }
  151. /** 将 JSON 对象反序列化为表单数据 */
  152. const deserializeFormData = (formData: Record<string, any>) => {
  153. if (!formData) return
  154. // 基本信息
  155. form.assessor = formData.assessor || ''
  156. form.assessDate = formData.assessDate ? dayjs(formData.assessDate).toDate() : ''
  157. // 攻击风险等级
  158. form.attackLevel = formData.attackLevel || 0
  159. // 各级别选择的具体项
  160. form.attackLevel1Items = formData.attackLevel1Items || []
  161. form.attackLevel2Items = formData.attackLevel2Items || []
  162. form.attackLevel3Items = formData.attackLevel3Items || []
  163. form.attackLevel4Items = formData.attackLevel4Items || []
  164. // 风险程度
  165. form.riskLevel = formData.riskLevel || ''
  166. // 预防措施
  167. form.preventiveMeasures = formData.preventiveMeasures || []
  168. form.preventiveMeasuresOther = formData.preventiveMeasuresOther || ''
  169. // 签名
  170. form.familySignature = formData.familySignature || ''
  171. form.familySignDate = formData.familySignDate ? dayjs(formData.familySignDate).toDate() : ''
  172. // 风险知情书内容
  173. if (formData.riskDisclosure) {
  174. riskDisclosureForm.companyName = formData.riskDisclosure.companyName || ''
  175. riskDisclosureForm.partyBSign = formData.riskDisclosure.partyBSign || ''
  176. riskDisclosureForm.partyBDate = formData.riskDisclosure.partyBDate || ''
  177. riskDisclosureForm.partyCSign = formData.riskDisclosure.partyCSign || ''
  178. riskDisclosureForm.partyCDate = formData.riskDisclosure.partyCDate || ''
  179. }
  180. // 附件图片内容
  181. if (formData.attachment) {
  182. attachmentForm.images = formData.attachment.images || []
  183. }
  184. }
  185. /** 重置攻击风险因素评估表表单数据 */
  186. const resetAttackRiskForm = () => {
  187. form.assessor = ''
  188. form.assessDate = ''
  189. // 攻击风险等级
  190. form.attackLevel = 0
  191. // 各级别选择的具体项
  192. form.attackLevel1Items = []
  193. form.attackLevel2Items = []
  194. form.attackLevel3Items = []
  195. form.attackLevel4Items = []
  196. // 风险程度
  197. form.riskLevel = ''
  198. // 预防措施
  199. form.preventiveMeasures = []
  200. form.preventiveMeasuresOther = ''
  201. // 签名
  202. form.familySignature = ''
  203. form.familySignDate = ''
  204. // 风险知情书
  205. riskDisclosureForm.companyName = '颐年医养'
  206. riskDisclosureForm.partyBSign = ''
  207. riskDisclosureForm.partyBDate = ''
  208. riskDisclosureForm.partyCSign = ''
  209. riskDisclosureForm.partyCDate = ''
  210. // 附件图片
  211. attachmentForm.images = []
  212. }
  213. /** 打开弹窗 */
  214. const open = async (tenantId, id?: any, detail: boolean = false) => {
  215. resetForm()
  216. dialogVisible.value = true
  217. dataForm.value.id = id || undefined
  218. dataForm.value.tenantId = tenantId
  219. isDetail.value = detail
  220. if (id) {
  221. title.value = "编辑-攻击风险因素评估"
  222. // 加载评估数据
  223. await loadAttackRiskData(id)
  224. } else {
  225. title.value = "新增-攻击风险因素评估"
  226. }
  227. }
  228. /** 加载评估数据 */
  229. const loadAttackRiskData = async (id: number) => {
  230. try {
  231. const res = await attackRiskGetById(id)
  232. if (res) {
  233. // 填充长者基本信息
  234. dataForm.value.elderName = res.elderName || ''
  235. dataForm.value.elderId = res.elderId || ''
  236. dataForm.value.elderSex = res.elderSex || ''
  237. dataForm.value.bedName = res.bedName || ''
  238. dataForm.value.checkInTime = res.checkInTime || ''
  239. dataForm.value.contractNumber = res.fileNumber || ''
  240. dataForm.value.elderAge = res.elderAge || ''
  241. await selectElderRef.value.upData(res.elderName, res.elderId)
  242. // 解析 assessData
  243. if (res.assessData) {
  244. const formData = JSON.parse(res.assessData)
  245. deserializeFormData(formData)
  246. }
  247. }
  248. } catch (error) {
  249. message.error('加载评估数据失败')
  250. }
  251. }
  252. /** 根据长者ID加载评估数据 */
  253. const loadAttackRiskByElderId = async (elderId: number) => {
  254. try {
  255. const res = await attackRiskGetByElderId(elderId)
  256. if (res && res.assessData) {
  257. const formData = JSON.parse(res.assessData)
  258. deserializeFormData(formData)
  259. }
  260. } catch (error) {
  261. // 无历史数据,不处理
  262. }
  263. }
  264. const form = reactive({
  265. // 基本信息
  266. assessor: '',
  267. assessDate: '',
  268. // 攻击风险等级(0=无,1=I级,2=II级,3=III级,4=IV级)
  269. attackLevel: 0,
  270. // 各级别选择的具体项
  271. attackLevel1Items: [], // I级选择的项(1-7)
  272. attackLevel2Items: [], // II级选择的项(1-3)
  273. attackLevel3Items: [], // III级选择的项(1-4)
  274. attackLevel4Items: [], // IV级选择的项(1-2)
  275. // 风险程度
  276. riskLevel: '',
  277. // 预防措施
  278. preventiveMeasures: [],
  279. preventiveMeasuresOther: '',
  280. // 签名
  281. familySignature: '',
  282. familySignDate: ''
  283. })
  284. // 监听攻击等级变化,自动判断风险等级
  285. watch(() => form.attackLevel, () => {
  286. autoJudgeRiskLevel()
  287. })
  288. // 风险知情书表单数据
  289. const riskDisclosureForm = reactive({
  290. elderName: '',
  291. companyName: '颐年医养',
  292. assessor: '',
  293. assessDate: '',
  294. partyBSign: '',
  295. partyBDate: '',
  296. partyCSign: '',
  297. partyCDate: ''
  298. })
  299. // 长者信息变化时同步到风险告知书
  300. watch(
  301. () => dataForm.value.elderName,
  302. (name) => {
  303. riskDisclosureForm.elderName = name
  304. }
  305. )
  306. // 记录人变化时同步
  307. watch(
  308. () => form.assessor,
  309. (val) => {
  310. riskDisclosureForm.assessor = val
  311. }
  312. )
  313. watch(
  314. () => form.assessDate,
  315. (val) => {
  316. riskDisclosureForm.assessDate = val ? dayjs(val).format('YYYY-MM-DD') : ''
  317. }
  318. )
  319. defineExpose({ open }) // 提供 open 方法,用于打开弹窗
  320. /** 提交表单 */
  321. const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
  322. const submitForm = async () => {
  323. if (formLoading.value) {
  324. return
  325. }
  326. formLoading.value = true
  327. // 提交请求
  328. try {
  329. const assessData = serializeFormData()
  330. const tempParams = {
  331. ...dataForm.value,
  332. assessData: JSON.stringify(assessData),
  333. attackLevel: form.attackLevel,
  334. riskLevel: form.riskLevel,
  335. assessor: form.assessor,
  336. assessScore: form.attackLevel,
  337. assessDate: form.assessDate ? dayjs(form.assessDate).format('YYYY-MM-DD') : ''
  338. }
  339. if (dataForm.value.id) {
  340. const res = await attackRiskUpdate(tempParams)
  341. if (res) {
  342. message.success(t('common.updateSuccess'))
  343. dialogVisible.value = false
  344. // 发送操作成功的事件
  345. emit('success')
  346. }
  347. } else {
  348. const res = await attackRiskCreate(tempParams)
  349. if (res) {
  350. message.success(t('common.createSuccess'))
  351. dialogVisible.value = false
  352. // 发送操作成功的事件
  353. emit('success')
  354. }
  355. }
  356. } finally {
  357. setTimeout(() => {
  358. formLoading.value = false
  359. }, 500)
  360. }
  361. }
  362. /** 重置表单 */
  363. const resetForm = () => {
  364. dataForm.value = {
  365. id: undefined,
  366. idCard: '',
  367. contractNumber: '', //档案号
  368. elderName: '',//长者姓名
  369. bedName: '', //床位号
  370. elderAge: '', //年龄
  371. elderSex: '', //性别
  372. checkInTime: '', //入院日期
  373. elderId: '',
  374. tenantId: undefined
  375. }
  376. formRef.value?.resetFields()
  377. // 重置攻击风险因素评估表表单
  378. resetAttackRiskForm()
  379. }
  380. // 关闭表单
  381. const handleClosed = () => {
  382. dialogVisible.value = false
  383. resetForm()
  384. }
  385. </script>
  386. <style scoped lang="scss">
  387. .form-title {
  388. text-align: center;
  389. font-size: 20px;
  390. width: 100%;
  391. margin-bottom: 20px;
  392. }
  393. .attack-risk-form {
  394. max-width: 1200px;
  395. margin: 0 auto;
  396. background: #fff;
  397. .info-row {
  398. display: flex;
  399. gap: 40px;
  400. width: 100%;
  401. flex-direction: row;
  402. align-items: center;
  403. margin-bottom: 10px;
  404. .info-item {
  405. display: flex;
  406. align-items: center;
  407. gap: 8px;
  408. .label {
  409. width: 82px;
  410. text-align: right;
  411. margin-right: 4px;
  412. white-space: nowrap;
  413. }
  414. }
  415. }
  416. .form-body {
  417. border: 1px solid #333;
  418. padding: 15px;
  419. }
  420. // 调整 Element Plus 组件样式
  421. :deep(.el-input__inner) {
  422. height: 28px;
  423. line-height: 28px;
  424. border-top: none;
  425. border-left: none;
  426. border-right: none;
  427. border-radius: 0;
  428. padding: 0 4px;
  429. &:focus {
  430. border-color: #409eff;
  431. }
  432. }
  433. :deep(.el-checkbox__label),
  434. :deep(.el-radio__label) {
  435. padding-left: 4px;
  436. }
  437. }
  438. .row{
  439. margin-bottom: 12px;
  440. display: flex;
  441. flex-direction: row;
  442. align-items: center;
  443. text{
  444. text-align: right;
  445. margin-right: 4px;
  446. width: 82px;
  447. }
  448. }
  449. // NGASR自杀风险评估量表 特有样式
  450. .total-score-section {
  451. display: flex;
  452. justify-content: space-between;
  453. align-items: center;
  454. padding: 15px;
  455. background: #f5f7fa;
  456. border-radius: 4px;
  457. margin-bottom: 20px;
  458. .total-score {
  459. display: flex;
  460. align-items: baseline;
  461. gap: 4px;
  462. .score-label {
  463. font-size: 16px;
  464. font-weight: bold;
  465. }
  466. .score-value {
  467. font-size: 28px;
  468. font-weight: bold;
  469. color: #409eff;
  470. }
  471. .score-max {
  472. font-size: 14px;
  473. color: #909399;
  474. }
  475. }
  476. .risk-level {
  477. font-size: 16px;
  478. font-weight: bold;
  479. padding: 8px 16px;
  480. border-radius: 4px;
  481. &.risk-none {
  482. color: #67c23a;
  483. background: #f0f9eb;
  484. }
  485. &.risk-low {
  486. color: #e6a23c;
  487. background: #fdf6ec;
  488. }
  489. &.risk-medium {
  490. color: #f56c6c;
  491. background: #fef0f0;
  492. }
  493. &.risk-high {
  494. color: #f56c6c;
  495. background: #fef0f0;
  496. border: 1px solid #f56c6c;
  497. }
  498. }
  499. }
  500. .assessment-table {
  501. width: 100%;
  502. border-collapse: collapse;
  503. margin-bottom: 20px;
  504. table {
  505. width: 100%;
  506. border: 1px solid #333;
  507. th, td {
  508. border: 1px solid #333;
  509. padding: 12px;
  510. text-align: left;
  511. vertical-align: top;
  512. }
  513. th {
  514. background: #f5f7fa;
  515. font-weight: bold;
  516. text-align: center;
  517. }
  518. .content-col {
  519. width: 65%;
  520. }
  521. .level-col {
  522. width: 10%;
  523. text-align: center;
  524. }
  525. .select-col {
  526. width: 25%;
  527. text-align: center;
  528. }
  529. .content {
  530. text-align: left;
  531. .content-title {
  532. font-weight: bold;
  533. margin-bottom: 8px;
  534. }
  535. .content-item {
  536. margin-left: 15px;
  537. margin-bottom: 4px;
  538. font-size: 13px;
  539. }
  540. }
  541. .level {
  542. text-align: center;
  543. font-weight: bold;
  544. }
  545. .select {
  546. text-align: center;
  547. :deep(.el-radio) {
  548. margin-right: 0;
  549. }
  550. }
  551. }
  552. }
  553. .risk-judgment-section {
  554. margin: 20px 0;
  555. padding: 15px;
  556. background: #f5f7fa;
  557. border-radius: 4px;
  558. border: 1px solid #e4e7ed;
  559. .risk-judgment-title {
  560. font-weight: bold;
  561. font-size: 16px;
  562. margin-bottom: 12px;
  563. color: #303133;
  564. }
  565. .risk-options {
  566. display: flex;
  567. flex-wrap: wrap;
  568. gap: 20px;
  569. :deep(.el-radio) {
  570. margin-right: 0;
  571. }
  572. }
  573. }
  574. .preventive-section {
  575. margin: 20px 0;
  576. padding: 15px;
  577. background: #f5f7fa;
  578. border-radius: 4px;
  579. border: 1px solid #e4e7ed;
  580. .preventive-title {
  581. font-weight: bold;
  582. font-size: 16px;
  583. margin-bottom: 12px;
  584. color: #303133;
  585. }
  586. .preventive-options {
  587. display: flex;
  588. flex-wrap: wrap;
  589. gap: 15px;
  590. :deep(.el-checkbox) {
  591. margin-right: 20px;
  592. margin-bottom: 8px;
  593. }
  594. .other-input {
  595. width: 200px;
  596. margin-left: 10px;
  597. }
  598. }
  599. }
  600. .signature-section {
  601. margin: 20px 0;
  602. padding: 15px;
  603. background: #f5f7fa;
  604. border-radius: 4px;
  605. border: 1px solid #e4e7ed;
  606. .signature-row {
  607. display: flex;
  608. justify-content: space-between;
  609. align-items: center;
  610. gap: 20px;
  611. }
  612. .signature-item {
  613. display: flex;
  614. align-items: center;
  615. gap: 10px;
  616. &.date-item {
  617. :deep(.el-date-picker) {
  618. width: 150px;
  619. }
  620. }
  621. }
  622. .signature-label {
  623. font-weight: bold;
  624. white-space: nowrap;
  625. }
  626. .signature-input {
  627. width: 150px;
  628. }
  629. .date-picker {
  630. width: 150px;
  631. }
  632. }
  633. .note-section {
  634. margin-top: 20px;
  635. padding: 15px;
  636. background: #f5f7fa;
  637. border-radius: 4px;
  638. border: 1px solid #e4e7ed;
  639. .note-title {
  640. font-weight: bold;
  641. font-size: 16px;
  642. margin-bottom: 12px;
  643. color: #303133;
  644. }
  645. .note-content {
  646. font-size: 13px;
  647. line-height: 1.8;
  648. color: #606266;
  649. p {
  650. margin: 5px 0;
  651. }
  652. .indent {
  653. margin-left: 20px;
  654. }
  655. }
  656. }
  657. </style>