未知(继上次中断)

This commit is contained in:
Cheng Zhou
2026-02-04 10:52:34 +08:00
parent 8e258d21a7
commit 737f84bf54
99 changed files with 5497 additions and 2143 deletions
@@ -1,10 +1,5 @@
<template>
<div class="page">
<div class="page-header">
<div>
<h1 class="page-title">{{ TEXT.modules.feeContracts.detailTitle }}</h1>
<p class="page-subtitle">{{ TEXT.modules.feeContracts.detailSubtitle }}</p>
</div>
<div class="actions">
<el-button type="primary" :disabled="!canWrite || isReadOnly" @click="goEdit" class="header-action-btn copy-btn">
<el-icon class="el-icon--left"><Edit /></el-icon>
@@ -14,7 +9,6 @@
{{ TEXT.common.actions.back }}
</el-button>
</div>
</div>
<StateError v-if="errorMessage" :description="errorMessage">
<template #action>
@@ -201,6 +195,11 @@ const load = async () => {
const { data } = await getContractFee(contractId);
Object.assign(detail, data?.data || {});
if (!Array.isArray(detail.payments)) detail.payments = [];
// 同步中心名称到面包屑
if (centerName.value) {
study.setViewContext({ siteName: centerName.value });
}
} catch (e: any) {
errorMessage.value = e?.response?.data?.message || TEXT.common.messages.loadFailed;
} finally {
@@ -233,7 +232,7 @@ onMounted(async () => {
.page {
display: flex;
flex-direction: column;
gap: 20px;
gap: 12px;
}
.page-header {
+95 -107
View File
@@ -1,14 +1,5 @@
<template>
<div class="page">
<div class="page-header">
<div>
<h1 class="page-title">{{ TEXT.modules.feeContracts.title }}</h1>
</div>
<el-button type="primary" :disabled="!canWrite" @click="goNew" class="header-action-btn">
<el-icon class="el-icon--left"><Plus /></el-icon>
{{ TEXT.modules.feeContracts.newTitle }}
</el-button>
</div>
<div class="overview">
<el-row :gutter="20">
@@ -55,34 +46,39 @@
</el-row>
</div>
<el-card class="filter-card" shadow="never">
<el-form :inline="true" :model="filters" class="filter-form">
<el-form-item label="" class="filter-item">
<el-select v-model="filters.centerId" clearable :placeholder="TEXT.common.fields.site" class="filter-select">
<template #prefix>
<el-icon><Location /></el-icon>
</template>
<el-option
v-for="site in sites"
:key="site.id"
:label="site.name || TEXT.common.fallback"
:value="site.id"
/>
</el-select>
</el-form-item>
<el-form-item label="" class="filter-item">
<el-input v-model="filters.q" :placeholder="TEXT.common.placeholders.keyword" clearable class="filter-input">
<template #prefix>
<el-icon><Search /></el-icon>
</template>
</el-input>
</el-form-item>
<el-form-item class="filter-actions">
<el-button type="primary" @click="load">{{ TEXT.common.actions.search }}</el-button>
<el-button @click="resetFilters">{{ TEXT.common.actions.reset }}</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card shadow="never" class="main-content-card" v-if="!errorMessage && !loading">
<div class="filter-container">
<el-form :inline="true" :model="filters" class="filter-form">
<el-form-item label="" class="filter-item">
<el-select v-model="filters.centerId" clearable :placeholder="TEXT.common.fields.site" class="filter-select">
<template #prefix>
<el-icon><Location /></el-icon>
</template>
<el-option
v-for="site in sites"
:key="site.id"
:label="site.name || TEXT.common.fallback"
:value="site.id"
/>
</el-select>
</el-form-item>
<el-form-item label="" class="filter-item">
<el-input v-model="filters.q" :placeholder="TEXT.common.placeholders.keyword" clearable class="filter-input">
<template #prefix>
<el-icon><Search /></el-icon>
</template>
</el-input>
</el-form-item>
<el-form-item class="filter-actions">
<el-button @click="resetFilters">{{ TEXT.common.actions.reset }}</el-button>
</el-form-item>
<div class="filter-spacer"></div>
<el-button type="primary" :disabled="!canWrite" @click="goNew" class="header-action-btn">
<el-icon class="el-icon--left"><Plus /></el-icon>
{{ TEXT.modules.feeContracts.newTitle }}
</el-button>
</el-form>
</div>
<StateError v-if="errorMessage" :description="errorMessage">
<template #action>
@@ -92,7 +88,6 @@
<StateLoading v-else-if="loading" :rows="6" />
<el-card v-else class="table-card" shadow="never">
<el-table
:data="sortedContracts"
style="width: 100%"
@@ -100,26 +95,26 @@
:row-class-name="contractRowClass"
:header-cell-style="{ background: '#f8f9fb' }"
>
<el-table-column prop="center_name" :label="TEXT.common.fields.site" min-width="180">
<el-table-column prop="center_name" :label="TEXT.common.fields.site" min-width="160">
<template #default="scope">
<div class="site-cell">
<span class="site-name">{{ scope.row.center_name || TEXT.common.fallback }}</span>
</div>
</template>
</el-table-column>
<el-table-column :label="TEXT.modules.feeContracts.contractAmount" width="160" align="right">
<el-table-column :label="TEXT.modules.feeContracts.contractAmount" width="140" align="right">
<template #default="scope">
<span class="amount-text">{{ formatAmountWan(scope.row.contract_amount) }}</span>
</template>
</el-table-column>
<el-table-column :label="TEXT.modules.feeContracts.caseProgress" min-width="150" align="center">
<el-table-column :label="TEXT.modules.feeContracts.caseProgress" min-width="140" align="center">
<template #default="scope">
<el-tag effect="plain" type="info">
{{ displayCases(scope.row.contract_cases, scope.row.actual_cases) }}
</el-tag>
</template>
</el-table-column>
<el-table-column :label="TEXT.modules.feeContracts.paidSummary" min-width="180">
<el-table-column :label="TEXT.modules.feeContracts.paidSummary" min-width="200">
<template #default="scope">
<div class="summary-line">
<span>{{ TEXT.modules.feeContracts.paidTotal }}</span>
@@ -132,7 +127,7 @@
</div>
</template>
</el-table-column>
<el-table-column :label="TEXT.modules.feeContracts.verifySummary" min-width="180">
<el-table-column :label="TEXT.modules.feeContracts.verifySummary" min-width="200">
<template #default="scope">
<div class="summary-line">
<span>{{ TEXT.modules.feeContracts.verifiedTotal }}</span>
@@ -145,7 +140,7 @@
</div>
</template>
</el-table-column>
<el-table-column :label="TEXT.modules.feeContracts.recentDates" min-width="200">
<el-table-column :label="TEXT.modules.feeContracts.recentDates" min-width="180">
<template #default="scope">
<div class="date-line">
<span class="date-label">{{ TEXT.modules.feeContracts.lastPaid }}</span>
@@ -157,12 +152,7 @@
</div>
</template>
</el-table-column>
<el-table-column :label="TEXT.common.fields.remark" min-width="180" show-overflow-tooltip>
<template #default="scope">
{{ scope.row.remark || TEXT.common.fallback }}
</template>
</el-table-column>
<el-table-column :label="TEXT.common.labels.actions" width="120">
<el-table-column :label="TEXT.common.labels.actions" width="100">
<template #default="scope">
<el-button
link
@@ -182,7 +172,7 @@
</template>
<script setup lang="ts">
import { computed, onMounted, reactive, ref } from "vue";
import { computed, onMounted, reactive, ref, watch } from "vue";
import { useRouter } from "vue-router";
import { ElMessage, ElMessageBox } from "element-plus";
import { Coin, Wallet, CircleCheck, CircleClose, Plus, Location, Search } from "@element-plus/icons-vue";
@@ -214,14 +204,26 @@ const siteActiveMap = computed(() => {
return map;
});
const isInactiveSite = (siteId?: string) => !!siteId && siteActiveMap.value[siteId] === false;
const filteredContracts = computed(() => {
const keyword = filters.q.trim().toLowerCase();
return contracts.value.filter((item) => {
if (filters.centerId && item?.center_id !== filters.centerId) return false;
if (keyword) {
const fields = [item?.center_name, item?.contract_no, item?.contract_name];
const matched = fields.some((field) => String(field || "").toLowerCase().includes(keyword));
if (!matched) return false;
}
return true;
});
});
const sortedContracts = computed(() =>
[...contracts.value].sort((a, b) => Number(isInactiveSite(a?.center_id)) - Number(isInactiveSite(b?.center_id)))
[...filteredContracts.value].sort((a, b) => Number(isInactiveSite(a?.center_id)) - Number(isInactiveSite(b?.center_id)))
);
const contractRowClass = ({ row }: { row: any }) =>
`${row?.id ? "clickable-row" : ""}${isInactiveSite(row?.center_id) ? " row-inactive" : ""}`.trim();
const filters = reactive({
centerId: "",
centerId: study.currentSite?.id || "",
q: "",
});
@@ -231,7 +233,7 @@ const toNumber = (value: any) => {
};
const overview = computed(() => {
const totals = contracts.value.reduce(
const totals = filteredContracts.value.reduce(
(acc, item) => {
acc.totalContractAmount += toNumber(item.contract_amount);
acc.totalPaidAmount += toNumber(item.paid_total);
@@ -248,7 +250,7 @@ const overview = computed(() => {
);
return {
...totals,
centerCount: contracts.value.length,
centerCount: filteredContracts.value.length,
};
});
@@ -269,11 +271,7 @@ const load = async () => {
loading.value = true;
errorMessage.value = "";
try {
const { data } = await listContractFees({
projectId,
centerId: filters.centerId || undefined,
q: filters.q || undefined,
});
const { data } = await listContractFees({ projectId });
contracts.value = data?.data || [];
} catch (e: any) {
errorMessage.value = e?.response?.data?.message || TEXT.common.messages.loadFailed;
@@ -285,7 +283,6 @@ const load = async () => {
const resetFilters = () => {
filters.centerId = "";
filters.q = "";
load();
};
const goNew = () => router.push("/fees/contracts/new");
@@ -327,6 +324,19 @@ const formatAmountWan = (value: any) => {
return (numberValue / 10000).toFixed(2);
};
watch(() => filters.centerId, (val: string) => {
if (val) {
const matched = sites.value.find(s => s.id === val);
if (matched) study.setCurrentSite(matched);
} else {
study.setCurrentSite(null);
}
});
watch(() => study.currentSite, (newSite: any) => {
filters.centerId = newSite?.id || "";
});
onMounted(async () => {
await loadSites();
load();
@@ -337,58 +347,21 @@ onMounted(async () => {
.page {
display: flex;
flex-direction: column;
gap: 20px;
}
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 4px;
.main-content-card :deep(.el-card__body) {
padding: 20px;
}
.page-title {
margin: 0;
font-size: 24px;
font-weight: 700;
color: var(--ctms-text-main);
letter-spacing: -0.5px;
}
.page-subtitle {
margin: 6px 0 0;
font-size: 14px;
color: var(--ctms-text-secondary);
}
.header-action-btn {
height: 36px;
padding: 0 20px;
border-radius: 8px;
}
.overview {
margin-bottom: 4px;
}
.filter-card {
border: none;
background-color: transparent;
padding: 0;
}
.filter-card :deep(.el-card__body) {
padding: 0;
.filter-container {
margin-bottom: 20px;
}
.filter-form {
display: flex;
width: 100%;
gap: 12px;
align-items: center;
background: white;
padding: 16px;
border-radius: 12px;
border: 1px solid var(--ctms-border-color);
}
.filter-item {
@@ -398,14 +371,17 @@ onMounted(async () => {
.filter-select,
.filter-input {
width: 240px;
width: 140px;
}
.filter-actions {
margin-left: auto !important;
margin-bottom: 0 !important;
}
.filter-spacer {
flex: 1;
}
.table-card {
border-radius: 12px;
border: 1px solid var(--ctms-border-color);
@@ -424,22 +400,32 @@ onMounted(async () => {
.summary-line {
display: flex;
justify-content: space-between;
justify-content: flex-start;
align-items: center;
font-size: 13px;
margin-bottom: 4px;
color: var(--ctms-text-regular);
gap: 8px;
}
.summary-line:last-child {
margin-bottom: 0;
}
.summary-line span:first-child {
color: var(--ctms-text-secondary);
}
.amount-highlight {
font-weight: 600;
font-family: var(--el-font-family);
}
.summary-line .amount-highlight,
.summary-line .amount-muted {
text-align: left;
}
.amount-highlight.success { color: var(--el-color-success); }
.amount-highlight.warning { color: var(--el-color-warning); }
.amount-highlight.info { color: var(--el-color-info); }
@@ -447,9 +433,11 @@ onMounted(async () => {
.date-line {
display: flex;
justify-content: space-between;
justify-content: flex-start;
align-items: center;
font-size: 12px;
margin-bottom: 2px;
gap: 8px;
}
.date-label {
@@ -1,19 +1,13 @@
<template>
<div class="page">
<div class="page-header">
<div>
<h1 class="page-title">{{ TEXT.modules.feeSpecials.detailTitle }}</h1>
<p class="page-subtitle">{{ TEXT.modules.feeSpecials.detailSubtitle }}</p>
</div>
<div class="actions">
<el-button type="primary" :disabled="!canWrite || isReadOnly" @click="goEdit" class="header-action-btn copy-btn">
<el-icon class="el-icon--left"><Edit /></el-icon>
{{ TEXT.common.actions.edit }}
</el-button>
<el-button @click="goBack" class="header-action-btn">
{{ TEXT.common.actions.back }}
</el-button>
</div>
<div class="actions">
<el-button type="primary" :disabled="!canWrite || isReadOnly" @click="goEdit" class="header-action-btn copy-btn">
<el-icon class="el-icon--left"><Edit /></el-icon>
{{ TEXT.common.actions.edit }}
</el-button>
<el-button @click="goBack" class="header-action-btn">
{{ TEXT.common.actions.back }}
</el-button>
</div>
<StateError v-if="errorMessage" :description="errorMessage">
@@ -178,6 +172,11 @@ const load = async () => {
try {
const { data } = await getSpecialExpense(expenseId);
Object.assign(detail, data?.data || {});
// 同步中心名称到面包屑
if (centerName.value) {
study.setViewContext({ siteName: centerName.value });
}
} catch (e: any) {
errorMessage.value = e?.response?.data?.message || TEXT.common.messages.loadFailed;
} finally {
+90 -137
View File
@@ -1,20 +1,7 @@
<template>
<div class="page">
<div class="page-header">
<div>
<h1 class="page-title">{{ TEXT.modules.feeSpecials.title }}</h1>
</div>
<el-button type="primary" :disabled="!canWrite" @click="goNew" class="header-action-btn">
<el-icon class="el-icon--left"><Plus /></el-icon>
{{ TEXT.modules.feeSpecials.newTitle }}
</el-button>
</div>
<div class="overview">
<div class="overview-header">
<span class="overview-title">{{ TEXT.modules.feeSpecials.overviewTitle }}</span>
<el-tag effect="plain" type="info" size="small">{{ TEXT.modules.feeSpecials.recordCount }} {{ overview.recordCount }}</el-tag>
</div>
<el-row :gutter="20">
<el-col :xs="24" :sm="12" :md="6">
<KpiCard
@@ -58,56 +45,51 @@
</el-row>
</div>
<el-card class="filter-card" shadow="never">
<el-form :inline="true" :model="filters" class="filter-form">
<el-form-item label="" class="filter-item">
<el-select v-model="filters.centerId" clearable :placeholder="TEXT.common.fields.site" class="filter-select">
<template #prefix>
<el-icon><Location /></el-icon>
</template>
<el-option
v-for="site in sites"
:key="site.id"
:label="site.name || TEXT.common.fallback"
:value="site.id"
<el-card shadow="never" class="main-content-card" v-if="!errorMessage && !loading">
<div class="filter-container">
<el-form :inline="true" :model="filters" class="filter-form">
<el-form-item label="" class="filter-item">
<el-select v-model="filters.centerId" clearable :placeholder="TEXT.common.fields.site" class="filter-select">
<template #prefix>
<el-icon><Location /></el-icon>
</template>
<el-option
v-for="site in sites"
:key="site.id"
:label="site.name || TEXT.common.fallback"
:value="site.id"
/>
</el-select>
</el-form-item>
<el-form-item label="" class="filter-item">
<el-select v-model="filters.category" clearable :placeholder="TEXT.common.fields.category" class="filter-select">
<template #prefix>
<el-icon><Menu /></el-icon>
</template>
<el-option v-for="option in categoryOptions" :key="option.value" :label="option.label" :value="option.value" />
</el-select>
</el-form-item>
<el-form-item label="" class="filter-item">
<el-date-picker
v-model="filters.dateRange"
type="daterange"
unlink-panels
value-format="YYYY-MM-DD"
:start-placeholder="TEXT.common.placeholders.startDate"
:end-placeholder="TEXT.common.placeholders.endDate"
class="filter-date"
/>
</el-select>
</el-form-item>
<el-form-item label="" class="filter-item">
<el-select v-model="filters.category" clearable :placeholder="TEXT.common.fields.category" class="filter-select">
<template #prefix>
<el-icon><Menu /></el-icon>
</template>
<el-option v-for="option in categoryOptions" :key="option.value" :label="option.label" :value="option.value" />
</el-select>
</el-form-item>
<el-form-item label="" class="filter-item">
<el-date-picker
v-model="filters.dateRange"
type="daterange"
unlink-panels
value-format="YYYY-MM-DD"
:start-placeholder="TEXT.common.placeholders.startDate"
:end-placeholder="TEXT.common.placeholders.endDate"
class="filter-date"
/>
</el-form-item>
<el-form-item class="filter-actions">
<el-button type="primary" @click="load">{{ TEXT.common.actions.search }}</el-button>
<el-button @click="resetFilters">{{ TEXT.common.actions.reset }}</el-button>
</el-form-item>
</el-form>
</el-card>
<StateError v-if="errorMessage" :description="errorMessage">
<template #action>
<el-button type="primary" @click="load">{{ TEXT.common.actions.retry }}</el-button>
</template>
</StateError>
<StateLoading v-else-if="loading" :rows="6" />
<el-card v-else class="table-card" shadow="never">
</el-form-item>
<el-form-item class="filter-actions">
<el-button @click="resetFilters">{{ TEXT.common.actions.reset }}</el-button>
</el-form-item>
<div class="filter-spacer"></div>
<el-button type="primary" :disabled="!canWrite" @click="goNew" class="header-action-btn">
<el-icon class="el-icon--left"><Plus /></el-icon>
{{ TEXT.modules.feeSpecials.newTitle }}
</el-button>
</el-form>
</div>
<el-table
:data="sortedExpenses"
style="width: 100%"
@@ -132,7 +114,7 @@
</el-tag>
</template>
</el-table-column>
<el-table-column :label="TEXT.common.fields.amount" width="140" align="right">
<el-table-column :label="TEXT.common.fields.amount" width="110" align="right">
<template #default="scope">
<span class="amount-text">{{ formatAmount(scope.row.amount) }}</span>
</template>
@@ -142,7 +124,7 @@
{{ scope.row.description || TEXT.common.fallback }}
</template>
</el-table-column>
<el-table-column width="140" align="center" :label="TEXT.common.labels.status">
<el-table-column width="140" align="center" :label="TEXT.common.fields.status">
<template #default="scope">
<el-tooltip :content="scope.row.is_paid ? `${TEXT.modules.feeSpecials.isPaid} (${displayDate(scope.row.paid_date)})` : TEXT.modules.feeSpecials.unpaid" placement="top">
<el-icon :class="scope.row.is_paid ? 'status-icon success' : 'status-icon muted'"><Wallet /></el-icon>
@@ -180,7 +162,7 @@
</template>
<script setup lang="ts">
import { computed, onMounted, reactive, ref } from "vue";
import { computed, onMounted, reactive, ref, watch } from "vue";
import { useRouter } from "vue-router";
import { ElMessage, ElMessageBox } from "element-plus";
import { Coin, Wallet, CircleCheck, Document, Plus, Location, Menu } from "@element-plus/icons-vue";
@@ -214,19 +196,33 @@ const siteActiveMap = computed(() => {
const isInactiveSite = (siteId?: string) => !!siteId && siteActiveMap.value[siteId] === false;
const expenseRowClass = ({ row }: { row: any }) =>
`${row?.id ? "clickable-row" : ""}${isInactiveSite(row?.center_id) ? " row-inactive" : ""}`.trim();
const filteredExpenses = computed(() => {
const [dateFrom, dateTo] = filters.dateRange || [];
return expenses.value.filter((item) => {
if (filters.centerId && item?.center_id !== filters.centerId) return false;
if (filters.category && item?.category !== filters.category) return false;
if (dateFrom || dateTo) {
const value = item?.happen_date;
if (!value) return false;
if (dateFrom && value < dateFrom) return false;
if (dateTo && value > dateTo) return false;
}
return true;
});
});
const sortedExpenses = computed(() =>
[...expenses.value].sort((a, b) => Number(isInactiveSite(a?.center_id)) - Number(isInactiveSite(b?.center_id)))
[...filteredExpenses.value].sort((a, b) => Number(isInactiveSite(a?.center_id)) - Number(isInactiveSite(b?.center_id)))
);
const filters = reactive({
centerId: "",
centerId: study.currentSite?.id || "",
category: "",
dateRange: [] as string[],
});
const categoryOptions = Object.keys(TEXT.enums.feeSpecialCategory).map((value) => ({
value,
label: TEXT.enums.feeSpecialCategory[value],
label: (TEXT.enums.feeSpecialCategory as any)[value],
}));
const toNumber = (value: any) => {
@@ -235,7 +231,7 @@ const toNumber = (value: any) => {
};
const overview = computed(() => {
const totals = expenses.value.reduce(
const totals = filteredExpenses.value.reduce(
(acc, item) => {
acc.totalAmount += toNumber(item.amount);
if (item.is_paid) acc.paidAmount += toNumber(item.amount);
@@ -252,7 +248,6 @@ const overview = computed(() => {
);
return {
...totals,
recordCount: expenses.value.length,
};
});
@@ -273,14 +268,7 @@ const load = async () => {
loading.value = true;
errorMessage.value = "";
try {
const [dateFrom, dateTo] = filters.dateRange || [];
const { data } = await listSpecialExpenses({
projectId,
centerId: filters.centerId || undefined,
category: filters.category || undefined,
dateFrom: dateFrom || undefined,
dateTo: dateTo || undefined,
});
const { data } = await listSpecialExpenses({ projectId });
expenses.value = data?.data || [];
} catch (e: any) {
errorMessage.value = e?.response?.data?.message || TEXT.common.messages.loadFailed;
@@ -293,7 +281,6 @@ const resetFilters = () => {
filters.centerId = "";
filters.category = "";
filters.dateRange = [];
load();
};
const goNew = () => router.push("/fees/special/new");
@@ -333,6 +320,19 @@ const getCategoryType = (category: string) => {
return 'info';
};
watch(() => filters.centerId, (val: string) => {
if (val) {
const matched = sites.value.find(s => s.id === val);
if (matched) study.setCurrentSite(matched);
} else {
study.setCurrentSite(null);
}
});
watch(() => study.currentSite, (newSite: any) => {
filters.centerId = newSite?.id || "";
});
onMounted(async () => {
await loadSites();
load();
@@ -343,71 +343,21 @@ onMounted(async () => {
.page {
display: flex;
flex-direction: column;
gap: 20px;
}
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 4px;
.main-content-card :deep(.el-card__body) {
padding: 20px;
}
.page-title {
margin: 0;
font-size: 24px;
font-weight: 700;
color: var(--ctms-text-main);
letter-spacing: -0.5px;
}
.page-subtitle {
margin: 6px 0 0;
font-size: 14px;
color: var(--ctms-text-secondary);
}
.header-action-btn {
height: 36px;
padding: 0 20px;
border-radius: 8px;
}
.overview {
margin-bottom: 4px;
}
.overview-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 4px 8px;
}
.overview-title {
font-size: 14px;
font-weight: 600;
color: var(--ctms-text-main);
}
.filter-card {
border: none;
background-color: transparent;
padding: 0;
}
.filter-card :deep(.el-card__body) {
padding: 0;
.filter-container {
margin-bottom: 20px;
}
.filter-form {
display: flex;
width: 100%;
gap: 12px;
align-items: center;
background: white;
padding: 16px;
border-radius: 12px;
border: 1px solid var(--ctms-border-color);
}
.filter-item {
@@ -416,18 +366,21 @@ onMounted(async () => {
}
.filter-select {
width: 200px;
width: 140px;
}
.filter-date {
width: 280px;
width: 220px;
}
.filter-actions {
margin-left: auto !important;
margin-bottom: 0 !important;
}
.filter-spacer {
flex: 1;
}
.table-card {
border-radius: 12px;
border: 1px solid var(--ctms-border-color);