feat: unify mobile account filters
This commit is contained in:
@@ -218,13 +218,18 @@ test('keeps the customer directory visible on mobile and opens details on demand
|
|||||||
const customer = await screen.findByRole('button', { name: /选择客户 华东客户/ });
|
const customer = await screen.findByRole('button', { name: /选择客户 华东客户/ });
|
||||||
expect(view.container.querySelector('.v2-user-mobile-picker')).not.toBeInTheDocument();
|
expect(view.container.querySelector('.v2-user-mobile-picker')).not.toBeInTheDocument();
|
||||||
expect(view.container.querySelector('.v2-customer-list.semi-list')).toBeInTheDocument();
|
expect(view.container.querySelector('.v2-customer-list.semi-list')).toBeInTheDocument();
|
||||||
expect(screen.getByRole('textbox', { name: '搜索客户账号' })).toBeInTheDocument();
|
expect(screen.queryByRole('textbox', { name: '搜索客户账号' })).not.toBeInTheDocument();
|
||||||
expect(screen.getByRole('button', { name: '新建客户账号' })).toHaveClass('v2-workspace-mobile-tool-button', 'is-primary');
|
expect(screen.getByRole('button', { name: '新建客户账号' })).toHaveClass('v2-workspace-mobile-tool-button', 'is-primary');
|
||||||
const filterToggle = screen.getByRole('button', { name: '修改账号范围:全部状态' });
|
const filterToggle = screen.getByRole('button', { name: '修改账号范围:全部状态' });
|
||||||
expect(filterToggle).toHaveAttribute('aria-expanded', 'false');
|
expect(filterToggle).toHaveAttribute('aria-expanded', 'false');
|
||||||
fireEvent.click(filterToggle);
|
fireEvent.click(filterToggle);
|
||||||
expect(screen.getByRole('button', { name: '收起账号范围:全部状态' })).toHaveAttribute('aria-expanded', 'true');
|
expect(screen.getByRole('button', { name: '关闭账号范围:全部状态' })).toHaveAttribute('aria-expanded', 'true');
|
||||||
|
expect(screen.getByRole('textbox', { name: '搜索客户账号' })).toBeInTheDocument();
|
||||||
|
expect(document.querySelector('.v2-user-mobile-filter-sidesheet .semi-sidesheet-inner')).toHaveAttribute('aria-label', '客户账号筛选');
|
||||||
|
expect(document.querySelector('.v2-user-filter-panel')).not.toBeInTheDocument();
|
||||||
expect(document.querySelector('.v2-user-editor-sidesheet')).not.toBeInTheDocument();
|
expect(document.querySelector('.v2-user-editor-sidesheet')).not.toBeInTheDocument();
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: '关闭客户账号筛选' }));
|
||||||
|
await waitFor(() => expect(screen.getByRole('button', { name: '修改账号范围:全部状态' })).toHaveAttribute('aria-expanded', 'false'));
|
||||||
fireEvent.click(customer);
|
fireEvent.click(customer);
|
||||||
expect(await screen.findByRole('button', { name: '关闭账号详情' })).toBeInTheDocument();
|
expect(await screen.findByRole('button', { name: '关闭账号详情' })).toBeInTheDocument();
|
||||||
expect(customer).toHaveAttribute('aria-expanded', 'true');
|
expect(customer).toHaveAttribute('aria-expanded', 'true');
|
||||||
@@ -234,6 +239,50 @@ test('keeps the customer directory visible on mobile and opens details on demand
|
|||||||
expect(screen.getAllByRole('button', { name: '新建客户账号' })).toHaveLength(1);
|
expect(screen.getAllByRole('button', { name: '新建客户账号' })).toHaveLength(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('applies mobile customer filters explicitly and discards unconfirmed draft changes', async () => {
|
||||||
|
layout.mobile = true;
|
||||||
|
mocks.adminUsers.mockResolvedValue([
|
||||||
|
{
|
||||||
|
id: 7, username: 'customer-east', displayName: '华东客户', userType: 'customer', status: 'enabled',
|
||||||
|
customerRef: 'east', tenantRef: '', authProvider: 'local', menuKeys: ['monitor'], vehicleVins: [],
|
||||||
|
vehicles: [], grantHistory: [], createdAt: '2026-07-16T00:00:00Z', updatedAt: '2026-07-16T00:00:00Z'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 8, username: 'customer-west', displayName: '西部客户', userType: 'customer', status: 'disabled',
|
||||||
|
customerRef: 'west', tenantRef: '', authProvider: 'local', menuKeys: ['monitor'], vehicleVins: [],
|
||||||
|
vehicles: [], grantHistory: [], createdAt: '2026-07-16T00:00:00Z', updatedAt: '2026-07-16T00:00:00Z'
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });
|
||||||
|
render(<QueryClientProvider client={client}><UsersPage /></QueryClientProvider>);
|
||||||
|
|
||||||
|
expect(await screen.findByRole('button', { name: /选择客户 华东客户/ })).toBeInTheDocument();
|
||||||
|
expect(screen.getByRole('button', { name: /选择客户 西部客户/ })).toBeInTheDocument();
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: '修改账号范围:全部状态' }));
|
||||||
|
fireEvent.change(screen.getByRole('textbox', { name: '搜索客户账号' }), { target: { value: '西部' } });
|
||||||
|
|
||||||
|
expect(screen.getByRole('button', { name: /选择客户 华东客户/ })).toBeInTheDocument();
|
||||||
|
expect(screen.getByRole('button', { name: /选择客户 西部客户/ })).toBeInTheDocument();
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: '关闭客户账号筛选' }));
|
||||||
|
await waitFor(() => expect(screen.getByRole('button', { name: '修改账号范围:全部状态' })).toBeInTheDocument());
|
||||||
|
expect(screen.getByRole('button', { name: /选择客户 华东客户/ })).toBeInTheDocument();
|
||||||
|
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: '修改账号范围:全部状态' }));
|
||||||
|
expect(screen.getByRole('textbox', { name: '搜索客户账号' })).toHaveValue('');
|
||||||
|
fireEvent.change(screen.getByRole('textbox', { name: '搜索客户账号' }), { target: { value: '西部' } });
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: '应用筛选' }));
|
||||||
|
|
||||||
|
await waitFor(() => expect(screen.queryByRole('button', { name: /选择客户 华东客户/ })).not.toBeInTheDocument());
|
||||||
|
expect(screen.getByRole('button', { name: /选择客户 西部客户/ })).toBeInTheDocument();
|
||||||
|
expect(screen.getByRole('button', { name: '修改账号范围:全部状态 · 西部' })).toBeInTheDocument();
|
||||||
|
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: '修改账号范围:全部状态 · 西部' }));
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: '重置条件' }));
|
||||||
|
await waitFor(() => expect(screen.getByRole('button', { name: /选择客户 华东客户/ })).toBeInTheDocument());
|
||||||
|
expect(screen.getByRole('button', { name: /选择客户 西部客户/ })).toBeInTheDocument();
|
||||||
|
expect(screen.getByRole('button', { name: '修改账号范围:全部状态' })).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
test('keeps granted vehicles primary and reveals mobile assignment tools on demand', async () => {
|
test('keeps granted vehicles primary and reveals mobile assignment tools on demand', async () => {
|
||||||
layout.mobile = true;
|
layout.mobile = true;
|
||||||
const vehicles = Array.from({ length: 5 }, (_, index) => ({
|
const vehicles = Array.from({ length: 5 }, (_, index) => ({
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { api } from '../../api/client';
|
|||||||
import type { AdminUser, CustomerUserInput, CustomerVehicleGrantInput } from '../../api/types';
|
import type { AdminUser, CustomerUserInput, CustomerVehicleGrantInput } from '../../api/types';
|
||||||
import { useMobileLayout } from '../hooks/useMobileLayout';
|
import { useMobileLayout } from '../hooks/useMobileLayout';
|
||||||
import { useSideSheetA11y } from '../hooks/useSideSheetA11y';
|
import { useSideSheetA11y } from '../hooks/useSideSheetA11y';
|
||||||
|
import { MobileFilterToggle } from '../shared/MobileFilterToggle';
|
||||||
import { TablePagination } from '../shared/TablePagination';
|
import { TablePagination } from '../shared/TablePagination';
|
||||||
import { VehicleCandidateList } from '../shared/VehicleCandidateList';
|
import { VehicleCandidateList } from '../shared/VehicleCandidateList';
|
||||||
import { WorkspaceFilterPanel } from '../shared/WorkspaceFilterPanel';
|
import { WorkspaceFilterPanel } from '../shared/WorkspaceFilterPanel';
|
||||||
@@ -42,6 +43,13 @@ type CustomerAccessState = {
|
|||||||
attention: boolean;
|
attention: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const customerScopeOptions: Array<{ value: CustomerScopeFilter; label: string }> = [
|
||||||
|
{ value: 'all', label: '全部状态' },
|
||||||
|
{ value: 'enabled', label: '启用账号' },
|
||||||
|
{ value: 'disabled', label: '停用账号' },
|
||||||
|
{ value: 'attention', label: '待完善权限' }
|
||||||
|
];
|
||||||
|
|
||||||
const shanghaiDateTimeFormatter = new Intl.DateTimeFormat('sv-SE', {
|
const shanghaiDateTimeFormatter = new Intl.DateTimeFormat('sv-SE', {
|
||||||
timeZone: 'Asia/Shanghai', year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', hour12: false
|
timeZone: 'Asia/Shanghai', year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', hour12: false
|
||||||
});
|
});
|
||||||
@@ -104,6 +112,8 @@ export default function UsersPage() {
|
|||||||
const [customerKeyword, setCustomerKeyword] = useState('');
|
const [customerKeyword, setCustomerKeyword] = useState('');
|
||||||
const deferredCustomerKeyword = useDeferredValue(customerKeyword.trim());
|
const deferredCustomerKeyword = useDeferredValue(customerKeyword.trim());
|
||||||
const [customerStatus, setCustomerStatus] = useState<CustomerScopeFilter>('all');
|
const [customerStatus, setCustomerStatus] = useState<CustomerScopeFilter>('all');
|
||||||
|
const [draftCustomerKeyword, setDraftCustomerKeyword] = useState('');
|
||||||
|
const [draftCustomerStatus, setDraftCustomerStatus] = useState<CustomerScopeFilter>('all');
|
||||||
const [filtersCollapsed, setFiltersCollapsed] = useState(true);
|
const [filtersCollapsed, setFiltersCollapsed] = useState(true);
|
||||||
const visibleCustomers = useMemo(() => {
|
const visibleCustomers = useMemo(() => {
|
||||||
const keyword = deferredCustomerKeyword.toLocaleLowerCase('zh-CN');
|
const keyword = deferredCustomerKeyword.toLocaleLowerCase('zh-CN');
|
||||||
@@ -139,9 +149,11 @@ export default function UsersPage() {
|
|||||||
const editingGrantPlate = editingGrant ? vehicleLabels[editingGrant.vin] : '';
|
const editingGrantPlate = editingGrant ? vehicleLabels[editingGrant.vin] : '';
|
||||||
const grantWindowInvalid = Boolean(editingGrant && (!editingGrant.validFrom || (editingGrant.validTo && editingGrant.validTo <= editingGrant.validFrom)));
|
const grantWindowInvalid = Boolean(editingGrant && (!editingGrant.validFrom || (editingGrant.validTo && editingGrant.validTo <= editingGrant.validFrom)));
|
||||||
const editorVisible = creating || Boolean(selected);
|
const editorVisible = creating || Boolean(selected);
|
||||||
|
const mobileFiltersOpen = mobileLayout && !filtersCollapsed;
|
||||||
const vehicleComposerVisible = !mobileLayout || creating || draft.vehicleGrants.length === 0 || vehicleComposerOpen;
|
const vehicleComposerVisible = !mobileLayout || creating || draft.vehicleGrants.length === 0 || vehicleComposerOpen;
|
||||||
const hasUnsavedChanges = useMemo(() => draftSignature(draft) !== draftSignature(baselineDraft), [baselineDraft, draft]);
|
const hasUnsavedChanges = useMemo(() => draftSignature(draft) !== draftSignature(baselineDraft), [baselineDraft, draft]);
|
||||||
const draftAccessState = customerAccessState(draft.status, draft.menuKeys.length, draft.vehicleGrants.length);
|
const draftAccessState = customerAccessState(draft.status, draft.menuKeys.length, draft.vehicleGrants.length);
|
||||||
|
useSideSheetA11y(mobileFiltersOpen, '.v2-user-mobile-filter-sidesheet', 'v2-user-mobile-filters', '客户账号筛选', '关闭客户账号筛选');
|
||||||
useSideSheetA11y(editorVisible, '.v2-user-editor-sidesheet', 'v2-user-editor-sheet', '客户账号详情', '关闭账号详情');
|
useSideSheetA11y(editorVisible, '.v2-user-editor-sidesheet', 'v2-user-editor-sheet', '客户账号详情', '关闭账号详情');
|
||||||
useSideSheetA11y(Boolean(editingGrant), '.v2-user-grant-sidesheet', 'v2-user-grant-window', '车辆授权有效期', '关闭车辆授权有效期');
|
useSideSheetA11y(Boolean(editingGrant), '.v2-user-grant-sidesheet', 'v2-user-grant-window', '车辆授权有效期', '关闭车辆授权有效期');
|
||||||
|
|
||||||
@@ -330,6 +342,32 @@ export default function UsersPage() {
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
persistDraft();
|
persistDraft();
|
||||||
};
|
};
|
||||||
|
const openMobileFilters = () => {
|
||||||
|
setDraftCustomerKeyword(customerKeyword);
|
||||||
|
setDraftCustomerStatus(customerStatus);
|
||||||
|
setFiltersCollapsed(false);
|
||||||
|
};
|
||||||
|
const closeMobileFilters = () => {
|
||||||
|
setDraftCustomerKeyword(customerKeyword);
|
||||||
|
setDraftCustomerStatus(customerStatus);
|
||||||
|
setFiltersCollapsed(true);
|
||||||
|
};
|
||||||
|
const applyMobileFilters = () => {
|
||||||
|
setCustomerKeyword(draftCustomerKeyword);
|
||||||
|
setCustomerStatus(draftCustomerStatus);
|
||||||
|
setFiltersCollapsed(true);
|
||||||
|
};
|
||||||
|
const resetMobileFilters = () => {
|
||||||
|
setDraftCustomerKeyword('');
|
||||||
|
setDraftCustomerStatus('all');
|
||||||
|
setCustomerKeyword('');
|
||||||
|
setCustomerStatus('all');
|
||||||
|
setFiltersCollapsed(true);
|
||||||
|
};
|
||||||
|
const submitMobileFilters = (event: FormEvent) => {
|
||||||
|
event.preventDefault();
|
||||||
|
applyMobileFilters();
|
||||||
|
};
|
||||||
|
|
||||||
return <div className="v2-user-admin">
|
return <div className="v2-user-admin">
|
||||||
<div className="v2-user-discovery-shell">
|
<div className="v2-user-discovery-shell">
|
||||||
@@ -342,18 +380,58 @@ export default function UsersPage() {
|
|||||||
meta={<Typography.Text type="tertiary">本地账号 · 可扩展外部身份源</Typography.Text>}
|
meta={<Typography.Text type="tertiary">本地账号 · 可扩展外部身份源</Typography.Text>}
|
||||||
actions={<Button className="v2-workspace-mobile-tool-button is-primary" theme="solid" icon={<IconPlus />} aria-label="新建客户账号" onClick={startCreate}>新建客户账号</Button>}
|
actions={<Button className="v2-workspace-mobile-tool-button is-primary" theme="solid" icon={<IconPlus />} aria-label="新建客户账号" onClick={startCreate}>新建客户账号</Button>}
|
||||||
/>
|
/>
|
||||||
<WorkspaceFilterPanel
|
{mobileLayout ? <div className="v2-user-mobile-discovery">
|
||||||
|
<MobileFilterToggle
|
||||||
|
title="账号范围"
|
||||||
|
summary={`${customerScopeLabel(customerStatus)}${customerKeyword.trim() ? ` · ${customerKeyword.trim()}` : ''}`}
|
||||||
|
expanded={mobileFiltersOpen}
|
||||||
|
expandedLabel="关闭"
|
||||||
|
collapsedLabel="修改"
|
||||||
|
onToggle={mobileFiltersOpen ? closeMobileFilters : openMobileFilters}
|
||||||
|
/>
|
||||||
|
<SideSheet
|
||||||
|
className="v2-user-mobile-filter-sidesheet"
|
||||||
|
visible={mobileFiltersOpen}
|
||||||
|
placement="bottom"
|
||||||
|
height="min(62dvh, 480px)"
|
||||||
|
aria-label="客户账号筛选"
|
||||||
|
title={<div className="v2-user-mobile-filter-title"><strong>筛选客户账号</strong><span>按账号信息与权限状态缩小客户范围</span></div>}
|
||||||
|
onCancel={closeMobileFilters}
|
||||||
|
footer={<div className="v2-user-mobile-filter-footer"><Button theme="light" onClick={resetMobileFilters}>重置条件</Button><Button theme="solid" onClick={applyMobileFilters}>应用筛选</Button></div>}
|
||||||
|
>
|
||||||
|
<form className="v2-user-mobile-filter-form" onSubmit={submitMobileFilters}>
|
||||||
|
<section aria-label="客户账号筛选条件">
|
||||||
|
<header><strong>账号范围</strong><span>筛选仅在应用后更新客户目录,关闭不会丢失当前结果。</span></header>
|
||||||
|
<div>
|
||||||
|
<label>
|
||||||
|
<span>客户账号</span>
|
||||||
|
<Input aria-label="搜索客户账号" prefix={<IconSearch />} showClear value={draftCustomerKeyword} onChange={setDraftCustomerKeyword} placeholder="搜索名称、账号或客户标识" />
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<span id="v2-user-mobile-status-filter-label">访问状态</span>
|
||||||
|
<Select
|
||||||
|
aria-labelledby="v2-user-mobile-status-filter-label"
|
||||||
|
value={draftCustomerStatus}
|
||||||
|
onChange={(value) => setDraftCustomerStatus(String(value) as CustomerScopeFilter)}
|
||||||
|
optionList={customerScopeOptions}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</form>
|
||||||
|
</SideSheet>
|
||||||
|
</div> : <WorkspaceFilterPanel
|
||||||
className="v2-user-filter-panel"
|
className="v2-user-filter-panel"
|
||||||
title="账号范围"
|
title="账号范围"
|
||||||
description="按客户名称、登录账号与权限可用状态查找"
|
description="按客户名称、登录账号与权限可用状态查找"
|
||||||
mobileSummary={`${customerScopeLabel(customerStatus)}${customerKeyword.trim() ? ` · ${customerKeyword.trim()}` : ''}`}
|
mobileSummary={`${customerScopeLabel(customerStatus)}${customerKeyword.trim() ? ` · ${customerKeyword.trim()}` : ''}`}
|
||||||
expanded={!filtersCollapsed}
|
expanded
|
||||||
status={users.isPending ? '正在读取账号' : `${visibleCustomers.length} / ${customers.length} 个账号`}
|
status={users.isPending ? '正在读取账号' : `${visibleCustomers.length} / ${customers.length} 个账号`}
|
||||||
statusColor={customerKeyword.trim() || customerStatus !== 'all' ? 'blue' : 'grey'}
|
statusColor={customerKeyword.trim() || customerStatus !== 'all' ? 'blue' : 'grey'}
|
||||||
collapsedLabel="修改"
|
collapsedLabel="修改"
|
||||||
onToggle={() => setFiltersCollapsed((value) => !value)}
|
onToggle={() => setFiltersCollapsed((value) => !value)}
|
||||||
>
|
>
|
||||||
<div className={`v2-user-filter-form${filtersCollapsed ? ' is-mobile-collapsed' : ''}`}>
|
<div className="v2-user-filter-form">
|
||||||
<label className="v2-user-filter-keyword">
|
<label className="v2-user-filter-keyword">
|
||||||
<span>客户账号</span>
|
<span>客户账号</span>
|
||||||
<Input className="v2-user-list-search" aria-label="搜索客户账号" prefix={<IconSearch />} showClear value={customerKeyword} onChange={setCustomerKeyword} placeholder="搜索名称、账号或客户标识" />
|
<Input className="v2-user-list-search" aria-label="搜索客户账号" prefix={<IconSearch />} showClear value={customerKeyword} onChange={setCustomerKeyword} placeholder="搜索名称、账号或客户标识" />
|
||||||
@@ -364,12 +442,7 @@ export default function UsersPage() {
|
|||||||
aria-labelledby="v2-user-status-filter-label"
|
aria-labelledby="v2-user-status-filter-label"
|
||||||
value={customerStatus}
|
value={customerStatus}
|
||||||
onChange={(value) => setCustomerStatus(String(value) as typeof customerStatus)}
|
onChange={(value) => setCustomerStatus(String(value) as typeof customerStatus)}
|
||||||
optionList={[
|
optionList={customerScopeOptions}
|
||||||
{ value: 'all', label: '全部状态' },
|
|
||||||
{ value: 'enabled', label: '启用账号' },
|
|
||||||
{ value: 'disabled', label: '停用账号' },
|
|
||||||
{ value: 'attention', label: '待完善权限' }
|
|
||||||
]}
|
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<Button
|
<Button
|
||||||
@@ -387,7 +460,7 @@ export default function UsersPage() {
|
|||||||
清空
|
清空
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</WorkspaceFilterPanel>
|
</WorkspaceFilterPanel>}
|
||||||
</div>
|
</div>
|
||||||
<div className={`v2-user-admin-grid${creating || selected ? ' is-editor-open' : ''}`}>
|
<div className={`v2-user-admin-grid${creating || selected ? ' is-editor-open' : ''}`}>
|
||||||
<Card className="v2-user-list" bodyStyle={{ padding: 0 }}>
|
<Card className="v2-user-list" bodyStyle={{ padding: 0 }}>
|
||||||
|
|||||||
@@ -17378,3 +17378,146 @@
|
|||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Semi UI account directory discovery.
|
||||||
|
* Keep the customer list in the first viewport and apply mobile filters only
|
||||||
|
* after an explicit confirmation, matching alerts, access and history.
|
||||||
|
*/
|
||||||
|
@media (max-width: 680px) {
|
||||||
|
.v2-user-mobile-discovery {
|
||||||
|
display: grid;
|
||||||
|
min-width: 0;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v2-user-mobile-discovery > .v2-mobile-filter-toggle.semi-button {
|
||||||
|
min-height: 58px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v2-user-mobile-filter-sidesheet .semi-sidesheet-inner {
|
||||||
|
width: 100vw !important;
|
||||||
|
max-width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 18px 18px 0 0;
|
||||||
|
background: #f4f7fb;
|
||||||
|
box-shadow: 0 -20px 56px rgba(25, 45, 72, .2);
|
||||||
|
padding-bottom: env(safe-area-inset-bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
.v2-user-mobile-filter-sidesheet.semi-sidesheet-bottom .semi-sidesheet-header {
|
||||||
|
min-height: 68px;
|
||||||
|
border-bottom: 1px solid #dfe7f0;
|
||||||
|
background: rgba(255, 255, 255, .98);
|
||||||
|
padding: 11px 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v2-user-mobile-filter-sidesheet.semi-sidesheet-bottom .semi-sidesheet-body {
|
||||||
|
min-height: 0;
|
||||||
|
overflow-y: auto;
|
||||||
|
background: #f4f7fb;
|
||||||
|
padding: 12px;
|
||||||
|
overscroll-behavior: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v2-user-mobile-filter-sidesheet.semi-sidesheet-bottom .semi-sidesheet-footer {
|
||||||
|
border-top: 1px solid #dfe7f0;
|
||||||
|
background: rgba(255, 255, 255, .98);
|
||||||
|
padding: 10px 12px calc(10px + env(safe-area-inset-bottom));
|
||||||
|
}
|
||||||
|
|
||||||
|
.v2-user-mobile-filter-title {
|
||||||
|
display: grid;
|
||||||
|
gap: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v2-user-mobile-filter-title > strong {
|
||||||
|
color: #253a52;
|
||||||
|
font-size: 15px;
|
||||||
|
line-height: 1.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v2-user-mobile-filter-title > span {
|
||||||
|
color: #8090a3;
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v2-user-mobile-filter-form {
|
||||||
|
display: grid;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v2-user-mobile-filter-form > section {
|
||||||
|
display: grid;
|
||||||
|
gap: 12px;
|
||||||
|
border: 1px solid #dfe7f0;
|
||||||
|
border-radius: 12px;
|
||||||
|
background: #fff;
|
||||||
|
padding: 12px;
|
||||||
|
box-shadow: 0 8px 24px rgba(31, 53, 80, .055);
|
||||||
|
}
|
||||||
|
|
||||||
|
.v2-user-mobile-filter-form > section > header {
|
||||||
|
display: grid;
|
||||||
|
min-width: 0;
|
||||||
|
gap: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v2-user-mobile-filter-form > section > header > strong {
|
||||||
|
color: #2b4058;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v2-user-mobile-filter-form > section > header > span {
|
||||||
|
color: #7c8b9e;
|
||||||
|
font-size: 10px;
|
||||||
|
line-height: 1.45;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v2-user-mobile-filter-form > section > div {
|
||||||
|
display: grid;
|
||||||
|
gap: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v2-user-mobile-filter-form label {
|
||||||
|
display: grid;
|
||||||
|
min-width: 0;
|
||||||
|
gap: 6px;
|
||||||
|
color: #5f7188;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 650;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v2-user-mobile-filter-form label > .semi-input-wrapper,
|
||||||
|
.v2-user-mobile-filter-form label > .semi-select {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 44px;
|
||||||
|
border-radius: 9px;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v2-user-mobile-filter-form .semi-input,
|
||||||
|
.v2-user-mobile-filter-form .semi-select-selection-text {
|
||||||
|
height: 42px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v2-user-mobile-filter-footer {
|
||||||
|
display: grid;
|
||||||
|
width: 100%;
|
||||||
|
grid-template-columns: minmax(0, .72fr) minmax(0, 1.28fr);
|
||||||
|
gap: 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v2-user-mobile-filter-footer > .semi-button {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 44px;
|
||||||
|
border-radius: 9px;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user