2026-07-30 17:59:43 +08:00
< script setup lang = "ts" >
2026-07-31 13:43:16 +08:00
import { computed , onMounted , reactive , ref , watch } from 'vue'
2026-07-30 17:59:43 +08:00
import { ElMessage , ElMessageBox } from 'element-plus'
import { MapPin , Pencil , Plus , Power , RotateCcw , Search , Warehouse as WarehouseIcon } from '@lucide/vue'
import MetricStrip from '@/components/MetricStrip.vue'
import { useUnsavedWorkRegistration } from '@/composables/useUnsavedWorkRegistration'
2026-07-31 13:43:16 +08:00
import { getApiErrorDetails , type ApiErrorDetails } from '@/api/http'
import {
changePlatformWarehouseStatus ,
createPlatformWarehouse ,
getPlatformWarehouse ,
listPlatformWarehouses ,
updatePlatformWarehouse ,
type PlatformWarehouseDetailResource ,
type PlatformWarehouseResource ,
type SavePlatformWarehouseRequest ,
} from '@/api/platformWarehouses'
2026-07-30 17:59:43 +08:00
import { useAccessPreviewStore } from '@/stores/accessPreview'
import type { Warehouse } from '@/types/admin'
2026-07-31 13:43:16 +08:00
const tenantStorageKey = 'cc-admin-warehouse-tenant-id'
2026-07-30 17:59:43 +08:00
const access = useAccessPreviewStore ()
const editorOpen = ref ( false )
2026-07-31 13:43:16 +08:00
const saving = ref ( false )
const loading = ref ( false )
2026-07-30 17:59:43 +08:00
const page = ref ( 1 )
const pageSize = 10
2026-07-31 13:43:16 +08:00
const tenantInput = ref ( sessionStorage . getItem ( tenantStorageKey ) || '' )
const loadedTenantId = ref < number | null >( null )
const loadError = ref < ApiErrorDetails | null >( null )
const detailWarningCount = ref ( 0 )
const statusLoadingIds = ref ( new Set < string >())
2026-07-30 17:59:43 +08:00
const filters = reactive ({ keyword : '' , status : '' as Warehouse [ 'status' ] | '' })
2026-07-31 13:43:16 +08:00
let loadSequence = 0
interface WarehouseRow extends Warehouse {
detail : PlatformWarehouseDetailResource | null
}
const rows = ref < WarehouseRow [] >([])
2026-07-30 17:59:43 +08:00
const emptyForm = () : Omit < Warehouse , ' updatedAt ' > => ({
id : '' , code : '' , name : '' , province : '' , city : '' , address : '' , contactName : '' , contactPhone : '' ,
businessHours : '周一至周六 08:30-18:00' , status : 'ACTIVE' , remark : '' ,
})
const form = reactive ( emptyForm ())
const editorLabel = computed (() => form . id ? '编辑收货仓' : '新增收货仓' )
2026-07-31 15:34:35 +08:00
const editorPermission = computed (() => form . id ? 'platform:warehouse:edit' : 'platform:warehouse:create' )
2026-07-30 17:59:43 +08:00
const { captureBaseline } = useUnsavedWorkRegistration ({
key : 'warehouse-editor' ,
label : editorLabel ,
isOpen : editorOpen ,
model : form ,
requiredPermission : editorPermission ,
})
2026-07-31 13:43:16 +08:00
function parseTenantId ( value = tenantInput . value ) {
const normalized = value . trim ()
if ( ! /^[1-9]\d*$/ . test ( normalized )) return null
const tenantId = Number ( normalized )
return Number . isSafeInteger ( tenantId ) ? tenantId : null
}
const tenantMatchesLoaded = computed (() => parseTenantId () === loadedTenantId . value )
const filteredRows = computed (() => rows . value . filter (( warehouse ) => {
2026-07-30 17:59:43 +08:00
const keyword = filters . keyword . trim (). toLowerCase ()
const matchesKeyword = ! keyword || [ warehouse . code , warehouse . name , warehouse . province , warehouse . city , warehouse . address ]
. some (( value ) => value . toLowerCase (). includes ( keyword ))
return matchesKeyword && ( ! filters . status || warehouse . status === filters . status )
}))
const pagedRows = computed (() => filteredRows . value . slice (( page . value - 1 ) * pageSize , page . value * pageSize ))
const metrics = computed (() => [
2026-07-31 13:43:16 +08:00
{ label : '全部收货仓' , value : rows . value . length , note : loadedTenantId . value ? `租户 ${ loadedTenantId . value } ` : '等待查询租户' },
{ label : '启用仓库' , value : rows . value . filter (( item ) => item . status === 'ACTIVE' ). length , note : '客户当前可选择' },
{ label : '暂停接单' , value : rows . value . filter (( item ) => item . status === 'INACTIVE' ). length , note : '不出现在客户下单选项' },
{ label : '覆盖城市' , value : new Set ( rows . value . map (( item ) => item . city ). filter ( Boolean )). size , note : '国内收货区域' },
2026-07-30 17:59:43 +08:00
])
2026-07-31 13:43:16 +08:00
const loadErrorDescription = computed (() => {
if ( ! loadError . value ) return ''
const references = [
loadError . value . code ? `错误码 ${ loadError . value . code } ` : '' ,
loadError . value . requestId ? `requestId ${ loadError . value . requestId } ` : '' ,
loadError . value . traceId ? `traceId ${ loadError . value . traceId } ` : '' ,
]. filter ( Boolean )
const retained = loadedTenantId . value ? `当前仍显示租户 ${ loadedTenantId . value } 最近一次成功加载的数据。` : ''
return [ loadError . value . message , references . join ( ' · ' ), retained ]. filter ( Boolean ). join ( ' ' )
})
2026-07-30 17:59:43 +08:00
watch ( filters , () => { page . value = 1 })
watch (
2026-07-31 15:34:35 +08:00
() => access . can ( editorPermission . value ),
2026-07-30 17:59:43 +08:00
( canWrite ) => {
if ( canWrite || ! editorOpen . value ) return
editorOpen . value = false
Object . assign ( form , emptyForm ())
},
{ flush : 'sync' },
)
2026-07-31 13:43:16 +08:00
function toRow ( warehouse : PlatformWarehouseResource , detail : PlatformWarehouseDetailResource | null ) : WarehouseRow {
const address = detail ? . addresses [ 0 ]
const receiving = detail ? . capabilities . find (( item ) => item . capabilityCode === 'RECEIVING' )
return {
id : String ( warehouse . id ),
code : warehouse . warehouseCode ,
name : warehouse . warehouseName ,
province : address ? . stateProvince || '' ,
city : address ? . city || '' ,
address : [ address ? . district , address ? . addressLine1 , address ? . addressLine2 ]. filter ( Boolean ). join ( ' ' ),
contactName : warehouse . contactName || '' ,
contactPhone : warehouse . contactPhone || '' ,
businessHours : receiving ? . description || ( receiving ? . cutoffTime ? `每日截单 ${ receiving . cutoffTime } ` : '' ),
status : warehouse . status ,
remark : warehouse . remark || '' ,
updatedAt : warehouse . updatedAt ? . replace ( 'T' , ' ' ). slice ( 0 , 16 ) || '' ,
detail ,
}
}
2026-07-30 17:59:43 +08:00
function resetFilters () {
filters . keyword = ''
filters . status = ''
}
2026-07-31 13:43:16 +08:00
async function loadWarehouses ( requestedTenantId = parseTenantId ()) {
if ( ! requestedTenantId ) {
ElMessage . warning ( '请输入有效的正整数租户 ID' )
return
}
const sequence = ++ loadSequence
loading . value = true
loadError . value = null
try {
const result = await listPlatformWarehouses ( requestedTenantId , 1 , 200 )
const details = await Promise . allSettled (
result . records . map (( warehouse ) => getPlatformWarehouse ( requestedTenantId , warehouse . id )),
)
if ( sequence !== loadSequence ) return
rows . value = result . records . map (( warehouse , index ) => {
const detailResult = details [ index ]
return toRow ( warehouse , detailResult ? . status === 'fulfilled' ? detailResult . value : null )
})
detailWarningCount . value = details . filter (( item ) => item . status === 'rejected' ). length
loadedTenantId . value = requestedTenantId
tenantInput . value = String ( requestedTenantId )
sessionStorage . setItem ( tenantStorageKey , tenantInput . value )
page . value = 1
} catch ( error ) {
if ( sequence !== loadSequence ) return
loadError . value = getApiErrorDetails ( error , '仓库数据加载失败,请稍后重试' )
} finally {
if ( sequence === loadSequence ) loading . value = false
}
}
function requireCurrentTenant () {
if ( ! loadedTenantId . value ) {
ElMessage . warning ( '请先查询目标租户的仓库数据' )
return null
}
if ( ! tenantMatchesLoaded . value ) {
ElMessage . warning ( '租户 ID 已变更,请先重新查询后再维护仓库' )
return null
}
return loadedTenantId . value
}
2026-07-30 17:59:43 +08:00
function openCreate () {
2026-07-31 13:43:16 +08:00
if ( ! requireCurrentTenant ()) return
2026-07-30 17:59:43 +08:00
Object . assign ( form , emptyForm ())
captureBaseline ()
editorOpen . value = true
}
2026-07-31 13:43:16 +08:00
async function ensureDetail ( warehouse : WarehouseRow ) {
if ( warehouse . detail ) return warehouse . detail
const tenantId = requireCurrentTenant ()
if ( ! tenantId ) return null
try {
const detail = await getPlatformWarehouse ( tenantId , warehouse . id )
Object . assign ( warehouse , toRow ( detail . warehouse , detail ))
detailWarningCount . value = rows . value . filter (( item ) => ! item . detail ). length
return detail
} catch ( error ) {
const details = getApiErrorDetails ( error , '仓库详情加载失败,请重试' )
ElMessage . error ( details . requestId ? ` ${ details . message } ( requestId: ${ details . requestId } ) ` : details . message )
return null
}
}
async function openEdit ( warehouse : WarehouseRow ) {
const detail = await ensureDetail ( warehouse )
if ( ! detail ) return
const address = detail . addresses [ 0 ]
Object . assign ( form , warehouse , {
province : address ? . stateProvince || '' ,
city : address ? . city || '' ,
address : address ? . addressLine1 || '' ,
})
2026-07-30 17:59:43 +08:00
captureBaseline ()
editorOpen . value = true
}
2026-07-31 13:43:16 +08:00
function nullable ( value : string ) {
return value . trim () || null
}
function buildSaveRequest ( tenantId : number ) : SavePlatformWarehouseRequest {
const existing = form . id ? rows . value . find (( item ) => item . id === form . id ) ? . detail : null
const warehouse = existing ? . warehouse
const currentAddress = existing ? . addresses [ 0 ]
const capabilities = ( existing ? . capabilities || []). map (( capability ) => ({
capabilityCode : capability . capabilityCode ,
capabilityName : capability . capabilityName ,
enabled : capability . enabled ?? true ,
cutoffTime : capability . cutoffTime ,
description : capability . capabilityCode === 'RECEIVING' ? nullable ( form . businessHours ) : capability . description ,
}))
if ( ! capabilities . some (( item ) => item . capabilityCode === 'RECEIVING' )) {
capabilities . push ({
capabilityCode : 'RECEIVING' ,
capabilityName : '收货服务' ,
enabled : true ,
cutoffTime : null ,
description : nullable ( form . businessHours ),
})
}
return {
tenantId ,
warehouseCode : form . code . trim (). toUpperCase (),
warehouseName : form . name . trim (),
warehouseType : warehouse ? . warehouseType || 'DOMESTIC' ,
countryCode : warehouse ? . countryCode || 'CN' ,
timezone : warehouse ? . timezone || 'Asia/Shanghai' ,
contactName : nullable ( form . contactName ),
contactPhone : nullable ( form . contactPhone ),
contactEmail : warehouse ? . contactEmail || null ,
status : form . status ,
remark : nullable ( form . remark ),
address : {
addressType : currentAddress ? . addressType || 'OPERATING' ,
countryCode : currentAddress ? . countryCode || warehouse ? . countryCode || 'CN' ,
stateProvince : nullable ( form . province ),
city : form . city . trim (),
district : currentAddress ? . district || null ,
postalCode : currentAddress ? . postalCode || null ,
addressLine1 : form . address . trim (),
addressLine2 : currentAddress ? . addressLine2 || null ,
latitude : currentAddress ? . latitude || null ,
longitude : currentAddress ? . longitude || null ,
},
capabilities ,
}
}
async function saveWarehouse () {
const tenantId = requireCurrentTenant ()
if ( ! tenantId ) return
2026-07-30 17:59:43 +08:00
if ( ! form . code . trim () || ! form . name . trim () || ! form . city . trim () || ! form . address . trim ()) {
ElMessage . warning ( '请填写仓库编码、名称、城市和详细地址' )
return
}
2026-07-31 13:43:16 +08:00
saving . value = true
2026-07-30 17:59:43 +08:00
try {
2026-07-31 13:43:16 +08:00
const payload = buildSaveRequest ( tenantId )
if ( form . id ) await updatePlatformWarehouse ( tenantId , form . id , payload )
else await createPlatformWarehouse ( payload )
const successMessage = form . id ? '仓库资料已更新' : '收货仓已创建'
2026-07-30 17:59:43 +08:00
editorOpen . value = false
2026-07-31 13:43:16 +08:00
ElMessage . success ( successMessage )
await loadWarehouses ( tenantId )
2026-07-30 17:59:43 +08:00
} catch ( error ) {
2026-07-31 13:43:16 +08:00
const details = getApiErrorDetails ( error , '仓库保存失败,请检查后重试' )
ElMessage . error ( details . requestId ? ` ${ details . message } ( requestId: ${ details . requestId } ) ` : details . message )
} finally {
saving . value = false
2026-07-30 17:59:43 +08:00
}
}
2026-07-31 13:43:16 +08:00
async function toggleStatus ( warehouse : WarehouseRow ) {
const tenantId = requireCurrentTenant ()
if ( ! tenantId ) return
2026-07-30 17:59:43 +08:00
const target = warehouse . status === 'ACTIVE' ? 'INACTIVE' : 'ACTIVE'
const targetText = target === 'ACTIVE' ? '启用' : '停用'
try {
await ElMessageBox . confirm (
target === 'INACTIVE'
? `停用“ ${ warehouse . name } ”后,客户新建头程订单时将无法选择该仓库。`
: `确认重新启用“ ${ warehouse . name } ”供客户下单选择吗?` ,
` ${ targetText } 收货仓` ,
{ confirmButtonText : `确认 ${ targetText } ` , cancelButtonText : '取消' , type : target === 'INACTIVE' ? 'warning' : 'info' },
)
2026-07-31 13:43:16 +08:00
statusLoadingIds . value = new Set ( statusLoadingIds . value ). add ( warehouse . id )
await changePlatformWarehouseStatus ( tenantId , warehouse . id , target )
warehouse . status = target
if ( warehouse . detail ) warehouse . detail . warehouse . status = target
2026-07-30 17:59:43 +08:00
ElMessage . success ( `仓库已 ${ targetText } ` )
} catch ( error ) {
if ( error === 'cancel' || error === 'close' ) return
2026-07-31 13:43:16 +08:00
const details = getApiErrorDetails ( error , `仓库 ${ targetText } 失败,请重试` )
ElMessage . error ( details . requestId ? ` ${ details . message } ( requestId: ${ details . requestId } ) ` : details . message )
} finally {
const next = new Set ( statusLoadingIds . value )
next . delete ( warehouse . id )
statusLoadingIds . value = next
2026-07-30 17:59:43 +08:00
}
}
2026-07-31 13:43:16 +08:00
onMounted (() => {
const tenantId = parseTenantId ()
if ( tenantId ) void loadWarehouses ( tenantId )
})
2026-07-30 17:59:43 +08:00
</ script >
< template >
< div class = "business-page" >
2026-07-31 13:43:16 +08:00
< section class = "surface-panel tenant-panel" aria -label =" 目标租户 " >
< div class = "tenant-copy" >
< h2 > 目标租户 </ h2 >
< p > 仓库数据按租户隔离 , 请输入企业租户 ID 后查询 。 </ p >
</ div >
< div class = "tenant-query" >
< el-input
v-model = "tenantInput"
inputmode = "numeric"
maxlength = "19"
placeholder = "请输入租户 ID"
aria -label =" 目标租户 ID "
@keyup.enter ="loadWarehouses()"
/>
< el-button type = "primary" :loading = "loading" @click ="loadWarehouses()" > 查询仓库 </ el -button >
</ div >
</ section >
< el-alert v-if = "loadError" type="error" :closable="false" show-icon class="state-alert" >
< template # title > 仓库数据加载失败 </ template >
< p >{{ loadErrorDescription }}</ p >
< el-button size = "small" @click ="loadWarehouses()" > 重新查询 </ el -button >
</ el-alert >
< el-alert v-else-if = "detailWarningCount" type="warning" :closable="false" show-icon class="state-alert" >
< template # title > 部分仓库详情暂未加载 </ template >
< p >{{ detailWarningCount }} 个仓库缺少地址或服务能力详情 ; 编辑时会再次尝试加载 , 不会用模拟数据补齐 。 </ p >
</ el-alert >
2026-07-30 17:59:43 +08:00
< MetricStrip :items = "metrics" />
< section class = "surface-panel filter-panel" aria -label =" 仓库筛选 " >
< div class = "filter-fields" >
< el-input v-model = "filters.keyword" clearable placeholder="仓库名称、编码、城市或地址" aria-label="仓库关键词" >
< template # prefix >< Search :size = "16" /></ template >
</ el-input >
< el-select v-model = "filters.status" clearable placeholder="全部状态" aria-label="仓库状态" >
< el-option label = "启用" value = "ACTIVE" />< el-option label = "停用" value = "INACTIVE" />
</ el-select >
</ div >
< div class = "filter-actions" >
< el-button @click ="resetFilters" >< RotateCcw :size = "16" /> 重置 </ el-button >
2026-07-31 13:43:16 +08:00
< el-button
2026-07-31 15:34:35 +08:00
v-if = "access.can('platform:warehouse:create')"
2026-07-31 13:43:16 +08:00
type = "primary"
: disabled = "!tenantMatchesLoaded || loading"
@click ="openCreate"
>< Plus :size = "16" /> 新增仓库 </ el-button >
2026-07-30 17:59:43 +08:00
</ div >
</ section >
2026-07-31 13:43:16 +08:00
< section class = "surface-panel table-panel" aria -live =" polite " >
< div class = "panel-heading" >
< div >
< h2 > 平台收货仓 </ h2 >
< p v-if = "loadedTenantId" > 当前数据 : 租户 {{ loadedTenantId }} · 仅启用仓库会进入客户头程下单选项 </ p >
< p v-else > 查询租户后显示服务端仓库数据 </ p >
</ div >
</ div >
< div v-loading = "loading" class="warehouse-table-state" >
< el-table v-if = "pagedRows.length" :data="pagedRows" row-key="id" >
< el-table-column label = "操作" width = "180" fixed = "left" >
< template # default = "{ row }" >
2026-07-31 15:34:35 +08:00
< div v-if = "access.can('platform:warehouse:edit') || access.can('platform:warehouse:status')" class="table-actions" >
< el-button v-if = "access.can('platform:warehouse:edit')" link type="primary" :disabled="!tenantMatchesLoaded" @click="openEdit(row as WarehouseRow)"><Pencil :size="15" /> 编辑 </ el-button >
2026-07-31 13:43:16 +08:00
< el-button
2026-07-31 15:34:35 +08:00
v-if = "access.can('platform:warehouse:status')"
2026-07-31 13:43:16 +08:00
link
: type = "(row as WarehouseRow).status === 'ACTIVE' ? 'danger' : 'success'"
:disabled = "!tenantMatchesLoaded"
: loading = "statusLoadingIds.has((row as WarehouseRow).id)"
@click ="toggleStatus(row as WarehouseRow)"
>< Power :size = "15" />{{ ( row as WarehouseRow ). status === 'ACTIVE' ? '停用' : '启用' }}</ el-button >
</ div >
< span v-else class = "warehouse-subline" > 仅查看 </ span >
</ template >
</ el-table-column >
< el-table-column label = "仓库" min -width =" 200 " >
< template # default = "{ row }" >< div class = "warehouse-cell" >< span >< WarehouseIcon :size = "18" /></ span >< div >< strong >{{ ( row as WarehouseRow ). name }}</ strong >< small >{{ ( row as WarehouseRow ). code }}</ small ></ div ></ div ></ template >
</ el-table-column >
< el-table-column label = "所在城市" min -width =" 130 " >< template # default = "{ row }" >{{ [( row as WarehouseRow ). province , ( row as WarehouseRow ). city ]. filter ( Boolean ). join ( ' · ' ) || '-' }}</ template ></ el-table-column >
< el-table-column label = "收货地址" min -width =" 260 " >< template # default = "{ row }" >< div class = "warehouse-address" >< MapPin :size = "15" />{{ ( row as WarehouseRow ). address || '详情暂未加载' }}</ div ></ template ></ el-table-column >
< el-table-column label = "联系人" min -width =" 150 " >< template # default = "{ row }" >< strong >{{ ( row as WarehouseRow ). contactName || '-' }}</ strong >< span class = "warehouse-subline" >{{ ( row as WarehouseRow ). contactPhone || '-' }}</ span ></ template ></ el-table-column >
< el-table-column label = "收货时间" min -width =" 190 " >< template # default = "{ row }" >{{ ( row as WarehouseRow ). businessHours || '-' }}</ template ></ el-table-column >
< el-table-column label = "状态" width = "100" >< template # default = "{ row }" >< el-tag : type = "(row as WarehouseRow).status === 'ACTIVE' ? 'success' : 'info'" effect = "light" >{{ ( row as WarehouseRow ). status === 'ACTIVE' ? '启用' : '停用' }}</ el-tag ></ template ></ el-table-column >
</ el-table >
< el-empty v-else-if = "!loading" :description="loadedTenantId ? '该租户暂无符合条件的仓库' : '请输入租户 ID 并查询真实仓库数据'" />
</ div >
< div v-if = "loadedTenantId" class="table-footer"><span>共 {{ filteredRows.length }} 个收货仓</span><el-pagination v-model:current-page="page" :page-size="pageSize" :total="filteredRows.length" layout="prev, pager, next" /></ div >
2026-07-30 17:59:43 +08:00
</ section >
< el-drawer v-model = "editorOpen" :title="editorLabel" size="620px" class="business-drawer editor-drawer" :close-on-click-modal="false" >
2026-07-31 13:43:16 +08:00
< el-form label -position =" top " class = "drawer-form" :disabled = "saving" >
< div class = "form-section" >< h3 > 仓库标识 </ h3 >< div class = "form-grid two-cols" >< el-form-item label = "仓库编码" required >< el-input v-model = "form.code" maxlength="50" placeholder="例如 CN-NB-01" /></el-form-item><el-form-item label="仓库名称" required><el-input v-model="form.name" maxlength="120" placeholder="客户下单时显示的名称" /></el-form-item><el-form-item label="状态"><el-radio-group v-model="form.status"><el-radio-button value="ACTIVE">启用</el-radio-button><el-radio-button value="INACTIVE" > 停用 </ el -radio-button ></ el-radio-group ></ el-form-item ></ div ></ div >
< div class = "form-section" >< h3 > 地址与收货 </ h3 >< div class = "form-grid two-cols" >< el-form-item label = "省份" >< el-input v-model = "form.province" maxlength="100" /></el-form-item><el-form-item label="城市" required><el-input v-model="form.city" maxlength="100" /></el-form-item><el-form-item label="联系人"><el-input v-model="form.contactName" maxlength="64" /></el-form-item><el-form-item label="联系电话"><el-input v-model="form.contactPhone" maxlength="32" /></el-form-item><el-form-item label="收货时间"><el-input v-model="form.businessHours" maxlength="500" /></el-form-item></div><el-form-item label="详细地址" required><el-input v-model="form.address" maxlength="255" type="textarea" :rows="3" /></ el-form-item ></ div >
2026-07-30 17:59:43 +08:00
< div class = "form-section" >< h3 > 备注 </ h3 >< el-form-item label = "客户可理解的收货说明" >< el-input v-model = "form.remark" maxlength="500" type="textarea" :rows="3" show -word-limit /></ el-form-item ></ div >
</ el-form >
2026-07-31 13:43:16 +08:00
< template # footer >< div class = "drawer-footer" >< el-button :disabled = "saving" @click ="editorOpen = false" > 取消 </ el -button >< el-button type = "primary" :loading = "saving" @click ="saveWarehouse" > 保存仓库 </ el -button ></ div ></ template >
2026-07-30 17:59:43 +08:00
</ el-drawer >
</ div >
</ template >
< style scoped >
2026-07-31 13:43:16 +08:00
. tenant - panel { display : flex ; align - items : flex - end ; justify - content : space - between ; gap : 24 px ; }
. tenant - copy h2 { margin : 0 ; font - size : 16 px ; }
. tenant - copy p { margin : 6 px 0 0 ; color : var ( -- cc - muted ); font - size : 13 px ; }
. tenant - query { display : flex ; width : min ( 100 % , 390 px ); gap : 10 px ; }
. tenant - query . el - input { flex : 1 ; }
. state - alert p { margin : 6 px 0 10 px ; line - height : 1.55 ; }
. warehouse - table - state { min - height : 180 px ; }
2026-07-30 17:59:43 +08:00
. warehouse - cell { display : flex ; align - items : center ; gap : 10 px ; }
. warehouse - cell > span { display : grid ; width : 34 px ; height : 34 px ; flex : 0 0 34 px ; place - items : center ; border - radius : 7 px ; background : # eef3fa ; color : var ( -- cc - primary ); }
. warehouse - cell > div { display : flex ; min - width : 0 ; flex - direction : column ; gap : 3 px ; }
2026-07-31 13:43:16 +08:00
. warehouse - cell strong { overflow - wrap : anywhere ; }
2026-07-30 17:59:43 +08:00
. warehouse - cell small , . warehouse - subline { display : block ; color : var ( -- cc - muted ); font - size : 11 px ; }
2026-07-31 13:43:16 +08:00
. warehouse - address { display : flex ; align - items : flex - start ; gap : 7 px ; line - height : 1.55 ; overflow - wrap : anywhere ; }
2026-07-30 17:59:43 +08:00
. warehouse - address svg { flex : 0 0 15 px ; margin - top : 2 px ; color : var ( -- cc - muted ); }
2026-07-31 13:43:16 +08:00
@ media ( max - width : 640 px ) {
. tenant - panel { align - items : stretch ; flex - direction : column ; gap : 16 px ; }
. tenant - query { width : 100 % ; }
. tenant - query . el - button { flex : 0 0 auto ; }
}
@ media ( max - width : 420 px ) {
. tenant - query { flex - direction : column ; }
. tenant - query . el - button { width : 100 % ; }
}
2026-07-30 17:59:43 +08:00
</ style >