Files
gao-customer-h5/src/pages/event/EventRecordList.vue
T
2026-08-30 01:40:51 +08:00

944 lines
25 KiB
Vue

<template>
<div v-can="permissionCodes.eventRecord.read" class="registration-record-list">
<div class="search-bar">
<t-input
v-model.trim="keyword"
class="search-input"
type="search"
borderless
placeholder="搜索客户、编码、事件、备注、登记人"
/>
<div class="filter-line">
<t-dropdown-menu class="filter-menu">
<t-dropdown-item
v-model="dateFilterType"
:label="dateFilterLabel"
:options="dateOptions"
@change="selectDateFilter"
/>
<t-dropdown-item
v-model="actionFilter"
:label="eventFilterLabel"
options-columns="2"
:options="actionOptions"
multiple
/>
<t-dropdown-item
v-model="verificationFilter"
:label="verificationFilterLabel"
:options="verificationOptions"
/>
</t-dropdown-menu>
</div>
</div>
<t-pull-down-refresh
v-model="isRefreshing"
:refresh-timeout="30000"
@refresh="reloadRecords"
@scrolltolower="loadRecords"
>
<div class="list-wrapper">
<t-cell-group v-entrance-transition class="summary" theme="card">
<t-cell
title="开始时间"
:note="dateRange[0] || '-'"
arrow
@click="openStartTime"
/>
<t-cell
title="结束时间"
:note="dateRange[1] || '-'"
arrow
@click="openEndTime"
/>
<t-cell title="事件" :note="eventFilterLabel" />
<t-cell title="数据量" :note="`共 ${pageResult.total} 条记录`" />
<t-cell v-if="summaryNumberValueText" title="数值合计" :note="summaryNumberValueText" />
</t-cell-group>
<t-cell-group
v-if="pageResult.list.length"
class="record-list"
theme="card"
>
<t-swipe-cell
v-entrance-transition="index * 30"
v-for="(item, index) in pageResult.list"
:key="item.id"
class="item"
>
<t-cell
class="cell"
:title="getRecordEventName(item)"
:note="getRecordRegisteredAt(item)"
arrow
hover
@click="toDetail(item.id || '')"
>
<template #description>
<div class="description-line">
<t-tag
v-if="item.verificationStatus && item.verificationStatus !== 'NOT_REQUIRED'"
:theme="getVerificationTag(item).theme"
variant="light-outline"
:content="getVerificationTag(item).text"
/>
<span v-text="getRecordCustomerName(item)" />
</div>
</template>
<template #leftIcon>
<t-image
v-if="getRecordPhotoURL(item)"
:src="getRecordPhotoURL(item)"
:alt="getRecordEventName(item)"
fit="cover"
class="avatar"
/>
<div v-else class="avatar fallback" v-text="getRecordCustomerInitial(item)" />
</template>
</t-cell>
<template v-if="canVerifyRecord || canDeleteRecord" #right>
<div class="actions">
<t-button
v-if="canVerifyRecord && item.verificationStatus === 'PENDING'"
class="action verify bg-orange white"
variant="text"
:loading="(isVerifying && verifyingId === item.id) || (isLoadingVerifyRecord && verifyLoadingId === item.id)"
@click.stop="confirmVerifyRecord(item)"
>
核销
</t-button>
<t-button
v-if="canDeleteRecord"
class="action delete"
theme="danger"
:loading="isDeleting && deletingId === item.id"
@click.stop="confirmRemoveRecord(item)"
>
删除
</t-button>
</div>
</template>
</t-swipe-cell>
</t-cell-group>
<div v-else class="load-state" v-text="isLoading ? '正在加载登记事件记录' : '暂无登记事件记录'" />
<div v-if="pageResult.list.length" class="load-state" v-text="loadStateText" />
</div>
</t-pull-down-refresh>
</div>
<event-record-verify-dialog
v-model:visible="verifyDialogVisible"
:record="verifyTarget"
:can-update="canUpdateRecord"
:submit="submitVerifyRecord"
@verified="handleVerifySuccess"
/>
<nav-bar-action-menu v-if="canShowActionMenu" :actions="actionList" />
<t-popup v-model="startVisible" placement="bottom">
<t-date-time-picker
v-if="startVisible"
v-model="startDraft"
format="YYYY-MM-DD HH:mm"
:mode="['date', 'minute']"
title="开始时间"
:default-value="dateRange[0]"
@cancel="cancelStartTime"
@confirm="confirmStartTime"
/>
</t-popup>
<t-popup v-model="endVisible" placement="bottom">
<t-date-time-picker
v-if="endVisible"
v-model="endDraft"
:mode="['date', 'minute']"
format="YYYY-MM-DD HH:mm"
title="结束时间"
:default-value="dateRange[1]"
@cancel="cancelEndTime"
@confirm="confirmEndTime"
/>
</t-popup>
</template>
<script setup lang="ts">
import { DialogPlugin, type DropdownValue } from "tdesign-mobile-vue";
import { Time, WebToolkit } from "timi-web";
import { useUserStore, type Attachment } from "timi-web-api";
import EventAPI from "@/api/EventAPI";
import EventRecordAPI from "@/api/EventRecordAPI";
import EventRecordVerifyDialog from "@/components/EventRecordVerifyDialog.vue";
import { useNavBarStore } from "@/store/navBarStore";
import type { EventItem } from "@/types/EventItem";
import type { EventRecord, EventRecordListItemView, EventRecordPage, EventRecordPageResult, VerificationStatus } from "@/types/EventRecord";
import type { NavBarAction } from "@/types/NavBarAction";
import { getCustomerPhotoURL } from "@/utils/customer";
import { getErrorMessage } from "@/utils/error";
import { formatEventNumberValue } from "@/utils/eventValue";
import { permissionCodes } from "@/utils/permission";
import { Message } from "@/utils/message.ts";
type DateFilterType = "today" | "yesterday" | "beforeYesterday" | "lastWeek" | "all" | "custom";
type VerificationFilter = "ALL" | VerificationStatus;
defineOptions({
name: "EventRecordList"
});
const pageSize = 20;
const route = useRoute();
const router = useRouter();
const navBarStore = useNavBarStore();
const userStore = useUserStore();
const dateOptions: Array<{ label: string; value: DateFilterType }> = [
{ label: "今天", value: "today" },
{ label: "昨天", value: "yesterday" },
{ label: "前天", value: "beforeYesterday" },
{ label: "上周", value: "lastWeek" },
{ label: "全部", value: "all" },
{ label: "自定义范围", value: "custom" }
];
// ---------- 筛选 ----------
const keyword = ref(getInitialKeyword());
const customerId = ref(getQueryText("customerId"));
const dateFilterType = ref<DateFilterType>(parseDateFilterType(route.query.dateType));
const dateRange = ref(parseDateRange(dateFilterType.value));
const startDraft = ref("");
const endDraft = ref("");
const actionFilter = ref<string[]>([]);
const verificationOptions: Array<{ label: string; value: VerificationFilter }> = [
{ label: "全部核销状态", value: "ALL" },
{ label: "待核销", value: "PENDING" },
{ label: "已核销", value: "VERIFIED" },
{ label: "无需核销", value: "NOT_REQUIRED" }
];
const verificationFilter = ref<VerificationFilter>(parseVerificationFilter(route.query.verificationStatus));
const eventList = ref<EventItem[]>([]);
const startVisible = ref(false);
const endVisible = ref(false);
let keywordTimer: number | undefined;
let initialized = false;
const isExporting = ref(false);
const canCreateRecord = computed(() => userStore.hasPermissions(permissionCodes.eventRecord.create));
const dateFilterLabel = computed(() => dateOptions.find(item => item.value === dateFilterType.value)?.label || "今天");
const actionOptions = computed(() => eventList.value.map(item => ({
label: item.name || "未命名事件",
value: item.id || ""
})).filter(item => item.value));
const eventFilterLabel = computed(() => {
if (!actionFilter.value.length || actionFilter.value.length === eventList.value.length) {
return "全部事件";
}
return eventList.value
.filter(item => item.id && actionFilter.value.includes(item.id))
.map(item => item.name || "未命名事件")
.join("、");
});
const verificationFilterLabel = computed(() => verificationOptions.find(item => item.value === verificationFilter.value)?.label || "全部核销状态");
const selectedNumberEvent = computed(() => {
if (actionFilter.value.length !== 1) {
return undefined;
}
const event = eventList.value.find(item => item.id === actionFilter.value[0]);
return event?.valueType === "NUMBER" ? event : undefined;
});
const canExportRecord = computed(() => userStore.hasPermissions(permissionCodes.eventRecord.export));
const canDeleteRecord = computed(() => userStore.hasPermissions(permissionCodes.eventRecord.delete));
const canVerifyRecord = computed(() => userStore.hasPermissions(permissionCodes.eventRecord.verify));
const canUpdateRecord = computed(() => userStore.hasPermissions(permissionCodes.eventRecord.update));
const canShowActionMenu = computed(() => canCreateRecord.value || canExportRecord.value);
const actionList = computed<NavBarAction[]>(() => {
const list: NavBarAction[] = [];
if (canCreateRecord.value) {
list.push({
action: toCreate,
theme: "primary",
icon: "add",
key: "create",
text: "添加"
});
}
if (canExportRecord.value) {
list.push({
action: exportExcel,
icon: "download",
key: "export",
loading: isExporting.value,
text: "导出 Excel"
});
}
return list;
});
watch(keyword, () => {
if (!initialized) {
return;
}
window.clearTimeout(keywordTimer);
keywordTimer = window.setTimeout(async () => {
await replaceFilterQuery();
await reloadRecords();
}, 300);
});
watch(actionFilter, async () => {
if (!initialized) {
return;
}
await replaceFilterQuery();
await reloadRecords();
});
watch(verificationFilter, async () => {
if (!initialized) {
return;
}
await replaceFilterQuery();
await reloadRecords();
});
watch(dateRange, async (value) => {
if (!initialized || dateFilterType.value !== "custom") {
return;
}
if (value[0] && value[1] && value[1] < value[0]) {
dateRange.value = [value[1], value[0]];
return;
}
await replaceFilterQuery();
await reloadRecords();
}, { deep: true });
// ---------- 列表分页 ----------
const isRefreshing = ref(false);
const isLoading = ref(false);
const isNoMore = ref(false);
const page = reactive<EventRecordPage>({
index: 0,
size: pageSize
});
const pageResult = reactive<EventRecordPageResult>({
total: 0,
list: [],
numberValueTotal: null
});
let loadToken = 0;
const summaryNumberValueText = computed(() => {
if (!selectedNumberEvent.value || pageResult.numberValueTotal === undefined || pageResult.numberValueTotal === null) {
return "";
}
return `${formatEventNumberValue(selectedNumberEvent.value, pageResult.numberValueTotal)}`;
});
const loadStateText = computed(() => {
if (isLoading.value) {
return "正在加载更多";
}
return isNoMore.value ? "没有更多记录" : "上拉加载更多";
});
async function reloadRecords(): Promise<void> {
isRefreshing.value = true;
try {
page.index = 0;
pageResult.total = 0;
pageResult.list = [];
pageResult.numberValueTotal = null;
isNoMore.value = false;
await loadRecords();
} finally {
isRefreshing.value = false;
}
}
async function loadRecords(): Promise<void> {
if (isLoading.value || isNoMore.value) {
return;
}
const token = loadToken + 1;
loadToken = token;
isLoading.value = true;
try {
const result = await EventRecordAPI.list({
index: page.index,
size: page.size,
...buildRangeReq()
});
if (token !== loadToken) {
return;
}
pageResult.list = pageResult.list.concat(result.list || []);
pageResult.total = result.total || 0;
pageResult.numberValueTotal = result.numberValueTotal ?? null;
page.index += 1;
isNoMore.value = pageResult.list.length >= pageResult.total || (result.list || []).length < page.size;
} finally {
if (token === loadToken) {
isLoading.value = false;
}
}
}
// ---------- 操作 ----------
const isDeleting = ref(false);
const deletingId = ref("");
const isVerifying = ref(false);
const verifyingId = ref("");
const isLoadingVerifyRecord = ref(false);
const verifyLoadingId = ref("");
const verifyDialogVisible = ref(false);
const verifyTarget = ref<EventRecord>();
async function loadActions(): Promise<void> {
eventList.value = await EventAPI.listValid();
const eventIdList = parseEventIdList(route.query.eventIds);
actionFilter.value = eventIdList.length
? eventIdList
: eventList.value.map(item => item.id).filter((item): item is string => Boolean(item));
}
async function selectDateFilter(type: DropdownValue): Promise<void> {
if (Array.isArray(type)) {
return;
}
dateFilterType.value = String(type) as DateFilterType;
if (dateFilterType.value !== "custom") {
dateRange.value = buildPresetRange(dateFilterType.value);
await replaceFilterQuery();
await reloadRecords();
} else {
await replaceFilterQuery();
}
}
function openStartTime(): void {
startDraft.value = dateRange.value[0];
startVisible.value = true;
}
function openEndTime(): void {
endDraft.value = dateRange.value[1];
endVisible.value = true;
}
function cancelStartTime(): void {
startVisible.value = false;
startDraft.value = dateRange.value[0];
}
function cancelEndTime(): void {
endVisible.value = false;
endDraft.value = dateRange.value[1];
}
async function confirmStartTime(value?: string | number): Promise<void> {
startVisible.value = false;
dateRange.value = [String(value || startDraft.value), dateRange.value[1]];
await confirmCustomRange();
}
async function confirmEndTime(value?: string | number): Promise<void> {
endVisible.value = false;
dateRange.value = [dateRange.value[0], String(value || endDraft.value)];
await confirmCustomRange();
}
async function confirmCustomRange(): Promise<void> {
if (dateFilterType.value === "custom") {
return;
}
dateFilterType.value = "custom";
await replaceFilterQuery();
await reloadRecords();
}
async function exportExcel(): Promise<void> {
if (isExporting.value) {
return;
}
isExporting.value = true;
try {
const blob = await EventRecordAPI.exportXSSF({
index: 0,
size: pageSize,
...buildRangeReq()
});
WebToolkit.downloadBlob(blob, `登记事件记录-${Time.formatDate(new Date())}.xlsx`);
Message.success("导出已开始");
} finally {
isExporting.value = false;
}
}
async function confirmVerifyRecord(record: EventRecordListItemView): Promise<void> {
if (!record.id || record.verificationStatus !== "PENDING" || isVerifying.value || isLoadingVerifyRecord.value) {
return;
}
const recordId = record.id;
isLoadingVerifyRecord.value = true;
verifyLoadingId.value = recordId;
try {
verifyTarget.value = await EventRecordAPI.detail(recordId);
verifyDialogVisible.value = true;
} catch (error) {
Message.error(getErrorMessage(error, "登记记录加载失败"));
} finally {
isLoadingVerifyRecord.value = false;
verifyLoadingId.value = "";
}
}
async function submitVerifyRecord(record: EventRecord, attachmentList: Attachment[]): Promise<void> {
const id = record.id;
if (!id) {
throw new Error("登记事件记录不存在");
}
isVerifying.value = true;
verifyingId.value = id;
try {
if (canUpdateRecord.value) {
await EventRecordAPI.update({
id,
remark: record.remark || "",
attachmentList
});
}
await EventRecordAPI.verify(id);
} finally {
isVerifying.value = false;
verifyingId.value = "";
}
}
async function handleVerifySuccess(): Promise<void> {
verifyDialogVisible.value = false;
verifyTarget.value = undefined;
try {
await reloadRecords();
} catch (error) {
Message.error(getErrorMessage(error, "登记记录刷新失败"));
}
}
async function toDetail(id: string): Promise<void> {
if (!id) {
return;
}
await router.push({
path: `/management/registration-event-records/${id}`,
query: buildFilterQuery(String(getCurrentRouteDepth() + 1))
});
}
async function toCreate(): Promise<void> {
await router.push({
path: "/management/registration-event-records/create",
query: {
transitionDepth: String(getCurrentRouteDepth() + 1)
}
});
}
async function confirmRemoveRecord(record: EventRecordListItemView): Promise<void> {
if (!record.id) {
return;
}
const recordId = record.id;
const dialog = DialogPlugin.confirm({
title: "删除登记记录",
content: `确定删除 ${getRecordCustomerName(record)} 的 ${getRecordEventName(record)} 记录吗?删除后无法恢复。`,
cancelBtn: "取消",
confirmBtn: "删除",
onCancel() {
dialog.destroy();
},
async onConfirm() {
dialog.destroy();
await removeRecord(recordId);
}
});
}
async function removeRecord(id: string): Promise<void> {
if (isDeleting.value) {
return;
}
isDeleting.value = true;
deletingId.value = id;
try {
await EventRecordAPI.remove(id);
await reloadRecords();
Message.success("登记记录已删除");
} catch (error) {
const message = getErrorMessage(error, "登记记录删除失败");
if (message.includes("积分余额不足") || message.includes("余额不足")) {
Message.warning("积分余额不足,请先人工调整积分后再删除登记记录");
return;
}
Message.error(message);
} finally {
isDeleting.value = false;
deletingId.value = "";
}
}
// ---------- 格式化 ----------
function getRecordEventName(record: EventRecordListItemView): string {
return record.event?.name || "未知事件";
}
function getRecordCustomerName(record: EventRecordListItemView): string {
return record.customer?.name || "未知客户";
}
function getRecordPhotoURL(record: EventRecordListItemView): string {
return getCustomerPhotoURL(record.customer, 64);
}
function getRecordCustomerInitial(record: EventRecordListItemView): string {
return getRecordCustomerName(record).slice(0, 1);
}
function getVerificationTag(record: EventRecordListItemView): { text: string; theme: "warning" | "success" } {
return record.verificationStatus === "VERIFIED"
? { text: "已核销", theme: "success" }
: { text: "待核销", theme: "warning" };
}
function getRecordRegisteredAt(record: EventRecordListItemView): string {
if (!record.registeredAt) {
return "-";
}
if (["today", "yesterday", "beforeYesterday"].includes(dateFilterType.value)) {
return Time.toTime(record.registeredAt);
}
return Time.toDateTime(record.registeredAt);
}
function buildRangeReq(): Omit<EventRecordPage, "index" | "size"> {
const [beginAt, endAt] = buildUnixRange();
const selectedAllEvent = !actionFilter.value.length || actionFilter.value.length === eventList.value.length;
return {
beginAt,
endAt,
customerId: customerId.value || undefined,
keyword: keyword.value.trim() || undefined,
eventIdList: actionFilter.value.length === 1 ? actionFilter.value : selectedAllEvent ? undefined : actionFilter.value,
verificationStatus: verificationFilter.value === "ALL" ? undefined : verificationFilter.value
};
}
function getQueryText(key: string): string {
const value = route.query[key];
return Array.isArray(value) ? String(value[0] || "") : String(value || "");
}
function parseDateFilterType(value: unknown): DateFilterType {
const text = Array.isArray(value) ? String(value[0] || "") : String(value || "");
return dateOptions.some(item => item.value === text) ? text as DateFilterType : "today";
}
function parseDateRange(type: DateFilterType): [string, string] {
if (type !== "custom") {
return buildPresetRange(type);
}
const beginAt = getQueryText("beginAt");
const endAt = getQueryText("endAt");
return [beginAt, endAt];
}
function parseEventIdList(value: unknown): string[] {
const text = Array.isArray(value) ? String(value[0] || "") : String(value || "");
return text.split(",").map(item => item.trim()).filter(Boolean);
}
function parseVerificationFilter(value: unknown): VerificationFilter {
const text = Array.isArray(value) ? String(value[0] || "") : String(value || "");
return verificationOptions.some(item => item.value === text) ? text as VerificationFilter : "ALL";
}
function buildFilterQuery(transitionDepth = getQueryText("transitionDepth")): Record<string, string> {
const query = {
keyword: keyword.value.trim(),
dateType: dateFilterType.value,
beginAt: dateRange.value[0] || "",
endAt: dateRange.value[1] || "",
customerId: customerId.value,
customerName: getQueryText("customerName"),
eventIds: actionFilter.value.join(","),
verificationStatus: verificationFilter.value === "ALL" ? "" : verificationFilter.value
};
if (isValidTransitionDepth(transitionDepth)) {
return {
...query,
transitionDepth
};
}
return query;
}
function getInitialKeyword(): string {
return getQueryText("keyword") || getQueryText("customerName");
}
function getCurrentRouteDepth(): number {
const transitionDepth = getQueryText("transitionDepth").trim();
if (isValidTransitionDepth(transitionDepth)) {
return Number(transitionDepth);
}
return Number(route.meta.depth ?? 0);
}
function isValidTransitionDepth(value: string): boolean {
const depth = Number(value);
return value !== "" && Number.isInteger(depth) && 0 <= depth && depth <= 20;
}
async function replaceFilterQuery(): Promise<void> {
await router.replace({
name: "EventRecordList",
query: buildFilterQuery()
});
}
function buildPresetRange(type: Exclude<DateFilterType, "custom">): [string, string] {
if (type === "all") {
return ["", ""];
}
if (type === "lastWeek") {
const current = new Date();
current.setHours(0, 0, 0, 0);
const day = current.getDay() || 7;
const thisWeekMonday = new Date(current);
thisWeekMonday.setDate(current.getDate() - day + 1);
const lastWeekMonday = new Date(thisWeekMonday);
lastWeekMonday.setDate(thisWeekMonday.getDate() - 7);
const lastWeekSunday = new Date(thisWeekMonday);
lastWeekSunday.setDate(thisWeekMonday.getDate() - 1);
return [`${Time.formatDate(lastWeekMonday)} 00:00`, `${Time.formatDate(lastWeekSunday)} 23:59`];
}
const offsetMap: Record<Exclude<DateFilterType, "all" | "custom" | "lastWeek">, number> = {
today: 0,
yesterday: 1,
beforeYesterday: 2
};
const date = new Date();
date.setHours(0, 0, 0, 0);
date.setDate(date.getDate() - offsetMap[type]);
const value = `${Time.formatDate(date)} 00:00`;
return [value, `${Time.formatDate(date)} 23:59`];
}
function buildUnixRange(): [number, number] {
if (dateFilterType.value === "all") {
return [0, Time.toTimestamp(new Date())];
}
return [
Time.toTimestamp(parseDateTime(dateRange.value[0], false)),
Time.toTimestamp(parseDateTime(dateRange.value[1], true))
];
}
function parseDateTime(value: string, isEnd: boolean): Date {
if (!value) {
return isEnd ? new Date() : new Date(0);
}
const [dateText, timeText = isEnd ? "23:59" : "00:00"] = value.split(" ");
const [year, month, day] = dateText.split("-").map(Number);
const [hour, minute] = timeText.split(":").map(Number);
return new Date(year, month - 1, day, hour || 0, minute || 0, isEnd ? 59 : 0, 0);
}
onMounted(async () => {
navBarStore.clearRight();
navBarStore.setCustomRightVisible(canShowActionMenu.value);
await loadActions();
initialized = true;
await reloadRecords();
});
onUnmounted(() => {
navBarStore.clearRight();
window.clearTimeout(keywordTimer);
});
</script>
<style scoped lang="less">
.registration-record-list {
gap: 1rem;
display: flex;
padding: calc(var(--app-nav-height)) 0 6rem 0;
flex-direction: column;
.search-bar {
top: var(--app-nav-height);
left: 0;
right: 0;
z-index: 10;
display: flex;
padding: .75rem 1rem;
position: sticky;
background: #F5F5F8;
box-shadow: var(--td-shadow-1);
flex-direction: column;
.search-input {
--td-input-vertical-padding: 8px;
border: 1px solid rgba(148, 163, 184, .28);
z-index: 1;
background: #f8fbff;
border-radius: 1rem;
}
.filter-menu {
--td-spacer-1: 6px;
width: 100%;
overflow: hidden;
margin-top: -1rem;
padding-top: .8rem;
background: #fff;
border: 1px solid rgba(148, 163, 184, .18);
border-radius: 0 0 1rem 1rem;
&:after {
display: none;
}
:deep(.t-dropdown-menu__item) {
min-width: 0;
}
:deep(.t-dropdown-menu__title) {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
}
.list-wrapper {
gap: 1rem;
display: flex;
flex-direction: column;
.summary {
overflow: hidden;
:deep(.t-cell__title) {
flex: 0 0 4.5rem;
}
:deep(.t-cell__note) {
flex: 1 1 0;
min-width: 0;
overflow: hidden;
text-align: right;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.record-list {
.item {
&:last-child {
:deep(.t-cell:after) {
display: none;
}
}
.cell {
padding: .5rem;
:deep(.t-cell__title) {
flex: 1 1 0;
min-width: 0;
}
:deep(.t-cell__title-text) {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
:deep(.t-cell__note) {
flex: 0 0 auto;
white-space: nowrap;
}
.avatar {
width: 3.5rem;
height: 3.5rem;
}
.description-line {
gap: .5rem;
display: flex;
align-items: center;
}
}
.actions {
display: flex;
height: 100%;
}
.action {
height: 100%;
padding: 0 1rem;
border-radius: 0;
}
}
}
}
}
:global(html.theme-dark) {
.registration-record-list {
.search-bar {
background: #10161f;
.search-input {
background: rgba(15, 23, 42, .72);
border-color: rgba(148, 163, 184, .16);
}
.filter-menu {
background: #172033;
border-color: rgba(148, 163, 184, .16);
}
}
.empty-block,
.load-state,
.summary {
color: #94a3b8;
}
}
}
</style>