feat: 删除二次确认弹窗(常驻/布局/快捷方式) + 常驻项目可删(removed列表) + emoji字号13px统一 + 项目名不加粗
This commit is contained in:
@@ -43,6 +43,8 @@ type ProjectsState = {
|
|||||||
nameOverrides: Record<string, string>
|
nameOverrides: Record<string, string>
|
||||||
/** 图标覆盖(点图标换 emoji;含入驻插件项目 id → emoji)。 */
|
/** 图标覆盖(点图标换 emoji;含入驻插件项目 id → emoji)。 */
|
||||||
iconOverrides: Record<string, string>
|
iconOverrides: Record<string, string>
|
||||||
|
/** 已从工作台删除的入驻项目 id(插件不卸载,仅不再展示;恢复默认找回)。 */
|
||||||
|
removed: string[]
|
||||||
/** 本地快捷方式条目。 */
|
/** 本地快捷方式条目。 */
|
||||||
shortcuts: Shortcut[]
|
shortcuts: Shortcut[]
|
||||||
/** 用户自建的布局条目(「+」新建工作区保存的 LayoutSpec)。 */
|
/** 用户自建的布局条目(「+」新建工作区保存的 LayoutSpec)。 */
|
||||||
@@ -186,6 +188,7 @@ const DEFAULT_PROJECTS: ProjectsState = {
|
|||||||
hidden: [],
|
hidden: [],
|
||||||
nameOverrides: {},
|
nameOverrides: {},
|
||||||
iconOverrides: {},
|
iconOverrides: {},
|
||||||
|
removed: [],
|
||||||
shortcuts: [],
|
shortcuts: [],
|
||||||
layouts: [],
|
layouts: [],
|
||||||
}
|
}
|
||||||
@@ -224,6 +227,7 @@ function loadProjects(): ProjectsState {
|
|||||||
hidden: Array.isArray(p.hidden) ? p.hidden.filter((x: unknown): x is string => typeof x === 'string') : [],
|
hidden: Array.isArray(p.hidden) ? p.hidden.filter((x: unknown): x is string => typeof x === 'string') : [],
|
||||||
nameOverrides: p.nameOverrides && typeof p.nameOverrides === 'object' ? p.nameOverrides : {},
|
nameOverrides: p.nameOverrides && typeof p.nameOverrides === 'object' ? p.nameOverrides : {},
|
||||||
iconOverrides: p.iconOverrides && typeof p.iconOverrides === 'object' ? p.iconOverrides : {},
|
iconOverrides: p.iconOverrides && typeof p.iconOverrides === 'object' ? p.iconOverrides : {},
|
||||||
|
removed: Array.isArray(p.removed) ? p.removed.filter((x: unknown): x is string => typeof x === 'string') : [],
|
||||||
shortcuts: Array.isArray(p.shortcuts)
|
shortcuts: Array.isArray(p.shortcuts)
|
||||||
? p.shortcuts.filter((s: any) => s && typeof s.id === 'string' && typeof s.name === 'string' && typeof s.href === 'string')
|
? p.shortcuts.filter((s: any) => s && typeof s.id === 'string' && typeof s.name === 'string' && typeof s.href === 'string')
|
||||||
: [],
|
: [],
|
||||||
@@ -311,6 +315,8 @@ function WorktableSection(props: any) {
|
|||||||
const [wsError, setWsError] = useState(false)
|
const [wsError, setWsError] = useState(false)
|
||||||
/** 图标选择器:kind + 目标 id + 弹窗锚点坐标(fixed 定位) */
|
/** 图标选择器:kind + 目标 id + 弹窗锚点坐标(fixed 定位) */
|
||||||
const [iconPick, setIconPick] = useState<{ kind: 'layout' | 'shortcut' | 'project'; id: string; x: number; y: number } | null>(null)
|
const [iconPick, setIconPick] = useState<{ kind: 'layout' | 'shortcut' | 'project'; id: string; x: number; y: number } | null>(null)
|
||||||
|
/** 删除二次确认:kind + 目标 id + 显示名 */
|
||||||
|
const [requestDelete, setRequestDelete] = useState<{ kind: 'layout' | 'shortcut' | 'project'; id: string; name: string } | null>(null)
|
||||||
const [float, setFloat] = useState<FloatRect | null>(() =>
|
const [float, setFloat] = useState<FloatRect | null>(() =>
|
||||||
view.dock === 'float' && view.floatTop != null ? { top: view.floatTop } : null,
|
view.dock === 'float' && view.floatTop != null ? { top: view.floatTop } : null,
|
||||||
)
|
)
|
||||||
@@ -497,7 +503,11 @@ function WorktableSection(props: any) {
|
|||||||
// 手动:持久化 order(过滤已卸载 id)→ 新注册 id 与布局 id 追加尾部;
|
// 手动:持久化 order(过滤已卸载 id)→ 新注册 id 与布局 id 追加尾部;
|
||||||
// 最近:有 lastUsed 的按时间降序在前,其余按手动序在后。
|
// 最近:有 lastUsed 的按时间降序在前,其余按手动序在后。
|
||||||
const layoutIds = useMemo(() => projects.layouts.map((l) => l.id), [projects.layouts])
|
const layoutIds = useMemo(() => projects.layouts.map((l) => l.id), [projects.layouts])
|
||||||
const allIds = useMemo(() => [...registeredIds, ...layoutIds], [registeredIds, layoutIds])
|
const aliveRegisteredIds = useMemo(
|
||||||
|
() => registeredIds.filter((id) => !projects.removed.includes(id)),
|
||||||
|
[registeredIds, projects.removed],
|
||||||
|
)
|
||||||
|
const allIds = useMemo(() => [...aliveRegisteredIds, ...layoutIds], [aliveRegisteredIds, layoutIds])
|
||||||
const effectiveOrder = useMemo(() => {
|
const effectiveOrder = useMemo(() => {
|
||||||
const known = new Set(allIds)
|
const known = new Set(allIds)
|
||||||
const stored = projects.order.filter((id) => known.has(id))
|
const stored = projects.order.filter((id) => known.has(id))
|
||||||
@@ -517,7 +527,7 @@ function WorktableSection(props: any) {
|
|||||||
wide,
|
wide,
|
||||||
managing,
|
managing,
|
||||||
order: effectiveOrder,
|
order: effectiveOrder,
|
||||||
hidden: projects.hidden,
|
hidden: [...projects.hidden, ...projects.removed],
|
||||||
nameOverrides: projects.nameOverrides,
|
nameOverrides: projects.nameOverrides,
|
||||||
iconOverrides: projects.iconOverrides,
|
iconOverrides: projects.iconOverrides,
|
||||||
reportMeta,
|
reportMeta,
|
||||||
@@ -627,7 +637,14 @@ function WorktableSection(props: any) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const resetProjects = () => {
|
const resetProjects = () => {
|
||||||
persistProjects((prev) => ({ ...prev, order: [], hidden: [], nameOverrides: {} }))
|
persistProjects((prev) => ({
|
||||||
|
...prev,
|
||||||
|
order: [],
|
||||||
|
hidden: [],
|
||||||
|
nameOverrides: {},
|
||||||
|
iconOverrides: {},
|
||||||
|
removed: [],
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── 快捷方式(表单已移除,仅保留存量条目的删除能力) ──
|
// ── 快捷方式(表单已移除,仅保留存量条目的删除能力) ──
|
||||||
@@ -651,6 +668,26 @@ function WorktableSection(props: any) {
|
|||||||
persistProjects((prev) => ({ ...prev, layouts: prev.layouts.filter((l) => l.id !== id) }))
|
persistProjects((prev) => ({ ...prev, layouts: prev.layouts.filter((l) => l.id !== id) }))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── 删除(全部走二次确认;常驻项目 = 移出工作台,插件不卸载) ──
|
||||||
|
const removeProject = (id: string) => {
|
||||||
|
persistProjects((prev) => ({
|
||||||
|
...prev,
|
||||||
|
removed: prev.removed.includes(id) ? prev.removed : [...prev.removed, id],
|
||||||
|
hidden: prev.hidden.filter((x) => x !== id),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
const askDelete = (kind: 'layout' | 'shortcut' | 'project', id: string, name: string) => {
|
||||||
|
setRequestDelete({ kind, id, name })
|
||||||
|
}
|
||||||
|
const doDelete = () => {
|
||||||
|
const r = requestDelete
|
||||||
|
if (!r) return
|
||||||
|
if (r.kind === 'layout') removeLayout(r.id)
|
||||||
|
else if (r.kind === 'shortcut') removeShortcut(r.id)
|
||||||
|
else removeProject(r.id)
|
||||||
|
setRequestDelete(null)
|
||||||
|
}
|
||||||
|
|
||||||
// ── 图标选择器(布局 / 快捷方式 / 入驻项目的侧栏 emoji 点击可换) ──
|
// ── 图标选择器(布局 / 快捷方式 / 入驻项目的侧栏 emoji 点击可换) ──
|
||||||
// anchor:DOM 锚点元素(自己卡片里的 icon 元素或事件 currentTarget)
|
// anchor:DOM 锚点元素(自己卡片里的 icon 元素或事件 currentTarget)
|
||||||
const openIconPick = (kind: 'layout' | 'shortcut' | 'project', id: string, anchor: HTMLElement) => {
|
const openIconPick = (kind: 'layout' | 'shortcut' | 'project', id: string, anchor: HTMLElement) => {
|
||||||
@@ -795,12 +832,12 @@ function WorktableSection(props: any) {
|
|||||||
const popTop = clamp(sectionTop, MIN_TOP, Math.max(MIN_TOP, window.innerHeight - 540))
|
const popTop = clamp(sectionTop, MIN_TOP, Math.max(MIN_TOP, window.innerHeight - 540))
|
||||||
|
|
||||||
if (!wide) {
|
if (!wide) {
|
||||||
const projectIcons = registeredIds.map((id) => projects.iconOverrides[id] ?? metas[id]?.icon ?? '📦')
|
const projectIcons = aliveRegisteredIds.map((id) => projects.iconOverrides[id] ?? metas[id]?.icon ?? '📦')
|
||||||
const shortcutIcons = projects.shortcuts.map((s) => s.icon)
|
const shortcutIcons = projects.shortcuts.map((s) => s.icon)
|
||||||
const layoutIcons = projects.layouts.map((l) => l.icon ?? '🧱')
|
const layoutIcons = projects.layouts.map((l) => l.icon ?? '🧱')
|
||||||
const icons = [...projectIcons, ...shortcutIcons, ...layoutIcons]
|
const icons = [...projectIcons, ...shortcutIcons, ...layoutIcons]
|
||||||
const railNames = [
|
const railNames = [
|
||||||
...registeredIds.map((id) => projects.nameOverrides[id] ?? metas[id]?.name ?? id),
|
...aliveRegisteredIds.map((id) => projects.nameOverrides[id] ?? metas[id]?.name ?? id),
|
||||||
...projects.shortcuts.map((s) => s.name),
|
...projects.shortcuts.map((s) => s.name),
|
||||||
...projects.layouts.map((l) => projects.nameOverrides[l.id] ?? l.title),
|
...projects.layouts.map((l) => projects.nameOverrides[l.id] ?? l.title),
|
||||||
]
|
]
|
||||||
@@ -1010,9 +1047,12 @@ function WorktableSection(props: any) {
|
|||||||
<button type="button" className="dsh-wt_manageBtn" title={isHidden ? t('manage.show') : t('manage.hide')} onClick={() => toggleHidden(id)}>
|
<button type="button" className="dsh-wt_manageBtn" title={isHidden ? t('manage.show') : t('manage.hide')} onClick={() => toggleHidden(id)}>
|
||||||
{isHidden ? '🙈' : '👁'}
|
{isHidden ? '🙈' : '👁'}
|
||||||
</button>
|
</button>
|
||||||
{layout && (
|
<button
|
||||||
<button type="button" className="dsh-wt_manageBtn" title={t('manage.deleteLayout')} onClick={() => removeLayout(id)}>✕</button>
|
type="button"
|
||||||
)}
|
className="dsh-wt_manageBtn"
|
||||||
|
title={layout ? t('manage.deleteLayout') : t('manage.deleteProject')}
|
||||||
|
onClick={() => askDelete(layout ? 'layout' : 'project', id, display)}
|
||||||
|
>✕</button>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
@@ -1027,13 +1067,31 @@ function WorktableSection(props: any) {
|
|||||||
onClick={(e) => { e.stopPropagation(); openIconPick('shortcut', s.id, e.currentTarget as HTMLElement) }}
|
onClick={(e) => { e.stopPropagation(); openIconPick('shortcut', s.id, e.currentTarget as HTMLElement) }}
|
||||||
>{s.icon}</span>
|
>{s.icon}</span>
|
||||||
<span className="dsh-wt_manageScName">{s.name}</span>
|
<span className="dsh-wt_manageScName">{s.name}</span>
|
||||||
<button type="button" className="dsh-wt_manageBtn" title={t('manage.deleteShortcut')} onClick={() => removeShortcut(s.id)}>✕</button>
|
<button type="button" className="dsh-wt_manageBtn" title={t('manage.deleteShortcut')} onClick={() => askDelete('shortcut', s.id, s.name)}>✕</button>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
<button type="button" className="dsh-wt_manageReset" onClick={resetProjects}>{t('manage.reset')}</button>
|
<button type="button" className="dsh-wt_manageReset" onClick={resetProjects}>{t('manage.reset')}</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{requestDelete && <div className="dsh-wt_confirmBackdrop" onClick={() => setRequestDelete(null)} />}
|
||||||
|
{requestDelete && (
|
||||||
|
<div className="dsh-wt_confirm" role="alertdialog">
|
||||||
|
<div className="dsh-wt_confirmTitle">⚠️ {t('confirm.title')}</div>
|
||||||
|
<div className="dsh-wt_confirmBody">
|
||||||
|
{requestDelete.kind === 'layout'
|
||||||
|
? t('confirm.layoutBody', { name: requestDelete.name })
|
||||||
|
: requestDelete.kind === 'shortcut'
|
||||||
|
? t('confirm.shortcutBody', { name: requestDelete.name })
|
||||||
|
: t('confirm.projectBody', { name: requestDelete.name })}
|
||||||
|
</div>
|
||||||
|
<div className="dsh-wt_confirmActions">
|
||||||
|
<button type="button" className="dsh-wt_confirmCancel" onClick={() => setRequestDelete(null)}>{t('confirm.cancel')}</button>
|
||||||
|
<button type="button" className="dsh-wt_confirmDelete" onClick={doDelete}>{t('confirm.delete')}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="dsh-wt_projects" data-managing={managing ? 'true' : undefined}>
|
<div className="dsh-wt_projects" data-managing={managing ? 'true' : undefined}>
|
||||||
{renderProjectSlot
|
{renderProjectSlot
|
||||||
? renderProjectSlot('sidebar.worktable.project', ownerProps)
|
? renderProjectSlot('sidebar.worktable.project', ownerProps)
|
||||||
|
|||||||
@@ -44,6 +44,13 @@ export const zh = {
|
|||||||
'add.layoutSave': '进入工作区',
|
'add.layoutSave': '进入工作区',
|
||||||
'add.layoutInvalid': '请填写布局名称',
|
'add.layoutInvalid': '请填写布局名称',
|
||||||
'manage.deleteLayout': '删除布局',
|
'manage.deleteLayout': '删除布局',
|
||||||
|
'manage.deleteProject': '删除项目',
|
||||||
|
'confirm.title': '删除项目',
|
||||||
|
'confirm.cancel': '取消',
|
||||||
|
'confirm.delete': '删除',
|
||||||
|
'confirm.layoutBody': '确定删除「{name}」?该工作区及其布局将被移除,此操作不可恢复。',
|
||||||
|
'confirm.shortcutBody': '确定删除快捷方式「{name}」?此操作不可恢复。',
|
||||||
|
'confirm.projectBody': '确定从工作台删除「{name}」?插件本身不会被卸载,之后可在「管理项目」→「恢复默认」找回。',
|
||||||
'pane.browser': '浏览器',
|
'pane.browser': '浏览器',
|
||||||
'pane.explorer': '资源管理器',
|
'pane.explorer': '资源管理器',
|
||||||
'pane.scm': '源代码管理',
|
'pane.scm': '源代码管理',
|
||||||
@@ -113,6 +120,13 @@ export const en = {
|
|||||||
'add.layoutSave': 'Enter workspace',
|
'add.layoutSave': 'Enter workspace',
|
||||||
'add.layoutInvalid': 'Enter a layout name',
|
'add.layoutInvalid': 'Enter a layout name',
|
||||||
'manage.deleteLayout': 'Delete layout',
|
'manage.deleteLayout': 'Delete layout',
|
||||||
|
'manage.deleteProject': 'Delete project',
|
||||||
|
'confirm.title': 'Delete project',
|
||||||
|
'confirm.cancel': 'Cancel',
|
||||||
|
'confirm.delete': 'Delete',
|
||||||
|
'confirm.layoutBody': 'Delete "{name}"? Its workspace and layout will be removed. This cannot be undone.',
|
||||||
|
'confirm.shortcutBody': 'Delete shortcut "{name}"? This cannot be undone.',
|
||||||
|
'confirm.projectBody': 'Remove "{name}" from the worktable? The plugin stays installed; restore it later via Manage → Reset defaults.',
|
||||||
'pane.browser': 'Browser',
|
'pane.browser': 'Browser',
|
||||||
'pane.explorer': 'Explorer',
|
'pane.explorer': 'Explorer',
|
||||||
'pane.scm': 'Source control',
|
'pane.scm': 'Source control',
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ export const css = [
|
|||||||
'.dsh-wt_layout[data-on=true] .dsh-wt_layoutName{color:var(--dsw-alias-state-accent-primary,#4f8ef7)}',
|
'.dsh-wt_layout[data-on=true] .dsh-wt_layoutName{color:var(--dsw-alias-state-accent-primary,#4f8ef7)}',
|
||||||
'.dsh-wt_layoutIcon{flex:none;font-size:13px}',
|
'.dsh-wt_layoutIcon{flex:none;font-size:13px}',
|
||||||
'.dsh-wt_layoutText{flex:1;min-width:0;display:flex;flex-direction:column;gap:0}',
|
'.dsh-wt_layoutText{flex:1;min-width:0;display:flex;flex-direction:column;gap:0}',
|
||||||
'.dsh-wt_layoutName{font-size:12px;font-weight:600;line-height:17px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}',
|
'.dsh-wt_layoutName{font-size:12px;font-weight:400;line-height:17px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}',
|
||||||
'.dsh-wt_layoutArrow{flex:none;color:var(--dsw-alias-label-tertiary,#6e7683);font-size:14px;line-height:20px}',
|
'.dsh-wt_layoutArrow{flex:none;color:var(--dsw-alias-label-tertiary,#6e7683);font-size:14px;line-height:20px}',
|
||||||
// 项目卡片统一:入驻插件卡片(travelatlas/planreview)与布局卡片同款常显框(静止态可见边框+底)
|
// 项目卡片统一:入驻插件卡片(travelatlas/planreview)与布局卡片同款常显框(静止态可见边框+底)
|
||||||
'.dsh-wt_projects .ta_card,.dsh-wt_projects .pr_card{border-color:var(--dsw-alias-border-l1,#262b36);background:var(--dsw-alias-fill-l1,rgba(255,255,255,.02))}',
|
'.dsh-wt_projects .ta_card,.dsh-wt_projects .pr_card{border-color:var(--dsw-alias-border-l1,#262b36);background:var(--dsw-alias-fill-l1,rgba(255,255,255,.02))}',
|
||||||
@@ -123,7 +123,10 @@ export const css = [
|
|||||||
'.dsh-wt_iconPick:hover{background:var(--dsw-alias-fill-l1,rgba(255,255,255,.08))}',
|
'.dsh-wt_iconPick:hover{background:var(--dsw-alias-fill-l1,rgba(255,255,255,.08))}',
|
||||||
// 入驻项目卡片图标覆盖:data-wt-icon 存在时用 attr() 替换显示(原字符字号压到 0)
|
// 入驻项目卡片图标覆盖:data-wt-icon 存在时用 attr() 替换显示(原字符字号压到 0)
|
||||||
'.dsh-wt_projects .ta_cardIcon[data-wt-icon],.dsh-wt_projects .pr_cardIcon[data-wt-icon]{font-size:0;line-height:20px}',
|
'.dsh-wt_projects .ta_cardIcon[data-wt-icon],.dsh-wt_projects .pr_cardIcon[data-wt-icon]{font-size:0;line-height:20px}',
|
||||||
'.dsh-wt_projects .ta_cardIcon[data-wt-icon]::before,.dsh-wt_projects .pr_cardIcon[data-wt-icon]::before{content:attr(data-wt-icon);font-size:15px;line-height:20px}',
|
'.dsh-wt_projects .ta_cardIcon[data-wt-icon]::before,.dsh-wt_projects .pr_cardIcon[data-wt-icon]::before{content:attr(data-wt-icon);font-size:13px;line-height:20px}',
|
||||||
|
// 常驻项目卡片与新建项目统一:emoji 字号 13px、名称不加粗
|
||||||
|
'.dsh-wt_projects .ta_cardIcon,.dsh-wt_projects .pr_cardIcon{font-size:13px;line-height:20px}',
|
||||||
|
'.dsh-wt_projects .ta_cardName,.dsh-wt_projects .pr_cardName{font-weight:400}',
|
||||||
// 头部按钮内的官方 SVG 图标
|
// 头部按钮内的官方 SVG 图标
|
||||||
'.dsh-wt_iconBtn svg{display:block}',
|
'.dsh-wt_iconBtn svg{display:block}',
|
||||||
// 窗内容:选择器 / 自定义 / 浏览器 / 占位
|
// 窗内容:选择器 / 自定义 / 浏览器 / 占位
|
||||||
@@ -149,6 +152,16 @@ export const css = [
|
|||||||
'.dsh-wt_splitFlip:hover{color:var(--dsw-alias-label-primary,#e6e8eb);background:var(--dsw-alias-fill-l1,rgba(255,255,255,.06))}',
|
'.dsh-wt_splitFlip:hover{color:var(--dsw-alias-label-primary,#e6e8eb);background:var(--dsw-alias-fill-l1,rgba(255,255,255,.06))}',
|
||||||
// + 右侧弹出面板
|
// + 右侧弹出面板
|
||||||
'.dsh-wt_popBackdrop{position:fixed;left:0;top:0;right:0;bottom:0;z-index:79;background:transparent}',
|
'.dsh-wt_popBackdrop{position:fixed;left:0;top:0;right:0;bottom:0;z-index:79;background:transparent}',
|
||||||
|
// 删除二次确认(fixed 居中模态,压在弹窗之上)
|
||||||
|
'.dsh-wt_confirmBackdrop{position:fixed;left:0;top:0;right:0;bottom:0;z-index:81;background:rgba(0,0,0,.35)}',
|
||||||
|
'.dsh-wt_confirm{position:fixed;left:50%;top:50%;transform:translate(-50%,-50%);z-index:82;width:292px;display:flex;flex-direction:column;gap:8px;padding:14px;border:1px solid var(--dsw-alias-border-l2,#3a4150);border-radius:10px;background:var(--dsw-alias-bg-base,#0b0e14);box-shadow:var(--dsw-shadow-lv2,0 8px 24px rgba(0,0,0,.4))}',
|
||||||
|
'.dsh-wt_confirmTitle{font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary,#e6e8eb)}',
|
||||||
|
'.dsh-wt_confirmBody{font-size:11.5px;line-height:17px;color:var(--dsw-alias-label-secondary,#9aa4b2)}',
|
||||||
|
'.dsh-wt_confirmActions{display:flex;justify-content:flex-end;gap:8px;margin-top:4px}',
|
||||||
|
'.dsh-wt_confirmCancel{padding:5px 12px;border:1px solid var(--dsw-alias-border-l2,#3a4150);border-radius:6px;background:transparent;color:var(--dsw-alias-label-secondary,#9aa4b2);font:inherit;font-size:12px;cursor:pointer}',
|
||||||
|
'.dsh-wt_confirmCancel:hover{color:var(--dsw-alias-label-primary,#e6e8eb);background:var(--dsw-alias-fill-l1,rgba(255,255,255,.05))}',
|
||||||
|
'.dsh-wt_confirmDelete{padding:5px 12px;border:none;border-radius:6px;background:var(--dsw-alias-state-danger,#e5484d);color:#fff;font:inherit;font-size:12px;cursor:pointer}',
|
||||||
|
'.dsh-wt_confirmDelete:hover{filter:brightness(1.1)}',
|
||||||
'.dsh-wt_pop{max-height:min(540px,calc(100vh - 32px));overflow:auto;box-shadow:var(--dsw-shadow-lv2,0 8px 24px rgba(0,0,0,.4))}',
|
'.dsh-wt_pop{max-height:min(540px,calc(100vh - 32px));overflow:auto;box-shadow:var(--dsw-shadow-lv2,0 8px 24px rgba(0,0,0,.4))}',
|
||||||
// 功能窗(资源管理器/SCM/任务/终端)
|
// 功能窗(资源管理器/SCM/任务/终端)
|
||||||
'.dsh-wt_subBar{flex:none;display:flex;align-items:center;gap:4px;padding:3px 6px;border-bottom:1px solid var(--dsw-alias-border-l1,#262b36);background:var(--dsw-alias-fill-l1,rgba(255,255,255,.02))}',
|
'.dsh-wt_subBar{flex:none;display:flex;align-items:center;gap:4px;padding:3px 6px;border-bottom:1px solid var(--dsw-alias-border-l1,#262b36);background:var(--dsw-alias-fill-l1,rgba(255,255,255,.02))}',
|
||||||
|
|||||||
@@ -554,3 +554,19 @@
|
|||||||
项目卡区 data-managing 弱化保持。
|
项目卡区 data-managing 弱化保持。
|
||||||
- 验证:functional-diag STEP9 managePos=fixed、manageLeft=288(同弹窗锚点)、rows=3、
|
- 验证:functional-diag STEP9 managePos=fixed、manageLeft=288(同弹窗锚点)、rows=3、
|
||||||
遮罩关闭 PASS;全 STEP 回归 ERROR_COUNT: 0。纯客户端改动,F5 生效。
|
遮罩关闭 PASS;全 STEP 回归 ERROR_COUNT: 0。纯客户端改动,F5 生效。
|
||||||
|
## 补记(删除二次确认 + 常驻项目可删 + 字号/字重统一)
|
||||||
|
|
||||||
|
- 用户反馈(四条):① 常驻两项目也要删除 ✕;② 删除(常驻/新建/快捷方式)都要二次确认警告弹窗;
|
||||||
|
③ 常驻项目 emoji 字号与新建项目不一致;④ 所有项目名改为不加粗。
|
||||||
|
- 处理:
|
||||||
|
① projects.v1 新增 removed 列表:删除常驻项目 = 移出工作台(插件不卸载),
|
||||||
|
卡片/管理行/rail 全部隐藏(ownerProps.hidden 并入 removed);「恢复默认」清空 removed/iconOverrides 找回;
|
||||||
|
管理面板全部行(常驻/布局/快捷方式)都有 ✕。
|
||||||
|
② 删除统一走 askDelete → fixed 居中警告弹窗(⚠️ 标题 + 按类型文案 + 取消/红色删除),
|
||||||
|
遮罩 81/弹窗 82 压在管理弹窗之上;取消不动、确认执行(布局删条目/常驻进 removed)。
|
||||||
|
③ 常驻卡片 emoji 统一 13px(含 data-wt-icon ::before 15px→13px);
|
||||||
|
④ ta/pr 卡片名 font-weight 400,.dsh-wt_layoutName 600→400。
|
||||||
|
- 验证(functional-diag STEP10):prIconSize=13px、taIconBeforeSize=13px、taNameWeight/laNameWeight=400;
|
||||||
|
管理行 ✕ → confirmShown=true → 取消(卡片仍在)→ 再 ✕ 删除 → cardsAfter pr:0 + removed=['planreview'];
|
||||||
|
布局 ✕ 确认后 layoutsLeft=0;rowsAfterDelete=2;全 STEP 回归 ERROR_COUNT: 0。
|
||||||
|
- 备注:纯客户端改动,F5 即生效,无需重启 dsh web。
|
||||||
|
|||||||
@@ -187,6 +187,63 @@ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|||||||
})()`);
|
})()`);
|
||||||
console.log('STEP9:', JSON.stringify(step9));
|
console.log('STEP9:', JSON.stringify(step9));
|
||||||
|
|
||||||
|
// 删除二次确认:常驻项目/布局 ✕ → 警告弹窗 → 取消不删 / 确认删除;emoji 字号与名称字重统一
|
||||||
|
const step10 = await evaluate(`(async function(){
|
||||||
|
var out={};
|
||||||
|
var prIcon=document.querySelector('.dsh-wt_projects .pr_cardIcon');
|
||||||
|
var taIcon=document.querySelector('.dsh-wt_projects .ta_cardIcon');
|
||||||
|
var taName=document.querySelector('.dsh-wt_projects .ta_cardName');
|
||||||
|
var laName=document.querySelector('.dsh-wt_layoutName');
|
||||||
|
if(prIcon) out.prIconSize=getComputedStyle(prIcon).fontSize;
|
||||||
|
if(taIcon) out.taIconBeforeSize=getComputedStyle(taIcon,'::before').fontSize;
|
||||||
|
if(taName) out.taNameWeight=getComputedStyle(taName).fontWeight;
|
||||||
|
if(laName) out.laNameWeight=getComputedStyle(laName).fontWeight;
|
||||||
|
out.cardsBefore={ta:document.querySelectorAll('.dsh-wt_projects .ta_card').length,pr:document.querySelectorAll('.dsh-wt_projects .pr_card').length};
|
||||||
|
var btn=document.querySelector('.dsh-wt_actions .dsh-wt_iconBtn:nth-child(2)');
|
||||||
|
if(btn){ btn.click(); }
|
||||||
|
await new Promise(function(r){setTimeout(r,250)});
|
||||||
|
var items=document.querySelectorAll('.dsh-wt_menu.dsh-wt_pop .dsh-wt_menuItem');
|
||||||
|
if(items.length){ items[items.length-1].click(); }
|
||||||
|
await new Promise(function(r){setTimeout(r,300)});
|
||||||
|
var rows=document.querySelectorAll('.dsh-wt_manage.dsh-wt_pop .dsh-wt_manageRow');
|
||||||
|
out.rows0=rows.length;
|
||||||
|
if(rows[0]) out.row0BtnCount=rows[0].querySelectorAll('.dsh-wt_manageBtn').length;
|
||||||
|
var xBtn=rows[0]&&rows[0].querySelectorAll('.dsh-wt_manageBtn')[rows[0].querySelectorAll('.dsh-wt_manageBtn').length-1];
|
||||||
|
if(xBtn){ xBtn.click(); }
|
||||||
|
await new Promise(function(r){setTimeout(r,250)});
|
||||||
|
out.confirmShown=!!document.querySelector('.dsh-wt_confirm');
|
||||||
|
var cancel=document.querySelector('.dsh-wt_confirmCancel');
|
||||||
|
if(cancel){ cancel.click(); }
|
||||||
|
await new Promise(function(r){setTimeout(r,200)});
|
||||||
|
out.confirmGoneAfterCancel=!document.querySelector('.dsh-wt_confirm');
|
||||||
|
out.taStillThere=!!document.querySelector('.dsh-wt_projects .ta_card');
|
||||||
|
var rows2=document.querySelectorAll('.dsh-wt_manage.dsh-wt_pop .dsh-wt_manageRow');
|
||||||
|
var xBtn2=rows2[0]&&rows2[0].querySelectorAll('.dsh-wt_manageBtn')[rows2[0].querySelectorAll('.dsh-wt_manageBtn').length-1];
|
||||||
|
if(xBtn2){ xBtn2.click(); }
|
||||||
|
await new Promise(function(r){setTimeout(r,250)});
|
||||||
|
var del=document.querySelector('.dsh-wt_confirmDelete');
|
||||||
|
if(del){ del.click(); }
|
||||||
|
await new Promise(function(r){setTimeout(r,300)});
|
||||||
|
out.cardsAfter={ta:document.querySelectorAll('.dsh-wt_projects .ta_card').length,pr:document.querySelectorAll('.dsh-wt_projects .pr_card').length};
|
||||||
|
try{ out.removed=JSON.parse(localStorage.getItem('dsh.worktable.projects.v1')).removed }catch(e){ out.removed='ERR' }
|
||||||
|
out.rowsAfterDelete=document.querySelectorAll('.dsh-wt_manage.dsh-wt_pop .dsh-wt_manageRow').length;
|
||||||
|
var rows3=document.querySelectorAll('.dsh-wt_manage.dsh-wt_pop .dsh-wt_manageRow');
|
||||||
|
var lastRow=rows3[rows3.length-1];
|
||||||
|
var xBtn3=lastRow&&lastRow.querySelectorAll('.dsh-wt_manageBtn')[lastRow.querySelectorAll('.dsh-wt_manageBtn').length-1];
|
||||||
|
if(xBtn3){ xBtn3.click(); }
|
||||||
|
await new Promise(function(r){setTimeout(r,250)});
|
||||||
|
var del2=document.querySelector('.dsh-wt_confirmDelete');
|
||||||
|
if(del2){ del2.click(); }
|
||||||
|
await new Promise(function(r){setTimeout(r,300)});
|
||||||
|
out.layoutGoneAfterDelete=!document.querySelector('.dsh-wt_layout');
|
||||||
|
try{ out.layoutsLeft=JSON.parse(localStorage.getItem('dsh.worktable.projects.v1')).layouts.length }catch(e){ out.layoutsLeft='ERR' }
|
||||||
|
var done=document.querySelector('.dsh-wt_manageDone');
|
||||||
|
if(done){ done.click(); }
|
||||||
|
await new Promise(function(r){setTimeout(r,200)});
|
||||||
|
return JSON.stringify(out);
|
||||||
|
})()`);
|
||||||
|
console.log('STEP10:', JSON.stringify(step10));
|
||||||
|
|
||||||
const errors = events.filter((e) =>
|
const errors = events.filter((e) =>
|
||||||
e.method === 'Runtime.exceptionThrown' ||
|
e.method === 'Runtime.exceptionThrown' ||
|
||||||
(e.method === 'Log.entryAdded' && e.params.entry.level === 'error') ||
|
(e.method === 'Log.entryAdded' && e.params.entry.level === 'error') ||
|
||||||
|
|||||||
Reference in New Issue
Block a user