/* Kole UI Mobile · Upload 样式 — 对齐移动端规格 §46 上传:选择触发 + 文件列表 + 四种状态(待上传 / 上传中 / 成功 / 失败)+ 可删除。 本组件**不发请求**:进度与成败由宿主的 upload 回调回传(file.status / file.percent)。 移动端与桌面端的关键差别是**没有拖拽**:手机上没有 hover、也没有拖放, 选择动作只能是点一下「选择文件」,因此触发器是主入口。文件行高 ≥44px,删除按钮 44px 热区。 */ .kole-m-upload { /* 组件级变量:业务侧可在容器上覆盖 */ --kole-m-upload-thumb: 40px; /* 缩略图/文件图标边长 */ --kole-m-upload-row-height: var(--kole-m-touch-target); /* 文件行最小高度 */ box-sizing: border-box; display: flex; flex-direction: column; gap: var(--kole-space-12); width: 100%; background: var(--kole-color-card-bg); color: var(--kole-color-text-body); font-family: var(--kole-font-family); font-size: var(--kole-m-font-size-body); line-height: 1.4; } .kole-m-upload__mask { position: fixed; inset: 0; z-index: 2000; background: var(--kole-color-mask); opacity: 0; pointer-events: none; transition: opacity var(--kole-m-duration-slide) var(--kole-m-ease-slide); } .kole-m-upload__mask.is-open { opacity: 1; pointer-events: auto; } .kole-m-upload__header { display: flex; align-items: baseline; justify-content: space-between; gap: var(--kole-space-8); } .kole-m-upload__title { color: var(--kole-color-text-title); font-weight: 500; } .kole-m-upload__count { color: var(--kole-color-text-secondary); font-size: var(--kole-m-font-size-label); font-family: var(--kole-font-family-num); } /* 选择触发器:点一下调起系统文件选择器(移动端唯一的入口,没有拖拽) */ .kole-m-upload__trigger { box-sizing: border-box; display: flex; align-items: center; justify-content: center; gap: var(--kole-space-8); width: 100%; min-height: var(--kole-m-touch-target); padding: var(--kole-space-12) var(--kole-m-gutter); margin: 0; border: 1px dashed var(--kole-color-border); border-radius: var(--kole-radius-base); background: var(--kole-color-card-bg); color: var(--kole-color-brand); font-family: inherit; font-size: var(--kole-m-font-size-label); line-height: 1.3; cursor: pointer; touch-action: manipulation; } /* 变体 variant=dashed:虚线框(页面级上传区;触发按钮是唯一入口,框本身不接收拖放) */ .kole-m-upload--dashed .kole-m-upload__trigger { border-style: dashed; } /* 变体 variant=button:实心按钮式(列表内嵌的「+ 添加」用) */ .kole-m-upload--button .kole-m-upload__trigger { border-style: solid; border-color: var(--kole-color-brand); background: var(--kole-color-brand); color: var(--kole-color-text-inverse); font-weight: 500; } .kole-m-upload__trigger:active { background: var(--kole-color-brand-bg); } .kole-m-upload--button .kole-m-upload__trigger:active { background: var(--kole-color-brand-active); border-color: var(--kole-color-brand-active); } .kole-m-upload__trigger:focus-visible { outline: 2px solid var(--kole-color-focus-ring); outline-offset: 2px; } /* 状态 disabled:触发器与删除按钮都不可用 */ .kole-m-upload.is-disabled .kole-m-upload__trigger, .kole-m-upload__trigger:disabled { border-color: var(--kole-color-border); background: var(--kole-color-disabled-bg); color: var(--kole-color-text-disabled); cursor: not-allowed; } /* 文件列表 */ .kole-m-upload__list { display: flex; flex-direction: column; gap: var(--kole-space-8); width: 100%; margin: 0; padding: 0; list-style: none; } .kole-m-upload__item { box-sizing: border-box; display: flex; align-items: center; gap: var(--kole-space-12); width: 100%; min-height: var(--kole-m-upload-row-height); padding: var(--kole-space-8) 0; } /* 文件行之间画分隔线(画在包裹层上沿,与 List 同一策略) */ .kole-m-upload__item + .kole-m-upload__item { border-top: 1px solid var(--kole-color-border); } /* 单行进度:由宿主以行内自定义属性传入百分比(如 style="--kole-m-upload-percent: 60%"), 样式表只消费,不写死任何长度 —— 百分比是数据。 */ .kole-m-upload__item { --kole-m-upload-percent: 0%; } /* 缩略图位:图片是背景图,其他类型是天生的扩展名文字块 */ .kole-m-upload__thumb { flex: 0 0 auto; box-sizing: border-box; display: flex; align-items: center; justify-content: center; width: var(--kole-m-upload-thumb); height: var(--kole-m-upload-thumb); overflow: hidden; border: 1px solid var(--kole-color-border); border-radius: var(--kole-radius-base); background: var(--kole-color-table-header-bg); background-position: center; background-repeat: no-repeat; background-size: cover; color: var(--kole-color-text-secondary); font-family: var(--kole-font-family-num); font-size: var(--kole-m-font-size-caption); text-transform: uppercase; } /* 内容区:文件名 +(进度条 / 状态文案) */ .kole-m-upload__body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: var(--kole-space-4); } .kole-m-upload__name { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; color: var(--kole-color-text-body); } .kole-m-upload__meta { display: flex; align-items: center; gap: var(--kole-space-8); color: var(--kole-color-text-secondary); font-size: var(--kole-m-font-size-label); } /* 进度条:只在上传中 / 成功 / 失败时出现(待上传不画,长度无意义) */ .kole-m-upload__track { position: relative; flex: 1 1 auto; min-width: 0; height: 2px; overflow: hidden; border-radius: 1px; background: var(--kole-color-border); } .kole-m-upload__bar { position: absolute; top: 0; left: 0; bottom: 0; width: 0; border-radius: 1px; background: var(--kole-color-brand); transition: width var(--kole-duration-base) var(--kole-ease-standard); } /* 状态 pending:待上传(灰点,未开始;进度条不占宽) */ .kole-m-upload__item.is-pending .kole-m-upload__bar { width: 0; background: var(--kole-color-border); } /* 状态 uploading:上传中(品牌色进度条,宽度取 --kole-m-upload-percent) */ .kole-m-upload__item.is-uploading .kole-m-upload__bar { width: var(--kole-m-upload-percent); background: var(--kole-color-brand); } /* 状态 success:成功(满格 + 品牌成功色 + 勾号) */ .kole-m-upload__item.is-success .kole-m-upload__bar { width: 100%; background: var(--kole-color-success); } .kole-m-upload__item.is-success .kole-m-upload__status { color: var(--kole-color-success); } /* 状态 error:失败(满格 + 错误色 + 可重试) */ .kole-m-upload__item.is-error .kole-m-upload__bar { width: 100%; background: var(--kole-color-error); } .kole-m-upload__item.is-error .kole-m-upload__status { color: var(--kole-color-error); } .kole-m-upload__status { flex: 0 0 auto; color: var(--kole-color-text-secondary); font-size: var(--kole-m-font-size-label); } /* 行内动作(重试 / 删除):各自 44px 热区 */ .kole-m-upload__actions { flex: 0 0 auto; display: inline-flex; align-items: center; gap: var(--kole-space-4); margin-right: calc(var(--kole-space-8) * -1); } .kole-m-upload__action { box-sizing: border-box; display: inline-flex; align-items: center; justify-content: center; min-width: var(--kole-m-touch-target); min-height: var(--kole-m-touch-target); padding: 0 var(--kole-space-4); margin: 0; border: 0; border-radius: var(--kole-radius-base); background: none; color: var(--kole-color-text-secondary); font-family: inherit; font-size: var(--kole-m-font-size-label); line-height: 1; cursor: pointer; touch-action: manipulation; } .kole-m-upload__action:active { background: var(--kole-color-table-header-bg); } .kole-m-upload__action:focus-visible { outline: 2px solid var(--kole-color-focus-ring); outline-offset: 2px; } .kole-m-upload__action--remove { color: var(--kole-color-error); } .kole-m-upload__action:disabled { color: var(--kole-color-text-disabled); cursor: not-allowed; } /* 变体 maxCount 到顶:触发器改成提示行(不再可点) */ .kole-m-upload--full .kole-m-upload__trigger { border-style: solid; color: var(--kole-color-text-secondary); cursor: default; } /* 状态 empty:还没有任何文件时的说明 */ .kole-m-upload__empty { margin: 0; padding: var(--kole-space-16) 0; color: var(--kole-color-text-secondary); font-size: var(--kole-m-font-size-label); text-align: center; } /* 视觉隐藏但读屏可读(原生 file input 的替身) */ .kole-m-upload__native { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; } .kole-m-upload__native:focus-visible + .kole-m-upload__trigger { outline: 2px solid var(--kole-color-focus-ring); outline-offset: 2px; } /* 演示页触发器(非组件交付样式) */ .kole-m-upload__demo-btn { box-sizing: border-box; min-height: var(--kole-m-touch-target); padding: 0 var(--kole-m-gutter); border: 1px solid var(--kole-color-border); border-radius: var(--kole-radius-base); background: var(--kole-color-card-bg); color: var(--kole-color-text-body); font-family: inherit; font-size: var(--kole-m-font-size-label); cursor: pointer; touch-action: manipulation; } .kole-m-upload__demo-btn:focus-visible { outline: 2px solid var(--kole-color-focus-ring); outline-offset: 2px; }