feat(ia): polish project operation workspaces
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
<template>
|
||||
<div class="page page--flush">
|
||||
<div v-if="study.currentStudy" class="unified-shell">
|
||||
<section class="unified-section section--flush">
|
||||
<div class="filter-row">
|
||||
<el-form :inline="true" :model="filters">
|
||||
<el-form-item :label="TEXT.common.fields.site">
|
||||
<div class="page">
|
||||
<div v-if="study.currentStudy" class="page-inner">
|
||||
<!-- ==================== 表格卡片(含筛选栏) ==================== -->
|
||||
<div class="table-card">
|
||||
<div class="table-card-toolbar">
|
||||
<div class="toolbar-filters">
|
||||
<div class="filter-item">
|
||||
<span class="filter-label">{{ TEXT.common.fields.site }}</span>
|
||||
<el-select v-model="filters.center_id" clearable :placeholder="TEXT.common.placeholders.select" class="filter-select">
|
||||
<el-option
|
||||
v-for="site in sites"
|
||||
@@ -13,78 +15,96 @@
|
||||
:value="site.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="TEXT.common.fields.direction">
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<span class="filter-label">{{ TEXT.common.fields.direction }}</span>
|
||||
<el-select v-model="filters.direction" clearable :placeholder="TEXT.common.placeholders.select" class="filter-select">
|
||||
<el-option :label="TEXT.enums.shipmentDirection.SEND" value="SEND" />
|
||||
<el-option :label="TEXT.enums.shipmentDirection.RETURN" value="RETURN" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="TEXT.common.fields.status">
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<span class="filter-label">{{ TEXT.common.fields.status }}</span>
|
||||
<el-select v-model="filters.status" clearable :placeholder="TEXT.common.placeholders.select" class="filter-select">
|
||||
<el-option :label="TEXT.enums.shipmentStatus.PENDING" value="PENDING" />
|
||||
<el-option :label="TEXT.enums.shipmentStatus.IN_TRANSIT" value="IN_TRANSIT" />
|
||||
<el-option :label="TEXT.enums.shipmentStatus.SIGNED" value="SIGNED" />
|
||||
<el-option :label="TEXT.enums.shipmentStatus.EXCEPTION" value="EXCEPTION" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSearch">{{ TEXT.common.actions.search }}</el-button>
|
||||
<el-button @click="resetFilters">{{ TEXT.common.actions.reset }}</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-button v-if="canCreate" type="primary" @click="openCreate">{{ TEXT.modules.drugShipments.newTitle }}</el-button>
|
||||
</div>
|
||||
<div class="filter-actions">
|
||||
<el-button type="primary" size="small" @click="handleSearch" class="filter-btn">{{ TEXT.common.actions.search }}</el-button>
|
||||
<el-button size="small" @click="resetFilters" class="filter-btn">{{ TEXT.common.actions.reset }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="toolbar-right">
|
||||
<div v-if="items.length" class="filter-summary">
|
||||
{{ items.length }} 条记录
|
||||
</div>
|
||||
<el-button v-if="canCreate" type="primary" @click="openCreate" class="create-btn">
|
||||
<el-icon class="el-icon--left"><Plus /></el-icon>
|
||||
{{ TEXT.modules.drugShipments.newTitle }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="sortedItems"
|
||||
class="ctms-table shipment-table"
|
||||
class="shipment-table"
|
||||
style="width: 100%"
|
||||
table-layout="fixed"
|
||||
:row-class-name="shipmentRowClass"
|
||||
@row-click="onRowClick"
|
||||
>
|
||||
<el-table-column prop="site_name" :label="TEXT.common.fields.site" show-overflow-tooltip>
|
||||
<template #default="{ row }">{{ row.site_name || TEXT.common.fallback }}</template>
|
||||
<template #default="{ row }">
|
||||
<span class="cell-primary cell-nowrap">{{ row.site_name || TEXT.common.fallback }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="direction" :label="TEXT.common.fields.direction">
|
||||
<template #default="{ row }">
|
||||
<el-tag :class="['type-tag', row.direction === 'SEND' ? 'type-send' : 'type-return']" effect="light">
|
||||
<span :class="['dir-tag', row.direction === 'SEND' ? 'dir-tag--send' : 'dir-tag--return']">
|
||||
{{ displayEnum(TEXT.enums.shipmentDirection, row.direction) }}
|
||||
</el-tag>
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="ship_date" :label="TEXT.common.fields.shipDate">
|
||||
<template #default="{ row }">{{ displayDate(row.ship_date) }}</template>
|
||||
<template #default="{ row }"><span class="cell-nowrap">{{ displayDate(row.ship_date) }}</span></template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="receive_date" :label="TEXT.common.fields.receiveDate">
|
||||
<template #default="{ row }">{{ displayDate(row.receive_date) }}</template>
|
||||
<template #default="{ row }"><span class="cell-nowrap">{{ displayDate(row.receive_date) }}</span></template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="quantity" :label="TEXT.common.fields.quantity">
|
||||
<template #default="{ row }">{{ typeof row.quantity === "number" ? row.quantity : TEXT.common.fallback }}</template>
|
||||
<template #default="{ row }">
|
||||
<span class="cell-mono cell-nowrap">{{ typeof row.quantity === "number" ? row.quantity : TEXT.common.fallback }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="batch_no" :label="TEXT.common.fields.batchNo" show-overflow-tooltip>
|
||||
<template #default="{ row }">{{ row.batch_no || TEXT.common.fallback }}</template>
|
||||
<template #default="{ row }">
|
||||
<span class="cell-mono cell-nowrap">{{ row.batch_no || TEXT.common.fallback }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="status" :label="TEXT.common.fields.status">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="statusType(row.status)" effect="plain" round size="small" class="status-tag">
|
||||
<span :class="['status-pill', `status-pill--${statusType(row.status)}`]">
|
||||
{{ displayEnum(TEXT.enums.shipmentStatus, row.status) }}
|
||||
</el-tag>
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="remark" :label="TEXT.common.fields.remark" show-overflow-tooltip>
|
||||
<template #default="{ row }">{{ row.remark || TEXT.common.fallback }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="canUpdate || canDelete" :label="TEXT.common.labels.actions" width="130">
|
||||
<template #default="{ row }">
|
||||
<div class="shipment-actions">
|
||||
<el-button v-if="canUpdate" link type="primary" @click.stop="openEdit(row)">{{ TEXT.common.actions.edit }}</el-button>
|
||||
<span class="cell-muted cell-nowrap">{{ row.remark || TEXT.common.fallback }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="canUpdate || canDelete" :label="TEXT.common.labels.actions" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<div class="cell-actions">
|
||||
<el-button v-if="canUpdate" link type="primary" size="small" @click.stop="openEdit(row)">{{ TEXT.common.actions.edit }}</el-button>
|
||||
<el-button
|
||||
v-if="canDelete"
|
||||
link
|
||||
type="danger"
|
||||
size="small"
|
||||
:disabled="isInactiveSite(row.center_id)"
|
||||
@click.stop="remove(row)"
|
||||
>
|
||||
@@ -94,13 +114,19 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<template #empty>
|
||||
<div v-if="!loading" class="table-empty">{{ TEXT.modules.drugShipments.empty }}</div>
|
||||
<div v-if="!loading" class="table-empty">
|
||||
<div class="empty-icon">
|
||||
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="14" rx="2" ry="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></svg>
|
||||
</div>
|
||||
<span>{{ TEXT.modules.drugShipments.empty }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
<StateEmpty v-else :description="TEXT.common.empty.selectProject" />
|
||||
|
||||
<!-- ==================== 编辑抽屉 ==================== -->
|
||||
<el-drawer
|
||||
v-if="drawerVisible"
|
||||
v-model="drawerVisible"
|
||||
@@ -118,115 +144,92 @@
|
||||
</template>
|
||||
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-position="top" class="shipment-form">
|
||||
<!-- 基本信息分组 -->
|
||||
<div class="form-group">
|
||||
<div class="form-group-title">
|
||||
<span class="group-dot group-dot-basic"></span>
|
||||
<div class="form-section">
|
||||
<div class="form-section-title">
|
||||
<span class="dot dot--blue" />
|
||||
{{ TEXT.common.labels.basicInfo }}
|
||||
</div>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item :label="TEXT.common.fields.site" prop="center_id">
|
||||
<el-select v-model="form.center_id" :placeholder="TEXT.common.placeholders.select" class="full-width" :disabled="isFormReadOnly">
|
||||
<el-option
|
||||
v-for="site in sites"
|
||||
:key="site.id"
|
||||
:label="site.name || TEXT.common.fallback"
|
||||
:value="site.id"
|
||||
:disabled="!site.is_active"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item :label="TEXT.common.fields.direction" prop="direction">
|
||||
<el-select v-model="form.direction" :placeholder="TEXT.common.placeholders.select" class="full-width" :disabled="isFormReadOnly">
|
||||
<el-option :label="TEXT.enums.shipmentDirection.SEND" value="SEND" />
|
||||
<el-option :label="TEXT.enums.shipmentDirection.RETURN" value="RETURN" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item :label="TEXT.common.fields.status" prop="status">
|
||||
<el-select v-model="form.status" :placeholder="TEXT.common.placeholders.select" class="full-width" :disabled="isFormReadOnly">
|
||||
<el-option :label="TEXT.enums.shipmentStatus.PENDING" value="PENDING" />
|
||||
<el-option :label="TEXT.enums.shipmentStatus.IN_TRANSIT" value="IN_TRANSIT" />
|
||||
<el-option :label="TEXT.enums.shipmentStatus.SIGNED" value="SIGNED" />
|
||||
<el-option :label="TEXT.enums.shipmentStatus.EXCEPTION" value="EXCEPTION" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div class="field-grid field-grid--2">
|
||||
<el-form-item :label="TEXT.common.fields.site" prop="center_id">
|
||||
<el-select v-model="form.center_id" :placeholder="TEXT.common.placeholders.select" class="full-width" :disabled="isFormReadOnly">
|
||||
<el-option
|
||||
v-for="site in sites"
|
||||
:key="site.id"
|
||||
:label="site.name || TEXT.common.fallback"
|
||||
:value="site.id"
|
||||
:disabled="!site.is_active"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="TEXT.common.fields.direction" prop="direction">
|
||||
<el-select v-model="form.direction" :placeholder="TEXT.common.placeholders.select" class="full-width" :disabled="isFormReadOnly">
|
||||
<el-option :label="TEXT.enums.shipmentDirection.SEND" value="SEND" />
|
||||
<el-option :label="TEXT.enums.shipmentDirection.RETURN" value="RETURN" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<el-form-item :label="TEXT.common.fields.status" prop="status">
|
||||
<el-select v-model="form.status" :placeholder="TEXT.common.placeholders.select" class="full-width" :disabled="isFormReadOnly">
|
||||
<el-option :label="TEXT.enums.shipmentStatus.PENDING" value="PENDING" />
|
||||
<el-option :label="TEXT.enums.shipmentStatus.IN_TRANSIT" value="IN_TRANSIT" />
|
||||
<el-option :label="TEXT.enums.shipmentStatus.SIGNED" value="SIGNED" />
|
||||
<el-option :label="TEXT.enums.shipmentStatus.EXCEPTION" value="EXCEPTION" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<div v-if="selectedSiteInactive" class="inactive-hint">
|
||||
<span class="hint-icon">!</span>
|
||||
<span>中心已停用,当前记录不可编辑</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 运输记录分组 -->
|
||||
<div class="form-group">
|
||||
<div class="form-group-title">
|
||||
<span class="group-dot group-dot-record"></span>
|
||||
<div class="form-section">
|
||||
<div class="form-section-title">
|
||||
<span class="dot dot--amber" />
|
||||
{{ TEXT.modules.drugShipments.recordLabel }}
|
||||
</div>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item :label="TEXT.common.fields.shipDate" prop="ship_date" :required="requiresShipmentDetails(form.status)">
|
||||
<el-date-picker
|
||||
v-model="form.ship_date"
|
||||
type="date"
|
||||
value-format="YYYY-MM-DD"
|
||||
:placeholder="TEXT.common.placeholders.select"
|
||||
class="full-width"
|
||||
:disabled="isFormReadOnly"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item :label="TEXT.common.fields.receiveDate" prop="receive_date" :required="requiresReceiveDate(form.status)">
|
||||
<el-date-picker
|
||||
v-model="form.receive_date"
|
||||
type="date"
|
||||
value-format="YYYY-MM-DD"
|
||||
:placeholder="TEXT.common.placeholders.select"
|
||||
class="full-width"
|
||||
:disabled="isFormReadOnly"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item :label="TEXT.common.fields.quantity" prop="quantity" :required="requiresShipmentDetails(form.status)">
|
||||
<el-input-number v-model="form.quantity" :min="0" :controls="false" class="full-width" :disabled="isFormReadOnly" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item :label="TEXT.common.fields.batchNo" prop="batch_no" :required="requiresShipmentDetails(form.status)">
|
||||
<el-input v-model="form.batch_no" :placeholder="TEXT.common.placeholders.input + TEXT.common.fields.batchNo" :disabled="isFormReadOnly" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item :label="TEXT.common.fields.carrier" prop="carrier" :required="requiresShipmentDetails(form.status)">
|
||||
<el-input v-model="form.carrier" :placeholder="TEXT.common.placeholders.input + TEXT.common.fields.carrier" :disabled="isFormReadOnly" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item :label="TEXT.common.fields.trackingNo" prop="tracking_no" :required="requiresShipmentDetails(form.status)">
|
||||
<el-input v-model="form.tracking_no" :placeholder="TEXT.common.placeholders.input + TEXT.common.fields.trackingNo" :disabled="isFormReadOnly" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div class="field-grid field-grid--2">
|
||||
<el-form-item :label="TEXT.common.fields.shipDate" prop="ship_date" :required="requiresShipmentDetails(form.status)">
|
||||
<el-date-picker
|
||||
v-model="form.ship_date"
|
||||
type="date"
|
||||
value-format="YYYY-MM-DD"
|
||||
:placeholder="TEXT.common.placeholders.select"
|
||||
class="full-width"
|
||||
:disabled="isFormReadOnly"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="TEXT.common.fields.receiveDate" prop="receive_date" :required="requiresReceiveDate(form.status)">
|
||||
<el-date-picker
|
||||
v-model="form.receive_date"
|
||||
type="date"
|
||||
value-format="YYYY-MM-DD"
|
||||
:placeholder="TEXT.common.placeholders.select"
|
||||
class="full-width"
|
||||
:disabled="isFormReadOnly"
|
||||
/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div class="field-grid field-grid--2">
|
||||
<el-form-item :label="TEXT.common.fields.quantity" prop="quantity" :required="requiresShipmentDetails(form.status)">
|
||||
<el-input-number v-model="form.quantity" :min="0" :controls="false" class="full-width" :disabled="isFormReadOnly" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="TEXT.common.fields.batchNo" prop="batch_no" :required="requiresShipmentDetails(form.status)">
|
||||
<el-input v-model="form.batch_no" :placeholder="TEXT.common.placeholders.input + TEXT.common.fields.batchNo" :disabled="isFormReadOnly" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div class="field-grid field-grid--2">
|
||||
<el-form-item :label="TEXT.common.fields.carrier" prop="carrier" :required="requiresShipmentDetails(form.status)">
|
||||
<el-input v-model="form.carrier" :placeholder="TEXT.common.placeholders.input + TEXT.common.fields.carrier" :disabled="isFormReadOnly" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="TEXT.common.fields.trackingNo" prop="tracking_no" :required="requiresShipmentDetails(form.status)">
|
||||
<el-input v-model="form.tracking_no" :placeholder="TEXT.common.placeholders.input + TEXT.common.fields.trackingNo" :disabled="isFormReadOnly" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 备注分组 -->
|
||||
<div class="form-group">
|
||||
<div class="form-group-title">
|
||||
<span class="group-dot group-dot-remark"></span>
|
||||
<div class="form-section">
|
||||
<div class="form-section-title">
|
||||
<span class="dot dot--green" />
|
||||
{{ TEXT.common.fields.remark }}
|
||||
</div>
|
||||
<el-form-item prop="remark" :required="requiresRemark(form.status)">
|
||||
@@ -240,10 +243,9 @@
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<!-- 附件分组 -->
|
||||
<div class="form-group">
|
||||
<div class="form-group-title">
|
||||
<span class="group-dot group-dot-attachment"></span>
|
||||
<div class="form-section">
|
||||
<div class="form-section-title">
|
||||
<span class="dot dot--purple" />
|
||||
{{ TEXT.common.labels.attachments }}
|
||||
</div>
|
||||
<AttachmentList
|
||||
@@ -259,8 +261,8 @@
|
||||
|
||||
<template #footer>
|
||||
<div class="drawer-footer">
|
||||
<el-button @click="drawerVisible = false">{{ TEXT.common.actions.cancel }}</el-button>
|
||||
<el-button type="primary" :loading="saving" :disabled="isFormReadOnly" @click="saveForm">{{ TEXT.common.actions.save }}</el-button>
|
||||
<el-button @click="drawerVisible = false" class="footer-btn">{{ TEXT.common.actions.cancel }}</el-button>
|
||||
<el-button type="primary" :loading="saving" :disabled="isFormReadOnly" @click="saveForm" class="footer-btn footer-btn--primary">{{ TEXT.common.actions.save }}</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-drawer>
|
||||
@@ -271,6 +273,7 @@
|
||||
import { computed, onMounted, reactive, ref, watch } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { ElMessage, ElMessageBox, type FormInstance, type FormRules } from "element-plus";
|
||||
import { Plus } from "@element-plus/icons-vue";
|
||||
import { useAuthStore } from "../../store/auth";
|
||||
import { useStudyStore } from "../../store/study";
|
||||
import {
|
||||
@@ -569,16 +572,11 @@ const onRowClick = (row: ShipmentRow) => {
|
||||
|
||||
const statusType = (status: string) => {
|
||||
switch (status) {
|
||||
case "PENDING":
|
||||
return "info";
|
||||
case "IN_TRANSIT":
|
||||
return "primary";
|
||||
case "SIGNED":
|
||||
return "success";
|
||||
case "EXCEPTION":
|
||||
return "danger";
|
||||
default:
|
||||
return "info";
|
||||
case "PENDING": return "info";
|
||||
case "IN_TRANSIT": return "primary";
|
||||
case "SIGNED": return "success";
|
||||
case "EXCEPTION": return "danger";
|
||||
default: return "info";
|
||||
}
|
||||
};
|
||||
|
||||
@@ -642,31 +640,260 @@ onMounted(async () => {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* ==================== Base ==================== */
|
||||
.page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 4px 0;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.filter-row {
|
||||
.page-inner {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
/* ==================== Page Header ==================== */
|
||||
.create-btn {
|
||||
height: 34px;
|
||||
border-radius: 8px;
|
||||
padding: 0 16px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* ==================== Table Toolbar ==================== */
|
||||
.table-card-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 12px;
|
||||
align-items: center;
|
||||
padding: 14px 20px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.filter-select {
|
||||
width: 150px;
|
||||
.toolbar-filters {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
/* ========== 抽屉头部 ========== */
|
||||
.toolbar-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-left: auto;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.filter-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.filter-label {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
color: #8a8a8a;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.03em;
|
||||
}
|
||||
|
||||
.filter-select {
|
||||
width: 140px;
|
||||
}
|
||||
|
||||
.filter-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: flex-end;
|
||||
padding-bottom: 1px;
|
||||
}
|
||||
|
||||
.filter-btn {
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.filter-summary {
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: #a3a3a3;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* ==================== Table Card ==================== */
|
||||
.table-card {
|
||||
background: #ffffff;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
box-shadow:
|
||||
0 1px 2px rgba(0, 0, 0, 0.04),
|
||||
0 2px 8px rgba(0, 0, 0, 0.02);
|
||||
}
|
||||
|
||||
.shipment-table {
|
||||
--el-table-border-color: transparent;
|
||||
--el-table-row-hover-bg-color: #f8f9fb;
|
||||
}
|
||||
|
||||
.shipment-table :deep(.el-table__inner-wrapper::before) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.shipment-table :deep(th.el-table__cell) {
|
||||
background: #f4f5f7;
|
||||
color: #2a2a2a;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
padding: 14px 14px;
|
||||
border-bottom: 2px solid #e0e0e0 !important;
|
||||
}
|
||||
|
||||
.shipment-table :deep(th.el-table__cell .cell) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.shipment-table :deep(td.el-table__cell) {
|
||||
padding: 12px 14px;
|
||||
color: #0a0a0a;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.shipment-table :deep(.el-table__body tr) {
|
||||
cursor: pointer;
|
||||
transition: background 0.1s ease;
|
||||
}
|
||||
|
||||
.cell-nowrap {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.cell-primary {
|
||||
font-weight: 600;
|
||||
color: #0a0a0a;
|
||||
}
|
||||
|
||||
.cell-mono {
|
||||
font-family: ui-monospace, SFMono-Regular, monospace;
|
||||
font-size: 13px;
|
||||
color: #0a0a0a;
|
||||
}
|
||||
|
||||
.cell-muted {
|
||||
color: #737373;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.cell-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.cell-actions :deep(.el-button) {
|
||||
height: auto;
|
||||
padding: 0;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.cell-actions :deep(.el-button + .el-button) {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
/* ==================== Direction Tags ==================== */
|
||||
.dir-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 2px 10px;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
border-radius: 4px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.dir-tag--send {
|
||||
background: #fff7ed;
|
||||
color: #ea580c;
|
||||
}
|
||||
|
||||
.dir-tag--return {
|
||||
background: #f0fdf4;
|
||||
color: #16a34a;
|
||||
}
|
||||
|
||||
/* ==================== Status Pills ==================== */
|
||||
.status-pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 2px 12px;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
border-radius: 20px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.status-pill--info {
|
||||
background: #f5f5f5;
|
||||
color: #737373;
|
||||
}
|
||||
|
||||
.status-pill--primary {
|
||||
background: #eff4ff;
|
||||
color: #2563eb;
|
||||
}
|
||||
|
||||
.status-pill--success {
|
||||
background: #dcfce7;
|
||||
color: #16a34a;
|
||||
}
|
||||
|
||||
.status-pill--danger {
|
||||
background: #fef2f2;
|
||||
color: #dc2626;
|
||||
}
|
||||
|
||||
/* ==================== Empty State ==================== */
|
||||
.table-empty {
|
||||
min-height: 200px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 50%;
|
||||
background: #f5f5f5;
|
||||
color: #c4c4c4;
|
||||
}
|
||||
|
||||
.table-empty span {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: #a3a3a3;
|
||||
}
|
||||
|
||||
/* ==================== Drawer Editor ==================== */
|
||||
:deep(.shipment-editor-drawer > .el-drawer__header) {
|
||||
margin-bottom: 0;
|
||||
padding: 22px 20px 8px;
|
||||
padding: 20px 24px 8px;
|
||||
}
|
||||
|
||||
:deep(.shipment-editor-drawer > .el-drawer__body) {
|
||||
padding: 0 20px 4px;
|
||||
padding: 0 24px 4px;
|
||||
}
|
||||
|
||||
.editor-header {
|
||||
@@ -675,69 +902,61 @@ onMounted(async () => {
|
||||
}
|
||||
|
||||
.editor-title {
|
||||
font-size: 20px;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: var(--ctms-text-main);
|
||||
color: #0a0a0a;
|
||||
line-height: 1.2;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
|
||||
/* ========== 表单整体 ========== */
|
||||
.shipment-form {
|
||||
padding: 0 4px 0 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* ========== 分组卡片 ========== */
|
||||
.form-group {
|
||||
border: 1px solid #e8eef6;
|
||||
border-radius: 10px;
|
||||
padding: 16px 18px 8px;
|
||||
background: #fbfcfe;
|
||||
transition: border-color 0.2s ease;
|
||||
/* ==================== Form Sections ==================== */
|
||||
.form-section {
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
|
||||
.form-group:hover {
|
||||
border-color: #d0dced;
|
||||
.form-section + .form-section {
|
||||
margin-top: 20px;
|
||||
padding-top: 20px;
|
||||
border-top: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.form-group + .form-group {
|
||||
margin-top: 14px;
|
||||
}
|
||||
|
||||
.form-group-title {
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 14px;
|
||||
color: #1a3560;
|
||||
.form-section-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
gap: 10px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #0a0a0a;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.group-dot {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
.dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* 基本信息 - 蓝色 */
|
||||
.group-dot-basic {
|
||||
background: #3b82f6;
|
||||
.dot--blue { background: #3b82f6; }
|
||||
.dot--amber { background: #f59e0b; }
|
||||
.dot--green { background: #22c55e; }
|
||||
.dot--purple { background: #8b5cf6; }
|
||||
|
||||
.field-grid {
|
||||
display: grid;
|
||||
gap: 0 16px;
|
||||
}
|
||||
|
||||
/* 运输记录 - 琥珀色 */
|
||||
.group-dot-record {
|
||||
background: #f0ad2c;
|
||||
.field-grid--2 {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
|
||||
/* 备注 - 绿色 */
|
||||
.group-dot-remark {
|
||||
background: #22c55e;
|
||||
}
|
||||
|
||||
/* 附件 - 紫色 */
|
||||
.group-dot-attachment {
|
||||
background: #8b5cf6;
|
||||
.full-width {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.inactive-hint {
|
||||
@@ -745,12 +964,12 @@ onMounted(async () => {
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 8px 12px;
|
||||
background: #fff7d6;
|
||||
background: #fffbeb;
|
||||
border: 1px solid #fde68a;
|
||||
border-radius: 8px;
|
||||
font-size: 13px;
|
||||
color: #92400e;
|
||||
margin-bottom: 8px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.hint-icon {
|
||||
@@ -766,85 +985,62 @@ onMounted(async () => {
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
/* ========== 表单元素细节 ========== */
|
||||
.full-width {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* ========== 底部按钮 ========== */
|
||||
.drawer-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
/* ========== 表单元素微调 ========== */
|
||||
.footer-btn {
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
padding: 0 20px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.footer-btn--primary {
|
||||
padding: 0 24px;
|
||||
}
|
||||
|
||||
.shipment-form :deep(.el-form-item) {
|
||||
margin-bottom: 12px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.shipment-form :deep(.el-form-item__label) {
|
||||
font-size: 13px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: #4a6283;
|
||||
color: #525252;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
.shipment-table :deep(.el-table__inner-wrapper::before) {
|
||||
display: none;
|
||||
.shipment-form :deep(.el-form-item__error) {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
/* ========== 表格居中 ========== */
|
||||
.shipment-table :deep(th.el-table__cell .cell),
|
||||
.shipment-table :deep(td.el-table__cell .cell) {
|
||||
text-align: center;
|
||||
/* ==================== Responsive ==================== */
|
||||
@media (max-width: 960px) {
|
||||
.field-grid--2 {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.toolbar-filters {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
.filter-select {
|
||||
width: 100%;
|
||||
}
|
||||
.table-card-toolbar {
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
align-items: stretch;
|
||||
}
|
||||
}
|
||||
|
||||
.shipment-actions {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.shipment-actions :deep(.el-button) {
|
||||
height: auto;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.shipment-actions :deep(.el-button + .el-button) {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.type-tag {
|
||||
font-weight: 600;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.type-send {
|
||||
background-color: #fff7ed !important;
|
||||
color: #ea580c !important;
|
||||
}
|
||||
|
||||
.type-return {
|
||||
background-color: #fefce8 !important;
|
||||
color: #ca8a04 !important;
|
||||
}
|
||||
|
||||
.status-tag {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.table-empty {
|
||||
min-height: 220px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #8a97ab;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.02em;
|
||||
@media (max-width: 640px) {
|
||||
.create-btn {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -854,7 +1050,8 @@ onMounted(async () => {
|
||||
background-color: #fff7d6 !important;
|
||||
}
|
||||
|
||||
.row-inactive .el-tag {
|
||||
.row-inactive .status-pill,
|
||||
.row-inactive .dir-tag {
|
||||
opacity: 0.6;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user