优化: 会话入口与对话交互打磨

- 完成卡点开子代理轨迹浮层,任务抽屉关联会话跳转
- 提及片段编辑差分三向判定,不再错位
- 长回复折叠完成瞬间锚定消息顶,视口不跳
- 空编辑/技能无匹配/回填覆盖草稿均有提示确认
- 队列编辑态切会话重置,刷新快捷键不再误触
- git 变更面板去重缓存,项目卡骨架与溢出重测
This commit is contained in:
lxy
2026-10-03 17:57:43 +08:00
parent 890ceef0ec
commit f67e65633b
8 changed files with 433 additions and 19 deletions
+33 -2
View File
@@ -24,7 +24,10 @@
<div class="card-footer">
<div class="footer-stat">
<span class="stat-icon">📁</span>
<span>{{ moduleCount }}</span>
<!-- UX走查 P3-6: 工程数异步入场,加载中显骨架条占位(对齐 GitChanges shimmer 范式),
防未加载数先显 0 再跳变——真 0 与"还没加载"不可混淆 -->
<span v-if="moduleCountLoading" class="stat-skeleton" aria-hidden="true"></span>
<span v-else>{{ moduleCount }}</span>
</div>
<div class="footer-stat">
<span class="stat-icon">📅</span>
@@ -48,7 +51,7 @@
// 依赖均为无副作用纯函数 + 共享常量;删除走 emit 交回父组件处理(confirm 弹层 + store 调用)。
// view prop:'list'(紧凑,默认)/'card'(宽松);描述 max-height 截断 + 展开/收起。
import { useRouter } from 'vue-router'
import { ref, onMounted, watch, nextTick } from 'vue'
import { ref, onMounted, onBeforeUnmount, watch, nextTick } from 'vue'
import { formatDate } from '@/utils/time'
import { parseStack } from '@/utils/project'
import { stripMd } from '@/utils/markdown'
@@ -71,11 +74,14 @@ const router = useRouter()
// 工程数(异步拉取,展示在卡片底部)
const moduleCount = ref(0)
// UX走查 P3-6: 加载态标记,模板据此显骨架占位而非 0(成功/失败都收 loading,失败仍显 0 属既有约定)
const moduleCountLoading = ref(true)
onMounted(async () => {
try {
const list = await moduleApi.listProjectModules(props.project.id)
moduleCount.value = list.length
} catch { /* 老项目无工程记录不报错 */ }
moduleCountLoading.value = false
// DOM 渲染完成后测描述是否溢出(决定是否显示「展开」)
await nextTick()
measureDesc()
@@ -91,6 +97,8 @@ const descExpanded = ref(false)
function measureDesc() {
const el = descRef.value
if (!el) return
// 展开态无行截断,测出的一律是"未溢出",会误收起按钮致无法「收起」;展开期间维持原判定
if (descExpanded.value) return
// scrollHeight > clientHeight 说明被 max-height 截断了
descOverflow.value = el.scrollHeight - el.clientHeight > 1
}
@@ -98,6 +106,13 @@ function measureDesc() {
// 描述/视图模式变化后重新测量(描述异步入库或切 view 行数阈值变,按钮显隐需重算)
watch(() => props.project.description, () => nextTick(measureDesc))
watch(() => props.view, () => nextTick(measureDesc))
// 收起后重测:展开期间判定被冻结,若窗口已放宽到不溢出,收起时自校正防残留多余「展开」按钮
watch(descExpanded, (v) => { if (!v) nextTick(measureDesc) })
// UX走查 P3-6: 窗口尺寸变化会改描述可用宽度,截断与否需重测,否则「展开」按钮显隐失准
function onWinResize() { measureDesc() }
onMounted(() => window.addEventListener('resize', onWinResize))
onBeforeUnmount(() => window.removeEventListener('resize', onWinResize))
</script>
<style scoped>
@@ -196,6 +211,22 @@ watch(() => props.view, () => nextTick(measureDesc))
}
.stat-icon { font-size: 13px; }
/* UX走查 P3-6: 工程数加载占位骨架条(shimmer 配方对齐 GitChanges .gc-skeleton-row),
宽度约两位数字占位,高度对齐 12px 字号,防 0 起显跳变 */
.stat-skeleton {
display: inline-block;
width: 18px;
height: 12px;
border-radius: 3px;
background: linear-gradient(90deg, rgba(255,255,255,0.04) 0%, rgba(255,255,255,0.08) 50%, rgba(255,255,255,0.04) 100%);
background-size: 200% 100%;
animation: pc-shimmer 1.5s infinite linear;
}
@keyframes pc-shimmer {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
.card-top-actions { display: flex; align-items: center; gap: 8px; }
.card-del-btn {
background: transparent; border: none; cursor: pointer;