|
|
<!-- pages/queue/index.vue -->
|
|
|
<template>
|
|
|
<view class="page-container bg-white">
|
|
|
<TopDivBar @back="goBack" />
|
|
|
|
|
|
<view class="tools-div">
|
|
|
<view class="tool-input">
|
|
|
<input
|
|
|
v-model="queryForm.name"
|
|
|
class="search-input"
|
|
|
placeholder="请输入姓名(模糊匹配)"
|
|
|
/>
|
|
|
<view class="tool-gap"></view>
|
|
|
<input
|
|
|
v-model="queryForm.idCard"
|
|
|
class="search-input"
|
|
|
placeholder="请输入身份证(模糊匹配)"
|
|
|
/>
|
|
|
<view class="tool-gap"></view>
|
|
|
<input
|
|
|
v-model="queryForm.phone"
|
|
|
class="search-input"
|
|
|
placeholder="请输入手机号(模糊匹配)"
|
|
|
/>
|
|
|
</view>
|
|
|
<view class="tools-btn">
|
|
|
<button
|
|
|
class="btn btn-primary action-btn"
|
|
|
:disabled="loading"
|
|
|
@click="handleSearch"
|
|
|
>
|
|
|
查询
|
|
|
</button>
|
|
|
<view class="tool-gap"></view>
|
|
|
<button
|
|
|
class="btn btn-default action-btn"
|
|
|
:disabled="loading"
|
|
|
@click="handleReset"
|
|
|
>
|
|
|
重置
|
|
|
</button>
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
<view class="table-wrapper">
|
|
|
<uni-table :loading="loading" border stripe emptyText="暂无更多数据">
|
|
|
<uni-tr>
|
|
|
<uni-th width="100" align="center">姓名</uni-th>
|
|
|
<uni-th width="180" align="center">身份证</uni-th>
|
|
|
<uni-th width="130" align="center">手机号</uni-th>
|
|
|
<uni-th width="100" align="center">票号</uni-th>
|
|
|
<uni-th width="200" align="center">核验结果</uni-th>
|
|
|
<uni-th width="170" align="center">核验时间</uni-th>
|
|
|
<uni-th width="100" align="center">查看照片</uni-th>
|
|
|
<uni-th width="200" align="center">操作</uni-th>
|
|
|
</uni-tr>
|
|
|
<uni-tr v-for="item in records" :key="item.uid">
|
|
|
<uni-td align="center">{{ item.name || "-" }}</uni-td>
|
|
|
<uni-td align="center">{{ maskIdCard(item.idCard) }}</uni-td>
|
|
|
<uni-td align="center">{{ maskPhone(item.phone) }}</uni-td>
|
|
|
<uni-td align="center">{{ item.ticketId || "-" }}</uni-td>
|
|
|
<uni-td align="center">
|
|
|
<text class="face-resp">{{ item.baishuiResp || "-" }}</text>
|
|
|
</uni-td>
|
|
|
<uni-td align="center">{{ item.verifyTime || "-" }}</uni-td>
|
|
|
<uni-td align="center">
|
|
|
<button
|
|
|
v-if="hasFaceImg(item.faceImg)"
|
|
|
class="btn btn-link face-photo-btn"
|
|
|
@click="openFaceImgPopup(item)"
|
|
|
>
|
|
|
查看照片
|
|
|
</button>
|
|
|
<text v-else>-</text>
|
|
|
</uni-td>
|
|
|
<uni-td align="center">
|
|
|
<view class="row-actions">
|
|
|
<button
|
|
|
class="btn btn-primary report-btn"
|
|
|
:disabled="!item.idCard || reportLoading"
|
|
|
@click="openHealthReport(item)"
|
|
|
>
|
|
|
健康报告
|
|
|
</button>
|
|
|
<button
|
|
|
class="btn btn-primary report-btn"
|
|
|
:disabled="ticketLoading"
|
|
|
@click="openTakeTicketPopup(item)"
|
|
|
>
|
|
|
取号
|
|
|
</button>
|
|
|
<button
|
|
|
v-if="SHOW_APPOINTMENT_SIGN_IN"
|
|
|
class="btn btn-default report-btn"
|
|
|
@click="openAppointmentPopup(item)"
|
|
|
>
|
|
|
预约签到
|
|
|
</button>
|
|
|
</view>
|
|
|
</uni-td>
|
|
|
</uni-tr>
|
|
|
</uni-table>
|
|
|
</view>
|
|
|
|
|
|
<view class="pagination-div">
|
|
|
<text>
|
|
|
共<span class="page-span">{{ total }}</span
|
|
|
>条数据,每页显示<span class="page-span">{{ size }}</span
|
|
|
>条
|
|
|
</text>
|
|
|
<view class="uni-pagination-box">
|
|
|
<uni-pagination
|
|
|
show-icon
|
|
|
:page-size="size"
|
|
|
:current="page"
|
|
|
:total="total"
|
|
|
@change="handlePageChange"
|
|
|
/>
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
<uni-popup ref="faceImgPopup" type="center" :is-mask-click="false">
|
|
|
<view class="face-img-popup">
|
|
|
<view class="report-header">
|
|
|
<text class="report-title">人脸照片</text>
|
|
|
<view class="report-close" @click="closeFaceImgPopup">×</view>
|
|
|
</view>
|
|
|
<view class="face-img-body">
|
|
|
<view v-if="faceImgLoading" class="report-loading"
|
|
|
>照片加载中...</view
|
|
|
>
|
|
|
<!-- H5 / Web 使用原生 img,uni-app image 对 dataUrl/Blob 支持不稳定 -->
|
|
|
<img
|
|
|
v-else-if="faceImgSrc && faceImgUseNative"
|
|
|
:src="faceImgSrc"
|
|
|
class="face-img face-img-native"
|
|
|
alt="人脸照片"
|
|
|
@error="onFaceImgError"
|
|
|
/>
|
|
|
<image
|
|
|
v-else-if="faceImgSrc"
|
|
|
:src="faceImgSrc"
|
|
|
mode="aspectFit"
|
|
|
class="face-img"
|
|
|
@error="onFaceImgError"
|
|
|
/>
|
|
|
<view v-else class="report-empty">暂无照片</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
</uni-popup>
|
|
|
|
|
|
<uni-popup ref="reportPopup" type="center" :is-mask-click="false">
|
|
|
<view class="report-popup">
|
|
|
<view class="report-header">
|
|
|
<text class="report-title">健康报告</text>
|
|
|
<view class="report-close" @click="closeReportPopup">×</view>
|
|
|
</view>
|
|
|
<view class="report-body">
|
|
|
<view v-if="reportLoading" class="report-loading">报告加载中...</view>
|
|
|
<view v-else-if="reportUrl" class="report-frame-wrap">
|
|
|
<iframe
|
|
|
:src="reportUrl"
|
|
|
class="report-frame"
|
|
|
:style="reportFrameStyle"
|
|
|
@load="updateReportFrameScale"
|
|
|
></iframe>
|
|
|
</view>
|
|
|
<view v-else class="report-empty">暂无报告地址</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
</uni-popup>
|
|
|
|
|
|
<TakeTicketPopup
|
|
|
ref="takeTicketPopupRef"
|
|
|
@success="fetchTableData"
|
|
|
@loading="(value) => (ticketLoading = value)"
|
|
|
/>
|
|
|
|
|
|
<uni-popup ref="appointmentPopup" type="center">
|
|
|
<view class="appoint-popup">
|
|
|
<view class="popup-header">
|
|
|
<text class="popup-title">预约信息</text>
|
|
|
<view class="report-close" @click="closeAppointmentPopup">×</view>
|
|
|
</view>
|
|
|
<view class="appoint-content">
|
|
|
<view class="appoint-row">
|
|
|
<text class="appoint-label">姓名</text>
|
|
|
<text class="appoint-value">{{ currentRow?.name || "-" }}</text>
|
|
|
</view>
|
|
|
<view class="appoint-row">
|
|
|
<text class="appoint-label">身份证</text>
|
|
|
<text class="appoint-value">{{
|
|
|
maskIdCard(currentRow?.idCard)
|
|
|
}}</text>
|
|
|
</view>
|
|
|
<view class="appoint-row">
|
|
|
<text class="appoint-label">手机号</text>
|
|
|
<text class="appoint-value">{{
|
|
|
maskPhone(currentRow?.phone)
|
|
|
}}</text>
|
|
|
</view>
|
|
|
<view class="appoint-row">
|
|
|
<text class="appoint-label">预约时间段</text>
|
|
|
<text class="appoint-value">{{
|
|
|
getAppointmentTimeRange(currentRow)
|
|
|
}}</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
<view class="appoint-actions">
|
|
|
<button
|
|
|
class="btn btn-primary report-btn"
|
|
|
@click="confirmAppointmentSignIn"
|
|
|
>
|
|
|
签到
|
|
|
</button>
|
|
|
</view>
|
|
|
</view>
|
|
|
</uni-popup>
|
|
|
</view>
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
import { getHealthReportUrl } from "@/api/index.js";
|
|
|
import { getFaceImage, searchTicketVerify } from "@/api/ticket.js";
|
|
|
import TakeTicketPopup from "@/components/TakeTicketPopup/TakeTicketPopup.vue";
|
|
|
import TopDivBar from "@/components/TopDivBar.vue";
|
|
|
import {
|
|
|
extractImageBase64FromResponse,
|
|
|
isWebPlatform,
|
|
|
loadFaceImageDisplaySrc,
|
|
|
} from "@/utils/faceImage.js";
|
|
|
import { onHide, onLoad, onShow, onUnload } from "@dcloudio/uni-app";
|
|
|
import { computed, getCurrentInstance, nextTick, reactive, ref } from "vue";
|
|
|
|
|
|
// 健康报告按桌面宽度渲染后再缩放,避免 iframe 内页面走移动端布局
|
|
|
const REPORT_DESKTOP_WIDTH = 1280;
|
|
|
/** 暂时隐藏预约签到入口,恢复时改为 true */
|
|
|
const SHOW_APPOINTMENT_SIGN_IN = false;
|
|
|
|
|
|
const loading = ref(false);
|
|
|
const page = ref(1);
|
|
|
const size = ref(10);
|
|
|
const total = ref(0);
|
|
|
const records = ref([]);
|
|
|
const faceImgPopup = ref(null);
|
|
|
const faceImgSrc = ref("");
|
|
|
const faceImgLoading = ref(false);
|
|
|
const faceImgRevoke = ref(null);
|
|
|
const faceImgUseNative = ref(isWebPlatform());
|
|
|
const reportPopup = ref(null);
|
|
|
const reportUrl = ref("");
|
|
|
const reportLoading = ref(false);
|
|
|
const reportFrameScale = ref(1);
|
|
|
const instance = getCurrentInstance();
|
|
|
|
|
|
const reportFrameStyle = computed(() => {
|
|
|
const scale = reportFrameScale.value || 1;
|
|
|
return {
|
|
|
width: `${REPORT_DESKTOP_WIDTH}px`,
|
|
|
height: `${100 / scale}%`,
|
|
|
transform: `scale(${scale})`,
|
|
|
transformOrigin: "0 0",
|
|
|
};
|
|
|
});
|
|
|
|
|
|
const updateReportFrameScale = () => {
|
|
|
nextTick(() => {
|
|
|
const query = uni.createSelectorQuery();
|
|
|
if (instance?.proxy) {
|
|
|
query.in(instance.proxy);
|
|
|
}
|
|
|
query
|
|
|
.select(".report-body")
|
|
|
.boundingClientRect((rect) => {
|
|
|
if (!rect?.width) return;
|
|
|
reportFrameScale.value = rect.width / REPORT_DESKTOP_WIDTH;
|
|
|
})
|
|
|
.exec();
|
|
|
});
|
|
|
};
|
|
|
const preChecked = 0;
|
|
|
const takeTicketPopupRef = ref(null);
|
|
|
const appointmentPopup = ref(null);
|
|
|
const ticketLoading = ref(false);
|
|
|
const currentRow = ref(null);
|
|
|
|
|
|
const queryForm = reactive({
|
|
|
name: "",
|
|
|
idCard: "",
|
|
|
phone: "",
|
|
|
});
|
|
|
|
|
|
const hasFaceImg = (faceImg) => Boolean(String(faceImg ?? "").trim());
|
|
|
|
|
|
const releaseFaceImgSrc = () => {
|
|
|
if (typeof faceImgRevoke.value === "function") {
|
|
|
faceImgRevoke.value();
|
|
|
}
|
|
|
faceImgRevoke.value = null;
|
|
|
faceImgSrc.value = "";
|
|
|
};
|
|
|
|
|
|
const onFaceImgError = (event) => {
|
|
|
console.log("[face-image] image 组件加载失败:", event);
|
|
|
uni.showToast({
|
|
|
title: "图片渲染失败",
|
|
|
icon: "none",
|
|
|
});
|
|
|
};
|
|
|
|
|
|
const openFaceImgPopup = async (row) => {
|
|
|
const uid = String(row?.uid ?? "").trim();
|
|
|
console.log("[face-image] 表格行 uid:", uid, "faceImg:", row?.faceImg);
|
|
|
if (!uid) {
|
|
|
uni.showToast({
|
|
|
title: "缺少记录 uid",
|
|
|
icon: "none",
|
|
|
});
|
|
|
return;
|
|
|
}
|
|
|
releaseFaceImgSrc();
|
|
|
faceImgLoading.value = true;
|
|
|
faceImgPopup.value?.open();
|
|
|
try {
|
|
|
const result = await getFaceImage(uid);
|
|
|
const base64 = extractImageBase64FromResponse(result);
|
|
|
console.log(
|
|
|
"[face-image] imageBase64 长度:",
|
|
|
base64 ? String(base64).length : 0,
|
|
|
);
|
|
|
if (!base64) {
|
|
|
uni.showToast({
|
|
|
title: "未获取到照片数据",
|
|
|
icon: "none",
|
|
|
});
|
|
|
return;
|
|
|
}
|
|
|
const { src, revoke } = await loadFaceImageDisplaySrc(base64);
|
|
|
faceImgRevoke.value = revoke;
|
|
|
faceImgUseNative.value =
|
|
|
isWebPlatform() || src.startsWith("blob:") || src.startsWith("data:");
|
|
|
await nextTick();
|
|
|
faceImgSrc.value = src;
|
|
|
await nextTick();
|
|
|
console.log(
|
|
|
"[face-image] 展示地址类型:",
|
|
|
src.startsWith("blob:")
|
|
|
? "blob"
|
|
|
: src.startsWith("data:")
|
|
|
? "dataUrl"
|
|
|
: "filePath",
|
|
|
"useNativeImg:",
|
|
|
faceImgUseNative.value,
|
|
|
);
|
|
|
} catch (error) {
|
|
|
console.log("获取人脸照片失败:", error);
|
|
|
uni.showToast({
|
|
|
title: "获取人脸照片失败",
|
|
|
icon: "none",
|
|
|
});
|
|
|
} finally {
|
|
|
faceImgLoading.value = false;
|
|
|
}
|
|
|
};
|
|
|
|
|
|
const closeFaceImgPopup = () => {
|
|
|
faceImgPopup.value?.close();
|
|
|
releaseFaceImgSrc();
|
|
|
faceImgLoading.value = false;
|
|
|
};
|
|
|
|
|
|
const maskIdCard = (value) => {
|
|
|
const text = String(value || "").trim();
|
|
|
if (!text) return "-";
|
|
|
if (text.length <= 8) return text;
|
|
|
return `${text.slice(0, 4)}********${text.slice(-4)}`;
|
|
|
};
|
|
|
|
|
|
const maskPhone = (value) => {
|
|
|
const text = String(value || "").trim();
|
|
|
if (!text) return "-";
|
|
|
if (text.length < 7) return text;
|
|
|
return `${text.slice(0, 3)}****${text.slice(-4)}`;
|
|
|
};
|
|
|
|
|
|
const formatDateYMD = (date) => {
|
|
|
const y = date.getFullYear();
|
|
|
const m = String(date.getMonth() + 1).padStart(2, "0");
|
|
|
const d = String(date.getDate()).padStart(2, "0");
|
|
|
return `${y}-${m}-${d}`;
|
|
|
};
|
|
|
|
|
|
// 固定查询当天至过去三天(含当天共 4 天)
|
|
|
const getVerifySearchDateRange = () => {
|
|
|
const endDate = new Date();
|
|
|
const startDate = new Date();
|
|
|
startDate.setDate(startDate.getDate() - 3);
|
|
|
return {
|
|
|
startDate: formatDateYMD(startDate),
|
|
|
endDate: formatDateYMD(endDate),
|
|
|
};
|
|
|
};
|
|
|
|
|
|
const fetchTableData = async () => {
|
|
|
loading.value = true;
|
|
|
try {
|
|
|
const { startDate, endDate } = getVerifySearchDateRange();
|
|
|
const result = await searchTicketVerify({
|
|
|
name: queryForm.name,
|
|
|
idCard: queryForm.idCard,
|
|
|
phone: queryForm.phone,
|
|
|
preChecked,
|
|
|
startDate,
|
|
|
endDate,
|
|
|
page: page.value,
|
|
|
size: size.value,
|
|
|
});
|
|
|
console.log("[queue-table] 查询参数:", {
|
|
|
name: queryForm.name,
|
|
|
idCard: queryForm.idCard,
|
|
|
phone: queryForm.phone,
|
|
|
preChecked,
|
|
|
startDate,
|
|
|
endDate,
|
|
|
page: page.value,
|
|
|
size: size.value,
|
|
|
});
|
|
|
console.log("[queue-table] 接口原始返回:", result);
|
|
|
records.value = Array.isArray(result.records) ? result.records : [];
|
|
|
total.value = Number(result.total) || 0;
|
|
|
console.log("[queue-table] 表格数据 records:", records.value);
|
|
|
console.log("[queue-table] 总条数 total:", total.value);
|
|
|
} catch (error) {
|
|
|
console.log("实名核验查询失败:", error);
|
|
|
} finally {
|
|
|
loading.value = false;
|
|
|
}
|
|
|
};
|
|
|
|
|
|
const handleSearch = () => {
|
|
|
page.value = 1;
|
|
|
fetchTableData();
|
|
|
};
|
|
|
|
|
|
const handleReset = () => {
|
|
|
queryForm.name = "";
|
|
|
queryForm.idCard = "";
|
|
|
queryForm.phone = "";
|
|
|
page.value = 1;
|
|
|
total.value = 0;
|
|
|
records.value = [];
|
|
|
};
|
|
|
|
|
|
const handlePageChange = (event) => {
|
|
|
page.value = event.current;
|
|
|
fetchTableData();
|
|
|
};
|
|
|
|
|
|
const openHealthReport = async (row) => {
|
|
|
if (!row?.idCard) {
|
|
|
uni.showToast({
|
|
|
title: "身份证号为空",
|
|
|
icon: "none",
|
|
|
});
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
reportLoading.value = true;
|
|
|
reportUrl.value = "";
|
|
|
reportFrameScale.value = 1;
|
|
|
reportPopup.value?.open();
|
|
|
nextTick(() => {
|
|
|
updateReportFrameScale();
|
|
|
});
|
|
|
try {
|
|
|
const result = await getHealthReportUrl({
|
|
|
id: row.idCard,
|
|
|
});
|
|
|
reportUrl.value = result?.url || "";
|
|
|
if (!reportUrl.value) {
|
|
|
uni.showToast({
|
|
|
title: "未获取到报告地址",
|
|
|
icon: "none",
|
|
|
});
|
|
|
} else {
|
|
|
nextTick(() => {
|
|
|
updateReportFrameScale();
|
|
|
});
|
|
|
}
|
|
|
} catch (error) {
|
|
|
console.log("获取健康报告失败:", error);
|
|
|
uni.showToast({
|
|
|
title: "获取健康报告失败",
|
|
|
icon: "none",
|
|
|
});
|
|
|
} finally {
|
|
|
reportLoading.value = false;
|
|
|
}
|
|
|
};
|
|
|
|
|
|
const closeReportPopup = () => {
|
|
|
reportPopup.value?.close();
|
|
|
reportUrl.value = "";
|
|
|
reportFrameScale.value = 1;
|
|
|
};
|
|
|
|
|
|
const openTakeTicketPopup = (row) => {
|
|
|
takeTicketPopupRef.value?.openWithRow(row);
|
|
|
};
|
|
|
|
|
|
const getAppointmentTimeRange = (row) => {
|
|
|
if (!row) return "-";
|
|
|
return (
|
|
|
row.appointmentTimeRange ||
|
|
|
row.appointmentTimeSlot ||
|
|
|
row.appointmentRange ||
|
|
|
"暂无预约时间段"
|
|
|
);
|
|
|
};
|
|
|
|
|
|
const openAppointmentPopup = (row) => {
|
|
|
currentRow.value = row;
|
|
|
appointmentPopup.value?.open();
|
|
|
};
|
|
|
|
|
|
const closeAppointmentPopup = () => {
|
|
|
appointmentPopup.value?.close();
|
|
|
};
|
|
|
|
|
|
const confirmAppointmentSignIn = () => {
|
|
|
if (!currentRow.value) return;
|
|
|
uni.showModal({
|
|
|
title: "确认签到",
|
|
|
content: `确认对 ${currentRow.value.name || "该用户"} 执行预约签到吗?`,
|
|
|
success: (res) => {
|
|
|
if (!res.confirm) return;
|
|
|
uni.showToast({
|
|
|
title: "签到成功",
|
|
|
icon: "success",
|
|
|
});
|
|
|
closeAppointmentPopup();
|
|
|
},
|
|
|
});
|
|
|
};
|
|
|
|
|
|
const goBack = () => {
|
|
|
uni.navigateBack();
|
|
|
};
|
|
|
|
|
|
let refreshTimer = null;
|
|
|
|
|
|
const startRefreshTimer = () => {
|
|
|
stopRefreshTimer();
|
|
|
refreshTimer = setInterval(() => {
|
|
|
fetchTableData();
|
|
|
}, 15000);
|
|
|
};
|
|
|
|
|
|
const stopRefreshTimer = () => {
|
|
|
if (refreshTimer) {
|
|
|
clearInterval(refreshTimer);
|
|
|
refreshTimer = null;
|
|
|
}
|
|
|
};
|
|
|
|
|
|
onLoad(() => {
|
|
|
if (process.env.NODE_ENV === "development") {
|
|
|
queryForm.name = "";
|
|
|
}
|
|
|
});
|
|
|
|
|
|
onShow(() => {
|
|
|
fetchTableData();
|
|
|
startRefreshTimer();
|
|
|
});
|
|
|
|
|
|
onHide(() => {
|
|
|
stopRefreshTimer();
|
|
|
});
|
|
|
|
|
|
onUnload(() => {
|
|
|
stopRefreshTimer();
|
|
|
});
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
|
.page-container {
|
|
|
min-height: 100vh;
|
|
|
padding-top: calc(6vh + 64px);
|
|
|
}
|
|
|
|
|
|
.bg-white {
|
|
|
background-color: #fff;
|
|
|
}
|
|
|
|
|
|
.tools-div {
|
|
|
display: flex;
|
|
|
justify-content: space-between;
|
|
|
align-items: center;
|
|
|
padding: 10px 20px;
|
|
|
}
|
|
|
|
|
|
.tool-input {
|
|
|
display: flex;
|
|
|
flex: 1;
|
|
|
min-width: 0;
|
|
|
align-items: center;
|
|
|
}
|
|
|
|
|
|
.tools-btn {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
margin-left: 14rpx;
|
|
|
}
|
|
|
|
|
|
.tool-gap {
|
|
|
width: 12px;
|
|
|
}
|
|
|
|
|
|
.search-input {
|
|
|
flex: 1;
|
|
|
min-width: 0;
|
|
|
border: 1px solid #d1d5db;
|
|
|
border-radius: 6rpx;
|
|
|
background: #fff;
|
|
|
padding: 0 12px;
|
|
|
font-size: 14px;
|
|
|
height: 36px;
|
|
|
box-sizing: border-box;
|
|
|
}
|
|
|
|
|
|
.btn {
|
|
|
min-width: 84px;
|
|
|
height: 32px;
|
|
|
border-radius: 4px;
|
|
|
font-size: 14px;
|
|
|
border: none;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
}
|
|
|
|
|
|
.action-btn {
|
|
|
width: 84px;
|
|
|
}
|
|
|
|
|
|
.report-btn {
|
|
|
width: 84px;
|
|
|
min-width: 84px;
|
|
|
height: 28px;
|
|
|
font-size: 12px;
|
|
|
}
|
|
|
|
|
|
.btn-primary {
|
|
|
background: #2563eb;
|
|
|
color: #fff;
|
|
|
}
|
|
|
|
|
|
.btn-default {
|
|
|
background: #e5e7eb;
|
|
|
color: #111827;
|
|
|
}
|
|
|
|
|
|
.table-wrapper {
|
|
|
padding: 0 20px;
|
|
|
}
|
|
|
|
|
|
.pagination-div {
|
|
|
display: flex;
|
|
|
justify-content: space-between;
|
|
|
padding: 10px 20px;
|
|
|
font-size: 14px;
|
|
|
}
|
|
|
|
|
|
.page-span {
|
|
|
color: #0099ff;
|
|
|
margin: 0 4px;
|
|
|
}
|
|
|
|
|
|
.uni-pagination-box {
|
|
|
display: flex;
|
|
|
justify-content: flex-end;
|
|
|
}
|
|
|
|
|
|
.report-popup {
|
|
|
width: 96vw;
|
|
|
max-width: none;
|
|
|
height: 92vh;
|
|
|
background: #fff;
|
|
|
border-radius: 12px;
|
|
|
overflow: hidden;
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
}
|
|
|
|
|
|
.report-header {
|
|
|
height: 48px;
|
|
|
padding: 0 14px;
|
|
|
border-bottom: 1px solid #eee;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: space-between;
|
|
|
}
|
|
|
|
|
|
.report-title {
|
|
|
font-size: 16px;
|
|
|
color: #111827;
|
|
|
font-weight: 600;
|
|
|
}
|
|
|
|
|
|
.report-close {
|
|
|
width: 28px;
|
|
|
height: 28px;
|
|
|
line-height: 28px;
|
|
|
text-align: center;
|
|
|
border-radius: 50%;
|
|
|
background: #f3f4f6;
|
|
|
color: #6b7280;
|
|
|
font-size: 18px;
|
|
|
}
|
|
|
|
|
|
.report-body {
|
|
|
flex: 1;
|
|
|
min-height: 0;
|
|
|
background: #fafafa;
|
|
|
overflow: hidden;
|
|
|
}
|
|
|
|
|
|
.report-frame-wrap {
|
|
|
width: 100%;
|
|
|
height: 100%;
|
|
|
overflow: hidden;
|
|
|
background: #fff;
|
|
|
}
|
|
|
|
|
|
.report-frame {
|
|
|
border: none;
|
|
|
background: #fff;
|
|
|
display: block;
|
|
|
}
|
|
|
|
|
|
.report-loading,
|
|
|
.report-empty {
|
|
|
height: 100%;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
color: #6b7280;
|
|
|
font-size: 14px;
|
|
|
}
|
|
|
|
|
|
.face-resp {
|
|
|
font-size: 13px;
|
|
|
color: #374151;
|
|
|
line-height: 1.4;
|
|
|
}
|
|
|
|
|
|
.face-photo-btn {
|
|
|
padding: 0;
|
|
|
min-width: auto;
|
|
|
height: auto;
|
|
|
line-height: 1.2;
|
|
|
font-size: 13px;
|
|
|
color: #2563eb;
|
|
|
background: transparent;
|
|
|
border: none;
|
|
|
flex-shrink: 0;
|
|
|
white-space: nowrap;
|
|
|
}
|
|
|
|
|
|
.face-img-popup {
|
|
|
width: 720rpx;
|
|
|
max-width: 92vw;
|
|
|
background: #fff;
|
|
|
border-radius: 12px;
|
|
|
overflow: hidden;
|
|
|
}
|
|
|
|
|
|
.face-img-body {
|
|
|
padding: 12px;
|
|
|
min-height: 320px;
|
|
|
max-height: 70vh;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
background: #f9fafb;
|
|
|
}
|
|
|
|
|
|
.face-img {
|
|
|
width: 100%;
|
|
|
max-height: 65vh;
|
|
|
}
|
|
|
|
|
|
.face-img-native {
|
|
|
display: block;
|
|
|
object-fit: contain;
|
|
|
min-height: 200px;
|
|
|
}
|
|
|
|
|
|
.row-actions {
|
|
|
display: flex;
|
|
|
gap: 6px;
|
|
|
justify-content: center;
|
|
|
}
|
|
|
|
|
|
.appoint-popup {
|
|
|
width: 680rpx;
|
|
|
max-width: 90vw;
|
|
|
background: #fff;
|
|
|
border-radius: 12px;
|
|
|
overflow: hidden;
|
|
|
}
|
|
|
|
|
|
.popup-header {
|
|
|
height: 48px;
|
|
|
padding: 0 14px;
|
|
|
border-bottom: 1px solid #eee;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: space-between;
|
|
|
}
|
|
|
|
|
|
.popup-title {
|
|
|
font-size: 15px;
|
|
|
color: #111827;
|
|
|
font-weight: 600;
|
|
|
}
|
|
|
|
|
|
.appoint-content {
|
|
|
padding: 14px;
|
|
|
}
|
|
|
|
|
|
.appoint-row {
|
|
|
display: flex;
|
|
|
justify-content: space-between;
|
|
|
gap: 12px;
|
|
|
padding: 10px 0;
|
|
|
border-bottom: 1px solid #f1f5f9;
|
|
|
}
|
|
|
|
|
|
.appoint-label {
|
|
|
color: #6b7280;
|
|
|
font-size: 14px;
|
|
|
}
|
|
|
|
|
|
.appoint-value {
|
|
|
color: #111827;
|
|
|
font-size: 14px;
|
|
|
text-align: right;
|
|
|
}
|
|
|
|
|
|
.appoint-actions {
|
|
|
padding: 12px 14px 14px;
|
|
|
display: flex;
|
|
|
justify-content: flex-end;
|
|
|
}
|
|
|
|
|
|
@media (max-width: 900px) {
|
|
|
.tools-div {
|
|
|
flex-direction: column;
|
|
|
align-items: stretch;
|
|
|
gap: 10px;
|
|
|
}
|
|
|
|
|
|
.tool-input {
|
|
|
flex-wrap: wrap;
|
|
|
}
|
|
|
|
|
|
.tool-gap {
|
|
|
display: none;
|
|
|
}
|
|
|
|
|
|
.search-input {
|
|
|
flex: none;
|
|
|
width: 100%;
|
|
|
margin-bottom: 8px;
|
|
|
}
|
|
|
|
|
|
.tools-btn {
|
|
|
margin-left: 0;
|
|
|
justify-content: flex-end;
|
|
|
}
|
|
|
}
|
|
|
</style>
|