| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408 |
- <template>
- <ContentWrap>
- <el-form
- class="-mb-15px"
- :model="queryParams"
- ref="queryFormRef"
- :inline="true"
- label-width="80px"
- >
- <el-form-item prop="tenantIds">
- <TenantSelect v-model="queryParams.tenantIds" placeholder="请选择机构名称" prop="tenantIds" />
- </el-form-item>
- <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>
- </el-form-item>
- </el-form>
- </ContentWrap>
- <ContentWrap>
- <Table2 v-loading="loading" :list="list" :columns="columns" :queryParams="queryParams">
- <template #pre="{ scope }">
- <el-button link type="primary" @click="openDetail(scope)">查看</el-button>
- </template>
- </Table2>
- <Pagination
- :total="total"
- v-model:page="queryParams.pageNo"
- v-model:limit="queryParams.pageSize"
- @pagination="getList"
- />
- <Dialog
- v-model="detailVisible"
- width="90%"
- title="安全检查日志详情"
- class="form-tag-dialog"
- scroll
- @close="closeDetail"
- >
- <div v-loading="detailLoading">
- <div class="info-title">长者信息</div>
- <div class="info-wrap mb-15px">
- <el-row :gutter="20">
- <el-col :span="8" :xs="24" class="header-item">长者姓名:{{ detailHeader.elderName || '-' }}</el-col>
- <el-col :span="8" :xs="24" class="header-item">房间号:{{ detailHeader.roomName || '-' }}</el-col>
- <el-col :span="8" :xs="24" class="header-item">床位号:{{ detailHeader.bedName || '-' }}</el-col>
- </el-row>
- </div>
- <div class="info-title">历史记录查询</div>
- <el-form class="detail-query-form mb-15px" :inline="true" @submit.prevent>
- <el-form-item label="检查日期">
- <el-date-picker
- v-model="detailCheckDateRange"
- 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 type="primary" :loading="detailLoading" @click="handleDetailQuery">查询</el-button>
- </el-form-item>
- </el-form>
- <el-table :data="detailRecords" max-height="58vh">
- <el-table-column type="expand">
- <template #default="scope">
- <div class="expand-wrap">
- <div class="expand-title">安全检查明细</div>
- <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.elderSelfEquipment || '-' }}
- </el-descriptions-item>
- <el-descriptions-item label="存在问题及整改要求">
- {{ scope.row.safeRemark || '-' }}
- </el-descriptions-item>
- <el-descriptions-item label="其他">
- {{ scope.row.other || '-' }}
- </el-descriptions-item>
- </el-descriptions>
- <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">
- <el-tag v-if="Number(s.row.status) === 1" type="success" size="small">无问题</el-tag>
- <el-tag v-else-if="Number(s.row.status) === 0" type="danger" size="small">有问题</el-tag>
- <span v-else>-</span>
- </template>
- </el-table-column>
- </el-table>
- </div>
- </template>
- </el-table-column>
- <el-table-column label="检查时间" prop="checkTime" min-width="160" show-overflow-tooltip>
- <template #default="scope">
- {{ formatBackendDateTime(scope.row.checkTime, 'YYYY-MM-DD HH:mm') || '-' }}
- </template>
- </el-table-column>
- <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>
- <span v-else>-</span>
- </template>
- </el-table-column>
- <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.createTime, 'YYYY-MM-DD HH:mm') || '-' }}
- </template>
- </el-table-column>
- </el-table>
- <div v-if="detailTotal > 0" class="flex justify-end">
- <Pagination
- :total="detailTotal"
- v-model:page="detailPageNo"
- v-model:limit="detailPageSize"
- @pagination="onDetailPagination"
- />
- </div>
- </div>
- <template #footer>
- <el-button @click="closeDetail">关闭</el-button>
- </template>
- </Dialog>
- </ContentWrap>
- </template>
- <script setup lang="ts">
- 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' })
- type SafetyChecklistItem = {
- key?: string
- label?: string
- status?: number | string
- }
- type SafetyCheckElderRow = {
- elderId: number | string
- elderName: string
- roomName: string
- bedName: string
- }
- 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
- }
- const userStore = useUserStore()
- const message = useMessage()
- const columns = reactive([
- { label: '长者姓名', field: 'elderName' },
- { label: '房间号', field: 'roomName' },
- { label: '床位号', field: 'bedName' }
- ])
- const queryParams = reactive({
- pageNo: 1,
- pageSize: 10,
- elderName: '',
- checkDate: undefined as string[] | undefined,
- tenantIds: userStore.orgTenantId
- })
- const loading = ref(true)
- const total = ref(0)
- const list = ref<SafetyCheckElderRow[]>([])
- const queryFormRef = ref()
- 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
- }
- const handleQuery = () => {
- queryParams.pageNo = 1
- getList()
- }
- const resetQuery = () => {
- queryFormRef.value?.resetFields?.()
- handleQuery()
- }
- const getList = async () => {
- loading.value = true
- try {
- const data = await getElderlySafeCheckElderPage(buildPageParams())
- list.value = (data?.list ?? []) as SafetyCheckElderRow[]
- total.value = Number(data?.total ?? 0)
- } finally {
- loading.value = false
- }
- }
- const detailVisible = ref(false)
- const detailLoading = ref(false)
- const detailHeader = reactive({
- elderId: undefined as number | string | undefined,
- elderName: '',
- roomName: '',
- bedName: ''
- })
- const detailCheckDateRange = ref<string[] | undefined>(undefined)
- const detailRecords = ref<SafetyCheckDetailRow[]>([])
- const detailPageNo = ref(1)
- const detailPageSize = ref(10)
- const detailTotal = ref(0)
- function createDetailDefaultDateRange(): string[] {
- const start = dayjs().startOf('month')
- const end = dayjs().endOf('month')
- return [start.format('YYYY-MM-DD'), end.format('YYYY-MM-DD')]
- }
- 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 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 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 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() {
- fetchDetailList()
- }
- function handleDetailQuery() {
- detailPageNo.value = 1
- fetchDetailList()
- }
- function resetDetailHeader() {
- detailHeader.elderId = undefined
- detailHeader.elderName = ''
- detailHeader.roomName = ''
- detailHeader.bedName = ''
- }
- function openDetail(row: SafetyCheckElderRow) {
- if (!row?.elderId && row?.elderId !== 0) {
- message.warning('该行缺少长者 id,无法查询详情')
- return
- }
- detailVisible.value = true
- detailPageNo.value = 1
- detailPageSize.value = 10
- detailCheckDateRange.value = createDetailDefaultDateRange()
- detailRecords.value = []
- detailHeader.elderId = row.elderId
- detailHeader.elderName = row.elderName || ''
- detailHeader.roomName = row.roomName || ''
- detailHeader.bedName = row.bedName || ''
- fetchDetailList()
- }
- function closeDetail() {
- detailVisible.value = false
- resetDetailHeader()
- detailCheckDateRange.value = undefined
- detailRecords.value = []
- detailTotal.value = 0
- detailPageNo.value = 1
- detailPageSize.value = 10
- }
- onMounted(() => {
- getList()
- })
- </script>
- <style lang="scss" scoped>
- .info-title {
- margin-bottom: 8px;
- font-weight: 600;
- color: var(--el-text-color-primary);
- }
- .header-item {
- margin-bottom: 6px;
- font-size: 14px;
- color: var(--el-text-color-regular);
- }
- .expand-wrap {
- padding: 10px 10px 6px;
- }
- .expand-title {
- margin-bottom: 8px;
- font-weight: 600;
- color: var(--el-text-color-primary);
- }
- .inner-table {
- width: 100%;
- }
- </style>
|