|
|
@@ -13,6 +13,18 @@
|
|
|
<el-form-item label="长者名称" prop="elderName">
|
|
|
<TgInput @keyup.enter="handleQuery" v-model="queryParams.elderName" class="!w-160px" />
|
|
|
</el-form-item>
|
|
|
+ <el-form-item label="检查日期" prop="checkDate">
|
|
|
+ <el-date-picker
|
|
|
+ v-model="queryParams.checkDate"
|
|
|
+ type="daterange"
|
|
|
+ range-separator="至"
|
|
|
+ start-placeholder="开始日期"
|
|
|
+ end-placeholder="结束日期"
|
|
|
+ value-format="YYYY-MM-DD"
|
|
|
+ clearable
|
|
|
+ class="!w-280px"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
<el-form-item>
|
|
|
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
|
|
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
|
|
@@ -25,9 +37,6 @@
|
|
|
<template #pre="{ scope }">
|
|
|
<el-button link type="primary" @click="openDetail(scope)">查看</el-button>
|
|
|
</template>
|
|
|
- <template #lastCheckTime="{ scope }">
|
|
|
- {{ formatBackendDateTime(scope.row.lastCheckTime, 'YYYY-MM-DD HH:mm') || '-' }}
|
|
|
- </template>
|
|
|
</Table2>
|
|
|
|
|
|
<Pagination
|
|
|
@@ -79,28 +88,22 @@
|
|
|
<template #default="scope">
|
|
|
<div class="expand-wrap">
|
|
|
<div class="expand-title">安全检查明细</div>
|
|
|
- <el-descriptions :column="2" border size="small" class="mb-10px">
|
|
|
- <el-descriptions-item label="检查时间">
|
|
|
- {{ formatBackendDateTime(scope.row.checkTime, 'YYYY-MM-DD HH:mm') || '-' }}
|
|
|
- </el-descriptions-item>
|
|
|
- <el-descriptions-item label="提交时间">
|
|
|
- {{ formatBackendDateTime(scope.row.submitAt, 'YYYY-MM-DD HH:mm') || '-' }}
|
|
|
+ <el-descriptions :column="4" border size="small" class="mb-10px" label-width="130px">
|
|
|
+ <el-descriptions-item label="被检查人签字">
|
|
|
+ {{ scope.row.beCheckSign || '-' }}
|
|
|
</el-descriptions-item>
|
|
|
<el-descriptions-item label="长者自带电器设备">
|
|
|
- {{ scope.row.elderDevices || '-' }}
|
|
|
- </el-descriptions-item>
|
|
|
- <el-descriptions-item label="被检查人签字">
|
|
|
- {{ scope.row.checkedSign || '-' }}
|
|
|
+ {{ scope.row.elderSelfEquipment || '-' }}
|
|
|
</el-descriptions-item>
|
|
|
- <el-descriptions-item label="存在问题及整改要求" :span="2">
|
|
|
- {{ scope.row.rectification || '-' }}
|
|
|
+ <el-descriptions-item label="存在问题及整改要求">
|
|
|
+ {{ scope.row.safeRemark || '-' }}
|
|
|
</el-descriptions-item>
|
|
|
- <el-descriptions-item label="其他" :span="2">
|
|
|
+ <el-descriptions-item label="其他">
|
|
|
{{ scope.row.other || '-' }}
|
|
|
</el-descriptions-item>
|
|
|
</el-descriptions>
|
|
|
|
|
|
- <el-table :data="scope.row.checklist || []" size="small" class="inner-table">
|
|
|
+ <el-table :data="scope.row.items || []" size="small" class="inner-table">
|
|
|
<el-table-column label="检查项" prop="label" min-width="220" show-overflow-tooltip />
|
|
|
<el-table-column label="结果" prop="status" width="96" align="center">
|
|
|
<template #default="s">
|
|
|
@@ -109,7 +112,6 @@
|
|
|
<span v-else>-</span>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
- <el-table-column label="问题说明/整改要求" prop="remark" min-width="220" show-overflow-tooltip />
|
|
|
</el-table>
|
|
|
</div>
|
|
|
</template>
|
|
|
@@ -121,7 +123,7 @@
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
|
|
|
- <el-table-column label="状态" prop="overallStatus" width="96" align="center">
|
|
|
+ <el-table-column label="状态" prop="overallStatus" align="center">
|
|
|
<template #default="scope">
|
|
|
<el-tag v-if="Number(scope.row.overallStatus) === 1" type="success" size="small">正常</el-tag>
|
|
|
<el-tag v-else-if="Number(scope.row.overallStatus) === 0" type="danger" size="small">异常</el-tag>
|
|
|
@@ -129,12 +131,13 @@
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
|
|
|
- <el-table-column label="异常项数" prop="abnormalCount" width="100" align="center" />
|
|
|
- <el-table-column label="整改要求" prop="rectification" min-width="180" show-overflow-tooltip />
|
|
|
- <el-table-column label="被检查人签字" prop="checkedSign" width="120" show-overflow-tooltip />
|
|
|
- <el-table-column label="提交时间" prop="submitAt" min-width="160" show-overflow-tooltip>
|
|
|
+ <el-table-column label="异常项数" prop="failCount" align="center" />
|
|
|
+ <el-table-column label="记录人" prop="recorder" show-overflow-tooltip />
|
|
|
+ <!-- <el-table-column label="问题与要求" prop="question" min-width="180" show-overflow-tooltip /> -->
|
|
|
+ <el-table-column label="被检查人签字" prop="beCheckSign" show-overflow-tooltip />
|
|
|
+ <el-table-column label="创建时间" prop="createTime" min-width="160" show-overflow-tooltip>
|
|
|
<template #default="scope">
|
|
|
- {{ formatBackendDateTime(scope.row.submitAt, 'YYYY-MM-DD HH:mm') || '-' }}
|
|
|
+ {{ formatBackendDateTime(scope.row.createTime, 'YYYY-MM-DD HH:mm') || '-' }}
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
</el-table>
|
|
|
@@ -159,6 +162,7 @@
|
|
|
import { formatBackendDateTime } from '@/utils/formatTime'
|
|
|
import dayjs from 'dayjs'
|
|
|
import { useUserStore } from '@/store/modules/user'
|
|
|
+import { getElderlySafeCheckElderPage, getElderlySafeCheckRecordPage } from '@/api/elderly/nursing'
|
|
|
|
|
|
defineOptions({ name: 'SafetyCheckLog' })
|
|
|
|
|
|
@@ -166,23 +170,6 @@ type SafetyChecklistItem = {
|
|
|
key?: string
|
|
|
label?: string
|
|
|
status?: number | string
|
|
|
- remark?: string
|
|
|
-}
|
|
|
-
|
|
|
-type SafetyCheckRecord = {
|
|
|
- batchId?: string
|
|
|
- elderId?: number | string
|
|
|
- elderName?: string
|
|
|
- bedName?: string
|
|
|
- roomName?: string
|
|
|
- checkTime?: number | string
|
|
|
- other?: string
|
|
|
- elderDevices?: string
|
|
|
- rectification?: string
|
|
|
- checkedSign?: string
|
|
|
- checklist?: SafetyChecklistItem[]
|
|
|
- submitBy?: string
|
|
|
- submitAt?: number | string
|
|
|
}
|
|
|
|
|
|
type SafetyCheckElderRow = {
|
|
|
@@ -190,13 +177,29 @@ type SafetyCheckElderRow = {
|
|
|
elderName: string
|
|
|
roomName: string
|
|
|
bedName: string
|
|
|
- recordCount: number
|
|
|
- lastCheckTime: number | string
|
|
|
}
|
|
|
|
|
|
-type SafetyCheckDetailRow = SafetyCheckRecord & {
|
|
|
+type ElderlySafeCheckRespVO = {
|
|
|
+ id?: number | string
|
|
|
+ elderId?: number | string
|
|
|
+ elderName?: string
|
|
|
+ roomName?: string
|
|
|
+ bedName?: string
|
|
|
+ safeSituation?: string
|
|
|
+ safeRemark?: string
|
|
|
+ elderSelfEquipment?: string
|
|
|
+ question?: string
|
|
|
+ beCheckSign?: string
|
|
|
+ failCount?: number | string
|
|
|
+ creator?: string
|
|
|
+ createTime?: number | string
|
|
|
+}
|
|
|
+
|
|
|
+type SafetyCheckDetailRow = ElderlySafeCheckRespVO & {
|
|
|
+ checkTime?: number | string
|
|
|
+ items?: SafetyChecklistItem[]
|
|
|
+ other?: string
|
|
|
overallStatus: 0 | 1
|
|
|
- abnormalCount: number
|
|
|
}
|
|
|
|
|
|
const userStore = useUserStore()
|
|
|
@@ -205,15 +208,14 @@ const message = useMessage()
|
|
|
const columns = reactive([
|
|
|
{ label: '长者姓名', field: 'elderName' },
|
|
|
{ label: '房间号', field: 'roomName' },
|
|
|
- { label: '床位号', field: 'bedName' },
|
|
|
- { label: '记录数', field: 'recordCount', width: 96 },
|
|
|
- { label: '最近检查时间', field: 'lastCheckTime', type: 4 }
|
|
|
+ { label: '床位号', field: 'bedName' }
|
|
|
])
|
|
|
|
|
|
const queryParams = reactive({
|
|
|
pageNo: 1,
|
|
|
pageSize: 10,
|
|
|
elderName: '',
|
|
|
+ checkDate: undefined as string[] | undefined,
|
|
|
tenantIds: userStore.orgTenantId
|
|
|
})
|
|
|
|
|
|
@@ -222,75 +224,13 @@ const total = ref(0)
|
|
|
const list = ref<SafetyCheckElderRow[]>([])
|
|
|
const queryFormRef = ref()
|
|
|
|
|
|
-function safeParseJsonArray(raw: string | null): any[] {
|
|
|
- if (!raw) return []
|
|
|
- try {
|
|
|
- const v = JSON.parse(raw)
|
|
|
- return Array.isArray(v) ? v : []
|
|
|
- } catch {
|
|
|
- return []
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-function readAllRecords(): SafetyCheckRecord[] {
|
|
|
- const fromLocal = safeParseJsonArray(localStorage.getItem('safetyCheckRecords'))
|
|
|
- if (fromLocal.length) return fromLocal as SafetyCheckRecord[]
|
|
|
- const fromSession = safeParseJsonArray(sessionStorage.getItem('safetyCheckRecords'))
|
|
|
- return fromSession as SafetyCheckRecord[]
|
|
|
-}
|
|
|
-
|
|
|
-function normalizeText(v: unknown) {
|
|
|
- return String(v ?? '').trim()
|
|
|
-}
|
|
|
-
|
|
|
-function toComparableTime(v: unknown): number {
|
|
|
- if (v == null || v === '') return 0
|
|
|
- if (typeof v === 'number' && Number.isFinite(v)) return v < 1e12 ? v * 1000 : v
|
|
|
- const s = String(v).trim()
|
|
|
- if (!s) return 0
|
|
|
- if (/^\d+$/.test(s)) {
|
|
|
- const n = Number(s)
|
|
|
- return n < 1e12 ? n * 1000 : n
|
|
|
- }
|
|
|
- const d = dayjs(s)
|
|
|
- return d.isValid() ? d.valueOf() : 0
|
|
|
-}
|
|
|
-
|
|
|
-function groupByElder(records: SafetyCheckRecord[]): SafetyCheckElderRow[] {
|
|
|
- const map = new Map<number | string, SafetyCheckElderRow>()
|
|
|
- for (const r of records) {
|
|
|
- const elderId = (r as any)?.elderId
|
|
|
- if (elderId == null || elderId === '') continue
|
|
|
- const curr = map.get(elderId)
|
|
|
- const elderName = normalizeText((r as any)?.elderName)
|
|
|
- const roomName = normalizeText((r as any)?.roomName)
|
|
|
- const bedName = normalizeText((r as any)?.bedName)
|
|
|
- const checkTime = (r as any)?.checkTime ?? (r as any)?.submitAt
|
|
|
- if (!curr) {
|
|
|
- map.set(elderId, {
|
|
|
- elderId,
|
|
|
- elderName,
|
|
|
- roomName,
|
|
|
- bedName,
|
|
|
- recordCount: 1,
|
|
|
- lastCheckTime: checkTime
|
|
|
- })
|
|
|
- continue
|
|
|
- }
|
|
|
- curr.recordCount += 1
|
|
|
- const prevMs = toComparableTime(curr.lastCheckTime)
|
|
|
- const nextMs = toComparableTime(checkTime)
|
|
|
- if (nextMs >= prevMs) curr.lastCheckTime = checkTime
|
|
|
- if (!curr.elderName && elderName) curr.elderName = elderName
|
|
|
- if (!curr.roomName && roomName) curr.roomName = roomName
|
|
|
- if (!curr.bedName && bedName) curr.bedName = bedName
|
|
|
- }
|
|
|
- return Array.from(map.values()).sort((a, b) => toComparableTime(b.lastCheckTime) - toComparableTime(a.lastCheckTime))
|
|
|
-}
|
|
|
-
|
|
|
function buildPageParams() {
|
|
|
const p = { ...queryParams } as Recordable
|
|
|
if (!p.elderName) delete p.elderName
|
|
|
+ const cd = p.checkDate as string[] | undefined
|
|
|
+ if (!Array.isArray(cd) || cd.length < 2 || !cd[0] || !cd[1]) {
|
|
|
+ delete p.checkDate
|
|
|
+ }
|
|
|
return p
|
|
|
}
|
|
|
|
|
|
@@ -307,15 +247,9 @@ const resetQuery = () => {
|
|
|
const getList = async () => {
|
|
|
loading.value = true
|
|
|
try {
|
|
|
- const p = buildPageParams()
|
|
|
- const all = groupByElder(readAllRecords())
|
|
|
- const keyword = normalizeText(p.elderName)
|
|
|
- const filtered = keyword
|
|
|
- ? all.filter((x) => normalizeText(x.elderName).includes(keyword))
|
|
|
- : all
|
|
|
- total.value = filtered.length
|
|
|
- const start = (queryParams.pageNo - 1) * queryParams.pageSize
|
|
|
- list.value = filtered.slice(start, start + queryParams.pageSize)
|
|
|
+ const data = await getElderlySafeCheckElderPage(buildPageParams())
|
|
|
+ list.value = (data?.list ?? []) as SafetyCheckElderRow[]
|
|
|
+ total.value = Number(data?.total ?? 0)
|
|
|
} finally {
|
|
|
loading.value = false
|
|
|
}
|
|
|
@@ -335,7 +269,6 @@ const detailRecords = ref<SafetyCheckDetailRow[]>([])
|
|
|
const detailPageNo = ref(1)
|
|
|
const detailPageSize = ref(10)
|
|
|
const detailTotal = ref(0)
|
|
|
-const detailAll = ref<SafetyCheckDetailRow[]>([])
|
|
|
|
|
|
function createDetailDefaultDateRange(): string[] {
|
|
|
const start = dayjs().startOf('month')
|
|
|
@@ -343,54 +276,65 @@ function createDetailDefaultDateRange(): string[] {
|
|
|
return [start.format('YYYY-MM-DD'), end.format('YYYY-MM-DD')]
|
|
|
}
|
|
|
|
|
|
-function computeOverallStatus(checklist: SafetyChecklistItem[] | undefined): { overallStatus: 0 | 1; abnormalCount: number } {
|
|
|
- const list = Array.isArray(checklist) ? checklist : []
|
|
|
- const abnormalCount = list.filter((x) => Number((x as any)?.status) === 0).length
|
|
|
- return { overallStatus: abnormalCount > 0 ? 0 : 1, abnormalCount }
|
|
|
+function safeParseJson<T>(raw: unknown): T | undefined {
|
|
|
+ if (raw == null || raw === '') return undefined
|
|
|
+ if (typeof raw === 'object') return raw as T
|
|
|
+ const s = String(raw)
|
|
|
+ try {
|
|
|
+ return JSON.parse(s) as T
|
|
|
+ } catch {
|
|
|
+ return undefined
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
-function matchesDateRange(checkTime: unknown, range: string[] | undefined): boolean {
|
|
|
- if (!Array.isArray(range) || range.length < 2 || !range[0] || !range[1]) return true
|
|
|
- const ms = toComparableTime(checkTime)
|
|
|
- if (!ms) return false
|
|
|
- const start = dayjs(range[0]).startOf('day').valueOf()
|
|
|
- const end = dayjs(range[1]).endOf('day').valueOf()
|
|
|
- return ms >= start && ms <= end
|
|
|
+function parseSafeSituation(raw: unknown): { checkTime?: number | string; items: SafetyChecklistItem[]; other?: string } {
|
|
|
+ const parsed = safeParseJson<any>(raw) ?? {}
|
|
|
+ const items = Array.isArray(parsed.items) ? (parsed.items as SafetyChecklistItem[]) : []
|
|
|
+ return { checkTime: parsed.checkTime, items, other: parsed.other }
|
|
|
}
|
|
|
|
|
|
-function syncDetailPagedSlice() {
|
|
|
- const all = detailAll.value
|
|
|
- detailTotal.value = all.length
|
|
|
- const start = (detailPageNo.value - 1) * detailPageSize.value
|
|
|
- detailRecords.value = all.slice(start, start + detailPageSize.value)
|
|
|
+function computeOverallStatusByFailCountAndItems(failCount: unknown, items: SafetyChecklistItem[]): 0 | 1 {
|
|
|
+ if (failCount !== undefined && failCount !== null && failCount !== '') {
|
|
|
+ const n = Number(failCount)
|
|
|
+ if (!Number.isNaN(n)) return n > 0 ? 0 : 1
|
|
|
+ }
|
|
|
+ return items.some((x) => Number((x as any)?.status) === 0) ? 0 : 1
|
|
|
}
|
|
|
|
|
|
async function fetchDetailList() {
|
|
|
if (detailHeader.elderId == null) return
|
|
|
detailLoading.value = true
|
|
|
try {
|
|
|
- const all = readAllRecords()
|
|
|
- .filter((r) => String((r as any)?.elderId) === String(detailHeader.elderId))
|
|
|
- .filter((r) => matchesDateRange((r as any)?.checkTime ?? (r as any)?.submitAt, detailCheckDateRange.value))
|
|
|
- .map((r) => {
|
|
|
- const computed = computeOverallStatus((r as any)?.checklist)
|
|
|
- return {
|
|
|
- ...(r as any),
|
|
|
- overallStatus: computed.overallStatus,
|
|
|
- abnormalCount: computed.abnormalCount
|
|
|
- } as SafetyCheckDetailRow
|
|
|
- })
|
|
|
- .sort((a, b) => toComparableTime((b as any)?.checkTime ?? (b as any)?.submitAt) - toComparableTime((a as any)?.checkTime ?? (a as any)?.submitAt))
|
|
|
-
|
|
|
- detailAll.value = all
|
|
|
- syncDetailPagedSlice()
|
|
|
+ const params: Recordable = {
|
|
|
+ tenantIds: queryParams.tenantIds,
|
|
|
+ elderId: detailHeader.elderId,
|
|
|
+ pageNo: detailPageNo.value,
|
|
|
+ pageSize: detailPageSize.value
|
|
|
+ }
|
|
|
+ const cd = detailCheckDateRange.value
|
|
|
+ if (Array.isArray(cd) && cd.length >= 2 && cd[0] && cd[1]) {
|
|
|
+ params.checkDate = [cd[0], cd[1]]
|
|
|
+ }
|
|
|
+ const data = await getElderlySafeCheckRecordPage(params)
|
|
|
+ detailTotal.value = Number(data?.total ?? 0)
|
|
|
+ const rawList = (data?.list ?? []) as ElderlySafeCheckRespVO[]
|
|
|
+ detailRecords.value = rawList.map((r) => {
|
|
|
+ const parsed = parseSafeSituation(r.safeSituation)
|
|
|
+ return {
|
|
|
+ ...r,
|
|
|
+ checkTime: parsed.checkTime,
|
|
|
+ items: parsed.items,
|
|
|
+ other: parsed.other,
|
|
|
+ overallStatus: computeOverallStatusByFailCountAndItems(r.failCount, parsed.items)
|
|
|
+ } as SafetyCheckDetailRow
|
|
|
+ })
|
|
|
} finally {
|
|
|
detailLoading.value = false
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function onDetailPagination() {
|
|
|
- syncDetailPagedSlice()
|
|
|
+ fetchDetailList()
|
|
|
}
|
|
|
|
|
|
function handleDetailQuery() {
|
|
|
@@ -414,7 +358,6 @@ function openDetail(row: SafetyCheckElderRow) {
|
|
|
detailPageNo.value = 1
|
|
|
detailPageSize.value = 10
|
|
|
detailCheckDateRange.value = createDetailDefaultDateRange()
|
|
|
- detailAll.value = []
|
|
|
detailRecords.value = []
|
|
|
|
|
|
detailHeader.elderId = row.elderId
|
|
|
@@ -429,7 +372,6 @@ function closeDetail() {
|
|
|
detailVisible.value = false
|
|
|
resetDetailHeader()
|
|
|
detailCheckDateRange.value = undefined
|
|
|
- detailAll.value = []
|
|
|
detailRecords.value = []
|
|
|
detailTotal.value = 0
|
|
|
detailPageNo.value = 1
|