index.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  1. <template>
  2. <ContentWrap>
  3. <!-- 搜索工作栏 -->
  4. <el-form
  5. class="-mb-15px"
  6. :model="queryParams"
  7. ref="queryFormRef"
  8. :inline="true"
  9. label-width="80px"
  10. >
  11. <el-form-item label="长者姓名" prop="elderName">
  12. <el-input
  13. v-model="queryParams.elderName"
  14. placeholder="请输入长者姓名"
  15. class="!w-240px"
  16. @keyup.enter="handleQuery"
  17. />
  18. </el-form-item>
  19. <!-- <el-form-item label="评估年份">-->
  20. <!-- <el-date-picker-->
  21. <!-- size="default"-->
  22. <!-- ref="selectRef"-->
  23. <!-- class="!w-240px"-->
  24. <!-- v-model="queryParams.recordMonthRange"-->
  25. <!-- type="year"-->
  26. <!-- :clearable="true"-->
  27. <!-- :editable="false"-->
  28. <!-- placeholder="选择记录年份"-->
  29. <!-- value-format="YYYY"-->
  30. <!-- format="YYYY"-->
  31. <!-- date-format="YYYY"-->
  32. <!-- />-->
  33. <!-- </el-form-item>-->
  34. <el-form-item>
  35. <el-button @click="handleQuery"> <Icon icon="ep:search" class="mr-5px" /> 搜索 </el-button>
  36. <el-button @click="resetQuery"> <Icon icon="ep:refresh" class="mr-5px" /> 重置 </el-button>
  37. </el-form-item>
  38. </el-form>
  39. </ContentWrap>
  40. <!-- 列表 -->
  41. <ContentWrap>
  42. <TabBarBtn>
  43. <template #next>
  44. <el-button type="primary" @click="handleAdd">
  45. <Icon icon="ep:plus" class="mr-5px" /> 新增
  46. </el-button>
  47. <el-button type="success" @click="handleExport">
  48. <Icon icon="ep:upload" class="mr-5px" /> 导出
  49. </el-button>
  50. </template>
  51. </TabBarBtn>
  52. <el-table v-loading="loading" :data="list" stripe>
  53. <el-table-column type="index" label="序号" width="60" align="center" />
  54. <el-table-column
  55. label="机构名称"
  56. v-if="userStore.getUser.id == 1"
  57. type="index"
  58. width="150"
  59. align="center"
  60. >
  61. <template #default="{ row }">
  62. {{ findLabelById(userStore.getOrgTenant.options, row.tenantId) }}
  63. </template>
  64. </el-table-column>
  65. <el-table-column label="长者姓名" prop="elderName" width="110" align="center" />
  66. <el-table-column label="性别" prop="elderSex" width="80" align="center">
  67. <template #default="{ row }">
  68. {{ getDictLabel(DICT_TYPE.SYSTEM_USER_SEX, row.elderSex) }}
  69. </template>
  70. </el-table-column>
  71. <el-table-column label="年龄" prop="elderAge" width="80" align="center" />
  72. <el-table-column label="床号" prop="bedName" width="200" align="center" />
  73. <el-table-column label="合同号" prop="contractNumber" width="150" align="center" />
  74. <el-table-column label="护理级别" prop="nurseLevelName" width="150" align="center" />
  75. <el-table-column label="防噎食评估" prop="asphyxiationRisk" width="130" align="center">
  76. <template #default="{ row }">
  77. <el-tag
  78. :type="getRiskType(row.riskData?.asphyxiation?.level)"
  79. :class="{ 'cursor-pointer': row.riskData?.asphyxiation?.id }"
  80. @click="
  81. row.riskData?.asphyxiation?.id && handleOpenAsphyxiation(row.riskData?.asphyxiation)
  82. "
  83. >{{ getRiskLevelText(row.riskData?.asphyxiation?.level || '-') }}</el-tag
  84. >
  85. </template>
  86. </el-table-column>
  87. <el-table-column label="防压疮评估" prop="pressureUlcerRisk" width="130" align="center">
  88. <template #default="{ row }">
  89. <el-tag
  90. :type="getRiskType(row.riskData?.pressureUlcer?.level)"
  91. :class="{ 'cursor-pointer': row.riskData?.pressureUlcer?.id }"
  92. @click="
  93. row.riskData?.pressureUlcer?.id &&
  94. handleOpenPressureUlcer(row.riskData?.pressureUlcer)
  95. "
  96. >{{ getRiskLevelText(row.riskData?.pressureUlcer?.level || '-') }}</el-tag
  97. >
  98. </template>
  99. </el-table-column>
  100. <el-table-column label="防跌倒评估" prop="fallRisk" width="130" align="center">
  101. <template #default="{ row }">
  102. <el-tag
  103. :type="getRiskType(row.riskData?.fall?.level)"
  104. :class="{ 'cursor-pointer': row.riskData?.fall?.id }"
  105. @click="row.riskData?.fall?.id && handleOpenFall(row.riskData?.fall)"
  106. >{{ getRiskLevelText(row.riskData?.fall?.level || '-') }}</el-tag
  107. >
  108. </template>
  109. </el-table-column>
  110. <el-table-column label="防坠床评估" prop="bedFallRisk" width="130" align="center">
  111. <template #default="{ row }">
  112. <el-tag
  113. :type="getRiskType(row.riskData?.bedFall?.level)"
  114. :class="{ 'cursor-pointer': row.riskData?.bedFall?.id }"
  115. @click="row.riskData?.bedFall?.id && handleOpenBedFall(row.riskData?.bedFall)"
  116. >{{ getRiskLevelText(row.riskData?.bedFall?.level || '-') }}</el-tag
  117. >
  118. </template>
  119. </el-table-column>
  120. <el-table-column label="防烫伤评估" prop="scaldRisk" width="130" align="center">
  121. <template #default="{ row }">
  122. <el-tag
  123. :type="getRiskType(row.riskData?.scald?.level)"
  124. :class="{ 'cursor-pointer': row.riskData?.scald?.id }"
  125. @click="row.riskData?.scald?.id && handleOpenScald(row.riskData?.scald)"
  126. >{{ getRiskLevelText(row.riskData?.scald?.level || '-') }}</el-tag
  127. >
  128. </template>
  129. </el-table-column>
  130. <el-table-column label="防走失评估" prop="wanderingRisk" width="130" align="center">
  131. <template #default="{ row }">
  132. <el-tag
  133. :type="getRiskType(row.riskData?.wandering?.level)"
  134. :class="{ 'cursor-pointer': row.riskData?.wandering?.id }"
  135. @click="row.riskData?.wandering?.id && handleOpenWandering(row.riskData?.wandering)"
  136. >{{ getRiskLevelText(row.riskData?.wandering?.level || '-') }}</el-tag
  137. >
  138. </template>
  139. </el-table-column>
  140. <el-table-column label="防自伤他伤评估" prop="suicideRisk" width="130" align="center">
  141. <template #default="{ row }">
  142. <el-tag
  143. :type="getRiskType(row.riskData?.selfHarm?.level)"
  144. :class="{ 'cursor-pointer': row.riskData?.selfHarm?.id }"
  145. @click="row.riskData?.selfHarm?.id && handleOpenSuicide(row.riskData?.selfHarm)"
  146. >{{ getRiskLevelText(row.riskData?.selfHarm?.level || '-') }}</el-tag
  147. >
  148. </template>
  149. </el-table-column>
  150. <el-table-column label="防食品药品误食评估" prop="perceptionRisk" width="152" align="center">
  151. <template #default="{ row }">
  152. <el-tag
  153. :type="getRiskType(row.riskData?.foodDrug?.level)"
  154. :class="{ 'cursor-pointer': row.riskData?.foodDrug?.id }"
  155. @click="row.riskData?.foodDrug?.id && handleOpenMental(row.riskData?.foodDrug)"
  156. >{{ getRiskLevelText(row.riskData?.foodDrug?.level || '-') }}</el-tag
  157. >
  158. </template>
  159. </el-table-column>
  160. <el-table-column label="防文娱活动意外" prop="balanceRisk" width="130" align="center">
  161. <template #default="{ row }">
  162. <el-tag
  163. :type="getRiskType(row.riskData?.entertainment?.level)"
  164. :class="{ 'cursor-pointer': row.riskData?.entertainment?.id }"
  165. @click="
  166. row.riskData?.entertainment?.id && handleOpenBalance(row.riskData?.entertainment)
  167. "
  168. >{{ getRiskLevelText(row.riskData?.entertainment?.level || '-') }}</el-tag
  169. >
  170. </template>
  171. </el-table-column>
  172. <el-table-column label="记录时间" prop="bedName" width="200" align="center">
  173. <template #default="{ row }">
  174. {{ formatToDateTime(row.createTime) }}
  175. </template>
  176. </el-table-column>
  177. <el-table-column label="记录人" prop="creator" width="150" align="center" />
  178. <el-table-column label="操作" align="center" width="200" fixed="right">
  179. <template #default="scope">
  180. <el-button link type="primary" @click="handleEdit(scope.row, scope.row.id)">
  181. 编辑
  182. </el-button>
  183. <el-button link type="warning" @click="handleView(scope.row, scope.row.id)">
  184. 详情
  185. </el-button>
  186. <el-button link type="danger" @click="handleDelete(scope.row)"> 删除 </el-button>
  187. </template>
  188. </el-table-column>
  189. </el-table>
  190. <!-- 分页 -->
  191. <Pagination
  192. :total="total"
  193. v-model:page="queryParams.pageNo"
  194. v-model:limit="queryParams.pageSize"
  195. @pagination="getList"
  196. />
  197. </ContentWrap>
  198. <!-- 新增/编辑弹窗 -->
  199. <SafetyRiskNoticeForm ref="formRef" @success="getList" />
  200. <!-- 各项评估详情弹窗 -->
  201. <AsphyxiationForm ref="asphyxiationFormRef" />
  202. <PressureSoresForm ref="pressureSoresFormRef" />
  203. <FallDownForm ref="fallDownFormRef" />
  204. <FallPreventionForm ref="fallPreventionFormRef" />
  205. <EmpyrosisForm ref="empyrosisFormRef" />
  206. <WanderAwayForm ref="wanderAwayFormRef" />
  207. <CommitSuicideForm ref="commitSuicideFormRef" />
  208. <MMSEForm ref="mmseFormRef" />
  209. <AttackForm ref="attackFormRef" />
  210. <AntiEntertainmentForm ref="equilibriumFormRef" />
  211. <DailyLifeForm ref="dailyLifeFormRef" />
  212. <NutritionalRiskForm ref="nutritionalRiskFormRef" />
  213. </template>
  214. <script setup lang="ts">
  215. import { DICT_TYPE, findLabelById, getDictLabel } from '@/utils/dict'
  216. import { getSafetyRiskNoticePage, deleteSafetyRiskNotice } from '@/api/elderly/apply/check-in'
  217. import { formatTime } from '@/utils'
  218. import SafetyRiskNoticeForm from './Form.vue'
  219. import AsphyxiationForm from '@/views/elderly/apply/nine-precautions/asphyxiation-by-choking/AddForm.vue'
  220. import PressureSoresForm from '@/views/elderly/apply/nine-precautions/pressure-sores/AddForm.vue'
  221. import FallDownForm from '@/views/elderly/apply/nine-precautions/fall-down/AddForm.vue'
  222. import FallPreventionForm from '@/views/elderly/apply/nine-precautions/fall-prevention-measures/AddForm.vue'
  223. import EmpyrosisForm from '@/views/elderly/apply/nine-precautions/empyrosis/AddForm.vue'
  224. import WanderAwayForm from '@/views/elderly/apply/nine-precautions/wander-away/AddForm.vue'
  225. import CommitSuicideForm from '@/views/elderly/apply/nine-precautions/commit-suicide/AddForm.vue'
  226. import MMSEForm from '@/views/elderly/apply/nine-precautions/MMSE/AddForm.vue'
  227. import AttackForm from '@/views/elderly/apply/nine-precautions/attack/AddForm.vue'
  228. import AntiEntertainmentForm from '@/views/elderly/apply/nine-precautions/anti-entertainment/AddForm.vue'
  229. import DailyLifeForm from '@/views/elderly/apply/nine-precautions/daily-life/AddForm.vue'
  230. import NutritionalRiskForm from '@/views/elderly/apply/nine-precautions/nutritional-risk/AddForm.vue'
  231. import { useUserStore } from '@/store/modules/user'
  232. import { formatToDateTime } from '@/utils/dateUtil'
  233. import { exportWithExpandedObjectArrays } from '@/utils/excel-export'
  234. const userStore = useUserStore()
  235. defineOptions({ name: 'RiskDisclosureStatement' })
  236. const message = useMessage()
  237. const { t } = useI18n()
  238. // 列表数据
  239. const loading = ref(false)
  240. const list = ref([])
  241. const total = ref(0)
  242. // 查询参数
  243. const queryParams = reactive({
  244. pageNo: 1,
  245. pageSize: 10,
  246. elderName: undefined,
  247. floorName: undefined,
  248. tenantIds: userStore.orgTenantId
  249. })
  250. const queryFormRef = ref()
  251. // 解析 riskData JSON 字符串
  252. const parseRiskData = (row: any) => {
  253. if (!row.riskData) return {}
  254. try {
  255. return typeof row.riskData === 'string' ? JSON.parse(row.riskData) : row.riskData
  256. } catch (e) {
  257. return {}
  258. }
  259. }
  260. // 获取列表
  261. const getList = async () => {
  262. loading.value = true
  263. try {
  264. const data = await getSafetyRiskNoticePage(queryParams)
  265. list.value = (data.list || []).map((item: any) => ({
  266. ...item,
  267. riskData: parseRiskData(item)
  268. }))
  269. total.value = data.total || 0
  270. } finally {
  271. loading.value = false
  272. }
  273. }
  274. // 搜索
  275. const handleQuery = () => {
  276. queryParams.pageNo = 1
  277. getList()
  278. }
  279. // 重置
  280. const resetQuery = () => {
  281. queryFormRef.value?.resetFields()
  282. handleQuery()
  283. }
  284. // 新增
  285. const formRef = ref()
  286. const handleAdd = () => {
  287. formRef.value?.open()
  288. }
  289. // 各项评估弹窗引用
  290. const asphyxiationFormRef = ref()
  291. const pressureSoresFormRef = ref()
  292. const fallDownFormRef = ref()
  293. const fallPreventionFormRef = ref()
  294. const empyrosisFormRef = ref()
  295. const wanderAwayFormRef = ref()
  296. const commitSuicideFormRef = ref()
  297. const mmseFormRef = ref()
  298. const attackFormRef = ref()
  299. const communicationFormRef = ref()
  300. const equilibriumFormRef = ref()
  301. const dailyLifeFormRef = ref()
  302. const nutritionalRiskFormRef = ref()
  303. // 各项评估点击处理方法
  304. const handleOpenAsphyxiation = (row: any) => {
  305. asphyxiationFormRef.value?.open(queryParams.tenantIds[0], row.id, true)
  306. }
  307. const handleOpenPressureUlcer = (row: any) => {
  308. pressureSoresFormRef.value?.open(queryParams.tenantIds[0], row.id, true)
  309. }
  310. const handleOpenFall = (row: any) => {
  311. fallDownFormRef.value?.open(queryParams.tenantIds[0], row.id, true)
  312. }
  313. const handleOpenBedFall = (row: any) => {
  314. fallPreventionFormRef.value?.open(queryParams.tenantIds[0], row.id, true)
  315. }
  316. const handleOpenScald = (row: any) => {
  317. empyrosisFormRef.value?.open(queryParams.tenantIds[0], row.id, true)
  318. }
  319. const handleOpenWandering = (row: any) => {
  320. wanderAwayFormRef.value?.open(queryParams.tenantIds[0], row.id, true)
  321. }
  322. const handleOpenSuicide = (row: any) => {
  323. commitSuicideFormRef.value?.open(queryParams.tenantIds[0], row.id, true)
  324. }
  325. const handleOpenMental = (row: any) => {
  326. mmseFormRef.value?.open(queryParams.tenantIds[0], row.id, true)
  327. }
  328. const handleOpenAttack = (row: any) => {
  329. attackFormRef.value?.open(queryParams.tenantIds[0], row.id, true)
  330. }
  331. const handleOpenPerception = (row: any) => {
  332. communicationFormRef.value?.open(queryParams.tenantIds[0], row.id, true)
  333. }
  334. const handleOpenBalance = (row: any) => {
  335. equilibriumFormRef.value?.open(queryParams.tenantIds[0], row.id, true)
  336. }
  337. const handleOpenDailyLiving = (row: any) => {
  338. dailyLifeFormRef.value?.open(queryParams.tenantIds[0], row.id, true)
  339. }
  340. const handleOpenNutrition = (row: any) => {
  341. nutritionalRiskFormRef.value?.open(queryParams.tenantIds[0], row.id, true)
  342. }
  343. // 获取风险等级文本
  344. const getRiskLevelText = (riskLevel: string) => {
  345. switch (riskLevel) {
  346. case 'none':
  347. return '无风险'
  348. case 'low':
  349. return '低风险'
  350. case 'medium':
  351. return '中度风险'
  352. case 'high':
  353. return '高风险'
  354. default:
  355. return '-'
  356. }
  357. }
  358. // 导出
  359. const handleExport = async () => {
  360. try {
  361. if (!list.value || (list.value as any[]).length <= 0) {
  362. message.error('暂无数据可以导出!')
  363. return
  364. }
  365. // 预处理数据:解析 riskData 中各风险字段并映射为中文
  366. const exportList = (list.value as any[]).map((row, index) => {
  367. const rd = row.riskData || {}
  368. return {
  369. index: index + 1,
  370. elderName: row.elderName || '',
  371. elderSex: getDictLabel(DICT_TYPE.SYSTEM_USER_SEX, row.elderSex) || '',
  372. elderAge: row.elderAge ?? '',
  373. bedName: row.bedName || '',
  374. contractNumber: row.contractNumber || '',
  375. nurseLevelName: row.nurseLevelName || '',
  376. asphyxiationRisk: getRiskLevelText(rd.asphyxiation?.level || '-'),
  377. pressureUlcerRisk: getRiskLevelText(rd.pressureUlcer?.level || '-'),
  378. fallRisk: getRiskLevelText(rd.fall?.level || '-'),
  379. bedFallRisk: getRiskLevelText(rd.bedFall?.level || '-'),
  380. scaldRisk: getRiskLevelText(rd.scald?.level || '-'),
  381. wanderingRisk: getRiskLevelText(rd.wandering?.level || '-'),
  382. selfHarmRisk: getRiskLevelText(rd.selfHarm?.level || '-'),
  383. foodDrugRisk: getRiskLevelText(rd.foodDrug?.level || '-'),
  384. entertainmentRisk: getRiskLevelText(rd.entertainment?.level || '-'),
  385. createTime: row.createTime ? formatToDateTime(row.createTime) : '',
  386. creator: row.creator || ''
  387. }
  388. })
  389. const headers = [
  390. { key: 'index', title: '序号' },
  391. { key: 'elderName', title: '长者姓名' },
  392. { key: 'elderSex', title: '性别' },
  393. { key: 'elderAge', title: '年龄' },
  394. { key: 'bedName', title: '床号' },
  395. { key: 'contractNumber', title: '合同号' },
  396. { key: 'nurseLevelName', title: '护理级别' },
  397. { key: 'asphyxiationRisk', title: '防噎食评估' },
  398. { key: 'pressureUlcerRisk', title: '防压疮评估' },
  399. { key: 'fallRisk', title: '防跌倒评估' },
  400. { key: 'bedFallRisk', title: '防坠床评估' },
  401. { key: 'scaldRisk', title: '防烫伤评估' },
  402. { key: 'wanderingRisk', title: '防走失评估' },
  403. { key: 'selfHarmRisk', title: '防自伤他伤评估' },
  404. { key: 'foodDrugRisk', title: '防食品药品误食评估' },
  405. { key: 'entertainmentRisk', title: '防文娱活动意外' },
  406. { key: 'createTime', title: '记录时间' },
  407. { key: 'creator', title: '记录人' }
  408. ]
  409. exportWithExpandedObjectArrays(
  410. exportList,
  411. headers,
  412. `安全风险告知书${formatToDateTime()}.xlsx`,
  413. '安全风险告知书'
  414. )
  415. } catch (error) {
  416. console.error('导出失败:', error)
  417. message.error('导出失败!')
  418. }
  419. }
  420. /**
  421. * 根据风险等级获取按钮类型(颜色)
  422. * @param riskLevel 风险等级:none(无风险)、low(低风险)、medium(中风险)、high(高风险)
  423. * @returns Element Plus 按钮类型:success(绿)、info(灰)、warning(黄)、danger(红)、primary(蓝)
  424. */
  425. const getRiskType = (riskLevel: string): string => {
  426. if (!riskLevel) return 'info'
  427. const level = riskLevel.trim().toLowerCase()
  428. if (level === 'none') return 'success'
  429. if (level === 'low') return 'info'
  430. if (level === 'medium') return 'warning'
  431. if (level === 'high') return 'danger'
  432. return 'primary'
  433. }
  434. // 编辑
  435. const handleEdit = (row: any) => {
  436. formRef.value?.open(row)
  437. }
  438. // 详情
  439. const handleView = (row: any) => {
  440. formRef.value?.open(row, true)
  441. }
  442. // 删除
  443. const handleDelete = async (row: any) => {
  444. try {
  445. await message.confirm('确认删除该记录吗?')
  446. await deleteSafetyRiskNotice(row.id)
  447. message.success('删除成功')
  448. getList()
  449. } catch (e) {
  450. // 用户取消
  451. }
  452. }
  453. // 初始化
  454. onMounted(() => {
  455. getList()
  456. })
  457. </script>
  458. <style scoped lang="scss"></style>