wip
This commit is contained in:
@@ -63,8 +63,33 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="任务进度">
|
||||
<div class="progress-input-wrap">
|
||||
<el-slider v-model="form.progress" :min="0" :max="100" :step="5" show-input />
|
||||
<!-- 海浪液位进度条:水位高度 = 进度值,浪尖从左往右涌动;点击/拖动轨道或键盘均可调节 -->
|
||||
<div class="gauge-progress" :class="{ 'is-dragging': dragging, 'is-hovering': hovering }"
|
||||
:style="{ '--progress-color': progressColor }">
|
||||
<div ref="trackRef" class="gp-track" role="slider" tabindex="0" :aria-valuemin="0" :aria-valuemax="100"
|
||||
:aria-valuenow="form.progress" @mousedown="onTrackMouseDown" @touchstart.prevent="onTrackTouchStart"
|
||||
@mousemove="onTrackMouseMove" @mouseenter="hovering = true" @mouseleave="hovering = false"
|
||||
@keydown.left.prevent="changeStep(-5)" @keydown.right.prevent="changeStep(5)"
|
||||
@keydown.home.prevent="form.progress = 0" @keydown.end.prevent="form.progress = 100">
|
||||
<div class="gp-clip">
|
||||
<!-- 液体铺满全宽,水位随进度上涨 -->
|
||||
<div class="gp-liquid" :class="{ 'is-empty': form.progress === 0 }" :style="{ height: form.progress + '%' }">
|
||||
<div class="gp-sheen"></div>
|
||||
<svg class="gp-waves gp-waves-back" viewBox="0 0 280 14" preserveAspectRatio="none" aria-hidden="true">
|
||||
<path d="M0,7 C12,0 23,14 35,7 C47,0 58,14 70,7 C82,0 93,14 105,7 C117,0 128,14 140,7 C152,0 163,14 175,7 C187,0 198,14 210,7 C222,0 233,14 245,7 C257,0 268,14 280,7 L280,14 L0,14 Z"></path>
|
||||
</svg>
|
||||
<svg class="gp-waves gp-waves-front" viewBox="0 0 300 12" preserveAspectRatio="none" aria-hidden="true">
|
||||
<path d="M0,6 C8,0 17,12 25,6 C33,0 42,12 50,6 C58,0 67,12 75,6 C83,0 92,12 100,6 C108,0 117,12 125,6 C133,0 142,12 150,6 C158,0 167,12 175,6 C183,0 192,12 200,6 C208,0 217,12 225,6 C233,0 242,12 250,6 C258,0 267,12 275,6 C283,0 292,12 300,6 L300,12 L0,12 Z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 气泡跟随光标水平位置,悬停/拖动时显示 -->
|
||||
<div class="gp-bubble" :style="{ left: bubbleX + '%' }">{{ form.progress }}%</div>
|
||||
</div>
|
||||
<div class="gp-meta">
|
||||
<span class="gp-pct">{{ form.progress }}</span>
|
||||
<span class="gp-stage">{{ progressStage }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
@@ -131,6 +156,91 @@ const handleCustomerChange = () => {
|
||||
form.operator_id = null;
|
||||
};
|
||||
|
||||
// 任务进度颜色:按进度值取语义色;填充主色通过 CSS transition 平滑过渡,跨阶段变色无硬切
|
||||
const progressColor = computed(() => {
|
||||
const p = form.progress;
|
||||
if (p >= 100) return "#10b981"; // 已完成 - 绿
|
||||
if (p >= 75) return "#409eff"; // 快完成 - 蓝
|
||||
if (p >= 40) return "#e6a23c"; // 进行中 - 橙
|
||||
return "#f56c6c"; // 刚开始 - 红
|
||||
});
|
||||
|
||||
// 进度阶段文案
|
||||
const progressStage = computed(() => {
|
||||
const p = form.progress;
|
||||
if (p >= 100) return "已完成";
|
||||
if (p >= 75) return "接近完成";
|
||||
if (p >= 40) return "推进中";
|
||||
return "刚开始";
|
||||
});
|
||||
|
||||
// --- 可拖动进度条交互 ---
|
||||
const dragging = ref(false);
|
||||
const hovering = ref(false);
|
||||
const bubbleX = ref(50); // 气泡跟随光标的水平位置(轨道内百分比)
|
||||
const trackRef = ref(null);
|
||||
|
||||
const setProgressFromClientX = (clientX) => {
|
||||
const el = trackRef.value;
|
||||
if (!el) return;
|
||||
const rect = el.getBoundingClientRect();
|
||||
const ratio = (clientX - rect.left) / rect.width;
|
||||
const pct = Math.min(100, Math.max(0, Math.round(ratio * 100)));
|
||||
form.progress = pct;
|
||||
};
|
||||
|
||||
// 气泡水平位置跟随光标,钳制在轨道两端以内避免溢出
|
||||
const updateBubbleX = (clientX) => {
|
||||
const el = trackRef.value;
|
||||
if (!el) return;
|
||||
const rect = el.getBoundingClientRect();
|
||||
const ratio = (clientX - rect.left) / rect.width;
|
||||
bubbleX.value = Math.min(94, Math.max(6, ratio * 100));
|
||||
};
|
||||
|
||||
const onTrackMouseMove = (e) => updateBubbleX(e.clientX);
|
||||
|
||||
const onTrackMouseDown = (e) => {
|
||||
e.preventDefault();
|
||||
setProgressFromClientX(e.clientX);
|
||||
updateBubbleX(e.clientX);
|
||||
dragging.value = true;
|
||||
const move = (ev) => {
|
||||
setProgressFromClientX(ev.clientX);
|
||||
updateBubbleX(ev.clientX);
|
||||
};
|
||||
const up = () => {
|
||||
dragging.value = false;
|
||||
window.removeEventListener("mousemove", move);
|
||||
window.removeEventListener("mouseup", up);
|
||||
};
|
||||
window.addEventListener("mousemove", move);
|
||||
window.addEventListener("mouseup", up);
|
||||
};
|
||||
|
||||
const onTrackTouchStart = (e) => {
|
||||
const touch = e.touches[0];
|
||||
setProgressFromClientX(touch.clientX);
|
||||
updateBubbleX(touch.clientX);
|
||||
dragging.value = true;
|
||||
const move = (ev) => {
|
||||
ev.preventDefault();
|
||||
setProgressFromClientX(ev.touches[0].clientX);
|
||||
updateBubbleX(ev.touches[0].clientX);
|
||||
};
|
||||
const up = () => {
|
||||
dragging.value = false;
|
||||
window.removeEventListener("touchmove", move);
|
||||
window.removeEventListener("touchend", up);
|
||||
};
|
||||
window.addEventListener("touchmove", move, { passive: false });
|
||||
window.addEventListener("touchend", up);
|
||||
};
|
||||
|
||||
const changeStep = (delta) => {
|
||||
form.progress = Math.min(100, Math.max(0, form.progress + delta));
|
||||
};
|
||||
|
||||
// 状态变为已完成(2)或已完结(3)时,自动将进度设为100
|
||||
watch(
|
||||
() => form.status,
|
||||
@@ -283,8 +393,242 @@ const handleSubmit = async () => {
|
||||
border-radius: var(--radius-sm);
|
||||
}
|
||||
|
||||
.progress-input-wrap {
|
||||
padding: 0 4px;
|
||||
/* ===== 海浪液位进度条:水位随进度上涨,浪尖从左往右涌动 ===== */
|
||||
.gauge-progress {
|
||||
--progress-color: #4f46e5; /* 兜底色,实际由行内样式注入 */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
padding: 10px 0 6px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* --- 轨道:玻璃胶囊舱 + 四分刻度线(刻度没入水中部分被液面覆盖) --- */
|
||||
.gp-track {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
height: 26px;
|
||||
margin: 0 16px;
|
||||
border-radius: 999px;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
background-image:
|
||||
linear-gradient(90deg, transparent calc(25% - 0.5px), rgba(15, 23, 42, 0.08) 25%, transparent calc(25% + 0.5px)),
|
||||
linear-gradient(90deg, transparent calc(50% - 0.5px), rgba(15, 23, 42, 0.08) 50%, transparent calc(50% + 0.5px)),
|
||||
linear-gradient(90deg, transparent calc(75% - 0.5px), rgba(15, 23, 42, 0.08) 75%, transparent calc(75% + 0.5px)),
|
||||
linear-gradient(180deg, #e2e7f0 0%, #edf0f7 45%, #f5f7fb 100%);
|
||||
box-shadow:
|
||||
inset 0 2px 5px rgba(15, 23, 42, 0.14),
|
||||
inset 0 -1px 0 rgba(255, 255, 255, 0.9),
|
||||
0 1px 2px rgba(255, 255, 255, 0.7);
|
||||
transition: box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.gp-track:hover {
|
||||
box-shadow:
|
||||
inset 0 2px 6px rgba(15, 23, 42, 0.18),
|
||||
inset 0 -1px 0 rgba(255, 255, 255, 0.9),
|
||||
0 1px 2px rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
.gp-track:focus-visible {
|
||||
box-shadow:
|
||||
0 0 0 3px color-mix(in srgb, var(--progress-color) 32%, transparent),
|
||||
inset 0 2px 5px rgba(15, 23, 42, 0.14);
|
||||
}
|
||||
|
||||
/* 裁剪层:液体与海浪只允许出现在胶囊舱内(气泡在裁剪层之外,不被裁掉) */
|
||||
.gp-clip {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: 999px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* --- 液体:铺满全宽,水位高度 = 进度值 --- */
|
||||
.gp-liquid {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: var(--progress-color); /* 主色可过渡:跨阶段变色平滑无硬切 */
|
||||
background-image:
|
||||
linear-gradient(180deg, rgba(255, 255, 255, 0.30) 0%, rgba(255, 255, 255, 0) 45%), /* 近水面提亮 */
|
||||
linear-gradient(0deg, rgba(0, 0, 0, 0.14) 0%, rgba(0, 0, 0, 0) 40%), /* 水深处压暗 */
|
||||
linear-gradient(90deg, rgba(255, 255, 255, 0.18) 0%, rgba(255, 255, 255, 0) 40%); /* 左浅右深 → 颜色渐变 */
|
||||
box-shadow: 0 3px 12px -2px color-mix(in srgb, var(--progress-color) 55%, transparent); /* 底部同色系辉光 */
|
||||
transition: height 0.22s ease, background-color 0.35s ease, box-shadow 0.35s ease;
|
||||
}
|
||||
|
||||
/* 进度为 0 时隐藏水体 */
|
||||
.gp-liquid.is-empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 流光扫过:一道斜向高光周期性掠过水体 */
|
||||
.gp-sheen {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.gp-sheen::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: -25%;
|
||||
bottom: -25%;
|
||||
left: -45%;
|
||||
width: 34%;
|
||||
background: linear-gradient(105deg, transparent 0%, rgba(255, 255, 255, 0.45) 50%, transparent 100%);
|
||||
transform: skewX(-12deg);
|
||||
animation: sheenSweep 3.6s cubic-bezier(0.4, 0, 0.2, 1) infinite;
|
||||
}
|
||||
|
||||
@keyframes sheenSweep {
|
||||
0% { left: -45%; }
|
||||
45% { left: 120%; }
|
||||
100% { left: 120%; }
|
||||
}
|
||||
|
||||
/* --- 海浪:前后两层浪尖横跨液面,视差涌动(从左往右) --- */
|
||||
.gp-waves {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 200%; /* 两个浪形周期,平移 50% 即可无缝循环 */
|
||||
pointer-events: none;
|
||||
animation: waveDriftX linear infinite;
|
||||
}
|
||||
|
||||
.gp-waves path {
|
||||
transition: fill 0.35s ease;
|
||||
}
|
||||
|
||||
/* 后层涌浪:主色半透明,幅面更高、流动更慢(约 2 个浪峰横跨轨道) */
|
||||
.gp-waves-back {
|
||||
top: -8px;
|
||||
height: 14px;
|
||||
opacity: 0.55;
|
||||
animation-duration: 5s;
|
||||
animation-delay: -1.6s; /* 错开相位 */
|
||||
}
|
||||
|
||||
.gp-waves-back path {
|
||||
fill: var(--progress-color);
|
||||
}
|
||||
|
||||
/* 前层浪花:主色调白的泡沫色,贴着液面、流动更快(约 3 个浪峰横跨轨道) */
|
||||
.gp-waves-front {
|
||||
top: -7px;
|
||||
height: 12px;
|
||||
animation-duration: 3.2s;
|
||||
}
|
||||
|
||||
.gp-waves-front path {
|
||||
fill: color-mix(in srgb, var(--progress-color) 35%, #ffffff);
|
||||
}
|
||||
|
||||
@keyframes waveDriftX {
|
||||
from { transform: translateX(-50%); }
|
||||
to { transform: translateX(0); }
|
||||
}
|
||||
|
||||
/* 拖动时:水位即时响应(无过渡延迟),海浪加速涌动 */
|
||||
.gauge-progress.is-dragging .gp-liquid {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.gauge-progress.is-dragging .gp-waves-front {
|
||||
animation-duration: 1.6s;
|
||||
}
|
||||
|
||||
.gauge-progress.is-dragging .gp-waves-back {
|
||||
animation-duration: 2.6s;
|
||||
}
|
||||
|
||||
/* --- 气泡:跟随光标的弹性提示 --- */
|
||||
.gp-bubble {
|
||||
position: absolute;
|
||||
bottom: calc(100% + 8px);
|
||||
transform: translateX(-50%) translateY(6px) scale(0.9);
|
||||
transform-origin: bottom center;
|
||||
background: var(--progress-color);
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
padding: 6px 10px;
|
||||
border-radius: 7px;
|
||||
white-space: nowrap;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
box-shadow: 0 6px 16px -4px color-mix(in srgb, var(--progress-color) 65%, transparent);
|
||||
transition: opacity 0.18s ease, transform 0.22s cubic-bezier(0.34, 1.56, 0.64, 1), background-color 0.35s ease, left 0.08s linear;
|
||||
}
|
||||
|
||||
.gp-bubble::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
border: 5px solid transparent;
|
||||
border-top-color: var(--progress-color);
|
||||
transition: border-top-color 0.35s ease;
|
||||
}
|
||||
|
||||
.gauge-progress.is-hovering .gp-bubble,
|
||||
.gauge-progress.is-dragging .gp-bubble {
|
||||
opacity: 1;
|
||||
transform: translateX(-50%) translateY(0) scale(1);
|
||||
}
|
||||
|
||||
/* --- 读数与阶段徽标 --- */
|
||||
.gp-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
flex-shrink: 0;
|
||||
min-width: 118px;
|
||||
}
|
||||
|
||||
.gp-pct {
|
||||
font-family: "Bahnschrift", "DIN Alternate", "Segoe UI", sans-serif;
|
||||
font-size: 27px;
|
||||
font-weight: 600;
|
||||
font-variant-numeric: tabular-nums;
|
||||
letter-spacing: -0.5px;
|
||||
line-height: 1;
|
||||
color: var(--text-primary, #1f2937);
|
||||
}
|
||||
|
||||
.gp-pct::after {
|
||||
content: "%";
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: var(--text-muted, #9ca3af);
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
.gp-stage {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
line-height: 1;
|
||||
padding: 5px 10px;
|
||||
border-radius: 999px;
|
||||
white-space: nowrap;
|
||||
color: var(--progress-color);
|
||||
background: color-mix(in srgb, var(--progress-color) 10%, #ffffff);
|
||||
border: 1px solid color-mix(in srgb, var(--progress-color) 32%, transparent);
|
||||
transition: color 0.35s ease, background-color 0.35s ease, border-color 0.35s ease;
|
||||
}
|
||||
|
||||
/* 尊重系统“减弱动态效果”设置 */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.gp-waves,
|
||||
.gp-sheen::before {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-form-item__label) {
|
||||
|
||||
Reference in New Issue
Block a user