增加订单号查询

This commit is contained in:
yml2213
2026-05-31 17:39:17 +08:00
parent e8c41f774c
commit 9219ad18a5
5 changed files with 530 additions and 25 deletions
@@ -62,6 +62,8 @@ import {
maskPhone,
} from "./mappers.js";
import { createHttpError } from "../../../../utils/http.js";
import { query as dbQuery } from "../../../../db/client.js";
import { parseJsonObject } from "../../../../utils/task-json.js";
import type {
AdminCloudtentaclesCatalogQueryInput,
@@ -79,6 +81,54 @@ import type {
type JsonObject = Record<string, any>;
type LocalCloudtentaclesTask = {
taskId: number;
taskNo: string;
platformOrderId: string;
orderShopId: string;
orderShopName: string;
taskStatus: string;
deliveryStatus: string;
resultCode: string;
resultMessage: string;
createdAt: string;
updatedAt: string;
skuCode: string;
skuName: string;
sourceKey: string;
vnId: number;
vnPhone: string;
roleName: string;
roleId: string;
localDispatchStatus: string;
localDispatchAt: string;
consumeShopId: string;
consumeShopName: string;
};
type LocalCloudtentaclesTaskRow = {
id: number;
task_no: string;
platform_order_id: string;
shop_id: string;
shop_name: string;
task_status: string;
delivery_status: string;
result_code: string;
result_message: string;
context_json: unknown;
created_at: string;
updated_at: string;
sku_code?: string;
sku_name?: string;
kuaishou_source_key?: string;
kuaishou_vn_phone?: string;
kuaishou_role_name?: string;
kuaishou_role_id?: string;
kuaishou_dispatch_status?: string;
kuaishou_consume_status?: string;
};
export function listAdminCloudtentaclesSources() {
const list = listCloudtentaclesSources();
const sessions = getAllCloudtentaclesSessionStates();
@@ -333,14 +383,35 @@ export async function getAdminCloudtentaclesKnapsack(
export async function listAdminCloudtentaclesDeliveryRecords(
payload: AdminCloudtentaclesDeliveryRecordQueryInput = {}
) {
const context = resolveAdminCloudtentaclesSessionPayload(payload);
const platformOrderId = normalizeText(payload.platformOrderId);
const preferredSourceKey = normalizeText(payload.sourceKey);
const orderMatchedTasks = platformOrderId
? await listLocalCloudtentaclesTasks({
platformOrderId,
limit: 50,
})
: [];
const orderSourceKey =
orderMatchedTasks.find((task) => !preferredSourceKey || task.sourceKey === preferredSourceKey)
?.sourceKey || orderMatchedTasks.find((task) => task.sourceKey)?.sourceKey;
const resolvedSourceKey =
orderSourceKey ||
preferredSourceKey ||
"default";
const context = resolveAdminCloudtentaclesSessionPayload({
...payload,
sourceKey: resolvedSourceKey,
});
const recordWindow = resolveRecordQueryWindow(payload, orderMatchedTasks);
const recordPage = platformOrderId ? 1 : payload.page;
const recordSize = platformOrderId ? 1000 : payload.size;
const query = {
...context,
...pickDefined({
page: payload.page,
size: payload.size,
startDate: payload.startDate,
endDate: payload.endDate,
page: recordPage,
size: recordSize,
startDate: recordWindow.startDate,
endDate: recordWindow.endDate,
recordCode: payload.recordCode,
}),
};
@@ -368,16 +439,59 @@ export async function listAdminCloudtentaclesDeliveryRecords(
}
const recordItems: JsonObject[] = Array.isArray(records.items) ? records.items : [];
const localTasks =
platformOrderId || recordItems.length === 0
? orderMatchedTasks
: await listLocalCloudtentaclesTasks({
sourceKey: resolvedSourceKey,
startDate: recordWindow.startDate,
endDate: recordWindow.endDate,
limit: 800,
});
const enrichedItems = recordItems
.map((record) => {
const localMatch = findBestLocalTaskForRecord(record, localTasks);
return {
...record,
...(skuImageByName.get(normalizeSkuName(record.name)) || {
skuId: 0,
skuName: "",
skuImage: "",
}),
...mapLocalTaskRecordEnhancement(localMatch?.task || null, localMatch?.confidence || ""),
};
})
.filter((record) => {
if (!platformOrderId) {
return true;
}
return normalizeText(record.platformOrderId) === platformOrderId;
});
const fallbackItems = platformOrderId
? orderMatchedTasks
.filter((task) => !enrichedItems.some((record) => Number(record.taskId || 0) === task.taskId))
.map((task) => ({
...createLocalTaskFallbackRecord(task),
...(skuImageByName.get(normalizeSkuName(task.skuName)) || {
skuId: 0,
skuName: "",
skuImage: "",
}),
...mapLocalTaskRecordEnhancement(task, "local"),
}))
: [];
const finalItems = platformOrderId ? [...enrichedItems, ...fallbackItems] : enrichedItems;
return {
...records,
items: recordItems.map((record) => ({
...record,
...(skuImageByName.get(normalizeSkuName(record.name)) || {
skuId: 0,
skuName: "",
skuImage: "",
}),
})),
sourceKey: resolvedSourceKey,
queryStartDate: recordWindow.startDate,
queryEndDate: recordWindow.endDate,
total: platformOrderId ? finalItems.length : records.total,
page: platformOrderId ? 1 : records.page,
size: platformOrderId ? recordSize : records.size,
localOrderMatched: platformOrderId ? orderMatchedTasks.length > 0 : false,
items: finalItems,
};
}
@@ -478,3 +592,284 @@ function pickDefined(values: JsonObject = {}) {
function normalizeSkuName(value: unknown) {
return String(value || "").trim().replace(/\s+/g, "").toLowerCase();
}
function normalizeText(value: unknown) {
return String(value || "").trim();
}
function normalizePhone(value: unknown) {
return normalizeText(value).replace(/\D+/g, "");
}
function normalizeNumber(value: unknown) {
const parsed = Number(value);
return Number.isFinite(parsed) && parsed > 0 ? parsed : 0;
}
function asRecord(value: unknown): JsonObject {
return value && typeof value === "object" && !Array.isArray(value)
? (value as JsonObject)
: {};
}
function pickFirstNonEmptyText(values: unknown[]) {
for (const value of values) {
const normalized = normalizeText(value);
if (normalized) {
return normalized;
}
}
return "";
}
function resolveRecordQueryWindow(
payload: AdminCloudtentaclesDeliveryRecordQueryInput = {},
tasks: LocalCloudtentaclesTask[] = []
) {
if (tasks.length === 0) {
return {
startDate: payload.startDate,
endDate: payload.endDate,
};
}
const timestamps = tasks
.flatMap((task) => [task.createdAt, task.updatedAt, task.localDispatchAt])
.map((value) => new Date(value).getTime())
.filter((value) => Number.isFinite(value));
if (timestamps.length === 0) {
return {
startDate: payload.startDate,
endDate: payload.endDate,
};
}
const hourMs = 60 * 60 * 1000;
return {
startDate: new Date(Math.min(...timestamps) - hourMs).toISOString(),
endDate: new Date(Math.max(...timestamps) + 6 * hourMs).toISOString(),
};
}
async function listLocalCloudtentaclesTasks(options: {
platformOrderId?: string | undefined;
sourceKey?: string | undefined;
startDate?: string | undefined;
endDate?: string | undefined;
limit?: number;
} = {}): Promise<LocalCloudtentaclesTask[]> {
const filters: string[] = ["ft.executor_key = 'kuaishou_ct_assisted'"];
const params: unknown[] = [];
const platformOrderId = normalizeText(options.platformOrderId);
const sourceKey = normalizeText(options.sourceKey);
const startDate = normalizeText(options.startDate);
const endDate = normalizeText(options.endDate);
if (platformOrderId) {
params.push(`%${platformOrderId}%`);
filters.push(`ft.platform_order_id ILIKE $${params.length}`);
}
if (sourceKey) {
params.push(sourceKey);
filters.push(
`COALESCE(NULLIF(kcts.source_key, ''), ft.context_json #>> '{kuaishouCloudFulfillment,binding,resolvedSourceKey}', '') = $${params.length}`
);
}
if (startDate) {
params.push(startDate);
filters.push(`ft.updated_at >= ($${params.length})::timestamptz - interval '1 day'`);
}
if (endDate) {
params.push(endDate);
filters.push(`ft.created_at <= ($${params.length})::timestamptz + interval '1 day'`);
}
params.push(Math.min(Math.max(Number(options.limit || 500), 1), 1000));
const result = await dbQuery<LocalCloudtentaclesTaskRow>(
`
SELECT
ft.id,
ft.task_no,
ft.platform_order_id,
ft.shop_id,
ft.shop_name,
ft.task_status,
ft.delivery_status,
ft.result_code,
ft.result_message,
ft.context_json,
ft.created_at,
ft.updated_at,
oi.sku_code,
oi.sku_name,
kcts.source_key AS kuaishou_source_key,
kcts.vn_phone AS kuaishou_vn_phone,
kcts.role_name AS kuaishou_role_name,
kcts.role_id AS kuaishou_role_id,
kcts.dispatch_status AS kuaishou_dispatch_status,
kcts.consume_status AS kuaishou_consume_status
FROM fulfillment_tasks ft
LEFT JOIN order_items oi ON oi.id = ft.order_item_id
LEFT JOIN kuaishou_cloud_task_states kcts ON kcts.task_id = ft.id
WHERE ${filters.join(" AND ")}
ORDER BY ft.id DESC
LIMIT $${params.length}
`,
params
);
return result.rows.map(mapLocalCloudtentaclesTask);
}
function mapLocalCloudtentaclesTask(row: LocalCloudtentaclesTaskRow): LocalCloudtentaclesTask {
const context = parseJsonObject(row.context_json);
const flow = asRecord(context.kuaishouCloudFulfillment);
const binding = asRecord(flow.binding);
const role = asRecord(flow.role);
const dispatch = asRecord(flow.dispatch);
const consume = asRecord(flow.consume);
return {
taskId: Number(row.id || 0),
taskNo: normalizeText(row.task_no),
platformOrderId: normalizeText(row.platform_order_id),
orderShopId: normalizeText(row.shop_id),
orderShopName: normalizeText(row.shop_name),
taskStatus: normalizeText(row.task_status),
deliveryStatus: normalizeText(row.delivery_status),
resultCode: normalizeText(row.result_code),
resultMessage: normalizeText(row.result_message),
createdAt: normalizeText(row.created_at),
updatedAt: normalizeText(row.updated_at),
skuCode: normalizeText(row.sku_code),
skuName: pickFirstNonEmptyText([row.sku_name, flow.internalSkuName]),
sourceKey: pickFirstNonEmptyText([row.kuaishou_source_key, binding.resolvedSourceKey]),
vnId: normalizeNumber(binding.vnId),
vnPhone: pickFirstNonEmptyText([row.kuaishou_vn_phone, binding.vnPhone]),
roleName: pickFirstNonEmptyText([row.kuaishou_role_name, role.name, binding.roleName]),
roleId: pickFirstNonEmptyText([row.kuaishou_role_id, role.rid, binding.roleId]),
localDispatchStatus: pickFirstNonEmptyText([row.kuaishou_dispatch_status, dispatch.status]),
localDispatchAt: normalizeText(dispatch.dispatchAt),
consumeShopId: normalizeText(consume.shopId),
consumeShopName: normalizeText(consume.shopName),
};
}
function findBestLocalTaskForRecord(
record: JsonObject,
tasks: LocalCloudtentaclesTask[] = []
) {
let best: { task: LocalCloudtentaclesTask; score: number; confidence: string } | null = null;
for (const task of tasks) {
const score = scoreLocalTaskRecordMatch(record, task);
if (score <= 0 || (best && score <= best.score)) {
continue;
}
best = {
task,
score,
confidence: score >= 80 ? "high" : score >= 45 ? "medium" : "low",
};
}
return best && best.score >= 35 ? best : null;
}
function scoreLocalTaskRecordMatch(record: JsonObject, task: LocalCloudtentaclesTask) {
let score = 0;
const recordVnId = normalizeNumber(record.virtualNumberId);
const recordPhone = normalizePhone(record.phone);
const taskPhone = normalizePhone(task.vnPhone);
const recordName = normalizeSkuName(record.name);
const taskSkuName = normalizeSkuName(task.skuName);
const recordRoleName = normalizeSkuName(record.fulfillUser);
const taskRoleName = normalizeSkuName(task.roleName);
if (recordVnId > 0 && task.vnId > 0 && recordVnId === task.vnId) {
score += 50;
}
if (recordPhone && taskPhone && recordPhone === taskPhone) {
score += 30;
}
if (recordName && taskSkuName && recordName === taskSkuName) {
score += 20;
}
if (recordRoleName && taskRoleName && recordRoleName.includes(taskRoleName)) {
score += 10;
}
const recordTime = new Date(record.createdAt).getTime();
const dispatchTime = new Date(task.localDispatchAt || task.updatedAt || task.createdAt).getTime();
if (Number.isFinite(recordTime) && Number.isFinite(dispatchTime)) {
const distanceHours = Math.abs(recordTime - dispatchTime) / (60 * 60 * 1000);
if (distanceHours <= 6) {
score += 15;
} else if (distanceHours <= 24) {
score += 6;
}
}
return score;
}
function mapLocalTaskRecordEnhancement(
task: LocalCloudtentaclesTask | null,
confidence = ""
) {
return {
platformOrderId: task?.platformOrderId || "",
taskId: task?.taskId || 0,
taskNo: task?.taskNo || "",
orderShopId: task?.orderShopId || "",
orderShopName: task?.orderShopName || "",
consumeShopId: task?.consumeShopId || "",
consumeShopName: task?.consumeShopName || "",
roleName: task?.roleName || "",
roleId: task?.roleId || "",
localTaskStatus: task?.taskStatus || "",
localDeliveryStatus: task?.deliveryStatus || "",
localDispatchStatus: task?.localDispatchStatus || "",
localDispatchAt: task?.localDispatchAt || "",
localResultCode: task?.resultCode || "",
localResultMessage: task?.resultMessage || "",
matchConfidence: confidence,
};
}
function createLocalTaskFallbackRecord(task: LocalCloudtentaclesTask) {
return {
createdAt: task.localDispatchAt || task.updatedAt || task.createdAt,
recordId: "",
virtualNumberId: task.vnId,
userId: 0,
count: 1,
cdk: "",
exchangeUrl: "",
name: task.skuName,
phone: task.vnPhone,
status: mapLocalDispatchStatusToCloudStatus(task.localDispatchStatus),
fulfillUser: task.roleName,
operatorName: "",
raw: {},
};
}
function mapLocalDispatchStatusToCloudStatus(status: string) {
const normalized = normalizeText(status);
if (normalized === "success") {
return 4;
}
if (normalized === "failed") {
return 3;
}
if (normalized === "pending") {
return 2;
}
return 0;
}
@@ -170,6 +170,7 @@ export type AdminCloudtentaclesDeliveryRecordQueryInput = AdminCloudtentaclesCat
startDate?: string
endDate?: string
recordCode?: number | string
platformOrderId?: string
}
export type AdminCloudtentaclesSkuBuyInput = {
@@ -195,6 +195,7 @@ export function fetchAdminCloudtentaclesDeliveryRecords(payload: {
size?: number
startDate?: string
endDate?: string
platformOrderId?: string
}) {
return apiPost<AdminCloudtentaclesDeliveryRecordListResult>(
'/api/v1/admin/cloudtentacles-records',
@@ -137,6 +137,22 @@ export interface AdminCloudtentaclesDeliveryRecordItem {
status: number
fulfillUser: string
operatorName: string
platformOrderId: string
taskId: number
taskNo: string
orderShopId: string
orderShopName: string
consumeShopId: string
consumeShopName: string
roleName: string
roleId: string
localTaskStatus: string
localDeliveryStatus: string
localDispatchStatus: string
localDispatchAt: string
localResultCode: string
localResultMessage: string
matchConfidence: string
raw: Record<string, unknown>
}
@@ -145,6 +161,10 @@ export interface AdminCloudtentaclesDeliveryRecordListResult {
page: number
size: number
total: number
sourceKey?: string
queryStartDate?: string
queryEndDate?: string
localOrderMatched?: boolean
items: AdminCloudtentaclesDeliveryRecordItem[]
raw: Record<string, unknown>
}
@@ -23,6 +23,7 @@ const loading = ref(false)
const sourceLoading = ref(false)
const errorMessage = ref('')
const sourceKey = ref('')
const platformOrderId = ref('')
const dateRange = ref<[string, string]>(createDefaultDateRange())
const page = ref(1)
const pageSize = ref(100)
@@ -58,7 +59,8 @@ async function loadSources() {
}
async function loadRecords(nextPage = page.value) {
if (!sourceKey.value) {
const orderKeyword = platformOrderId.value.trim()
if (!sourceKey.value && !orderKeyword) {
showError('请先选择 cloudtentacles 账号')
return
}
@@ -74,12 +76,16 @@ async function loadRecords(nextPage = page.value) {
try {
const response = await fetchAdminCloudtentaclesDeliveryRecords({
sourceKey: sourceKey.value,
sourceKey: sourceKey.value || undefined,
page: nextPage,
size: pageSize.value,
startDate,
endDate,
platformOrderId: orderKeyword || undefined,
})
if (response.data.sourceKey && sourceKey.value !== response.data.sourceKey) {
sourceKey.value = response.data.sourceKey
}
page.value = response.data.page
pageSize.value = response.data.size
total.value = response.data.total
@@ -92,6 +98,7 @@ async function loadRecords(nextPage = page.value) {
}
function resetFilters() {
platformOrderId.value = ''
dateRange.value = createDefaultDateRange()
page.value = 1
pageSize.value = 100
@@ -145,6 +152,22 @@ function getStatusType(status: number) {
return 'info'
}
function getLocalDispatchStatusLabel(status: string) {
const normalized = String(status || '').trim()
if (normalized === 'success') return '本地已发货'
if (normalized === 'failed') return '本地发货失败'
if (normalized === 'pending') return '本地待发货'
return normalized || '-'
}
function getMatchConfidenceLabel(value: string) {
if (value === 'high') return '高'
if (value === 'medium') return '中'
if (value === 'low') return '低'
if (value === 'local') return '本地'
return ''
}
function createDefaultDateRange(): [string, string] {
const end = new Date()
const start = new Date(end)
@@ -165,7 +188,7 @@ async function drawRecordImage(record: AdminCloudtentaclesDeliveryRecordItem) {
const canvas = document.createElement('canvas')
const scale = window.devicePixelRatio || 1
const width = 720
const height = 260
const height = 330
canvas.width = width * scale
canvas.height = height * scale
canvas.style.width = `${width}px`
@@ -193,6 +216,7 @@ async function drawRecordImage(record: AdminCloudtentaclesDeliveryRecordItem) {
ctx.textAlign = 'left'
drawTicketCard(ctx, record, productImage)
drawRecordMeta(ctx, record)
return canvas.toDataURL('image/png')
}
@@ -246,7 +270,7 @@ function drawTicketCard(
ctx.textAlign = 'left'
const contentX = x + imageWidth + 16
const orderText = `订单号:${record.virtualNumberId || record.recordId.slice(-6) || '-'}`
const orderText = `订单号:${record.platformOrderId || record.recordId.slice(-8) || '-'}`
ctx.fillStyle = '#1d5ea8'
ctx.font = '16px sans-serif'
drawEllipsisText(ctx, record.name || '未命名商品', contentX, y + 31, 170)
@@ -275,6 +299,23 @@ function drawTicketCard(
drawEllipsisText(ctx, `账号:${record.fulfillUser || record.phone || '-'}`, contentX, y + 90, 250)
}
function drawRecordMeta(ctx: CanvasRenderingContext2D, record: AdminCloudtentaclesDeliveryRecordItem) {
const left = 80
const top = 250
const lineHeight = 22
const values = [
`订单店铺:${record.orderShopName || record.orderShopId || '-'}`,
`核销店铺:${record.consumeShopName || record.consumeShopId || '-'}`,
`绑定角色:${record.roleName || record.fulfillUser || '-'}${record.roleId ? `${record.roleId}` : ''}`,
]
ctx.fillStyle = '#374151'
ctx.font = '13px sans-serif'
values.forEach((value, index) => {
drawEllipsisText(ctx, value, left, top + index * lineHeight, 560)
})
}
async function loadRecordProductImage(imageUrl: string) {
const normalizedUrl = String(imageUrl || '').trim()
if (!normalizedUrl) {
@@ -352,7 +393,9 @@ function drawEllipsisText(
function buildRecordImageFileName(record: AdminCloudtentaclesDeliveryRecordItem) {
const name = sanitizeFileName(record.name || '发货记录')
const id = sanitizeFileName(record.recordId.slice(0, 8) || String(record.virtualNumberId || 'record'))
const id = sanitizeFileName(
record.platformOrderId || record.recordId.slice(0, 8) || String(record.virtualNumberId || 'record'),
)
return `${name}-${id}.png`
}
@@ -370,7 +413,7 @@ onMounted(async () => {
<template>
<div class="cloud-records-page list-page">
<AdminPageHeader title="查询发货记录" description="按 cloudtentacles 账号和时间范围查询平台发货记录。">
<AdminPageHeader title="查询发货记录" description="按订单号、cloudtentacles 账号和时间范围查询平台发货记录。">
<template #extra>
<span class="total-badge"> {{ total }} 条记录</span>
</template>
@@ -380,7 +423,7 @@ onMounted(async () => {
<template #header>
<div class="card-header">
<span class="card-title">查询条件</span>
<span class="card-desc">查询 cloudtentacles 的已发货记录记录码默认使用平台发货记录</span>
<span class="card-desc">输入订单号时会先关联本地任务再匹配 cloudtentacles 发货记录</span>
</div>
</template>
@@ -399,6 +442,13 @@ onMounted(async () => {
:disabled="!source.enabled || !source.hasToken"
/>
</el-select>
<el-input
v-model.trim="platformOrderId"
class="filter-control"
clearable
placeholder="订单号"
@keyup.enter="loadRecords(1)"
/>
<el-date-picker
v-model="dateRange"
class="filter-control cloud-records-date-range"
@@ -450,6 +500,24 @@ onMounted(async () => {
<span class="cell-subline" :title="row.fulfillUser">
账号{{ row.fulfillUser || '-' }}
</span>
<span v-if="row.roleName || row.roleId" class="cell-subline" :title="`${row.roleName} ${row.roleId}`">
角色{{ row.roleName || '-' }}{{ row.roleId ? `${row.roleId}` : '' }}
</span>
</div>
</template>
</el-table-column>
<el-table-column label="订单 / 店铺" min-width="260">
<template #default="{ row }">
<div class="cell-stack">
<span class="cell-title" :title="row.platformOrderId">
{{ row.platformOrderId || '-' }}
</span>
<span class="cell-subline" :title="row.orderShopName || row.orderShopId">
订单店铺{{ row.orderShopName || row.orderShopId || '-' }}
</span>
<span class="cell-subline" :title="row.consumeShopName || row.consumeShopId">
核销店铺{{ row.consumeShopName || row.consumeShopId || '-' }}
</span>
</div>
</template>
</el-table-column>
@@ -463,9 +531,14 @@ onMounted(async () => {
</el-table-column>
<el-table-column label="状态" width="110">
<template #default="{ row }">
<el-tag :type="getStatusType(row.status)" effect="plain">
{{ getStatusLabel(row.status) }}
</el-tag>
<div class="status-stack">
<el-tag :type="getStatusType(row.status)" effect="plain">
{{ getStatusLabel(row.status) }}
</el-tag>
<span v-if="row.localDispatchStatus" class="cell-subline">
{{ getLocalDispatchStatusLabel(row.localDispatchStatus) }}
</span>
</div>
</template>
</el-table-column>
<el-table-column label="操作人" min-width="120">
@@ -476,7 +549,15 @@ onMounted(async () => {
</el-table-column>
<el-table-column label="记录 ID" min-width="210">
<template #default="{ row }">
<span class="cell-subline" :title="row.recordId">{{ row.recordId || '-' }}</span>
<div class="cell-stack">
<span class="cell-subline" :title="row.recordId">{{ row.recordId || '-' }}</span>
<span v-if="row.taskNo" class="cell-subline" :title="row.taskNo">
任务{{ row.taskNo }}
<template v-if="getMatchConfidenceLabel(row.matchConfidence)">
· 匹配{{ getMatchConfidenceLabel(row.matchConfidence) }}
</template>
</span>
</div>
</template>
</el-table-column>
<el-table-column label="操作" width="136" fixed="right">
@@ -519,7 +600,7 @@ onMounted(async () => {
@import '@/styles/admin-list-pages.css';
.cloud-records-filter {
grid-template-columns: minmax(180px, 0.34fr) minmax(360px, 1fr) auto;
grid-template-columns: minmax(180px, 0.28fr) minmax(220px, 0.36fr) minmax(360px, 1fr) auto;
}
.cloud-records-date-range {
@@ -549,6 +630,13 @@ onMounted(async () => {
background: #fff;
}
.status-stack {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 4px;
}
@media (max-width: 900px) {
.cloud-records-filter {
grid-template-columns: 1fr;