feat:编辑页响应式样式修改

This commit is contained in:
R524809
2026-09-16 17:21:33 +08:00
parent a66b07652b
commit 52ef75f719
10 changed files with 145 additions and 34 deletions
+5
View File
@@ -2,6 +2,11 @@
min-height: 100vh;
}
/* 除左侧菜单外的完整内容区域,供页面按实际可用宽度响应。 */
.main-content-layout {
container-type: inline-size;
}
.main-sider {
background: var(--sider-bg) !important;
display: flex;
@@ -55,6 +55,16 @@
gap: 16px;
}
/*
* 主内容区不足 1360px 时,采集面板移至编辑卡片下方;大于等于该宽度时
* 保持两列。断点依据除菜单栏外的实际内容宽度,不依赖浏览器窗口宽度。
*/
@container (max-width: 1199px) {
.editor-workspace {
grid-template-columns: 1fr;
}
}
/* 区块卡片公共外框 */
.editor-section.ant-card {
border: 1px solid #e9e7ef;
@@ -69,6 +79,52 @@
font-weight: 600;
}
/*
* 01 / 02 / 04 共用的横向表单:Label 与控件列作为最大 780px 的整体居中。
* 宽度充足时控件不超过 680px;卡片变窄时先吃掉两侧留白,再收缩控件列。
* 容器查询以卡片实际宽度为准,不受左侧菜单或采集面板布局的间接影响。
*/
.editor-horizontal-section.ant-card {
container-type: inline-size;
}
.editor-horizontal-section .ant-form-item-row {
justify-content: center;
min-width: 0;
}
.editor-horizontal-section .ant-form-item-label {
flex: 0 0 100px !important;
max-width: 100px !important;
}
.editor-horizontal-section .ant-form-item-control {
flex: 0 1 min(680px, calc(100% - 100px)) !important;
width: auto;
max-width: min(680px, calc(100% - 100px)) !important;
min-width: 0;
}
/*
* 100px 标签列与包装尺寸输入组在 480px 以下已没有足够可读空间;此时才
* 切换为纵向表单。该断点读取卡片宽度,非浏览器窗口宽度。
*/
@container (max-width: 479px) {
.editor-horizontal-section .ant-form-item-row {
flex-direction: column;
row-gap: 4px;
}
.editor-horizontal-section .ant-form-item-row > .ant-form-item-label {
flex: 0 0 auto !important;
max-width: 100% !important;
padding: 0;
text-align: left;
}
.editor-horizontal-section .ant-form-item-row > .ant-form-item-control {
flex: 1 1 auto !important;
width: 100%;
max-width: 100% !important;
min-width: 0;
}
}
/* 表单栅格(主要信息与其它区块使用) */
.editor-form-grid {
display: grid;
@@ -123,12 +179,6 @@
display: flex;
}
@media (max-width: 1180px) {
/* 窄屏:采集面板移到主编辑列下方 */
.editor-workspace {
grid-template-columns: 1fr;
}
}
/*
* ≤700px:整张表单切换为纵向布局(标签在上、控件在下)。
* 更宽时保持"标签 | 控件"同行,随宽度缩小优先压缩控件长度
@@ -136,17 +186,23 @@
* labelCol/wrapperCol 以内联样式注入 flex,故此处需要 !important 覆盖。
*/
@media (max-width: 700px) {
.product-editor .ant-form-item-row {
.product-editor .editor-section:not(.editor-horizontal-section) .ant-form-item-row {
flex-direction: column;
row-gap: 4px;
}
.product-editor .ant-form-item-row > .ant-form-item-label {
.product-editor
.editor-section:not(.editor-horizontal-section)
.ant-form-item-row
> .ant-form-item-label {
flex: 0 0 auto !important;
max-width: 100% !important;
padding: 0;
text-align: left;
}
.product-editor .ant-form-item-row > .ant-form-item-control {
.product-editor
.editor-section:not(.editor-horizontal-section)
.ant-form-item-row
> .ant-form-item-control {
flex: 1 1 auto !important;
width: 100%;
max-width: 100% !important;
@@ -889,7 +889,7 @@ const ProductEditPage = () => {
form={form}
layout="horizontal"
labelAlign="right"
labelCol={{ flex: '190px' }}
labelCol={{ flex: '0 0 100px' }}
wrapperCol={{ flex: '0 1 680px' }}
onFinish={save}
requiredMark
@@ -171,8 +171,8 @@
aspect-ratio: 3 / 4;
object-fit: cover;
}
@media (max-width: 1180px) {
/* 窄屏下面板退化为工作区下方整行,不再固定高度 */
@container (max-width: 1199px) {
/* 主内容区不足 1360px 时,面板退化为工作区下方整行,不再固定高度。 */
.editor-aside {
position: static;
}
@@ -69,7 +69,7 @@ export function ProductAttributeSection({
setDictionarySearches({});
}, [selectedCategoryId]);
return (
<Card className="editor-section" bordered={false}>
<Card className="editor-section editor-horizontal-section" bordered={false}>
<SectionTitle
number="02"
title="产品属性"
@@ -41,7 +41,8 @@
display: flex;
align-items: center;
}
@media (max-width: 860px) {
/* 包装尺寸跟随主要信息卡片的实际宽度,和表单 Label 的换行阈值保持一致。 */
@container (max-width: 479px) {
.package-dimensions-controls {
width: 100%;
grid-template-columns: 1fr;
@@ -68,7 +68,7 @@ export function ProductMainInfoSection({
onCategorySearch,
}: Props) {
return (
<Card className="editor-section" bordered={false}>
<Card className="editor-section editor-horizontal-section" bordered={false}>
<SectionTitle
number="01"
title="主要信息"
@@ -10,7 +10,7 @@ type Props = {
/** 「04 其它」区块:货源链接与备注;已上架 SKU 仅保存 Atlas 草稿,不回写平台商品。 */
export function ProductOtherSection({ canOpenSupplyUrl, platformName, onOpenSupplyUrl }: Props) {
return (
<Card className="editor-section" bordered={false}>
<Card className="editor-section editor-horizontal-section" bordered={false}>
<div className="editor-section-heading" style={{ marginBottom: 8 }}>
<div>
<Typography.Title level={4}>
@@ -98,10 +98,12 @@
font-weight: 500;
}
.sku-card-field {
flex: 1 1 120px;
/* 三个可编辑字段在同一行时,输入框允许收缩至 100px 后才换行。 */
flex: 1 1 100px;
min-width: 100px;
}
.sku-price-field {
flex: 1 1 130px;
flex: 1 1 100px;
}
.sku-card-field > span,
.sku-card-value > span {
@@ -157,34 +159,59 @@
margin-left: auto;
}
/* 展开区:左侧采集图片、右侧规格面板 */
/* 展开区:图片与规格使用同层级面板,宽度不足时再改为上下排列。 */
.sku-card-expanded {
display: grid;
grid-template-columns: minmax(0, 1.08fr) minmax(390px, 0.92fr);
gap: 32px;
grid-template-columns: minmax(0, 1fr) minmax(360px, 1fr);
gap: 20px;
margin-top: 16px;
padding-top: 16px;
border-top: 1px solid #f0edf3;
}
.sku-media-panel,
.sku-spec-panel {
padding: 18px;
border: 1px solid #eeeaf3;
border-radius: 10px;
background: #fcfbfd;
}
.sku-media-panel h5.ant-typography,
.sku-spec-panel h5.ant-typography {
margin: 0 0 12px;
}
.sku-media-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 10px;
align-items: start;
}
.sku-media-item {
position: relative;
min-width: 0;
/* 每张图片框固定为 3:4;实际图片使用 contain,完整居中展示。 */
aspect-ratio: 3 / 4;
align-self: start;
overflow: hidden;
border: 1px solid #ece8f0;
border-radius: 8px;
background: #f5f3f7;
}
.sku-media-item--main {
grid-column: span 2;
grid-row: span 2;
}
.sku-media-main-badge {
position: absolute;
top: 6px;
left: 6px;
z-index: 1;
padding: 2px 6px;
border-radius: 4px;
background: rgb(64 57 79 / 72%);
color: #fff;
font-size: 11px;
line-height: 18px;
}
.sku-media-item .ant-btn {
position: absolute;
right: 6px;
@@ -200,24 +227,28 @@
height: 100%;
}
.sku-media-item img {
display: block;
object-fit: contain;
object-position: center;
}
.sku-media-empty {
.sku-media-picker.ant-btn {
display: flex;
grid-column: 1 / -1;
min-height: 190px;
width: 100%;
height: 100%;
aspect-ratio: 3 / 4;
align-self: start;
align-items: center;
justify-content: center;
border: 1px dashed #d9d4df;
border-radius: 8px;
color: #90889c;
font-size: 13px;
}
.sku-media-picker--empty {
grid-column: span 2;
grid-row: span 2;
}
.sku-spec-panel {
padding: 18px 18px 6px;
border: 1px solid #eeeaf3;
border-radius: 10px;
background: #fcfbfd;
padding-bottom: 6px;
}
.sku-spec-panel .ant-form-item {
margin-bottom: 16px;
@@ -354,7 +354,7 @@ export function ProductVariantSection({
</Typography.Title>
{imageCandidates.length ? (
<div className="sku-media-grid">
{imageCandidates.map((candidate) => {
{imageCandidates.map((candidate, imageIndex) => {
// 仅"转存成功"的候选可直接勾选;
// 其余按转存状态展示对应按钮。
const selectable =
@@ -368,9 +368,14 @@ export function ProductVariantSection({
);
return (
<div
className={`sku-media-item ${selected ? 'selected' : ''}`}
className={`sku-media-item ${imageIndex === 0 ? 'sku-media-item--main' : ''} ${selected ? 'selected' : ''}`}
key={candidate.id}
>
{imageIndex === 0 && (
<span className="sku-media-main-badge">
主图
</span>
)}
<Image
src={
candidate.source_url
@@ -428,10 +433,23 @@ export function ProductVariantSection({
</div>
);
})}
{/* 仅提供素材选择入口的视觉占位,实际选择流程后续接入。 */}
<Button
className="sku-media-picker"
type="dashed"
>
选择图片
</Button>
</div>
) : (
<div className="sku-media-empty">
暂无采集图片
<div className="sku-media-grid">
{/* 没有采集图片时仍保留选择素材的入口。 */}
<Button
className="sku-media-picker sku-media-picker--empty"
type="dashed"
>
选择图片
</Button>
</div>
)}
</div>